Further yang-parser-impl checkstyle fixes
[yangtools.git] / yang / yang-parser-impl / src / main / java / org / opendaylight / yangtools / yang / parser / rfc6020 / 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.rfc6020.repo;
9
10 import static org.opendaylight.yangtools.yang.parser.rfc6020.repo.StatementSourceReferenceHandler.extractRef;
11
12 import com.google.common.annotations.Beta;
13 import com.google.common.base.MoreObjects;
14 import com.google.common.base.Preconditions;
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 javax.annotation.Nonnull;
21 import javax.xml.transform.TransformerException;
22 import org.opendaylight.yangtools.concepts.Identifiable;
23 import org.opendaylight.yangtools.yang.common.QName;
24 import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
25 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
26 import org.opendaylight.yangtools.yang.model.repo.api.YinDomSchemaSource;
27 import org.opendaylight.yangtools.yang.model.repo.api.YinXmlSchemaSource;
28 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
29 import org.opendaylight.yangtools.yang.parser.spi.source.PrefixToModule;
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.StatementSourceReference;
33 import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
34 import org.opendaylight.yangtools.yang.parser.spi.source.StatementWriter;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37 import org.w3c.dom.Attr;
38 import org.w3c.dom.Element;
39 import org.w3c.dom.NamedNodeMap;
40 import org.w3c.dom.Node;
41 import org.w3c.dom.NodeList;
42
43 /**
44  * A {@link StatementStreamSource} based on a {@link YinXmlSchemaSource}. Internal implementation works on top
45  * of {@link YinDomSchemaSource} and its DOM document.
46  *
47  * @author Robert Varga
48  */
49 @Beta
50 public final class YinStatementStreamSource implements Identifiable<SourceIdentifier>, StatementStreamSource {
51     private static final Logger LOG = LoggerFactory.getLogger(YinStatementStreamSource.class);
52     private static final LoadingCache<String, URI> URI_CACHE = CacheBuilder.newBuilder().weakValues().build(
53         new CacheLoader<String, URI>() {
54             @Override
55             public URI load(@Nonnull final String key) throws URISyntaxException {
56                 return new URI(key);
57             }
58         });
59     private final SourceIdentifier identifier;
60     private final Node root;
61
62     private YinStatementStreamSource(final SourceIdentifier identifier, final Node root) {
63         this.identifier = Preconditions.checkNotNull(identifier);
64         this.root = Preconditions.checkNotNull(root);
65     }
66
67     public static StatementStreamSource create(final YinXmlSchemaSource source) throws TransformerException {
68         return create(YinDomSchemaSource.transform(source));
69     }
70
71     public static StatementStreamSource create(final YinDomSchemaSource source) {
72         return new YinStatementStreamSource(source.getIdentifier(), source.getSource().getNode());
73     }
74
75     @Override
76     public SourceIdentifier getIdentifier() {
77         return identifier;
78     }
79
80     private static StatementDefinition getValidDefinition(final Node node, final StatementWriter writer,
81             final QNameToStatementDefinition stmtDef, final StatementSourceReference ref) {
82         final URI uri = URI_CACHE.getUnchecked(node.getNamespaceURI());
83         final StatementDefinition def = stmtDef.getByNamespaceAndLocalName(uri, node.getLocalName());
84
85         if (def == null) {
86             SourceException.throwIf(writer.getPhase().equals(ModelProcessingPhase.FULL_DECLARATION), ref,
87                 "%s is not a YIN statement or use of extension.", node.getLocalName());
88         }
89         return def;
90     }
91
92     private static void processAttribute(final int childId, final Attr attr, final StatementWriter writer,
93             final QNameToStatementDefinition stmtDef, final StatementSourceReference ref) {
94         final StatementDefinition def = getValidDefinition(attr, writer, stmtDef, ref);
95         if (def == null) {
96             return;
97         }
98
99         final String value = attr.getValue();
100         writer.startStatement(childId, def.getStatementName(), value.isEmpty() ? null : value, ref);
101         writer.endStatement(ref);
102     }
103
104     private static String getArgValue(final Element element, final QName argName, final boolean yinElement) {
105         if (yinElement) {
106             final NodeList children = element.getElementsByTagNameNS(argName.getNamespace().toString(),
107                 argName.getLocalName());
108             if (children.getLength() == 0) {
109                 return null;
110             }
111             return children.item(0).getTextContent();
112         }
113
114         final Attr attr = element.getAttributeNode(argName.getLocalName());
115         if (attr == null) {
116             return null;
117         }
118
119         return attr.getValue();
120     }
121
122     private static void processElement(final int childId, final Element element, final StatementWriter writer,
123             final QNameToStatementDefinition stmtDef) {
124         final StatementSourceReference ref = extractRef(element);
125         final StatementDefinition def = getValidDefinition(element, writer, stmtDef, ref);
126         if (def == null) {
127             LOG.debug("Skipping element {}", element);
128             return;
129         }
130
131         final QName argName = def.getArgumentName();
132         final String argValue;
133         final boolean allAttrs;
134         final boolean allElements;
135         if (argName != null) {
136             allAttrs = def.isArgumentYinElement();
137             allElements = !allAttrs;
138
139             argValue = getArgValue(element, argName, allAttrs);
140             SourceException.throwIfNull(argValue, ref, "Statement {} is missing mandatory argument %s",
141                 def.getStatementName(), argName);
142         } else {
143             argValue = null;
144             allAttrs = false;
145             allElements = false;
146         }
147
148         writer.startStatement(childId, def.getStatementName(), argValue, ref);
149
150         // Child counter
151         int childCounter = 0;
152
153         // First process any statements defined as attributes. We need to skip argument, if present
154         final NamedNodeMap attributes = element.getAttributes();
155         if (attributes != null) {
156             for (int i = 0, len = attributes.getLength(); i < len; ++i) {
157                 final Attr attr = (Attr) attributes.item(i);
158                 if (allAttrs || !isArgument(argName, attr)) {
159                     processAttribute(childCounter++, attr, writer, stmtDef, ref);
160                 }
161             }
162         }
163
164         // Now process child elements, if present
165         final NodeList children = element.getChildNodes();
166         for (int i = 0, len = children.getLength(); i < len; ++i) {
167             final Node child = children.item(i);
168             if (child.getNodeType() == Node.ELEMENT_NODE) {
169                 if (allElements || !isArgument(argName, child)) {
170                     processElement(childCounter++, (Element) child, writer, stmtDef);
171                 }
172             }
173         }
174
175         writer.endStatement(ref);
176     }
177
178     private static boolean isArgument(final QName argName, final Node node) {
179         return argName != null && argName.getLocalName().equals(node.getLocalName()) && node.getPrefix() == null;
180     }
181
182     private void walkTree(final StatementWriter writer, final QNameToStatementDefinition stmtDef) {
183         final NodeList children = root.getChildNodes();
184
185         int childCounter = 0;
186         for (int i = 0, len = children.getLength(); i < len; ++i) {
187             final Node child = children.item(i);
188             if (child.getNodeType() == Node.ELEMENT_NODE) {
189                 processElement(childCounter++, (Element) child, writer, stmtDef);
190             }
191         }
192     }
193
194     @Override
195     public void writePreLinkage(final StatementWriter writer, final QNameToStatementDefinition stmtDef) {
196         walkTree(writer, stmtDef);
197     }
198
199     @Override
200     public void writeLinkage(final StatementWriter writer, final QNameToStatementDefinition stmtDef,
201             final PrefixToModule preLinkagePrefixes) {
202         walkTree(writer, stmtDef);
203     }
204
205     @Override
206     public void writeLinkageAndStatementDefinitions(final StatementWriter writer,
207             final QNameToStatementDefinition stmtDef, final PrefixToModule prefixes) {
208         walkTree(writer, stmtDef);
209     }
210
211     @Override
212     public void writeFull(final StatementWriter writer, final QNameToStatementDefinition stmtDef,
213             final PrefixToModule prefixes) {
214         walkTree(writer, stmtDef);
215     }
216
217     @Override
218     public String toString() {
219         return MoreObjects.toStringHelper(this).add("identifier", getIdentifier()).toString();
220     }
221 }