/*
* Copyright 2003-2011 JetBrains s.r.o.
*
* 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.
*/
package jetbrains.mps.smodel.persistence.def;
public class XmlFastScanner {
public static final int EOI = 0;
public static final int SIMPLE_TAG = 1;
public static final int OPEN_TAG = 2;
public static final int CLOSE_TAG = 3;
public static final int OTHER = 4;
private int tokenOffset;
private int currOffset;
private String name;
private int currDepth = 0;
private int lastTag = -1; // remember whether we yielded SIMPLE_TAG or OPEN_TAG to decrease currDepth accordingly
final private char[] data;
private char chr;
public XmlFastScanner(char[] data) {
this.data = data;
reset();
}
private XmlFastScanner reset() {
this.currOffset = 0;
chr = data.length > 0 ? data[0] : 0;
return this;
}
private void shift() {
if (currOffset < data.length) {
currOffset++;
}
chr = currOffset < data.length ? data[currOffset] : 0;
}
private void skipSpaces() {
while (chr != 0 && Character.isWhitespace(chr)) {
shift();
}
}
private void skipName() {
assert Character.isJavaIdentifierStart(chr);
int start = currOffset;
shift();
while (Character.isJavaIdentifierPart(chr)) {
shift();
}
name = new String(data, start, currOffset - start);
}
public int next() {
tokenOffset = currOffset;
name = null;
if (lastTag == OPEN_TAG) {
currDepth++;
}
lastTag = -1;
if (chr == '<') {
shift();
skipSpaces();
if (Character.isJavaIdentifierStart(chr)) {
skipName();
while (chr != 0 && chr != '>') {
shift();
}
if (chr == '>') {
int slashIndex = currOffset - 1;
shift();
while (slashIndex > tokenOffset && Character.isWhitespace(data[slashIndex])) {
slashIndex--;
}
return lastTag = data[slashIndex] == '/' ? SIMPLE_TAG : OPEN_TAG;
}
return OTHER;
} else if (chr == '/') {
shift();
skipSpaces();
if (Character.isJavaIdentifierStart(chr)) {
skipName();
while (chr != 0 && chr != '>') {
shift();
}
if (chr == '>') {
shift();
currDepth--;
return lastTag = CLOSE_TAG;
}
}
return OTHER;
}
}
if (chr != 0) {
while (chr != 0 && chr != '<') {
shift();
}
return OTHER;
}
return EOI;
}
/**
* 0-based (top xml element has depth == 0) value indicating nesting of a tag. Meaningless for tokens other than OPEN_TAG, CLOSE_TAG, SIMPLE_TAG.
*/
public int tagDepth() {
return currDepth;
}
public String token() {
return new String(data, tokenOffset, currOffset - tokenOffset);
}
public String getText(int start, int end) {
return new String(data, start, end - start);
}
public int getTokenOffset() {
return tokenOffset;
}
public int getOffset() {
return currOffset;
}
public String getName() {
return name;
}
public static void main(String[] args) {
XmlFastScanner s = new XmlFastScanner("<l1 a=1><l2 b=2><l3 c=3/><l3/><l3/></l2><l2><l3></l3></l2></l1>".toCharArray());
assertNextTag(s, OPEN_TAG, 0);// <l1>
assertNextTag(s, OPEN_TAG, 1);// <l2>
assertNextTag(s, SIMPLE_TAG, 2);// <l3/>
assertNextTag(s, SIMPLE_TAG, 2);// <l3/>
assertNextTag(s, SIMPLE_TAG, 2);// <l3/>
assertNextTag(s, CLOSE_TAG, 1);// </l2>
assertNextTag(s, OPEN_TAG, 1);// <l2>
assertNextTag(s, OPEN_TAG, 2);// <l3>
assertNextTag(s, CLOSE_TAG, 2);// </l3>
assertNextTag(s, CLOSE_TAG, 1);// </l2>
assertNextTag(s, CLOSE_TAG, 0);// </l1>
print(s.reset());
}
private static void assertNextTag(XmlFastScanner s, int tagKind, int depth) {
if (s.next() != tagKind) {
throw new IllegalStateException();
}
if (s.tagDepth() != depth) {
throw new IllegalStateException();
}
}
private static void print(XmlFastScanner s) {
int token;
while ((token = s.next()) != EOI) {
if (token == SIMPLE_TAG || token == OPEN_TAG || token == CLOSE_TAG) {
for (int i = s.tagDepth(); i > 0; i--) {
System.out.print(" ");
}
System.out.println(s.getName());
}
}
}
}