1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 /* 19 * $Id$ 20 */ 21 22 package org.apache.qetest.xalanj2; 23 import java.io.PrintWriter; 24 import java.io.StringWriter; 25 import java.lang.reflect.Method; 26 import java.util.Properties; 27 28 import javax.xml.transform.Transformer; 29 30 import org.apache.xalan.templates.ElemLiteralResult; 31 import org.apache.xalan.templates.ElemTemplate; 32 import org.apache.xalan.templates.ElemTemplateElement; 33 import org.apache.xalan.templates.ElemTextLiteral; 34 import org.apache.xalan.transformer.TransformerImpl; 35 import org.apache.xml.dtm.ref.DTMNodeProxy; 36 import org.apache.xpath.XPath; 37 import org.w3c.dom.Attr; 38 import org.w3c.dom.Element; 39 import org.w3c.dom.Node; 40 import org.w3c.dom.NodeList; 41 42 /** 43 * Static utility for dumping info about common Xalan objects. 44 * Cheap-o string representations of some common properties 45 * of various objects; supports some formatting and encapsulation 46 * but could use improvements. 47 * Note: currently purposefully outputs plain strings, not 48 * any XML-like elements, so it's easier for other XML-like 49 * logging utilities to output our data without escaping, etc. 50 * 51 * @author shane_curcuru@lotus.com 52 * @version $Id$ 53 */ 54 public abstract class XalanDumper 55 { 56 // abstract class cannot be instantiated 57 58 /** Simple text constants: for items that are null. */ 59 public static final String NULL = "NULL"; 60 /** Simple text constants: separator between items. */ 61 public static final String SEP = ";"; 62 /** Simple text constants: beginning a block of items. */ 63 public static final String LBRACKET = "["; 64 /** Simple text constants: ending a block of items. */ 65 public static final String RBRACKET = "]"; 66 /** Simple text constants: line number. */ 67 public static final String LNUM = "L"; 68 /** Simple text constants: column number. */ 69 public static final String CNUM = "C"; 70 71 /** Simple output formats: default behavior. */ 72 public static final int DUMP_DEFAULT = 0; 73 /** Simple output formats: verbose: extra output. */ 74 public static final int DUMP_VERBOSE = 1; 75 /** Simple output formats: a contained object. */ 76 public static final int DUMP_CONTAINED = 2; 77 /** Simple output formats: don't close block. */ 78 public static final int DUMP_NOCLOSE = 4; 79 /** Simple output formats: don't include id's or other items likely to change. */ 80 public static final int DUMP_NOIDS = 8; 81 82 /** Cheap-o recursion marker: already recursing in Nodes/NodeLists. */ 83 public static final int DUMP_NODE_RECURSION = 16; 84 85 /** 86 * Return String describing an ElemTemplateElement. 87 * 88 * @param elem the ElemTemplateElement to print info of 89 * @param dumpLevel what format/how much to dump 90 */ dump(ElemTemplateElement elem, int dumpLevel)91 public static String dump(ElemTemplateElement elem, int dumpLevel) 92 { 93 StringBuffer buf = new StringBuffer("ElemTemplateElement" + LBRACKET); 94 if (null == elem) 95 return buf.toString() + NULL + RBRACKET; 96 97 // Note for user if it's an LRE or an xsl element 98 if(elem instanceof ElemLiteralResult) 99 buf.append("LRE:"); 100 else 101 buf.append("xsl:"); 102 103 buf.append(elem.getNodeName()); 104 buf.append(SEP + LNUM + elem.getLineNumber()); 105 buf.append(SEP + CNUM + elem.getColumnNumber()); 106 buf.append(SEP + "getLength=" + elem.getLength()); 107 if (DUMP_VERBOSE == (dumpLevel & DUMP_VERBOSE)) 108 { 109 // Only include systemIds (which are long) if verbose 110 buf.append(SEP + "getSystemId=" + elem.getSystemId()); 111 buf.append(SEP + "getStylesheet=" + elem.getStylesheet().getSystemId()); 112 } 113 try 114 { 115 Class cl = ((Object)elem).getClass(); 116 Method getSelect = cl.getMethod("getSelect", null); 117 if(null != getSelect) 118 { 119 buf.append(SEP + "select="); 120 XPath xpath = (XPath)getSelect.invoke(elem, null); 121 buf.append(xpath.getPatternString()); 122 } 123 } 124 catch(Exception e) 125 { 126 // no-op: just don't put in the select info for these items 127 } 128 if (DUMP_NOCLOSE == (dumpLevel & DUMP_NOCLOSE)) 129 return buf.toString(); 130 else 131 return buf.toString() + RBRACKET; 132 } 133 134 135 /** 136 * Return String describing an ElemTextLiteral. 137 * 138 * @param elem the ElemTextLiteral to print info of 139 * @param dumpLevel what format/how much to dump 140 */ dump(ElemTextLiteral elem, int dumpLevel)141 public static String dump(ElemTextLiteral elem, int dumpLevel) 142 { 143 StringBuffer buf = new StringBuffer("ElemTextLiteral" + LBRACKET); 144 if (null == elem) 145 return buf.toString() + NULL + RBRACKET; 146 147 buf.append(elem.getNodeName()); // I don't think this ever changes from #Text? 148 buf.append(SEP + LNUM + elem.getLineNumber()); 149 buf.append(SEP + CNUM + elem.getColumnNumber()); 150 151 String chars = new String(elem.getChars(), 0, elem.getChars().length); 152 buf.append(SEP + "chars=" + chars.trim()); 153 154 if (DUMP_NOCLOSE == (dumpLevel & DUMP_NOCLOSE)) 155 return buf.toString(); 156 else 157 return buf.toString() + RBRACKET; 158 } 159 160 /** 161 * Return String describing an ElemTemplate. 162 * 163 * @param elem the ElemTemplate to print info of 164 * @param dumpLevel what format/how much to dump 165 */ dump(ElemTemplate elem, int dumpLevel)166 public static String dump(ElemTemplate elem, int dumpLevel) 167 { 168 StringBuffer buf = new StringBuffer("ElemTemplate" + LBRACKET); 169 if (null == elem) 170 return buf.toString() + NULL + RBRACKET; 171 172 buf.append("xsl:" + elem.getNodeName()); 173 buf.append(SEP + LNUM + elem.getLineNumber()); 174 buf.append(SEP + CNUM + elem.getColumnNumber()); 175 if (DUMP_VERBOSE == (dumpLevel & DUMP_VERBOSE)) 176 { 177 // Only include systemIds (which are long) if verbose 178 buf.append(SEP + "getSystemId=" + elem.getSystemId()); 179 buf.append(SEP + "getStylesheet=" + elem.getStylesheet().getSystemId()); 180 } 181 try 182 { 183 Class cl = ((Object)elem).getClass(); 184 Method getSelect = cl.getMethod("getSelect", null); 185 if(null != getSelect) 186 { 187 buf.append(SEP + "select="); 188 XPath xpath = (XPath)getSelect.invoke(elem, null); 189 buf.append(xpath.getPatternString()); 190 } 191 } 192 catch(Exception e) 193 { 194 // no-op: just don't put in the select info for these items 195 } 196 if (null != elem.getMatch()) 197 buf.append(SEP + "match=" + elem.getMatch().getPatternString()); 198 199 if (null != elem.getName()) 200 buf.append(SEP + "name=" + elem.getName()); 201 202 if (null != elem.getMode()) 203 buf.append(SEP + "mode=" + elem.getMode()); 204 205 buf.append(SEP + "priority=" + elem.getPriority()); 206 207 if (DUMP_NOCLOSE == (dumpLevel & DUMP_NOCLOSE)) 208 return buf.toString(); 209 else 210 return buf.toString() + RBRACKET; 211 } 212 213 214 /** 215 * Return String describing a Transformer. 216 * Currently just returns info about a get selected public 217 * getter methods from a Transformer. 218 * Only really useful when it can do instanceof TransformerImpl 219 * to return custom info about Xalan 220 * 221 * @param t the Transformer to print info of 222 * @param dumpLevel what format/how much to dump 223 */ dump(Transformer trans, int dumpLevel)224 public static String dump(Transformer trans, int dumpLevel) 225 { 226 if (null == trans) 227 return "Transformer" + LBRACKET + NULL + RBRACKET; 228 229 StringBuffer buf = new StringBuffer(); 230 231 StringWriter sw = new StringWriter(); 232 Properties p = trans.getOutputProperties(); 233 if (null != p) 234 { 235 p.list(new PrintWriter(sw)); 236 buf.append("getOutputProperties{" + sw.toString() + "}"); 237 } 238 239 if (trans instanceof TransformerImpl) 240 { 241 final TransformerImpl timpl = (TransformerImpl)trans; 242 // We have a Xalan-J 2.x basic transformer 243 244 // Android-changed: TransformerImpl in 2.7.1 doesn't have getBaseURLOfSource() method. 245 // buf.append("getBaseURLOfSource=" + timpl.getBaseURLOfSource() + SEP); 246 // Result getOutputTarget() 247 // ContentHandler getInputContentHandler(boolean doDocFrag) 248 // DeclHandler getInputDeclHandler() 249 // LexicalHandler getInputLexicalHandler() 250 // OutputProperties getOutputFormat() 251 // Serializer getSerializer() 252 // ElemTemplateElement getCurrentElement() 253 // int getCurrentNode() 254 // ElemTemplate getCurrentTemplate() 255 // ElemTemplate getMatchedTemplate() 256 // int getMatchedNode() 257 // DTMIterator getContextNodeList() 258 // StylesheetRoot getStylesheet() 259 // int getRecursionLimit() 260 buf.append("getMode=" + timpl.getMode() + SEP); 261 } 262 263 return "Transformer" + LBRACKET 264 + buf.toString() + RBRACKET; 265 } 266 267 268 /** 269 * Return String describing a Node. 270 * Currently just returns TracerEvent.printNode(n) 271 * 272 * @param n the Node to print info of 273 * @param dumpLevel what format/how much to dump 274 */ dump(Node n, int dumpLevel)275 public static String dump(Node n, int dumpLevel) 276 { 277 if (null == n) 278 return "Node" + LBRACKET + NULL + RBRACKET; 279 280 // Copied but modified from TracerEvent; ditch hashCode 281 StringBuffer buf = new StringBuffer(); 282 283 if (n instanceof Element) 284 { 285 buf.append(n.getNodeName()); 286 287 Node c = n.getFirstChild(); 288 289 while (null != c) 290 { 291 if (c instanceof Attr) 292 { 293 buf.append(dump(c, dumpLevel | DUMP_NODE_RECURSION) + " "); 294 } 295 c = c.getNextSibling(); 296 } 297 } 298 else 299 { 300 if (n instanceof Attr) 301 { 302 buf.append(n.getNodeName() + "=" + n.getNodeValue()); 303 } 304 else 305 { 306 buf.append(n.getNodeName()); 307 } 308 } 309 310 311 // If we're already recursing, don't bother printing out 'Node' again 312 if (DUMP_NODE_RECURSION == (dumpLevel & DUMP_NODE_RECURSION)) 313 return LBRACKET + buf.toString() + RBRACKET; 314 else 315 return "Node" + LBRACKET + buf.toString() + RBRACKET; 316 } 317 318 /** 319 * Return String describing a DTMNodeProxy. 320 * This is the Xalan-J 2.x internal wrapper for Nodes. 321 * 322 * @param n the DTMNodeProxy to print info of 323 * @param dumpLevel what format/how much to dump 324 */ dump(DTMNodeProxy n, int dumpLevel)325 public static String dump(DTMNodeProxy n, int dumpLevel) 326 { 327 if (null == n) 328 return "DTMNodeProxy" + LBRACKET + NULL + RBRACKET; 329 330 // Copied but modified from TracerEvent; ditch hashCode 331 StringBuffer buf = new StringBuffer(); 332 333 if (DUMP_NOIDS != (dumpLevel & DUMP_NOIDS)) 334 { 335 // Only include the DTM node number if asked 336 buf.append(n.getDTMNodeNumber()); 337 } 338 339 if (n instanceof Element) 340 { 341 buf.append(n.getNodeName()); 342 // Also output first x chars of value 343 buf.append(substr(n.getNodeValue())); 344 345 DTMNodeProxy c = (DTMNodeProxy)n.getFirstChild(); 346 347 while (null != c) 348 { 349 buf.append(dump(c, dumpLevel | DUMP_NODE_RECURSION) + " "); 350 c = (DTMNodeProxy)c.getNextSibling(); 351 } 352 } 353 else 354 { 355 if (n instanceof Attr) 356 { 357 buf.append(n.getNodeName() + "=" + n.getNodeValue()); 358 } 359 else 360 { 361 buf.append(n.getNodeName()); 362 // Also output first x chars of value 363 buf.append(substr(n.getNodeValue())); 364 } 365 } 366 367 368 // If we're already recursing, don't bother printing out 'Node' again 369 if (DUMP_NODE_RECURSION == (dumpLevel & DUMP_NODE_RECURSION)) 370 return LBRACKET + buf.toString() + RBRACKET; 371 else 372 return "DTMNodeProxy" + LBRACKET + buf.toString() + RBRACKET; 373 } 374 375 /** Cheap-o worker method to substring a string. */ 376 public static int MAX_SUBSTR = 8; 377 378 /** Cheap-o worker method to substring a string. */ 379 public static String SUBSTR_PREFIX = ":"; 380 381 /** Cheap-o worker method to substring a string. */ substr(String s)382 protected static String substr(String s) 383 { 384 if (null == s) 385 return ""; 386 return SUBSTR_PREFIX + s.substring(0, Math.min(s.length(), MAX_SUBSTR)); 387 } 388 389 /** 390 * Return String describing a NodeList. 391 * Currently just returns TracerEvent.printNode(n) 392 * 393 * @param nl the NodeList to print info of 394 * @param dumpLevel what format/how much to dump 395 */ dump(NodeList nl, int dumpLevel)396 public static String dump(NodeList nl, int dumpLevel) 397 { 398 if (null == nl) 399 return "NodeList" + LBRACKET + NULL + RBRACKET; 400 401 StringBuffer buf = new StringBuffer(); 402 403 int len = nl.getLength() - 1; 404 int i = 0; 405 while (i < len) 406 { 407 Node n = nl.item(i); 408 if (null != n) 409 { 410 buf.append(dump(n, dumpLevel) + ", "); 411 } 412 ++i; 413 } 414 415 if (i == len) 416 { 417 Node n = nl.item(len); 418 if (null != n) 419 { 420 buf.append(dump(n, dumpLevel)); 421 } 422 } 423 return "NodeList" + LBRACKET 424 + buf.toString() + RBRACKET; 425 } 426 427 428 /** 429 * Print String type of node. 430 * @param n Node to report type of 431 * @return String type name 432 */ dumpNodeType(Node n)433 public static String dumpNodeType(Node n) 434 { 435 if (null == n) 436 return NULL; 437 switch (n.getNodeType()) 438 { 439 case Node.DOCUMENT_NODE : 440 return "DOCUMENT_NODE"; 441 442 case Node.ELEMENT_NODE : 443 return "ELEMENT_NODE"; 444 445 case Node.CDATA_SECTION_NODE : 446 return "CDATA_SECTION_NODE"; 447 448 case Node.ENTITY_REFERENCE_NODE : 449 return "ENTITY_REFERENCE_NODE"; 450 451 case Node.ATTRIBUTE_NODE : 452 return "ATTRIBUTE_NODE"; 453 454 case Node.COMMENT_NODE : 455 return "COMMENT_NODE"; 456 457 case Node.ENTITY_NODE : 458 return "ENTITY_NODE"; 459 460 case Node.NOTATION_NODE : 461 return "NOTATION_NODE"; 462 463 case Node.PROCESSING_INSTRUCTION_NODE : 464 return "PROCESSING_INSTRUCTION_NODE"; 465 466 case Node.TEXT_NODE : 467 return "TEXT_NODE"; 468 469 default : 470 return "UNKNOWN_NODE"; 471 } 472 } // end of dumpNodeType() 473 474 } 475