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