/* * 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.data.api.schema.tree; import com.google.common.annotations.Beta; import com.google.common.base.Preconditions; import java.util.Collection; import java.util.Iterator; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; @Beta public final class DataTreeCandidateNodes { private DataTreeCandidateNodes() { throw new UnsupportedOperationException(); } public static DataTreeCandidateNode fromNormalizedNode(final NormalizedNode<?, ?> node) { return new NormalizedNodeDataTreeCandidateNode(node); } /** * Applies the {@code node} to the {@code cursor}, note that if the top node of (@code node} is RootNode * you need to use {@link #applyRootedNodeToCursor(DataTreeModificationCursor, YangInstanceIdentifier, DataTreeCandidateNode) applyRootedNodeToCursor} * method that works with rooted node candidates * @param cursor cursor from the modification we want to apply the {@code node} to * @param node candidate tree to apply */ public static void applyToCursor(final DataTreeModificationCursor cursor, final DataTreeCandidateNode node) { switch (node.getModificationType()) { case DELETE: cursor.delete(node.getIdentifier()); break; case SUBTREE_MODIFIED: cursor.enter(node.getIdentifier()); AbstractNodeIterator iterator = new ExitingNodeIterator(null, node.getChildNodes().iterator()); do { iterator = iterator.next(cursor); } while (iterator != null); break; case UNMODIFIED: // No-op break; case WRITE: cursor.write(node.getIdentifier(), node.getDataAfter().get()); break; default: throw new IllegalArgumentException("Unsupported modification " + node.getModificationType()); } } /** * Applies the {@code node} that is rooted(doesn't have an identifier) in tree A to tree B's {@code cursor} * at location specified by {@code rootPath} * @param cursor cursor from the modification we want to apply the {@code node} to * @param rootPath path in the {@code cursor}'s tree we want to apply to candidate to * @param node candidate tree to apply */ public static void applyRootedNodeToCursor(final DataTreeModificationCursor cursor, final YangInstanceIdentifier rootPath, final DataTreeCandidateNode node) { switch (node.getModificationType()) { case DELETE: cursor.delete(rootPath.getLastPathArgument()); break; case SUBTREE_MODIFIED: cursor.enter(rootPath.getLastPathArgument()); AbstractNodeIterator iterator = new ExitingNodeIterator(null, node.getChildNodes().iterator()); do { iterator = iterator.next(cursor); } while (iterator != null); break; case UNMODIFIED: // No-op break; case WRITE: cursor.write(rootPath.getLastPathArgument(), node.getDataAfter().get()); break; default: throw new IllegalArgumentException("Unsupported modification " + node.getModificationType()); } } public static void applyRootToCursor(final DataTreeModificationCursor cursor, final DataTreeCandidateNode node) { switch (node.getModificationType()) { case DELETE: throw new IllegalArgumentException("Can not delete root."); case WRITE: case SUBTREE_MODIFIED: AbstractNodeIterator iterator = new RootNonExitingIterator(node.getChildNodes().iterator()); do { iterator = iterator.next(cursor); } while (iterator != null); break; case UNMODIFIED: // No-op break; default: throw new IllegalArgumentException("Unsupported modification " + node.getModificationType()); } } private abstract static class AbstractNodeIterator { private final Iterator<DataTreeCandidateNode> iterator; AbstractNodeIterator(final Iterator<DataTreeCandidateNode> iterator) { this.iterator = Preconditions.checkNotNull(iterator); } AbstractNodeIterator next(final DataTreeModificationCursor cursor) { while (iterator.hasNext()) { final DataTreeCandidateNode node = iterator.next(); switch (node.getModificationType()) { case DELETE: cursor.delete(node.getIdentifier()); break; case APPEARED: case DISAPPEARED: case SUBTREE_MODIFIED: final Collection<DataTreeCandidateNode> children = node.getChildNodes(); if (!children.isEmpty()) { cursor.enter(node.getIdentifier()); return new ExitingNodeIterator(this, children.iterator()); } break; case UNMODIFIED: // No-op break; case WRITE: cursor.write(node.getIdentifier(), node.getDataAfter().get()); break; default: throw new IllegalArgumentException("Unsupported modification " + node.getModificationType()); } } exitNode(cursor); return getParent(); } protected abstract @Nullable AbstractNodeIterator getParent(); protected abstract void exitNode(DataTreeModificationCursor cursor); } private static final class RootNonExitingIterator extends AbstractNodeIterator { protected RootNonExitingIterator(@Nonnull final Iterator<DataTreeCandidateNode> iterator) { super(iterator); } @Override protected void exitNode(final DataTreeModificationCursor cursor) { // Intentional noop. } @Override protected AbstractNodeIterator getParent() { return null; } } private static final class ExitingNodeIterator extends AbstractNodeIterator { private final AbstractNodeIterator parent; public ExitingNodeIterator(@Nullable final AbstractNodeIterator parent, @Nonnull final Iterator<DataTreeCandidateNode> iterator) { super(iterator); this.parent = parent; } @Override protected AbstractNodeIterator getParent() { return parent; } @Override protected void exitNode(final DataTreeModificationCursor cursor) { cursor.exit(); } } }