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 }