X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=binding%2Fmdsal-binding-runtime-api%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fmdsal%2Fbinding%2Fruntime%2Fapi%2FAbstractBindingRuntimeContext.java;h=3fed0a6a7677d9d5fd5840252153555fbbb1676a;hb=4c2b08900d7a4a06bf99698801f339c8bf22e07c;hp=603624cd8f69e3358b66dec2c662bcacf6569bcf;hpb=baa84422d58ab5d2573a76fac6674bad1dbdd26c;p=mdsal.git diff --git a/binding/mdsal-binding-runtime-api/src/main/java/org/opendaylight/mdsal/binding/runtime/api/AbstractBindingRuntimeContext.java b/binding/mdsal-binding-runtime-api/src/main/java/org/opendaylight/mdsal/binding/runtime/api/AbstractBindingRuntimeContext.java index 603624cd8f..3fed0a6a76 100644 --- a/binding/mdsal-binding-runtime-api/src/main/java/org/opendaylight/mdsal/binding/runtime/api/AbstractBindingRuntimeContext.java +++ b/binding/mdsal-binding-runtime-api/src/main/java/org/opendaylight/mdsal/binding/runtime/api/AbstractBindingRuntimeContext.java @@ -8,321 +8,112 @@ package org.opendaylight.mdsal.binding.runtime.api; 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.Throwables; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Iterables; -import java.util.AbstractMap.SimpleEntry; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Optional; -import java.util.Set; +import java.util.concurrent.ExecutionException; import org.eclipse.jdt.annotation.NonNull; -import org.opendaylight.mdsal.binding.model.api.DefaultType; -import org.opendaylight.mdsal.binding.model.api.GeneratedType; -import org.opendaylight.mdsal.binding.model.api.MethodSignature; -import org.opendaylight.mdsal.binding.model.api.ParameterizedType; -import org.opendaylight.mdsal.binding.model.api.Type; -import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedTypeBuilder; +import org.opendaylight.mdsal.binding.model.api.JavaTypeName; import org.opendaylight.yangtools.yang.binding.Action; import org.opendaylight.yangtools.yang.binding.Augmentation; +import org.opendaylight.yangtools.yang.binding.BaseIdentity; +import org.opendaylight.yangtools.yang.binding.Notification; +import org.opendaylight.yangtools.yang.binding.RpcInput; +import org.opendaylight.yangtools.yang.binding.RpcOutput; import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier; -import org.opendaylight.yangtools.yang.model.api.ActionDefinition; -import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode; -import org.opendaylight.yangtools.yang.model.api.AugmentationTarget; -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.DocumentedNode.WithStatus; -import org.opendaylight.yangtools.yang.model.api.SchemaNode; import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute; -import org.opendaylight.yangtools.yang.model.util.EffectiveAugmentationSchema; -import org.opendaylight.yangtools.yang.model.util.SchemaNodeUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * Runtime Context for Java YANG Binding classes. It provides information derived from the backing effective model, * which is not captured in generated classes (and hence cannot be obtained from {@code BindingReflections}. - * - *

Some of this information are for example list of all available children for cases - * {@link #getChoiceCaseChildren(DataNodeContainer)}, since choices are augmentable and new choices may be introduced - * by additional models. Same goes for all possible augmentations. */ @Beta public abstract class AbstractBindingRuntimeContext implements BindingRuntimeContext { - private static final Logger LOG = LoggerFactory.getLogger(AbstractBindingRuntimeContext.class); - - private final LoadingCache> identityClasses = CacheBuilder.newBuilder().weakValues().build( - new CacheLoader>() { + private final LoadingCache<@NonNull QName, @NonNull Class> identityClasses = + CacheBuilder.newBuilder().weakValues().build(new CacheLoader<>() { @Override - public Class load(final QName key) { - final Optional identityType = getTypes().findIdentity(key); - checkArgument(identityType.isPresent(), "Supplied QName %s is not a valid identity", key); + public Class load(final QName key) { + final var type = getTypes().findIdentity(key).orElseThrow( + () -> new IllegalArgumentException("Supplied QName " + key + " is not a valid identity")); try { - return loadClass(identityType.get()); - } catch (final ClassNotFoundException e) { - throw new IllegalArgumentException("Required class " + identityType + "was not found.", e); + return loadClass(type.getIdentifier()).asSubclass(BaseIdentity.class); + } catch (ClassNotFoundException e) { + throw new IllegalArgumentException("Required class " + type + " was not found.", e); + } catch (ClassCastException e) { + throw new IllegalArgumentException(key + " resolves to a non-identity class", e); } } }); @Override - public final > AugmentationSchemaNode getAugmentationDefinition(final Class augClass) { - return getTypes().findAugmentation(DefaultType.of(augClass)).orElse(null); + public final > AugmentRuntimeType getAugmentationDefinition(final Class augClass) { + return getTypes().findSchema(JavaTypeName.create(augClass)) + .filter(AugmentRuntimeType.class::isInstance) + .map(AugmentRuntimeType.class::cast) + .orElse(null); } @Override - public final DataSchemaNode getSchemaDefinition(final Class cls) { + public final CompositeRuntimeType getSchemaDefinition(final Class cls) { checkArgument(!Augmentation.class.isAssignableFrom(cls), "Supplied class must not be an augmentation (%s is)", cls); checkArgument(!Action.class.isAssignableFrom(cls), "Supplied class must not be an action (%s is)", cls); - return (DataSchemaNode) getTypes().findSchema(DefaultType.of(cls)).orElse(null); + checkArgument(!Notification.class.isAssignableFrom(cls), "Supplied class must not be a notification (%s is)", + cls); + return (CompositeRuntimeType) getTypes().findSchema(JavaTypeName.create(cls)).orElse(null); } @Override - public final ActionDefinition getActionDefinition(final Class> cls) { - return (ActionDefinition) getTypes().findSchema(DefaultType.of(cls)).orElse(null); + public final ActionRuntimeType getActionDefinition(final Class> cls) { + return (ActionRuntimeType) getTypes().findSchema(JavaTypeName.create(cls)).orElse(null); } @Override - public final Absolute getActionIdentifier(final Class> cls) { - return getTypes().findSchemaNodeIdentifier(DefaultType.of(cls)).orElse(null); + public final RuntimeType getTypeWithSchema(final Class type) { + return getTypes().findSchema(JavaTypeName.create(type)) + .orElseThrow(() -> new IllegalArgumentException("Failed to find schema for " + type)); } @Override - public final Entry getResolvedAugmentationSchema( - final DataNodeContainer target, final Class> aug) { - final AugmentationSchemaNode origSchema = getAugmentationDefinition(aug); - checkArgument(origSchema != null, "Augmentation %s is not known in current schema context", aug); - /* - * FIXME: Validate augmentation schema lookup - * - * Currently this algorithm, does not verify if instantiated child nodes - * are real one derived from augmentation schema. The problem with - * full validation is, if user used copy builders, he may use - * augmentation which was generated for different place. - * - * If this augmentations have same definition, we emit same identifier - * with data and it is up to underlying user to validate data. - * - */ - final Set childNames = new HashSet<>(); - final Set realChilds = new HashSet<>(); - for (final DataSchemaNode child : origSchema.getChildNodes()) { - final DataSchemaNode dataChildQNname = target.getDataChildByName(child.getQName()); - final String childLocalName = child.getQName().getLocalName(); - if (dataChildQNname == null) { - for (DataSchemaNode dataSchemaNode : target.getChildNodes()) { - if (childLocalName.equals(dataSchemaNode.getQName().getLocalName())) { - realChilds.add(dataSchemaNode); - childNames.add(dataSchemaNode.getQName()); - } - } - } else { - realChilds.add(dataChildQNname); - childNames.add(child.getQName()); - } - } - - final AugmentationIdentifier identifier = AugmentationIdentifier.create(childNames); - final AugmentationSchemaNode proxy = new EffectiveAugmentationSchema(origSchema, realChilds); - return new SimpleEntry<>(identifier, proxy); + public final Class getClassForSchema(final Absolute schema) { + final var child = getTypes().schemaTreeChild(schema); + checkArgument(child != null, "Failed to find binding type for %s", schema); + return loadClass(child); } @Override - public final Optional getCaseSchemaDefinition(final ChoiceSchemaNode schema, - final Class childClass) { - final DataSchemaNode origSchema = getSchemaDefinition(childClass); - checkArgument(origSchema instanceof CaseSchemaNode, "Supplied schema %s is not case.", origSchema); - - /* FIXME: Make sure that if there are multiple augmentations of same - * named case, with same structure we treat it as equals - * this is due property of Binding specification and copy builders - * that user may be unaware that he is using incorrect case - * which was generated for choice inside grouping. - */ - return findInstantiatedCase(schema, (CaseSchemaNode) origSchema); - } - - @Override - public final Entry getTypeWithSchema(final Class type) { - return getTypeWithSchema(getTypes(), DefaultType.of(type)); - } - - private static @NonNull Entry getTypeWithSchema(final BindingRuntimeTypes types, - final Type referencedType) { - final WithStatus schema = types.findSchema(referencedType).orElseThrow( - () -> new NullPointerException("Failed to find schema for type " + referencedType)); - final Type definedType = types.findType(schema).orElseThrow( - () -> new NullPointerException("Failed to find defined type for " + referencedType + " schema " + schema)); - - if (definedType instanceof GeneratedTypeBuilder) { - return new SimpleEntry<>(((GeneratedTypeBuilder) definedType).build(), schema); + public final Class getIdentityClass(final QName input) { + try { + return identityClasses.get(requireNonNull(input)); + } catch (ExecutionException e) { + Throwables.throwIfUnchecked(e.getCause()); + throw new IllegalStateException("Unexpected error looking up " + input, e); } - checkArgument(definedType instanceof GeneratedType, "Type %s is not a GeneratedType", referencedType); - return new SimpleEntry<>((GeneratedType) definedType, schema); } @Override - public final Map> getChoiceCaseChildren(final DataNodeContainer schema) { - return getChoiceCaseChildren(getTypes(), schema); - } - - private static @NonNull ImmutableMap> getChoiceCaseChildren(final BindingRuntimeTypes types, - final DataNodeContainer schema) { - final Map> childToCase = new HashMap<>(); - - for (final ChoiceSchemaNode choice : Iterables.filter(schema.getChildNodes(), ChoiceSchemaNode.class)) { - final ChoiceSchemaNode originalChoice = getOriginalSchema(choice); - final Optional optType = types.findType(originalChoice); - checkState(optType.isPresent(), "Failed to find generated type for choice %s", originalChoice); - final Type choiceType = optType.get(); - - for (Type caze : types.findCases(choiceType)) { - final Entry caseIdentifier = new SimpleEntry<>(choiceType, caze); - final HashSet caseChildren = new HashSet<>(); - if (caze instanceof GeneratedTypeBuilder) { - caze = ((GeneratedTypeBuilder) caze).build(); - } - collectAllContainerTypes((GeneratedType) caze, caseChildren); - for (final Type caseChild : caseChildren) { - childToCase.put(caseChild, caseIdentifier); - } - } - } - return ImmutableMap.copyOf(childToCase); + public final Class getRpcInput(final QName rpcName) { + return loadClass(getTypes().findRpcInput(rpcName) + .orElseThrow(() -> new IllegalArgumentException("Failed to find RpcInput for " + rpcName))) + .asSubclass(RpcInput.class); } @Override - public final Set> getCases(final Class choice) { - final Collection cazes = getTypes().findCases(DefaultType.of(choice)); - final Set> ret = new HashSet<>(cazes.size()); - for (final Type caze : cazes) { - try { - ret.add(loadClass(caze)); - } catch (final ClassNotFoundException e) { - LOG.warn("Failed to load class for case {}, ignoring it", caze, e); - } - } - return ret; + public final Class getRpcOutput(final QName rpcName) { + return loadClass(getTypes().findRpcOutput(rpcName) + .orElseThrow(() -> new IllegalArgumentException("Failed to find RpcOutput for " + rpcName))) + .asSubclass(RpcOutput.class); } - @Override - public final Class getClassForSchema(final SchemaNode childSchema) { - final SchemaNode origSchema = getOriginalSchema(childSchema); - final Optional clazzType = getTypes().findType(origSchema); - checkArgument(clazzType.isPresent(), "Failed to find binding type for %s (original %s)", - childSchema, origSchema); - + private Class loadClass(final RuntimeType type) { try { - return loadClass(clazzType.get()); + return loadClass(type.javaType()); } catch (final ClassNotFoundException e) { throw new IllegalStateException(e); } } - - @Override - public final ImmutableMap getAvailableAugmentationTypes( - final DataNodeContainer container) { - if (container instanceof AugmentationTarget) { - final Map identifierToType = new HashMap<>(); - final BindingRuntimeTypes types = getTypes(); - for (final AugmentationSchemaNode augment : ((AugmentationTarget) container).getAvailableAugmentations()) { - // Augmentation must have child nodes if is to be used with Binding classes - AugmentationSchemaNode augOrig = augment; - while (augOrig.getOriginalDefinition().isPresent()) { - augOrig = augOrig.getOriginalDefinition().get(); - } - - if (!augment.getChildNodes().isEmpty()) { - final Optional augType = types.findType(augOrig); - if (augType.isPresent()) { - identifierToType.put(getAugmentationIdentifier(augment), augType.get()); - } - } - } - return ImmutableMap.copyOf(identifierToType); - } - - return ImmutableMap.of(); - } - - @Override - public final Class getIdentityClass(final QName input) { - return identityClasses.getUnchecked(input); - } - - private static AugmentationIdentifier getAugmentationIdentifier(final AugmentationSchemaNode augment) { - // FIXME: use DataSchemaContextNode.augmentationIdentifierFrom() once it does caching - return AugmentationIdentifier.create(augment.getChildNodes().stream().map(DataSchemaNode::getQName) - .collect(ImmutableSet.toImmutableSet())); - } - - private static Set collectAllContainerTypes(final GeneratedType type, final Set collection) { - for (final MethodSignature definition : type.getMethodDefinitions()) { - Type childType = definition.getReturnType(); - if (childType instanceof ParameterizedType) { - childType = ((ParameterizedType) childType).getActualTypeArguments()[0]; - } - if (childType instanceof GeneratedType || childType instanceof GeneratedTypeBuilder) { - collection.add(childType); - } - } - for (final Type parent : type.getImplements()) { - if (parent instanceof GeneratedType) { - collectAllContainerTypes((GeneratedType) parent, collection); - } - } - return collection; - } - - private static T getOriginalSchema(final T choice) { - @SuppressWarnings("unchecked") - final T original = (T) SchemaNodeUtils.getRootOriginalIfPossible(choice); - if (original != null) { - return original; - } - return choice; - } - - private static @NonNull Optional findInstantiatedCase(final ChoiceSchemaNode instantiatedChoice, - final CaseSchemaNode originalDefinition) { - CaseSchemaNode potential = instantiatedChoice.findCase(originalDefinition.getQName()).orElse(null); - if (originalDefinition.equals(potential)) { - return Optional.of(potential); - } - if (potential != null) { - SchemaNode potentialRoot = SchemaNodeUtils.getRootOriginalIfPossible(potential); - if (originalDefinition.equals(potentialRoot)) { - return Optional.of(potential); - } - } - - // We try to find case by name, then lookup its root definition - // and compare it with original definition - // This solves case, if choice was inside grouping - // which was used in different module and thus namespaces are - // different, but local names are still same. - // - // Still we need to check equality of definition, because local name is not - // sufficient to uniquelly determine equality of cases - // - for (CaseSchemaNode caze : instantiatedChoice.findCaseNodes(originalDefinition.getQName().getLocalName())) { - if (originalDefinition.equals(SchemaNodeUtils.getRootOriginalIfPossible(caze))) { - return Optional.of(caze); - } - } - return Optional.empty(); - } }