X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=yang%2Fyang-data-codec-gson%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fyangtools%2Fyang%2Fdata%2Fcodec%2Fgson%2FJsonParserStream.java;h=3c53de312462566128982b4096b960433041f2c5;hb=b67f19b2bc2ac96ef94e3360b3fb188db6b49670;hp=aad07b35665f9c8c15390a659cb0af448cd45983;hpb=2719da7f1a121b999310aa1e075872e1a1d8d196;p=yangtools.git diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JsonParserStream.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JsonParserStream.java index aad07b3566..3c53de3124 100644 --- a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JsonParserStream.java +++ b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JsonParserStream.java @@ -7,8 +7,11 @@ */ package org.opendaylight.yangtools.yang.data.codec.gson; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; +import static java.util.Objects.requireNonNull; + import com.google.common.annotations.Beta; -import com.google.common.base.Preconditions; import com.google.gson.JsonIOException; import com.google.gson.JsonParseException; import com.google.gson.JsonSyntaxException; @@ -19,28 +22,41 @@ import java.io.EOFException; import java.io.Flushable; import java.io.IOException; import java.net.URI; +import java.util.AbstractMap.SimpleImmutableEntry; import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; import java.util.Deque; import java.util.HashSet; -import java.util.List; +import java.util.Iterator; +import java.util.Map.Entry; import java.util.Set; -import org.opendaylight.yangtools.yang.common.QName; +import javax.xml.transform.dom.DOMSource; +import org.eclipse.jdt.annotation.NonNull; +import org.opendaylight.yangtools.odlext.model.api.YangModeledAnyxmlSchemaNode; +import org.opendaylight.yangtools.util.xml.UntrustedXML; import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; -import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode; -import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode; +import org.opendaylight.yangtools.yang.data.util.AbstractNodeDataWithSchema; +import org.opendaylight.yangtools.yang.data.util.AnyXmlNodeDataWithSchema; +import org.opendaylight.yangtools.yang.data.util.CompositeNodeDataWithSchema; +import org.opendaylight.yangtools.yang.data.util.LeafListNodeDataWithSchema; +import org.opendaylight.yangtools.yang.data.util.LeafNodeDataWithSchema; +import org.opendaylight.yangtools.yang.data.util.ListNodeDataWithSchema; +import org.opendaylight.yangtools.yang.data.util.MultipleEntryDataWithSchema; +import org.opendaylight.yangtools.yang.data.util.OperationAsContainer; +import org.opendaylight.yangtools.yang.data.util.ParserStreamUtils; +import org.opendaylight.yangtools.yang.data.util.SimpleNodeDataWithSchema; +import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode; import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode; import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; -import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode; -import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; import org.opendaylight.yangtools.yang.model.api.Module; -import org.opendaylight.yangtools.yang.model.api.RpcDefinition; -import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.api.OperationDefinition; import org.opendaylight.yangtools.yang.model.api.SchemaNode; -import org.opendaylight.yangtools.yang.model.api.TypeDefinition; +import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Text; /** * This class parses JSON elements from a GSON JsonReader. It disallows multiple elements of the same name unlike the @@ -48,167 +64,288 @@ import org.opendaylight.yangtools.yang.model.api.TypeDefinition; */ @Beta public final class JsonParserStream implements Closeable, Flushable { + static final String ANYXML_ARRAY_ELEMENT_ID = "array-element"; + + private static final Logger LOG = LoggerFactory.getLogger(JsonParserStream.class); private final Deque namespaces = new ArrayDeque<>(); private final NormalizedNodeStreamWriter writer; private final JSONCodecFactory codecs; - private final SchemaContext schema; private final DataSchemaNode parentNode; - private JsonParserStream(final NormalizedNodeStreamWriter writer, final SchemaContext schemaContext, final DataSchemaNode parentNode) { - this.schema = Preconditions.checkNotNull(schemaContext); - this.writer = Preconditions.checkNotNull(writer); - this.codecs = JSONCodecFactory.create(schemaContext); + // TODO: consider class specialization to remove this field + private final boolean lenient; + + private JsonParserStream(final NormalizedNodeStreamWriter writer, final JSONCodecFactory codecs, + final DataSchemaNode parentNode, final boolean lenient) { + this.writer = requireNonNull(writer); + this.codecs = requireNonNull(codecs); this.parentNode = parentNode; + this.lenient = lenient; } - public static JsonParserStream create(final NormalizedNodeStreamWriter writer, final SchemaContext schemaContext, final SchemaNode parentNode ) { - if(parentNode instanceof RpcDefinition) { - return new JsonParserStream(writer, schemaContext, new RpcAsContainer((RpcDefinition) parentNode)); - } - Preconditions.checkArgument(parentNode instanceof DataSchemaNode, "Instance of DataSchemaNode class awaited."); - return new JsonParserStream(writer, schemaContext, (DataSchemaNode) parentNode); + /** + * Create a new {@link JsonParserStream} backed by specified {@link NormalizedNodeStreamWriter} + * and {@link JSONCodecFactory}. The stream will be logically rooted at the top of the SchemaContext associated + * with the specified codec factory. + * + * @param writer NormalizedNodeStreamWriter to use for instantiation of normalized nodes + * @param codecFactory {@link JSONCodecFactory} to use for parsing leaves + * @return A new {@link JsonParserStream} + * @throws NullPointerException if any of the arguments are null + */ + public static @NonNull JsonParserStream create(final @NonNull NormalizedNodeStreamWriter writer, + final @NonNull JSONCodecFactory codecFactory) { + return new JsonParserStream(writer, codecFactory, codecFactory.getSchemaContext(), false); + } + + /** + * Create a new {@link JsonParserStream} backed by specified {@link NormalizedNodeStreamWriter} + * and {@link JSONCodecFactory}. The stream will be logically rooted at the specified parent node. + * + * @param writer NormalizedNodeStreamWriter to use for instantiation of normalized nodes + * @param codecFactory {@link JSONCodecFactory} to use for parsing leaves + * @param parentNode Logical root node + * @return A new {@link JsonParserStream} + * @throws NullPointerException if any of the arguments are null + */ + public static @NonNull JsonParserStream create(final @NonNull NormalizedNodeStreamWriter writer, + final @NonNull JSONCodecFactory codecFactory, final @NonNull SchemaNode parentNode) { + return new JsonParserStream(writer, codecFactory, validateParent(parentNode), false); } - public static JsonParserStream create(final NormalizedNodeStreamWriter writer, final SchemaContext schemaContext) { - return new JsonParserStream(writer, schemaContext, schemaContext); + /** + * Create a new {@link JsonParserStream} backed by specified {@link NormalizedNodeStreamWriter} + * and {@link JSONCodecFactory}. The stream will be logically rooted at the top of the SchemaContext associated + * with the specified codec factory. + * + *

+ * Returned parser will treat incoming JSON data leniently: + *

+ * + * @param writer NormalizedNodeStreamWriter to use for instantiation of normalized nodes + * @param codecFactory {@link JSONCodecFactory} to use for parsing leaves + * @return A new {@link JsonParserStream} + * @throws NullPointerException if any of the arguments are null + */ + public static @NonNull JsonParserStream createLenient(final @NonNull NormalizedNodeStreamWriter writer, + final @NonNull JSONCodecFactory codecFactory) { + return new JsonParserStream(writer, codecFactory, codecFactory.getSchemaContext(), true); } - public JsonParserStream parse(final JsonReader reader) throws JsonIOException, JsonSyntaxException { + /** + * Create a new {@link JsonParserStream} backed by specified {@link NormalizedNodeStreamWriter} + * and {@link JSONCodecFactory}. The stream will be logically rooted at the specified parent node. + * + *

+ * Returned parser will treat incoming JSON data leniently: + *

+ * + * @param writer NormalizedNodeStreamWriter to use for instantiation of normalized nodes + * @param codecFactory {@link JSONCodecFactory} to use for parsing leaves + * @param parentNode Logical root node + * @return A new {@link JsonParserStream} + * @throws NullPointerException if any of the arguments are null + */ + public static @NonNull JsonParserStream createLenient(final @NonNull NormalizedNodeStreamWriter writer, + final @NonNull JSONCodecFactory codecFactory, final @NonNull SchemaNode parentNode) { + return new JsonParserStream(writer, codecFactory, validateParent(parentNode), true); + } + + public JsonParserStream parse(final JsonReader reader) { // code copied from gson's JsonParser and Stream classes - final boolean lenient = reader.isLenient(); + final boolean readerLenient = reader.isLenient(); reader.setLenient(true); boolean isEmpty = true; try { reader.peek(); isEmpty = false; - final CompositeNodeDataWithSchema compositeNodeDataWithSchema = new CompositeNodeDataWithSchema(parentNode); + final CompositeNodeDataWithSchema compositeNodeDataWithSchema = + new CompositeNodeDataWithSchema<>(parentNode); read(reader, compositeNodeDataWithSchema); compositeNodeDataWithSchema.write(writer); return this; - // return read(reader); } catch (final EOFException e) { if (isEmpty) { return this; - // return JsonNull.INSTANCE; } // The stream ended prematurely so it is likely a syntax error. throw new JsonSyntaxException(e); - } catch (final MalformedJsonException e) { + } catch (final MalformedJsonException | NumberFormatException e) { throw new JsonSyntaxException(e); } catch (final IOException e) { throw new JsonIOException(e); - } catch (final NumberFormatException e) { - throw new JsonSyntaxException(e); } catch (StackOverflowError | OutOfMemoryError e) { throw new JsonParseException("Failed parsing JSON source: " + reader + " to Json", e); } finally { - reader.setLenient(lenient); + reader.setLenient(readerLenient); } } - private final void setValue(final AbstractNodeDataWithSchema parent, final String value) { - Preconditions.checkArgument(parent instanceof SimpleNodeDataWithSchema, "Node %s is not a simple type", parent); + private void traverseAnyXmlValue(final JsonReader in, final Document doc, final Element parentElement) + throws IOException { + switch (in.peek()) { + case STRING: + case NUMBER: + Text textNode = doc.createTextNode(in.nextString()); + parentElement.appendChild(textNode); + break; + case BOOLEAN: + textNode = doc.createTextNode(Boolean.toString(in.nextBoolean())); + parentElement.appendChild(textNode); + break; + case NULL: + in.nextNull(); + textNode = doc.createTextNode("null"); + parentElement.appendChild(textNode); + break; + case BEGIN_ARRAY: + in.beginArray(); + while (in.hasNext()) { + final Element childElement = doc.createElement(ANYXML_ARRAY_ELEMENT_ID); + parentElement.appendChild(childElement); + traverseAnyXmlValue(in, doc, childElement); + } + in.endArray(); + break; + case BEGIN_OBJECT: + in.beginObject(); + while (in.hasNext()) { + final Element childElement = doc.createElement(in.nextName()); + parentElement.appendChild(childElement); + traverseAnyXmlValue(in, doc, childElement); + } + in.endObject(); + break; + default: + break; + } + } + + private void readAnyXmlValue(final JsonReader in, final AnyXmlNodeDataWithSchema parent, + final String anyXmlObjectName) throws IOException { + final String anyXmlObjectNS = getCurrentNamespace().toString(); + final Document doc = UntrustedXML.newDocumentBuilder().newDocument(); + final Element rootElement = doc.createElementNS(anyXmlObjectNS, anyXmlObjectName); + doc.appendChild(rootElement); + traverseAnyXmlValue(in, doc, rootElement); - final Object translatedValue = translateValueByType(value, parent.getSchema()); - ((SimpleNodeDataWithSchema) parent).setValue(translatedValue); + final DOMSource domSource = new DOMSource(doc.getDocumentElement()); + parent.setValue(domSource); } - public void read(final JsonReader in, final AbstractNodeDataWithSchema parent) throws IOException { + public void read(final JsonReader in, AbstractNodeDataWithSchema parent) throws IOException { switch (in.peek()) { - case STRING: - case NUMBER: - setValue(parent, in.nextString()); - break; - case BOOLEAN: - setValue(parent, Boolean.toString(in.nextBoolean())); - break; - case NULL: - in.nextNull(); - setValue(parent, null); - break; - case BEGIN_ARRAY: - in.beginArray(); - while (in.hasNext()) { - AbstractNodeDataWithSchema newChild = null; - if (parent instanceof ListNodeDataWithSchema) { - newChild = new ListEntryNodeDataWithSchema(parent.getSchema()); - ((CompositeNodeDataWithSchema) parent).addChild(newChild); - } else if (parent instanceof LeafListNodeDataWithSchema) { - newChild = new LeafListEntryNodeDataWithSchema(parent.getSchema()); - ((CompositeNodeDataWithSchema) parent).addChild(newChild); - } - read(in, newChild); - } - in.endArray(); - return; - case BEGIN_OBJECT: - final Set namesakes = new HashSet<>(); - in.beginObject(); - while (in.hasNext()) { - final String jsonElementName = in.nextName(); - final NamespaceAndName namespaceAndName = resolveNamespace(jsonElementName, parent.getSchema()); - final String localName = namespaceAndName.getName(); - addNamespace(namespaceAndName.getUri()); - if (namesakes.contains(jsonElementName)) { - throw new JsonSyntaxException("Duplicate name " + jsonElementName + " in JSON input."); + case STRING: + case NUMBER: + setValue(parent, in.nextString()); + break; + case BOOLEAN: + setValue(parent, Boolean.toString(in.nextBoolean())); + break; + case NULL: + in.nextNull(); + setValue(parent, null); + break; + case BEGIN_ARRAY: + in.beginArray(); + while (in.hasNext()) { + if (parent instanceof LeafNodeDataWithSchema) { + read(in, parent); + } else { + final AbstractNodeDataWithSchema newChild = newArrayEntry(parent); + read(in, newChild); + } } - namesakes.add(jsonElementName); - final Deque childDataSchemaNodes = findSchemaNodeByNameAndNamespace(parent.getSchema(), - localName, getCurrentNamespace()); - if (childDataSchemaNodes.isEmpty()) { - throw new IllegalStateException("Schema for node with name " + localName + " and namespace " - + getCurrentNamespace() + " doesn't exist."); + in.endArray(); + return; + case BEGIN_OBJECT: + final Set namesakes = new HashSet<>(); + in.beginObject(); + /* + * This allows parsing of incorrectly /as showcased/ + * in testconf nesting of list items - eg. + * lists with one value are sometimes serialized + * without wrapping array. + * + */ + if (isArray(parent)) { + parent = newArrayEntry(parent); } + while (in.hasNext()) { + final String jsonElementName = in.nextName(); + DataSchemaNode parentSchema = parent.getSchema(); + if (parentSchema instanceof YangModeledAnyxmlSchemaNode) { + parentSchema = ((YangModeledAnyxmlSchemaNode) parentSchema).getSchemaOfAnyXmlData(); + } + final Entry namespaceAndName = resolveNamespace(jsonElementName, parentSchema); + final String localName = namespaceAndName.getKey(); + final URI namespace = namespaceAndName.getValue(); + if (lenient && (localName == null || namespace == null)) { + LOG.debug("Schema node with name {} was not found under {}", localName, + parentSchema.getQName()); + in.skipValue(); + continue; + } + addNamespace(namespace); + if (!namesakes.add(jsonElementName)) { + throw new JsonSyntaxException("Duplicate name " + jsonElementName + " in JSON input."); + } - AbstractNodeDataWithSchema newChild; - newChild = ((CompositeNodeDataWithSchema) parent).addChild(childDataSchemaNodes); -// FIXME:anyxml data shouldn't be skipped but should be loaded somehow. will be specified after 17AUG2014 - if (newChild instanceof AnyXmlNodeDataWithSchema) { - in.skipValue(); - } else { - read(in, newChild); + final Deque childDataSchemaNodes = + ParserStreamUtils.findSchemaNodeByNameAndNamespace(parentSchema, localName, + getCurrentNamespace()); + checkState(!childDataSchemaNodes.isEmpty(), + "Schema for node with name %s and namespace %s does not exist at %s", + localName, getCurrentNamespace(), parentSchema.getPath()); + + + final AbstractNodeDataWithSchema newChild = ((CompositeNodeDataWithSchema) parent) + .addChild(childDataSchemaNodes); + if (newChild instanceof AnyXmlNodeDataWithSchema) { + readAnyXmlValue(in, (AnyXmlNodeDataWithSchema) newChild, jsonElementName); + } else { + read(in, newChild); + } + removeNamespace(); } - removeNamespace(); - } - in.endObject(); - return; - case END_DOCUMENT: - case NAME: - case END_OBJECT: - case END_ARRAY: - break; + in.endObject(); + return; + default: + break; } } - private Object translateValueByType(final String value, final DataSchemaNode node) { - final TypeDefinition typeDefinition = typeDefinition(node); - if (typeDefinition == null) { - return value; - } - - return codecs.codecFor(typeDefinition).deserialize(value); + private static boolean isArray(final AbstractNodeDataWithSchema parent) { + return parent instanceof ListNodeDataWithSchema || parent instanceof LeafListNodeDataWithSchema; } - private static TypeDefinition typeDefinition(final DataSchemaNode node) { - TypeDefinition baseType = null; - if (node instanceof LeafListSchemaNode) { - baseType = ((LeafListSchemaNode) node).getType(); - } else if (node instanceof LeafSchemaNode) { - baseType = ((LeafSchemaNode) node).getType(); - } else if (node instanceof AnyXmlSchemaNode) { - return null; - } else { - throw new IllegalArgumentException("Unhandled parameter types: " + Arrays. asList(node).toString()); + private static AbstractNodeDataWithSchema newArrayEntry(final AbstractNodeDataWithSchema parent) { + if (!(parent instanceof MultipleEntryDataWithSchema)) { + throw new IllegalStateException("Found an unexpected array nested under " + parent.getSchema().getQName()); } + return ((MultipleEntryDataWithSchema) parent).newChildEntry(); + } - if (baseType != null) { - while (baseType.getBaseType() != null) { - baseType = baseType.getBaseType(); - } - } - return baseType; + private void setValue(final AbstractNodeDataWithSchema parent, final String value) { + checkArgument(parent instanceof SimpleNodeDataWithSchema, "Node %s is not a simple type", + parent.getSchema().getQName()); + final SimpleNodeDataWithSchema parentSimpleNode = (SimpleNodeDataWithSchema) parent; + checkArgument(parentSimpleNode.getValue() == null, "Node '%s' has already set its value to '%s'", + parentSimpleNode.getSchema().getQName(), parentSimpleNode.getValue()); + + final Object translatedValue = translateValueByType(value, parentSimpleNode.getSchema()); + parentSimpleNode.setValue(translatedValue); + } + + private Object translateValueByType(final String value, final DataSchemaNode node) { + checkArgument(node instanceof TypedDataSchemaNode); + return codecs.codecFor((TypedDataSchemaNode) node).parseValue(null, value); } private void removeNamespace() { @@ -219,7 +356,7 @@ public final class JsonParserStream implements Closeable, Flushable { namespaces.push(namespace); } - private NamespaceAndName resolveNamespace(final String childName, final DataSchemaNode dataSchemaNode) { + private Entry resolveNamespace(final String childName, final DataSchemaNode dataSchemaNode) { final int lastIndexOfColon = childName.lastIndexOf(':'); String moduleNamePart = null; String nodeNamePart = null; @@ -228,35 +365,36 @@ public final class JsonParserStream implements Closeable, Flushable { moduleNamePart = childName.substring(0, lastIndexOfColon); nodeNamePart = childName.substring(lastIndexOfColon + 1); - final Module m = schema.findModuleByName(moduleNamePart, null); - namespace = m == null ? null : m.getNamespace(); + final Iterator m = codecs.getSchemaContext().findModules(moduleNamePart).iterator(); + namespace = m.hasNext() ? m.next().getNamespace() : null; } else { nodeNamePart = childName; } if (namespace == null) { - Set potentialUris = Collections.emptySet(); - potentialUris = resolveAllPotentialNamespaces(nodeNamePart, dataSchemaNode); + final Set potentialUris = resolveAllPotentialNamespaces(nodeNamePart, dataSchemaNode); if (potentialUris.contains(getCurrentNamespace())) { namespace = getCurrentNamespace(); } else if (potentialUris.size() == 1) { namespace = potentialUris.iterator().next(); } else if (potentialUris.size() > 1) { - throw new IllegalStateException("Choose suitable module name for element "+nodeNamePart+":"+toModuleNames(potentialUris)); - } else if (potentialUris.isEmpty()) { - throw new IllegalStateException("Schema node with name "+nodeNamePart+" wasn't found."); + throw new IllegalStateException("Choose suitable module name for element " + nodeNamePart + ":" + + toModuleNames(potentialUris)); + } else if (potentialUris.isEmpty() && !lenient) { + throw new IllegalStateException("Schema node with name " + nodeNamePart + " was not found under " + + dataSchemaNode.getQName() + "."); } } - return new NamespaceAndName(nodeNamePart, namespace); + return new SimpleImmutableEntry<>(nodeNamePart, namespace); } private String toModuleNames(final Set potentialUris) { final StringBuilder builder = new StringBuilder(); for (final URI potentialUri : potentialUris) { - builder.append("\n"); + builder.append('\n'); //FIXME how to get information about revision from JSON input? currently first available is used. - builder.append(schema.findModuleByNamespace(potentialUri).iterator().next().getName()); + builder.append(codecs.getSchemaContext().findModules(potentialUri).iterator().next().getName()); } return builder.toString(); } @@ -274,7 +412,7 @@ public final class JsonParserStream implements Closeable, Flushable { } for (final ChoiceSchemaNode choiceNode : choices) { - for (final ChoiceCaseNode concreteCase : choiceNode.getCases()) { + for (final CaseSchemaNode concreteCase : choiceNode.getCases().values()) { potentialUris.addAll(resolveAllPotentialNamespaces(elementName, concreteCase)); } } @@ -286,64 +424,13 @@ public final class JsonParserStream implements Closeable, Flushable { return namespaces.peek(); } - /** - * Returns stack of schema nodes via which it was necessary to pass to get schema node with specified - * {@code childName} and {@code namespace} - * - * @param dataSchemaNode - * @param childName - * @param namespace - * @return stack of schema nodes via which it was passed through. If found schema node is direct child then stack - * contains only one node. If it is found under choice and case then stack should contains 2*n+1 element - * (where n is number of choices through it was passed) - */ - private Deque findSchemaNodeByNameAndNamespace(final DataSchemaNode dataSchemaNode, - final String childName, final URI namespace) { - final Deque result = new ArrayDeque<>(); - final List childChoices = new ArrayList<>(); - if (dataSchemaNode instanceof DataNodeContainer) { - for (final DataSchemaNode childNode : ((DataNodeContainer) dataSchemaNode).getChildNodes()) { - if (childNode instanceof ChoiceSchemaNode) { - childChoices.add((ChoiceSchemaNode) childNode); - } else { - final QName childQName = childNode.getQName(); - if (childQName.getLocalName().equals(childName) && childQName.getNamespace().equals(namespace)) { - result.push(childNode); - return result; - } - } - } - } - // try to find data schema node in choice (looking for first match) - for (final ChoiceSchemaNode choiceNode : childChoices) { - for (final ChoiceCaseNode concreteCase : choiceNode.getCases()) { - final Deque resultFromRecursion = findSchemaNodeByNameAndNamespace(concreteCase, childName, - namespace); - if (!resultFromRecursion.isEmpty()) { - resultFromRecursion.push(concreteCase); - resultFromRecursion.push(choiceNode); - return resultFromRecursion; - } - } - } - return result; - } - - private static class NamespaceAndName { - private final URI uri; - private final String name; - - public NamespaceAndName(final String name, final URI uri) { - this.name = name; - this.uri = uri; - } - - public String getName() { - return name; - } - - public URI getUri() { - return uri; + private static DataSchemaNode validateParent(final SchemaNode parent) { + if (parent instanceof DataSchemaNode) { + return (DataSchemaNode) parent; + } else if (parent instanceof OperationDefinition) { + return OperationAsContainer.of((OperationDefinition) parent); + } else { + throw new IllegalArgumentException("Illegal parent node " + requireNonNull(parent)); } }