/* * Copyright 2010 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 com.google.gwt.uibinder.rebind; import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.dev.resource.ResourceOracle; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.Text; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.Locator; import org.xml.sax.SAXParseException; import org.xml.sax.ext.DefaultHandler2; import java.util.Stack; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; /** * Uses SAX events to construct a DOM Document. Each node in the Document will * have a {@link XMLElement.Location} object attached to that Node's user data * with the {@value XMLElement#LOCATION_KEY} key. */ class W3cDocumentBuilder extends DefaultHandler2 { private final Document document; private final Stack<Node> eltStack = new Stack<Node>(); private Locator locator; private final TreeLogger logger; private final GwtResourceEntityResolver resolver; public W3cDocumentBuilder(TreeLogger logger, String pathBase, ResourceOracle resourceOracle) throws ParserConfigurationException { this.logger = logger; document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); eltStack.push(document); resolver = new GwtResourceEntityResolver(logger, resourceOracle, pathBase); } /** * Appends to the existing Text node, if possible. */ @Override public void characters(char[] ch, int start, int length) { Node current = eltStack.peek(); if (current.getChildNodes().getLength() == 1 && current.getChildNodes().item(0).getNodeType() == Node.TEXT_NODE) { Text text = (Text) current.getChildNodes().item(0); text.appendData(new String(ch, start, length)); } else { Text text = document.createTextNode(new String(ch, start, length)); eltStack.peek().appendChild(text); } } @Override public void endElement(String uri, String localName, String qName) { Node elt = eltStack.pop(); assert elt.getLocalName().equals(localName); } @Override public void error(SAXParseException exception) { logger.log(TreeLogger.ERROR, exception.getMessage()); logger.log(TreeLogger.DEBUG, "SAXParseException", exception); } @Override public void fatalError(SAXParseException exception) { /* * Fatal errors seem to be no scarier than error errors, and simply happen * due to badly formed XML. */ logger.log(TreeLogger.ERROR, exception.getMessage()); logger.log(TreeLogger.DEBUG, "SAXParseException", exception); } public Document getDocument() { return document; } @Override public InputSource resolveEntity(String name, String publicId, String baseURI, String systemId) { return resolver.resolveEntity(publicId, systemId); } /** * This is the whole reason for this mess. We want to know where a given * element comes from. */ @Override public void setDocumentLocator(Locator locator) { this.locator = locator; } @Override public void startElement(String uri, String localName, String qName, Attributes attributes) { Element elt = document.createElementNS(uri, qName); eltStack.peek().appendChild(elt); eltStack.push(elt); for (int i = 0, j = attributes.getLength(); i < j; i++) { elt.setAttributeNS(attributes.getURI(i), attributes.getQName(i), attributes.getValue(i)); } XMLElement.Location location = new XMLElement.Location( locator.getSystemId(), locator.getLineNumber()); elt.setUserData(XMLElement.LOCATION_KEY, location, null); } @Override public void warning(SAXParseException exception) { logger.log(TreeLogger.WARN, exception.getMessage()); logger.log(TreeLogger.DEBUG, "SAXParseException", exception); } }