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     */
017    package org.apache.camel.builder.xml;
018    
019    import java.io.IOException;
020    import java.io.InputStream;
021    import javax.xml.transform.Source;
022    import javax.xml.transform.TransformerException;
023    import javax.xml.transform.URIResolver;
024    import javax.xml.transform.stream.StreamSource;
025    
026    import org.apache.camel.spi.ClassResolver;
027    import org.apache.camel.util.FileUtil;
028    import org.apache.camel.util.ObjectHelper;
029    import org.apache.camel.util.ResourceHelper;
030    import org.slf4j.Logger;
031    import 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     */
045    public 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    }