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;
19 import java.net.URISyntaxException;
20 import java.util.Optional;
21 import javax.xml.transform.TransformerException;
22 import org.opendaylight.yangtools.concepts.AbstractSimpleIdentifiable;
23 import org.opendaylight.yangtools.yang.common.QName;
24 import org.opendaylight.yangtools.yang.common.YangVersion;
25 import org.opendaylight.yangtools.yang.model.api.meta.ArgumentDefinition;
26 import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
27 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
28 import org.opendaylight.yangtools.yang.model.repo.api.YinDomSchemaSource;
29 import org.opendaylight.yangtools.yang.model.repo.api.YinXmlSchemaSource;
30 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
31 import org.opendaylight.yangtools.yang.parser.spi.source.PrefixToModule;
32 import org.opendaylight.yangtools.yang.parser.spi.source.QNameToStatementDefinition;
33 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
34 import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
35 import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
36 import org.opendaylight.yangtools.yang.parser.spi.source.StatementWriter;
37 import org.opendaylight.yangtools.yang.parser.spi.source.StatementWriter.ResumedStatement;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
40 import org.w3c.dom.Attr;
41 import org.w3c.dom.Element;
42 import org.w3c.dom.NamedNodeMap;
43 import org.w3c.dom.Node;
44 import org.w3c.dom.NodeList;
47 * A {@link StatementStreamSource} based on a {@link YinXmlSchemaSource}. Internal implementation works on top
48 * of {@link YinDomSchemaSource} and its DOM document.
50 * @author Robert Varga
53 public final class YinStatementStreamSource extends AbstractSimpleIdentifiable<SourceIdentifier>
54 implements StatementStreamSource {
55 private static final Logger LOG = LoggerFactory.getLogger(YinStatementStreamSource.class);
56 private static final LoadingCache<String, URI> URI_CACHE = CacheBuilder.newBuilder().weakValues().build(
57 new CacheLoader<String, URI>() {
59 public URI load(final String key) throws URISyntaxException {
63 private final Node root;
65 private YinStatementStreamSource(final SourceIdentifier identifier, final Node root) {
67 this.root = requireNonNull(root);
70 public static StatementStreamSource create(final YinXmlSchemaSource source) throws TransformerException {
71 return create(YinDomSchemaSource.transform(source));
74 public static StatementStreamSource create(final YinDomSchemaSource source) {
75 return new YinStatementStreamSource(source.getIdentifier(), source.getSource().getNode());
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 boolean processAttribute(final int childId, final Attr attr, final StatementWriter writer,
91 final QNameToStatementDefinition stmtDef, final StatementSourceReference ref) {
92 final Optional<? extends ResumedStatement> optResumed = writer.resumeStatement(childId);
93 if (optResumed.isPresent()) {
94 final ResumedStatement resumed = optResumed.get();
95 checkState(resumed.isFullyDefined(), "Statement %s is not fully defined", resumed);
99 final StatementDefinition def = getValidDefinition(attr, writer, stmtDef, ref);
104 final String value = attr.getValue();
105 writer.startStatement(childId, def.getStatementName(), value.isEmpty() ? null : value, ref);
106 writer.storeStatement(0, true);
107 writer.endStatement(ref);
111 private static String getArgValue(final Element element, final QName argName, final boolean yinElement) {
113 final NodeList children = element.getElementsByTagNameNS(argName.getNamespace().toString(),
114 argName.getLocalName());
115 if (children.getLength() == 0) {
118 return children.item(0).getTextContent();
121 final Attr attr = element.getAttributeNode(argName.getLocalName());
126 return attr.getValue();
129 private static boolean processElement(final int childId, final Element element, final StatementWriter writer,
130 final QNameToStatementDefinition stmtDef) {
132 final Optional<? extends ResumedStatement> optResumed = writer.resumeStatement(childId);
133 final StatementSourceReference ref;
135 final boolean allAttrs;
136 final boolean allElements;
137 if (optResumed.isPresent()) {
138 final ResumedStatement resumed = optResumed.get();
139 if (resumed.isFullyDefined()) {
143 final StatementDefinition def = resumed.getDefinition();
144 ref = resumed.getSourceReference();
145 final Optional<ArgumentDefinition> optArgDef = def.getArgumentDefinition();
146 if (optArgDef.isPresent()) {
147 final ArgumentDefinition argDef = optArgDef.get();
148 argName = argDef.getArgumentName();
149 allAttrs = argDef.isYinElement();
150 allElements = !allAttrs;
157 ref = extractRef(element);
158 final StatementDefinition def = getValidDefinition(element, writer, stmtDef, ref);
160 LOG.debug("Skipping element {}", element);
164 final String argValue;
165 final Optional<ArgumentDefinition> optArgDef = def.getArgumentDefinition();
166 if (optArgDef.isPresent()) {
167 final ArgumentDefinition argDef = optArgDef.get();
168 argName = argDef.getArgumentName();
169 allAttrs = argDef.isYinElement();
170 allElements = !allAttrs;
172 argValue = getArgValue(element, argName, allAttrs);
173 SourceException.throwIfNull(argValue, ref, "Statement {} is missing mandatory argument %s",
174 def.getStatementName(), argName);
182 writer.startStatement(childId, def.getStatementName(), argValue, ref);
186 int childCounter = 0;
187 boolean fullyDefined = true;
189 // First process any statements defined as attributes. We need to skip argument, if present
190 final NamedNodeMap attributes = element.getAttributes();
191 if (attributes != null) {
192 for (int i = 0, len = attributes.getLength(); i < len; ++i) {
193 final Attr attr = (Attr) attributes.item(i);
194 if ((allAttrs || !isArgument(argName, attr))
195 && !processAttribute(childCounter++, attr, writer, stmtDef, ref)) {
196 fullyDefined = false;
201 // Now process child elements, if present
202 final NodeList children = element.getChildNodes();
203 for (int i = 0, len = children.getLength(); i < len; ++i) {
204 final Node child = children.item(i);
205 if (child.getNodeType() == Node.ELEMENT_NODE && (allElements || !isArgument(argName, child))
206 && !processElement(childCounter++, (Element) child, writer, stmtDef)) {
207 fullyDefined = false;
211 writer.storeStatement(childCounter, fullyDefined);
212 writer.endStatement(ref);
216 private static boolean isArgument(final QName argName, final Node node) {
217 return argName != null && argName.getLocalName().equals(node.getLocalName()) && node.getPrefix() == null;
220 private void walkTree(final StatementWriter writer, final QNameToStatementDefinition stmtDef) {
221 final NodeList children = root.getChildNodes();
223 int childCounter = 0;
224 for (int i = 0, len = children.getLength(); i < len; ++i) {
225 final Node child = children.item(i);
226 if (child.getNodeType() == Node.ELEMENT_NODE) {
227 processElement(childCounter++, (Element) child, writer, stmtDef);
233 public void writePreLinkage(final StatementWriter writer, final QNameToStatementDefinition stmtDef) {
234 walkTree(writer, stmtDef);
238 public void writeLinkage(final StatementWriter writer, final QNameToStatementDefinition stmtDef,
239 final PrefixToModule preLinkagePrefixes, final YangVersion yangVersion) {
240 walkTree(writer, stmtDef);
244 public void writeLinkageAndStatementDefinitions(final StatementWriter writer,
245 final QNameToStatementDefinition stmtDef, final PrefixToModule prefixes, final YangVersion yangVersion) {
246 walkTree(writer, stmtDef);
250 public void writeFull(final StatementWriter writer, final QNameToStatementDefinition stmtDef,
251 final PrefixToModule prefixes, final YangVersion yangVersion) {
252 walkTree(writer, stmtDef);