org.nuiton.eugene.ResourceResolver Maven / Gradle / Ivy
Show all versions of eugene-core Show documentation
package org.nuiton.eugene;
/*-
* #%L
* EUGene :: EUGene Core
* %%
* Copyright (C) 2004 - 2017 Code Lutin, Ultreia.io
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Lesser Public License for more details.
*
* You should have received a copy of the GNU General Lesser Public
* License along with this program. If not, see
* .
* #L%
*/
import io.ultreia.java4all.lang.ClassLoaders;
import io.ultreia.java4all.lang.Strings;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.URIResolver;
import javax.xml.transform.stream.StreamSource;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
*
* ResourceResolver is a URIResolver for XSL transformation.
*
*
* Its purpose is to catch the XSL document(...) function call and return a
* valid data source only if the wanted resource is present in the project
* resources.
*
* The main goal of ResourceResolver is to retrieve datasources locally, if the
* wanted resource is not present in project resource then null will be
* returned.
*
* The resolve function search for the file part of href parameter:
*
* - href: http://argouml.org/profiles/uml14/default-uml14.xmi
* - file part: default-uml14.xmi.
*
* The resource is searched this way:
*
* - eg: {@code [file part extension]/[file part]}
* - eg: {@code xmi/default-uml14.xmi}
*
*
* You should assign this ResourceResolver on
* {@link Transformer} but not on {@link TransformerFactory}.
*
* @author chorlet
*/
public class ResourceResolver implements URIResolver {
/**
* le pattern de detection d'une uri
*/
public static final Pattern HREF_PATTERN =
Pattern.compile("([a-zA-Z]+)\\:\\/\\/(.+)");
/**
* Shared Cache to not search in full classpath at each request.
*/
protected static final SortedMap sourceCache =
new TreeMap<>();
/**
* Shared Cache of not local resources
*/
protected static final Set unresolvedCache = new HashSet<>();
/**
* log.
*/
private static final Logger log = LogManager.getLogger(ResourceResolver.class);
protected String base;
/**
* le niveau de verbosite
*/
protected boolean verbose = log.isDebugEnabled();
/**
* le classe loader utilise pour recuperer les resources
*/
protected ClassLoader cl = getClass().getClassLoader();
public ResourceResolver() {
this(null);
}
public ResourceResolver(String base) {
if (base != null && base.endsWith("/") && base.length() > 1) {
base = base.substring(0, base.length() - 1);
}
this.base = base;
if (log.isTraceEnabled()) {
log.trace(this + ", base : " + this.base);
}
}
/**
* Pour vider le cache partage.
*/
public static synchronized void clearCache() {
sourceCache.clear();
unresolvedCache.clear();
}
/**
* Resolve href on local resource.
*
* @return null if local resource not found
*/
@Override
public synchronized Source resolve(String href, String base) {
if (unresolvedCache.contains(href)) {
// href was already unfound in class-path,
// do not search twice (class-path search can be expensive)
if (verbose) {
log.info("Skip unresolved " + href);
}
return null;
}
if (sourceCache.containsKey(href)) {
// directly use the cached source, skip all other stuff
if (verbose) {
log.info("use cached source " + href);
}
return sourceCache.get(href);
}
// at this point, the href is undiscovered, try to find in in class-path
if (verbose) {
log.info("Resolving " + href);
}
// URI :
// example 1 : pathmap://UML_METAMODELS/UML.metamodel.uml
// example 2 : http://argouml.org/profiles/uml14/default-java.xmi
// relative path :
// example 3 : xxx/zzz/ttt.uml
Source source;
// if URI
Matcher matcher = HREF_PATTERN.matcher(href);
if (matcher.matches()) {
// String protocol = matcher.group(1);
String path = matcher.group(2);
// try look only with the filename
// this is the last chance to find something :)
source = findHrefSource(path);
} else {
// no protocol, so should be a relative path location
source = findRelativeSource(href);
}
if (source == null) {
// means this resolver was not able to find the source
if (verbose) {
log.info("detect unresolved source " + href);
}
unresolvedCache.add(href);
} else {
// find a new cacheable source, add it in cache
if (verbose) {
log.info("detect cacheable source " + href);
}
sourceCache.put(href, source);
}
// if (href.matches("[a-zA-Z]+://.+")) {
// String filename = null;
// int beginIndex = href.lastIndexOf('/');
// if (beginIndex > -1) {
// filename = href.substring(beginIndex + 1);
// }
// if (filename != null && !filename.isEmpty()) {
// source = findSource(filename, true);
// }
// } else {
// source = findSource(href, false);
// }
return source;
}
public void setVerbose(boolean verbose) {
this.verbose = verbose;
}
public void setCl(ClassLoader cl) {
this.cl = cl;
}
protected Source findHrefSource(String path) {
long t0 = System.nanoTime();
String filename;
int beginIndex = path.lastIndexOf('/');
if (beginIndex > -1) {
filename = path.substring(beginIndex + 1);
} else {
filename = path;
}
if (filename == null || filename.isEmpty()) {
return null;
}
String resource;
resource = ".*/" + filename;
if (verbose) {
log.info("will discover " + resource);
}
URL url = null;
// use given classloader to work in maven
List urls = null;
URLClassLoader ucl = null;
if (cl == null) {
ClassLoader cl2 = getClass().getClassLoader();
if (cl2 instanceof URLClassLoader) {
ucl = (URLClassLoader) cl2;
}
}
if (cl instanceof URLClassLoader) {
ucl = (URLClassLoader) cl;
}
try {
urls = ClassLoaders.getURLs(resource, ucl);
} catch (Exception rnfe) {
// Nothing to do
}
if (urls != null && !urls.isEmpty()) {
url = urls.get(0);
}
Source source = null;
if (url != null) {
if (verbose) {
log.info(url.toString());
}
source = new StreamSource(url.toString());
}
if (verbose) {
String time = Strings.convertTime(System.nanoTime() - t0);
log.info("resolved in " + time);
}
return source;
}
protected Source findRelativeSource(String path) {
long t0 = System.nanoTime();
String filename = path;
// int beginIndex = path.lastIndexOf('/');
// if (beginIndex > -1) {
// filename = path.substring(beginIndex + 1);
// } else {
// filename = path;
// }
// if (filename == null || filename.isEmpty()) {
// return null;
// }
String resource;
if (base != null) {
resource = base + "/" + filename;
} else {
resource = filename;
}
if (verbose) {
log.info("will discover " + resource);
}
URL url = ClassLoaders.getURLOrNull(resource);
Source source = null;
if (url != null) {
if (verbose) {
log.info(url.toString());
}
source = new StreamSource(url.toString());
}
if (verbose) {
String time = Strings.convertTime(System.nanoTime() - t0);
log.info("resolved in " + time);
}
return source;
}
}