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