1 /*
   2  * reserved comment block
   3  * DO NOT REMOVE OR ALTER!
   4  */
   5 /*
   6  * Copyright 2001-2004 The Apache Software Foundation.
   7  *
   8  * Licensed under the Apache License, Version 2.0 (the "License");
   9  * you may not use this file except in compliance with the License.
  10  * You may obtain a copy of the License at
  11  *
  12  *      http://www.apache.org/licenses/LICENSE-2.0
  13  *
  14  * Unless required by applicable law or agreed to in writing, software
  15  * distributed under the License is distributed on an "AS IS" BASIS,
  16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17  * See the License for the specific language governing permissions and
  18  * limitations under the License.
  19  */
  20 
  21 package com.sun.org.apache.xerces.internal.impl.xs.opti;
  22 
  23 import java.util.ArrayList;
  24 import java.util.Enumeration;
  25 
  26 import com.sun.org.apache.xerces.internal.util.XMLSymbols;
  27 import com.sun.org.apache.xerces.internal.xni.NamespaceContext;
  28 import com.sun.org.apache.xerces.internal.xni.QName;
  29 import com.sun.org.apache.xerces.internal.xni.XMLAttributes;
  30 import com.sun.org.apache.xerces.internal.xni.XMLString;
  31 import org.w3c.dom.Attr;
  32 import org.w3c.dom.DOMImplementation;
  33 import org.w3c.dom.Element;
  34 import org.w3c.dom.NamedNodeMap;
  35 import org.w3c.dom.Node;
  36 
  37 /**
  38  * @xerces.internal
  39  *
  40  * @author Rahul Srivastava, Sun Microsystems Inc.
  41  * @author Sandy Gao, IBM
  42  *
  43  * @version $Id: SchemaDOM.java,v 1.7 2010-11-01 04:40:01 joehw Exp $
  44  */
  45 public class SchemaDOM extends DefaultDocument {
  46 
  47     static final int relationsRowResizeFactor = 15;
  48     static final int relationsColResizeFactor = 10;
  49 
  50     NodeImpl[][] relations;
  51     // parent must be an element in this scheme
  52     ElementImpl parent;
  53     int currLoc;
  54     int nextFreeLoc;
  55     boolean hidden;
  56     boolean inCDATA;
  57 
  58     // for annotation support:
  59     private StringBuffer fAnnotationBuffer = null;
  60 
  61     public SchemaDOM() {
  62         reset();
  63     }
  64 
  65 
  66     public ElementImpl startElement(QName element, XMLAttributes attributes,
  67             int line, int column, int offset) {
  68         ElementImpl node = new ElementImpl(line, column, offset);
  69         processElement(element, attributes, node);
  70         // now the current node added, becomes the parent
  71         parent = node;
  72         return node;
  73     }
  74 
  75     public ElementImpl emptyElement(QName element, XMLAttributes attributes,
  76             int line, int column, int offset) {
  77         ElementImpl node = new ElementImpl(line, column, offset);
  78         processElement(element, attributes, node);
  79         return node;
  80     }
  81 
  82     public ElementImpl startElement(QName element, XMLAttributes attributes,
  83             int line, int column) {
  84         return startElement(element, attributes, line, column, -1);
  85     }
  86 
  87     public ElementImpl emptyElement(QName element, XMLAttributes attributes,
  88             int line, int column) {
  89         return emptyElement(element, attributes, line, column, -1);
  90     }
  91 
  92     private void processElement(QName element, XMLAttributes attributes, ElementImpl node) {
  93 
  94         // populate node
  95         node.prefix = element.prefix;
  96         node.localpart = element.localpart;
  97         node.rawname = element.rawname;
  98         node.uri = element.uri;
  99         node.schemaDOM = this;
 100 
 101         // set the attributes
 102         Attr[] attrs = new Attr[attributes.getLength()];
 103         for (int i=0; i<attributes.getLength(); i++) {
 104             attrs[i] = new AttrImpl(node,
 105                     attributes.getPrefix(i),
 106                     attributes.getLocalName(i),
 107                     attributes.getQName(i),
 108                     attributes.getURI(i),
 109                     attributes.getValue(i));
 110         }
 111         node.attrs = attrs;
 112 
 113         // check if array needs to be resized
 114         if (nextFreeLoc == relations.length) {
 115             resizeRelations();
 116         }
 117 
 118         // store the current parent
 119         //if (relations[currLoc][0] == null || relations[currLoc][0] != parent) {
 120         if (relations[currLoc][0] != parent) {
 121             relations[nextFreeLoc][0] = parent;
 122             currLoc = nextFreeLoc++;
 123         }
 124 
 125         // add the current node as child of parent
 126         boolean foundPlace = false;
 127         int i = 1;
 128         for (i = 1; i<relations[currLoc].length; i++) {
 129             if (relations[currLoc][i] == null) {
 130                 foundPlace = true;
 131                 break;
 132             }
 133         }
 134 
 135         if (!foundPlace) {
 136             resizeRelations(currLoc);
 137         }
 138         relations[currLoc][i] = node;
 139 
 140         parent.parentRow = currLoc;
 141         node.row = currLoc;
 142         node.col = i;
 143     }
 144 
 145 
 146     public void endElement()  {
 147         // the parent of current parent node becomes the parent
 148         // for the next node.
 149         currLoc = parent.row;
 150         parent = (ElementImpl)relations[currLoc][0];
 151     }
 152 
 153     // note that this will only be called within appinfo/documentation
 154     void comment(XMLString text) {
 155         fAnnotationBuffer.append("<!--");
 156         if (text.length > 0) {
 157             fAnnotationBuffer.append(text.ch, text.offset, text.length);
 158         }
 159         fAnnotationBuffer.append("-->");
 160     }
 161 
 162     // note that this will only be called within appinfo/documentation
 163     void processingInstruction(String target, XMLString data) {
 164         fAnnotationBuffer.append("<?").append(target);
 165         if (data.length > 0) {
 166             fAnnotationBuffer.append(' ').append(data.ch, data.offset, data.length);
 167         }
 168         fAnnotationBuffer.append("?>");
 169     }
 170 
 171     // note that this will only be called within appinfo/documentation
 172     void characters(XMLString text) {
 173 
 174         // escape characters if necessary
 175         if (!inCDATA) {
 176             final StringBuffer annotationBuffer = fAnnotationBuffer;
 177             for (int i = text.offset; i < text.offset+text.length; ++i) {
 178                 char ch = text.ch[i];
 179                 if (ch == '&') {
 180                     annotationBuffer.append("&amp;");
 181                 }
 182                 else if (ch == '<') {
 183                     annotationBuffer.append("&lt;");
 184                 }
 185                 // character sequence "]]>" cannot appear in content,
 186                 // therefore we should escape '>'.
 187                 else if (ch == '>') {
 188                     annotationBuffer.append("&gt;");
 189                 }
 190                 // If CR is part of the document's content, it
 191                 // must not be printed as a literal otherwise
 192                 // it would be normalized to LF when the document
 193                 // is reparsed.
 194                 else if (ch == '\r') {
 195                     annotationBuffer.append("&#xD;");
 196                 }
 197                 else {
 198                     annotationBuffer.append(ch);
 199                 }
 200             }
 201         }
 202         else {
 203             fAnnotationBuffer.append(text.ch, text.offset, text.length);
 204         }
 205     }
 206 
 207     // note that this will only be called within appinfo/documentation
 208     void charactersRaw(String text) {
 209         fAnnotationBuffer.append(text);
 210     }
 211 
 212     void endAnnotation(QName elemName, ElementImpl annotation) {
 213         fAnnotationBuffer.append("\n</").append(elemName.rawname).append(">");
 214         annotation.fAnnotation = fAnnotationBuffer.toString();
 215         // apparently, there is no sensible way of resetting these things
 216         fAnnotationBuffer = null;
 217     }
 218 
 219     void endAnnotationElement(QName elemName) {
 220         endAnnotationElement(elemName.rawname);
 221     }
 222 
 223     void endAnnotationElement(String elemRawName) {
 224         fAnnotationBuffer.append("</").append(elemRawName).append(">");
 225     }
 226 
 227     void endSyntheticAnnotationElement(QName elemName, boolean complete) {
 228         endSyntheticAnnotationElement(elemName.rawname, complete);
 229     }
 230 
 231     void endSyntheticAnnotationElement(String elemRawName, boolean complete) {
 232         if(complete) {
 233             fAnnotationBuffer.append("\n</").append(elemRawName).append(">");
 234             // note that this is always called after endElement on <annotation>'s
 235             // child and before endElement on annotation.
 236             // hence, we must make this the child of the current
 237             // parent's only child.
 238             parent.fSyntheticAnnotation = fAnnotationBuffer.toString();
 239 
 240             // apparently, there is no sensible way of resetting
 241             // these things
 242             fAnnotationBuffer = null;
 243         } else      //capturing character calls
 244             fAnnotationBuffer.append("</").append(elemRawName).append(">");
 245     }
 246 
 247     void startAnnotationCDATA() {
 248         inCDATA = true;
 249         fAnnotationBuffer.append("<![CDATA[");
 250     }
 251 
 252     void endAnnotationCDATA() {
 253         fAnnotationBuffer.append("]]>");
 254         inCDATA = false;
 255     }
 256 
 257     private void resizeRelations() {
 258         NodeImpl[][] temp = new NodeImpl[relations.length+relationsRowResizeFactor][];
 259         System.arraycopy(relations, 0, temp, 0, relations.length);
 260         for (int i = relations.length ; i < temp.length ; i++) {
 261             temp[i] = new NodeImpl[relationsColResizeFactor];
 262         }
 263         relations = temp;
 264     }
 265 
 266     private void resizeRelations(int i) {
 267         NodeImpl[] temp = new NodeImpl[relations[i].length+relationsColResizeFactor];
 268         System.arraycopy(relations[i], 0, temp, 0, relations[i].length);
 269         relations[i] = temp;
 270     }
 271 
 272 
 273     public void reset() {
 274 
 275         // help out the garbage collector
 276         if(relations != null)
 277             for(int i=0; i<relations.length; i++)
 278                 for(int j=0; j<relations[i].length; j++)
 279                     relations[i][j] = null;
 280         relations = new NodeImpl[relationsRowResizeFactor][];
 281         parent = new ElementImpl(0, 0, 0);
 282         parent.rawname = "DOCUMENT_NODE";
 283         currLoc = 0;
 284         nextFreeLoc = 1;
 285         inCDATA = false;
 286         for (int i=0; i<relationsRowResizeFactor; i++) {
 287             relations[i] = new NodeImpl[relationsColResizeFactor];
 288         }
 289         relations[currLoc][0] = parent;
 290     }
 291 
 292 
 293     public void printDOM() {
 294         /*
 295          for (int i=0; i<relations.length; i++) {
 296          if (relations[i][0] != null) {
 297          for (int j=0; j<relations[i].length; j++) {
 298          if (relations[i][j] != null) {
 299          System.out.print(relations[i][j].nodeType+"-"+relations[i][j].parentRow+"  ");
 300          }
 301          }
 302          System.out.println("");
 303          }
 304          }
 305          */
 306         //traverse(getDocumentElement(), 0);
 307     }
 308 
 309 
 310     // debug methods
 311 
 312     public static void traverse(Node node, int depth) {
 313         indent(depth);
 314         System.out.print("<"+node.getNodeName());
 315 
 316         if (node.hasAttributes()) {
 317             NamedNodeMap attrs = node.getAttributes();
 318             for (int i=0; i<attrs.getLength(); i++) {
 319                 System.out.print("  "+((Attr)attrs.item(i)).getName()+"=\""+((Attr)attrs.item(i)).getValue()+"\"");
 320             }
 321         }
 322 
 323         if (node.hasChildNodes()) {
 324             System.out.println(">");
 325             depth+=4;
 326             for (Node child = node.getFirstChild(); child != null; child = child.getNextSibling()) {
 327                 traverse(child, depth);
 328             }
 329             depth-=4;
 330             indent(depth);
 331             System.out.println("</"+node.getNodeName()+">");
 332         }
 333         else {
 334             System.out.println("/>");
 335         }
 336     }
 337 
 338     public static void indent(int amount) {
 339         for (int i = 0; i < amount; i++) {
 340             System.out.print(' ');
 341         }
 342     }
 343 
 344     // org.w3c.dom methods
 345     public Element getDocumentElement() {
 346         // this returns a parent node, known to be an ElementImpl
 347         return (ElementImpl)relations[0][1];
 348     }
 349 
 350     public DOMImplementation getImplementation() {
 351         return SchemaDOMImplementation.getDOMImplementation();
 352     }
 353 
 354     // commence the serialization of an annotation
 355     void startAnnotation(QName elemName, XMLAttributes attributes,
 356             NamespaceContext namespaceContext) {
 357         startAnnotation(elemName.rawname, attributes, namespaceContext);
 358     }
 359     void startAnnotation(String elemRawName, XMLAttributes attributes,
 360             NamespaceContext namespaceContext) {
 361         if(fAnnotationBuffer == null) fAnnotationBuffer = new StringBuffer(256);
 362         fAnnotationBuffer.append("<").append(elemRawName).append(" ");
 363 
 364         // attributes are a bit of a pain.  To get this right, we have to keep track
 365         // of the namespaces we've seen declared, then examine the namespace context
 366         // for other namespaces so that we can also include them.
 367         // optimized for simplicity and the case that not many
 368         // namespaces are declared on this annotation...
 369         ArrayList namespaces = new ArrayList();
 370         for (int i = 0; i < attributes.getLength(); ++i) {
 371             String aValue = attributes.getValue(i);
 372             String aPrefix = attributes.getPrefix(i);
 373             String aQName = attributes.getQName(i);
 374             // if it's xmlns:* or xmlns, must be a namespace decl
 375             if (aPrefix == XMLSymbols.PREFIX_XMLNS || aQName == XMLSymbols.PREFIX_XMLNS) {
 376                 namespaces.add(aPrefix == XMLSymbols.PREFIX_XMLNS ?
 377                         attributes.getLocalName(i) : XMLSymbols.EMPTY_STRING);
 378             }
 379             fAnnotationBuffer.append(aQName).append("=\"").append(processAttValue(aValue)).append("\" ");
 380         }
 381         // now we have to look through currently in-scope namespaces to see what
 382         // wasn't declared here
 383         Enumeration currPrefixes = namespaceContext.getAllPrefixes();
 384         while(currPrefixes.hasMoreElements()) {
 385             String prefix = (String)currPrefixes.nextElement();
 386             String uri = namespaceContext.getURI(prefix);
 387             if (uri == null) {
 388                 uri = XMLSymbols.EMPTY_STRING;
 389             }
 390             if (!namespaces.contains(prefix)) {
 391                 // have to declare this one
 392                 if(prefix == XMLSymbols.EMPTY_STRING) {
 393                     fAnnotationBuffer.append("xmlns").append("=\"").append(processAttValue(uri)).append("\" ");
 394                 }
 395                 else {
 396                     fAnnotationBuffer.append("xmlns:").append(prefix).append("=\"").append(processAttValue(uri)).append("\" ");
 397                 }
 398             }
 399         }
 400         fAnnotationBuffer.append(">\n");
 401     }
 402     void startAnnotationElement(QName elemName, XMLAttributes attributes) {
 403         startAnnotationElement(elemName.rawname, attributes);
 404     }
 405     void startAnnotationElement(String elemRawName, XMLAttributes attributes) {
 406         fAnnotationBuffer.append("<").append(elemRawName);
 407         for(int i=0; i<attributes.getLength(); i++) {
 408             String aValue = attributes.getValue(i);
 409             fAnnotationBuffer.append(" ").append(attributes.getQName(i)).append("=\"").append(processAttValue(aValue)).append("\"");
 410         }
 411         fAnnotationBuffer.append(">");
 412     }
 413 
 414     private static String processAttValue(String original) {
 415         final int length = original.length();
 416         // normally, nothing will happen
 417         for (int i = 0; i < length; ++i) {
 418             char currChar = original.charAt(i);
 419             if (currChar == '"' || currChar == '<' || currChar == '&' ||
 420                     currChar == 0x09 || currChar == 0x0A || currChar == 0x0D) {
 421                 return escapeAttValue(original, i);
 422             }
 423         }
 424         return original;
 425     }
 426 
 427     private static String escapeAttValue(String original, int from) {
 428         int i;
 429         final int length = original.length();
 430         StringBuffer newVal = new StringBuffer(length);
 431         newVal.append(original.substring(0, from));
 432         for (i = from; i < length; ++i) {
 433             char currChar = original.charAt(i);
 434             if (currChar == '"') {
 435                 newVal.append("&quot;");
 436             }
 437             else if (currChar == '<') {
 438                 newVal.append("&lt;");
 439             }
 440             else if (currChar == '&') {
 441                 newVal.append("&amp;");
 442             }
 443             // Must escape 0x09, 0x0A and 0x0D if they appear in attribute
 444             // value so that they may be round-tripped. They would otherwise
 445             // be transformed to a 0x20 during attribute value normalization.
 446             else if (currChar == 0x09) {
 447                 newVal.append("&#x9;");
 448             }
 449             else if (currChar == 0x0A) {
 450                 newVal.append("&#xA;");
 451             }
 452             else if (currChar == 0x0D) {
 453                 newVal.append("&#xD;");
 454             }
 455             else {
 456                 newVal.append(currChar);
 457             }
 458         }
 459         return newVal.toString();
 460     }
 461 }