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
024package org.eigenbase.xom.wrappers;
025
026import org.eigenbase.xom.*;
027import org.w3c.dom.*;
028import org.xml.sax.ErrorHandler;
029import org.xml.sax.InputSource;
030import org.xml.sax.SAXException;
031import org.xml.sax.SAXParseException;
032
033import java.io.*;
034import 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 **/
044abstract 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