Refactor AbstractIdentifiable
[yangtools.git] / yang / yang-parser-rfc7950 / src / main / java / org / opendaylight / yangtools / yang / parser / rfc7950 / repo / YinStatementStreamSource.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.yangtools.yang.parser.rfc7950.repo;
9
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;
13
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 java.net.URI;
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;
45
46 /**
47  * A {@link StatementStreamSource} based on a {@link YinXmlSchemaSource}. Internal implementation works on top
48  * of {@link YinDomSchemaSource} and its DOM document.
49  *
50  * @author Robert Varga
51  */
52 @Beta
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>() {
58             @Override
59             public URI load(final String key) throws URISyntaxException {
60                 return new URI(key);
61             }
62         });
63     private final Node root;
64
65     private YinStatementStreamSource(final SourceIdentifier identifier, final Node root) {
66         super(identifier);
67         this.root = requireNonNull(root);
68     }
69
70     public static StatementStreamSource create(final YinXmlSchemaSource source) throws TransformerException {
71         return create(YinDomSchemaSource.transform(source));
72     }
73
74     public static StatementStreamSource create(final YinDomSchemaSource source) {
75         return new YinStatementStreamSource(source.getIdentifier(), source.getSource().getNode());
76     }
77
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());
82
83         if (def == null) {
84             SourceException.throwIf(writer.getPhase().equals(ModelProcessingPhase.FULL_DECLARATION), ref,
85                 "%s is not a YIN statement or use of extension.", node.getLocalName());
86         }
87         return def;
88     }
89
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);
96             return true;
97         }
98
99         final StatementDefinition def = getValidDefinition(attr, writer, stmtDef, ref);
100         if (def == null) {
101             return false;
102         }
103
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);
108         return true;
109     }
110
111     private static String getArgValue(final Element element, final QName argName, final boolean yinElement) {
112         if (yinElement) {
113             final NodeList children = element.getElementsByTagNameNS(argName.getNamespace().toString(),
114                 argName.getLocalName());
115             if (children.getLength() == 0) {
116                 return null;
117             }
118             return children.item(0).getTextContent();
119         }
120
121         final Attr attr = element.getAttributeNode(argName.getLocalName());
122         if (attr == null) {
123             return null;
124         }
125
126         return attr.getValue();
127     }
128
129     private static boolean processElement(final int childId, final Element element, final StatementWriter writer,
130             final QNameToStatementDefinition stmtDef) {
131
132         final Optional<? extends ResumedStatement> optResumed = writer.resumeStatement(childId);
133         final StatementSourceReference ref;
134         final QName argName;
135         final boolean allAttrs;
136         final boolean allElements;
137         if (optResumed.isPresent()) {
138             final ResumedStatement resumed = optResumed.get();
139             if (resumed.isFullyDefined()) {
140                 return true;
141             }
142
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;
151             } else {
152                 argName = null;
153                 allAttrs = false;
154                 allElements = false;
155             }
156         } else {
157             ref = extractRef(element);
158             final StatementDefinition def = getValidDefinition(element, writer, stmtDef, ref);
159             if (def == null) {
160                 LOG.debug("Skipping element {}", element);
161                 return false;
162             }
163
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;
171
172                 argValue = getArgValue(element, argName, allAttrs);
173                 SourceException.throwIfNull(argValue, ref, "Statement {} is missing mandatory argument %s",
174                     def.getStatementName(), argName);
175             } else {
176                 argName = null;
177                 argValue = null;
178                 allAttrs = false;
179                 allElements = false;
180             }
181
182             writer.startStatement(childId, def.getStatementName(), argValue, ref);
183         }
184
185         // Child counter
186         int childCounter = 0;
187         boolean fullyDefined = true;
188
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;
197                 }
198             }
199         }
200
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;
208             }
209         }
210
211         writer.storeStatement(childCounter, fullyDefined);
212         writer.endStatement(ref);
213         return fullyDefined;
214     }
215
216     private static boolean isArgument(final QName argName, final Node node) {
217         return argName != null && argName.getLocalName().equals(node.getLocalName()) && node.getPrefix() == null;
218     }
219
220     private void walkTree(final StatementWriter writer, final QNameToStatementDefinition stmtDef) {
221         final NodeList children = root.getChildNodes();
222
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);
228             }
229         }
230     }
231
232     @Override
233     public void writePreLinkage(final StatementWriter writer, final QNameToStatementDefinition stmtDef) {
234         walkTree(writer, stmtDef);
235     }
236
237     @Override
238     public void writeLinkage(final StatementWriter writer, final QNameToStatementDefinition stmtDef,
239             final PrefixToModule preLinkagePrefixes, final YangVersion yangVersion) {
240         walkTree(writer, stmtDef);
241     }
242
243     @Override
244     public void writeLinkageAndStatementDefinitions(final StatementWriter writer,
245             final QNameToStatementDefinition stmtDef, final PrefixToModule prefixes,  final YangVersion yangVersion) {
246         walkTree(writer, stmtDef);
247     }
248
249     @Override
250     public void writeFull(final StatementWriter writer, final QNameToStatementDefinition stmtDef,
251             final PrefixToModule prefixes, final YangVersion yangVersion) {
252         walkTree(writer, stmtDef);
253     }
254 }