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