//Copyright (c)2005 Holobloc Inc.
//Contributed to the OOONEIDA FBench project under the Common Public License.
package fbench.dom;
import java.awt.Point;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.StringWriter;
import java.util.Enumeration;
import java.util.Hashtable;
import javax.swing.JTextArea;
import javax.swing.text.BadLocationException;
import fbench.LibraryElementView;
//import fbench.XMLCodeGenerator;
import fbench.dom.events.ElementSelectionEvent;
import fbench.tree.DOMTree;
import org.apache.xerces.dom.AttrImpl;
import org.apache.xerces.dom.DocumentImpl;
import org.apache.xerces.dom.events.MutationEventImpl;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.NodeList;
import org.w3c.dom.events.Event;
import org.w3c.dom.events.EventListener;
import org.w3c.dom.events.MutationEvent;
/**
* A model for displaying and navigating in a JTextArea the XML source code of
* an IEC 61499 LibraryElement whose underlying model is a DOM Document.
*
* @author JHC
* @version 20080515/DG - Moved Xml writer to its own class.
* @version 20051115/JHC - Made a DOM EventListener displaying only selected
* Element.
* @version 20050420/JHC
*/
public class DOMTextModel extends ElementModel implements EventListener,
DOMWriterListener {
protected JTextArea textarea;
protected Hashtable<Element, Point> selectionRanges;
protected Document document;
private boolean modified = false;
public DOMTextModel(JTextArea textArea, Document document) {
super();
this.textarea = textArea;
this.textarea.addKeyListener(new KeyListener() {
public void keyPressed(KeyEvent evt) {
}
public void keyReleased(KeyEvent evt) {
}
public void keyTyped(KeyEvent evt) {
setModified(true);
}
});
this.selectionRanges = new Hashtable<Element, Point>();
setDocument(document);
}
public boolean isModified() {
return modified;
}
public void setModified(boolean mod) {
modified = mod;
}
/** Set the document and refresh the display. */
public void setDocument(Document doc) {
if (this.document != null) {
((DocumentImpl) document).removeEventListener(
MutationEventImpl.DOM_ATTR_MODIFIED, this, false);
((DocumentImpl) document).removeEventListener(
MutationEventImpl.DOM_CHARACTER_DATA_MODIFIED, this, false);
((DocumentImpl) document).removeEventListener(
MutationEventImpl.DOM_NODE_INSERTED, this, false);
((DocumentImpl) document).removeEventListener(
MutationEventImpl.DOM_NODE_REMOVED, this, false);
((DocumentImpl) document).removeEventListener(
"ElementSelectionEvent", this, false);
}
this.document = doc;
this.selectionRanges.clear();
// FIXED: Gsha041 added a MutationEvent listener :P
// ((org.w3c.dom.events.EventTarget)
// doc).addEventListener("MutationEvent", this,
// false);
((DocumentImpl) document).addEventListener(
MutationEventImpl.DOM_ATTR_MODIFIED, this, false);
((DocumentImpl) document).addEventListener(
MutationEventImpl.DOM_CHARACTER_DATA_MODIFIED, this, false);
((DocumentImpl) document).addEventListener(
MutationEventImpl.DOM_NODE_INSERTED, this, false);
((DocumentImpl) document).addEventListener(
MutationEventImpl.DOM_NODE_REMOVED, this, false);
((DocumentImpl) document).addEventListener("ElementSelectionEvent",
this, false);
// textarea.setText("");
// textarea.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
// Element rootElement = document.getDocumentElement();
// String root = rootElement.getNodeName();
// textarea.append("<!DOCTYPE " + root + " SYSTEM
// \""+this.document.getDoctype().getSystemId()+"\">\n");
//
// /*textarea.append("<!DOCTYPE " + root
// // TODO: change to auck uni website?
// + " SYSTEM \"http://www.holobloc.com/xml/");
// textarea.append(root.equals("DataType") ? root : "LibraryElement");
// textarea.append(".dtd\" >\n");*/
// write(rootElement, "");
try {
textarea.setText(DOMWriter.writeXmlString(this.document, this));
} catch (XmlException e) {
e.printStackTrace();
}
}
public void scrollToLine(int lineNum) {
try {
textarea.setCaretPosition(textarea.getLineStartOffset(lineNum));
} catch (BadLocationException blex) {
// Eg if lineNum = 0.. -1 -> line -1 does not exist... (but return
// no indent.. cause that is right)
blex.printStackTrace();
}
}
// /**
// * Write out the given DOM Element and its children to the text area with
// * the given indentation.
// */
// private void write(Element el, String indent) {
// Point p = new Point();
// textarea.append(indent);
// p.x = textarea.getText().length();
// textarea.append("<");
// String nname = el.getNodeName();
// textarea.append(nname);
// NamedNodeMap atts = el.getAttributes();
// int n = atts.getLength();
// if (n > 0) {
// for (int i = 0; i < n; i++) {
// Attr att = (Attr) atts.item(i);
// textarea.append(" ");
// textarea.append(att.getName());
// textarea.append("=\"");
// String val = att.getValue();
// val = XMLCodeGenerator.replaceReservedCharacters(val);
// textarea.append(val);
// textarea.append("\"");
// }
// }
// NodeList nodes = el.getChildNodes();
// n = nodes.getLength();
// if (n == 0) {
// textarea.append("/>\n");
// } else {
// textarea.append(">\n");
// String newindent = indent + " ";
// for (int i = 0; i < n; i++)
// write((Element) nodes.item(i), newindent);
// textarea.append(indent);
// textarea.append("</");
// textarea.append(nname);
// textarea.append(">\n");
// }
// p.y = textarea.getText().length() - 1;
// selectionRanges.put(el, p);
// }
//
// private String writeElement(Element el, String indent) {
// StringBuilder str = new StringBuilder();
// str.append(indent);
// str.append("<");
// String nname = el.getNodeName();
// str.append(nname);
// NamedNodeMap atts = el.getAttributes();
// int n = atts.getLength();
// if (n > 0) {
// for (int i = 0; i < n; i++) {
// Attr att = (Attr) atts.item(i);
// str.append(" ");
// str.append(att.getName());
// str.append("=\"");
// String val = att.getValue();
// val = XMLCodeGenerator.replaceReservedCharacters(val);
// str.append(val);
// str.append("\"");
// }
// }
// NodeList nodes = el.getChildNodes();
// n = nodes.getLength();
// if (n == 0) {
// str.append("/>\n");
// } else {
// str.append(">\n");
// String newindent = indent + " ";
// for (int i = 0; i < n; i++)
// str.append(writeElement((Element) nodes.item(i), newindent));
// str.append(indent);
// str.append("</");
// str.append(nname);
// str.append(">\n");
// }
//
// return str.toString();
// }
// private void updateSelRange(int startUpdate, int incrInLength) {
// Enumeration<Point> points = selectionRanges.elements();
// // System.out.println("updateSelRange " + StartUpdate + " " +
// // incrInLength);
// while (points.hasMoreElements()) {
// Point p = points.nextElement();
// // System.out.println("old " + p.toString());
// if (p.x > startUpdate)
// p.x += incrInLength;
// if (p.y > startUpdate)
// p.y += incrInLength;
// // System.out.println("new " + p.toString());
// }
// }
// /**
// * @return space between position & start of line
// */
// private String getIndentStr(int position) {
// try {
// int lineNum = textarea.getLineOfOffset(position);
// int prevEndIndex = textarea.getLineEndOffset(lineNum - 1);
// // int startIndex = textarea.getLineStartOffset(lineNum);
// StringBuilder str = new StringBuilder();
// for (int i = 0; i < position - prevEndIndex; i++) {
// str.append(" ");
// }
// return str.toString();// textarea.getText(prevEndIndex+1,
// // position-prevEndIndex+1);
// } catch (BadLocationException blex) {
// // Eg if lineNum = 0.. -1 -> line -1 does not exist... (but return
// // no indent.. cause that is right)
// blex.printStackTrace();
// return "";
// }
// }
public void handleEvent(Event evt) {
// System.out.println("[DOMTextModel] got event:" + evt.toString());
if (evt instanceof MutationEvent) {
// This should re-draw all the xml on addition of node etc...
try {
MutationEvent mevt = (MutationEvent) evt;
Element el = null;
if (mevt.getType().equals(MutationEventImpl.DOM_ATTR_MODIFIED)) {
AttrImpl attr = (AttrImpl) mevt.getRelatedNode();
el = attr.getOwnerElement();
} else
el = (Element) mevt.getRelatedNode();
// There's no point replacing the text selectively because it's
// going to rewrite the whole document anyway...
this.rewrite();
Point p = selectionRanges.get(el);
textarea.setCaretPosition(p.x);
// // System.out.println("[DOMTextModel] got Mutation:" +
// // mevt.toString() + " " + mevt.getType());
// if
// (mevt.getType().equals(MutationEventImpl.DOM_ATTR_MODIFIED))
// {
// // Get Node
// // AttrImpl attr = (AttrImpl) mevt.getRelatedNode();
// // Get Parent Element (Cuz node is an Attribute)
// // Element el = attr.getOwnerElement();
//
// // Get location of this element in textarea
// Point oldRange = selectionRanges.get(el);
// int position = oldRange.x;
//
// // Get the new element text
// String elString = DOMWriter.writeXmlString(el, this);
//
// // Replace only the relevant section of text.
// textarea.replaceRange("", position, oldRange.y);
// textarea.insert(elString, position);
//
// // Update the stored range -- bugged!!!
//
// int difference = elString.length()
// - (oldRange.y - oldRange.x);
// for (Point p : selectionRanges.values()) {
// if (p.x > position) {
// p.x += difference;
// if (p.y > position)
// p.y += difference;
// }
// }
//
// Point newRange = selectionRanges.get(el);
// newRange.x = position;
// newRange.y = position + elString.length();
// // ----
//
// // Set the caret position
// textarea.requestFocus();
// textarea.setCaretPosition(newRange.x);
// // textarea.setSelectionStart(newRange.x);
// // textarea.setSelectionEnd(newRange.y);
// } else {
// this.rewrite();
// Point p = selectionRanges.get(el);
// textarea.setCaretPosition(p.x);
// }
modified = true;
// evt.stopPropagation(); // event handled for the last time( is
// it ?)
} catch (Exception ex) {
ex.printStackTrace();
}
} else if (evt instanceof ElementSelectionEvent) {
Element e = ((ElementSelectionEvent) evt).getSelectedElement();
Point p = selectionRanges.get(e);
textarea.requestFocus();
// textarea.setSelectionStart(p.x);
// textarea.setSelectionEnd(p.y);
textarea.setCaretPosition(p.x);
}
}
public void setElement(Element el) {
super.setElement(el);
((org.w3c.dom.events.EventTarget) el).addEventListener("MutationEvent",
this, false);
if (el == document.getDocumentElement())
setDocument(document);
else {
try {
textarea.setText(DOMWriter.writeXmlString(el, this));
} catch (XmlException e) {
e.printStackTrace();
}
// textarea.setText("");
// write(el, "");
}
}
public void rewrite() {
try {
selectionRanges.clear();
textarea.setText(DOMWriter.writeXmlString(this.document, this));
} catch (XmlException e) {
e.printStackTrace();
}
}
public String getText(boolean forSave) {
if (forSave)
this.modified = false;
return textarea.getText();
}
/*
* (non-Javadoc)
*
* @see fbench.dom.DOMWriterListener#beginElement(org.w3c.dom.Element, int)
*/
@Override
public void beginElement(Element element, int position) {
selectionRanges.put(element, new Point(position, 0));
}
/*
* (non-Javadoc)
*
* @see fbench.dom.DOMWriterListener#endElement(org.w3c.dom.Element, int)
*/
@Override
public void endElement(Element element, int position) {
Point p = selectionRanges.get(element);
if (p != null)
p.y = position;
}
}