1
2
3
4
5
6 package gov.nist.secauto.metaschema.databind.io.xml;
7
8 import com.ctc.wstx.stax.WstxInputFactory;
9
10 import gov.nist.secauto.metaschema.core.configuration.IMutableConfiguration;
11 import gov.nist.secauto.metaschema.core.metapath.item.node.IDocumentNodeItem;
12 import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItemFactory;
13 import gov.nist.secauto.metaschema.core.model.IBoundObject;
14 import gov.nist.secauto.metaschema.core.util.AutoCloser;
15 import gov.nist.secauto.metaschema.core.util.ObjectUtils;
16 import gov.nist.secauto.metaschema.databind.io.AbstractDeserializer;
17 import gov.nist.secauto.metaschema.databind.io.DeserializationFeature;
18 import gov.nist.secauto.metaschema.databind.model.IBoundDefinitionModelAssembly;
19
20 import org.codehaus.stax2.XMLEventReader2;
21 import org.codehaus.stax2.XMLInputFactory2;
22
23 import java.io.IOException;
24 import java.io.Reader;
25 import java.net.URI;
26
27 import javax.xml.stream.EventFilter;
28 import javax.xml.stream.XMLEventReader;
29 import javax.xml.stream.XMLInputFactory;
30 import javax.xml.stream.XMLResolver;
31 import javax.xml.stream.XMLStreamException;
32
33 import edu.umd.cs.findbugs.annotations.NonNull;
34 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
35 import nl.talsmasoftware.lazy4j.Lazy;
36
37
38
39
40
41
42
43
44 public class DefaultXmlDeserializer<CLASS extends IBoundObject>
45 extends AbstractDeserializer<CLASS> {
46 private Lazy<XMLInputFactory2> factory;
47
48 @NonNull
49 private final IBoundDefinitionModelAssembly rootDefinition;
50
51
52
53
54
55
56
57
58
59 @SuppressFBWarnings(value = "CT_CONSTRUCTOR_THROW", justification = "Use of final fields")
60 public DefaultXmlDeserializer(@NonNull IBoundDefinitionModelAssembly definition) {
61 super(definition);
62 this.rootDefinition = definition;
63 if (!definition.isRoot()) {
64 throw new UnsupportedOperationException(
65 String.format("The assembly '%s' is not a root assembly.", definition.getBoundClass().getName()));
66 }
67 resetFactory();
68 }
69
70
71
72
73
74
75 protected final void resetFactory() {
76 this.factory = Lazy.lazy(this::newFactoryInstance);
77 }
78
79 @Override
80 protected void configurationChanged(IMutableConfiguration<DeserializationFeature<?>> config) {
81 super.configurationChanged(config);
82 resetFactory();
83 }
84
85
86
87
88
89
90
91
92
93 @SuppressWarnings("resource")
94 @NonNull
95 protected XMLInputFactory2 newFactoryInstance() {
96 XMLInputFactory2 retval = (XMLInputFactory2) XMLInputFactory.newInstance();
97 assert retval instanceof WstxInputFactory;
98 retval.configureForXmlConformance();
99 retval.setProperty(XMLInputFactory.IS_COALESCING, false);
100 retval.setProperty(XMLInputFactory2.P_PRESERVE_LOCATION, true);
101
102
103 if (isFeatureEnabled(DeserializationFeature.DESERIALIZE_XML_ALLOW_ENTITY_RESOLUTION)) {
104 retval.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, true);
105 retval.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, true);
106 retval.setProperty(XMLInputFactory.SUPPORT_DTD, true);
107 retval.setProperty(XMLInputFactory.RESOLVER,
108 (XMLResolver) (publicID, systemID, baseURI, namespace) -> {
109 URI base = URI.create(baseURI);
110 URI resource = base.resolve(systemID);
111 try {
112
113 return ObjectUtils.notNull(resource.toURL().openStream());
114 } catch (IOException ex) {
115 throw new XMLStreamException(ex);
116 }
117 });
118 }
119 return retval;
120 }
121
122
123
124
125
126
127
128
129
130 @NonNull
131 private XMLInputFactory2 getXMLInputFactory() {
132 return ObjectUtils.notNull(factory.get());
133 }
134
135 @NonNull
136 private XMLEventReader2 newXMLEventReader2(
137 @NonNull URI documentUri,
138 @NonNull Reader reader) throws XMLStreamException {
139 XMLEventReader2 eventReader
140 = (XMLEventReader2) getXMLInputFactory().createXMLEventReader(documentUri.toASCIIString(), reader);
141 EventFilter filter = new CommentFilter();
142 return ObjectUtils.notNull((XMLEventReader2) getXMLInputFactory().createFilteredReader(eventReader, filter));
143 }
144
145 @Override
146 protected final IDocumentNodeItem deserializeToNodeItemInternal(Reader reader, URI documentUri) throws IOException {
147 Object value = deserializeToValueInternal(reader, documentUri);
148 return INodeItemFactory.instance().newDocumentNodeItem(rootDefinition, documentUri, value);
149 }
150
151 @Override
152 public final CLASS deserializeToValueInternal(Reader reader, URI resource) throws IOException {
153
154 try (AutoCloser<XMLEventReader2, XMLStreamException> closer = AutoCloser.autoClose(
155 newXMLEventReader2(resource, reader), XMLEventReader::close)) {
156 return parseXmlInternal(closer.getResource(), resource);
157 } catch (XMLStreamException ex) {
158 throw new IOException("Unable to create a new XMLEventReader2 instance.", ex);
159 }
160 }
161
162 @NonNull
163 private CLASS parseXmlInternal(@NonNull XMLEventReader2 reader, @NonNull URI resource)
164 throws IOException {
165
166 MetaschemaXmlReader parser = new MetaschemaXmlReader(reader, resource, new DefaultXmlProblemHandler());
167
168 try {
169 return parser.read(rootDefinition);
170 } catch (IOException | AssertionError ex) {
171 throw new IOException(
172 String.format("An unexpected error occurred during parsing: %s", ex.getMessage()),
173 ex);
174 }
175 }
176 }