From 4e6ff7fec6bf7f16bfc36116b8fe01656962e821 Mon Sep 17 00:00:00 2001 From: aclement Date: Tue, 1 Mar 2011 20:18:34 +0000 Subject: [PATCH] 329126: own our XML parser! --- .../loadtime/definition/DocumentParser.java | 81 +-- .../loadtime/definition/LightXMLParser.java | 481 ++++++++++++++++++ .../loadtime/definition/SimpleAOPParser.java | 199 ++++++++ 3 files changed, 731 insertions(+), 30 deletions(-) create mode 100644 weaver/src/org/aspectj/weaver/loadtime/definition/LightXMLParser.java create mode 100644 weaver/src/org/aspectj/weaver/loadtime/definition/SimpleAOPParser.java diff --git a/weaver/src/org/aspectj/weaver/loadtime/definition/DocumentParser.java b/weaver/src/org/aspectj/weaver/loadtime/definition/DocumentParser.java index 926448bc9..958bd1a3d 100644 --- a/weaver/src/org/aspectj/weaver/loadtime/definition/DocumentParser.java +++ b/weaver/src/org/aspectj/weaver/loadtime/definition/DocumentParser.java @@ -12,6 +12,7 @@ *******************************************************************************/ package org.aspectj.weaver.loadtime.definition; +import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.Hashtable; @@ -77,15 +78,25 @@ public class DocumentParser extends DefaultHandler { private static Hashtable parsedFiles = new Hashtable(); private static final boolean CACHE; + private static final boolean LIGHTPARSER; + static { boolean value = false; try { - value = System.getProperty("org.aspectj.weaver.loadtime.configuration.cache", "false").equalsIgnoreCase("true"); + value = System.getProperty("org.aspectj.weaver.loadtime.configuration.cache", "true").equalsIgnoreCase("true"); } catch (Throwable t) { t.printStackTrace(); } CACHE = value; + + value = false; + try { + value = System.getProperty("org.aspectj.weaver.loadtime.configuration.lightxmlparser", "false").equalsIgnoreCase("true"); + } catch (Throwable t) { + t.printStackTrace(); + } + LIGHTPARSER = value; } private DocumentParser() { @@ -98,38 +109,19 @@ public class DocumentParser extends DefaultHandler { if (CACHE && parsedFiles.containsKey(url.toString())) { return parsedFiles.get(url.toString()); } - - DocumentParser parser = new DocumentParser(); - - XMLReader xmlReader = getXMLReader(); - xmlReader.setContentHandler(parser); - xmlReader.setErrorHandler(parser); - - try { - xmlReader.setFeature("http://xml.org/sax/features/validation", false); - } catch (SAXException e) { - // fine, the parser don't do validation - } - try { - xmlReader.setFeature("http://xml.org/sax/features/external-general-entities", false); - } catch (SAXException e) { - // fine, the parser don't do validation - } - try { - xmlReader.setFeature("http://xml.org/sax/features/external-parameter-entities", false); - } catch (SAXException e) { - // fine, the parser don't do validation + Definition def=null; + + if(LIGHTPARSER){ + def = SimpleAOPParser.parse(url); + }else{ + def = saxParsing(url); } - - xmlReader.setEntityResolver(parser); - in = url.openStream(); - xmlReader.parse(new InputSource(in)); - - if (CACHE && parser.m_definition.getAspectClassNames().size() > 0) { - parsedFiles.put(url.toString(), parser.m_definition); + + if (CACHE && def.getAspectClassNames().size() > 0) { + parsedFiles.put(url.toString(), def); } - return parser.m_definition; + return def; } finally { try { in.close(); @@ -139,6 +131,35 @@ public class DocumentParser extends DefaultHandler { } } + private static Definition saxParsing(URL url) throws SAXException, ParserConfigurationException, IOException { + DocumentParser parser = new DocumentParser(); + + XMLReader xmlReader = getXMLReader(); + xmlReader.setContentHandler(parser); + xmlReader.setErrorHandler(parser); + + try { + xmlReader.setFeature("http://xml.org/sax/features/validation", false); + } catch (SAXException e) { + // fine, the parser don't do validation + } + try { + xmlReader.setFeature("http://xml.org/sax/features/external-general-entities", false); + } catch (SAXException e) { + // fine, the parser don't do validation + } + try { + xmlReader.setFeature("http://xml.org/sax/features/external-parameter-entities", false); + } catch (SAXException e) { + // fine, the parser don't do validation + } + + xmlReader.setEntityResolver(parser); + InputStream in = url.openStream(); + xmlReader.parse(new InputSource(in)); + return parser.m_definition; + } + private static XMLReader getXMLReader() throws SAXException, ParserConfigurationException { XMLReader xmlReader = null; /* Try this first for Java 5 */ diff --git a/weaver/src/org/aspectj/weaver/loadtime/definition/LightXMLParser.java b/weaver/src/org/aspectj/weaver/loadtime/definition/LightXMLParser.java new file mode 100644 index 000000000..d93a6bd62 --- /dev/null +++ b/weaver/src/org/aspectj/weaver/loadtime/definition/LightXMLParser.java @@ -0,0 +1,481 @@ +/******************************************************************************* + * Copyright (c) 2011 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Abraham Nevado - Lucierna initial implementation + *******************************************************************************/ +package org.aspectj.weaver.loadtime.definition; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.io.Reader; + +public class LightXMLParser { + + private final static char NULL_CHAR = '\0'; + private Map attributes; + private ArrayList children; + private String name; + private char pushedBackChar; + private Reader reader; + + private static Map entities = new HashMap(); + + + static{ + entities.put("amp", new char[] { '&' }); + entities.put("quot", new char[] { '"' }); + entities.put("apos", new char[] { '\'' }); + entities.put("lt", new char[] { '<' }); + entities.put("gt", new char[] { '>' }); + } + + public LightXMLParser() { + this.name = null; + this.attributes = new HashMap(); + this.children = new ArrayList(); + } + + public ArrayList getChildrens() { + return this.children; + } + + public String getName() { + return this.name; + } + + public void parseFromReader(Reader reader) throws Exception { + this.pushedBackChar = NULL_CHAR; + this.attributes = new HashMap(); + this.name = null; + this.children = new ArrayList(); + this.reader = reader; + + while (true) { + // Skips whiteSpaces, blanks, \r\n.. + char c = this.skipBlanks(); + + // All xml should start by 'z')) && ((c > 'Z') || (c < 'A')) + && ((c > '9') || (c < '0')) && (c != '_') && (c != '-') + && (c != '.') && (c != ':')) { + this.pushBackChar(c); + return; + } + result.append(c); + } + } + + private void getString(StringBuffer string) throws Exception { + char delimiter = this.getNextChar(); + if ((delimiter != '\'') && (delimiter != '"')) { + throw new Exception("Parsing error. Expected ' or \" but got: " + + delimiter); + + } + + + while (true) { + char c = this.getNextChar(); + if (c == delimiter) { + return; + } else if (c == '&') { + this.mapEntity(string); + } else { + string.append(c); + } + } + } + + private void getPCData(StringBuffer data) throws Exception { + while (true) { + char c = this.getNextChar(); + if (c == '<') { + c = this.getNextChar(); + if (c == '!') { + this.checkCDATA(data); + } else { + this.pushBackChar(c); + return; + } + } else { + data.append(c); + } + } + } + + private boolean checkCDATA(StringBuffer buf) throws Exception { + char c = this.getNextChar(); + if (c != '[') { + this.pushBackChar(c); + this.skipCommentOrXmlTag(0); + return false; + } else if (!this.checkLiteral("CDATA[")) { + this.skipCommentOrXmlTag(1); // one [ has already been read + return false; + } else { + int delimiterCharsSkipped = 0; + while (delimiterCharsSkipped < 3) { + c = this.getNextChar(); + switch (c) { + case ']': + if (delimiterCharsSkipped < 2) { + delimiterCharsSkipped++; + } else { + buf.append(']'); + buf.append(']'); + delimiterCharsSkipped = 0; + } + break; + case '>': + if (delimiterCharsSkipped < 2) { + for (int i = 0; i < delimiterCharsSkipped; i++) { + buf.append(']'); + } + delimiterCharsSkipped = 0; + buf.append('>'); + } else { + delimiterCharsSkipped = 3; + } + break; + default: + for (int i = 0; i < delimiterCharsSkipped; i++) { + buf.append(']'); + } + buf.append(c); + delimiterCharsSkipped = 0; + } + } + return true; + } + } + + private void skipCommentOrXmlTag(int bracketLevel) throws Exception { + char delim = NULL_CHAR; + int level = 1; + char c; + if (bracketLevel == 0) { + c = this.getNextChar(); + if (c == '-') { + c = this.getNextChar(); + if (c == ']') { + bracketLevel--; + } else if (c == '[') { + bracketLevel++; + } else if (c == '-') { + this.skipComment(); + return; + } + } else if (c == '[') { + bracketLevel++; + } + } + while (level > 0) { + c = this.getNextChar(); + if (delim == NULL_CHAR) { + if ((c == '"') || (c == '\'')) { + delim = c; + } else if (bracketLevel <= 0) { + if (c == '<') { + level++; + } else if (c == '>') { + level--; + } + } + if (c == '[') { + bracketLevel++; + } else if (c == ']') { + bracketLevel--; + } + } else { + if (c == delim) { + delim = NULL_CHAR; + } + } + } + } + + private void parseNode(LightXMLParser elt) throws Exception { + // Now we are in a new node element. Get its name + StringBuffer buf = new StringBuffer(); + this.getNodeName(buf); + String name = buf.toString(); + elt.setName(name); + + char c = this.skipBlanks(); + while ((c != '>') && (c != '/')) { + // Get attributes + emptyBuf(buf); + this.pushBackChar(c); + this.getNodeName(buf); + String key = buf.toString(); + c = this.skipBlanks(); + if (c != '=') { + throw new Exception("Parsing error. Expected = but got: " + c); + } + // Go up to " character and push it back + this.pushBackChar(this.skipBlanks()); + + emptyBuf(buf); + this.getString(buf); + + elt.setAttribute(key, buf); + + // Skip blanks + c = this.skipBlanks(); + } + if (c == '/') { + c = this.getNextChar(); + if (c != '>') { + throw new Exception("Parsing error. Expected > but got: " + c); + } + return; + } + + // Now see if we got content, or CDATA, if content get it: it is free... + emptyBuf(buf); + c = this.getWhitespaces(buf); + if (c != '<') { + // It is PCDATA + this.pushBackChar(c); + this.getPCData(buf); + } else { + // It is content: get it, or CDATA. + while (true) { + c = this.getNextChar(); + if (c == '!') { + if (this.checkCDATA(buf)) { + this.getPCData(buf); + break; + } else { + c = this.getWhitespaces(buf); + if (c != '<') { + this.pushBackChar(c); + this.getPCData(buf); + break; + } + } + } else { + if (c != '/') { + emptyBuf(buf); + } + if (c == '/') { + this.pushBackChar(c); + } + break; + } + } + } + if (buf.length() == 0) { + // It is a comment + while (c != '/') { + if (c == '!') { + for (int i = 0; i < 2; i++) { + c = this.getNextChar(); + if (c != '-') { + throw new Exception( + "Parsing error. Expected element or comment"); + } + } + this.skipComment(); + } else { + // it is a new node + this.pushBackChar(c); + LightXMLParser child = this.createAnotherElement(); + this.parseNode(child); + elt.addChild(child); + } + c = this.skipBlanks(); + if (c != '<') { + throw new Exception("Parsing error. Expected <, but got: " + + c); + } + c = this.getNextChar(); + } + this.pushBackChar(c); + } // Here content could be grabbed + + c = this.getNextChar(); + if (c != '/') { + throw new Exception("Parsing error. Expected /, but got: " + c); + } + this.pushBackChar(this.skipBlanks()); + if (!this.checkLiteral(name)) { + throw new Exception("Parsing error. Expected " + name); + } + if (this.skipBlanks() != '>') { + throw new Exception("Parsing error. Expected >, but got: " + c); + } + } + + private void skipComment() throws Exception { + int dashes = 2; + while (dashes > 0) { + char ch = this.getNextChar(); + if (ch == '-') { + dashes -= 1; + } else { + dashes = 2; + } + } + + char nextChar = this.getNextChar(); + if (nextChar != '>') { + throw new Exception("Parsing error. Expected > but got: " + + nextChar); + } + } + + private boolean checkLiteral(String literal) throws Exception { + int length = literal.length(); + for (int i = 0; i < length; i++) { + if (this.getNextChar() != literal.charAt(i)) { + return false; + } + } + return true; + } + + private char getNextChar() throws Exception { + if (this.pushedBackChar != NULL_CHAR) { + char c = this.pushedBackChar; + this.pushedBackChar = NULL_CHAR; + return c; + } else { + int i = this.reader.read(); + if (i < 0) { + throw new Exception("Parsing error. Unexpected end of data"); + } else { + return (char) i; + } + } + } + + + private void mapEntity(StringBuffer buf) + throws Exception + { + char c = this.NULL_CHAR; + StringBuffer keyBuf = new StringBuffer(); + while (true) { + c = this.getNextChar(); + if (c == ';') { + break; + } + keyBuf.append(c); + } + String key = keyBuf.toString(); + if (key.charAt(0) == '#') { + try { + if (key.charAt(1) == 'x') { + c = (char) Integer.parseInt(key.substring(2), 16); + } else { + c = (char) Integer.parseInt(key.substring(1), 10); + } + } catch (NumberFormatException e) { + throw new Exception("Unknown entity: " + key); + } + buf.append(c); + } else { + char[] value = (char[]) entities.get(key); + if (value == null) { + throw new Exception("Unknown entity: " + key); + } + buf.append(value); + } + } + private void pushBackChar(char c) { + this.pushedBackChar = c; + } + + private void addChild(LightXMLParser child) { + this.children.add(child); + } + + private void setAttribute(String name, Object value) { + this.attributes.put(name, value.toString()); + } + + public Map getAttributes() { + return this.attributes; + } + + private LightXMLParser createAnotherElement() { + return new LightXMLParser(); + } + + private void setName(String name) { + this.name = name; + } + + private void emptyBuf(StringBuffer buf) { + buf.setLength(0); + } + +} diff --git a/weaver/src/org/aspectj/weaver/loadtime/definition/SimpleAOPParser.java b/weaver/src/org/aspectj/weaver/loadtime/definition/SimpleAOPParser.java new file mode 100644 index 000000000..88b52e9a2 --- /dev/null +++ b/weaver/src/org/aspectj/weaver/loadtime/definition/SimpleAOPParser.java @@ -0,0 +1,199 @@ +/******************************************************************************* + * Copyright (c) 2011 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Abraham Nevado - Lucierna initial implementation + * Just a slight variation of current DocumentParser.java from Alexandre Vasseur. + *******************************************************************************/ +package org.aspectj.weaver.loadtime.definition; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URL; +import java.util.ArrayList; +import java.util.Map; + +import org.aspectj.util.LangUtil; + +/** + * This class has been created to avoid deadlocks when instrumenting SAXParser. + * So it is used as a wrapper for the ligthweigh XML parser LightXMLParser. + * + * @author A. Nevado + */ +public class SimpleAOPParser { + + private final static String ASPECTJ_ELEMENT = "aspectj"; + private final static String WEAVER_ELEMENT = "weaver"; + private final static String DUMP_ELEMENT = "dump"; + private final static String DUMP_BEFOREANDAFTER_ATTRIBUTE = "beforeandafter"; + private final static String DUMP_PERCLASSLOADERDIR_ATTRIBUTE = "perclassloaderdumpdir"; + private final static String INCLUDE_ELEMENT = "include"; + private final static String EXCLUDE_ELEMENT = "exclude"; + private final static String OPTIONS_ATTRIBUTE = "options"; + private final static String ASPECTS_ELEMENT = "aspects"; + private final static String ASPECT_ELEMENT = "aspect"; + private final static String CONCRETE_ASPECT_ELEMENT = "concrete-aspect"; + private final static String NAME_ATTRIBUTE = "name"; + private final static String SCOPE_ATTRIBUTE = "scope"; + private final static String REQUIRES_ATTRIBUTE = "requires"; + private final static String EXTEND_ATTRIBUTE = "extends"; + private final static String PRECEDENCE_ATTRIBUTE = "precedence"; + private final static String PERCLAUSE_ATTRIBUTE = "perclause"; + private final static String POINTCUT_ELEMENT = "pointcut"; + private final static String WITHIN_ATTRIBUTE = "within"; + private final static String EXPRESSION_ATTRIBUTE = "expression"; + private final Definition m_definition; + private boolean m_inAspectJ; + private boolean m_inWeaver; + private boolean m_inAspects; + + private Definition.ConcreteAspect m_lastConcreteAspect; + + private SimpleAOPParser() { + m_definition = new Definition(); + } + + public static Definition parse(final URL url) throws Exception { + // FileReader freader = new FileReader("/tmp/aop.xml"); + InputStream in = url.openStream(); + LightXMLParser xml = new LightXMLParser(); + xml.parseFromReader(new InputStreamReader(in)); + SimpleAOPParser sap = new SimpleAOPParser(); + traverse(sap, xml); + return sap.m_definition; + } + + private void startElement(String qName, Map attrMap) throws Exception { + if (ASPECT_ELEMENT.equals(qName)) { + String name = (String) attrMap.get(NAME_ATTRIBUTE); + String scopePattern = replaceXmlAnd((String) attrMap + .get(SCOPE_ATTRIBUTE)); + String requiredType = (String) attrMap.get(REQUIRES_ATTRIBUTE); + if (!isNull(name)) { + m_definition.getAspectClassNames().add(name); + if (scopePattern != null) { + m_definition.addScopedAspect(name, scopePattern); + } + if (requiredType != null) { + m_definition.setAspectRequires(name, requiredType); + } + } + } else if (WEAVER_ELEMENT.equals(qName)) { + String options = (String) attrMap.get(OPTIONS_ATTRIBUTE); + if (!isNull(options)) { + m_definition.appendWeaverOptions(options); + } + m_inWeaver = true; + } else if (CONCRETE_ASPECT_ELEMENT.equals(qName)) { + String name = (String) attrMap.get(NAME_ATTRIBUTE); + String extend = (String) attrMap.get(EXTEND_ATTRIBUTE); + String precedence = (String) attrMap.get(PRECEDENCE_ATTRIBUTE); + String perclause = (String) attrMap.get(PERCLAUSE_ATTRIBUTE); + if (!isNull(name)) { + m_lastConcreteAspect = new Definition.ConcreteAspect(name, + extend, precedence, perclause); + m_definition.getConcreteAspects().add(m_lastConcreteAspect); + } + } else if (POINTCUT_ELEMENT.equals(qName) + && m_lastConcreteAspect != null) { + String name = (String) attrMap.get(NAME_ATTRIBUTE); + String expression = (String) attrMap.get(EXPRESSION_ATTRIBUTE); + if (!isNull(name) && !isNull(expression)) { + m_lastConcreteAspect.pointcuts.add(new Definition.Pointcut( + name, replaceXmlAnd(expression))); + } + } else if (ASPECTJ_ELEMENT.equals(qName)) { + if (m_inAspectJ) { + throw new Exception("Found nested element"); + } + m_inAspectJ = true; + } else if (ASPECTS_ELEMENT.equals(qName)) { + m_inAspects = true; + } else if (INCLUDE_ELEMENT.equals(qName) && m_inWeaver) { + String typePattern = getWithinAttribute(attrMap); + if (!isNull(typePattern)) { + m_definition.getIncludePatterns().add(typePattern); + } + } else if (EXCLUDE_ELEMENT.equals(qName) && m_inWeaver) { + String typePattern = getWithinAttribute(attrMap); + if (!isNull(typePattern)) { + m_definition.getExcludePatterns().add(typePattern); + } + } else if (DUMP_ELEMENT.equals(qName) && m_inWeaver) { + String typePattern = getWithinAttribute(attrMap); + if (!isNull(typePattern)) { + m_definition.getDumpPatterns().add(typePattern); + } + String beforeAndAfter = (String) attrMap + .get(DUMP_BEFOREANDAFTER_ATTRIBUTE); + if (isTrue(beforeAndAfter)) { + m_definition.setDumpBefore(true); + } + String perWeaverDumpDir = (String) attrMap + .get(DUMP_PERCLASSLOADERDIR_ATTRIBUTE); + if (isTrue(perWeaverDumpDir)) { + m_definition.setCreateDumpDirPerClassloader(true); + } + } else if (EXCLUDE_ELEMENT.equals(qName) && m_inAspects) { + String typePattern = getWithinAttribute(attrMap); + if (!isNull(typePattern)) { + m_definition.getAspectExcludePatterns().add(typePattern); + } + } else if (INCLUDE_ELEMENT.equals(qName) && m_inAspects) { + String typePattern = getWithinAttribute(attrMap); + if (!isNull(typePattern)) { + m_definition.getAspectIncludePatterns().add(typePattern); + } + } else { + throw new Exception( + "Unknown element while parsing element: " + qName); + } + } + + private void endElement(String qName) throws Exception { + if (CONCRETE_ASPECT_ELEMENT.equals(qName)) { + m_lastConcreteAspect = null; + } else if (ASPECTJ_ELEMENT.equals(qName)) { + m_inAspectJ = false; + } else if (WEAVER_ELEMENT.equals(qName)) { + m_inWeaver = false; + } else if (ASPECTS_ELEMENT.equals(qName)) { + m_inAspects = false; + } + } + + private String getWithinAttribute(Map attributes) { + return replaceXmlAnd((String) attributes.get(WITHIN_ATTRIBUTE)); + } + + private static String replaceXmlAnd(String expression) { + // TODO AV do we need to handle "..)AND" or "AND(.." ? + return LangUtil.replace(expression, " AND ", " && "); + } + + private boolean isNull(String s) { + return (s == null || s.length() <= 0); + } + + private boolean isTrue(String s) { + return (s != null && s.equals("true")); + } + + private static void traverse(SimpleAOPParser sap, LightXMLParser xml) + throws Exception { + sap.startElement(xml.getName(), xml.getAttributes()); + ArrayList childrens = xml.getChildrens(); + for (int i = 0; i < childrens.size(); i++) { + LightXMLParser child = (LightXMLParser) childrens.get(i); + traverse(sap, child); + } + sap.endElement(xml.getName()); + + } +} -- 2.39.5