/** * Copyright 2008 Google Inc. * * 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 org.waveprotocol.wave.model.document.raw; import org.waveprotocol.wave.model.document.parser.ItemType; import org.waveprotocol.wave.model.document.parser.XmlParseException; import org.waveprotocol.wave.model.document.parser.XmlParserFactory; import org.waveprotocol.wave.model.document.parser.SafeXmlPullParser; import org.waveprotocol.wave.model.document.util.DocumentParser; import org.waveprotocol.wave.model.util.CollectionUtils; /** * Parses a string into a RawDocument * * @author danilatos@google.com (Daniel Danilatos) * @author hearnden@google.com (David Hearnden) */ public class RawDocumentParserImpl<N, E extends N, T extends N, D extends RawDocument<N, E, T>> implements DocumentParser<D> { private final RawDocument.Factory<D> factory; /** * Creates a parser that uses a particular builder. * * @param factory builder to use when parsing * @return a new parser. */ public static <N, E extends N, T extends N, D extends RawDocument<N, E, T>> RawDocumentParserImpl<N, E, T, D> create(RawDocument.Factory<D> factory) { return new RawDocumentParserImpl<N, E, T, D>(factory); } /** * Creates a parser. * * @param factory builder to use */ private RawDocumentParserImpl(RawDocument.Factory<D> factory) { this.factory = factory; } /** * @param xmlString * @return parsed string */ public D parse(String xmlString) { SafeXmlPullParser parser; try { parser = XmlParserFactory.buffered(xmlString); } catch (XmlParseException e) { throw new RuntimeException("Cannot parse xml: " + xmlString, e); } // TODO(ohler): This can be an infinite loop. Fix that. while (parser.getCurrentType() != ItemType.START_ELEMENT) { parser.next(); } D document = factory.create(parser.getTagName(), CollectionUtils.newJavaMap(parser.getAttributes())); parseChildren(parser, document, document.getDocumentElement()); return document; } /** * Parses an element. * * @param parser tokenizer * @param parentElement the parent element to attach the parsed node to * @return a new element. */ private E parseElement(SafeXmlPullParser parser, D doc, E parentElement) { E element = doc.createElement(parser.getTagName(), CollectionUtils.newJavaMap(parser.getAttributes()), parentElement, null); parseChildren(parser, doc, element); return element; } private void parseChildren(SafeXmlPullParser parser, D doc, E element) { boolean done = false; do { N child = null; parser.next(); switch (parser.getCurrentType()) { case TEXT: child = parseText(parser, doc, element); break; case START_ELEMENT: child = parseElement(parser, doc, element); break; case END_ELEMENT: done = true; break; } if (child != null) { doc.insertBefore(element, child, null); } // This is a bit of judgment call. If this happens, the document is // invalid, since the closing tag is missing. By exiting the loop when // parser is out of tokens we're silently repairing the invalid doc. } while (!done && parser.hasNext()); } /** * Parses a text node. * * @param parser tokenizer * @param parentElement the parent element to attach the parsed node to * @return a new text node. */ private T parseText(SafeXmlPullParser parser, D doc, E parentElement) { String text = parser.getText(); T child = null; if (text.length() > 0) { child = doc.createTextNode(text, parentElement, null); } return child; } }