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.parser.rfc6020.repo;
10 import static org.opendaylight.yangtools.yang.parser.rfc6020.repo.StatementSourceReferenceHandler.extractRef;
12 import com.google.common.annotations.Beta;
13 import com.google.common.base.Preconditions;
14 import com.google.common.cache.CacheBuilder;
15 import com.google.common.cache.CacheLoader;
16 import com.google.common.cache.LoadingCache;
18 import java.net.URISyntaxException;
19 import javax.xml.transform.TransformerException;
20 import org.opendaylight.yangtools.concepts.Identifiable;
21 import org.opendaylight.yangtools.yang.common.QName;
22 import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
23 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
24 import org.opendaylight.yangtools.yang.model.repo.api.YinDomSchemaSource;
25 import org.opendaylight.yangtools.yang.model.repo.api.YinXmlSchemaSource;
26 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
27 import org.opendaylight.yangtools.yang.parser.spi.source.PrefixToModule;
28 import org.opendaylight.yangtools.yang.parser.spi.source.QNameToStatementDefinition;
29 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
30 import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
31 import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
32 import org.opendaylight.yangtools.yang.parser.spi.source.StatementWriter;
33 import org.slf4j.Logger;
34 import org.slf4j.LoggerFactory;
35 import org.w3c.dom.Attr;
36 import org.w3c.dom.Element;
37 import org.w3c.dom.NamedNodeMap;
38 import org.w3c.dom.Node;
39 import org.w3c.dom.NodeList;
42 * A {@link StatementStreamSource} based on a {@link YinXmlSchemaSource}. Internal implementation works on top
43 * of {@link YinDomSchemaSource} and its DOM document.
45 * @author Robert Varga
48 public final class YinStatementStreamSource implements Identifiable<SourceIdentifier>, StatementStreamSource {
49 private static final Logger LOG = LoggerFactory.getLogger(YinStatementStreamSource.class);
50 private static final LoadingCache<String, URI> URI_CACHE = CacheBuilder.newBuilder().weakValues().build(
51 new CacheLoader<String, URI>() {
53 public URI load(final String key) throws URISyntaxException {
57 private final SourceIdentifier identifier;
58 private final Node root;
60 private YinStatementStreamSource(final SourceIdentifier identifier, final Node root) {
61 this.identifier = Preconditions.checkNotNull(identifier);
62 this.root = Preconditions.checkNotNull(root);
65 public static StatementStreamSource create(final YinXmlSchemaSource source) throws TransformerException {
66 return create(YinDomSchemaSource.transform(source));
69 public static StatementStreamSource create(final YinDomSchemaSource source) {
70 return new YinStatementStreamSource(source.getIdentifier(), source.getSource().getNode());
74 public SourceIdentifier getIdentifier() {
78 private static StatementDefinition getValidDefinition(final Node node, final StatementWriter writer,
79 final QNameToStatementDefinition stmtDef, final StatementSourceReference ref) {
80 final URI uri = URI_CACHE.getUnchecked(node.getNamespaceURI());
81 final StatementDefinition def = stmtDef.getByNamespaceAndLocalName(uri, node.getLocalName());
84 SourceException.throwIf(writer.getPhase().equals(ModelProcessingPhase.FULL_DECLARATION), ref,
85 "%s is not a YIN statement or use of extension.", node.getLocalName());
90 private static void processAttribute(final Attr attr, final StatementWriter writer,
91 final QNameToStatementDefinition stmtDef, final StatementSourceReference ref) {
92 final StatementDefinition def = getValidDefinition(attr, writer, stmtDef, ref);
97 final String value = attr.getValue();
98 writer.startStatement(def.getStatementName(), value.isEmpty() ? null : value, ref);
99 writer.endStatement(ref);
102 private static String getArgValue(final Element element, final QName argName, final boolean yinElement) {
104 final NodeList children = element.getElementsByTagNameNS(argName.getNamespace().toString(),
105 argName.getLocalName());
106 if (children.getLength() == 0) {
109 return children.item(0).getTextContent();
112 final Attr attr = element.getAttributeNode(argName.getLocalName());
117 return attr.getValue();
120 private static void processElement(final Element element, final StatementWriter writer,
121 final QNameToStatementDefinition stmtDef) {
122 final StatementSourceReference ref = extractRef(element);
123 final StatementDefinition def = getValidDefinition(element, writer, stmtDef, ref);
125 LOG.debug("Skipping element {}", element);
129 final QName argName = def.getArgumentName();
130 final String argValue;
131 final boolean allAttrs;
132 final boolean allElements;
133 if (argName != null) {
134 allAttrs = def.isArgumentYinElement();
135 allElements = !allAttrs;
137 argValue = getArgValue(element, argName, allAttrs);
138 SourceException.throwIfNull(argValue, ref, "Statement {} is missing mandatory argument %s",
139 def.getStatementName(), argName);
146 writer.startStatement(def.getStatementName(), argValue, ref);
148 // First process any statements defined as attributes. We need to skip argument, if present
149 final NamedNodeMap attributes = element.getAttributes();
150 if (attributes != null) {
151 for (int i = 0, len = attributes.getLength(); i < len; ++i) {
152 final Attr attr = (Attr) attributes.item(i);
153 if (allAttrs || !isArgument(argName, attr)) {
154 processAttribute(attr, writer, stmtDef, ref);
159 // Now process child elements, if present
160 final NodeList children = element.getChildNodes();
161 for (int i = 0, len = children.getLength(); i < len; ++i) {
162 final Node child = children.item(i);
163 if (child.getNodeType() == Node.ELEMENT_NODE) {
164 if (allElements || !isArgument(argName, child)) {
165 processElement((Element) child, writer, stmtDef);
170 writer.endStatement(ref);
173 private static boolean isArgument(final QName argName, final Node node) {
174 return argName != null && argName.getLocalName().equals(node.getLocalName()) && node.getPrefix() == null;
177 private void walkTree(final StatementWriter writer, final QNameToStatementDefinition stmtDef) {
178 final NodeList children = root.getChildNodes();
179 for (int i = 0, len = children.getLength(); i < len; ++i) {
180 final Node child = children.item(i);
181 if (child.getNodeType() == Node.ELEMENT_NODE) {
182 processElement((Element) child, writer, stmtDef);
188 public void writePreLinkage(final StatementWriter writer, final QNameToStatementDefinition stmtDef) {
189 walkTree(writer, stmtDef);
193 public void writeLinkage(final StatementWriter writer, final QNameToStatementDefinition stmtDef,
194 final PrefixToModule preLinkagePrefixes) {
195 walkTree(writer, stmtDef);
199 public void writeLinkageAndStatementDefinitions(final StatementWriter writer,
200 final QNameToStatementDefinition stmtDef, final PrefixToModule prefixes) {
201 walkTree(writer, stmtDef);
205 public void writeFull(final StatementWriter writer, final QNameToStatementDefinition stmtDef,
206 final PrefixToModule prefixes) {
207 walkTree(writer, stmtDef);