/* * 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()); } } } }