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 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;
17 import com.google.common.base.MoreObjects;
18 import com.google.common.base.MoreObjects.ToStringHelper;
19 import java.util.Optional;
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.eclipse.jdt.annotation.NonNull;
26 import org.eclipse.jdt.annotation.Nullable;
27 import org.opendaylight.yangtools.yang.common.QName;
28 import org.opendaylight.yangtools.yang.common.Revision;
29 import org.opendaylight.yangtools.yang.common.YangConstants;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
32 import org.w3c.dom.Attr;
33 import org.w3c.dom.Element;
34 import org.w3c.dom.Node;
35 import org.w3c.dom.NodeList;
38 * Utility {@link YinXmlSchemaSource} exposing a W3C {@link DOMSource} representation of YIN model.
40 public abstract class YinDomSchemaSource implements YinXmlSchemaSource {
41 private static final Logger LOG = LoggerFactory.getLogger(YinDomSchemaSource.class);
42 private static final TransformerFactory TRANSFORMER_FACTORY = TransformerFactory.newInstance();
43 private static final QName REVISION_STMT = REVISION.getStatementName();
44 private static final String MODULE_ARG = MODULE.getArgumentDefinition().get().getArgumentName().getLocalName();
45 private static final String REVISION_ARG = REVISION.getArgumentDefinition().get().getArgumentName().getLocalName();
47 YinDomSchemaSource() {
48 // Prevent outside instantiation
52 * Create a new {@link YinDomSchemaSource} using an identifier and a source.
54 * @param identifier Schema source identifier
55 * @param source W3C DOM source
56 * @return A new {@link YinDomSchemaSource} instance.
57 * @deprecated Use {@link #create(SourceIdentifier, DOMSource, String)} instead.
59 @Deprecated(forRemoval = true)
60 public static @NonNull YinDomSchemaSource create(final @NonNull SourceIdentifier identifier,
61 final @NonNull DOMSource source) {
62 return create(identifier, source, null);
66 * Create a new {@link YinDomSchemaSource} using an identifier and a source.
68 * @param identifier Schema source identifier
69 * @param source W3C DOM source
70 * @param symbolicName Source symbolic name
71 * @return A new {@link YinDomSchemaSource} instance.
73 public static @NonNull YinDomSchemaSource create(final @NonNull SourceIdentifier identifier,
74 final @NonNull DOMSource source, final @Nullable String symbolicName) {
76 final Node root = source.getNode().getFirstChild();
77 final String rootNs = root.getNamespaceURI();
79 // Let whoever is using this deal with this
80 return new Simple(identifier, source, symbolicName);
83 final QName qname = QName.create(rootNs, root.getLocalName());
84 checkArgument(RFC6020_YIN_MODULE.equals(qname.getModule()),
85 "Root node namepsace %s does not match %s", rootNs, YangConstants.RFC6020_YIN_NAMESPACE);
86 checkArgument(MODULE.getStatementName().equals(qname)
87 || SUBMODULE.getStatementName().equals(qname), "Root element %s is not a module nor a submodule", qname);
89 checkArgument(root instanceof Element, "Root node %s is not an element", root);
90 final Element element = (Element)root;
92 final Attr nameAttr = element.getAttributeNode(MODULE_ARG);
93 checkArgument(nameAttr != null, "No %s name argument found in %s", element.getLocalName());
95 final NodeList revisions = element.getElementsByTagNameNS(REVISION_STMT.getNamespace().toString(),
96 REVISION_STMT.getLocalName());
97 if (revisions.getLength() == 0) {
98 // FIXME: is module name important (as that may have changed)
99 return new Simple(identifier, source, symbolicName);
102 final Element revisionStmt = (Element) revisions.item(0);
103 final Attr dateAttr = revisionStmt.getAttributeNode(REVISION_ARG);
104 checkArgument(dateAttr != null, "No revision statement argument found in %s", revisionStmt);
106 final SourceIdentifier parsedId = RevisionSourceIdentifier.create(nameAttr.getValue(),
107 Revision.of(dateAttr.getValue()));
108 final SourceIdentifier id;
109 if (!parsedId.equals(identifier)) {
110 LOG.debug("Changed identifier from {} to {}", identifier, parsedId);
116 return new Simple(id, source, symbolicName);
120 * Create a {@link YinDomSchemaSource} from a {@link YinXmlSchemaSource}. If the argument is already a
121 * YinDomSchemaSource, this method returns the same instance. The source will be translated on first access,
122 * at which point an {@link IllegalStateException} may be raised.
124 * @param xmlSchemaSource Backing schema source
125 * @return A {@link YinDomSchemaSource} instance
127 public static @NonNull YinDomSchemaSource lazyTransform(final YinXmlSchemaSource xmlSchemaSource) {
128 final YinDomSchemaSource cast = castSchemaSource(xmlSchemaSource);
129 return cast != null ? cast : new Transforming(xmlSchemaSource);
133 * Create a {@link YinDomSchemaSource} from a {@link YinXmlSchemaSource}. If the argument is already a
134 * YinDomSchemaSource, this method returns the same instance. The source will be translated immediately.
136 * @param xmlSchemaSource Backing schema source
137 * @return A {@link YinDomSchemaSource} instance
138 * @throws TransformerException when the provided source fails to transform
140 public static @NonNull YinDomSchemaSource transform(final YinXmlSchemaSource xmlSchemaSource)
141 throws TransformerException {
142 final YinDomSchemaSource cast = castSchemaSource(xmlSchemaSource);
143 return cast != null ? cast :
144 create(xmlSchemaSource.getIdentifier(), transformSource(xmlSchemaSource.getSource()),
145 xmlSchemaSource.getSymbolicName().orElse(null));
149 public abstract DOMSource getSource();
152 public final Class<? extends YinXmlSchemaSource> getType() {
153 return YinDomSchemaSource.class;
157 public final String toString() {
158 return addToStringAttributes(MoreObjects.toStringHelper(this).add("identifier", getIdentifier())).toString();
162 * Add subclass-specific attributes to the output {@link #toString()} output. Since
163 * subclasses are prevented from overriding {@link #toString()} for consistency
164 * reasons, they can add their specific attributes to the resulting string by attaching
165 * attributes to the supplied {@link ToStringHelper}.
167 * @param toStringHelper ToStringHelper onto the attributes can be added
168 * @return ToStringHelper supplied as input argument.
170 protected abstract ToStringHelper addToStringAttributes(ToStringHelper toStringHelper);
172 static @NonNull DOMSource transformSource(final Source source) throws TransformerException {
173 final DOMResult result = new DOMResult();
174 TRANSFORMER_FACTORY.newTransformer().transform(source, result);
176 return new DOMSource(result.getNode(), result.getSystemId());
179 private static @Nullable YinDomSchemaSource castSchemaSource(final YinXmlSchemaSource xmlSchemaSource) {
180 if (xmlSchemaSource instanceof YinDomSchemaSource) {
181 return (YinDomSchemaSource) xmlSchemaSource;
184 final Source source = xmlSchemaSource.getSource();
185 if (source instanceof DOMSource) {
186 return create(xmlSchemaSource.getIdentifier(), (DOMSource) source,
187 xmlSchemaSource.getSymbolicName().orElse(null));
193 private static final class Simple extends YinDomSchemaSource {
194 private final @NonNull SourceIdentifier identifier;
195 private final @NonNull DOMSource source;
196 private final String symbolicName;
198 Simple(final @NonNull SourceIdentifier identifier, final @NonNull DOMSource source,
199 final @Nullable String symbolicName) {
200 this.identifier = requireNonNull(identifier);
201 this.source = requireNonNull(source);
202 this.symbolicName = symbolicName;
206 public DOMSource getSource() {
211 public SourceIdentifier getIdentifier() {
216 public Optional<String> getSymbolicName() {
217 return Optional.ofNullable(symbolicName);
221 protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
222 return toStringHelper.add("source", source);
226 private static final class Transforming extends YinDomSchemaSource {
227 private final YinXmlSchemaSource xmlSchemaSource;
228 private volatile DOMSource source;
230 Transforming(final YinXmlSchemaSource xmlSchemaSource) {
231 this.xmlSchemaSource = requireNonNull(xmlSchemaSource);
235 public DOMSource getSource() {
236 DOMSource ret = source;
238 synchronized (this) {
242 ret = transformSource(xmlSchemaSource.getSource());
243 } catch (TransformerException e) {
244 throw new IllegalStateException("Failed to transform schema source " + xmlSchemaSource, e);
255 public SourceIdentifier getIdentifier() {
256 return xmlSchemaSource.getIdentifier();
260 public Optional<String> getSymbolicName() {
261 return xmlSchemaSource.getSymbolicName();
265 protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
266 return toStringHelper.add("xmlSchemaSource", xmlSchemaSource);