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.spi.ClassResolver;
027import org.apache.camel.util.FileUtil;
028import org.apache.camel.util.ObjectHelper;
029import org.apache.camel.util.ResourceHelper;
030import org.slf4j.Logger;
031import org.slf4j.LoggerFactory;
032
033/**
034 * Camel specific {@link javax.xml.transform.URIResolver} which is capable of loading files
035 * from the classpath and file system.
036 * <p/>
037 * Use prefix <tt>classpath:</tt> or <tt>file:</tt> to denote either classpath or file system.
038 * If no prefix is provided then the prefix from the <tt>location</tt> parameter is used.
039 * If it neither has a prefix then <tt>classpath:</tt> is used.
040 * <p/>
041 * This implementation <b>cannot</b> load files over http.
042 *
043 * @version 
044 */
045public class XsltUriResolver implements URIResolver {
046
047    private static final Logger LOG = LoggerFactory.getLogger(XsltUriResolver.class);
048
049    private final ClassResolver resolver;
050    private final String location;
051    private final String baseScheme;
052
053    public XsltUriResolver(ClassResolver resolver, String location) {
054        this.resolver = resolver;
055        this.location = location;
056        if (ResourceHelper.hasScheme(location)) {
057            baseScheme = ResourceHelper.getScheme(location);
058        } else {
059            // default to use classpath
060            baseScheme = "classpath:";
061        }
062    }
063
064    public Source resolve(String href, String base) throws TransformerException {
065        // supports the empty href
066        if (ObjectHelper.isEmpty(href)) {
067            href = location;
068        }
069        if (ObjectHelper.isEmpty(href)) {
070            throw new TransformerException("include href is empty");
071        }
072
073        LOG.trace("Resolving URI with href: {} and base: {}", href, base);
074
075        String scheme = ResourceHelper.getScheme(href);
076        if (scheme != null) {
077            // need to compact paths for file/classpath as it can be relative paths using .. to go backwards
078            if ("file:".equals(scheme)) {
079                // compact path use file OS separator
080                href = FileUtil.compactPath(href);
081            } else if ("classpath:".equals(scheme)) {
082                // for classpath always use /
083                href = FileUtil.compactPath(href, '/');
084            }
085            LOG.debug("Resolving URI from {}: {}", scheme, href);
086
087            InputStream is;
088            try {
089                is = ResourceHelper.resolveMandatoryResourceAsInputStream(resolver, href);
090            } catch (IOException e) {
091                throw new TransformerException(e);
092            }
093            return new StreamSource(is);
094        }
095
096        // if href and location is the same, then its the initial resolve
097        if (href.equals(location)) {
098            String path = baseScheme + href;
099            return resolve(path, base);
100        }
101
102        // okay then its relative to the starting location from the XSLT component
103        String path = FileUtil.onlyPath(location);
104        if (ObjectHelper.isEmpty(path)) {
105            path = baseScheme + href;
106            return resolve(path, base);
107        } else {
108            if (ResourceHelper.hasScheme(path)) {
109                path = path + "/" + href;
110            } else {
111                path = baseScheme + path + "/" + href;
112            }
113            return resolve(path, base);
114        }
115    }
116    
117}