001    /*
002    // $Id: //open/util/resgen/src/org/eigenbase/xom/wrappers/GenericDOMParser.java#6 $
003    // Package org.eigenbase.xom is an XML Object Mapper.
004    // Copyright (C) 2005-2005 The Eigenbase Project
005    // Copyright (C) 2005-2005 Disruptive Tech
006    // Copyright (C) 2005-2005 LucidEra, Inc.
007    // Portions Copyright (C) 2001-2005 Kana Software, Inc. and others.
008    //
009    // This library is free software; you can redistribute it and/or modify it
010    // under the terms of the GNU Lesser General Public License as published by the
011    // Free Software Foundation; either version 2 of the License, or (at your
012    // option) any later version approved by The Eigenbase Project.
013    //
014    // This library is distributed in the hope that it will be useful,
015    // but WITHOUT ANY WARRANTY; without even the implied warranty of
016    // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
017    // GNU Lesser General Public License for more details.
018    //
019    // You should have received a copy of the GNU Lesser General Public License
020    // along with this library; if not, write to the Free Software
021    // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
022    */
023    
024    package org.eigenbase.xom.wrappers;
025    
026    import org.eigenbase.xom.*;
027    import org.w3c.dom.*;
028    import org.xml.sax.ErrorHandler;
029    import org.xml.sax.InputSource;
030    import org.xml.sax.SAXException;
031    import org.xml.sax.SAXParseException;
032    
033    import java.io.*;
034    import java.net.URL;
035    
036    /**
037     * A <code>GenericDOMParser</code> is an abstract base class for {@link
038     * XercesDOMParser} and {@link JaxpDOMParser}.
039     *
040     * @author jhyde
041     * @since Aug 29, 2002
042     * @version $Id: //open/util/resgen/src/org/eigenbase/xom/wrappers/GenericDOMParser.java#6 $
043     **/
044    abstract class GenericDOMParser
045        implements ErrorHandler, org.eigenbase.xom.Parser, Locator
046    {
047    
048        // Used for capturing error messages as they occur.
049        StringWriter errorBuffer = null;
050        PrintWriter errorOut = null;
051    
052        /** The document which spawns elements. The constructor of the derived
053         * class must set this. **/
054        protected Document document;
055    
056        static final String LOAD_EXTERNAL_DTD_FEATURE =
057                "http://apache.org/xml/features/nonvalidating/load-external-dtd";
058        static final String VALIDATION_FEATURE =
059                "http://xml.org/sax/features/validation";
060        private boolean keepPositions;
061        private Annotator annotator;
062    
063        public DOMWrapper create(String tagName) {
064            Element element = document.createElement(tagName);
065            return new W3CDOMWrapper(element, this);
066        }
067    
068        public DOMWrapper parse(InputStream is) throws XOMException {
069            TeeInputStream tis = new TeeInputStream(is);
070            InputSource source = new InputSource(tis);
071            Document document = parseInputSource(source);
072            final W3CDOMWrapper wrapper =
073                new W3CDOMWrapper(document.getDocumentElement(), this);
074            if (keepPositions) {
075                String xmlString = new String(tis.getBytes());
076                annotator = new Annotator(xmlString, wrapper);
077            }
078            return wrapper;
079        }
080    
081        public void setKeepPositions(boolean keepPositions) {
082            this.keepPositions = keepPositions;
083        }
084    
085        public boolean isKeepPositions() {
086            return keepPositions;
087        }
088    
089        public DOMWrapper parse(String xmlString) throws XOMException {
090            final DOMWrapper wrapper = parse(new StringReader(xmlString));
091            if (keepPositions) {
092                annotator = new Annotator(xmlString, wrapper);
093            }
094            return wrapper;
095        }
096    
097        public DOMWrapper parse(Reader reader) throws XOMException {
098            Document document = parseInputSource(new InputSource(reader));
099            return new W3CDOMWrapper(document.getDocumentElement(), this);
100        }
101    
102        /**
103         * Parses the specified URI and returns the document.
104         * @param in Input source
105         * @return Document
106         * @throws org.eigenbase.xom.XOMException on error
107         */
108        protected abstract Document parseInputSource(InputSource in)
109                throws XOMException;
110    
111        /** Warning. */
112        public void warning(SAXParseException ex) {
113            errorOut.println("[Warning] " +
114                    getLocationString(ex) + ": " +
115                    ex.getMessage());
116        }
117    
118        /** Error. */
119        public void error(SAXParseException ex) {
120            errorOut.println("[Error] " +
121                    getLocationString(ex) + ": " +
122                    ex.getMessage());
123        }
124    
125        /** Fatal error. */
126        public void fatalError(SAXParseException ex)
127                throws SAXException {
128            errorOut.println("[Fatal Error] " +
129                    getLocationString(ex) + ": " +
130                    ex.getMessage());
131            throw ex;
132        }
133    
134        /** Returns a string of the location.
135         * @param ex Exception
136         * @return Location string, e.g. "file.xml:4:72"
137         */
138        private String getLocationString(SAXParseException ex) {
139            StringBuffer str = new StringBuffer();
140    
141            String systemId = ex.getSystemId();
142            if (systemId != null) {
143                int index = systemId.lastIndexOf('/');
144                if (index != -1) {
145                    systemId = systemId.substring(index + 1);
146                }
147                str.append(systemId);
148            }
149            str.append(':');
150            str.append(ex.getLineNumber());
151            str.append(':');
152            str.append(ex.getColumnNumber());
153            return str.toString();
154        }
155    
156        // implement Parser
157        public DOMWrapper parse(URL url)
158                throws XOMException {
159            try {
160                return parse(new BufferedInputStream(url.openStream()));
161            } catch (IOException ex) {
162                throw new XOMException(ex, "Document parse failed");
163            }
164        }
165    
166        // Helper: reset the error buffer to prepare for a new parse.
167        protected void prepareParse() {
168            errorBuffer = new StringWriter();
169            errorOut = new PrintWriter(errorBuffer);
170        }
171    
172        // Helper: throw an exception with messages of any errors
173        // accumulated during the parse.
174        protected void handleErrors() throws XOMException {
175            errorOut.flush();
176            String errorStr = errorBuffer.toString();
177            if (errorStr.length() > 0) {
178                throw new XOMException("Document parse failed: " + errorStr);
179            }
180        }
181    
182        // implement Locator
183        public Location getLocation(DOMWrapper wrapper) {
184            return annotator.getLocation(wrapper);
185        }
186    
187        /**
188         * Input stream that keeps a copy of every byte that flows through it.
189         */
190        private static class TeeInputStream extends FilterInputStream {
191            private final ByteArrayOutputStream baos = new ByteArrayOutputStream();
192    
193            TeeInputStream(InputStream in) {
194                super(in);
195            }
196    
197            public int read() throws IOException {
198                int x = super.read();
199                baos.write(x);
200                return x;
201            }
202    
203            /**
204             * Returns the bytes that have been read from this stream.
205             *
206             * @return Array of bytes that have been read from this stream
207             */
208            public byte[] getBytes() {
209                return baos.toByteArray();
210            }
211        }
212    }
213    
214    // End GenericDOMParser.java