1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 package gov.nist.secauto.metaschema.core.model.xml;
28
29 import gov.nist.secauto.metaschema.core.model.IModule;
30 import gov.nist.secauto.metaschema.core.model.MetaschemaException;
31 import gov.nist.secauto.metaschema.core.model.constraint.IConstraintSet;
32 import gov.nist.secauto.metaschema.core.model.xml.xmlbeans.METASCHEMADocument;
33 import gov.nist.secauto.metaschema.core.model.xml.xmlbeans.MetaschemaImportType;
34 import gov.nist.secauto.metaschema.core.util.CollectionUtil;
35 import gov.nist.secauto.metaschema.core.util.ObjectUtils;
36
37 import org.apache.xmlbeans.XmlException;
38 import org.apache.xmlbeans.XmlOptions;
39 import org.xml.sax.EntityResolver;
40 import org.xml.sax.InputSource;
41 import org.xml.sax.SAXException;
42 import org.xml.sax.XMLReader;
43
44 import java.io.IOException;
45 import java.net.URI;
46 import java.util.ArrayList;
47 import java.util.Collection;
48 import java.util.Collections;
49 import java.util.Deque;
50 import java.util.LinkedHashMap;
51 import java.util.List;
52 import java.util.Map;
53 import java.util.Set;
54
55 import javax.xml.XMLConstants;
56 import javax.xml.parsers.ParserConfigurationException;
57 import javax.xml.parsers.SAXParser;
58 import javax.xml.parsers.SAXParserFactory;
59
60 import edu.umd.cs.findbugs.annotations.NonNull;
61
62
63
64
65
66
67
68 public class ModuleLoader
69 extends AbstractLoader<IModule> {
70 private boolean resolveEntities;
71
72 @NonNull
73 private final Set<IConstraintSet> registeredConstraintSets;
74
75
76
77
78 public ModuleLoader() {
79 this(CollectionUtil.emptySet());
80 }
81
82
83
84
85
86
87
88
89 public ModuleLoader(@NonNull Set<IConstraintSet> additionalConstraintSets) {
90 this.registeredConstraintSets = CollectionUtil.unmodifiableSet(additionalConstraintSets);
91 }
92
93
94
95
96
97
98 @NonNull
99 protected Set<IConstraintSet> getRegisteredConstraintSets() {
100 return registeredConstraintSets;
101 }
102
103
104
105
106
107
108 public void allowEntityResolution() {
109 resolveEntities = true;
110 }
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126 protected IModule newXmlMetaschema(
127 @NonNull URI resource,
128 @NonNull METASCHEMADocument xmlObject,
129 @NonNull List<IModule> importedModules) throws MetaschemaException {
130 IModule retval = new XmlModule(resource, xmlObject, importedModules);
131
132 IConstraintSet.applyConstraintSetToModule(getRegisteredConstraintSets(), retval);
133
134 return retval;
135 }
136
137 @Override
138 protected IModule parseResource(@NonNull URI resource, @NonNull Deque<URI> visitedResources)
139 throws IOException {
140
141 METASCHEMADocument xmlObject = parseModule(resource);
142
143
144 int size = xmlObject.getMETASCHEMA().sizeOfImportArray();
145 @NonNull Map<URI, IModule> importedModules;
146 if (size == 0) {
147 importedModules = ObjectUtils.notNull(Collections.emptyMap());
148 } else {
149 try {
150 importedModules = new LinkedHashMap<>();
151 for (MetaschemaImportType imported : xmlObject.getMETASCHEMA().getImportList()) {
152 URI importedResource = URI.create(imported.getHref());
153 importedResource = ObjectUtils.notNull(resource.resolve(importedResource));
154 importedModules.put(importedResource, loadInternal(importedResource, visitedResources));
155 }
156 } catch (MetaschemaException ex) {
157 throw new IOException(ex);
158 }
159 }
160
161
162 Collection<IModule> values = importedModules.values();
163 try {
164 return newXmlMetaschema(resource, xmlObject, new ArrayList<>(values));
165 } catch (MetaschemaException ex) {
166 throw new IOException(ex);
167 }
168 }
169
170
171
172
173
174
175
176
177
178
179 protected METASCHEMADocument parseModule(@NonNull URI resource) throws IOException {
180 METASCHEMADocument metaschemaXml;
181 try {
182 XmlOptions options = new XmlOptions();
183 if (resolveEntities) {
184 SAXParserFactory factory = SAXParserFactory.newInstance();
185
186 try {
187
188 factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", false);
189 factory.setFeature("http://xml.org/sax/features/external-general-entities", true);
190 factory.setFeature("http://xml.org/sax/features/external-parameter-entities", true);
191 SAXParser parser = factory.newSAXParser();
192 parser.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "file");
193 XMLReader reader = parser.getXMLReader();
194 reader.setEntityResolver(new EntityResolver() {
195
196 @Override
197 public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
198 return null;
199 }
200
201 });
202 options.setLoadUseXMLReader(reader);
203 } catch (SAXException | ParserConfigurationException ex) {
204 throw new IOException(ex);
205 }
206
207
208 options.setEntityResolver(new EntityResolver() {
209
210 @Override
211 public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
212 String effectiveSystemId = systemId;
213
214 if (effectiveSystemId.startsWith("file://file://")) {
215 effectiveSystemId = effectiveSystemId.substring(14);
216 }
217 URI resolvedSystemId = resource.resolve(effectiveSystemId);
218 return new InputSource(resolvedSystemId.toString());
219 }
220
221 });
222 options.setLoadDTDGrammar(true);
223 }
224 options.setBaseURI(resource);
225 options.setLoadLineNumbers();
226 metaschemaXml = METASCHEMADocument.Factory.parse(resource.toURL(), options);
227 } catch (XmlException ex) {
228 throw new IOException(ex);
229 }
230 return metaschemaXml;
231 }
232
233 }