/**
* Copyright 2008 - 2011
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*
* @project loon
* @author cping
* @email:javachenpeng@yahoo.com
* @version 0.1
*/
package loon.utils.xml;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Stack;
import loon.LSystem;
import loon.core.LRelease;
import loon.core.resource.Resources;
// 这是一个简易的XML解析器,自0.3.2起开始支持(为了多平台移植方便……)
public class XMLParser implements LRelease {
static final int OPEN_TAG = 0;
static final int CLOSE_TAG = 1;
static final int OPEN_CLOSE_TAG = 2;
private Stack<XMLElement> stack = new Stack<XMLElement>();
private XMLElement topElement;
private XMLElement rootElement;
private StringBuffer header = new StringBuffer(1024);
private void pushElement(XMLElement root, int idx, XMLListener l) {
if (this.topElement == null) {
this.rootElement = root;
} else {
this.topElement.addContents(root);
}
this.stack.push(root);
this.topElement = root;
if (l != null) {
l.addElement(idx, root);
}
}
private void popElement(int idx, XMLListener l) {
if (l != null) {
l.endElement(idx, this.topElement);
}
this.stack.pop();
if (stack.size() > 0) {
this.topElement = this.stack.peek();
} else {
this.topElement = null;
}
}
private void newElement(String context, XMLListener l, int index) {
String o = "";
int i;
String str1;
if (context.endsWith("/>")) {
i = 2;
str1 = context.substring(1, context.length() - 2);
} else if (context.startsWith("</")) {
i = 1;
str1 = context.substring(2, context.length() - 1);
} else {
i = 0;
str1 = context.substring(1, context.length() - 1);
}
try {
if (str1.indexOf(' ') < 0) {
o = str1;
switch (i) {
case OPEN_TAG:
pushElement(new XMLElement(o), index, l);
break;
case CLOSE_TAG:
if (this.topElement.getName().equals(o)) {
popElement(index, l);
} else {
throw new RuntimeException("Expected close of '"
+ this.topElement.getName() + "' instead of "
+ context);
}
break;
case OPEN_CLOSE_TAG:
pushElement(new XMLElement(o), index, l);
popElement(index, l);
break;
}
} else {
XMLElement el = null;
o = str1.substring(0, str1.indexOf(' '));
switch (i) {
case OPEN_TAG:
el = new XMLElement(o);
pushElement(el, index, l);
break;
case CLOSE_TAG:
throw new RuntimeException("Syntax Error: " + context);
case OPEN_CLOSE_TAG:
el = new XMLElement(o);
pushElement(el, index, l);
popElement(index, l);
break;
}
String str2 = str1.substring(str1.indexOf(' ') + 1);
int start = 0;
int end = 0;
StringBuffer sbr1 = new StringBuffer(128);
StringBuffer sbr2 = new StringBuffer(32);
for (int m = 0; m < str2.length(); m++) {
switch (str2.charAt(m)) {
case '"':
start = start != 0 ? 0 : 1;
break;
case ' ':
if ((end == 1) && (start == 1)) {
sbr1.append(str2.charAt(m));
} else if (sbr2.length() > 0) {
String key = sbr2.toString();
String value = sbr1.toString();
if (key.length() > 0) {
XMLAttribute a = el.addAttribute(key, value);
a.element = el;
if (l != null) {
l.addAttribute(index, a);
}
}
end = 0;
sbr1.delete(0, sbr1.length());
sbr2.delete(0, sbr2.length());
}
break;
case '=':
if (start == 0) {
end = 1;
}
break;
case '!':
case '#':
case '$':
case '%':
case '&':
case '\'':
case '(':
case ')':
case '*':
case '+':
case ',':
case '-':
case '.':
case '/':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case ':':
case ';':
case '<':
default:
if (end != 0) {
sbr1.append(str2.charAt(m));
} else {
sbr2.append(str2.charAt(m));
}
}
}
if (sbr1.length() > 0) {
String key = sbr2.toString();
String value = sbr1.toString();
XMLAttribute a = el.addAttribute(key, value);
a.element = el;
if (l != null) {
l.addAttribute(index, a);
}
}
}
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("Cannot parse element '" + context
+ "' - (" + e + ")");
}
}
private void newData(String data, XMLListener l, int index) {
if (this.topElement != null) {
XMLData xdata = new XMLData(data);
this.topElement.addContents(xdata);
if (l != null) {
l.addData(index, xdata);
}
} else if (this.rootElement == null) {
this.header.append(data);
}
}
private void newComment(String comment, XMLListener l, int index) {
if (this.topElement != null) {
XMLComment c = new XMLComment(comment.substring(4,
comment.length() - 3));
this.topElement.addContents(c);
if (l != null) {
l.addComment(index, c);
}
} else if (this.rootElement == null) {
this.header.append(comment);
}
}
private void newProcessing(String p, XMLListener l, int index) {
if (this.topElement != null) {
XMLProcessing xp = new XMLProcessing(p.substring(2, p.length() - 2));
this.topElement.addContents(xp);
if (l != null) {
l.addHeader(index, xp);
}
} else if (this.rootElement == null) {
this.header.append(p);
}
}
private XMLDocument parseText(String text, XMLListener l) {
int count = 0;
for (XMLTokenizer tokenizer = new XMLTokenizer(text); tokenizer
.hasMoreElements();) {
String str = tokenizer.nextElement();
if ((str.startsWith("<?")) && (str.endsWith("?>"))) {
newProcessing(str, l, count);
} else if ((str.startsWith("<!--")) && (str.endsWith("-->"))) {
newComment(str, l, count);
} else if (str.charAt(0) == '<') {
newElement(str, l, count);
} else {
newData(str, l, count);
}
count++;
}
return new XMLDocument(this.header.toString(), this.rootElement);
}
public static XMLDocument parse(String file) {
return parse(file, null);
}
public static XMLDocument parse(String file, XMLListener l) {
try {
return parse(Resources.openResource(file), l);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static XMLDocument parse(InputStream in) {
return parse(in, null);
}
public static XMLDocument parse(InputStream in, XMLListener l) {
StringBuffer sbr = new StringBuffer(10000);
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(
in, LSystem.encoding));
while (reader.ready()) {
sbr.append(reader.readLine());
sbr.append('\n');
}
reader.close();
} catch (Exception ex) {
ex.printStackTrace();
}
return new XMLParser().parseText(sbr.toString(), l);
}
public void dispose() {
if (stack != null) {
stack.clear();
stack = null;
}
if (topElement != null) {
topElement.dispose();
topElement = null;
}
if (rootElement != null) {
rootElement.dispose();
rootElement = null;
}
}
}