/****************************************************************************/
/* File: SaxonPkgInfo.java */
/* Author: F. Georges - H2O Consulting */
/* Date: 2010-09-19 */
/* Tags: */
/* Copyright (c) 2010-2015 Florent Georges (see end of file.) */
/* ------------------------------------------------------------------------ */
package org.expath.pkg.saxon;
import java.io.Reader;
import java.io.StringReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.AccessControlException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import net.sf.saxon.Configuration;
import net.sf.saxon.lib.ExtensionFunctionDefinition;
import net.sf.saxon.trans.XPathException;
import org.expath.pkg.repo.Package;
import org.expath.pkg.repo.*;
import org.expath.pkg.repo.Storage.PackageResolver;
import org.expath.pkg.repo.tools.Logger;
import org.expath.tools.ToolsException;
import org.expath.tools.saxon.fun.Definition;
import org.expath.tools.saxon.fun.Function;
import org.expath.tools.saxon.fun.Library;
/**
* TODO: ...
*
* @author Florent Georges
*/
public class SaxonPkgInfo
extends PackageInfo
{
public SaxonPkgInfo(Package pkg)
{
super("saxon", pkg);
}
public void registerExtensionFunctions(Configuration config)
throws PackageException
{
try {
// the libraries
for ( String name : myLibs ) {
LOG.fine("Register library class {0}", name);
Class clazz;
try {
clazz = Class.forName(name);
}
catch ( ClassNotFoundException ex ) {
ClassLoader loader = getClassLoader(getPackage(), myJars);
clazz = Class.forName(name, true, loader);
}
if ( Library.class.isAssignableFrom(clazz) ) {
Class<Library> c = clazz.asSubclass(Library.class);
Library lib = c.newInstance();
lib.register(config);
}
else {
throw new PackageException("Not a proper library: " + clazz);
}
}
// the functions
for ( String name : myFuns ) {
LOG.fine("Register function class {0}", name);
Class clazz;
try {
clazz = Class.forName(name);
}
catch ( ClassNotFoundException ex ) {
ClassLoader loader = getClassLoader(getPackage(), myJars);
clazz = Class.forName(name, true, loader);
}
if ( Function.class.isAssignableFrom(clazz) ) {
Class<Function> c = clazz.asSubclass(Function.class);
Function fun = c.newInstance();
Definition def = fun.definition();
config.registerExtensionFunction(def);
}
else if ( EXPathFunctionDefinition.class.isAssignableFrom(clazz) ) {
Class<EXPathFunctionDefinition> c = clazz.asSubclass(EXPathFunctionDefinition.class);
EXPathFunctionDefinition fun = c.newInstance();
fun.setConfiguration(config);
config.registerExtensionFunction(fun);
}
else if ( ExtensionFunctionDefinition.class.isAssignableFrom(clazz) ) {
Class<ExtensionFunctionDefinition> c = clazz.asSubclass(ExtensionFunctionDefinition.class);
ExtensionFunctionDefinition fun = c.newInstance();
config.registerExtensionFunction(fun);
}
else {
throw new PackageException("Not a proper extension function: " + clazz);
}
}
}
catch ( ToolsException ex ) {
throw new PackageException("Error registering Java extension functions", ex);
}
catch ( ClassNotFoundException ex ) {
throw new PackageException("Error registering Java extension functions", ex);
}
catch ( InstantiationException ex ) {
throw new PackageException("Error registering Java extension functions", ex);
}
catch ( IllegalAccessException ex ) {
throw new PackageException("Error registering Java extension functions", ex);
}
catch ( XPathException ex ) {
throw new PackageException("Error registering Java extension functions", ex);
}
}
@Override
public Source resolve(String href, URISpace space)
throws PackageException
{
PackageResolver resolver = getPackage().getResolver();
// if XQuery, try a direct module, or a wrapper for java
if ( space == URISpace.XQUERY ) {
String f = myXquery.get(href);
if ( f != null ) {
try {
return resolver.resolveComponent(f);
}
catch ( Storage.NotExistException ex ) {
throw new PackageException("Query not found in the package", ex);
}
}
f = myXqueryWrappers.get(href);
if ( f != null ) {
try {
return resolveXqueryWrapper(href);
}
catch ( XPathException ex ) {
throw new PackageException("Error resolving the URI: " + href, ex);
}
}
}
// or if XSLT, try a direct stylesheet, or a wrapper for java
else if ( space == URISpace.XSLT ) {
String f = myXslt.get(href);
if ( f != null ) {
try {
return resolver.resolveComponent(f);
}
catch ( Storage.NotExistException ex ) {
throw new PackageException("Stylesheet not found in the package", ex);
}
}
f = myXsltWrappers.get(href);
if ( f != null ) {
return resolveXsltWrapper(href);
}
}
// ignore any non-supported spaces
else {
// nothing
}
// return null if nothing has been found
return null;
}
/**
* Must resolve to the empty wrapper.
*/
private Source resolveXsltWrapper(String href)
{
Reader r = new StringReader(EMPTY_STYLESHEET);
return new StreamSource(r);
}
/**
* Must resolve to an empty wrapper, generated with the correct module URI.
*/
private Source resolveXqueryWrapper(String href)
throws XPathException
{
String module = "module namespace tns = '" + href + "';\n";
Reader r = new StringReader(module);
Source src = new StreamSource(r);
src.setSystemId("http://expath.org/pkg/saxon/xquery#empty");
return src;
}
// TODO: Actually, the resulting ClassLoader must be cached, as a same class
// will be loaded twice if via 2 different loaders (even if they have the
// exact same classpath... !!! (or maybe the resulting loaded class object?)
private ClassLoader getClassLoader(Package pkg, Set<String> jars)
throws XPathException
, PackageException
{
List<URL> cp = new ArrayList<URL>();
PackageResolver resolver = pkg.getResolver();
for ( String j : jars ) {
try {
Source src = resolver.resolveComponent(j);
String sysid = src.getSystemId();
cp.add(new URL(sysid));
}
catch ( Storage.NotExistException ex ) {
throw new PackageException("JAR not found in the package", ex);
}
catch ( MalformedURLException ex ) {
throw new XPathException("The file does not result in a valid URI", ex);
}
}
LOG.fine("Use the following CP for registerExtensionFunctions: {0}", cp);
try {
ClassLoader parent = SaxonPkgInfo.class.getClassLoader();
return new URLClassLoader(cp.toArray(new URL[0]), parent);
}
// can be thrown in a servlet container with restrictions...
catch ( AccessControlException ex ) {
LOG.info("Access control error: {0}", ex);
return null;
}
}
public boolean hasJars() {
return ! myJars.isEmpty();
}
public Set<String> getJars() {
return myJars;
}
public void addJar(String jar) {
myJars.add(jar);
}
public void addFunction(String fun) {
myFuns.add(fun);
}
public void addLibrary(String lib) {
myLibs.add(lib);
}
public void addXslt(String href, String file) {
myXslt.put(href, file);
}
public void addXQuery(String href, String file) {
myXquery.put(href, file);
}
public void addXsltWrapper(String href, String file) {
myXsltWrappers.put(href, file);
}
public void addXQueryWrapper(String href, String file) {
myXqueryWrappers.put(href, file);
}
private final Set<String> myJars = new HashSet<String>();
private final Set<String> myFuns = new HashSet<String>();
private final Set<String> myLibs = new HashSet<String>();
private final Map<String, String> myXslt = new HashMap<String, String>();
private final Map<String, String> myXquery = new HashMap<String, String>();
private final Map<String, String> myXsltWrappers = new HashMap<String, String>();
private final Map<String, String> myXqueryWrappers = new HashMap<String, String>();
/** The logger. */
private static final Logger LOG = Logger.getLogger(SaxonPkgInfo.class);
/** The content of the empty wrapper stylesheet. */
private static final String EMPTY_STYLESHEET =
"<xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' version='2.0'/>\n";
}
/* ------------------------------------------------------------------------ */
/* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS COMMENT. */
/* */
/* The contents of this file are subject to the Mozilla Public License */
/* Version 1.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.mozilla.org/MPL/. */
/* */
/* Software distributed under the License is distributed on an "AS IS" */
/* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See */
/* the License for the specific language governing rights and limitations */
/* under the License. */
/* */
/* The Original Code is: all this file. */
/* */
/* The Initial Developer of the Original Code is Florent Georges. */
/* */
/* Contributor(s): none. */
/* ------------------------------------------------------------------------ */