Remove explicit default super-constructor calls
[yangtools.git] / yang / yang-model-export / src / main / java / org / opendaylight / yangtools / yang / model / export / SingleModuleYinStatementWriter.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.model.export;
9
10 import com.google.common.annotations.Beta;
11 import com.google.common.base.Preconditions;
12 import com.google.common.collect.BiMap;
13 import com.google.common.collect.HashBiMap;
14 import java.net.URI;
15 import java.util.Iterator;
16 import java.util.Map;
17 import java.util.Map.Entry;
18 import javax.annotation.Nullable;
19 import javax.annotation.concurrent.NotThreadSafe;
20 import javax.xml.XMLConstants;
21 import javax.xml.stream.XMLStreamException;
22 import javax.xml.stream.XMLStreamWriter;
23 import org.opendaylight.yangtools.yang.common.QName;
24 import org.opendaylight.yangtools.yang.common.YangConstants;
25 import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
26 import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
27 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
28 import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
29
30 @Beta
31 @NotThreadSafe
32 class SingleModuleYinStatementWriter implements StatementTextWriter {
33
34     private final XMLStreamWriter writer;
35     private final URI currentModuleNs;
36     private final BiMap<String, URI> prefixToNamespace;
37     private StatementDefinition currentStatement;
38
39     private SingleModuleYinStatementWriter(final XMLStreamWriter writer, final URI moduleNamespace,
40             final Map<String, URI> prefixToNs) {
41         this.writer = writer;
42         this.currentModuleNs = moduleNamespace;
43         this.prefixToNamespace = HashBiMap.create(prefixToNs);
44         initializeYinNamespaceInXml();
45     }
46
47     private void initializeYinNamespaceInXml() {
48        try {
49             final String defaultNs = writer.getNamespaceContext().getNamespaceURI(XMLConstants.NULL_NS_URI);
50             if (defaultNs == null) {
51                 writer.setDefaultNamespace(YangConstants.RFC6020_YIN_NAMESPACE.toString());
52             } else if (!YangConstants.RFC6020_YIN_NAMESPACE.toString().equals(defaultNs)) {
53                 // FIXME: Implement support for exporting YIN as part of other XML document.
54                 throw new UnsupportedOperationException("Not implemented support for nesting YIN in different XML element.");
55             }
56         } catch (final XMLStreamException e) {
57             throw new IllegalStateException(e);
58         }
59     }
60
61     static StatementTextWriter create(final XMLStreamWriter writer, final URI moduleNs,
62             final Map<String, URI> prefixToNs) {
63         return new SingleModuleYinStatementWriter(writer, moduleNs, prefixToNs);
64     }
65
66     @Override
67     public void startStatement(final StatementDefinition statement) {
68         currentStatement = Preconditions.checkNotNull(statement);
69         try {
70             writeStartXmlElement(statement.getStatementName());
71             if (YangStmtMapping.MODULE.equals(statement) || YangStmtMapping.SUBMODULE.equals(statement)) {
72                 declareXmlNamespaces(prefixToNamespace);
73             }
74         } catch (final XMLStreamException e) {
75             // FIXME: Introduce proper expression
76             throw new IllegalStateException(e);
77         }
78     }
79
80     @Override
81     public void endStatement() {
82         currentStatement = null;
83         try {
84             writeXmlEndElement();
85         } catch (final XMLStreamException e) {
86             // FIXME: Introduce proper expression
87             throw new IllegalStateException(e);
88         }
89     }
90
91     @Override
92     public void writeArgument(final String strRep) {
93         checkArgumentApplicable();
94         writeArgument0(strRep);
95     }
96
97     @Override
98     public void writeArgument(final QName value) {
99         checkArgumentApplicable();
100         final String valueStr = toPrefixedString(value);
101         writeArgument0(valueStr);
102     }
103
104     @Override
105     public void writeArgument(final SchemaPath targetPath) {
106         checkArgumentApplicable();
107         final StringBuilder valueStr = new StringBuilder();
108         if (targetPath.isAbsolute()) {
109             valueStr.append("/");
110         }
111         final Iterator<QName> argIt = targetPath.getPathFromRoot().iterator();
112         while (argIt.hasNext()) {
113             valueStr.append(toPrefixedString(argIt.next()));
114             if (argIt.hasNext()) {
115                 valueStr.append("/");
116             }
117         }
118         writeArgument0(valueStr.toString());
119     }
120
121     @Override
122     public void writeArgument(final RevisionAwareXPath xpath) {
123         checkArgumentApplicable();
124         // FIXME: This implementation assumes prefixes are unchanged
125         // and were not changed in schema context.
126         writeArgument0(xpath.toString());
127     }
128
129     private void writeArgument0(final String strRep) {
130         try {
131             if (isArgumentYinElement(currentStatement)) {
132                 writeStartXmlElement(currentStatement.getArgumentName());
133                 writeXmlText(strRep);
134                 writeXmlEndElement();
135             } else {
136                 writeXmlArgument(currentStatement.getArgumentName(), strRep);
137             }
138         } catch (final XMLStreamException e) {
139             // FIXME: throw proper exception
140             throw new IllegalStateException(e);
141         }
142     }
143
144     private static boolean isArgumentYinElement(final StatementDefinition currentStatement) {
145         if (currentStatement instanceof YangStmtMapping || currentStatement instanceof ExtensionStatement) {
146             return currentStatement.isArgumentYinElement();
147         }
148         return false;
149     }
150
151     private void checkArgumentApplicable() {
152         Preconditions.checkState(currentStatement != null, "No statement is opened.");
153         Preconditions.checkState(currentStatement.getArgumentName() != null, "Statement %s does not take argument.",
154                 currentStatement.getArgumentName());
155     }
156
157     private static String toPrefixedString(@Nullable final String prefix, final String localName) {
158         if (prefix == null || prefix.isEmpty()) {
159             return localName;
160         }
161         return prefix + ":" + localName;
162     }
163
164     private String toPrefixedString(final QName value) {
165         final URI valueNs = value.getNamespace();
166         final String valueLocal = value.getLocalName();
167         if (currentModuleNs.equals(valueNs)) {
168             return valueLocal;
169         }
170         final String prefix = ensureAndGetXmlNamespacePrefix(valueNs);
171         return toPrefixedString(prefix, valueLocal);
172     }
173
174     private @Nullable String ensureAndGetXmlNamespacePrefix(final URI namespace) {
175         if (YangConstants.RFC6020_YANG_NAMESPACE.equals(namespace)) {
176          // YANG namespace does not have prefix if used in arguments.
177             return null;
178
179         }
180         String prefix = writer.getNamespaceContext().getPrefix(namespace.toString());
181         if (prefix == null) {
182             // FIXME: declare prefix
183             prefix =prefixToNamespace.inverse().get(namespace);
184         }
185         if (prefix == null) {
186             throw new IllegalArgumentException("Namespace " + namespace + " is not bound to imported prefixes.");
187         }
188         return prefix;
189     }
190
191     private void writeXmlText(final String strRep) throws XMLStreamException {
192         writer.writeCharacters(strRep);
193     }
194
195     private void declareXmlNamespaces(final Map<String, URI> prefixToNamespace) {
196         try {
197             writer.writeDefaultNamespace(YangConstants.RFC6020_YIN_NAMESPACE.toString());
198             for (final Entry<String, URI> nsDeclaration : prefixToNamespace.entrySet()) {
199                 writer.writeNamespace(nsDeclaration.getKey(), nsDeclaration.getValue().toString());
200             }
201         } catch (final XMLStreamException e) {
202             throw new IllegalStateException(e);
203         }
204     }
205
206     private void writeXmlEndElement() throws XMLStreamException {
207         writer.writeEndElement();
208     }
209
210     private void writeXmlArgument(final QName qName, final String value) throws XMLStreamException {
211         writer.writeAttribute(qName.getNamespace().toString(), qName.getLocalName(), value);
212     }
213
214     private void writeStartXmlElement(final QName name) throws XMLStreamException {
215         writer.writeStartElement(name.getNamespace().toString(), name.getLocalName());
216     }
217 }