2 * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.yangtools.yang.model.export;
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;
15 import java.util.Iterator;
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;
32 class SingleModuleYinStatementWriter implements StatementTextWriter {
34 private final XMLStreamWriter writer;
35 private final URI currentModuleNs;
36 private final BiMap<String, URI> prefixToNamespace;
37 private StatementDefinition currentStatement;
39 private SingleModuleYinStatementWriter(final XMLStreamWriter writer, final URI moduleNamespace,
40 final Map<String, URI> prefixToNs) {
43 this.currentModuleNs = moduleNamespace;
44 this.prefixToNamespace = HashBiMap.create(prefixToNs);
45 initializeYinNamespaceInXml();
48 private void initializeYinNamespaceInXml() {
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.");
57 } catch (final XMLStreamException e) {
58 throw new IllegalStateException(e);
62 static StatementTextWriter create(final XMLStreamWriter writer, final URI moduleNs,
63 final Map<String, URI> prefixToNs) {
64 return new SingleModuleYinStatementWriter(writer, moduleNs, prefixToNs);
68 public void startStatement(final StatementDefinition statement) {
69 currentStatement = Preconditions.checkNotNull(statement);
71 writeStartXmlElement(statement.getStatementName());
72 if (Rfc6020Mapping.MODULE.equals(statement) || Rfc6020Mapping.SUBMODULE.equals(statement)) {
73 declareXmlNamespaces(prefixToNamespace);
75 } catch (final XMLStreamException e) {
76 // FIXME: Introduce proper expression
77 throw new IllegalStateException(e);
82 public void endStatement() {
83 currentStatement = null;
86 } catch (final XMLStreamException e) {
87 // FIXME: Introduce proper expression
88 throw new IllegalStateException(e);
93 public void writeArgument(final String strRep) {
94 checkArgumentApplicable();
95 writeArgument0(strRep);
99 public void writeArgument(final QName value) {
100 checkArgumentApplicable();
101 final String valueStr = toPrefixedString(value);
102 writeArgument0(valueStr);
106 public void writeArgument(final SchemaPath targetPath) {
107 checkArgumentApplicable();
108 final StringBuilder valueStr = new StringBuilder();
109 if(targetPath.isAbsolute()) {
110 valueStr.append("/");
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("/");
119 writeArgument0(valueStr.toString());
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());
130 private void writeArgument0(final String strRep) {
132 if (isArgumentYinElement(currentStatement)) {
133 writeStartXmlElement(currentStatement.getArgumentName());
134 writeXmlText(strRep);
135 writeXmlEndElement();
137 writeXmlArgument(currentStatement.getArgumentName(), strRep);
139 } catch (final XMLStreamException e) {
140 // FIXME: throw proper exception
141 throw new IllegalStateException(e);
145 private static boolean isArgumentYinElement(final StatementDefinition currentStatement) {
146 if (currentStatement instanceof Rfc6020Mapping) {
147 return ((Rfc6020Mapping) currentStatement).isArgumentYinElement();
148 } else if (currentStatement instanceof ExtensionStatement) {
149 return ((ExtensionStatement) currentStatement).isArgumentYinElement();
154 private void checkArgumentApplicable() {
155 Preconditions.checkState(currentStatement != null, "No statement is opened.");
156 Preconditions.checkState(currentStatement.getArgumentName() != null, "Statement %s does not take argument.",
157 currentStatement.getArgumentName());
160 private static String toPrefixedString(@Nullable final String prefix, final String localName) {
161 if (prefix == null || prefix.isEmpty()) {
164 return prefix + ":" + localName;
167 private String toPrefixedString(final QName value) {
168 final URI valueNs = value.getNamespace();
169 final String valueLocal = value.getLocalName();
170 if (currentModuleNs.equals(valueNs)) {
173 final String prefix = ensureAndGetXmlNamespacePrefix(valueNs);
174 return toPrefixedString(prefix, valueLocal);
177 private @Nullable String ensureAndGetXmlNamespacePrefix(final URI namespace) {
178 if(YangConstants.RFC6020_YANG_NAMESPACE.equals(namespace)) {
179 // YANG namespace does not have prefix if used in arguments.
183 String prefix = writer.getNamespaceContext().getPrefix(namespace.toString());
184 if (prefix == null) {
185 // FIXME: declare prefix
186 prefix =prefixToNamespace.inverse().get(namespace);
189 throw new IllegalArgumentException("Namespace " + namespace + " is not bound to imported prefixes.");
194 private void writeXmlText(final String strRep) throws XMLStreamException {
195 writer.writeCharacters(strRep);
198 private void declareXmlNamespaces(final Map<String, URI> prefixToNamespace) {
200 writer.writeDefaultNamespace(YangConstants.RFC6020_YIN_NAMESPACE.toString());
201 for (final Entry<String, URI> nsDeclaration : prefixToNamespace.entrySet()) {
202 writer.writeNamespace(nsDeclaration.getKey(), nsDeclaration.getValue().toString());
204 } catch (final XMLStreamException e) {
205 throw new IllegalStateException(e);
209 private void writeXmlEndElement() throws XMLStreamException {
210 writer.writeEndElement();
213 private void writeXmlArgument(final QName qName, final String value) throws XMLStreamException {
214 writer.writeAttribute(qName.getNamespace().toString(), qName.getLocalName(), value);
217 private void writeStartXmlElement(final QName name) throws XMLStreamException {
218 writer.writeStartElement(name.getNamespace().toString(), name.getLocalName());