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