001/**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.camel.builder.xml;
018
019import java.io.IOException;
020import java.io.InputStream;
021
022import javax.xml.transform.Source;
023import javax.xml.transform.TransformerException;
024import javax.xml.transform.URIResolver;
025import javax.xml.transform.stream.StreamSource;
026
027import org.apache.camel.CamelContext;
028import org.apache.camel.util.FileUtil;
029import org.apache.camel.util.ObjectHelper;
030import org.apache.camel.util.ResourceHelper;
031import org.apache.camel.util.StringHelper;
032import org.slf4j.Logger;
033import org.slf4j.LoggerFactory;
034
035/**
036 * Camel specific {@link javax.xml.transform.URIResolver} which is capable of loading files
037 * from classpath, file system and more.
038 * <p/>
039 * You can prefix with: classpath, file, http, ref, or bean.
040 * classpath, file and http loads the resource using these protocols (classpath is default).
041 * ref will lookup the resource in the registry.
042 * bean will call a method on a bean to be used as the resource.
043 * For bean you can specify the method name after dot, eg bean:myBean.myMethod
044 *
045 * @version 
046 */
047public class XsltUriResolver implements URIResolver {
048
049    private static final Logger LOG = LoggerFactory.getLogger(XsltUriResolver.class);
050
051    private final CamelContext context;
052    private final String location;
053    private final String baseScheme;
054
055    public XsltUriResolver(CamelContext context, String location) {
056        this.context = context;
057        this.location = location;
058        if (ResourceHelper.hasScheme(location)) {
059            baseScheme = ResourceHelper.getScheme(location);
060        } else {
061            // default to use classpath
062            baseScheme = "classpath:";
063        }
064    }
065
066    @Override
067    public Source resolve(String href, String base) throws TransformerException {
068        // supports the empty href
069        if (ObjectHelper.isEmpty(href)) {
070            href = location;
071        }
072        if (ObjectHelper.isEmpty(href)) {
073            throw new TransformerException("include href is empty");
074        }
075
076        LOG.trace("Resolving URI with href: {} and base: {}", href, base);
077
078        String scheme = ResourceHelper.getScheme(href);
079
080        if (scheme != null) {
081            // need to compact paths for file/classpath as it can be relative paths using .. to go backwards
082            String hrefPath = StringHelper.after(href, scheme);
083            if ("file:".equals(scheme)) {
084                // compact path use file OS separator
085                href = scheme + FileUtil.compactPath(hrefPath);
086            } else if ("classpath:".equals(scheme)) {
087                // for classpath always use /
088                href = scheme + FileUtil.compactPath(hrefPath, '/');
089            }
090            LOG.debug("Resolving URI from {}: {}", scheme, href);
091
092            InputStream is;
093            try {
094                is = ResourceHelper.resolveMandatoryResourceAsInputStream(context, href);
095            } catch (IOException e) {
096                throw new TransformerException(e);
097            }
098            return new StreamSource(is, href);
099        }
100
101        // if href and location is the same, then its the initial resolve
102        if (href.equals(location)) {
103            String path = baseScheme + href;
104            return resolve(path, base);
105        }
106
107        // okay then its relative to the starting location from the XSLT importing this one
108        String path = FileUtil.onlyPath(base);
109        if (ObjectHelper.isEmpty(path)) {
110            path = baseScheme + href;
111            return resolve(path, base);
112        } else {
113            if (ResourceHelper.hasScheme(path)) {
114                path = path + "/" + href;
115            } else {
116                path = baseScheme + path + "/" + href;
117            }
118            return resolve(path, base);
119        }
120    }
121    
122}