View Javadoc
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 }