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