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 = 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                InputStream is = getByteStream();
121                return camelContext.getTypeConverter().convertTo(Reader.class, is);
122            }
123    
124            @Override
125            public void setCharacterStream(Reader reader) {
126                // noop
127            }
128    
129            @Override
130            public InputStream getByteStream() {
131                try {
132                    return ResourceHelper.resolveMandatoryResourceAsInputStream(camelContext.getClassResolver(), uri);
133                } catch (IOException e) {
134                    throw ObjectHelper.wrapRuntimeCamelException(e);
135                }
136            }
137    
138            @Override
139            public void setByteStream(InputStream inputStream) {
140                // noop
141            }
142    
143            @Override
144            public String getStringData() {
145                InputStream is = getByteStream();
146                return camelContext.getTypeConverter().convertTo(String.class, is);
147            }
148    
149            @Override
150            public void setStringData(String stringData) {
151                // noop
152            }
153    
154            @Override
155            public String getSystemId() {
156                return systemId;
157            }
158    
159            @Override
160            public void setSystemId(String systemId) {
161                // noop
162            }
163    
164            @Override
165            public String getPublicId() {
166                return publicId;
167            }
168    
169            @Override
170            public void setPublicId(String publicId) {
171                // noop
172            }
173    
174            @Override
175            public String getBaseURI() {
176                return baseURI;
177            }
178    
179            @Override
180            public void setBaseURI(String baseURI) {
181                // noop
182            }
183    
184            @Override
185            public String getEncoding() {
186                return null;
187            }
188    
189            @Override
190            public void setEncoding(String encoding) {
191                // noop
192            }
193    
194            @Override
195            public boolean getCertifiedText() {
196                return false;
197            }
198    
199            @Override
200            public void setCertifiedText(boolean certifiedText) {
201                // noop
202            }
203    
204            @Override
205            public String toString() {
206                return "DefaultLSInput[" + uri + "]";
207            }
208        }
209    
210    }