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("&"); 180 } 181 else if (ch == '<') { 182 annotationBuffer.append("<"); 183 } 184 // character sequence "]]>" cannot appear in content, 185 // therefore we should escape '>'. 186 else if (ch == '>') { 187 annotationBuffer.append(">"); 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("
"); 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("""); 435 } 436 else if (currChar == '<') { 437 newVal.append("<"); 438 } 439 else if (currChar == '&') { 440 newVal.append("&"); 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("	"); 447 } 448 else if (currChar == 0x0A) { 449 newVal.append("
"); 450 } 451 else if (currChar == 0x0D) { 452 newVal.append("
"); 453 } 454 else { 455 newVal.append(currChar); 456 } 457 } 458 return newVal.toString(); 459 } 460 }