org.aspectj.weaver.loadtime.definition.LightXMLParser Maven / Gradle / Ivy
The newest version!
/*******************************************************************************
* 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.io.Reader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
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);
}
}