Bug 3874: Support of yang modeled AnyXML - XML deserialization
[yangtools.git] / yang / yang-parser-impl / src / main / java / org / opendaylight / yangtools / yang / parser / impl / YinStatementParserImpl.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
9 package org.opendaylight.yangtools.yang.parser.impl;
10
11 import com.google.common.base.Preconditions;
12 import java.net.URI;
13 import java.net.URISyntaxException;
14 import java.util.ArrayList;
15 import java.util.List;
16 import javax.xml.stream.XMLStreamConstants;
17 import javax.xml.stream.XMLStreamException;
18 import javax.xml.stream.XMLStreamReader;
19 import org.opendaylight.yangtools.yang.common.QName;
20 import org.opendaylight.yangtools.yang.common.YangConstants;
21 import org.opendaylight.yangtools.yang.model.api.Rfc6020Mapping;
22 import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
23 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
24 import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupport;
25 import org.opendaylight.yangtools.yang.parser.spi.source.DeclarationInTextSource;
26 import org.opendaylight.yangtools.yang.parser.spi.source.PrefixToModule;
27 import org.opendaylight.yangtools.yang.parser.spi.source.QNameToStatementDefinition;
28 import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
29 import org.opendaylight.yangtools.yang.parser.spi.source.StatementWriter;
30 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.TypeUtils;
31 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.Utils;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
34
35 public class YinStatementParserImpl {
36
37     private static final Logger LOG = LoggerFactory.getLogger(YinStatementParserImpl.class);
38
39     private StatementWriter writer;
40     private QNameToStatementDefinition stmtDef;
41     private PrefixToModule prefixes;
42     private final String sourceName;
43     private String uriStr;
44     private boolean isType = false;
45     private boolean action = true;
46     private boolean yinElement = false;
47     private List<String> toBeSkipped = new ArrayList<>();
48
49     public YinStatementParserImpl(String sourceName) {
50         this.sourceName = Preconditions.checkNotNull(sourceName);
51     }
52
53     /**
54      *
55      * This method is supposed to be called in linkage phase, when YinStatementParserImpl instance has already been
56      * created.
57      * When done, start walking through YIN source
58      *
59      *
60      * @param writer - instance of StatementWriter to emit declared statements
61      * @param stmtDef - map of valid statement definitions for linkage phase
62      *
63      */
64     public void setAttributes(StatementWriter writer, QNameToStatementDefinition stmtDef) {
65         this.writer = writer;
66         this.stmtDef = stmtDef;
67     }
68
69     /**
70      *
71      * This method is supposed to be called in any phase but linkage, when YinStatementParserImpl instance has already
72      * been created.
73      * When done, start walking through YIN source
74      *
75      * @param writer - instance of StatementWriter to emit declared statements
76      * @param stmtDef - map of valid statement definitions for any phase but linkage
77      * @param prefixes - map of valid prefixes for any phase but linkage
78      *
79      */
80     public void setAttributes(StatementWriter writer, QNameToStatementDefinition stmtDef, PrefixToModule prefixes) {
81         this.writer = writer;
82         this.stmtDef = stmtDef;
83         this.prefixes = prefixes;
84     }
85
86     /**
87      *
88      * This method executes parsing YIN source and emitting declared statements via attached StatementWriter
89      *
90      * @param inputReader - instance of XMlStreamReader, allows forward, read-only access to XML.
91      *
92      */
93     public void walk(XMLStreamReader inputReader) {
94         try {
95             while (inputReader.hasNext()) {
96                 inputReader.next();
97                 if (inputReader.hasName() && inputReader.getEventType() == XMLStreamConstants.START_ELEMENT) {
98                     enterStatement(inputReader);
99                 }
100
101                 if (inputReader.hasName() && inputReader.getEventType() == XMLStreamConstants.END_ELEMENT) {
102                     exitStatement(inputReader);
103                 }
104             }
105         } catch (XMLStreamException e) {
106             LOG.warn("Fatal error detecting the next state of XMLStreamReader", e);
107         } catch (URISyntaxException e) {
108             LOG.warn("Given string {} violates RFC2396", uriStr, e);
109         }
110     }
111
112     private void startStatement(QName identifier, StatementSourceReference ref) {
113         writer.startStatement(identifier, ref);
114     }
115
116     private void argumentValue(XMLStreamReader inputReader, StatementSourceReference ref, QName identifier, boolean
117             yinElement) {
118         if (yinElement) {
119             writeTextOnlyElement(inputReader, ref);
120         } else {
121             writeNormalizedAttributeValue(inputReader, identifier, ref);
122         }
123     }
124
125     private void endStatement(StatementSourceReference ref) {
126         writer.endStatement(ref);
127     }
128
129     private void enterStatement(XMLStreamReader inputReader) throws URISyntaxException {
130         final StatementSourceReference ref = DeclarationInTextSource.atPosition(sourceName, inputReader
131                 .getLocation().getLineNumber(), inputReader.getLocation().getColumnNumber());
132         uriStr = inputReader.getNamespaceURI();
133         final QName identifier = new QName(new URI(uriStr), getElementFullName(inputReader));
134         if (yinElement && toBeSkipped.isEmpty()) {
135             //at yin element, it has be read as argument
136             argumentValue(inputReader, ref, identifier, true);
137         } else {
138             if (isStatementWithYinElement(identifier, stmtDef)) {
139                 //at statement with yin element, so next statement will be read as argument
140                 yinElement = true;
141             }
142
143             final QName validStatementDefinition = Utils.getValidStatementDefinition(prefixes, stmtDef, identifier);
144
145             //main part -> valid statement for actual phase
146             if (stmtDef != null && validStatementDefinition != null && toBeSkipped.isEmpty()) {
147                 if (identifier.equals(Rfc6020Mapping.TYPE.getStatementName())) {
148                     isType = true;
149                 } else {
150                     startStatement(validStatementDefinition, ref);
151                     if (isStatementWithYinElement(identifier, stmtDef)) {
152                         action = false;
153                     }
154                 }
155             } else {
156                 //if statement not found through all phases, throw exception
157                 if (writer.getPhase().equals(ModelProcessingPhase.FULL_DECLARATION)) {
158                     throw new IllegalArgumentException(identifier.getLocalName() + " is not a YIN " +
159                             "statement or use of extension. Source: " + ref);
160                 } else {
161                     //otherwise skip it (statement not to be read yet)
162                     action = false;
163                     toBeSkipped.add(getElementFullName(inputReader));
164                 }
165             }
166
167             if (isType) {
168                 writeTypeStmtAndArg(inputReader, identifier, ref);
169             } else if (action & isStatementWithArgument(identifier, stmtDef)) {
170                 argumentValue(inputReader, ref, identifier, false);
171             } else {
172                 action = true;
173             }
174         }
175     }
176
177     private void exitStatement(XMLStreamReader inputReader) throws URISyntaxException {
178         final String statementName = getElementFullName(inputReader);
179         final QName identifier = new QName(new URI(inputReader.getNamespaceURI()), statementName);
180         final StatementSourceReference ref = DeclarationInTextSource.atPosition(sourceName, inputReader
181                 .getLocation().getLineNumber(), inputReader.getLocation().getColumnNumber());
182         final QName validStatementDefinition = Utils.getValidStatementDefinition(prefixes, stmtDef, identifier);
183
184         if ((stmtDef != null && validStatementDefinition != null && toBeSkipped.isEmpty()) && !yinElement) {
185             endStatement(ref);
186         }
187
188         //back to normal mode
189         if (yinElement) {
190             yinElement = false;
191         }
192
193         if (toBeSkipped.contains(statementName)) {
194             toBeSkipped.remove(statementName);
195         }
196     }
197
198     private void writeTextOnlyElement(XMLStreamReader inputReader, StatementSourceReference ref) {
199         try {
200             writer.argumentValue(inputReader.getElementText(), ref);
201         } catch (XMLStreamException e) {
202             LOG.warn("Current event is not a START_ELEMENT or a non text element is encountered ", ref, e);
203         }
204     }
205
206     private void writeNormalizedAttributeValue(XMLStreamReader inputReader, QName
207             identifier, StatementSourceReference ref) {
208         final String attributeValue = getAttributeValue(inputReader, identifier, stmtDef);
209         if (attributeValue != null)
210             writer.argumentValue(attributeValue, ref);
211     }
212
213     private void writeTypeStmtAndArg(XMLStreamReader inputReader, QName identifier, StatementSourceReference ref) {
214         String argument = getAttributeValue(inputReader, identifier, stmtDef);
215         if (TypeUtils.isYangTypeBodyStmtString(argument)) {
216             startStatement(new QName(YangConstants.RFC6020_YIN_NAMESPACE, argument), ref);
217         } else {
218             startStatement(new QName(YangConstants.RFC6020_YIN_NAMESPACE, Rfc6020Mapping
219                     .TYPE.getStatementName().getLocalName()), ref);
220         }
221         argumentValue(inputReader, ref, identifier, false);
222         isType = false;
223     }
224
225     private static String getElementFullName(XMLStreamReader inputReader) {
226         if (!inputReader.getPrefix().isEmpty()) {
227             return inputReader.getPrefix() + ":" + inputReader.getLocalName();
228         } else {
229             return inputReader.getLocalName();
230         }
231     }
232
233     private static boolean isStatementWithArgument(QName identifier, QNameToStatementDefinition stmtDef) {
234         if (stmtDef != null && stmtDef.get(Utils.trimPrefix(identifier)) == null) {
235             return false;
236         } else if (((StatementSupport) stmtDef.get(Utils.trimPrefix(identifier)))
237                 .getPublicView().getArgumentName() == null) {
238             return false;
239         }
240         return true;
241     }
242
243     private static boolean isStatementWithYinElement(QName identifier, QNameToStatementDefinition stmtDef) {
244         final StatementDefinition statementDefinition = stmtDef.get(Utils.trimPrefix(identifier));
245         if (statementDefinition != null) {
246             return ((Rfc6020Mapping) ((StatementSupport) statementDefinition).getPublicView()).isArgumentYinElement();
247         } else {
248             return false;
249         }
250     }
251
252     private static String getAttributeValue(XMLStreamReader inputReader, QName identifier, QNameToStatementDefinition
253             stmtDef) {
254         String namespace = null;
255         return inputReader.getAttributeValue(namespace, (((StatementSupport) stmtDef.get(Utils.trimPrefix(identifier)))
256                 .getPublicView()).getArgumentName().getLocalName());
257     }
258 }