2 * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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 static com.google.common.collect.Iterators.singletonIterator;
11 import static com.google.common.collect.Iterators.transform;
12 import static java.util.Collections.emptyIterator;
13 import static java.util.Objects.requireNonNull;
15 import java.nio.charset.StandardCharsets;
16 import java.util.ArrayDeque;
17 import java.util.Arrays;
18 import java.util.Deque;
19 import java.util.Iterator;
20 import java.util.Map.Entry;
21 import java.util.NoSuchElementException;
22 import java.util.Optional;
23 import java.util.Queue;
24 import javax.xml.XMLConstants;
25 import javax.xml.stream.XMLEventFactory;
26 import javax.xml.stream.XMLEventReader;
27 import javax.xml.stream.XMLStreamException;
28 import javax.xml.stream.events.Attribute;
29 import javax.xml.stream.events.Characters;
30 import javax.xml.stream.events.EndElement;
31 import javax.xml.stream.events.StartElement;
32 import javax.xml.stream.events.XMLEvent;
33 import org.opendaylight.yangtools.yang.common.QName;
34 import org.opendaylight.yangtools.yang.model.api.meta.ArgumentDefinition;
35 import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
36 import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
38 final class YinXMLEventReader implements XMLEventReader {
39 private static final class OpenElement {
40 final Iterator<? extends DeclaredStatement<?>> children;
43 OpenElement(final Iterator<? extends DeclaredStatement<?>> children) {
44 this.children = requireNonNull(children);
48 OpenElement(final QName name, final Iterator<? extends DeclaredStatement<?>> children) {
49 this.children = requireNonNull(children);
50 this.name = requireNonNull(name);
54 private final Deque<OpenElement> stack = new ArrayDeque<>(8);
55 private final Queue<XMLEvent> events = new ArrayDeque<>();
56 private final ModuleNamespaceContext namespaceContext;
57 private final XMLEventFactory eventFactory;
59 YinXMLEventReader(final XMLEventFactory eventFactory, final ModuleNamespaceContext namespaceContext,
60 final DeclaredStatement<?> root) {
61 this.eventFactory = requireNonNull(eventFactory);
62 this.namespaceContext = requireNonNull(namespaceContext);
64 events.add(eventFactory.createStartDocument(StandardCharsets.UTF_8.name()));
66 final StatementDefinition def = root.statementDefinition();
67 final QName name = def.getStatementName();
68 final ArgumentDefinition arg = def.getArgumentDefinition().get();
70 events.add(eventFactory.createStartElement(XMLConstants.DEFAULT_NS_PREFIX, name.getNamespace().toString(),
71 name.getLocalName(), singletonIterator(attribute(arg.getArgumentName(), root.rawArgument())),
72 transform(namespaceContext.prefixesAndNamespaces().entrySet().iterator(),
73 e -> eventFactory.createNamespace(e.getKey(), e.getValue())),
76 stack.push(new OpenElement(name, root.declaredSubstatements().iterator()));
80 public XMLEvent next() {
81 XMLEvent event = events.poll();
87 event = events.poll();
89 throw new NoSuchElementException("All events have been processed");
95 public XMLEvent nextEvent() {
100 public boolean hasNext() {
101 if (events.isEmpty()) {
103 return events.isEmpty();
109 public XMLEvent peek() throws XMLStreamException {
110 if (events.isEmpty()) {
114 return events.peek();
118 public String getElementText() throws XMLStreamException {
119 XMLEvent current = peek();
120 if (current == null) {
121 throw new XMLStreamException("End of event stream");
123 if (!(current instanceof StartElement)) {
124 throw new XMLStreamException("Current event is " + current);
128 if (!(current instanceof Characters)) {
129 throw new XMLStreamException("Encountered non-text event " + current);
131 final String ret = ((Characters)current).getData();
134 if (!(current instanceof EndElement)) {
135 throw new XMLStreamException("Encountered unexpected event " + current);
141 public XMLEvent nextTag() throws XMLStreamException {
142 final XMLEvent next = next();
143 if (next instanceof Characters) {
144 throw new XMLStreamException("Significant characters encountered: " + next);
150 public Object getProperty(final String name) {
151 throw new IllegalArgumentException("Property " + name + " not supported");
155 public void close() {
160 private Attribute attribute(final QName qname, final String value) {
161 final Entry<String, String> ns = namespaceContext.prefixAndNamespaceFor(qname.getModule());
162 return eventFactory.createAttribute(ns.getKey(), ns.getValue(), qname.getLocalName(), value);
165 private StartElement startElement(final QName qname) {
166 final Entry<String, String> ns = namespaceContext.prefixAndNamespaceFor(qname.getModule());
167 return eventFactory.createStartElement(ns.getKey(), ns.getValue(), qname.getLocalName(), emptyIterator(),
168 emptyIterator(), namespaceContext);
171 private EndElement endElement(final QName qname) {
172 final Entry<String, String> ns = namespaceContext.prefixAndNamespaceFor(qname.getModule());
173 return eventFactory.createEndElement(ns.getKey(), ns.getValue(), qname.getLocalName());
176 private void nextStatement() {
177 OpenElement current = stack.peek();
178 if (current == null) {
183 while (current.children.hasNext()) {
184 // We have to mind child statement origin and not emit empty implicit children
185 final DeclaredStatement<?> child = current.children.next();
186 switch (child.statementOrigin()) {
188 final Iterator<? extends DeclaredStatement<?>> it = child.declaredSubstatements().iterator();
190 current = new OpenElement(it);
197 throw new IllegalStateException("Unhandled statement origin " + child.statementOrigin());
201 if (current.name != null) {
202 events.add(endElement(current.name));
205 if (stack.isEmpty()) {
206 events.add(eventFactory.createEndDocument());
208 } while (events.isEmpty());
211 private void addStatement(final DeclaredStatement<?> statement) {
212 final StatementDefinition def = statement.statementDefinition();
213 final QName name = def.getStatementName();
214 final Optional<ArgumentDefinition> optArgDef = def.getArgumentDefinition();
215 if (optArgDef.isPresent()) {
216 final ArgumentDefinition argDef = optArgDef.get();
217 final QName argName = argDef.getArgumentName();
218 if (argDef.isYinElement()) {
219 events.addAll(Arrays.asList(startElement(name), startElement(argName),
220 eventFactory.createCharacters(statement.rawArgument()), endElement(argName)));
222 final Entry<String, String> ns = namespaceContext.prefixAndNamespaceFor(name.getModule());
223 events.add(eventFactory.createStartElement(ns.getKey(), ns.getValue(), name.getLocalName(),
224 singletonIterator(attribute(argName, statement.rawArgument())), emptyIterator(), namespaceContext));
227 // No attributes: just emit a start
228 events.add(startElement(name));
231 stack.push(new OpenElement(name, statement.declaredSubstatements().iterator()));