/* * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.yangtools.yang.model.export; import com.google.common.annotations.Beta; import com.google.common.base.Preconditions; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; import java.net.URI; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import javax.annotation.Nullable; import javax.annotation.concurrent.NotThreadSafe; import javax.xml.XMLConstants; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.common.YangConstants; import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath; import org.opendaylight.yangtools.yang.model.api.YangStmtMapping; import org.opendaylight.yangtools.yang.model.api.SchemaPath; import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition; @Beta @NotThreadSafe class SingleModuleYinStatementWriter implements StatementTextWriter { private final XMLStreamWriter writer; private final URI currentModuleNs; private final BiMap<String, URI> prefixToNamespace; private StatementDefinition currentStatement; private SingleModuleYinStatementWriter(final XMLStreamWriter writer, final URI moduleNamespace, final Map<String, URI> prefixToNs) { super(); this.writer = writer; this.currentModuleNs = moduleNamespace; this.prefixToNamespace = HashBiMap.create(prefixToNs); initializeYinNamespaceInXml(); } private void initializeYinNamespaceInXml() { try { final String defaultNs = writer.getNamespaceContext().getNamespaceURI(XMLConstants.NULL_NS_URI); if (defaultNs == null) { writer.setDefaultNamespace(YangConstants.RFC6020_YIN_NAMESPACE.toString()); } else if (!YangConstants.RFC6020_YIN_NAMESPACE.toString().equals(defaultNs)) { // FIXME: Implement support for exporting YIN as part of other XML document. throw new UnsupportedOperationException("Not implemented support for nesting YIN in different XML element."); } } catch (final XMLStreamException e) { throw new IllegalStateException(e); } } static StatementTextWriter create(final XMLStreamWriter writer, final URI moduleNs, final Map<String, URI> prefixToNs) { return new SingleModuleYinStatementWriter(writer, moduleNs, prefixToNs); } @Override public void startStatement(final StatementDefinition statement) { currentStatement = Preconditions.checkNotNull(statement); try { writeStartXmlElement(statement.getStatementName()); if (YangStmtMapping.MODULE.equals(statement) || YangStmtMapping.SUBMODULE.equals(statement)) { declareXmlNamespaces(prefixToNamespace); } } catch (final XMLStreamException e) { // FIXME: Introduce proper expression throw new IllegalStateException(e); } } @Override public void endStatement() { currentStatement = null; try { writeXmlEndElement(); } catch (final XMLStreamException e) { // FIXME: Introduce proper expression throw new IllegalStateException(e); } } @Override public void writeArgument(final String strRep) { checkArgumentApplicable(); writeArgument0(strRep); } @Override public void writeArgument(final QName value) { checkArgumentApplicable(); final String valueStr = toPrefixedString(value); writeArgument0(valueStr); } @Override public void writeArgument(final SchemaPath targetPath) { checkArgumentApplicable(); final StringBuilder valueStr = new StringBuilder(); if (targetPath.isAbsolute()) { valueStr.append("/"); } final Iterator<QName> argIt = targetPath.getPathFromRoot().iterator(); while (argIt.hasNext()) { valueStr.append(toPrefixedString(argIt.next())); if (argIt.hasNext()) { valueStr.append("/"); } } writeArgument0(valueStr.toString()); } @Override public void writeArgument(final RevisionAwareXPath xpath) { checkArgumentApplicable(); // FIXME: This implementation assumes prefixes are unchanged // and were not changed in schema context. writeArgument0(xpath.toString()); } private void writeArgument0(final String strRep) { try { if (isArgumentYinElement(currentStatement)) { writeStartXmlElement(currentStatement.getArgumentName()); writeXmlText(strRep); writeXmlEndElement(); } else { writeXmlArgument(currentStatement.getArgumentName(), strRep); } } catch (final XMLStreamException e) { // FIXME: throw proper exception throw new IllegalStateException(e); } } private static boolean isArgumentYinElement(final StatementDefinition currentStatement) { if (currentStatement instanceof YangStmtMapping || currentStatement instanceof ExtensionStatement) { return currentStatement.isArgumentYinElement(); } return false; } private void checkArgumentApplicable() { Preconditions.checkState(currentStatement != null, "No statement is opened."); Preconditions.checkState(currentStatement.getArgumentName() != null, "Statement %s does not take argument.", currentStatement.getArgumentName()); } private static String toPrefixedString(@Nullable final String prefix, final String localName) { if (prefix == null || prefix.isEmpty()) { return localName; } return prefix + ":" + localName; } private String toPrefixedString(final QName value) { final URI valueNs = value.getNamespace(); final String valueLocal = value.getLocalName(); if (currentModuleNs.equals(valueNs)) { return valueLocal; } final String prefix = ensureAndGetXmlNamespacePrefix(valueNs); return toPrefixedString(prefix, valueLocal); } private @Nullable String ensureAndGetXmlNamespacePrefix(final URI namespace) { if (YangConstants.RFC6020_YANG_NAMESPACE.equals(namespace)) { // YANG namespace does not have prefix if used in arguments. return null; } String prefix = writer.getNamespaceContext().getPrefix(namespace.toString()); if (prefix == null) { // FIXME: declare prefix prefix =prefixToNamespace.inverse().get(namespace); } if (prefix == null) { throw new IllegalArgumentException("Namespace " + namespace + " is not bound to imported prefixes."); } return prefix; } private void writeXmlText(final String strRep) throws XMLStreamException { writer.writeCharacters(strRep); } private void declareXmlNamespaces(final Map<String, URI> prefixToNamespace) { try { writer.writeDefaultNamespace(YangConstants.RFC6020_YIN_NAMESPACE.toString()); for (final Entry<String, URI> nsDeclaration : prefixToNamespace.entrySet()) { writer.writeNamespace(nsDeclaration.getKey(), nsDeclaration.getValue().toString()); } } catch (final XMLStreamException e) { throw new IllegalStateException(e); } } private void writeXmlEndElement() throws XMLStreamException { writer.writeEndElement(); } private void writeXmlArgument(final QName qName, final String value) throws XMLStreamException { writer.writeAttribute(qName.getNamespace().toString(), qName.getLocalName(), value); } private void writeStartXmlElement(final QName name) throws XMLStreamException { writer.writeStartElement(name.getNamespace().toString(), name.getLocalName()); } }