package com.ibm.nmon.parser;
import org.slf4j.Logger;
import com.ibm.nmon.parser.util.XMLParserHelper;
import com.ibm.nmon.util.DataHelper;
import java.io.IOException;
import java.io.LineNumberReader;
import java.io.InputStream;
import java.util.Map;
/**
* Simple non-validating XML parser.
*/
public abstract class BasicXMLParser {
protected final Logger logger = org.slf4j.LoggerFactory.getLogger(getClass());
private LineNumberReader in;
protected boolean skip = false;
protected final void parse(String filename) throws IOException {
in = new LineNumberReader(new java.io.FileReader(filename));
parse();
}
protected final void parse(InputStream stream) throws IOException {
in = new LineNumberReader(new java.io.InputStreamReader(stream));
parse();
}
private void parse() throws IOException {
try {
String line = null;
// attempt to search for <?xml> first - abort if not present
// only search the first few lines
boolean valid = false;
for (int i = 0; i < 10; i++) {
line = in.readLine();
if (line == null) {
throw new IllegalArgumentException("file ended before " + "<?xml> tag");
}
else if (line.contains("<?xml")) {
valid = true;
break;
}
}
if (!valid) {
throw new IllegalArgumentException("file contains no " + "<?xml> tag" + " in the first 10 lines ");
}
line = in.readLine();
int searchStart = 0;
while (line != null) {
int elementStart = line.indexOf('<', searchStart) + 1;
// no xml element => try next line
if (elementStart == 0) {
line = in.readLine();
searchStart = 0;
continue;
}
char c = line.charAt(elementStart);
if (c == '!') {
// comment or other xml directive
// lastIndexOf here because the comment can contain other xml elements
// look for the very last > to find the end of the comment
int elementEnd = line.lastIndexOf('>');
// no '>' => append next line and try again
if (elementEnd == -1) {
line += in.readLine();
continue;
}
else if (line.charAt(elementEnd - 1) == '-') {
// comment complete
line = in.readLine();
searchStart = 0;
}
else {
line += in.readLine();
}
}
else {
// no '>' => append next line and try again
int elementEnd = line.indexOf('>', elementStart);
if (elementEnd == -1) {
line += in.readLine();
continue;
}
boolean start = true;
boolean end = false;
int nameStart = elementStart;
// </element...
if (c == '/') {
start = false;
end = true;
++nameStart;
}
if (nameStart == elementEnd) {
logger.warn("ignoring unnamed element at line {}", getLineNumber());
searchStart = elementEnd + 1;
continue;
}
int nameEnd = line.indexOf(' ', nameStart);
// <element... /
if (line.charAt(elementEnd - 1) == '/') {
end = true;
if (nameEnd == -1) {
// <element/> => drop '/' at end of name
nameEnd = elementEnd - 1;
}
}
else {
if (nameEnd == -1) {
nameEnd = elementEnd;
}
}
String name = DataHelper.newString(line.substring(nameStart, nameEnd));
if (start) {
String unparsedAttributes = line.substring(nameEnd, elementEnd);
logger.trace("start element '{}' with attributes '{}'", name, unparsedAttributes);
startElement(name, unparsedAttributes);
}
if (end) {
logger.trace("end element '{}'", name);
endElement(name);
}
// more data on the current line?
if (elementEnd == (line.length() - 1)) {
line = in.readLine();
searchStart = 0;
}
else {
searchStart = elementEnd + 1;
}
}
}
}
finally {
if (in != null) {
try {
in.close();
}
catch (Exception e) {
// ignore
}
in = null;
}
}
}
protected void reset() {
skip = false;
}
protected abstract void startElement(String element, String unparsedAttributes);
protected abstract void endElement(String element);
/**
* Attributes are not parsed unless needed. Subclasses should call this method to turn the
* unparsed attributes string into a Map of strings to values.
*/
protected final Map<String, String> parseAttributes(String unparsedAttributes) {
return XMLParserHelper.parseAttributes(unparsedAttributes);
}
/**
* Only valid during parsing.
*
* @throws NullPointerException if a parse is not currently in progress
*/
protected final int getLineNumber() {
return in.getLineNumber();
}
}