OSDN Git Service

196a90084a4549b4da642bae26eb89ec4114c4f4
[mikutoga/TogaGem.git] / src / main / java / jp / sfjp / mikutoga / xml / XmlResourceResolver.java
1 /*
2  * xml resource resolver
3  *
4  * License : The MIT License
5  * Copyright(c) 2009 olyutorskii
6  */
7
8 package jp.sfjp.mikutoga.xml;
9
10 import java.io.IOException;
11 import java.io.InputStream;
12 import java.io.Reader;
13 import java.net.URI;
14 import java.net.URISyntaxException;
15 import java.net.URL;
16 import java.util.Collections;
17 import java.util.HashMap;
18 import java.util.Map;
19 import org.w3c.dom.ls.LSInput;
20 import org.w3c.dom.ls.LSResourceResolver;
21 import org.xml.sax.EntityResolver;
22 import org.xml.sax.InputSource;
23 import org.xml.sax.SAXException;
24
25 /**
26  * URL変換マップに従い、XML文書からの外部参照をリダイレクトする。
27  * 相対URIはこのクラスをベースに解決される。
28  * 主な用途は外部スキーマのリソース化など。
29  */
30 public class XmlResourceResolver
31         implements LSResourceResolver, EntityResolver {
32
33     /** XML Schema. */
34     public static final String SCHEMA_XML =
35             "http://www.w3.org/2001/xml.xsd";
36
37     /** XSD名前空間。 */
38     public static final String NS_XSD =
39             "http://www.w3.org/2001/XMLSchema-instance";
40
41     private static final String LOCAL_SCHEMA_XML =
42             "resources/xmlspace.xsd";
43
44     private static final URI EMPTY_URI = URI.create("");
45
46     private static final Class<?> THISCLASS = XmlResourceResolver.class;
47
48
49     private final Map<URI, URI> uriMap;
50
51
52     /**
53      * コンストラクタ。
54      */
55     public XmlResourceResolver(){
56         super();
57
58         assert this.getClass().equals(THISCLASS);
59
60         Map<URI, URI> map;
61         map = new HashMap<>();
62         map = Collections.synchronizedMap(map);
63         this.uriMap = map;
64
65         URL redirectRes = THISCLASS.getResource(LOCAL_SCHEMA_XML);
66         String redirectResName = redirectRes.toString();
67
68         URI originalURI = URI.create(SCHEMA_XML);
69         URI redirectURI = URI.create(redirectResName);
70
71         putRedirectedImpl(originalURI, redirectURI);
72
73         return;
74     }
75
76
77     /**
78      * 絶対URIと相対URIを合成したURIを返す。
79      * 正規化も行われる。
80      *
81      * @param base 絶対URIでなければならない。nullでもよい。
82      * @param relative 絶対URIでもよいがその場合baseは無視される。null可。
83      * @return 合成結果のURLオブジェクト。必ず絶対URIになる。
84      * @throws java.net.URISyntaxException URIとして変。
85      * @throws java.lang.IllegalArgumentException 絶対URIが生成できない。
86      */
87     protected static URI buildBaseRelativeURI(String base, String relative)
88             throws URISyntaxException,
89                    IllegalArgumentException {
90         URI baseURI;
91         if(base != null){
92             baseURI = new URI(base);
93             if( ! baseURI.isAbsolute() ){
94                 throw new IllegalArgumentException();
95             }
96         }else{
97             baseURI = null;
98         }
99
100         URI relativeURI;
101         if(relative != null){
102             relativeURI = new URI(relative);
103         }else{
104             relativeURI = EMPTY_URI;
105         }
106         
107         URI result = buildBaseRelativeURI(baseURI, relativeURI);
108         return result;
109     }
110
111     /**
112      * 絶対URIと相対URIを合成したURIを返す。
113      * 正規化も行われる。
114      *
115      * @param baseURI 絶対URIでなければならない。nullでもよい。
116      * @param relativeURI 絶対URIでもよいがその場合baseは無視される。
117      * @return 合成結果のURLオブジェクト。必ず絶対URIになる。
118      * @throws java.lang.IllegalArgumentException 絶対URIが生成できない。
119      */
120     private static URI buildBaseRelativeURI(URI baseURI, URI relativeURI)
121             throws IllegalArgumentException {
122         URI resultURI;
123
124         if(baseURI == null || relativeURI.isAbsolute()){
125             resultURI = relativeURI;
126         }else{
127             resultURI = baseURI.resolve(relativeURI);
128         }
129
130         if( ! resultURI.isAbsolute() ){
131             throw new IllegalArgumentException();
132         }
133
134         resultURI = resultURI.normalize();
135
136         return resultURI;
137     }
138
139     /**
140      * LSInput実装を生成する。
141      * @return LSInput実装
142      */
143     public static LSInput createLSInput(){
144         LSInput input = new LSInputImpl();
145         return input;
146     }
147
148
149     /**
150      * オリジナルURIとリダイレクト先のURIを登録する。
151      * オリジナルURIへのアクセスはリダイレクトされる。
152      * @param original オリジナルURI
153      * @param redirect リダイレクトURI
154      */
155     private void putRedirectedImpl(URI original, URI redirect){
156         URI oridinalNorm = original.normalize();
157         URI redirectNorm = redirect.normalize();
158
159         this.uriMap.put(oridinalNorm, redirectNorm);
160
161         return;
162     }
163
164     /**
165      * オリジナルURIとリダイレクト先のURIを登録する。
166      * オリジナルURIへのアクセスはリダイレクトされる。
167      * @param original オリジナルURI
168      * @param redirect リダイレクトURI
169      */
170     public void putRedirected(URI original, URI redirect){
171         putRedirectedImpl(original, redirect);
172         return;
173     }
174
175     /**
176      * ローカル版リソース参照解決を登録する。
177      * @param lsc ローカル版リソース参照解決
178      */
179     public void putRedirected(LocalXmlResource lsc){
180         URI original = lsc.getOriginalResource();
181         if(original == null) return;
182
183         URI local = lsc.getLocalResource();
184
185         putRedirected(original, local);
186
187         return;
188     }
189
190     /**
191      * 別リゾルバの登録内容を追加登録する。
192      * @param other 別リゾルバ
193      */
194     public void putRedirected(XmlResourceResolver other){
195         this.uriMap.putAll(other.uriMap);
196         return;
197     }
198
199     /**
200      * 登録済みリダイレクト先URIを返す。
201      * @param original オリジナルURI
202      * @return リダイレクト先URI。未登録の場合はnull
203      */
204     public URI getRedirected(URI original){
205         URI keyURI = original.normalize();
206         URI resourceURI = this.uriMap.get(keyURI);
207         return resourceURI;
208     }
209
210     /**
211      * 登録済みリダイレクト先URIを返す。
212      * @param original オリジナルURI
213      * @return リダイレクト先URI。未登録の場合はオリジナルを返す
214      */
215     public URI resolveRedirected(URI original){
216         URI result = getRedirected(original);
217         if(result == null) result = original;
218         return result;
219     }
220
221     /**
222      * 登録済みリダイレクト先リソースの入力ストリームを得る。
223      * @param originalURI オリジナルURI
224      * @return 入力ストリーム。リダイレクト先が未登録の場合はnull
225      * @throws java.io.IOException 入出力エラー。
226      *     もしくはリソースが見つからない。
227      */
228     private InputStream getXMLResourceAsStream(URI originalURI)
229             throws IOException{
230         URI resourceURI = getRedirected(originalURI);
231         if(resourceURI == null) return null;
232
233         URL resourceURL = resourceURI.toURL();
234         InputStream is = resourceURL.openStream();
235
236         return is;
237     }
238
239     /**
240      * {@inheritDoc}
241      * URL変換したあとの入力ソースを返す。
242      * @param type {@inheritDoc}
243      * @param namespaceURI {@inheritDoc}
244      * @param publicId {@inheritDoc}
245      * @param systemId {@inheritDoc}
246      * @param baseURI {@inheritDoc}
247      * @return {@inheritDoc}
248      */
249     @Override
250     public LSInput resolveResource(String type,
251                                      String namespaceURI,
252                                      String publicId,
253                                      String systemId,
254                                      String baseURI ){
255         if(systemId == null) return null;
256
257         URI originalURI;
258         try{
259             originalURI = buildBaseRelativeURI(baseURI, systemId);
260         }catch(URISyntaxException e){
261             return null;
262         }
263
264         InputStream is;
265         try{
266             is = getXMLResourceAsStream(originalURI);
267         }catch(IOException e){
268             return null;
269         }
270         if(is == null) return null;
271
272         LSInput input = createLSInput();
273         input.setBaseURI(baseURI);
274         input.setPublicId(publicId);
275         input.setSystemId(systemId);
276         input.setByteStream(is);
277
278         return input;
279     }
280
281     /**
282      * {@inheritDoc}
283      * URL変換したあとの入力ソースを返す。
284      * @param publicId {@inheritDoc}
285      * @param systemId {@inheritDoc}
286      * @return {@inheritDoc}
287      * @throws org.xml.sax.SAXException {@inheritDoc}
288      * @throws java.io.IOException {@inheritDoc}
289      */
290     @Override
291     public InputSource resolveEntity(String publicId, String systemId)
292             throws SAXException, IOException{
293         if(systemId == null) return null;
294
295         URI originalUri;
296         try{
297             originalUri = new URI(systemId);
298         }catch(URISyntaxException e){
299             return null;
300         }
301
302         InputStream is = getXMLResourceAsStream(originalUri);
303         if(is == null) return null;
304
305         InputSource source = new InputSource(is);
306         source.setPublicId(publicId);
307         source.setSystemId(systemId);
308
309         return source;
310     }
311
312     /**
313      * JRE1.5用LSInput実装。
314      * JRE1.6なら
315      * org.w3c.dom.ls.DOMImplementationLS#createLSInput()
316      * で生成可能かも。
317      */
318     private static final class LSInputImpl implements LSInput {
319
320         private String baseURI = null;
321         private InputStream byteStream = null;
322         private boolean certifiedText = false;
323         private Reader characterStream = null;
324         private String encoding = null;
325         private String publicId = null;
326         private String stringData = null;
327         private String systemId = null;
328
329         /**
330          * コンストラクタ。
331          */
332         LSInputImpl(){
333             super();
334             return;
335         }
336
337         /**
338          * {@inheritDoc}
339          * @return {@inheritDoc}
340          */
341         @Override
342         public String getBaseURI(){
343             return this.baseURI;
344         }
345
346         /**
347          * {@inheritDoc}
348          * @param baseURI {@inheritDoc}
349          */
350         @Override
351         public void setBaseURI(String baseURI){
352             this.baseURI = baseURI;
353             return;
354         }
355
356         /**
357          * {@inheritDoc}
358          * @return {@inheritDoc}
359          */
360         @Override
361         public InputStream getByteStream(){
362             return this.byteStream;
363         }
364
365         /**
366          * {@inheritDoc}
367          * @param byteStream {@inheritDoc}
368          */
369         @Override
370         public void setByteStream(InputStream byteStream){
371             this.byteStream = byteStream;
372         }
373
374         /**
375          * {@inheritDoc}
376          * @return {@inheritDoc}
377          */
378         @Override
379         public boolean getCertifiedText(){
380             return this.certifiedText;
381         }
382
383         /**
384          * {@inheritDoc}
385          * @param certifiedText {@inheritDoc}
386          */
387         @Override
388         public void setCertifiedText(boolean certifiedText){
389             this.certifiedText = certifiedText;
390             return;
391         }
392
393         /**
394          * {@inheritDoc}
395          * @return {@inheritDoc}
396          */
397         @Override
398         public Reader getCharacterStream(){
399             return this.characterStream;
400         }
401
402         /**
403          * {@inheritDoc}
404          * @param characterStream {@inheritDoc}
405          */
406         @Override
407         public void setCharacterStream(Reader characterStream){
408             this.characterStream = characterStream;
409         }
410
411         /**
412          * {@inheritDoc}
413          * @return {@inheritDoc}
414          */
415         @Override
416         public String getEncoding(){
417             return this.encoding;
418         }
419
420         /**
421          * {@inheritDoc}
422          * @param encoding {@inheritDoc}
423          */
424         @Override
425         public void setEncoding(String encoding){
426             this.encoding = encoding;
427             return;
428         }
429
430         /**
431          * {@inheritDoc}
432          * @return {@inheritDoc}
433          */
434         @Override
435         public String getPublicId(){
436             return this.publicId;
437         }
438
439         /**
440          * {@inheritDoc}
441          * @param publicId {@inheritDoc}
442          */
443         @Override
444         public void setPublicId(String publicId){
445             this.publicId = publicId;
446             return;
447         }
448
449         /**
450          * {@inheritDoc}
451          * @return {@inheritDoc}
452          */
453         @Override
454         public String getStringData(){
455             return this.stringData;
456         }
457
458         /**
459          * {@inheritDoc}
460          * @param stringData {@inheritDoc}
461          */
462         @Override
463         public void setStringData(String stringData){
464             this.stringData = stringData;
465             return;
466         }
467
468         /**
469          * {@inheritDoc}
470          * @return {@inheritDoc}
471          */
472         @Override
473         public String getSystemId(){
474             return this.systemId;
475         }
476
477         /**
478          * {@inheritDoc}
479          * @param systemId {@inheritDoc}
480          */
481         @Override
482         public void setSystemId(String systemId){
483             this.systemId = systemId;
484             return;
485         }
486
487     }
488
489 }