/* * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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.sun.org.apache.xerces.internal.impl.xs.opti; import com.sun.org.apache.xerces.internal.util.XMLSymbols; import com.sun.org.apache.xerces.internal.xni.NamespaceContext; import com.sun.org.apache.xerces.internal.xni.QName; import com.sun.org.apache.xerces.internal.xni.XMLAttributes; import com.sun.org.apache.xerces.internal.xni.XMLString; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; import org.w3c.dom.Attr; import org.w3c.dom.DOMImplementation; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; /** * @xerces.internal * * @author Rahul Srivastava, Sun Microsystems Inc. * @author Sandy Gao, IBM * * @LastModified: Oct 2017 */ public class SchemaDOM extends DefaultDocument { static final int relationsRowResizeFactor = 15; static final int relationsColResizeFactor = 10; NodeImpl[][] relations; // parent must be an element in this scheme ElementImpl parent; int currLoc; int nextFreeLoc; boolean hidden; boolean inCDATA; // for annotation support: private StringBuffer fAnnotationBuffer = null; public SchemaDOM() { reset(); } public ElementImpl startElement(QName element, XMLAttributes attributes, int line, int column, int offset) { ElementImpl node = new ElementImpl(line, column, offset); processElement(element, attributes, node); // now the current node added, becomes the parent parent = node; return node; } public ElementImpl emptyElement(QName element, XMLAttributes attributes, int line, int column, int offset) { ElementImpl node = new ElementImpl(line, column, offset); processElement(element, attributes, node); return node; } public ElementImpl startElement(QName element, XMLAttributes attributes, int line, int column) { return startElement(element, attributes, line, column, -1); } public ElementImpl emptyElement(QName element, XMLAttributes attributes, int line, int column) { return emptyElement(element, attributes, line, column, -1); } private void processElement(QName element, XMLAttributes attributes, ElementImpl node) { // populate node node.prefix = element.prefix; node.localpart = element.localpart; node.rawname = element.rawname; node.uri = element.uri; node.schemaDOM = this; // set the attributes Attr[] attrs = new Attr[attributes.getLength()]; for (int i=0; i 0) { fAnnotationBuffer.append(text.ch, text.offset, text.length); } fAnnotationBuffer.append("-->"); } // note that this will only be called within appinfo/documentation void processingInstruction(String target, XMLString data) { fAnnotationBuffer.append(" 0) { fAnnotationBuffer.append(' ').append(data.ch, data.offset, data.length); } fAnnotationBuffer.append("?>"); } // note that this will only be called within appinfo/documentation void characters(XMLString text) { // escape characters if necessary if (!inCDATA) { final StringBuffer annotationBuffer = fAnnotationBuffer; for (int i = text.offset; i < text.offset+text.length; ++i) { char ch = text.ch[i]; if (ch == '&') { annotationBuffer.append("&"); } else if (ch == '<') { annotationBuffer.append("<"); } // character sequence "]]>" cannot appear in content, // therefore we should escape '>'. else if (ch == '>') { annotationBuffer.append(">"); } // If CR is part of the document's content, it // must not be printed as a literal otherwise // it would be normalized to LF when the document // is reparsed. else if (ch == '\r') { annotationBuffer.append(" "); } else { annotationBuffer.append(ch); } } } else { fAnnotationBuffer.append(text.ch, text.offset, text.length); } } // note that this will only be called within appinfo/documentation void charactersRaw(String text) { fAnnotationBuffer.append(text); } void endAnnotation(QName elemName, ElementImpl annotation) { fAnnotationBuffer.append("\n"); annotation.fAnnotation = fAnnotationBuffer.toString(); // apparently, there is no sensible way of resetting these things fAnnotationBuffer = null; } void endAnnotationElement(QName elemName) { endAnnotationElement(elemName.rawname); } void endAnnotationElement(String elemRawName) { fAnnotationBuffer.append(""); } void endSyntheticAnnotationElement(QName elemName, boolean complete) { endSyntheticAnnotationElement(elemName.rawname, complete); } void endSyntheticAnnotationElement(String elemRawName, boolean complete) { if(complete) { fAnnotationBuffer.append("\n"); // note that this is always called after endElement on 's // child and before endElement on annotation. // hence, we must make this the child of the current // parent's only child. parent.fSyntheticAnnotation = fAnnotationBuffer.toString(); // apparently, there is no sensible way of resetting // these things fAnnotationBuffer = null; } else //capturing character calls fAnnotationBuffer.append(""); } void startAnnotationCDATA() { inCDATA = true; fAnnotationBuffer.append(""); inCDATA = false; } private void resizeRelations() { NodeImpl[][] temp = new NodeImpl[relations.length+relationsRowResizeFactor][]; System.arraycopy(relations, 0, temp, 0, relations.length); for (int i = relations.length ; i < temp.length ; i++) { temp[i] = new NodeImpl[relationsColResizeFactor]; } relations = temp; } private void resizeRelations(int i) { NodeImpl[] temp = new NodeImpl[relations[i].length+relationsColResizeFactor]; System.arraycopy(relations[i], 0, temp, 0, relations[i].length); relations[i] = temp; } public void reset() { // help out the garbage collector if(relations != null) for(int i=0; i"); depth+=4; for (Node child = node.getFirstChild(); child != null; child = child.getNextSibling()) { traverse(child, depth); } depth-=4; indent(depth); System.out.println(""); } else { System.out.println("/>"); } } public static void indent(int amount) { for (int i = 0; i < amount; i++) { System.out.print(' '); } } // org.w3c.dom methods public Element getDocumentElement() { // this returns a parent node, known to be an ElementImpl return (ElementImpl)relations[0][1]; } public DOMImplementation getImplementation() { return SchemaDOMImplementation.getDOMImplementation(); } // commence the serialization of an annotation void startAnnotation(QName elemName, XMLAttributes attributes, NamespaceContext namespaceContext) { startAnnotation(elemName.rawname, attributes, namespaceContext); } void startAnnotation(String elemRawName, XMLAttributes attributes, NamespaceContext namespaceContext) { if(fAnnotationBuffer == null) fAnnotationBuffer = new StringBuffer(256); fAnnotationBuffer.append("<").append(elemRawName).append(" "); // attributes are a bit of a pain. To get this right, we have to keep track // of the namespaces we've seen declared, then examine the namespace context // for other namespaces so that we can also include them. // optimized for simplicity and the case that not many // namespaces are declared on this annotation... List namespaces = new ArrayList<>(); for (int i = 0; i < attributes.getLength(); ++i) { String aValue = attributes.getValue(i); String aPrefix = attributes.getPrefix(i); String aQName = attributes.getQName(i); // if it's xmlns:* or xmlns, must be a namespace decl if (aPrefix == XMLSymbols.PREFIX_XMLNS || aQName == XMLSymbols.PREFIX_XMLNS) { namespaces.add(aPrefix == XMLSymbols.PREFIX_XMLNS ? attributes.getLocalName(i) : XMLSymbols.EMPTY_STRING); } fAnnotationBuffer.append(aQName).append("=\"").append(processAttValue(aValue)).append("\" "); } // now we have to look through currently in-scope namespaces to see what // wasn't declared here Enumeration currPrefixes = namespaceContext.getAllPrefixes(); while(currPrefixes.hasMoreElements()) { String prefix = currPrefixes.nextElement(); String uri = namespaceContext.getURI(prefix); if (uri == null) { uri = XMLSymbols.EMPTY_STRING; } if (!namespaces.contains(prefix)) { // have to declare this one if(prefix == XMLSymbols.EMPTY_STRING) { fAnnotationBuffer.append("xmlns").append("=\"").append(processAttValue(uri)).append("\" "); } else { fAnnotationBuffer.append("xmlns:").append(prefix).append("=\"").append(processAttValue(uri)).append("\" "); } } } fAnnotationBuffer.append(">\n"); } void startAnnotationElement(QName elemName, XMLAttributes attributes) { startAnnotationElement(elemName.rawname, attributes); } void startAnnotationElement(String elemRawName, XMLAttributes attributes) { fAnnotationBuffer.append("<").append(elemRawName); for(int i=0; i"); } private static String processAttValue(String original) { final int length = original.length(); // normally, nothing will happen for (int i = 0; i < length; ++i) { char currChar = original.charAt(i); if (currChar == '"' || currChar == '<' || currChar == '&' || currChar == 0x09 || currChar == 0x0A || currChar == 0x0D) { return escapeAttValue(original, i); } } return original; } private static String escapeAttValue(String original, int from) { int i; final int length = original.length(); StringBuffer newVal = new StringBuffer(length); newVal.append(original.substring(0, from)); for (i = from; i < length; ++i) { char currChar = original.charAt(i); if (currChar == '"') { newVal.append("""); } else if (currChar == '<') { newVal.append("<"); } else if (currChar == '&') { newVal.append("&"); } // Must escape 0x09, 0x0A and 0x0D if they appear in attribute // value so that they may be round-tripped. They would otherwise // be transformed to a 0x20 during attribute value normalization. else if (currChar == 0x09) { newVal.append(" "); } else if (currChar == 0x0A) { newVal.append(" "); } else if (currChar == 0x0D) { newVal.append(" "); } else { newVal.append(currChar); } } return newVal.toString(); } }