Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
195 changes: 0 additions & 195 deletions src/main/java/org/apache/commons/xml/AndroidProvider.java

This file was deleted.

65 changes: 65 additions & 0 deletions src/main/java/org/apache/commons/xml/HardeningSAXParser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.commons.xml;

import javax.xml.parsers.SAXParser;

import org.xml.sax.Parser;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderAdapter;

/**
* {@link SAXParser} that exposes a hardened {@link XMLReader} and a matching SAX 1 {@link Parser}.
*
* <p>Both views are produced from the same hardened reader, so a caller reaching the parser through either the SAX 2 ({@link #getXMLReader()}) or the legacy
* SAX 1 ({@link #getParser()}) path gets the same hardening. The SAX 1 view matters because some consumers, such as Xalan's identity transformer, still ask
* for a {@link Parser}.</p>
*
* <p>The hardened reader is computed lazily on first access and cached: hardening an {@link XMLReader} can install a fresh wrapper (Android's Expat path), so
* every parse must run through the same instance. The {@code parse(...)} overloads inherited from {@link SAXParser} dispatch virtually to {@link #getXMLReader()}
* and {@link #getParser()}, so they too run through the hardened views without further overrides.</p>
*/
final class HardeningSAXParser extends DelegatingSAXParser {

private XMLReader hardenedReader;
private Parser hardenedParser;

HardeningSAXParser(final SAXParser delegate) {
super(delegate);
}

@Override
public XMLReader getXMLReader() throws SAXException {
if (hardenedReader == null) {
hardenedReader = SAXParserHardener.hardenReader(super.getXMLReader());
}
return hardenedReader;
}

@Override
@SuppressWarnings("deprecation")
public Parser getParser() throws SAXException {
if (hardenedParser == null) {
final XMLReader reader = getXMLReader();
// Reuse the reader directly if it already is a SAX 1 parser; otherwise adapt it, so the SAX 1 path runs through the same hardened reader.
hardenedParser = reader instanceof Parser ? (Parser) reader : new XMLReaderAdapter(reader);
}
return hardenedParser;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@

package org.apache.commons.xml;

import java.util.function.UnaryOperator;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
Expand All @@ -27,24 +25,20 @@
import org.xml.sax.XMLReader;

/**
* Universal SAX wrapper that defers per-parser hardening to a supplied {@link XMLReader} hardener.
* Universal SAX factory wrapper that funnels every produced parser through {@link SAXParserHardener#hardenReader(XMLReader)}.
*
* <p>{@link SAXParserFactory} has no property API, only a feature API. Therefore, complex configuration must be performed on each new
* {@link XMLReader} parser.</p>
* <p>{@link SAXParserFactory} exposes only a feature API and no property API, so the per-parse hardening (limits, entity blocking, implementation-specific
* fixups) has to run on each {@link XMLReader} the factory produces. This wrapper returns a {@link HardeningSAXParser}, which applies that hardening lazily to
* both the SAX 2 {@link XMLReader} and the SAX 1 {@link org.xml.sax.Parser} it exposes.</p>
*/
final class HardeningSAXParserFactory extends DelegatingSAXParserFactory {

private final UnaryOperator<XMLReader> hardener;

HardeningSAXParserFactory(final SAXParserFactory delegate, final UnaryOperator<XMLReader> hardener) {
HardeningSAXParserFactory(final SAXParserFactory delegate) {
super(delegate);
this.hardener = hardener;
}

@Override
public SAXParser newSAXParser() throws ParserConfigurationException, SAXException {
final SAXParser parser = super.newSAXParser();
hardener.apply(parser.getXMLReader());
return parser;
return new HardeningSAXParser(super.newSAXParser());
}
}
25 changes: 25 additions & 0 deletions src/main/java/org/apache/commons/xml/JaxpSetters.java
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,14 @@ static void setFeature(final XMLReader reader, final String feature, final boole
apply(reader, KIND_FEATURE, feature, () -> reader.setFeature(feature, value));
}

static void setOptionalFeature(final XMLReader reader, final String feature, final boolean value) {
try {
reader.setFeature(feature, value);
} catch (final Exception e) {
// Ignored: the implementation does not recognize this feature.
}
}

static void setProperty(final XMLInputFactory factory, final String property, final Object value) {
apply(factory, KIND_PROPERTY, property, () -> factory.setProperty(property, value));
}
Expand All @@ -137,6 +145,23 @@ static void setProperty(final XMLReader reader, final String property, final Obj
apply(reader, KIND_PROPERTY, property, () -> reader.setProperty(property, value));
}

/**
* Sets a property on an {@link XMLReader} and returns whether the implementation accepted it.
*
* @param reader The target reader on which to set the property.
* @param property The name of the property to set.
* @param value The value of the property to set.
* @return {@code true} if the property was applied, {@code false} if the implementation rejected it.
*/
static boolean trySetProperty(final XMLReader reader, final String property, final Object value) {
try {
reader.setProperty(property, value);
return true;
} catch (final Exception e) {
return false;
}
}

static void setProperty(final SchemaFactory factory, final String property, final Object value) {
apply(factory, KIND_PROPERTY, property, () -> factory.setProperty(property, value));
}
Expand Down
Loading
Loading