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;
021import javax.xml.transform.Source;
022import javax.xml.transform.TransformerException;
023import javax.xml.transform.URIResolver;
024import javax.xml.transform.stream.StreamSource;
025
026import org.apache.camel.CamelContext;
027import org.apache.camel.util.FileUtil;
028import org.apache.camel.util.ObjectHelper;
029import org.apache.camel.util.ResourceHelper;
030import org.apache.camel.util.StringHelper;
031import org.slf4j.Logger;
032import org.slf4j.LoggerFactory;
033
034/**
035 * Camel specific {@link javax.xml.transform.URIResolver} which is capable of loading files
036 * from classpath, file system and more.
037 * <p/>
038 * You can prefix with: classpath, file, http, ref, or bean.
039 * classpath, file and http loads the resource using these protocols (classpath is default).
040 * ref will lookup the resource in the registry.
041 * bean will call a method on a bean to be used as the resource.
042 * For bean you can specify the method name after dot, eg bean:myBean.myMethod
043 *
044 * @version 
045 */
046public class XsltUriResolver implements URIResolver {
047
048    private static final Logger LOG = LoggerFactory.getLogger(XsltUriResolver.class);
049
050    private final CamelContext context;
051    private final String location;
052    private final String baseScheme;
053
054    public XsltUriResolver(CamelContext context, String location) {
055        this.context = context;
056        this.location = location;
057        if (ResourceHelper.hasScheme(location)) {
058            baseScheme = ResourceHelper.getScheme(location);
059        } else {
060            // default to use classpath
061            baseScheme = "classpath:";
062        }
063    }
064
065    @Override
066    public Source resolve(String href, String base) throws TransformerException {
067        // supports the empty href
068        if (ObjectHelper.isEmpty(href)) {
069            href = location;
070        }
071        if (ObjectHelper.isEmpty(href)) {
072            throw new TransformerException("include href is empty");
073        }
074
075        LOG.trace("Resolving URI with href: {} and base: {}", href, base);
076
077        String scheme = ResourceHelper.getScheme(href);
078
079        if (scheme != null) {
080            // need to compact paths for file/classpath as it can be relative paths using .. to go backwards
081            String hrefPath = StringHelper.after(href, scheme);
082            if ("file:".equals(scheme)) {
083                // compact path use file OS separator
084                href = scheme + FileUtil.compactPath(hrefPath);
085            } else if ("classpath:".equals(scheme)) {
086                // for classpath always use /
087                href = scheme + FileUtil.compactPath(hrefPath, '/');
088            }
089            LOG.debug("Resolving URI from {}: {}", scheme, href);
090
091            InputStream is;
092            try {
093                is = ResourceHelper.resolveMandatoryResourceAsInputStream(context, href);
094            } catch (IOException e) {
095                throw new TransformerException(e);
096            }
097            return new StreamSource(is, href);
098        }
099
100        // if href and location is the same, then its the initial resolve
101        if (href.equals(location)) {
102            String path = baseScheme + href;
103            return resolve(path, base);
104        }
105
106        // okay then its relative to the starting location from the XSLT importing this one
107        String path = FileUtil.onlyPath(base);
108        if (ObjectHelper.isEmpty(path)) {
109            path = baseScheme + href;
110            return resolve(path, base);
111        } else {
112            if (ResourceHelper.hasScheme(path)) {
113                path = path + "/" + href;
114            } else {
115                path = baseScheme + path + "/" + href;
116            }
117            return resolve(path, base);
118        }
119    }
120    
121}