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    }