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.component.validator;
018
019import java.io.IOException;
020import java.io.InputStream;
021import java.io.Reader;
022import java.util.HashMap;
023import java.util.Map;
024
025import org.w3c.dom.ls.LSInput;
026import org.w3c.dom.ls.LSResourceResolver;
027
028import org.apache.camel.CamelContext;
029import org.apache.camel.util.FileUtil;
030import org.apache.camel.util.ObjectHelper;
031import org.apache.camel.util.ResourceHelper;
032
033/**
034 * Default {@link LSResourceResolver} which can included schema resources.
035 */
036public class DefaultLSResourceResolver implements LSResourceResolver {
037
038    private final CamelContext camelContext;
039    private final String resourceUri;
040    private final String resourcePath;
041    private String relatedURI;
042    private Map<String, String> relatedURIMap = new HashMap<String, String>();
043
044    public DefaultLSResourceResolver(CamelContext camelContext, String resourceUri) {
045        this.camelContext = camelContext;
046        this.resourceUri = resourceUri;
047        this.resourcePath = FileUtil.onlyPath(resourceUri);
048    }
049    
050    private String getUri(String systemId) {
051        if (resourcePath != null) {
052            return FileUtil.onlyPath(resourceUri) + "/" + systemId;
053        } else {
054            return systemId;
055        }
056    }
057
058    @Override
059    public LSInput resolveResource(String type, String namespaceURI, String publicId, String systemId, String baseURI) {
060        // systemId should be mandatory
061        if (systemId == null) {
062            throw new IllegalArgumentException(String.format("Resource: %s refers an invalid resource without SystemId."
063                    + " Invalid resource has type: %s, namespaceURI: %s, publicId: %s, systemId: %s, baseURI: %s", resourceUri, type, namespaceURI, publicId, systemId, baseURI));
064        }
065        String resourceURI = null;
066        // Build up the relative path for using relatedURI and baseURI
067        if (baseURI == null) {
068            relatedURI = FileUtil.compactPath(getUri(systemId), '/');
069            resourceURI = relatedURI;
070        } else {
071            String relatedPath = relatedURIMap.get(baseURI);
072            if (relatedPath == null) {
073                relatedPath = FileUtil.onlyPath(relatedURI);
074                if (relatedPath == null) {
075                    relatedPath = "";
076                }
077                relatedURI = FileUtil.compactPath(FileUtil.onlyPath(relatedURI) + "/" + systemId, '/');
078                resourceURI = relatedURI;
079                relatedURIMap.put(baseURI, relatedPath);
080            } else {
081                resourceURI = FileUtil.compactPath(relatedPath + "/" + systemId, '/');
082                relatedURI = resourceURI;
083            }
084        }
085        return new DefaultLSInput(publicId, systemId, baseURI, resourceURI);
086    }
087    
088    private final class DefaultLSInput implements LSInput {
089        
090        private final String publicId;
091        private final String systemId;
092        private final String baseURI;
093        private final String relatedURI;
094        private final String uri;
095
096        private DefaultLSInput(String publicId, String systemId, String basedURI, String relatedURI) {
097            this.publicId = publicId;
098            this.systemId = systemId;
099            this.baseURI = basedURI;
100            this.relatedURI = relatedURI;
101            this.uri = getInputUri();
102        }
103
104        private String getInputUri() {
105            // find the xsd with relative path
106            if (ObjectHelper.isNotEmpty(relatedURI)) {
107                try {
108                    ResourceHelper.resolveMandatoryResourceAsInputStream(camelContext.getClassResolver(), relatedURI);
109                    return relatedURI;
110                } catch (IOException e) {
111                   // ignore the exception
112                }
113            }
114            // don't use the relative path
115            return getUri(systemId);
116        }
117        
118        @Override
119        public Reader getCharacterStream() {
120            return null;
121        }
122
123        @Override
124        public void setCharacterStream(Reader reader) {
125            // noop
126        }
127
128        @Override
129        public InputStream getByteStream() {
130            try {
131                return ResourceHelper.resolveMandatoryResourceAsInputStream(camelContext.getClassResolver(), uri);
132            } catch (IOException e) {
133                throw ObjectHelper.wrapRuntimeCamelException(e);
134            }
135        }
136
137        @Override
138        public void setByteStream(InputStream inputStream) {
139            // noop
140        }
141
142        @Override
143        public String getStringData() {
144            InputStream is = getByteStream();
145            return camelContext.getTypeConverter().convertTo(String.class, is);
146        }
147
148        @Override
149        public void setStringData(String stringData) {
150            // noop
151        }
152
153        @Override
154        public String getSystemId() {
155            return systemId;
156        }
157
158        @Override
159        public void setSystemId(String systemId) {
160            // noop
161        }
162
163        @Override
164        public String getPublicId() {
165            return publicId;
166        }
167
168        @Override
169        public void setPublicId(String publicId) {
170            // noop
171        }
172
173        @Override
174        public String getBaseURI() {
175            return baseURI;
176        }
177
178        @Override
179        public void setBaseURI(String baseURI) {
180            // noop
181        }
182
183        @Override
184        public String getEncoding() {
185            return null;
186        }
187
188        @Override
189        public void setEncoding(String encoding) {
190            // noop
191        }
192
193        @Override
194        public boolean getCertifiedText() {
195            return false;
196        }
197
198        @Override
199        public void setCertifiedText(boolean certifiedText) {
200            // noop
201        }
202
203        @Override
204        public String toString() {
205            return "DefaultLSInput[" + uri + "]";
206        }
207    }
208
209}