package org.pegadi.artis; import org.pegadi.artis.text.PersonInText; import org.pegadi.artis.text.PersonInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.*; import org.w3c.dom.Element; import javax.swing.text.*; public abstract class AbstractTextDocument extends javax.swing.text.DefaultStyledDocument { /** The document type definition for this document. */ DocumentType dt; /** The schema element representing the root node. */ Element schemaRoot; /** Next five int's are needed to make automatic uppercase-letters when writing documents*/ protected static final int SPACE_CASE_INIT=1; protected static final int SPACE_CASE_DOT=2; protected static final int SPACE_CASE_SPACE=3; protected int spaceCaseState; private static final int EASTER_INIT=1; private static final int EASTER_NEXT=2; private int easterState = EASTER_INIT; protected int lastOffset; protected boolean autoCorrection=false; public Logger log = LoggerFactory.getLogger(getClass()); public AbstractTextDocument(Node root, StyleContext styles, DocumentType docType) { super(styles); log.info("root: " + root); NodeList elements = root.getChildNodes(); for (int i = 0; i < elements.getLength(); i++) { addNode(elements.item(i)); } // XMLUtil.debugDump(docType, " "); dt = docType; //schemaRoot = XMLUtil.getSchemaElement(root.getNodeName(), // root.getOwnerDocument()); schemaRoot = null; // Remove the extra element that was added when the document was created. // But only if we have some content if (getLength() > 0) { try { this.remove(this.getLength()-1, 1); } catch (BadLocationException ble) { log.error("Failed to remove extra element at the end.", ble); } } spaceCaseState = SPACE_CASE_INIT; } public void toggleAutoCorrection(){ autoCorrection = !autoCorrection; } public boolean getAutoCorrectionState(){ return autoCorrection; } public void setAutoCorrectState(boolean state){ autoCorrection = state; } /** * Inserts a new paragraph at the given position. This method will return * <code>false</code> if the schema used does not allow a new paragraph at this * point.<br> * If multiple paragraph types is available at the given point this method will * first attempt to insert a paragraph of the same type as the previous paragrap, * or the first encoutered paragrap if the same as the previous is not allowed. * * @param offset Position to insert at. * @param str The text to insert. * @param a Attributes that will be applied to the text. * @return <code>true</code> if the paragraph was created. * @throws BadLocationException If the offset given is not available. * https://pegadi.underdusken.no/ticket/83 */ protected boolean createParagraph(int offset, String str, AttributeSet a) { // Walk through the document to find the current paragraph int pos = 0; javax.swing.text.Element curPara; Element schemaNode = (Element)schemaRoot.getFirstChild(); log.info("First schema node is " + schemaNode.getAttribute("name")); while (offset > pos) { curPara = super.getParagraphElement(pos); pos = curPara.getEndOffset(); } return false; } public void checkEaster(String str) { if(easterState==EASTER_INIT && str.equalsIgnoreCase("l")) { //\u00f8 easterState=EASTER_NEXT; } else if (easterState == EASTER_NEXT && str.equalsIgnoreCase("s")) { easterState=EASTER_INIT; // Try to confuse the user a bit :-) if(System.currentTimeMillis() % 10 == 0) { fireEaster(); } } else { easterState=EASTER_INIT; } } /** * Checks if this node is a ignorable whitespace node */ public boolean isIgnorable(Node node) { if (node instanceof Text) { String data = ((Text)node).getData(); for(int i = 0;i<data.length();i++) { if(!Character.isWhitespace(data.charAt(i))) { return false; } } } else return false; return true; } /** * Adds a new XML node to the document. This method does not check if this is * legal according to the DTD. * * @param node The node to add. */ protected void addNode(Node node) { if (node instanceof Text && !isIgnorable(node)) { try { // Styles are applied on the above level String data = ((Text)node).getData(); String txt = data.replace('\n', ' '); // Remove all consecutive spaces. // FIXME: The XML parser should do this instead, but we don't // know how to make it do it. int i; while ((i = txt.indexOf(" ")) != -1) { int j = i+2; while (j > txt.length() && txt.charAt(j) == ' ') { j++; } txt = txt.substring(0, i+1) + txt.substring(j); } super.insertString(getLength(), txt, null); return; } catch (BadLocationException ble) { log.error("ERROR inserting text", ble); } } int before = getLength(); if(node.getNodeName().equals("person")) { StringBuilder name = new StringBuilder(); for(int i = 0; i < node.getChildNodes().getLength(); i++) { if(node.getChildNodes().item(i) instanceof Text) { name.append(((Text)node.getChildNodes().item(i)).getData()); } } Style personStyle = addStyle("person", StyleContext.getDefaultStyleContext().getStyle(StyleContext.DEFAULT_STYLE)); PersonInText box = new PersonInText(new PersonInfo("0", name.toString(), "Default")); StyleConstants.setComponent(personStyle, box); try { super.insertString(getLength(), " ", personStyle); return; } catch (BadLocationException e) { log.error("Badlocation", e); } } NodeList children = node.getChildNodes(); if (children != null && children.getLength() > 0) { for (int i = 0; i < children.getLength(); i++) { addNode(children.item(i)); } } else { return; } Style s = getStyle(node.getNodeName()); if (s == null) { log.info("Style {} is unknown, adding style.", node.getNodeName()); Style def = StyleContext.getDefaultStyleContext().getStyle(StyleContext.DEFAULT_STYLE); s = addStyle(node.getNodeName(), def); } Object display = s.getAttribute("display"); if (display != null && display.equals("block")) { try { super.insertString(getLength(), "\n", null); } catch (BadLocationException e) { log.error("Unable to add terminating '\\n' at block element.", e); } super.setParagraphAttributes(before, getLength() - before, s, false); } else { super.setCharacterAttributes(before, getLength() - before, s, false); } } public void addEasterListener(EasterListener l) { listenerList.add(EasterListener.class, l); } public void fireEaster() { Object[] listeners = listenerList.getListenerList(); // Process the listeners last to first, notifying // those that are interested in this event for (int i = listeners.length-2; i>=0; i-=2) { if (listeners[i] == EasterListener.class) { ((EasterListener)listeners[i+1]).easterEventHappened(); } } } public void fireUpdatePerformed(int offset) { Object[] listeners = listenerList.getListenerList(); // Process the listeners last to first, notifying // those that are interested in this event for (int i = listeners.length-2; i>=0; i-=2) { if (listeners[i] == UpdatePerformedListener.class) { ((UpdatePerformedListener)listeners[i+1]).updatePerformed(offset); } } } public void addUpdatePerformedListener(UpdatePerformedListener upl) { listenerList.add(UpdatePerformedListener.class, upl); } }