Cleanup use of Guava library
[yangtools.git] / yang / yang-model-api / src / main / java / org / opendaylight / yangtools / yang / model / repo / api / YinDomSchemaSource.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.yangtools.yang.model.repo.api;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static java.util.Objects.requireNonNull;
12 import static org.opendaylight.yangtools.yang.common.YangConstants.RFC6020_YIN_MODULE;
13 import static org.opendaylight.yangtools.yang.model.api.YangStmtMapping.MODULE;
14 import static org.opendaylight.yangtools.yang.model.api.YangStmtMapping.REVISION;
15 import static org.opendaylight.yangtools.yang.model.api.YangStmtMapping.SUBMODULE;
16
17 import com.google.common.base.MoreObjects;
18 import com.google.common.base.MoreObjects.ToStringHelper;
19 import java.util.Optional;
20 import javax.annotation.Nonnull;
21 import javax.xml.transform.Source;
22 import javax.xml.transform.TransformerException;
23 import javax.xml.transform.TransformerFactory;
24 import javax.xml.transform.dom.DOMResult;
25 import javax.xml.transform.dom.DOMSource;
26 import org.opendaylight.yangtools.yang.common.QName;
27 import org.opendaylight.yangtools.yang.common.YangConstants;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
30 import org.w3c.dom.Attr;
31 import org.w3c.dom.Element;
32 import org.w3c.dom.Node;
33 import org.w3c.dom.NodeList;
34
35 /**
36  * Utility {@link YinXmlSchemaSource} exposing a W3C {@link DOMSource} representation of YIN model.
37  */
38 public abstract class YinDomSchemaSource implements YinXmlSchemaSource {
39     private static final Logger LOG = LoggerFactory.getLogger(YinDomSchemaSource.class);
40     private static final TransformerFactory TRANSFORMER_FACTORY = TransformerFactory.newInstance();
41     private static final QName REVISION_STMT = REVISION.getStatementName();
42
43     YinDomSchemaSource() {
44         // Prevent outside instantiation
45     }
46
47     /**
48      * Create a new {@link YinDomSchemaSource} using an identifier and a source.
49      *
50      * @param identifier Schema source identifier
51      * @param source W3C DOM source
52      * @return A new {@link YinDomSchemaSource} instance.
53      */
54     public static @Nonnull YinDomSchemaSource create(@Nonnull final SourceIdentifier identifier,
55             final @Nonnull DOMSource source) {
56
57         final Node root = source.getNode().getFirstChild();
58         final String rootNs = root.getNamespaceURI();
59         if (rootNs == null) {
60             // Let whoever is using this deal with this
61             return new Simple(identifier, source);
62         }
63
64         final QName qname = QName.create(rootNs, root.getLocalName());
65         checkArgument(RFC6020_YIN_MODULE.equals(qname.getModule()),
66             "Root node namepsace %s does not match %s", rootNs, YangConstants.RFC6020_YIN_NAMESPACE);
67         checkArgument(MODULE.getStatementName().equals(qname)
68             || SUBMODULE.getStatementName().equals(qname), "Root element %s is not a module nor a submodule", qname);
69
70         checkArgument(root instanceof Element, "Root node %s is not an element", root);
71         final Element element = (Element)root;
72
73         final Attr nameAttr = element.getAttributeNode(MODULE.getArgumentName().getLocalName());
74         checkArgument(nameAttr != null, "No %s name argument found in %s", element.getLocalName());
75
76         final NodeList revisions = element.getElementsByTagNameNS(REVISION_STMT.getNamespace().toString(),
77             REVISION_STMT.getLocalName());
78         if (revisions.getLength() == 0) {
79             // FIXME: is module name important (as that may have changed)
80             return new Simple(identifier, source);
81         }
82
83         final Element revisionStmt = (Element) revisions.item(0);
84         final Attr dateAttr = revisionStmt.getAttributeNode(REVISION.getArgumentName().getLocalName());
85         checkArgument(dateAttr != null, "No revision statement argument found in %s", revisionStmt);
86
87         final SourceIdentifier parsedId = RevisionSourceIdentifier.create(nameAttr.getValue(),
88             Optional.of(dateAttr.getValue()));
89         final SourceIdentifier id;
90         if (!parsedId.equals(identifier)) {
91             LOG.debug("Changed identifier from {} to {}", identifier, parsedId);
92             id = parsedId;
93         } else {
94             id = identifier;
95         }
96
97         return new Simple(id, source);
98     }
99
100     /**
101      * Create a {@link YinDomSchemaSource} from a {@link YinXmlSchemaSource}. If the argument is already a
102      * YinDomSchemaSource, this method returns the same instance. The source will be translated on first access,
103      * at which point an {@link IllegalStateException} may be raised.
104      *
105      * @param xmlSchemaSource Backing schema source
106      * @return A {@link YinDomSchemaSource} instance
107      */
108     @Nonnull public static YinDomSchemaSource lazyTransform(final YinXmlSchemaSource xmlSchemaSource) {
109         final YinDomSchemaSource cast = castSchemaSource(xmlSchemaSource);
110         return cast != null ? cast : new Transforming(xmlSchemaSource);
111     }
112
113     /**
114      * Create a {@link YinDomSchemaSource} from a {@link YinXmlSchemaSource}. If the argument is already a
115      * YinDomSchemaSource, this method returns the same instance. The source will be translated immediately.
116      *
117      * @param xmlSchemaSource Backing schema source
118      * @return A {@link YinDomSchemaSource} instance
119      * @throws TransformerException when the provided source fails to transform
120      */
121     @Nonnull public static YinDomSchemaSource transform(final YinXmlSchemaSource xmlSchemaSource)
122             throws TransformerException {
123         final YinDomSchemaSource cast = castSchemaSource(xmlSchemaSource);
124         return cast != null ? cast :
125             create(xmlSchemaSource.getIdentifier(), transformSource(xmlSchemaSource.getSource()));
126     }
127
128     @Override
129     @Nonnull public abstract DOMSource getSource();
130
131     @Override
132     public final Class<? extends YinXmlSchemaSource> getType() {
133         return YinDomSchemaSource.class;
134     }
135
136     @Override
137     public final String toString() {
138         return addToStringAttributes(MoreObjects.toStringHelper(this).add("identifier", getIdentifier())).toString();
139     }
140
141     /**
142      * Add subclass-specific attributes to the output {@link #toString()} output. Since
143      * subclasses are prevented from overriding {@link #toString()} for consistency
144      * reasons, they can add their specific attributes to the resulting string by attaching
145      * attributes to the supplied {@link ToStringHelper}.
146      *
147      * @param toStringHelper ToStringHelper onto the attributes can be added
148      * @return ToStringHelper supplied as input argument.
149      */
150     protected abstract ToStringHelper addToStringAttributes(ToStringHelper toStringHelper);
151
152     static DOMSource transformSource(final Source source) throws TransformerException {
153         final DOMResult result = new DOMResult();
154         TRANSFORMER_FACTORY.newTransformer().transform(source, result);
155
156         return new DOMSource(result.getNode(), result.getSystemId());
157     }
158
159     private static YinDomSchemaSource castSchemaSource(final YinXmlSchemaSource xmlSchemaSource) {
160         if (xmlSchemaSource instanceof YinDomSchemaSource) {
161             return (YinDomSchemaSource) xmlSchemaSource;
162         }
163
164         final Source source = xmlSchemaSource.getSource();
165         if (source instanceof DOMSource) {
166             return create(xmlSchemaSource.getIdentifier(), (DOMSource) source);
167         }
168
169         return null;
170     }
171
172     private static final class Simple extends YinDomSchemaSource {
173         private final SourceIdentifier identifier;
174         private final DOMSource source;
175
176         Simple(@Nonnull final SourceIdentifier identifier, @Nonnull final DOMSource source) {
177             this.identifier = requireNonNull(identifier);
178             this.source = requireNonNull(source);
179         }
180
181         @Nonnull
182         @Override
183         public DOMSource getSource() {
184             return source;
185         }
186
187         @Override
188         public SourceIdentifier getIdentifier() {
189             return identifier;
190         }
191
192         @Override
193         protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
194             return toStringHelper.add("source", source);
195         }
196     }
197
198     private static final class Transforming extends YinDomSchemaSource {
199         private final YinXmlSchemaSource xmlSchemaSource;
200         private volatile DOMSource source;
201
202         Transforming(final YinXmlSchemaSource xmlSchemaSource) {
203             this.xmlSchemaSource = requireNonNull(xmlSchemaSource);
204         }
205
206         @Nonnull
207         @Override
208         public DOMSource getSource() {
209             DOMSource ret = source;
210             if (ret == null) {
211                 synchronized (this) {
212                     ret = source;
213                     if (ret == null) {
214                         try {
215                             ret = transformSource(xmlSchemaSource.getSource());
216                         } catch (TransformerException e) {
217                             throw new IllegalStateException("Failed to transform schema source " + xmlSchemaSource, e);
218                         }
219                         source = ret;
220                     }
221                 }
222             }
223
224             return ret;
225         }
226
227         @Override
228         public SourceIdentifier getIdentifier() {
229             return xmlSchemaSource.getIdentifier();
230         }
231
232         @Override
233         protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
234             return toStringHelper.add("xmlSchemaSource", xmlSchemaSource);
235         }
236     }
237 }