blob: af01b8a74300cf6702d0537f0677031d4939ba78 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one or more contributor
// license agreements. See the NOTICE.txt 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
//
// 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.apache.oodt.commons.util;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;
import java.io.InputStreamReader;
/** XML entity resolver for enterprise applications.
*
* This resolver attempts to retrieves entities from local sources before deferring to the
* default parser's entity resolver.
*
* @author Kelly
*/
public class EnterpriseEntityResolver implements EntityResolver {
/** Mapping of public identifiers to known file names. */
static Map entities;
/** Initialize the class by reading the entites.xml file. */
static {
entities = new HashMap();
try {
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setNamespaceAware(false);
factory.setValidating(true);
javax.xml.parsers.SAXParser p = factory.newSAXParser();
p.parse(new InputSource(EnterpriseEntityResolver.class.getResourceAsStream("entities.xml")),
new DefaultHandler() {
private StringBuffer token = new StringBuffer();
private String pi;
public void characters(char[] ch, int start, int length) {
token.append(ch, start, length);
}
public void endElement(String ns, String name, String qual) {
if ("pi".equals(qual))
pi = token.toString().trim();
else if ("filename".equals(qual)) {
entities.put(pi, token.toString().trim());
}
token.delete(0, token.length());
}
});
} catch (ParserConfigurationException ex) {
throw new IllegalStateException("Unexpected ParserConfigurationException: " + ex.getMessage());
} catch (SAXParseException ex) {
System.err.println("Error parsing entities.xml at line " + ex.getLineNumber() + ", column "
+ ex.getColumnNumber() + "; ignoring entity lookup");
} catch (SAXException ex) {
System.err.println("Exception parsing entities.xml: " + ex.getMessage() + "; ignoring entity lookup");
} catch (IOException ex) {
System.err.println("I/O error reading entities.xml: " + ex.getMessage() + "; ignoring entity lookup");
}
}
public InputSource resolveEntity(String publicID, String systemID) throws SAXException, IOException {
String filename = computeFilename(publicID, systemID);
if (filename == null) return null;
// Resolve it using class loader first. Any DTD in the toplevel directory
// of any jar present to the application is a potential source.
InputStream in = getClass().getResourceAsStream("/" + filename);
if (in != null)
return new InputSource(new BufferedReader(new InputStreamReader(in)));
// OK, try the filesystem next. You can control what directories get
// searched by setting the entity.dirs property.
File file = findFile(getEntityRefDirs(System.getProperty("entity.dirs", "")), filename);
if (file != null) try {
return new InputSource(new BufferedReader(new FileReader(file)));
} catch (IOException ignore) {}
// No luck either way.
return null;
}
/** Compute the possible filename from public and system identifiers.
*
* This attempts to map the public ID to a known file based on a table (see
* <code>entity.xml</code>). If that doesn't work, then it will use the
* file part of the system ID. If there's no file part, it returns null.
*
* @param publicID The public identifier.
* @param systemID The system identifier.
* @return A file computed from <var>publicID</var> and <var>systemID</var>.
*/
static String computeFilename(String publicID, String systemID) {
String name = (String) entities.get(publicID);
if (name == null) try {
URL url = new URL(systemID);
File file = new File(url.getFile());
name = file.getName();
} catch (MalformedURLException ignore) {}
return name;
}
/**
* Get a list of entity directories from the given string specification.
*
* @param spec Directory names separated by commas.
* @return a {@link List} of those directory names.
*/
static List getEntityRefDirs(String spec) {
List dirs = new ArrayList();
for (StringTokenizer t = new StringTokenizer(spec, ",;|"); t.hasMoreTokens();)
dirs.add(t.nextToken());
return dirs;
}
/** Find a file under a list of directories.
*
* @param dirs List of {@link java.lang.String} directory names.
* @param filename Name of the file to find under one of the directories named in <var>dirs</var>.
* @return The first path to the file named by <var>filename</var> under a directory in <var>dirs</var>,
* or null if no directory in <var>dirs</var> contains a file named <var>filename</var>.
*/
static File findFile(List dirs, String filename) {
for (Iterator i = dirs.iterator(); i.hasNext();) {
File potentialFile = new File((String) i.next(), filename);
if (potentialFile.isFile()) return potentialFile;
}
return null;
}
}