/* * Copyright (c) 2014 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.binding.util; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; import com.google.common.base.Optional; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap.Builder; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.opendaylight.yangtools.yang.binding.Augmentable; import org.opendaylight.yangtools.yang.binding.Augmentation; import org.opendaylight.yangtools.yang.binding.DataContainer; import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.Identifiable; import org.opendaylight.yangtools.yang.binding.Identifier; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.IdentifiableItem; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.Item; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument; public class DataObjectReadingUtil { private static final DataObjectReadingStrategy REAUSABLE_AUGMENTATION_READING_STRATEGY = new AugmentationReadingStrategy(); private DataObjectReadingUtil() { throw new UnsupportedOperationException("Utility class. Instantion is not allowed."); } /** * * @param parent * Parent object on which read operation will be performed * @param parentPath * Path, to parent object. * @param childPath * Path, which is nested to parent, and should be readed. * @return Value of object. */ public static final Map, T> readData(final P parent, final InstanceIdentifier

parentPath, final InstanceIdentifier childPath) { checkArgument(parent != null, "Parent must not be null."); checkArgument(parentPath != null, "Parent path must not be null"); checkArgument(childPath != null, "Child path must not be null"); checkArgument(parentPath.containsWildcarded(childPath), "Parent object must be parent of child."); List pathArgs = subList(parentPath.getPathArguments(), childPath.getPathArguments()); @SuppressWarnings("rawtypes") Map lastFound = Collections . singletonMap(parentPath, parent); for (PathArgument pathArgument : pathArgs) { @SuppressWarnings("rawtypes") final ImmutableMap.Builder potentialBuilder = ImmutableMap.builder(); for (@SuppressWarnings("rawtypes") Entry entry : lastFound.entrySet()) { potentialBuilder.putAll(readData(entry, pathArgument)); } lastFound = potentialBuilder.build(); if (lastFound.isEmpty()) { return Collections.emptyMap(); } } @SuppressWarnings({ "unchecked", "rawtypes" }) final Map, T> result = (Map) lastFound; return result; } @SuppressWarnings("rawtypes") private static Map readData(final Entry entry, final PathArgument pathArgument) { return readData(entry.getValue(), entry.getKey(), pathArgument); } public static final Optional readData(final DataObject source, final Class child) { checkArgument(source != null, "Object should not be null."); checkArgument(child != null, "Child type should not be null"); Class parentClass = source.getImplementedInterface(); @SuppressWarnings("unchecked") T potential = (T) resolveReadStrategy(parentClass, child).read(source, child); return Optional.fromNullable(potential); } @SuppressWarnings("rawtypes") private static final Map readData(final DataContainer parent, final InstanceIdentifier parentPath, final PathArgument child) { checkArgument(parent != null, "Object should not be null."); checkArgument(child != null, "Child argument should not be null"); Class parentClass = parent.getImplementedInterface(); return resolveReadStrategy(parentClass, child.getType()).readUsingPathArgument(parent, child, parentPath); } private static DataObjectReadingStrategy resolveReadStrategy(final Class parentClass, final Class type) { // FIXME: Add caching of strategies return createReadStrategy(parentClass, type); } private static DataObjectReadingStrategy createReadStrategy(final Class parent, final Class child) { if (Augmentable.class.isAssignableFrom(parent) && Augmentation.class.isAssignableFrom(child)) { return REAUSABLE_AUGMENTATION_READING_STRATEGY; } /* * FIXME Ensure that this strategies also works for children of cases. * Possible edge-case is : Parent container uses grouping foo case is * added by augmentation also uses foo. */ if (Identifiable.class.isAssignableFrom(child)) { @SuppressWarnings("unchecked") final Class> identifiableClass = (Class>) child; return new ListItemReadingStrategy(parent, identifiableClass); } return new ContainerReadingStrategy(parent, child); } @SuppressWarnings("rawtypes") private abstract static class DataObjectReadingStrategy { private final Class parentType; private final Class childType; private final Method getterMethod; @SuppressWarnings("unchecked") public DataObjectReadingStrategy(final Class parentType, final Class childType) { super(); checkArgument(DataContainer.class.isAssignableFrom(parentType)); checkArgument(DataContainer.class.isAssignableFrom(childType)); this.parentType = parentType; this.childType = childType; this.getterMethod = resolveGetterMethod(parentType, childType); } @SuppressWarnings("unchecked") public DataObjectReadingStrategy(final Class parentType, final Class childType, final Method getter) { this.parentType = parentType; this.childType = childType; this.getterMethod = getter; } @SuppressWarnings("unused") protected Class getParentType() { return parentType; } protected Class getChildType() { return childType; } protected Method getGetterMethod() { return getterMethod; } public abstract Map readUsingPathArgument(DataContainer parent, PathArgument childArgument, InstanceIdentifier targetBuilder); public abstract DataContainer read(DataContainer parent, Class childType); private static Method resolveGetterMethod(final Class parent, final Class child) { String methodName = "get" + child.getSimpleName(); try { return parent.getMethod(methodName); } catch (NoSuchMethodException e) { throw new IllegalArgumentException(e); } catch (SecurityException e) { throw new IllegalStateException(e); } } } @SuppressWarnings("rawtypes") private static class ContainerReadingStrategy extends DataObjectReadingStrategy { public ContainerReadingStrategy(final Class parent, final Class child) { super(parent, child); checkArgument(child.isAssignableFrom(getGetterMethod().getReturnType())); } @Override public Map readUsingPathArgument(final DataContainer parent, final PathArgument childArgument, final InstanceIdentifier parentPath) { final DataContainer result = read(parent, childArgument.getType()); if (result != null) { @SuppressWarnings("unchecked") InstanceIdentifier childPath = parentPath.child(childArgument.getType()); return Collections.singletonMap(childPath, result); } return Collections.emptyMap(); } @Override public DataContainer read(final DataContainer parent, final Class childType) { try { Object potentialData = getGetterMethod().invoke(parent); checkState(potentialData instanceof DataContainer); return (DataContainer) potentialData; } catch (IllegalAccessException | InvocationTargetException e) { throw new IllegalArgumentException(e); } } } @SuppressWarnings("rawtypes") private static class ListItemReadingStrategy extends DataObjectReadingStrategy { public ListItemReadingStrategy(final Class parent, final Class child) { super(parent, child); checkArgument(Iterable.class.isAssignableFrom(getGetterMethod().getReturnType())); } @Override public DataContainer read(final DataContainer parent, final Class childType) { // This will always fail since we do not have key. return null; } @SuppressWarnings("unchecked") @Override public Map readUsingPathArgument(final DataContainer parent, final PathArgument childArgument, final InstanceIdentifier builder) { try { Object potentialList = getGetterMethod().invoke(parent); if (potentialList instanceof Iterable) { final Iterable dataList = (Iterable) potentialList; if (childArgument instanceof IdentifiableItem) { return readUsingIdentifiableItem(dataList, (IdentifiableItem) childArgument, builder); } else { return readAll(dataList, builder); } } } catch (InvocationTargetException | IllegalArgumentException | IllegalAccessException e) { throw new IllegalStateException(e); } return Collections.emptyMap(); } private Map readAll(final Iterable dataList, final InstanceIdentifier parentPath) { Builder result = ImmutableMap . builder(); for (Identifiable item : dataList) { @SuppressWarnings("unchecked") InstanceIdentifier childPath = parentPath.child(getChildType(), item.getKey()); result.put(childPath, (DataContainer) item); } return result.build(); } @SuppressWarnings("unchecked") private static Map readUsingIdentifiableItem(final Iterable dataList, final IdentifiableItem childArgument, final InstanceIdentifier parentPath) { final Identifier key = childArgument.getKey(); for (Identifiable item : dataList) { if (key.equals(item.getKey()) && item instanceof DataContainer) { checkState(childArgument.getType().isInstance(item), "Found child is not instance of requested type"); InstanceIdentifier childPath = parentPath .child(childArgument.getType(), item.getKey()); return Collections.singletonMap(childPath, (DataContainer) item); } } return Collections.emptyMap(); } } private static final class AugmentationReadingStrategy extends DataObjectReadingStrategy { public AugmentationReadingStrategy() { super(Augmentable.class, Augmentation.class, null); } @SuppressWarnings("rawtypes") @Override public Map readUsingPathArgument(final DataContainer parent, final PathArgument childArgument, final InstanceIdentifier builder) { checkArgument(childArgument instanceof Item, "Path Argument must be Item without keys"); DataContainer aug = read(parent, childArgument.getType()); if (aug != null) { @SuppressWarnings("unchecked") final InstanceIdentifier childPath = builder.child(childArgument.getType()); return Collections.singletonMap(childPath, aug); } else { return Collections.emptyMap(); } } @Override public DataContainer read(final DataContainer parent, final Class childType) { checkArgument(Augmentation.class.isAssignableFrom(childType), "Parent must be Augmentable."); checkArgument(parent instanceof Augmentable, "Parent must be Augmentable."); @SuppressWarnings({ "rawtypes", "unchecked" }) Augmentation potential = ((Augmentable) parent).getAugmentation(childType); checkState(potential instanceof DataContainer, "Readed augmention must be data object"); return (DataContainer) potential; } } /** * Create sublist view of child from element on [size-of-parent] position to * last element. * * @param parent * @param child * @return sublist view of child argument * @throws IllegalArgumentException * if parent argument is bigger than child */ private static List subList(final Iterable

parent, final Iterable child) { Iterator

iParent = parent.iterator(); List result = new ArrayList<>(); for (C arg : child) { if (iParent.hasNext()) { iParent.next(); } else { result.add(arg); } } if (iParent.hasNext()) { throw new IllegalArgumentException("Parent argument is bigger than child."); } return result; } }