2 * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.yangtools.yang.model.repo.api;
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;
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;
35 * Utility {@link YinXmlSchemaSource} exposing a W3C {@link DOMSource} representation of YIN model.
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();
42 YinDomSchemaSource() {
43 // Prevent outside instantiation
47 * Create a new {@link YinDomSchemaSource} using an identifier and a source.
49 * @param identifier Schema source identifier
50 * @param source W3C DOM source
51 * @return A new {@link YinDomSchemaSource} instance.
53 public static @Nonnull YinDomSchemaSource create(@Nonnull final SourceIdentifier identifier,
54 final @Nonnull DOMSource source) {
56 final Node root = source.getNode().getFirstChild();
57 final String rootNs = root.getNamespaceURI();
59 // Let whoever is using this deal with this
60 return new Simple(identifier, source);
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);
69 Preconditions.checkArgument(root instanceof Element, "Root node %s is not an element", root);
70 final Element element = (Element)root;
72 final Attr nameAttr = element.getAttributeNode(MODULE.getArgumentName().getLocalName());
73 Preconditions.checkArgument(nameAttr != null, "No %s name argument found in %s", element.getLocalName());
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);
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);
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);
96 return new Simple(id, source);
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.
104 * @param xmlSchemaSource Backing schema source
105 * @return A {@link YinDomSchemaSource} instance
107 @Nonnull public static YinDomSchemaSource lazyTransform(final YinXmlSchemaSource xmlSchemaSource) {
108 final YinDomSchemaSource cast = castSchemaSource(xmlSchemaSource);
109 return cast != null ? cast : new Transforming(xmlSchemaSource);
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.
116 * @param xmlSchemaSource Backing schema source
117 * @return A {@link YinDomSchemaSource} instance
118 * @throws TransformerException when the provided source fails to transform
120 @Nonnull public static YinDomSchemaSource transform(final YinXmlSchemaSource xmlSchemaSource) throws TransformerException {
121 final YinDomSchemaSource cast = castSchemaSource(xmlSchemaSource);
122 return cast != null ? cast :
123 create(xmlSchemaSource.getIdentifier(), transformSource(xmlSchemaSource.getSource()));
127 @Nonnull public abstract DOMSource getSource();
130 public final Class<? extends YinXmlSchemaSource> getType() {
131 return YinDomSchemaSource.class;
135 public final String toString() {
136 return addToStringAttributes(MoreObjects.toStringHelper(this).add("identifier", getIdentifier())).toString();
140 * Add subclass-specific attributes to the output {@link #toString()} output. Since
141 * subclasses are prevented from overriding {@link #toString()} for consistency
142 * reasons, they can add their specific attributes to the resulting string by attaching
143 * attributes to the supplied {@link ToStringHelper}.
145 * @param toStringHelper ToStringHelper onto the attributes can be added
146 * @return ToStringHelper supplied as input argument.
148 protected abstract ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper);
150 static DOMSource transformSource(final Source source) throws TransformerException {
151 final DOMResult result = new DOMResult();
152 TRANSFORMER_FACTORY.newTransformer().transform(source, result);
154 return new DOMSource(result.getNode(), result.getSystemId());
157 private static YinDomSchemaSource castSchemaSource(final YinXmlSchemaSource xmlSchemaSource) {
158 if (xmlSchemaSource instanceof YinDomSchemaSource) {
159 return (YinDomSchemaSource) xmlSchemaSource;
162 final Source source = xmlSchemaSource.getSource();
163 if (source instanceof DOMSource) {
164 return create(xmlSchemaSource.getIdentifier(), (DOMSource) source);
170 private static final class Simple extends YinDomSchemaSource {
171 private final SourceIdentifier identifier;
172 private final DOMSource source;
174 Simple(@Nonnull final SourceIdentifier identifier, @Nonnull final DOMSource source) {
175 this.identifier = Preconditions.checkNotNull(identifier);
176 this.source = Preconditions.checkNotNull(source);
181 public DOMSource getSource() {
186 public SourceIdentifier getIdentifier() {
191 protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
192 return toStringHelper.add("source", source);
196 private static final class Transforming extends YinDomSchemaSource {
197 private final YinXmlSchemaSource xmlSchemaSource;
198 private volatile DOMSource source;
200 Transforming(final YinXmlSchemaSource xmlSchemaSource) {
201 this.xmlSchemaSource = Preconditions.checkNotNull(xmlSchemaSource);
206 public DOMSource getSource() {
207 DOMSource ret = source;
209 synchronized (this) {
213 ret = transformSource(xmlSchemaSource.getSource());
214 } catch (TransformerException e) {
215 throw new IllegalStateException("Failed to transform schema source " + xmlSchemaSource, e);
226 public SourceIdentifier getIdentifier() {
227 return xmlSchemaSource.getIdentifier();
231 protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
232 return toStringHelper.add("xmlSchemaSource", xmlSchemaSource);