/*
* ModeShape (http://www.modeshape.org)
*
* Licensed 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
*
* http://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.modeshape.jcr.xml;
import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsNull.notNullValue;
import static org.hamcrest.core.IsNull.nullValue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import java.io.InputStream;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import org.modeshape.common.FixFor;
import org.modeshape.common.text.NoOpEncoder;
import org.modeshape.jcr.ExecutionContext;
import org.modeshape.jcr.JcrLexicon;
import org.modeshape.jcr.api.JcrConstants;
import org.modeshape.jcr.value.NamespaceRegistry;
import org.modeshape.jcr.value.Path;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;
/**
* Test case for {@link NodeImportXmlHandler}
*
* @author Randall Hauch
* @author Horia Chiorean (hchiorea@redhat.com)
*/
public class NodeImportXmlHandlerTest {
private static final String NT_NAMESPACE_URI = "http://www.jcp.org/jcr/nt/1.0";
private NodeImportXmlHandler handler;
protected ExecutionContext context;
protected Map<Path, NodeImportXmlHandler.ImportElement> parseResults;
private NodeImportDestination parseDestination;
@Before
public void beforeEach() {
context = new ExecutionContext();
context.getNamespaceRegistry().register(JcrLexicon.Namespace.PREFIX, JcrLexicon.Namespace.URI);
context.getNamespaceRegistry().register("nt", NT_NAMESPACE_URI);
parseDestination = new NodeImportDestination() {
@Override
public ExecutionContext getExecutionContext() {
return context;
}
@Override
public void submit( LinkedHashMap<Path, NodeImportXmlHandler.ImportElement> parseResults ) {
NodeImportXmlHandlerTest.this.parseResults = parseResults;
}
};
handler = new NodeImportXmlHandler(parseDestination);
}
@Test( expected = SAXException.class )
public void shouldNotParseXmlWithInvalidRootElement() throws Exception {
parse("xmlImport/docWithoutJcrRoot.xml");
}
@Test
public void shouldParseXmlDocumentWithoutNamespaces() throws Exception {
parse("xmlImport/docWithoutNamespaces.xml");
assertNode("Cars");
assertNode("Cars/Hybrid");
assertNode("Cars/Hybrid/car", "name=Toyota Prius", "maker=Toyota", "model=Prius");
assertNode("Cars/Hybrid/car[2]", "name=Toyota Highlander", "maker=Toyota", "model=Highlander");
assertNode("Cars/Hybrid/car[3]", "name=Nissan Altima", "maker=Nissan", "model=Altima");
assertNode("Cars/Sports");
assertNode("Cars/Sports/car", "name=Aston Martin DB9", "maker=Aston Martin", "model=DB9");
assertNode("Cars/Sports/car[2]", "name=Infiniti G37", "maker=Infiniti", "model=G37");
}
@Test
public void shouldParseXmlDocumentWithNamespaces() throws Exception {
context.getNamespaceRegistry().register("c", "http://default.namespace.com");
parse("xmlImport/docWithNamespaces.xml");
// Check the generated content.
// Note that the attribute name DOES match, so the nodes names come from "jcr:name" attribute
// Also, the "jcr:name" attribute values use the default namespace, which is "c" in the registry
assertNode("c:Cars");
assertNode("c:Cars/c:Hybrid");
assertNode("c:Cars/c:Hybrid/c:Toyota Prius", "c:maker=Toyota", "c:model=Prius");
assertNode("c:Cars/c:Hybrid/c:Toyota Highlander", "c:maker=Toyota", "c:model=Highlander");
assertNode("c:Cars/c:Hybrid/c:Nissan Altima", "c:maker=Nissan", "c:model=Altima");
assertNode("c:Cars/c:Sports");
assertNode("c:Cars/c:Sports/c:Aston Martin DB9", "c:maker=Aston Martin", "c:model=DB9");
assertNode("c:Cars/c:Sports/c:Infiniti G37", "c:maker=Infiniti", "c:model=G37");
}
@Test
public void shouldParseXmlDocumentWithNestedNamespaceDeclarations() throws Exception {
context.getNamespaceRegistry().register("c", "http://default.namespace.com");
context.getNamespaceRegistry().register("i", "http://attributes.com");
parse("xmlImport/docWithNestedNamespaces.xml");
// Check the generated content; note that the attribute name DOES match, so the nodes names come from "jcr:name" attribute
assertNode("Cars");
assertNode("Cars/c:Hybrid");
assertNode("Cars/c:Hybrid/c:Toyota Prius", "c:maker=Toyota", "c:model=Prius");
assertNode("Cars/c:Hybrid/c:Toyota Highlander", "c:maker=Toyota", "c:model=Highlander");
assertNode("Cars/c:Hybrid/c:Nissan Altima", "c:maker=Nissan", "c:model=Altima");
assertNode("Cars/Sports");
assertNode("Cars/Sports/Aston Martin DB9", "i:maker=Aston Martin", "model=DB9");
assertNode("Cars/Sports/Infiniti G37", "i:maker=Infiniti", "model=G37");
}
@Test
public void shouldParseXmlDocumentWithNamespacesThatAreNotYetInRegistry() throws Exception {
NamespaceRegistry reg = context.getNamespaceRegistry();
reg.unregister(JcrLexicon.Namespace.URI);
reg.unregister(NT_NAMESPACE_URI);
// Verify the prefixes don't exist ...
assertThat(reg.getPrefixForNamespaceUri(JcrLexicon.Namespace.URI, false), is(nullValue()));
assertThat(reg.getPrefixForNamespaceUri(NT_NAMESPACE_URI, false), is(nullValue()));
assertThat(reg.getPrefixForNamespaceUri("http://default.namespace.com", false), is(nullValue()));
// Parse the XML file ...
parse("xmlImport/docWithNestedNamespaces.xml");
// Get the prefix for the default namespace ...
String c = reg.getPrefixForNamespaceUri("http://default.namespace.com", false);
String i = reg.getPrefixForNamespaceUri("http://attributes.com", false);
String d = reg.getPrefixForNamespaceUri(reg.getDefaultNamespaceUri(), false);
assertThat("Namespace not properly registered in primary registry", c, is(notNullValue()));
assertThat("Namespace not properly registered in primary registry", d, is(notNullValue()));
assertThat("Namespace not properly registered in primary registry", i, is(notNullValue()));
if (c.length() != 0) {
c = c + ":";
}
if (d.length() != 0) {
d = d + ":";
}
if (i.length() != 0) {
i = i + ":";
}
// Check the generated content; note that the attribute name DOES match, so the nodes names come from "jcr:name" attribute
assertNode(d + "Cars");
assertNode(d + "Cars/" + c + "Hybrid");
assertNode(d + "Cars/" + c + "Hybrid/" + c + "Toyota Prius", c + "maker=Toyota", c + "model=Prius");
assertNode(d + "Cars/" + c + "Hybrid/" + c + "Toyota Highlander", c + "maker=Toyota", c + "model=Highlander");
assertNode(d + "Cars/" + c + "Hybrid/" + c + "Nissan Altima", c + "maker=Nissan", c + "model=Altima");
assertNode(d + "Cars/" + d + "Sports");
assertNode(d + "Cars/" + d + "Sports/Aston Martin DB9", i + "maker=Aston Martin", "model=DB9");
assertNode(d + "Cars/" + d + "Sports/Infiniti G37", i + "maker=Infiniti", "model=G37");
}
@Test
public void shouldParseXmlDocumentWithoutDefaultNamespaceThatUsesNameAttribute() throws Exception {
parse("xmlImport/docWithNamespacesWithoutDefault.xml");
// Check the generated content; note that the attribute name DOES match, so the nodes names come from "jcr:name" attribute
assertNode("Cars");
assertNode("Cars/Hybrid");
assertNode("Cars/Hybrid/Toyota Prius", "maker=Toyota", "model=Prius");
assertNode("Cars/Hybrid/Toyota Highlander", "maker=Toyota", "model=Highlander");
assertNode("Cars/Hybrid/Nissan Altima", "maker=Nissan", "model=Altima");
assertNode("Cars/Sports");
assertNode("Cars/Sports/Aston Martin DB9", "maker=Aston Martin", "model=DB9");
assertNode("Cars/Sports/Infiniti G37", "maker=Infiniti", "model=G37");
}
@Test
public void shouldParseXmlDocumentThatContainsNoContent() throws Exception {
parse("xmlImport/docWithOnlyRootElement.xml");
assertTrue(parseResults.isEmpty());
}
@Test
public void shouldParseXmlDocumentWithXmlComments() throws Exception {
context.getNamespaceRegistry().register("c", "http://default.namespace.com");
parse("xmlImport/docWithComments.xml");
assertNode("c:Cars");
assertNode("c:Cars/c:Hybrid");
assertNode("c:Cars/c:Hybrid/c:Toyota Prius", "c:maker=Toyota", "c:model=Prius");
assertNode("c:Cars/c:Hybrid/c:Toyota Highlander", "c:maker=Toyota", "c:model=Highlander");
assertNode("c:Cars/c:Hybrid/c:Nissan Altima", "c:maker=Nissan", "c:model=Altima");
assertNode("c:Cars/c:Sports");
assertNode("c:Cars/c:Sports/c:Aston Martin DB9", "c:maker=Aston Martin", "c:model=DB9");
assertNode("c:Cars/c:Sports/c:Infiniti G37", "c:maker=Infiniti", "c:model=G37");
}
@Test
public void shouldParseXmlDocumentWithoutNamespacesUsingTypeAttributeValue() throws Exception {
String typeAttribute = JcrConstants.JCR_PRIMARY_TYPE;
String typeAttributeValue = JcrConstants.NT_UNSTRUCTURED;
handler = new NodeImportXmlHandler(parseDestination, null, typeAttribute, typeAttributeValue, null, null);
parse("xmlImport/docWithoutNamespaces.xml");
// Check the generated content; note that the attribute name doesn't match, so the nodes don't get special names
String unstructPrimaryType = "jcr:primaryType=nt:unstructured";
assertNode("Cars", unstructPrimaryType);
assertNode("Cars/Hybrid", unstructPrimaryType);
assertNode("Cars/Hybrid/car", unstructPrimaryType, "name=Toyota Prius", "maker=Toyota", "model=Prius");
assertNode("Cars/Hybrid/car[2]", unstructPrimaryType, "name=Toyota Highlander", "maker=Toyota", "model=Highlander");
assertNode("Cars/Hybrid/car[3]", unstructPrimaryType, "name=Nissan Altima", "maker=Nissan", "model=Altima");
assertNode("Cars/Sports", unstructPrimaryType);
assertNode("Cars/Sports/car", unstructPrimaryType, "name=Aston Martin DB9", "maker=Aston Martin", "model=DB9");
assertNode("Cars/Sports/car[2]", unstructPrimaryType, "name=Infiniti G37", "maker=Infiniti", "model=G37");
}
@Test
public void shouldParseXmlDocumentWithNamespacesUsingTypeAttributeValue() throws Exception {
context.getNamespaceRegistry().register("c", "http://default.namespace.com");
String nameAttribute = JcrConstants.JCR_NAME;
String typeAttribute = JcrConstants.JCR_PRIMARY_TYPE;
String typeAttributeValue = JcrConstants.NT_UNSTRUCTURED;
handler = new NodeImportXmlHandler(parseDestination, nameAttribute, typeAttribute, typeAttributeValue, null, null);
parse("xmlImport/docWithNamespaces.xml");
// Check the generated content; note that the attribute name DOES match
String unstructPrimaryType = "jcr:primaryType=nt:unstructured";
assertNode("c:Cars", unstructPrimaryType);
assertNode("c:Cars/c:Hybrid", unstructPrimaryType);
assertNode("c:Cars/c:Hybrid/c:Toyota Prius", unstructPrimaryType, "c:maker=Toyota", "c:model=Prius");
assertNode("c:Cars/c:Hybrid/c:Toyota Highlander", unstructPrimaryType, "c:maker=Toyota", "c:model=Highlander");
assertNode("c:Cars/c:Hybrid/c:Nissan Altima", unstructPrimaryType, "c:maker=Nissan", "c:model=Altima");
assertNode("c:Cars/c:Sports", unstructPrimaryType);
assertNode("c:Cars/c:Sports/c:Aston Martin DB9", unstructPrimaryType, "c:maker=Aston Martin", "c:model=DB9");
assertNode("c:Cars/c:Sports/c:Infiniti G37", unstructPrimaryType, "c:maker=Infiniti", "c:model=G37");
}
@Test
public void shouldParseXmlDocumentWithNestedProperties() throws Exception {
context.getNamespaceRegistry().register("jcr", "http://www.jcp.org/jcr/1.0");
handler = new NodeImportXmlHandler(parseDestination, JcrConstants.JCR_NAME, null, null, null, null);
parse("xmlImport/docWithNestedProperties.xml");
// Check the generated content; note that the attribute name DOES match, so the nodes names come from "jcr:name" attribute
assertNode("Cars");
assertNode("Cars/Hybrid");
assertNode("Cars/Hybrid/car", "name=Toyota Prius", "maker=Toyota", "model=Prius");
assertNode("Cars/Hybrid/car[2]", "name=Toyota Highlander", "maker=Toyota", "model=Highlander");
assertNode("Cars/Hybrid/car[3]", "name=Nissan Altima", "maker=Nissan", "model=Altima");
assertNode("Cars/Sports");
assertNode("Cars/Sports/car", "name=Aston Martin DB9", "maker=Aston Martin", "model=DB9");
assertNode("Cars/Sports/car[2]", "name=Infiniti G37", "maker=Infiniti", "category=Turbocharged");
assertNode("Cars/Sports/car[2]/driver", "name=Tony Stewart");
assertNode("Cars/Sports/car[2]", "model=G37", "category=My Sedan");
assertNode("Cars/Sports/car[3]");
assertNode("Cars/Sports/car[3]/jcr:xmltext", "jcr:xmlcharacters=This is my text ");
assertNode("Cars/Sports/car[3]/jcr:xmltext[2]", "jcr:xmlcharacters=that should be merged");
}
@Test
public void shouldParseXmlDocumentWithMixins() throws Exception {
parse("xmlImport/docWithMixins.xml");
assertNode("cars", "jcr:mixinTypes=mix:created,mix:lastModified");
}
@Test
public void shouldParseXmlDocumentWithCustomPrimaryType() throws Exception {
parse("xmlImport/docWithCustomType.xml");
assertNode("folder", "jcr:mixinTypes=mix:created,mix:lastModified", "jcr:primaryType=nt:folder");
assertNode("folder/file1", "jcr:primaryType=nt:file");
assertNode("folder/file1/jcr:content");
assertNode("folder/file2", "jcr:primaryType=nt:file");
assertNode("folder/file2/jcr:content");
}
@Test
@FixFor("MODE-1788")
public void shouldParseXmlDocumentWithMixinsCustomSeparator() throws Exception {
parse("xmlImport/docWithMixinsCustomSeparator.xml");
assertNode("cars", "jcr:mixinTypes=mix:created,mix:lastModified");
}
@Test
@FixFor("MODE-1788")
public void shouldParseXmlDocumentWithPropertiesCustomSeparator() throws Exception {
parse("xmlImport/docWithPropertiesCustomSeparator.xml");
assertImportElementWithStrings("story", ";",
"title=Story with commas in the text lead and body text",
"lead=Lead text in attribute, split with a comma.",
"body=Body text in sub element, split by the comma.",
"multiBody=Body text in sub element; split by semicolon");
}
@Test
@FixFor( "MODE-2241" )
public void shouldParseXmlDocumentWithReferences() throws Exception {
parse("xmlImport/docWithReferences.xml");
assertImportElementWithReferences("node1", "hard_ref11=/node2", "hard_ref12=/node2,/node3");
assertImportElementWithReferences("node2", "hard_ref21=/node1", "hard_ref22=/node1,/node3");
assertImportElementWithWeakReferences("node1", "weak_ref11=/node2", "weak_ref12=/node2");
assertImportElementWithWeakReferences("node2", "weak_ref21=/node1", "weak_ref22=/node1");
assertImportElementWithSimpleReferences("node1", "simple_ref11=/node2", "simple_ref12=/node2");
assertImportElementWithSimpleReferences("node2", "simple_ref21=/node1", "simple_ref22=/node1");
}
@Test
@FixFor( "MODE-2241" )
public void shouldParseXmlDocumentWithPropertyTypes() throws Exception {
parse("xmlImport/docWithPropertyTypes.xml");
assertImportElementWithStrings("node1", "string_prop=string");
assertImportElementWithBooleans("node1", "boolean_prop=true");
assertImportElementWithDecimals("node1", "decimal_prop=12.3");
assertImportElementWithDoubles("node1", "double_prop=12.3");
assertImportElementWithLongs("node1", "long_prop=123456");
assertImportElementWithDates("node1", "date_prop=1994-11-05T13:15:30Z");
assertImportElementWithNames("node1", "name_prop=nt:undefined");
assertImportElementWithURIs("node1", "uri_prop=http://www.google.com");
assertImportElementWithPaths("node1", "path_prop=/a/b/c");
assertImportElementWithBinaries("node1", "binary_prop=io/file1.txt");
assertImportElementWithReferences("node1", "reference_prop=/node2");
assertImportElementWithWeakReferences("node1", "weakreference_prop=/node2");
assertImportElementWithSimpleReferences("node1", "simplereference_prop=/node2");
}
private void assertNode( String path,
String... expectedProperties ) {
assertImportElementWithStrings(path, NodeImportXmlHandler.DEFAULT_MULTI_VALUE_SEPARATOR, expectedProperties);
}
private void assertImportElementWithReferences( String path, String... expectedReferences ) {
NodeImportXmlHandler.ImportElement element = assertImportElementExists(path);
assertImportElementHasProperties(element,
NodeImportXmlHandler.DEFAULT_MULTI_VALUE_SEPARATOR,
org.modeshape.jcr.value.PropertyType.REFERENCE,
expectedReferences);
}
private void assertImportElementWithWeakReferences( String path, String... expectedReferences ) {
NodeImportXmlHandler.ImportElement element = assertImportElementExists(path);
assertImportElementHasProperties(element,
NodeImportXmlHandler.DEFAULT_MULTI_VALUE_SEPARATOR,
org.modeshape.jcr.value.PropertyType.WEAKREFERENCE,
expectedReferences);
}
private void assertImportElementWithSimpleReferences( String path, String... expectedReferences ) {
NodeImportXmlHandler.ImportElement element = assertImportElementExists(path);
assertImportElementHasProperties(element,
NodeImportXmlHandler.DEFAULT_MULTI_VALUE_SEPARATOR,
org.modeshape.jcr.value.PropertyType.SIMPLEREFERENCE,
expectedReferences);
}
private void assertImportElementWithBooleans( String path, String... expectedProperties ) {
NodeImportXmlHandler.ImportElement element = assertImportElementExists(path);
assertImportElementHasProperties(element,
NodeImportXmlHandler.DEFAULT_MULTI_VALUE_SEPARATOR,
org.modeshape.jcr.value.PropertyType.BOOLEAN,
expectedProperties);
}
private void assertImportElementWithDoubles( String path, String... expectedProperties ) {
NodeImportXmlHandler.ImportElement element = assertImportElementExists(path);
assertImportElementHasProperties(element,
NodeImportXmlHandler.DEFAULT_MULTI_VALUE_SEPARATOR,
org.modeshape.jcr.value.PropertyType.DOUBLE,
expectedProperties);
}
private void assertImportElementWithDecimals( String path, String... expectedProperties ) {
NodeImportXmlHandler.ImportElement element = assertImportElementExists(path);
assertImportElementHasProperties(element,
NodeImportXmlHandler.DEFAULT_MULTI_VALUE_SEPARATOR,
org.modeshape.jcr.value.PropertyType.DECIMAL,
expectedProperties);
}
private void assertImportElementWithDates( String path, String... expectedProperties ) {
NodeImportXmlHandler.ImportElement element = assertImportElementExists(path);
assertImportElementHasProperties(element,
NodeImportXmlHandler.DEFAULT_MULTI_VALUE_SEPARATOR,
org.modeshape.jcr.value.PropertyType.DATE,
expectedProperties);
}
private void assertImportElementWithLongs( String path, String... expectedProperties ) {
NodeImportXmlHandler.ImportElement element = assertImportElementExists(path);
assertImportElementHasProperties(element,
NodeImportXmlHandler.DEFAULT_MULTI_VALUE_SEPARATOR,
org.modeshape.jcr.value.PropertyType.LONG,
expectedProperties);
}
private void assertImportElementWithURIs( String path, String... expectedProperties ) {
NodeImportXmlHandler.ImportElement element = assertImportElementExists(path);
assertImportElementHasProperties(element,
NodeImportXmlHandler.DEFAULT_MULTI_VALUE_SEPARATOR,
org.modeshape.jcr.value.PropertyType.URI,
expectedProperties);
}
private void assertImportElementWithNames( String path, String... expectedProperties ) {
NodeImportXmlHandler.ImportElement element = assertImportElementExists(path);
assertImportElementHasProperties(element,
NodeImportXmlHandler.DEFAULT_MULTI_VALUE_SEPARATOR,
org.modeshape.jcr.value.PropertyType.NAME,
expectedProperties);
}
private void assertImportElementWithPaths( String path, String... expectedProperties ) {
NodeImportXmlHandler.ImportElement element = assertImportElementExists(path);
assertImportElementHasProperties(element,
NodeImportXmlHandler.DEFAULT_MULTI_VALUE_SEPARATOR,
org.modeshape.jcr.value.PropertyType.PATH,
expectedProperties);
}
private void assertImportElementWithBinaries( String path, String... expectedProperties ) {
NodeImportXmlHandler.ImportElement element = assertImportElementExists(path);
assertImportElementHasProperties(element,
NodeImportXmlHandler.DEFAULT_MULTI_VALUE_SEPARATOR,
org.modeshape.jcr.value.PropertyType.BINARY,
expectedProperties);
}
private void assertImportElementWithStrings( String path,
String multiValueSeparator,
String... expectedProperties ) {
NodeImportXmlHandler.ImportElement element = assertImportElementExists(path);
assertImportElementHasProperties(element, multiValueSeparator, org.modeshape.jcr.value.PropertyType.STRING, expectedProperties);
}
private void assertImportElementHasProperties( NodeImportXmlHandler.ImportElement element,
String multiValueSeparator,
org.modeshape.jcr.value.PropertyType expectedPropertyType,
String... expectedProperties ) {
for (String propertyValueString : expectedProperties) {
String[] parts = propertyValueString.split("=");
String propertyName = context.getValueFactories()
.getNameFactory()
.create(parts[0])
.getString(NoOpEncoder.getInstance());
String propertyValue = parts[1];
org.modeshape.jcr.value.PropertyType propertyType = element.getPropertyType(propertyName);
if (propertyName.equals(JcrConstants.JCR_PRIMARY_TYPE)) {
assertEquals(propertyValue, element.getType());
} else {
Collection<String> actualPropertyValue = propertyName.equalsIgnoreCase(JcrConstants.JCR_MIXIN_TYPES)
? element.getMixins() : element.getProperties().get(propertyName);
assertNotNull(actualPropertyValue);
String[] values = propertyValue.split(multiValueSeparator);
for (String value : values) {
assertTrue("Expected property not found: " + value, actualPropertyValue.contains(value));
}
assertEquals("Invalid property type: " + propertyType, expectedPropertyType, propertyType);
}
}
}
private NodeImportXmlHandler.ImportElement assertImportElementExists( String path ) {
Path expectedPath = context.getValueFactories().getPathFactory().create("/" + path);
NodeImportXmlHandler.ImportElement element = parseResults.get(expectedPath);
assertNotNull(path + " not found among parsed elements", element);
return element;
}
private void parse( String relativePathToXmlFile ) throws Exception {
InputStream stream = getClass().getClassLoader().getResourceAsStream(relativePathToXmlFile);
try {
XMLReader reader = XMLReaderFactory.createXMLReader();
reader.setContentHandler(handler);
reader.setErrorHandler(handler);
reader.parse(new InputSource(stream));
} finally {
if (stream != null) {
stream.close();
}
}
}
}