OSDN Git Service

8d5f98933a98f6ae9eed0d629cf139c4d6299eb0
[mikutoga/Pmd2XML.git] / src / main / java / jp / sfjp / mikutoga / pmd2xml / XmlInputUtil.java
1 /*
2  * xml input utility
3  *
4  * License : The MIT License
5  * Copyright(c) 2013 MikuToga Partners
6  */
7
8 package jp.sfjp.mikutoga.pmd2xml;
9
10 import java.io.BufferedInputStream;
11 import java.io.File;
12 import java.io.IOException;
13 import java.io.InputStream;
14 import java.net.MalformedURLException;
15 import java.net.URI;
16 import java.net.URL;
17 import javax.xml.XMLConstants;
18 import javax.xml.parsers.ParserConfigurationException;
19 import javax.xml.parsers.SAXParser;
20 import javax.xml.parsers.SAXParserFactory;
21 import javax.xml.validation.Schema;
22 import jp.sfjp.mikutoga.pmd.model.xml.Schema101009;
23 import jp.sfjp.mikutoga.pmd.model.xml.Schema130128;
24 import jp.sfjp.mikutoga.xml.BotherHandler;
25 import jp.sfjp.mikutoga.xml.NoopEntityResolver;
26 import jp.sfjp.mikutoga.xml.SchemaUtil;
27 import org.xml.sax.InputSource;
28 import org.xml.sax.SAXException;
29 import org.xml.sax.SAXNotRecognizedException;
30 import org.xml.sax.SAXNotSupportedException;
31 import org.xml.sax.XMLReader;
32
33 /**
34  * XML入力に関する各種ユーティリティ。
35  */
36 final class XmlInputUtil {
37
38     private static final String F_DISALLOW_DOCTYPE_DECL =
39             "http://apache.org/xml/features/disallow-doctype-decl";
40     private static final String F_EXTERNAL_GENERAL_ENTITIES =
41             "http://xml.org/sax/features/external-general-entities";
42     private static final String F_EXTERNAL_PARAMETER_ENTITIES =
43             "http://xml.org/sax/features/external-parameter-entities";
44     private static final String F_LOAD_EXTERNAL_DTD =
45             "http://apache.org/xml/features/nonvalidating/load-external-dtd";
46
47
48     /**
49      * 隠しコンストラクタ。
50      */
51     private XmlInputUtil(){
52         assert false;
53         throw new AssertionError();
54     }
55
56
57     /**
58      * 実在ファイルからXML入力ソースを得る。
59      * @param file 実在ファイル
60      * @return XML入力ソース
61      */
62     static InputSource fileToSource(File file){
63         assert file.exists();
64
65         URI uri = file.toURI();
66
67         URL url;
68         try{
69             url = uri.toURL();
70         }catch(MalformedURLException e){
71             // 実在File由来のURLでは起こりえない
72             assert false;
73             throw new AssertionError(e);
74         }
75
76         String systemId = url.toString();
77
78         InputSource source = new InputSource(systemId);
79
80         return source;
81     }
82
83     /**
84      * InputSourceからInputStreamを得る。
85      * <p>入力ソースには、少なくともバイトストリームか
86      * URL文字列(SystemId)のいずれかが設定されていなければならない。
87      * @param source 入力ソース
88      * @return 入力バイトストリーム
89      * @throws IllegalArgumentException 入力ソースの設定が足りない。
90      * @throws IOException 入力ソースにアクセス不能。
91      */
92     static InputStream openInputSource(InputSource source)
93             throws IllegalArgumentException, IOException{
94         InputStream is;
95
96         is = source.getByteStream();
97
98         if(is == null){
99             String systemId = source.getSystemId();
100             if(systemId == null) throw new IllegalArgumentException();
101
102             URL url = new URL(systemId);
103             is = url.openStream();
104         }
105
106         is = new BufferedInputStream(is);
107
108         return is;
109     }
110
111     /**
112      * SAXパーサファクトリを生成する。
113      * <ul>
114      * <li>XML名前空間機能は有効になる。
115      * <li>DTDによる形式検証は無効となる。
116      * <li>XIncludeによる差し込み機能は無効となる。
117      * </ul>
118      * @param schema スキーマ
119      * @return ファクトリ
120      */
121     private static SAXParserFactory buildFactory(Schema schema){
122         SAXParserFactory factory = SAXParserFactory.newInstance();
123
124         factory.setNamespaceAware(true);
125         factory.setValidating(false);
126         factory.setXIncludeAware(false);
127
128         try{
129             factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
130             factory.setFeature(F_DISALLOW_DOCTYPE_DECL, true);
131             factory.setFeature(F_EXTERNAL_GENERAL_ENTITIES, false);
132             factory.setFeature(F_EXTERNAL_PARAMETER_ENTITIES, false);
133             factory.setFeature(F_LOAD_EXTERNAL_DTD, false);
134         }catch(   ParserConfigurationException
135                 | SAXNotRecognizedException
136                 | SAXNotSupportedException e
137                 ){
138             assert false;
139             throw new AssertionError(e);
140         }
141
142         factory.setSchema(schema);
143
144         return factory;
145     }
146
147     /**
148      * SAXパーサを生成する。
149      * @param schema スキーマ
150      * @return SAXパーサ
151      */
152     private static SAXParser buildParser(Schema schema){
153         SAXParserFactory factory = buildFactory(schema);
154
155         SAXParser parser;
156         try{
157             parser = factory.newSAXParser();
158         }catch(ParserConfigurationException | SAXException e){
159             assert false;
160             throw new AssertionError(e);
161         }
162
163         try{
164             parser.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
165             parser.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
166         }catch(SAXNotRecognizedException | SAXNotSupportedException e){
167             assert false;
168             throw new AssertionError(e);
169         }
170
171         return parser;
172     }
173
174     /**
175      * スキーマを生成する。
176      * @param resolver リゾルバ
177      * @param xmlInType 入力XML種別
178      * @return スキーマ
179      */
180     private static Schema buildSchema(ModelFileType xmlInType ){
181         URI[] schemaUris;
182         switch(xmlInType){
183         case XML_101009:
184             schemaUris = new URI[]{
185                 Schema101009.RES_SCHEMA_PMDXML,
186             };
187             break;
188         case XML_130128:
189             schemaUris = new URI[]{
190                 Schema130128.RES_SCHEMA_PMDXML,
191             };
192             break;
193         case XML_AUTO:
194             schemaUris = new URI[]{
195                 Schema101009.RES_SCHEMA_PMDXML,
196                 Schema130128.RES_SCHEMA_PMDXML,
197             };
198             break;
199         default:
200             throw new IllegalStateException();
201         }
202
203         Schema schema;
204         try{
205             schema = SchemaUtil.newSchema(schemaUris);
206         }catch(IOException | SAXException e){
207             assert false;
208             throw new AssertionError(e);
209         }
210
211         return schema;
212     }
213
214     /**
215      * XMLリーダを生成する。
216      * <p>エラーハンドラには{@link BotherHandler}が指定される。
217      * @param xmlInType 入力XML種別
218      * @return XMLリーダ
219      */
220     static XMLReader buildReader(ModelFileType xmlInType){
221         Schema schema = buildSchema(xmlInType);
222
223         SAXParser parser = buildParser(schema);
224
225         XMLReader reader;
226         try{
227             reader = parser.getXMLReader();
228         }catch(SAXException e){
229             assert false;
230             throw new AssertionError(e);
231         }
232
233         reader.setEntityResolver(NoopEntityResolver.NOOP_RESOLVER);
234         reader.setErrorHandler(BotherHandler.HANDLER);
235
236         return reader;
237     }
238
239 }