org.fife.rsta.ac.xml.XmlParser Maven / Gradle / Ivy
Show all versions of languagesupport Show documentation
/*
* 04/07/2012
*
* Copyright (C) 2012 Robert Futrell
* robert_futrell at users.sourceforge.net
* http://fifesoft.com/rsyntaxtextarea
*
* This library is distributed under a modified BSD license. See the included
* RSTALanguageSupport.License.txt file for details.
*/
package org.fife.rsta.ac.xml;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import javax.swing.text.BadLocationException;
import javax.swing.text.Element;
import javax.swing.text.Segment;
import org.fife.io.DocumentReader;
import org.fife.rsta.ac.xml.tree.XmlTreeNode;
import org.fife.ui.rsyntaxtextarea.RSyntaxDocument;
import org.fife.ui.rsyntaxtextarea.parser.AbstractParser;
import org.fife.ui.rsyntaxtextarea.parser.DefaultParseResult;
import org.fife.ui.rsyntaxtextarea.parser.DefaultParserNotice;
import org.fife.ui.rsyntaxtextarea.parser.ParseResult;
import org.fife.ui.rsyntaxtextarea.parser.ParserNotice;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLReaderFactory;
/**
* Parses XML code in an RSyntaxTextArea
.
*
* Like all RSTA Parser
s, an XmlParser
instance is
* notified when the RSTA's text content changes. After a small delay, it will
* parse the content as XML, building an AST and looking for any errors. When
* parsing is complete, a property change event of type {@link #PROPERTY_AST}
* is fired. Listeners can check the new value of the property for an
* {@link XmlTreeNode} that represents the root of a tree structure modeling
* the XML content in the text area. Note that the XmlTreeNode
* may be incomplete if there were parsing/syntax errors (it will usually be
* complete "up to" the error in the content).
*
* This parser cannot be shared amongst multiple instances of
* RSyntaxTextArea
.
*
* @author Robert Futrell
* @version 1.0
*/
public class XmlParser extends AbstractParser {
/**
* The property change event that's fired when the document is re-parsed.
* Applications can listen for this property change and update themselves
* accordingly. The property's "new value" will be an {@link XmlTreeNode}
* representing the root of a tree modeling the XML content. The "old
* value" is always null
.
*/
public static final String PROPERTY_AST = "XmlAST";
private XmlLanguageSupport xls;
private PropertyChangeSupport support;
private XmlTreeNode curElem;
private XmlTreeNode root;
private Locator locator;
public XmlParser(XmlLanguageSupport xls) {
this.xls = xls;
support = new PropertyChangeSupport(this);
}
public void addPropertyChangeListener(String prop, PropertyChangeListener l) {
support.addPropertyChangeListener(prop, l);
}
/**
* Creates the XML reader to use. Note that in 1.4 JRE's, the reader
* class wasn't defined by default, but in 1.5+ it is.
*
* @return The XML reader to use.
*/
private XMLReader createReader() {
XMLReader reader = null;
try {
reader = XMLReaderFactory.createXMLReader();
} catch (SAXException e) {
// Happens in JRE 1.4.x; 1.5+ define the reader class properly
try {
reader = XMLReaderFactory.createXMLReader(
"org.apache.crimson.parser.XMLReaderImpl");
} catch (SAXException se) {
//owner.displayException(se);
se.printStackTrace();
}
}
return reader;
}
/**
* Returns the XML model from the last time it was parsed.
*
* @return The root node of the XML model, or null
if it has
* not yet been parsed or an error occurred while parsing.
*/
public XmlTreeNode getAst() {
return root;
}
/**
* Returns a string representing the "main" attribute for an element.
*
* @param attributes The attributes of an element. Calling code should
* have already verified this has length > 0.
* @return The "main" attribute.
*/
private String getMainAttribute(Attributes attributes) {
int nameIndex = -1;
int idIndex = -1;
for (int i=0; i>> " + offs + "-" + len + " -> "+ pn);
}
public void endElement(String uri, String localName, String qName) {
curElem = (XmlTreeNode)curElem.getParent();
}
public void error(SAXParseException e) throws SAXException {
doError(e, ParserNotice.ERROR);
}
public void fatalError(SAXParseException e) throws SAXException {
doError(e, ParserNotice.ERROR);
}
private int getTagStart(int end) {
Element root = doc.getDefaultRootElement();
int line = root.getElementIndex(end);
Element elem = root.getElement(line);
int start = elem.getStartOffset();
int lastCharOffs = -1;
try {
while (line>=0) {
doc.getText(start, end-start, s);
for (int i=s.offset+s.count-1; i>=s.offset; i--) {
char ch = s.array[i];
if (ch=='<') {
return lastCharOffs;
}
else if (Character.isLetterOrDigit(ch)) {
lastCharOffs = start + i - s.offset;
}
}
if (--line>=0) {
elem = root.getElement(line);
start = elem.getStartOffset();
end = elem.getEndOffset();
}
}
} catch (BadLocationException ble) {
ble.printStackTrace();
}
return -1;
}
public void setDocumentLocator(Locator l) {
locator = l;
}
public void startElement(String uri, String localName, String qName,
Attributes attributes) {
XmlTreeNode newElem = new XmlTreeNode(qName);
if (attributes.getLength()>0) {
newElem.setMainAttribute(getMainAttribute(attributes));
}
if (locator!=null) {
int line = locator.getLineNumber();
if (line!=-1) {
int offs = doc.getDefaultRootElement().
getElement(line-1).getStartOffset();
int col = locator.getColumnNumber();
if (col!=-1) {
offs += col - 1;
}
// "offs" is now the end of the tag. Find the beginning of it.
offs = getTagStart(offs);
try {
newElem.setStartOffset(doc.createPosition(offs));
int endOffs = offs + qName.length();
newElem.setEndOffset(doc.createPosition(endOffs));
} catch (BadLocationException ble) {
ble.printStackTrace();
}
}
}
curElem.add(newElem);
curElem = newElem;
}
public void warning(SAXParseException e) throws SAXException {
doError(e, ParserNotice.WARNING);
}
}
}