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