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