org.opendaylight.mdsal.binding.spec.util.DataObjectReadingUtil Maven / Gradle / Ivy
/*
* 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.mdsal.binding.spec.util;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
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 java.util.Optional;
import org.opendaylight.mdsal.binding.spec.naming.BindingMapping;
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;
@Deprecated(since = "11.0.3", forRemoval = true)
public final class DataObjectReadingUtil {
private static final DataObjectReadingStrategy REAUSABLE_AUGMENTATION_READING_STRATEGY =
new AugmentationReadingStrategy();
private DataObjectReadingUtil() {
// Hidden on purpose
}
/**
* Read data from parent at specified path.
*
* @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 read.
* @return Value of object.
*/
public static 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 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 extends DataContainer> parentClass = source.implementedInterface();
@SuppressWarnings("unchecked")
T potential = (T) resolveReadStrategy(parentClass, child).read(source, child);
return Optional.ofNullable(potential);
}
@SuppressWarnings("rawtypes")
private static 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 extends DataContainer> parentClass = parent.implementedInterface();
return resolveReadStrategy(parentClass, child.getType()).readUsingPathArgument(parent, child, parentPath);
}
private static DataObjectReadingStrategy resolveReadStrategy(final Class extends DataContainer> parentClass,
final Class extends DataContainer> type) {
// FIXME: Add caching of strategies
return createReadStrategy(parentClass, type);
}
private static DataObjectReadingStrategy createReadStrategy(final Class extends DataContainer> parent,
final Class extends DataContainer> 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 extends Identifiable>> identifiableClass = (Class extends Identifiable>>) child;
return new ListItemReadingStrategy(parent, identifiableClass);
}
return new ContainerReadingStrategy(parent, child);
}
@SuppressWarnings("rawtypes")
private abstract static class DataObjectReadingStrategy {
private final Class extends DataContainer> parentType;
private final Class extends DataContainer> childType;
private final Method getterMethod;
@SuppressWarnings("unchecked")
DataObjectReadingStrategy(final Class parentType, final Class childType) {
checkArgument(DataContainer.class.isAssignableFrom(parentType));
checkArgument(DataContainer.class.isAssignableFrom(childType));
this.parentType = parentType;
this.childType = childType;
getterMethod = resolveGetterMethod(parentType, childType);
}
@SuppressWarnings("unchecked")
DataObjectReadingStrategy(final Class parentType, final Class childType, final Method getter) {
this.parentType = parentType;
this.childType = childType;
getterMethod = getter;
}
@SuppressWarnings("unused")
protected Class extends DataContainer> getParentType() {
return parentType;
}
protected Class extends DataContainer> 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> child);
private static Method resolveGetterMethod(final Class extends DataContainer> parent, final Class> child) {
String methodName = BindingMapping.GETTER_PREFIX + 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 final class ContainerReadingStrategy extends DataObjectReadingStrategy {
ContainerReadingStrategy(final Class extends DataContainer> parent,
final Class extends DataContainer> 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> child) {
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 final class ListItemReadingStrategy extends DataObjectReadingStrategy {
ListItemReadingStrategy(final Class extends DataContainer> parent, final Class child) {
super(parent, child);
checkArgument(Iterable.class.isAssignableFrom(getGetterMethod().getReturnType()));
}
@Override
public DataContainer read(final DataContainer parent, final Class> child) {
// 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;
return childArgument instanceof IdentifiableItem
? readUsingIdentifiableItem(dataList, (IdentifiableItem) childArgument, builder)
: 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.key());
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.key()) && item instanceof DataContainer) {
checkState(childArgument.getType().isInstance(item),
"Found child is not instance of requested type");
InstanceIdentifier childPath = parentPath
.child(childArgument.getType(), item.key());
return Collections.singletonMap(childPath, (DataContainer) item);
}
}
return Collections.emptyMap();
}
}
private static final class AugmentationReadingStrategy extends DataObjectReadingStrategy {
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) {
return Collections.emptyMap();
}
@SuppressWarnings("unchecked")
final InstanceIdentifier childPath = builder.child(childArgument.getType());
return Collections.singletonMap(childPath, aug);
}
@Override
@SuppressWarnings({ "rawtypes", "unchecked" })
public DataContainer read(final DataContainer parent, final Class> child) {
checkArgument(Augmentation.class.isAssignableFrom(child), "Child must be Augmentation.");
checkArgument(parent instanceof Augmentable>, "Parent must be Augmentable.");
return ((Augmentable) parent).augmentation(child);
}
}
/**
* Create sublist view of child from element on [size-of-parent] position to last element.
*
* @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 parentIt = parent.iterator();
List result = new ArrayList<>();
for (C arg : child) {
if (parentIt.hasNext()) {
parentIt.next();
} else {
result.add(arg);
}
}
if (parentIt.hasNext()) {
throw new IllegalArgumentException("Parent argument is bigger than child.");
}
return result;
}
}