/* * Copyright (c) 2013 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.operations; import java.util.List; import java.util.Set; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode; import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode; import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode; import org.opendaylight.yangtools.yang.data.api.schema.LeafNode; import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode; import org.opendaylight.yangtools.yang.data.api.schema.MapNode; import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder; import org.opendaylight.yangtools.yang.model.api.AugmentationSchema; import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode; import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; import com.google.common.base.Optional; import com.google.common.base.Preconditions; import com.google.common.collect.Lists; import com.google.common.collect.Sets; abstract class AbstractContainerNodeModification> implements Modification { @Override public final Optional modify(S schema, Optional actual, Optional modification, OperationStack operationStack) throws DataModificationException { operationStack.enteringNode(modification); Optional result; QName nodeQName = getQName(schema); switch (operationStack.getCurrentOperation()) { case DELETE: { DataModificationException.DataMissingException.check(nodeQName, actual); } case REMOVE: { result = Optional.absent(); break; } case CREATE: { DataModificationException.DataExistsException.check(nodeQName, actual, null); } case REPLACE: { result = modification; break; } case NONE: { DataModificationException.DataMissingException.check(nodeQName, actual); } case MERGE: { // Recursively modify all child nodes result = modifyContainer(schema, actual, modification, operationStack); break; } default: throw new UnsupportedOperationException(String.format("Unable to perform operation: %s on: %s, unknown", operationStack.getCurrentOperation(), schema)); } operationStack.exitingNode(modification); return result; } protected abstract QName getQName(S schema); private Optional modifyContainer(S schema, Optional actual, Optional modification, OperationStack operationStack) throws DataModificationException { if (actual.isPresent() == false) { return modification; } if (modification.isPresent() == false) { return actual; } Set toProcess = getChildrenToProcess(schema, actual, modification); List> merged = modifyContainerChildNodes(schema, operationStack, actual.get(), modification.get(), toProcess); return build(schema, merged); } private List> modifyContainerChildNodes(S schema, OperationStack operationStack, N actual, N modification, Set toProcess) throws DataModificationException { List> result = Lists.newArrayList(); for (InstanceIdentifier.PathArgument childToProcessId : toProcess) { Object schemaOfChildToProcess = findSchema(schema, childToProcessId); Optional> modifiedValues = modifyContainerNode(operationStack, actual, modification, childToProcessId, schemaOfChildToProcess); if (modifiedValues.isPresent()) { result.add(modifiedValues.get()); } } return result; } private Optional> modifyContainerNode(OperationStack operationStack, N actual, N modification, InstanceIdentifier.PathArgument childToProcess, Object schemaChild) throws DataModificationException { Optional> storedChildren = actual.getChild(childToProcess); Optional> modifiedChildren = modification.getChild(childToProcess); return NodeDispatcher.dispatchChildModification(schemaChild, storedChildren, modifiedChildren, operationStack); } private Object findSchema(S schema, InstanceIdentifier.PathArgument childToProcessId) { if (childToProcessId instanceof InstanceIdentifier.AugmentationIdentifier) { return findSchemaForAugment(schema, (InstanceIdentifier.AugmentationIdentifier) childToProcessId); } else { return findSchemaForChild(schema, childToProcessId.getNodeType()); } } protected abstract Object findSchemaForChild(S schema, QName nodeType); protected abstract Object findSchemaForAugment(S schema, InstanceIdentifier.AugmentationIdentifier childToProcessId); private Optional build(S schema, List> result) { DataContainerNodeBuilder b = getBuilder(schema); // TODO skip empty container nodes ? e.g. if container looses all its child nodes // if(result.isEmpty()) { // return Optional.absent(); // } for (DataContainerChild dataContainerChild : result) { b.withChild(dataContainerChild); } return Optional.of(b.build()); } protected abstract DataContainerNodeBuilder getBuilder(S schema); protected Set getChildrenToProcess(S schema, Optional actual, Optional modification) throws DataModificationException { Set qNames = Sets.newLinkedHashSet(); qNames.addAll(getChildQNames(actual)); qNames.addAll(getChildQNames(modification)); return qNames; } private Set getChildQNames(Optional actual) { Set qNames = Sets.newLinkedHashSet(); for (DataContainerChild child : actual.get().getValue()) { qNames.add(child.getIdentifier()); } return qNames; } private static final class NodeDispatcher { private static final LeafNodeModification LEAF_NODE_MODIFICATION = new LeafNodeModification(); private static final LeafSetNodeModification LEAF_SET_NODE_MODIFICATION = new LeafSetNodeModification(); private static final AugmentationNodeModification AUGMENTATION_NODE_MODIFICATION = new AugmentationNodeModification(); private static final MapNodeModification MAP_NODE_MODIFICATION = new MapNodeModification(); private static final ContainerNodeModification CONTAINER_NODE_MODIFICATION = new ContainerNodeModification(); private static final ChoiceNodeModification CHOICE_NODE_MODIFICATION = new ChoiceNodeModification(); static Optional> dispatchChildModification(Object schemaChild, Optional> actual, Optional> modification, OperationStack operations) throws DataModificationException { if (schemaChild instanceof LeafSchemaNode) { return onLeafNode((LeafSchemaNode) schemaChild, actual, modification, operations); } else if (schemaChild instanceof ContainerSchemaNode) { return onContainerNode((ContainerSchemaNode) schemaChild, actual, modification, operations); } else if (schemaChild instanceof LeafListSchemaNode) { return onLeafSetNode((LeafListSchemaNode) schemaChild, actual, modification, operations); } else if (schemaChild instanceof AugmentationSchema) { return onAugmentationNode((AugmentationSchema) schemaChild, actual, modification, operations); } else if (schemaChild instanceof ListSchemaNode) { return onMapNode((ListSchemaNode) schemaChild, actual, modification, operations); } else if (schemaChild instanceof org.opendaylight.yangtools.yang.model.api.ChoiceNode) { return onChoiceNode((org.opendaylight.yangtools.yang.model.api.ChoiceNode) schemaChild, actual, modification, operations); } throw new IllegalArgumentException("Unknown schema node type " + schemaChild); } private static Optional> onChoiceNode( org.opendaylight.yangtools.yang.model.api.ChoiceNode schemaChild, Optional> actual, Optional> modification, OperationStack operations) throws DataModificationException { checkType(actual, ChoiceNode.class); checkType(modification, ChoiceNode.class); return CHOICE_NODE_MODIFICATION.modify(schemaChild, (Optional) actual, (Optional) modification, operations); } private static Optional> onMapNode(ListSchemaNode schemaChild, Optional> actual, Optional> modification, OperationStack operations) throws DataModificationException { checkType(actual, MapNode.class); checkType(modification, MapNode.class); return MAP_NODE_MODIFICATION.modify(schemaChild, (Optional) actual, (Optional) modification, operations); } private static Optional> onAugmentationNode(AugmentationSchema schemaChild, Optional> actual, Optional> modification, OperationStack operations) throws DataModificationException { checkType(actual, AugmentationNode.class); checkType(modification, AugmentationNode.class); return AUGMENTATION_NODE_MODIFICATION.modify(schemaChild, (Optional) actual, (Optional) modification, operations); } private static Optional> onLeafSetNode(LeafListSchemaNode schemaChild, Optional> actual, Optional> modification, OperationStack operations) throws DataModificationException { checkType(actual, LeafSetNode.class); checkType(modification, LeafSetNode.class); return LEAF_SET_NODE_MODIFICATION.modify(schemaChild, (Optional>) actual, (Optional>) modification, operations); } private static Optional> onContainerNode(ContainerSchemaNode schemaChild, Optional> actual, Optional> modification, OperationStack operations) throws DataModificationException { checkType(actual, ContainerNode.class); checkType(modification, ContainerNode.class); return CONTAINER_NODE_MODIFICATION.modify(schemaChild, (Optional) actual, (Optional) modification, operations); } private static Optional> onLeafNode(LeafSchemaNode schemaChild, Optional> actual, Optional> modification, OperationStack operations) throws DataModificationException { checkType(actual, LeafNode.class); checkType(modification, LeafNode.class); return LEAF_NODE_MODIFICATION.modify(schemaChild, (Optional>) actual, (Optional>) modification, operations); } private static void checkType(Optional> actual, Class leafNodeClass) { if (actual.isPresent()) { Preconditions.checkArgument(leafNodeClass.isAssignableFrom(actual.get().getClass()), "Unexpected node type, should be: %s, but was: %s, for: %s", leafNodeClass, actual.getClass(), actual); } } } }