| /* |
| * Copyright (c) 1998, 2020 Oracle and/or its affiliates. All rights reserved. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0, |
| * or the Eclipse Distribution License v. 1.0 which is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause |
| */ |
| |
| // Contributors: |
| // Oracle - initial API and implementation from Oracle TopLink |
| package org.eclipse.persistence.platform.xml.jaxp; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.Reader; |
| import java.net.URL; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| |
| import javax.xml.parsers.DocumentBuilder; |
| import javax.xml.parsers.DocumentBuilderFactory; |
| import javax.xml.parsers.ParserConfigurationException; |
| import javax.xml.transform.Source; |
| import javax.xml.transform.Transformer; |
| import javax.xml.transform.TransformerException; |
| import javax.xml.transform.TransformerFactory; |
| import javax.xml.transform.sax.SAXResult; |
| import javax.xml.validation.Schema; |
| |
| import org.eclipse.persistence.internal.helper.XMLHelper; |
| import org.eclipse.persistence.platform.xml.DefaultErrorHandler; |
| import org.eclipse.persistence.platform.xml.SAXDocumentBuilder; |
| import org.eclipse.persistence.platform.xml.XMLParser; |
| import org.eclipse.persistence.platform.xml.XMLPlatformException; |
| import org.w3c.dom.Document; |
| import org.xml.sax.EntityResolver; |
| import org.xml.sax.ErrorHandler; |
| import org.xml.sax.InputSource; |
| import org.xml.sax.SAXException; |
| import org.xml.sax.SAXParseException; |
| |
| /** |
| * <p><b>Purpose</b>: An implementation of XMLParser using JAXP 1.3 APIs.</p> |
| * |
| * <p>JAXPParser is NOT thread safe.</p> |
| */ |
| public class JAXPParser implements XMLParser { |
| private static final String SCHEMA_LANGUAGE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage"; |
| private static final String XML_SCHEMA = "http://www.w3.org/2001/XMLSchema"; |
| private static final String JAXP_SCHEMA_SOURCE = "http://java.sun.com/xml/jaxp/properties/schemaSource"; |
| private DocumentBuilderFactory documentBuilderFactory; |
| private EntityResolver entityResolver; |
| private ErrorHandler errorHandler; |
| private DocumentBuilder documentBuilder; |
| private TransformerFactory transformerFactory; |
| |
| /** |
| * Default constructor. |
| */ |
| public JAXPParser() { |
| super(); |
| errorHandler = DefaultErrorHandler.getInstance(); |
| } |
| |
| /** |
| * This constructor can increase performance by providing existing documentBuilderFactory and errorHandler. |
| * |
| * @param documentBuilderFactory existing document builder factory |
| * @param errorHandler existing error handler |
| */ |
| public JAXPParser(DocumentBuilderFactory documentBuilderFactory, ErrorHandler errorHandler) { |
| super(); |
| |
| this.documentBuilderFactory = documentBuilderFactory; |
| if (null != errorHandler) { |
| this.errorHandler = errorHandler; |
| } else { |
| this.errorHandler = DefaultErrorHandler.getInstance(); |
| } |
| } |
| |
| /** |
| * This constructor provides way to specify features for parser. |
| * |
| * @param parserFeatures features for parser |
| */ |
| public JAXPParser(Map<String, Boolean> parserFeatures) { |
| this(); |
| loadDocumentBuilderFactory(); |
| try { |
| if(null != parserFeatures) { |
| for(Entry<String, Boolean> entry : parserFeatures.entrySet()) { |
| documentBuilderFactory.setFeature(entry.getKey(), entry.getValue()); |
| } |
| } |
| } catch(Exception e) { |
| throw XMLPlatformException.xmlPlatformParseException(e); |
| } |
| } |
| |
| private void loadDocumentBuilderFactory() { |
| documentBuilderFactory = XMLHelper.createDocumentBuilderFactory(false); |
| setNamespaceAware(true); |
| setWhitespacePreserving(false); |
| } |
| |
| /** |
| * Changes namespaceAware behavior of the parser. |
| * |
| * @param isNamespaceAware if the parser should be namespace aware |
| */ |
| @Override |
| public void setNamespaceAware(boolean isNamespaceAware) { |
| if (null == documentBuilderFactory) { |
| loadDocumentBuilderFactory(); |
| } |
| documentBuilderFactory.setNamespaceAware(isNamespaceAware); |
| } |
| |
| /** |
| * Changes preservation of white spaces. |
| * |
| * @param isWhitespacePreserving if the parser should preserve white spaces |
| */ |
| @Override |
| public void setWhitespacePreserving(boolean isWhitespacePreserving) { |
| if (null == documentBuilderFactory) { |
| loadDocumentBuilderFactory(); |
| } |
| documentBuilderFactory.setIgnoringElementContentWhitespace(!isWhitespacePreserving); |
| } |
| |
| /** |
| * Returns validtion mode of the parser. |
| * |
| * @return validation mode of the parser |
| */ |
| @Override |
| public int getValidationMode() { |
| if (null == documentBuilderFactory) { |
| loadDocumentBuilderFactory(); |
| } |
| if (!documentBuilderFactory.isValidating()) { |
| return XMLParser.NONVALIDATING; |
| } |
| |
| try { |
| if (null == documentBuilderFactory.getAttribute(SCHEMA_LANGUAGE)) { |
| return XMLParser.DTD_VALIDATION; |
| } |
| } catch (IllegalArgumentException e) { |
| return XMLParser.DTD_VALIDATION; |
| } |
| |
| return XMLParser.SCHEMA_VALIDATION; |
| } |
| |
| /** |
| * Sets validation mode of the parser. |
| * |
| * @param validationMode validation mode to set |
| */ |
| @Override |
| public void setValidationMode(int validationMode) { |
| if (null == documentBuilderFactory) { |
| loadDocumentBuilderFactory(); |
| } |
| switch (validationMode) { |
| case XMLParser.NONVALIDATING: { |
| documentBuilderFactory.setValidating(false); |
| // documentBuilderFactory.setAttribute(SCHEMA_LANGUAGE, null); |
| return; |
| } |
| case XMLParser.DTD_VALIDATION: { |
| documentBuilderFactory.setValidating(true); |
| XMLHelper.allowExternalDTDAccess(documentBuilderFactory, "all", false); |
| // documentBuilderFactory.setAttribute(SCHEMA_LANGUAGE, null); |
| return; |
| } |
| case XMLParser.SCHEMA_VALIDATION: { |
| try { |
| documentBuilderFactory.setAttribute(SCHEMA_LANGUAGE, XML_SCHEMA); |
| documentBuilderFactory.setValidating(true); |
| XMLHelper.allowExternalAccess(documentBuilderFactory, "all", false); |
| } catch (IllegalArgumentException e) { |
| // This parser does not support XML Schema validation so leave it as |
| // a non-validating parser. |
| } |
| return; |
| } |
| } |
| } |
| |
| /** |
| * Returns entity resolver of the parser. |
| * |
| * @return entity resolver of the parser |
| */ |
| @Override |
| public EntityResolver getEntityResolver() { |
| return entityResolver; |
| } |
| |
| /** |
| * Sets entity resolver for the parser. |
| * |
| * @param entityResolver entity resolver to set |
| */ |
| @Override |
| public void setEntityResolver(EntityResolver entityResolver) { |
| this.entityResolver = entityResolver; |
| } |
| |
| /** |
| * Returns error handler of the parser. |
| * |
| * @return error handler of the parser |
| */ |
| @Override |
| public ErrorHandler getErrorHandler() { |
| return errorHandler; |
| } |
| |
| /** |
| * Sets error handler for the parser. |
| * |
| * @param errorHandler error handler for the parser |
| */ |
| @Override |
| public void setErrorHandler(ErrorHandler errorHandler) { |
| this.errorHandler = errorHandler; |
| } |
| |
| /** |
| * Sets XML Schema for the parser. |
| * |
| * @param url url of the XMLSchema |
| * @throws XMLPlatformException exception occurred while setting XMLSchema |
| */ |
| @Override |
| public void setXMLSchema(URL url) throws XMLPlatformException { |
| if (null == url) { |
| return; |
| } |
| if (null == documentBuilderFactory) { |
| loadDocumentBuilderFactory(); |
| } |
| try { |
| documentBuilderFactory.setAttribute(SCHEMA_LANGUAGE, XML_SCHEMA); |
| documentBuilderFactory.setAttribute(JAXP_SCHEMA_SOURCE, url.toString()); |
| } catch (IllegalArgumentException e) { |
| // The attribute isn't supported so do nothing |
| } catch (Exception e) { |
| XMLPlatformException.xmlPlatformErrorResolvingXMLSchema(url, e); |
| } |
| } |
| |
| /** |
| * Sets XML Schema for the parser. |
| * |
| * @param schema schema for the parser |
| * @throws XMLPlatformException exception occurred while setting XMLSchema |
| */ |
| @Override |
| public void setXMLSchema(Schema schema) throws XMLPlatformException { |
| documentBuilderFactory.setSchema(schema); |
| } |
| |
| /** |
| * Returns XML Schema of the parser. |
| * |
| * @return schema of the parser |
| * @throws XMLPlatformException exception occurred while getting XMLSchema |
| */ |
| @Override |
| public Schema getXMLSchema() throws XMLPlatformException { |
| return documentBuilderFactory.getSchema(); |
| } |
| |
| /** |
| * Sets XML Schema(s) for the parser. |
| * |
| * @param schemas XML schemas to set |
| * @throws XMLPlatformException exception occurred while setting XMLSchema(s) |
| */ |
| @Override |
| public void setXMLSchemas(Object[] schemas) throws XMLPlatformException { |
| if ((null == schemas) || (schemas.length == 0)) { |
| return; |
| } |
| if (null == documentBuilderFactory) { |
| loadDocumentBuilderFactory(); |
| } |
| try { |
| documentBuilderFactory.setAttribute(SCHEMA_LANGUAGE, XML_SCHEMA); |
| documentBuilderFactory.setAttribute(JAXP_SCHEMA_SOURCE, schemas); |
| } catch (IllegalArgumentException e) { |
| // The attribute isn't supported so do nothing |
| } catch (Exception e) { |
| XMLPlatformException.xmlPlatformErrorResolvingXMLSchemas(schemas, e); |
| } |
| } |
| |
| /** |
| * Parses given input source. |
| * |
| * @param inputSource input source to parse |
| * @return parsed document |
| * @throws XMLPlatformException exception occurred while parsing input source |
| */ |
| @Override |
| public Document parse(InputSource inputSource) throws XMLPlatformException { |
| try { |
| return getDocumentBuilder().parse(inputSource); |
| } catch (SAXException e) { |
| throw XMLPlatformException.xmlPlatformParseException(e); |
| } catch (IOException e) { |
| throw XMLPlatformException.xmlPlatformParseException(e); |
| } |
| } |
| |
| /** |
| * Parses given file. |
| * |
| * @param file file to parse |
| * @return parsed document |
| * @throws XMLPlatformException exception occurred while parsing given file |
| */ |
| @Override |
| public Document parse(File file) throws XMLPlatformException { |
| try { |
| return getDocumentBuilder().parse(file); |
| } catch (SAXParseException e) { |
| throw XMLPlatformException.xmlPlatformSAXParseException(e); |
| } catch (SAXException e) { |
| throw XMLPlatformException.xmlPlatformParseException(e); |
| } catch (IOException e) { |
| throw XMLPlatformException.xmlPlatformFileNotFoundException(file, e); |
| } |
| } |
| |
| /** |
| * Parses given input stream. |
| * |
| * @param inputStream input stream to parse |
| * @return parsed document |
| * @throws XMLPlatformException exception occurred while parsing input stream |
| */ |
| @Override |
| public Document parse(InputStream inputStream) throws XMLPlatformException { |
| try { |
| return getDocumentBuilder().parse(inputStream); |
| } catch (SAXParseException e) { |
| throw XMLPlatformException.xmlPlatformSAXParseException(e); |
| } catch (SAXException e) { |
| throw XMLPlatformException.xmlPlatformParseException(e); |
| } catch (IOException e) { |
| throw XMLPlatformException.xmlPlatformParseException(e); |
| } |
| } |
| |
| /** |
| * Parses given reader. |
| * |
| * @param reader reader to parse |
| * @return parsed document |
| * @throws XMLPlatformException exception occurred while parsing given reader |
| */ |
| @Override |
| public Document parse(Reader reader) throws XMLPlatformException { |
| InputSource inputSource = new InputSource(reader); |
| return parse(inputSource); |
| } |
| |
| /** |
| * Parses given source. |
| * |
| * @param source source to parse |
| * @return parsed document |
| * @throws XMLPlatformException exception occurred while parsing given source |
| */ |
| @Override |
| public Document parse(Source source) throws XMLPlatformException { |
| try { |
| if (null == transformerFactory) { |
| transformerFactory = XMLHelper.createTransformerFactory(false); |
| } |
| Transformer transformer = transformerFactory.newTransformer(); |
| SAXResult saxResult = new SAXResult(); |
| SAXDocumentBuilder builder = new SAXDocumentBuilder(); |
| saxResult.setHandler(builder); |
| transformer.transform(source, saxResult); |
| return builder.getDocument(); |
| } catch (TransformerException e) { |
| throw XMLPlatformException.xmlPlatformParseException(e); |
| } |
| } |
| |
| /** |
| * Parses given url. |
| * |
| * @param url url to parse |
| * @return parsed document |
| * @throws XMLPlatformException exception occurred while parsing stream with given url |
| */ |
| @Override |
| public Document parse(URL url) throws XMLPlatformException { |
| InputStream inputStream = null; |
| try { |
| inputStream = url.openStream(); |
| } catch (IllegalArgumentException | IOException e) { |
| throw XMLPlatformException.xmlPlatformParseException(e); |
| } |
| |
| boolean hasThrownException = false; |
| try { |
| return parse(inputStream); |
| } catch (RuntimeException e) { |
| hasThrownException = true; |
| throw e; |
| } finally { |
| try { |
| inputStream.close(); |
| } catch (IOException e) { |
| if (!hasThrownException) { |
| //don't override runtime exception |
| throw XMLPlatformException.xmlPlatformParseException(e); |
| } |
| } |
| } |
| } |
| |
| private DocumentBuilder getDocumentBuilder() { |
| try { |
| if (null == documentBuilder) { |
| documentBuilder = getNewDocumentBuilder(); |
| } else { |
| try { |
| documentBuilder.reset(); |
| } catch (UnsupportedOperationException uoe) { |
| // https://bugs.eclipse.org/bugs/show_bug.cgi?id=490229 |
| // if reset is not supported by the parser, just return new one |
| documentBuilder = getNewDocumentBuilder(); |
| } |
| } |
| |
| return documentBuilder; |
| } catch (ParserConfigurationException e) { |
| throw XMLPlatformException.xmlPlatformParseException(e); |
| } |
| } |
| |
| private DocumentBuilder getNewDocumentBuilder() throws ParserConfigurationException { |
| if (null == documentBuilderFactory) { |
| loadDocumentBuilderFactory(); |
| } |
| DocumentBuilder newDb = documentBuilderFactory.newDocumentBuilder(); |
| newDb.setEntityResolver(entityResolver); |
| newDb.setErrorHandler(errorHandler); |
| return newDb; |
| } |
| } |