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=bf0dc18a1de77c4392c859b2b8e247dbd45fdf64;hb=fdcf549686105e2baf76a06e94a2da5ba0f73544;hp=3b0d074c373d0fb268d0275f4169bf5c91a739a9;hpb=a0a90e8fef2771ede2f19c547726d6234f5eeeef;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 3b0d074c37..bf0dc18a1d 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,356 +8,131 @@ 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.eclipse.jdt.annotation.Nullable; -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.Rpc; +import org.opendaylight.yangtools.yang.binding.RpcInput; +import org.opendaylight.yangtools.yang.binding.RpcOutput; +import org.opendaylight.yangtools.yang.binding.YangData; import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.common.QNameModule; -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.DerivableSchemaNode; -import org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus; -import org.opendaylight.yangtools.yang.model.api.SchemaNode; -import org.opendaylight.yangtools.yang.model.util.EffectiveAugmentationSchema; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.opendaylight.yangtools.yang.common.YangDataName; +import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute; /** * 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(Type.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(Type.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 DataSchemaNode findChildSchemaDefinition(final DataNodeContainer parentSchema, - final QNameModule parentNamespace, final Class childClass) { - final DataSchemaNode origDef = getSchemaDefinition(childClass); - if (origDef == null) { - // Weird, the child does not have an associated definition - return null; - } - - // Direct instantiation or use in same module in which grouping was defined. - final QName origName = origDef.getQName(); - final DataSchemaNode sameName = parentSchema.dataChildByName(origName); - if (sameName != null) { - // Check if it is: - // - exactly same schema node, or - // - instantiated node was added via uses statement and is instantiation of same grouping - if (origDef.equals(sameName) || origDef.equals(getRootOriginalIfPossible(sameName))) { - return sameName; - } - - // Node has same name, but clearly is different - return null; - } - - // We are looking for instantiation via uses in other module - final DataSchemaNode potential = parentSchema.dataChildByName(origName.bindTo(parentNamespace)); - // We check if it is really instantiated from same definition as class was derived - if (potential != null && origDef.equals(getRootOriginalIfPossible(potential))) { - return potential; - } - return null; - } - - private static @Nullable SchemaNode getRootOriginalIfPossible(final SchemaNode data) { - SchemaNode previous = null; - SchemaNode next = originalNodeOf(data); - while (next != null) { - previous = next; - next = originalNodeOf(next); - } - return previous; + public final ActionRuntimeType getActionDefinition(final Class> cls) { + return (ActionRuntimeType) getTypes().findSchema(JavaTypeName.create(cls)).orElse(null); } @Override - public final ActionDefinition getActionDefinition(final Class> cls) { - return (ActionDefinition) getTypes().findSchema(Type.of(cls)).orElse(null); + public final RpcRuntimeType getRpcDefinition(final Class> cls) { + return (RpcRuntimeType) getTypes().findSchema(JavaTypeName.create(cls)).orElse(null); } @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.dataChildByName(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 RuntimeType getTypeWithSchema(final Class type) { + return getTypes().findSchema(JavaTypeName.create(type)) + .orElseThrow(() -> new IllegalArgumentException("Failed to find schema for " + type)); } @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); + 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 Entry getTypeWithSchema(final Class type) { - return getTypeWithSchema(getTypes(), Type.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); + public final Class getRpcInput(final QName rpcName) { + return loadClass(getRpc(rpcName).input()).asSubclass(RpcInput.class); } - 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); + @Override + public final Class getRpcOutput(final QName rpcName) { + return loadClass(getRpc(rpcName).output()).asSubclass(RpcOutput.class); } - @Override - public final Set> getCases(final Class choice) { - final Collection cazes = getTypes().findCases(Type.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); - } + private @NonNull RpcRuntimeType getRpc(final QName rpcName) { + if (getTypes().schemaTreeChild(rpcName) instanceof RpcRuntimeType rpc) { + return rpc; } - return ret; + throw new IllegalArgumentException("Failed to find RPC for " + rpcName); } @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); + @SuppressWarnings("unchecked") + public final Class> getYangDataClass(final YangDataName templateName) { + return (Class) loadClass(getTypes().findYangData(templateName) + .orElseThrow(() -> new IllegalArgumentException("Failed to find YangData for " + templateName))) + .asSubclass(YangData.class); + } + 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 var augmentations = ((AugmentationTarget) container).getAvailableAugmentations(); - if (!augmentations.isEmpty()) { - final var identifierToType = new HashMap(); - final var types = getTypes(); - for (var augment : augmentations) { - types.findOriginalAugmentationType(augment).ifPresent(augType -> { - identifierToType.put(getAugmentationIdentifier(augment), augType); - }); - } - 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) originalNodeOf(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 = originalNodeOf(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(originalNodeOf(caze))) { - return Optional.of(caze); - } - } - return Optional.empty(); - } - - private static @Nullable SchemaNode originalNodeOf(final SchemaNode node) { - return node instanceof DerivableSchemaNode ? ((DerivableSchemaNode) node).getOriginal().orElse(null) : null; - } }