blob: bd92e4223d96468e12a1b800231face1629c9dc6 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2002, 2005 Object Factory Inc.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Object Factory Inc. - Initial implementation
*******************************************************************************/
package org.eclipse.ant.internal.ui.dtd;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.eclipse.ant.internal.ui.dtd.schema.SchemaFactory;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXNotRecognizedException;
import org.xml.sax.SAXNotSupportedException;
import org.xml.sax.XMLReader;
import org.xml.sax.ext.DeclHandler;
/**
* Simple parser for DTDs. Returns ISchema representing the DTD.
*
* To parse a DTD, you must parse an XML document. The <code>parseDTD()</code> method builds a temporary XML document in memory that refers to or
* includes the DTD.
*
* There is no dependency in this package on any code outside the package except XMLReader.
*
* To hide the underlying parser, XML parser exceptions are wrapped by a ParseError. Unless debugging, the two string constants are sufficient to
* determine the cause of the error.
*
* @author Bob Foster
*/
public class Parser {
/** ParseError message when system parser doesn't do the job */
public static final String NOT_SUPPORTED = AntDTDMessages.Parser_XML_parser_does_not_support_DeclHandler_1;
/**
* ParseError message for a well-formed or validation error in XML or DTD. Currently not returned.
*/
public static final String PARSE_ERROR = AntDTDMessages.Parser_Error_parsing_XML_document_or_DTD_2;
private static final String INTERNAL = "internal://usereader.objfac.com"; //$NON-NLS-1$
/**
* Parse the XML document at the input source and return a document walker that can be used to validate any document with the same DTD (internal
* and external) or provide user assistance for this document.
*
* @param inputSource
* Source for XML document to start DTD parse. Must contain a DOCTYPE declaration with internal or external subset, or both.
* @param entityResolver
* EntityResolver or null.
* @return schema for document.
* @throws ParseError
* for NOT_SUPPORTED or PARSE_ERROR.
* @throws IOException
*/
public ISchema parse(InputSource inputSource, EntityResolver entityResolver) throws ParseError, IOException {
XMLReader parser = null;
SchemaFactory factory = new SchemaFactory();
try {
parser = getXMLReader();
DeclHandler handler = factory;
parser.setProperty("http://xml.org/sax/properties/declaration-handler", handler); //$NON-NLS-1$
if (entityResolver != null) {
parser.setEntityResolver(entityResolver);
}
parser.parse(inputSource);
}
catch (SAXNotRecognizedException e) {
throw new ParseError(NOT_SUPPORTED, e);
}
catch (SAXNotSupportedException e) {
throw new ParseError(NOT_SUPPORTED, e);
}
catch (SAXException e) {
// Don't care about errors in XML, so just fall thru.
// If parse failed in DTD, may have incomplete schema,
// but this is better than no schema.
factory.setErrorException(e);
}
return factory.getSchema();
}
private XMLReader getXMLReader() throws ParseError {
SAXParser parser = null;
try {
parser = SAXParserFactory.newInstance().newSAXParser();
return parser.getXMLReader();
}
catch (ParserConfigurationException e) {
throw new ParseError(e.getMessage(), e);
}
catch (SAXException e) {
throw new ParseError(e.getMessage(), e);
}
}
/**
* Parse the XML document at the argument URL and return a document walker that can be used to validate any document with the same DTD (internal
* and external) or provide user assistance for this document.
*
* @param url
* Of XML document to start DTD parse. Must contain a DOCTYPE declaration with internal or external subset, or both.
* @return IWalker that can be used to traverse document.
* @throws ParseError
* for NOT_SUPPORTED or PARSE_ERROR.
* @throws IOException
*/
public ISchema parse(String url) throws ParseError, IOException {
return parse(new InputSource(url), null);
}
/**
* Parse the XML document using the argument reader and return a document walker that can be used to validate any document with the same DTD
* (internal and external) or provide user assistance for this document.
*
* @param reader
* Reader for XML document to start DTD parse. Must contain a DOCTYPE declaration with internal or external subset, or both.
* @return IWalker that can be used to traverse document.
* @throws ParseError
* for NOT_SUPPORTED or PARSE_ERROR.
* @throws IOException
*/
public ISchema parse(Reader reader) throws ParseError, IOException {
return parse(new InputSource(reader), null);
}
/**
* Parse the DTD with the given public and system ids and return a document walker that can be used to validate or provide user assistance for any
* document with the same external DTD and no internal subset.
*
* @param pub
* PUBLIC id of DTD.
* @param sys
* SYSTEM id of DTD.
* @param root
* Plausible root element qname. Any name will do but a name that will not cause a validation error is preferred.
* @return IWalker that can be used to traverse document.
* @throws ParseError
* for NOT_SUPPORTED or PARSE_ERROR.
* @throws IOException
*/
public ISchema parseDTD(String pub, String sys, String root) throws ParseError, IOException {
return parse(new InputSource(new DTDReader(pub, sys, root)), null);
}
/**
* Parse the DTD from the reader and return a document walker that can be used to validate or provide user assistance for any document with the
* same external DTD and no internal subset.
*
* @param reader
* Reader for external subset DTD
* @param root
* Plausible root element qname. Any name will do but a name that will not cause a validation error is preferred.
* @return ISchema that can be used to traverse document.
* @throws ParseError
* for NOT_SUPPORTED or PARSE_ERROR.
* @throws IOException
*/
public ISchema parseDTD(Reader reader, String root) throws ParseError, IOException {
return parse(new InputSource(new DTDReader(INTERNAL, INTERNAL, root)), new DTDEntityResolver(reader));
}
private static class DTDReader extends Reader {
private Reader fDelegate;
public DTDReader(String pub, String sys, String root) {
String document = "<!DOCTYPE " + root + " PUBLIC '" + pub + "' '" + sys + "'><" + root + "/>"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
fDelegate = new StringReader(document);
}
/**
* @see java.io.Reader#close()
*/
@Override
public void close() throws IOException {
fDelegate.close();
}
/*
* (non-Javadoc)
*
* @see java.io.Reader#read(char[], int, int)
*/
@Override
public int read(char[] cbuf, int off, int len) throws IOException {
return fDelegate.read(cbuf, off, len);
}
}
private static class DTDEntityResolver implements EntityResolver {
private Reader reader;
public DTDEntityResolver(Reader reader) {
this.reader = reader;
}
/**
* @see org.xml.sax.EntityResolver#resolveEntity(java.lang.String, java.lang.String)
*/
@Override
public InputSource resolveEntity(String publicId, String systemId) {
if (publicId.equals(INTERNAL) && systemId.equals(INTERNAL))
return new InputSource(reader);
return null;
}
}
}