001 package com.mockrunner.mock.jdbc; 002 003 import java.io.ByteArrayInputStream; 004 import java.io.ByteArrayOutputStream; 005 import java.io.InputStream; 006 import java.io.OutputStream; 007 import java.io.Reader; 008 import java.io.StringReader; 009 import java.io.StringWriter; 010 import java.io.Writer; 011 import java.sql.SQLException; 012 import java.sql.SQLXML; 013 014 import javax.xml.parsers.DocumentBuilder; 015 import javax.xml.parsers.DocumentBuilderFactory; 016 import javax.xml.parsers.ParserConfigurationException; 017 import javax.xml.parsers.SAXParser; 018 import javax.xml.parsers.SAXParserFactory; 019 import javax.xml.stream.XMLInputFactory; 020 import javax.xml.stream.XMLOutputFactory; 021 import javax.xml.stream.XMLStreamException; 022 import javax.xml.stream.XMLStreamReader; 023 import javax.xml.stream.XMLStreamWriter; 024 import javax.xml.transform.Result; 025 import javax.xml.transform.Source; 026 import javax.xml.transform.dom.DOMResult; 027 import javax.xml.transform.dom.DOMSource; 028 import javax.xml.transform.sax.SAXResult; 029 import javax.xml.transform.sax.SAXSource; 030 import javax.xml.transform.stax.StAXResult; 031 import javax.xml.transform.stax.StAXSource; 032 import javax.xml.transform.stream.StreamResult; 033 import javax.xml.transform.stream.StreamSource; 034 035 import org.jdom.Document; 036 import org.jdom.input.DOMBuilder; 037 import org.jdom.input.SAXBuilder; 038 import org.jdom.input.SAXHandler; 039 import org.jdom.output.DOMOutputter; 040 import org.jdom.output.Format; 041 import org.jdom.output.XMLOutputter; 042 import org.w3c.dom.Node; 043 import org.xml.sax.InputSource; 044 045 import com.mockrunner.base.NestedApplicationException; 046 import com.mockrunner.util.common.StreamUtil; 047 048 /** 049 * Mock implementation of <code>MockSQLXML</code>. 050 * Uses JDOM for XML handling. 051 */ 052 public class MockSQLXML implements SQLXML, Cloneable 053 { 054 private DocumentBuilder domParser; 055 private SAXParser saxParser; 056 private XMLOutputFactory outputFactory; 057 private XMLInputFactory inputFactory; 058 private SAXBuilder jdomParser; 059 private DOMBuilder jdomDOMBuilder; 060 private XMLOutputter xmlPrintOutputter; 061 private DOMOutputter domOutputter; 062 private XMLOutputter xmlCompareOutputter; 063 private Object content; 064 private boolean wasFreeCalled; 065 private boolean wasWriteMethodCalled; 066 private boolean wasReadMethodCalled; 067 068 public MockSQLXML() 069 { 070 createXMLObjects(); 071 content = null; 072 wasFreeCalled = false; 073 wasWriteMethodCalled = false; 074 wasReadMethodCalled = false; 075 } 076 077 public MockSQLXML(String stringContent) 078 { 079 createXMLObjects(); 080 content = stringContent; 081 wasFreeCalled = false; 082 wasWriteMethodCalled = false; 083 wasReadMethodCalled = false; 084 } 085 086 public MockSQLXML(Reader readerContent) 087 { 088 createXMLObjects(); 089 content = StreamUtil.getReaderAsString(readerContent); 090 wasFreeCalled = false; 091 wasWriteMethodCalled = false; 092 wasReadMethodCalled = false; 093 } 094 095 public MockSQLXML(InputStream inputStreamContent) 096 { 097 createXMLObjects(); 098 content = StreamUtil.getStreamAsByteArray(inputStreamContent); 099 wasFreeCalled = false; 100 wasWriteMethodCalled = false; 101 wasReadMethodCalled = false; 102 } 103 104 public MockSQLXML(org.w3c.dom.Document documentContent) 105 { 106 createXMLObjects(); 107 content = documentContent; 108 wasFreeCalled = false; 109 wasWriteMethodCalled = false; 110 wasReadMethodCalled = false; 111 } 112 113 protected DocumentBuilder createDocumentBuilder() 114 { 115 try 116 { 117 return DocumentBuilderFactory.newInstance().newDocumentBuilder(); 118 } 119 catch(ParserConfigurationException exc) 120 { 121 throw new NestedApplicationException(exc); 122 } 123 } 124 125 protected SAXParser createSAXParser() 126 { 127 try 128 { 129 return SAXParserFactory.newInstance().newSAXParser(); 130 } 131 catch(Exception exc) 132 { 133 throw new NestedApplicationException(exc); 134 } 135 } 136 137 protected XMLOutputFactory createXMLOutputFactory() 138 { 139 return XMLOutputFactory.newInstance(); 140 } 141 142 protected XMLInputFactory createXMLInputFactory() 143 { 144 return XMLInputFactory.newInstance(); 145 } 146 147 protected SAXBuilder createJDOMSAXBuilder() 148 { 149 SAXBuilder builder = new SAXBuilder(); 150 builder.setValidation(false); 151 return builder; 152 } 153 154 protected DOMBuilder createJDOMDOMBuilder() 155 { 156 return new DOMBuilder(); 157 } 158 159 protected XMLOutputter createJDOMXMLPrintOutputter() 160 { 161 return new XMLOutputter(Format.getPrettyFormat()); 162 } 163 164 protected XMLOutputter createJDOMXMLCompareOutputter() 165 { 166 Format format = Format.getCompactFormat(); 167 format.setOmitDeclaration(true); 168 format.setOmitEncoding(true); 169 return new XMLOutputter(format); 170 } 171 172 protected DOMOutputter createJDOMDOMOutputter() 173 { 174 return new DOMOutputter(); 175 } 176 177 /** 178 * Returns the XML content as a string without affecting the state of 179 * the object. This method can be called multiple times unlike 180 * the <code>get</code> methods of <code>java.sql.SQLXML</code>. 181 * @return the XML content as a string 182 */ 183 public String getContentAsString() 184 { 185 try 186 { 187 return contentToString(); 188 } 189 catch(Exception exc) 190 { 191 throw new NestedApplicationException(exc); 192 } 193 } 194 195 /** 196 * Returns the XML content as an <code>InputStream</code> without affecting the 197 * state of the object. This method can be called multiple times unlike 198 * the <code>get</code> methods of <code>java.sql.SQLXML</code>. 199 * @return the XML content as an <code>InputStream</code> 200 */ 201 public InputStream getContentAsInputStream() throws SQLException 202 { 203 try 204 { 205 return contentToInputStream(); 206 } 207 catch(Exception exc) 208 { 209 throw new NestedApplicationException(exc); 210 } 211 } 212 213 /** 214 * Returns the XML content as a <code>Reader</code> without affecting the 215 * state of the object. This method can be called multiple times unlike 216 * the <code>get</code> methods of <code>java.sql.SQLXML</code>. 217 * @return the XML content as a <code>Reader</code> 218 */ 219 public Reader getContentAsReader() throws SQLException 220 { 221 try 222 { 223 return contentToReader(); 224 } 225 catch(Exception exc) 226 { 227 throw new NestedApplicationException(exc); 228 } 229 } 230 231 /** 232 * Returns the XML content as a W3C <code>Document</code> without affecting 233 * the state of the object. This method can be called multiple times unlike 234 * the <code>get</code> methods of <code>java.sql.SQLXML</code>. 235 * @return the XML content as a W3C <code>Document</code> 236 */ 237 public org.w3c.dom.Document getContentAsW3CDocument() 238 { 239 try 240 { 241 return contentToW3CDocument(); 242 } 243 catch(Exception exc) 244 { 245 throw new NestedApplicationException(exc); 246 } 247 } 248 249 public InputStream getBinaryStream() throws SQLException 250 { 251 verifyRead(); 252 wasReadMethodCalled = true; 253 try 254 { 255 return contentToInputStream(); 256 } 257 catch(Exception exc) 258 { 259 throw new SQLException(exc); 260 } 261 } 262 263 public Reader getCharacterStream() throws SQLException 264 { 265 verifyRead(); 266 wasReadMethodCalled = true; 267 try 268 { 269 return contentToReader(); 270 } 271 catch(Exception exc) 272 { 273 throw new SQLException(exc); 274 } 275 } 276 277 public Source getSource(Class sourceClass) throws SQLException 278 { 279 verifyRead(); 280 wasReadMethodCalled = true; 281 try 282 { 283 if(null == sourceClass || StreamSource.class.equals(sourceClass)) 284 { 285 return new StreamSource(contentToInputStream()); 286 } 287 if(DOMSource.class.equals(sourceClass)) 288 { 289 return new DOMSource(contentToW3CDocument()); 290 } 291 if(SAXSource.class.equals(sourceClass)) 292 { 293 return new SAXSource(saxParser.getXMLReader(), new InputSource(contentToInputStream())); 294 } 295 if(StAXSource.class.equals(sourceClass)) 296 { 297 return new StAXSource(contentToXMLStreamReader()); 298 } 299 } 300 catch(Exception exc) 301 { 302 throw new SQLException(exc); 303 } 304 throw new SQLException(sourceClass.getName() + " not supported as Source"); 305 } 306 307 public String getString() throws SQLException 308 { 309 verifyRead(); 310 wasReadMethodCalled = true; 311 try 312 { 313 return contentToString(); 314 } 315 catch(Exception exc) 316 { 317 throw new SQLException(exc); 318 } 319 } 320 321 public OutputStream setBinaryStream() throws SQLException 322 { 323 verifyWrite(); 324 wasWriteMethodCalled = true; 325 content = new ByteArrayOutputStream(); 326 return (OutputStream)content; 327 } 328 329 public Writer setCharacterStream() throws SQLException 330 { 331 verifyWrite(); 332 wasWriteMethodCalled = true; 333 content = new StringWriter(); 334 return (Writer)content; 335 } 336 337 public Result setResult(Class resultClass) throws SQLException 338 { 339 verifyWrite(); 340 wasWriteMethodCalled = true; 341 if(null == resultClass || StreamResult.class.equals(resultClass)) 342 { 343 content = new ByteArrayOutputStream(); 344 return new StreamResult((OutputStream)content); 345 } 346 if(DOMResult.class.equals(resultClass)) 347 { 348 org.w3c.dom.Document document = domParser.newDocument(); 349 content = new DOMResult(document); 350 return (DOMResult)content; 351 } 352 if(SAXResult.class.equals(resultClass)) 353 { 354 content = new SAXHandler(); 355 return new SAXResult((SAXHandler)content); 356 } 357 if(StAXResult.class.equals(resultClass)) 358 { 359 XMLStreamWriter xmlWriter; 360 ByteArrayOutputStream outStream = new ByteArrayOutputStream(); 361 try 362 { 363 xmlWriter = outputFactory.createXMLStreamWriter(outStream); 364 } 365 catch(XMLStreamException exc) 366 { 367 throw new SQLException(exc); 368 } 369 content = new StreamWriterOutputStreamMapping(xmlWriter, outStream); 370 return new StAXResult(xmlWriter); 371 } 372 throw new SQLException(resultClass.getName() + " not supported as Result"); 373 } 374 375 public void setString(String value) throws SQLException 376 { 377 verifyWrite(); 378 wasWriteMethodCalled = true; 379 content = value; 380 } 381 382 public void free() throws SQLException 383 { 384 wasFreeCalled = true; 385 } 386 387 /** 388 * Returns if {@link #free} has been called. 389 * @return <code>true</code> if {@link #free} has been called, 390 * <code>false</code> otherwise 391 */ 392 public boolean wasFreeCalled() 393 { 394 return wasFreeCalled; 395 } 396 397 /** 398 * Returns if this object is readable. 399 * @return <code>true</code> this object is readable, 400 * <code>false</code> otherwise 401 */ 402 public boolean isReadable() 403 { 404 return !(wasFreeCalled || wasReadMethodCalled); 405 } 406 407 /** 408 * Returns if this object is writeable. 409 * @return <code>true</code> this object is writeable, 410 * <code>false</code> otherwise 411 */ 412 public boolean isWriteable() 413 { 414 return !(wasFreeCalled || wasWriteMethodCalled); 415 } 416 417 public boolean equals(Object otherObject) 418 { 419 if(null == otherObject) return false; 420 if(this == otherObject) return true; 421 if(!otherObject.getClass().equals(this.getClass())) return false; 422 MockSQLXML otherSQLXML = (MockSQLXML)otherObject; 423 if(wasFreeCalled != otherSQLXML.wasFreeCalled()) return false; 424 if(null == content && null == otherSQLXML.content) return true; 425 if(null == content || null == otherSQLXML.content) return false; 426 try 427 { 428 Document thisContent = contentToJDOMDocument(); 429 Document otherContent = otherSQLXML.contentToJDOMDocument(); 430 if(null == thisContent || null == otherContent) return false; 431 String thisContentAsString = xmlCompareOutputter.outputString(thisContent); 432 String otherContentAsString = xmlCompareOutputter.outputString(otherContent); 433 return thisContentAsString.equals(otherContentAsString); 434 } 435 catch(Exception exc) 436 { 437 return false; 438 } 439 } 440 441 public int hashCode() 442 { 443 int hashCode = 17; 444 if(null != content) 445 { 446 try 447 { 448 Document document = contentToJDOMDocument(); 449 if(null == document) return hashCode; 450 String documentAsString = xmlCompareOutputter.outputString(document); 451 if(null != documentAsString) hashCode = (31 * hashCode) + documentAsString.hashCode(); 452 } 453 catch(Exception exc) 454 { 455 456 } 457 } 458 hashCode = (31 * hashCode) + (wasFreeCalled ? 31 : 62); 459 return hashCode; 460 } 461 462 public Object clone() 463 { 464 try 465 { 466 MockSQLXML other = (MockSQLXML)super.clone(); 467 other.domParser = createDocumentBuilder(); 468 other.saxParser = createSAXParser(); 469 other.outputFactory = createXMLOutputFactory(); 470 other.inputFactory = createXMLInputFactory(); 471 other.jdomParser = createJDOMSAXBuilder(); 472 other.jdomDOMBuilder = createJDOMDOMBuilder(); 473 other.xmlPrintOutputter = createJDOMXMLPrintOutputter(); 474 other.domOutputter = createJDOMDOMOutputter(); 475 other.xmlCompareOutputter = createJDOMXMLCompareOutputter(); 476 if(null != content) 477 { 478 try 479 { 480 Document document = contentToJDOMDocument(); 481 other.content = document.clone(); 482 } 483 catch(Exception exc) 484 { 485 other.content = null; 486 } 487 } 488 return other; 489 } 490 catch(CloneNotSupportedException exc) 491 { 492 throw new NestedApplicationException(exc); 493 } 494 } 495 496 public String toString() 497 { 498 StringBuffer buffer = new StringBuffer("XML data:\n"); 499 if(null == content) 500 { 501 buffer.append("null"); 502 } 503 else 504 { 505 try 506 { 507 Document document = contentToJDOMDocument(); 508 if(null != document) buffer.append(document.toString()); 509 } 510 catch(Exception exc) 511 { 512 buffer.append(exc.getMessage()); 513 } 514 } 515 return buffer.toString(); 516 } 517 518 private void createXMLObjects() 519 { 520 domParser = createDocumentBuilder(); 521 saxParser = createSAXParser(); 522 outputFactory = createXMLOutputFactory(); 523 inputFactory = createXMLInputFactory(); 524 jdomParser = createJDOMSAXBuilder(); 525 jdomDOMBuilder = createJDOMDOMBuilder(); 526 xmlPrintOutputter = createJDOMXMLPrintOutputter(); 527 domOutputter = createJDOMDOMOutputter(); 528 xmlCompareOutputter = createJDOMXMLCompareOutputter(); 529 } 530 531 private void verifyWrite() throws SQLException 532 { 533 if(!isWriteable()) 534 { 535 throw new SQLException("not writeable"); 536 } 537 } 538 539 private void verifyRead() throws SQLException 540 { 541 if(!isReadable()) 542 { 543 throw new SQLException("not readable"); 544 } 545 if(null == content) 546 { 547 throw new SQLException("no content"); 548 } 549 } 550 551 private Document contentToJDOMDocument() throws Exception 552 { 553 Document jdomDocument = null; 554 if(content instanceof Document) 555 { 556 jdomDocument = (Document)content; 557 } 558 else if(content instanceof String) 559 { 560 jdomDocument = jdomParser.build(new StringReader((String)content)); 561 } 562 else if(content instanceof StringWriter) 563 { 564 jdomDocument = jdomParser.build(new StringReader(((StringWriter)content).toString())); 565 } 566 else if(content instanceof ByteArrayOutputStream) 567 { 568 jdomDocument = jdomParser.build(new ByteArrayInputStream(((ByteArrayOutputStream)content).toByteArray())); 569 } 570 else if(content instanceof byte[]) 571 { 572 jdomDocument = jdomParser.build(new ByteArrayInputStream((byte[])content)); 573 } 574 else if(content instanceof org.w3c.dom.Document) 575 { 576 jdomDocument = jdomDOMBuilder.build((org.w3c.dom.Document)content); 577 } 578 else if(content instanceof DOMResult) 579 { 580 Node node = ((DOMResult)content).getNode(); 581 org.w3c.dom.Document document = null; 582 if(node instanceof org.w3c.dom.Document) 583 { 584 document = (org.w3c.dom.Document)node; 585 } 586 else 587 { 588 document = domParser.newDocument(); 589 document.appendChild(document.importNode(node, true)); 590 } 591 jdomDocument = jdomDOMBuilder.build(document); 592 } 593 else if(content instanceof SAXHandler) 594 { 595 jdomDocument = ((SAXHandler)content).getDocument(); 596 } 597 else if(content instanceof StreamWriterOutputStreamMapping) 598 { 599 XMLStreamWriter xmlWriter = ((StreamWriterOutputStreamMapping)content).getStreamWriter(); 600 xmlWriter.flush(); 601 xmlWriter.close(); 602 ByteArrayOutputStream outStream = ((StreamWriterOutputStreamMapping)content).getOutputStream(); 603 jdomDocument = jdomParser.build(new ByteArrayInputStream(outStream.toByteArray())); 604 } 605 return jdomDocument; 606 } 607 608 private String contentToString() throws Exception 609 { 610 Document jdomDocument = contentToJDOMDocument(); 611 if(null != jdomDocument) 612 { 613 return xmlPrintOutputter.outputString(jdomDocument); 614 } 615 return null; 616 } 617 618 private Reader contentToReader() throws Exception 619 { 620 Document jdomDocument = contentToJDOMDocument(); 621 if(null != jdomDocument) 622 { 623 return new StringReader(xmlPrintOutputter.outputString(jdomDocument)); 624 } 625 return null; 626 } 627 628 private InputStream contentToInputStream() throws Exception 629 { 630 Document jdomDocument = contentToJDOMDocument(); 631 if(null != jdomDocument) 632 { 633 ByteArrayOutputStream outStream = new ByteArrayOutputStream(); 634 xmlPrintOutputter.output(jdomDocument, outStream); 635 outStream.flush(); 636 return new ByteArrayInputStream(outStream.toByteArray()); 637 } 638 return null; 639 } 640 641 private org.w3c.dom.Document contentToW3CDocument() throws Exception 642 { 643 Document jdomDocument = contentToJDOMDocument(); 644 if(null != jdomDocument) 645 { 646 return domOutputter.output(jdomDocument); 647 } 648 return null; 649 } 650 651 private XMLStreamReader contentToXMLStreamReader() throws Exception 652 { 653 Document jdomDocument = contentToJDOMDocument(); 654 if(null != jdomDocument) 655 { 656 ByteArrayOutputStream outStream = new ByteArrayOutputStream(); 657 xmlPrintOutputter.output(jdomDocument, outStream); 658 outStream.flush(); 659 InputStream inStream = new ByteArrayInputStream(outStream.toByteArray()); 660 return inputFactory.createXMLStreamReader(inStream); 661 } 662 return null; 663 } 664 665 private class StreamWriterOutputStreamMapping 666 { 667 private XMLStreamWriter streamWriter; 668 private ByteArrayOutputStream outputStream; 669 670 public StreamWriterOutputStreamMapping(XMLStreamWriter streamWriter, ByteArrayOutputStream outputStream) 671 { 672 this.streamWriter = streamWriter; 673 this.outputStream = outputStream; 674 } 675 676 public XMLStreamWriter getStreamWriter() 677 { 678 return streamWriter; 679 } 680 681 public ByteArrayOutputStream getOutputStream() 682 { 683 return outputStream; 684 } 685 } 686 }