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