OSDN Git Service

escape &,> in javadoc
[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<URI, URI>();
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      * @param base 絶対URIでなければならない。nullでもよい。
81      * @param relative 絶対URIでもよいがその場合baseは無視される。null可。
82      * @return 合成結果のURLオブジェクト。必ず絶対URIになる。
83      * @throws java.net.URISyntaxException URIとして変。
84      * @throws java.lang.IllegalArgumentException 絶対URIが生成できない。
85      */
86     protected static URI buildBaseRelativeURI(String base, String relative)
87             throws URISyntaxException,
88                    IllegalArgumentException {
89         URI baseURI;
90         if(base != null){
91             baseURI = new URI(base);
92             if( ! baseURI.isAbsolute() ){
93                 throw new IllegalArgumentException();
94             }
95         }else{
96             baseURI = null;
97         }
98
99         URI relativeURI;
100         if(relative != null){
101             relativeURI = new URI(relative);
102         }else{
103             relativeURI = EMPTY_URI;
104         }
105
106         URI resultURI;
107         if(baseURI == null || relativeURI.isAbsolute()){
108             resultURI = relativeURI;
109         }else{
110             resultURI = baseURI.resolve(relativeURI);
111         }
112
113         if( ! resultURI.isAbsolute() ){
114             throw new IllegalArgumentException();
115         }
116
117         resultURI = resultURI.normalize();
118
119         return resultURI;
120     }
121
122     /**
123      * LSInput実装を生成する。
124      * @return LSInput実装
125      */
126     public static LSInput createLSInput(){
127         LSInput input = new LSInputImpl();
128         return input;
129     }
130
131
132     /**
133      * オリジナルURIとリダイレクト先のURIを登録する。
134      * オリジナルURIへのアクセスはリダイレクトされる。
135      * @param original オリジナルURI
136      * @param redirect リダイレクトURI
137      */
138     private void putRedirectedImpl(URI original, URI redirect){
139         URI oridinalNorm = original.normalize();
140         URI redirectNorm = redirect.normalize();
141
142         this.uriMap.put(oridinalNorm, redirectNorm);
143
144         return;
145     }
146
147     /**
148      * オリジナルURIとリダイレクト先のURIを登録する。
149      * オリジナルURIへのアクセスはリダイレクトされる。
150      * @param original オリジナルURI
151      * @param redirect リダイレクトURI
152      */
153     public void putRedirected(URI original, URI redirect){
154         putRedirectedImpl(original, redirect);
155         return;
156     }
157
158     /**
159      * ローカル版リソース参照解決を登録する。
160      * @param lsc ローカル版リソース参照解決
161      */
162     public void putRedirected(LocalXmlResource lsc){
163         URI original = lsc.getOriginalResource();
164         if(original == null) return;
165
166         URI local = lsc.getLocalResource();
167
168         putRedirected(original, local);
169
170         return;
171     }
172
173     /**
174      * 別リゾルバの登録内容を追加登録する。
175      * @param other 別リゾルバ
176      */
177     public void putRedirected(XmlResourceResolver other){
178         this.uriMap.putAll(other.uriMap);
179         return;
180     }
181
182     /**
183      * 登録済みリダイレクト先URIを返す。
184      * @param original オリジナルURI
185      * @return リダイレクト先URI。未登録の場合はnull
186      */
187     public URI getRedirected(URI original){
188         URI keyURI = original.normalize();
189         URI resourceURI = this.uriMap.get(keyURI);
190         return resourceURI;
191     }
192
193     /**
194      * 登録済みリダイレクト先URIを返す。
195      * @param original オリジナルURI
196      * @return リダイレクト先URI。未登録の場合はオリジナルを返す
197      */
198     public URI resolveRedirected(URI original){
199         URI result = getRedirected(original);
200         if(result == null) result = original;
201         return result;
202     }
203
204     /**
205      * 登録済みリダイレクト先リソースの入力ストリームを得る。
206      * @param originalURI オリジナルURI
207      * @return 入力ストリーム。リダイレクト先が未登録の場合はnull
208      * @throws java.io.IOException 入出力エラー。
209      * もしくはリソースが見つからない。
210      */
211     private InputStream getXMLResourceAsStream(URI originalURI)
212             throws IOException{
213         URI resourceURI = getRedirected(originalURI);
214         if(resourceURI == null) return null;
215
216         URL resourceURL = resourceURI.toURL();
217         InputStream is = resourceURL.openStream();
218
219         return is;
220     }
221
222     /**
223      * {@inheritDoc}
224      * URL変換したあとの入力ソースを返す。
225      * @param type {@inheritDoc}
226      * @param namespaceURI {@inheritDoc}
227      * @param publicId {@inheritDoc}
228      * @param systemId {@inheritDoc}
229      * @param baseURI {@inheritDoc}
230      * @return {@inheritDoc}
231      */
232     @Override
233     public LSInput resolveResource(String type,
234                                      String namespaceURI,
235                                      String publicId,
236                                      String systemId,
237                                      String baseURI ){
238         if(systemId == null) return null;
239
240         URI originalURI;
241         try{
242             originalURI = buildBaseRelativeURI(baseURI, systemId);
243         }catch(URISyntaxException e){
244             return null;
245         }
246
247         InputStream is;
248         try{
249             is = getXMLResourceAsStream(originalURI);
250         }catch(IOException e){
251             return null;
252         }
253         if(is == null) return null;
254
255         LSInput input = createLSInput();
256         input.setBaseURI(baseURI);
257         input.setPublicId(publicId);
258         input.setSystemId(systemId);
259         input.setByteStream(is);
260
261         return input;
262     }
263
264     /**
265      * {@inheritDoc}
266      * URL変換したあとの入力ソースを返す。
267      * @param publicId {@inheritDoc}
268      * @param systemId {@inheritDoc}
269      * @return {@inheritDoc}
270      * @throws org.xml.sax.SAXException {@inheritDoc}
271      * @throws java.io.IOException {@inheritDoc}
272      */
273     @Override
274     public InputSource resolveEntity(String publicId, String systemId)
275             throws SAXException, IOException{
276         if(systemId == null) return null;
277
278         URI originalUri;
279         try{
280             originalUri = new URI(systemId);
281         }catch(URISyntaxException e){
282             return null;
283         }
284
285         InputStream is = getXMLResourceAsStream(originalUri);
286         if(is == null) return null;
287
288         InputSource source = new InputSource(is);
289         source.setPublicId(publicId);
290         source.setSystemId(systemId);
291
292         return source;
293     }
294
295     /**
296      * JRE1.5用LSInput実装。
297      * JRE1.6なら
298      * org.w3c.dom.ls.DOMImplementationLS#createLSInput()
299      * で生成可能かも。
300      */
301     private static final class LSInputImpl implements LSInput {
302
303         private String baseURI = null;
304         private InputStream byteStream = null;
305         private boolean certifiedText = false;
306         private Reader characterStream = null;
307         private String encoding = null;
308         private String publicId = null;
309         private String stringData = null;
310         private String systemId = null;
311
312         /**
313          * コンストラクタ。
314          */
315         LSInputImpl(){
316             super();
317             return;
318         }
319
320         /**
321          * {@inheritDoc}
322          * @return {@inheritDoc}
323          */
324         @Override
325         public String getBaseURI(){
326             return this.baseURI;
327         }
328
329         /**
330          * {@inheritDoc}
331          * @param baseURI {@inheritDoc}
332          */
333         @Override
334         public void setBaseURI(String baseURI){
335             this.baseURI = baseURI;
336             return;
337         }
338
339         /**
340          * {@inheritDoc}
341          * @return {@inheritDoc}
342          */
343         @Override
344         public InputStream getByteStream(){
345             return this.byteStream;
346         }
347
348         /**
349          * {@inheritDoc}
350          * @param byteStream {@inheritDoc}
351          */
352         @Override
353         public void setByteStream(InputStream byteStream){
354             this.byteStream = byteStream;
355         }
356
357         /**
358          * {@inheritDoc}
359          * @return {@inheritDoc}
360          */
361         @Override
362         public boolean getCertifiedText(){
363             return this.certifiedText;
364         }
365
366         /**
367          * {@inheritDoc}
368          * @param certifiedText {@inheritDoc}
369          */
370         @Override
371         public void setCertifiedText(boolean certifiedText){
372             this.certifiedText = certifiedText;
373             return;
374         }
375
376         /**
377          * {@inheritDoc}
378          * @return {@inheritDoc}
379          */
380         @Override
381         public Reader getCharacterStream(){
382             return this.characterStream;
383         }
384
385         /**
386          * {@inheritDoc}
387          * @param characterStream {@inheritDoc}
388          */
389         @Override
390         public void setCharacterStream(Reader characterStream){
391             this.characterStream = characterStream;
392         }
393
394         /**
395          * {@inheritDoc}
396          * @return {@inheritDoc}
397          */
398         @Override
399         public String getEncoding(){
400             return this.encoding;
401         }
402
403         /**
404          * {@inheritDoc}
405          * @param encoding {@inheritDoc}
406          */
407         @Override
408         public void setEncoding(String encoding){
409             this.encoding = encoding;
410             return;
411         }
412
413         /**
414          * {@inheritDoc}
415          * @return {@inheritDoc}
416          */
417         @Override
418         public String getPublicId(){
419             return this.publicId;
420         }
421
422         /**
423          * {@inheritDoc}
424          * @param publicId {@inheritDoc}
425          */
426         @Override
427         public void setPublicId(String publicId){
428             this.publicId = publicId;
429             return;
430         }
431
432         /**
433          * {@inheritDoc}
434          * @return {@inheritDoc}
435          */
436         @Override
437         public String getStringData(){
438             return this.stringData;
439         }
440
441         /**
442          * {@inheritDoc}
443          * @param stringData {@inheritDoc}
444          */
445         @Override
446         public void setStringData(String stringData){
447             this.stringData = stringData;
448             return;
449         }
450
451         /**
452          * {@inheritDoc}
453          * @return {@inheritDoc}
454          */
455         @Override
456         public String getSystemId(){
457             return this.systemId;
458         }
459
460         /**
461          * {@inheritDoc}
462          * @param systemId {@inheritDoc}
463          */
464         @Override
465         public void setSystemId(String systemId){
466             this.systemId = systemId;
467             return;
468         }
469
470     }
471
472 }