////////////////////////////////////////////////////////////////////////////////
// checkstyle: Checks Java source code for adherence to a set of rules.
// Copyright (C) 2001-2011 Oliver Burn
//
// This library 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 2.1 of the License, or (at your option) any later version.
//
// This library 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 this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
////////////////////////////////////////////////////////////////////////////////
package com.puppycrawl.tools.checkstyle;
import com.puppycrawl.tools.checkstyle.api.DetailAST;
import com.puppycrawl.tools.checkstyle.api.XmlTokenTypes;
import java.io.File;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.LocatorImpl;
/**
* XML parser used to build an AST tree of a document.
*
* @author Yoann Ciabaud<y.ciabaud@gmail.com>
*/
public class XmlContentHandler implements ContentHandler {
/** Stores the location of the parser in the file. */
private Locator locator;
/** Root of the AST tree. */
private DetailAST root;
/** Pointer on the last parsed node. */
private DetailAST currentNode;
/** Document beeing parsed. */
private File file;
/** logger for debug purpose. */
private static final Log LOG =
LogFactory.getLog("com.puppycrawl.tools.checkstyle.XmlContentHandler");
/**
* Default constructor.
*/
public XmlContentHandler(File file) {
super();
locator = new LocatorImpl();
root = null;
this.file = file;
}
/** {@inheritDoc} */
@Override
public void setDocumentLocator(Locator value) {
locator = value;
}
/** {@inheritDoc} */
@Override
public void startDocument() throws SAXException {
LOG.debug("Starting XML parsing");
// XmlToken
XmlToken token = new XmlToken();
token.setLine(locator.getLineNumber());
token.setColumn(locator.getColumnNumber());
token.setType(XmlTokenTypes.DOCUMENT);
token.setText(file.getName());
// Node
root = new DetailAST();
root.initialize(token);
currentNode = root;
// Path = package
token = new XmlToken();
token.setLine(locator.getLineNumber());
token.setColumn(locator.getColumnNumber());
token.setType(XmlTokenTypes.PATH);
token.setText(file.getParent());
DetailAST path = new DetailAST();
path.initialize(token);
root.addChild(path);
DetailAST pathIdent = new DetailAST();
token = new XmlToken();
token.setLine(locator.getLineNumber());
token.setColumn(locator.getColumnNumber());
token.setType(XmlTokenTypes.IDENT);
token.setText(file.getParent());
pathIdent.initialize(token);
path.addChild(pathIdent);
// Fake child to match Java grammar structure
pathIdent = new DetailAST();
pathIdent.initialize(token);
path.addChild(pathIdent);
// Name = Type
token = new XmlToken();
token.setLine(locator.getLineNumber());
token.setColumn(locator.getColumnNumber());
token.setType(XmlTokenTypes.IDENT);
token.setText(file.getName());
DetailAST name = new DetailAST();
name.initialize(token);
root.addChild(name);
DetailAST nameIdent = new DetailAST();
token = new XmlToken();
token.setLine(locator.getLineNumber());
token.setColumn(locator.getColumnNumber());
token.setType(XmlTokenTypes.IDENT);
token.setText(file.getName());
nameIdent.initialize(token);
name.addChild(nameIdent);
}
/** {@inheritDoc} */
public void endDocument() throws SAXException {
LOG.debug("Ended parsing");
}
/** {@inheritDoc} */
public void startPrefixMapping(String prefix, String URI) throws SAXException {
LOG.debug("Prefix mapping : " + URI + ", chosen prefix : " + prefix);
// XmlToken
XmlToken token = new XmlToken();
token.setLine(locator.getLineNumber());
token.setColumn(locator.getColumnNumber());
token.setText(prefix);
token.setType(XmlTokenTypes.PREFIX_MAPPING);
// Node
DetailAST child = new DetailAST();
child.initialize(token);
// Prefix
token = new XmlToken();
token.setLine(locator.getLineNumber());
token.setColumn(locator.getColumnNumber());
token.setText(prefix);
token.setType(XmlTokenTypes.IDENT);
DetailAST prefixAST = new DetailAST();
prefixAST.initialize(token);
prefixAST.setText(token.getText());
child.addChild(prefixAST);
// URI
token = new XmlToken();
token.setLine(locator.getLineNumber());
token.setColumn(locator.getColumnNumber());
token.setText(URI);
token.setType(XmlTokenTypes.STRING_LITERAL);
DetailAST uri = new DetailAST();
uri.initialize(token);
child.addChild(uri);
// Parent
currentNode.addChild(child);
}
/** {@inheritDoc} */
public void endPrefixMapping(String prefix) throws SAXException {
LOG.debug("End of prefix mapping : " + prefix);
}
/** {@inheritDoc} */
public void startElement(String nameSpaceURI, String localName,
String rawName, Attributes attributs) throws SAXException {
LOG.debug("Opening element : " + localName);
int col = locator.getColumnNumber();
// XmlToken
XmlToken token = new XmlToken();
token.setLine(locator.getLineNumber());
token.setColumn(locator.getColumnNumber());
token.setText(rawName);
token.setType(XmlTokenTypes.ELEMENT);
// Node
DetailAST child = new DetailAST();
child.initialize(token);
// Parent
currentNode.addChild(child);
// Ident
DetailAST ident = new DetailAST();
token = new XmlToken();
token.setLine(locator.getLineNumber());
col++;
token.setColumn(col);
token.setText(localName);
token.setType(XmlTokenTypes.IDENT);
ident.initialize(token);
child.addChild(ident);
// Attributes
LOG.debug(" Attributes : ");
DetailAST attrs = new DetailAST();
token = new XmlToken();
token.setLine(locator.getLineNumber());
col += rawName.length() + 1;
token.setColumn( col );
token.setText(rawName);
token.setType(XmlTokenTypes.ATTRIBUTES);
attrs.initialize(token);
child.addChild(attrs);
for (int index = 0; index < attributs.getLength(); index++) {
// Attribut
DetailAST attr = new DetailAST();
token = new XmlToken();
token.setLine(locator.getLineNumber());
token.setColumn( index );
token.setText(rawName);
token.setType(XmlTokenTypes.ATTRIBUTE);
attr.initialize(token);
attrs.addChild(attr);
LOG.debug(" - " + attributs.getLocalName(index) + " = "
+ attributs.getValue(index));
// Ident
ident = new DetailAST();
token = new XmlToken();
token.setLine(locator.getLineNumber());
token.setColumn( index );
token.setText(attributs.getLocalName(index));
token.setType(XmlTokenTypes.IDENT);
ident.initialize(token);
attr.addChild(ident);
// Value
DetailAST value = new DetailAST();
token = new XmlToken();
token.setLine(locator.getLineNumber());
token.setColumn( index + attributs.getLocalName(index).length() + 1 );
token.setText(attributs.getValue(index));
token.setType(XmlTokenTypes.STRING_LITERAL);
value.initialize(token);
attr.addChild(value);
// Go forward
col += attributs.getLocalName(index).length()
+ attributs.getValue(index).length() + 2;
}
// This node is now he current node
currentNode = child;
}
/** {@inheritDoc} */
public void endElement(String nameSpaceURI, String localName,
String rawName) throws SAXException {
LOG.debug("End of the element : " + localName);
// Go to the parent
currentNode = currentNode.getParent();
}
/** {@inheritDoc} */
public void characters(char[] ch, int start, int end) throws SAXException {
String value = new String(ch, start, end);
LOG.debug("#PCDATA : " + value);
// XmlToken
XmlToken token = new XmlToken();
token.setLine(locator.getLineNumber());
token.setColumn(locator.getColumnNumber() + start);
token.setText(value);
token.setType(XmlTokenTypes.PCDATA);
// Node
DetailAST child = new DetailAST();
child.initialize(token);
child.setText(token.getText());
// Parent
currentNode.addChild(child);
}
/** {@inheritDoc} */
public void ignorableWhitespace(char[] ch, int start, int end) throws SAXException {
String value = new String(ch, start, end);
LOG.debug("ignorable whitespace : ..." + value + "...");
// XmlToken
XmlToken token = new XmlToken();
token.setLine(locator.getLineNumber());
token.setColumn(locator.getColumnNumber() + start);
token.setText(value);
token.setType(XmlTokenTypes.WHITE_SPACE);
// Node
DetailAST child = new DetailAST();
child.initialize(token);
child.setText(token.getText());
// Parent
currentNode.addChild(child);
}
/** {@inheritDoc} */
public void processingInstruction(String target, String data) throws SAXException {
LOG.debug("Processing instruction : " + target);
LOG.debug(" Args : " + data);
// XmlToken
XmlToken token = new XmlToken();
token.setLine(locator.getLineNumber());
token.setColumn(locator.getColumnNumber());
token.setText(target);
token.setType(XmlTokenTypes.PROCESSING_INSTRUCTION);
// Node
DetailAST child = new DetailAST();
child.initialize(token);
child.setText(token.getText());
// Target
token = new XmlToken();
token.setLine(locator.getLineNumber());
token.setColumn(locator.getColumnNumber());
token.setText(target);
token.setType(XmlTokenTypes.PROCESSING_TARGET);
DetailAST targetAST = new DetailAST();
targetAST.initialize(token);
targetAST.setText(token.getText());
child.addChild(targetAST);
// Data
token = new XmlToken();
token.setLine(locator.getLineNumber());
token.setColumn(locator.getColumnNumber());
token.setText(data);
token.setType(XmlTokenTypes.PROCESSING_DATA);
DetailAST dataAST = new DetailAST();
dataAST.initialize(token);
dataAST.setText(token.getText());
child.addChild(dataAST);
// Parent
currentNode.addChild(child);
}
/** {@inheritDoc} */
public void skippedEntity(String text) throws SAXException {
// XmlToken
XmlToken token = new XmlToken();
token.setLine(locator.getLineNumber());
token.setColumn(locator.getColumnNumber() );
token.setText(text);
token.setType(XmlTokenTypes.SKIPPED_ENTITY);
// Node
DetailAST child = new DetailAST();
child.initialize(token);
child.setText(token.getText());
// Parent
currentNode.addChild(child);
}
/** {@inheritDoc} */
public DetailAST getAST() {
return root;
}
}