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.rfc7950.repo;
10 import static com.google.common.base.Preconditions.checkState;
11 import static java.util.Objects.requireNonNull;
12 import static org.opendaylight.yangtools.yang.parser.rfc7950.repo.StatementSourceReferenceHandler.extractRef;
14 import com.google.common.annotations.Beta;
15 import com.google.common.cache.CacheBuilder;
16 import com.google.common.cache.CacheLoader;
17 import com.google.common.cache.LoadingCache;
18 import javax.xml.transform.TransformerException;
19 import org.opendaylight.yangtools.concepts.AbstractSimpleIdentifiable;
20 import org.opendaylight.yangtools.yang.common.QName;
21 import org.opendaylight.yangtools.yang.common.XMLNamespace;
22 import org.opendaylight.yangtools.yang.common.YangVersion;
23 import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
24 import org.opendaylight.yangtools.yang.model.api.meta.StatementSourceReference;
25 import org.opendaylight.yangtools.yang.model.api.source.SourceIdentifier;
26 import org.opendaylight.yangtools.yang.model.spi.source.YinDomSource;
27 import org.opendaylight.yangtools.yang.model.spi.source.YinXmlSource;
28 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
29 import org.opendaylight.yangtools.yang.parser.spi.source.PrefixResolver;
30 import org.opendaylight.yangtools.yang.parser.spi.source.QNameToStatementDefinition;
31 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
32 import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
33 import org.opendaylight.yangtools.yang.parser.spi.source.StatementWriter;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36 import org.w3c.dom.Attr;
37 import org.w3c.dom.Element;
38 import org.w3c.dom.NamedNodeMap;
39 import org.w3c.dom.Node;
40 import org.w3c.dom.NodeList;
43 * A {@link StatementStreamSource} based on a {@link YinXmlSource}. Internal implementation works on top
44 * of {@link YinDomSource} and its DOM document.
46 * @author Robert Varga
49 public final class YinStatementStreamSource extends AbstractSimpleIdentifiable<SourceIdentifier>
50 implements StatementStreamSource {
51 private static final Logger LOG = LoggerFactory.getLogger(YinStatementStreamSource.class);
52 private static final LoadingCache<String, XMLNamespace> NS_CACHE = CacheBuilder.newBuilder().weakValues().build(
53 new CacheLoader<String, XMLNamespace>() {
55 public XMLNamespace load(final String key) {
56 return XMLNamespace.of(key).intern();
59 private final Node root;
61 private YinStatementStreamSource(final SourceIdentifier sourceId, final Node root) {
63 this.root = requireNonNull(root);
66 public static StatementStreamSource create(final YinXmlSource source) throws TransformerException {
67 return create(YinDomSource.transform(source));
70 public static StatementStreamSource create(final YinDomSource source) {
71 return new YinStatementStreamSource(source.sourceId(), source.getSource().getNode());
74 private static StatementDefinition getValidDefinition(final Node node, final StatementWriter writer,
75 final QNameToStatementDefinition stmtDef, final StatementSourceReference ref) {
76 final XMLNamespace uri = NS_CACHE.getUnchecked(node.getNamespaceURI());
77 final StatementDefinition def = stmtDef.getByNamespaceAndLocalName(uri, node.getLocalName());
80 SourceException.throwIf(writer.getPhase().equals(ModelProcessingPhase.FULL_DECLARATION), ref,
81 "%s is not a YIN statement or use of extension.", node.getLocalName());
86 private static boolean processAttribute(final int childId, final Attr attr, final StatementWriter writer,
87 final QNameToStatementDefinition stmtDef, final StatementSourceReference ref) {
88 final var optResumed = writer.resumeStatement(childId);
89 if (optResumed.isPresent()) {
90 final var resumed = optResumed.orElseThrow();
91 checkState(resumed.isFullyDefined(), "Statement %s is not fully defined", resumed);
95 final StatementDefinition def = getValidDefinition(attr, writer, stmtDef, ref);
100 final String value = attr.getValue();
101 writer.startStatement(childId, def.getStatementName(), value.isEmpty() ? null : value, ref);
102 writer.storeStatement(0, true);
103 writer.endStatement(ref);
107 private static String getArgValue(final Element element, final QName argName, final boolean yinElement) {
109 final NodeList children = element.getElementsByTagNameNS(argName.getNamespace().toString(),
110 argName.getLocalName());
111 if (children.getLength() == 0) {
114 return children.item(0).getTextContent();
117 final Attr attr = element.getAttributeNode(argName.getLocalName());
122 return attr.getValue();
125 private static boolean processElement(final int childId, final Element element, final StatementWriter writer,
126 final QNameToStatementDefinition stmtDef) {
128 final var optResumed = writer.resumeStatement(childId);
129 final StatementSourceReference ref;
131 final boolean allAttrs;
132 final boolean allElements;
133 if (optResumed.isPresent()) {
134 final var resumed = optResumed.orElseThrow();
135 if (resumed.isFullyDefined()) {
139 final StatementDefinition def = resumed.getDefinition();
140 ref = resumed.getSourceReference();
141 final var optArgDef = def.getArgumentDefinition();
142 if (optArgDef.isPresent()) {
143 final var argDef = optArgDef.orElseThrow();
144 argName = argDef.argumentName();
145 allAttrs = argDef.isYinElement();
146 allElements = !allAttrs;
153 ref = extractRef(element);
154 final StatementDefinition def = getValidDefinition(element, writer, stmtDef, ref);
156 LOG.debug("Skipping element {}", element);
160 final String argValue;
161 final var optArgDef = def.getArgumentDefinition();
162 if (optArgDef.isPresent()) {
163 final var argDef = optArgDef.orElseThrow();
164 argName = argDef.argumentName();
165 allAttrs = argDef.isYinElement();
166 allElements = !allAttrs;
168 argValue = getArgValue(element, argName, allAttrs);
169 SourceException.throwIfNull(argValue, ref, "Statement {} is missing mandatory argument %s",
170 def.getStatementName(), argName);
178 writer.startStatement(childId, def.getStatementName(), argValue, ref);
182 int childCounter = 0;
183 boolean fullyDefined = true;
185 // First process any statements defined as attributes. We need to skip argument, if present
186 final NamedNodeMap attributes = element.getAttributes();
187 if (attributes != null) {
188 for (int i = 0, len = attributes.getLength(); i < len; ++i) {
189 final Attr attr = (Attr) attributes.item(i);
190 if ((allAttrs || !isArgument(argName, attr))
191 && !processAttribute(childCounter++, attr, writer, stmtDef, ref)) {
192 fullyDefined = false;
197 // Now process child elements, if present
198 final NodeList children = element.getChildNodes();
199 for (int i = 0, len = children.getLength(); i < len; ++i) {
200 final Node child = children.item(i);
201 if (child.getNodeType() == Node.ELEMENT_NODE && (allElements || !isArgument(argName, child))
202 && !processElement(childCounter++, (Element) child, writer, stmtDef)) {
203 fullyDefined = false;
207 writer.storeStatement(childCounter, fullyDefined);
208 writer.endStatement(ref);
212 private static boolean isArgument(final QName argName, final Node node) {
213 return argName != null && argName.getLocalName().equals(node.getLocalName()) && node.getPrefix() == null;
216 private void walkTree(final StatementWriter writer, final QNameToStatementDefinition stmtDef) {
217 final NodeList children = root.getChildNodes();
219 int childCounter = 0;
220 for (int i = 0, len = children.getLength(); i < len; ++i) {
221 final Node child = children.item(i);
222 if (child.getNodeType() == Node.ELEMENT_NODE) {
223 processElement(childCounter++, (Element) child, writer, stmtDef);
229 public void writePreLinkage(final StatementWriter writer, final QNameToStatementDefinition stmtDef) {
230 walkTree(writer, stmtDef);
234 public void writeLinkage(final StatementWriter writer, final QNameToStatementDefinition stmtDef,
235 final PrefixResolver preLinkagePrefixes, final YangVersion yangVersion) {
236 walkTree(writer, stmtDef);
240 public void writeLinkageAndStatementDefinitions(final StatementWriter writer,
241 final QNameToStatementDefinition stmtDef, final PrefixResolver prefixes, final YangVersion yangVersion) {
242 walkTree(writer, stmtDef);
246 public void writeFull(final StatementWriter writer, final QNameToStatementDefinition stmtDef,
247 final PrefixResolver prefixes, final YangVersion yangVersion) {
248 walkTree(writer, stmtDef);