Java tutorial
/** * SwingBoxEditorKit.java * (c) Peter Bielik and Radek Burget, 2011-2012 * * SwingBox is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * SwingBox is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with SwingBox. If not, see <http://www.gnu.org/licenses/>. * */ package org.fit.cssbox.swingbox; import java.awt.Container; import java.awt.Dimension; import java.awt.Toolkit; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Reader; import java.io.Writer; import java.net.URL; import java.nio.charset.Charset; import java.util.List; import javax.swing.JEditorPane; import javax.swing.JViewport; import javax.swing.text.BadLocationException; import javax.swing.text.Caret; import javax.swing.text.DefaultStyledDocument.ElementSpec; import javax.swing.text.Document; import javax.swing.text.StyledEditorKit; import javax.swing.text.ViewFactory; import org.apache.commons.io.input.ReaderInputStream; import org.fit.cssbox.io.DocumentSource; import org.fit.cssbox.io.StreamDocumentSource; import org.fit.cssbox.layout.Viewport; import org.fit.cssbox.swingbox.util.CSSBoxAnalyzer; import org.fit.cssbox.swingbox.util.Constants; import org.fit.cssbox.swingbox.util.ContentReader; import org.fit.cssbox.swingbox.util.ContentWriter; import org.fit.cssbox.swingbox.util.GeneralEvent; import org.fit.cssbox.swingbox.util.GeneralEvent.EventType; import org.fit.cssbox.swingbox.util.MouseController; /** * This is custom implementation of EditoKit for (X)HTML with use of CSSBox. * * @author Peter Bielik * @version 1.0 * @since 1.0 - 28.9.2010 */ public class SwingBoxEditorKit extends StyledEditorKit { private static final long serialVersionUID = -2774578978116020429L; /*private static final Pattern charsetPattern = Pattern .compile("charset\\s*=[\\s'\"]*([\\-\\.\\:_0-9a-zA-Z]+)[\\s'\\\",;]*");*/ private CSSBoxAnalyzer cbanalyzer; private ViewFactory vfactory; private JEditorPane component; private MouseController mcontroller; /** * Instantiates a new swing box editor kit. */ public SwingBoxEditorKit() { super(); String tmp; tmp = System.getProperty(Constants.DEFAULT_ANALYZER_PROPERTY, Constants.PROPERTY_NOT_SET); if (tmp == Constants.PROPERTY_NOT_SET) { // sets property for default analyzer, the fully qualified classname // which is used to instantiate this class by reflection System.setProperty(Constants.DEFAULT_ANALYZER_PROPERTY, "org.fit.cssbox.swingbox.util.DefaultAnalyzer"); } tmp = System.getProperty(Constants.DOCUMENT_ASYNCHRONOUS_LOAD_PRIORITY_PROPERTY, Constants.PROPERTY_NOT_SET); if (tmp == Constants.PROPERTY_NOT_SET) { // property not set, load synchronously ! System.setProperty(Constants.DOCUMENT_ASYNCHRONOUS_LOAD_PRIORITY_PROPERTY, "-1"); } mcontroller = new MouseController(); } /** * Instantiates a new swing box editor kit with CSSBoxAnalyzer set. * * @param cba * the CSSBoxAnalyzer to be set */ public SwingBoxEditorKit(CSSBoxAnalyzer cba) { this(); this.cbanalyzer = cba; } @Override public void install(JEditorPane c) { super.install(c); c.addMouseListener(mcontroller); c.addMouseMotionListener(mcontroller); component = c; } @Override public void deinstall(JEditorPane c) { super.deinstall(c); c.removeMouseListener(mcontroller); c.removeMouseMotionListener(mcontroller); component = null; } @Override public Document createDefaultDocument() { SwingBoxDocument doc = new SwingBoxDocument(); // set asynchronous load priority. If set to -1, load synchronously, // otherwise load asynchronously, with given priority :) // this value is stored as internal property under // AbstractDocument.AsyncLoadPriority key. int priority = -1;// -1 == synchronously String tmp = System.getProperty(Constants.DOCUMENT_ASYNCHRONOUS_LOAD_PRIORITY_PROPERTY, Constants.PROPERTY_NOT_SET); if (tmp != Constants.PROPERTY_NOT_SET) { try { priority = Integer.parseInt(tmp); } catch (Exception ignored) { } } doc.setAsynchronousLoadPriority(priority); return doc; } @Override public ViewFactory getViewFactory() { if (vfactory == null) { vfactory = new SwingBoxViewFactory(); } return vfactory; } @Override public String getContentType() { return "text/html"; } @Override public Caret createCaret() { return null; } @Override public void write(OutputStream out, Document doc, int pos, int len) throws IOException, BadLocationException { // this method closes OutputStream if (doc instanceof SwingBoxDocument) { Writer tmpOut = new BufferedWriter(new OutputStreamWriter(out, Charset.defaultCharset()), 8 * 1024); writeImpl(tmpOut, (SwingBoxDocument) doc, pos, len); tmpOut.flush(); tmpOut.close(); } else { super.write(out, doc, pos, len); } } @Override public void write(Writer out, Document doc, int pos, int len) throws IOException, BadLocationException { // this method closes OutputStream if (doc instanceof SwingBoxDocument) { Writer tmpOut = new BufferedWriter(out, 8 * 1024); writeImpl(tmpOut, (SwingBoxDocument) doc, pos, len); tmpOut.flush(); tmpOut.close(); } else { super.write(out, doc, pos, len); } } @Override public void read(InputStream in, Document doc, int pos) throws IOException, BadLocationException { if (doc instanceof org.fit.cssbox.swingbox.SwingBoxDocument) { readImpl(in, (org.fit.cssbox.swingbox.SwingBoxDocument) doc, pos); } else { super.read(in, doc, pos); } } @Override public void read(Reader in, Document doc, int pos) throws IOException, BadLocationException { if (doc instanceof org.fit.cssbox.swingbox.SwingBoxDocument) { InputStream is = new ReaderInputStream(in); readImpl(is, (org.fit.cssbox.swingbox.SwingBoxDocument) doc, pos); } else { super.read(in, doc, pos); } } /** * Updates layout, using new dimensions. * * @param doc * the document * @param root * the root box * @param dim * new dimension * @throws IOException * Signals that an I/O exception has occurred. */ public void update(SwingBoxDocument doc, Viewport root, Dimension dim) throws IOException { ContentReader rdr = new ContentReader(); List<ElementSpec> elements = rdr.update(root, dim, getCSSBoxAnalyzer()); ElementSpec elementsArray[] = elements.toArray(new ElementSpec[0]); doc.create(elementsArray); } /** * Allows to set custom CSSBoxAnalyzer * * @param cba * the instance of CSSBoxAnalyzer * @see CSSBoxAnalyzer */ public void setCSSBoxAnalyzer(CSSBoxAnalyzer cba) { this.cbanalyzer = cba; } /** * Gets current instance of {@link CSSBoxAnalyzer} * * @return the instance of {@link CSSBoxAnalyzer} */ public CSSBoxAnalyzer getCSSBoxAnalyzer() { if (cbanalyzer == null) { cbanalyzer = getDefaultAnalyzer(); } return cbanalyzer; } @SuppressWarnings("rawtypes") protected CSSBoxAnalyzer getDefaultAnalyzer() { // possible to provide custom implementation CSSBoxAnalyzer cba; String cname = System.getProperty(Constants.DEFAULT_ANALYZER_PROPERTY, Constants.PROPERTY_NOT_SET); if (Constants.PROPERTY_NOT_SET == cname) { cba = null; } else { try { Class c; ClassLoader loader = getClass().getClassLoader(); if (loader != null) { c = loader.loadClass(cname); } else { c = Class.forName(cname); } Object o = c.newInstance(); if (o instanceof CSSBoxAnalyzer) { cba = (CSSBoxAnalyzer) o; } else { cba = null; } } catch (Exception e) { cba = null; } } return cba; } private void readImpl(InputStream in, SwingBoxDocument doc, int pos) throws IOException, BadLocationException { if (component == null) throw new IllegalStateException( "Component is null, editor kit is probably deinstalled from a JEditorPane."); if (pos > doc.getLength() || pos < 0) { BadLocationException e = new BadLocationException("Invalid location", pos); readError(null, e); throw e; } ContentReader rdr = new ContentReader(); URL url = (URL) doc.getProperty(Document.StreamDescriptionProperty); CSSBoxAnalyzer analyzer = getCSSBoxAnalyzer(); Container parent = component.getParent(); Dimension dim; if (parent != null && parent instanceof JViewport) { dim = ((JViewport) parent).getExtentSize(); } else { dim = component.getBounds().getSize(); } if (dim.width <= 10) { // component might not be initialized, use screen size :) Dimension tmp = Toolkit.getDefaultToolkit().getScreenSize(); dim.setSize(tmp.width / 2.5, tmp.height / 2.5); } // long time = System.currentTimeMillis(); List<ElementSpec> elements; try { String ctype = null; Object ct = doc.getProperty("Content-Type"); if (ct != null) { if (ct instanceof List) ctype = (String) ((List<?>) ct).get(0); else ctype = ct.toString(); } DocumentSource docSource = new StreamDocumentSource(in, url, ctype); elements = rdr.read(docSource, analyzer, dim); String title = analyzer.getDocumentTitle(); if (title == null) title = "No title"; doc.putProperty(Document.TitleProperty, title); } catch (IOException e) { readError(url, e); throw e; } // System.out.println(System.currentTimeMillis() - time + " ms"); ElementSpec elementsArray[] = elements.toArray(new ElementSpec[0]); doc.create(elementsArray); // component.revalidate(); // component.repaint(); // System.out.println(System.currentTimeMillis() - time + " ms"); // Dictionary<Object, Object> dic = doc.getDocumentProperties(); // Enumeration<Object> en = dic.keys(); // while( en.hasMoreElements()) { // Object k = en.nextElement(); // System.out.println(k + " " + dic.get(k)); // } readFinish(url); } private void readError(URL url, Exception e) { if (component instanceof BrowserPane) { ((BrowserPane) component) .fireGeneralEvent(new GeneralEvent(this, EventType.page_loading_error, url, e)); // NodeList nodes = // analyzer.getDocument().getElementsByTagName("meta"); } } private void readFinish(URL url) { if (component instanceof BrowserPane) { ((BrowserPane) component) .fireGeneralEvent(new GeneralEvent(this, EventType.page_loading_end, url, null)); // NodeList nodes = // analyzer.getDocument().getElementsByTagName("meta"); } } private void writeImpl(Writer out, SwingBoxDocument doc, int pos, int len) throws BadLocationException, IOException { if (pos > doc.getLength() || pos < 0) { throw new BadLocationException("Invalid location", pos); } if (len < 0) len = 0; ContentWriter wrt = new ContentWriter(); StringBuilder sb = wrt.write(getCSSBoxAnalyzer().getDocument()); out.write(sb.toString()); out.flush(); } }