X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=code-generator%2Fbinding-generator-impl%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fyangtools%2Fsal%2Fbinding%2Fgenerator%2Fimpl%2FLazyGeneratedCodecRegistry.java;h=86f0cdf881a64b156cde44b0a45ccb6270da2563;hb=5feac31a11a337a0c840f73c5c4612a6c997fa2a;hp=c890252a3fb00808dec50e1b54cd6018bea1e603;hpb=b0acc8cf2b17ea7ba67a33acc4e868c4433c45b5;p=yangtools.git diff --git a/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/LazyGeneratedCodecRegistry.java b/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/LazyGeneratedCodecRegistry.java index c890252a3f..86f0cdf881 100644 --- a/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/LazyGeneratedCodecRegistry.java +++ b/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/LazyGeneratedCodecRegistry.java @@ -7,6 +7,18 @@ */ package org.opendaylight.yangtools.sal.binding.generator.impl; +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Iterables; +import com.google.common.collect.Multimap; +import com.google.common.collect.Multimaps; + import java.lang.ref.WeakReference; import java.lang.reflect.Field; import java.util.AbstractMap.SimpleEntry; @@ -68,23 +80,13 @@ import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; import org.opendaylight.yangtools.yang.model.api.Module; import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.opendaylight.yangtools.yang.model.api.SchemaContextListener; +import org.opendaylight.yangtools.yang.model.api.SchemaNode; import org.opendaylight.yangtools.yang.model.api.SchemaPath; +import org.opendaylight.yangtools.yang.model.util.SchemaNodeUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.base.Optional; -import com.google.common.base.Preconditions; -import com.google.common.collect.BiMap; -import com.google.common.collect.HashBiMap; -import com.google.common.collect.HashMultimap; -import com.google.common.collect.Iterables; -import com.google.common.collect.Multimap; -import com.google.common.collect.Multimaps; - -class LazyGeneratedCodecRegistry implements // - CodecRegistry, // - SchemaContextListener, // - GeneratorListener { +class LazyGeneratedCodecRegistry implements CodecRegistry, SchemaContextListener, GeneratorListener { private static final Logger LOG = LoggerFactory.getLogger(LazyGeneratedCodecRegistry.class); @@ -131,6 +133,9 @@ class LazyGeneratedCodecRegistry implements // private final AbstractTransformerGenerator generator; private final SchemaLock lock; + private static final LoadingCache, AugmentationFieldGetter> AUGMENTATION_GETTERS = + CacheBuilder.newBuilder().weakKeys().softValues().build(new AugmentationGetterLoader()); + // FIXME: how is this protected? private SchemaContext currentSchema; @@ -194,7 +199,12 @@ class LazyGeneratedCodecRegistry implements // @Override public Class getClassForPath(final List names) { - final DataSchemaNode node = getSchemaNode(names); + DataSchemaNode node = getSchemaNode(names); + Preconditions.checkArgument(node != null, "Path %s points to invalid schema location",names); + SchemaNode originalDefinition = SchemaNodeUtils.getRootOriginalIfPossible(node); + if(originalDefinition instanceof DataSchemaNode) { + node =(DataSchemaNode) originalDefinition; + } final SchemaPath path = node.getPath(); final Type t = pathToType.get(path); @@ -208,9 +218,14 @@ class LazyGeneratedCodecRegistry implements // @SuppressWarnings("rawtypes") final WeakReference weakRef = typeToClass.get(type); - Preconditions.checkState(weakRef != null, "Could not find loaded class for path: %s and type: %s", path, - type.getFullyQualifiedName()); - return weakRef.get(); + if(weakRef != null) { + return weakRef.get(); + } + try { + return classLoadingStrategy.loadClass(type); + } catch (ClassNotFoundException e) { + throw new IllegalStateException(String.format("Could not find loaded class for path: %s and type: %s", path,type.getFullyQualifiedName())); + } } @Override @@ -254,7 +269,7 @@ class LazyGeneratedCodecRegistry implements // WeakReference weakRef = new WeakReference<>(cls); typeToClass.put(typeRef, weakRef); if (Augmentation.class.isAssignableFrom(cls)) { - + // Intentionally NOOP } else if (DataObject.class.isAssignableFrom(cls)) { getCodecForDataObject((Class) cls); } @@ -296,8 +311,7 @@ class LazyGeneratedCodecRegistry implements // } private DataSchemaNode searchInChoices(final DataNodeContainer node, final QName arg) { - Set children = node.getChildNodes(); - for (DataSchemaNode child : children) { + for (DataSchemaNode child : node.getChildNodes()) { if (child instanceof ChoiceNode) { ChoiceNode choiceNode = (ChoiceNode) child; DataSchemaNode potential = searchInCases(choiceNode, arg); @@ -410,8 +424,8 @@ class LazyGeneratedCodecRegistry implements // for (Map.Entry entry : bimap.entrySet()) { Type key = entry.getKey(); AugmentationSchema value = entry.getValue(); - Set augmentedNodes = value.getChildNodes(); - if (augmentedNodes != null && !(augmentedNodes.isEmpty())) { + Collection augmentedNodes = value.getChildNodes(); + if (augmentedNodes != null && !augmentedNodes.isEmpty()) { typeToAugment.put(key, value); } } @@ -506,8 +520,52 @@ class LazyGeneratedCodecRegistry implements // return ret; } - private static abstract class IntermediateCodec implements // - DomCodec, Delegator, Object>> { + private static final class AugmentationGetterLoader extends CacheLoader, AugmentationFieldGetter> { + private static final AugmentationFieldGetter DUMMY = new AugmentationFieldGetter() { + @Override + Map>, Augmentation> getAugmentations(final Object input) { + return Collections.emptyMap(); + } + }; + + @Override + public AugmentationFieldGetter load(final Class key) throws Exception { + Field field; + try { + field = key.getDeclaredField("augmentation"); + } catch (NoSuchFieldException | SecurityException e) { + LOG.debug("Failed to acquire augmentation field", e); + return DUMMY; + } + field.setAccessible(true); + + return new ReflectionAugmentationFieldGetter(field); + } + } + + private static abstract class AugmentationFieldGetter { + abstract Map>, Augmentation> getAugmentations(final Object input); + } + + private static final class ReflectionAugmentationFieldGetter extends AugmentationFieldGetter { + private final Field augmentationField; + + ReflectionAugmentationFieldGetter(final Field augmentationField) { + this.augmentationField = Preconditions.checkNotNull(augmentationField); + } + + @Override + @SuppressWarnings("unchecked") + Map>, Augmentation> getAugmentations(final Object input) { + try { + return (Map>, Augmentation>) augmentationField.get(input); + } catch (IllegalArgumentException | IllegalAccessException e) { + throw new IllegalStateException("Failed to access augmentation field", e); + } + } + } + + private static abstract class IntermediateCodec implements DomCodec, Delegator, Object>> { private final BindingCodec, Object> delegate; @@ -528,9 +586,7 @@ class LazyGeneratedCodecRegistry implements // } - private static class IdentifierCodecImpl> // - extends IntermediateCodec // - implements IdentifierCodec { + private static class IdentifierCodecImpl> extends IntermediateCodec implements IdentifierCodec { public IdentifierCodecImpl(final BindingCodec, Object> delegate) { super(delegate); @@ -558,9 +614,7 @@ class LazyGeneratedCodecRegistry implements // } } - private static class DataContainerCodecImpl // - extends IntermediateCodec // - implements DataContainerCodec { + private static class DataContainerCodecImpl extends IntermediateCodec implements DataContainerCodec { public DataContainerCodecImpl(final BindingCodec, Object> delegate) { super(delegate); @@ -674,7 +728,7 @@ class LazyGeneratedCodecRegistry implements // /* In case of none is applicable, we return * null. Since there is no mixin which * is applicable in this location. - */ + */ if(applicable.isEmpty()) { return null; } @@ -759,7 +813,7 @@ class LazyGeneratedCodecRegistry implements // @SuppressWarnings("rawtypes") private static class ChoiceCaseCodecImpl implements ChoiceCaseCodec, // - Delegator, LocationAwareBindingCodec, ValueWithQName> { + Delegator, LocationAwareBindingCodec, ValueWithQName> { private final BindingCodec delegate; private final ChoiceCaseNode schema; private final Map, ChoiceCaseNode> instantiatedLocations; @@ -886,8 +940,7 @@ class LazyGeneratedCodecRegistry implements // } } - private static class PublicChoiceCodecImpl implements ChoiceCodec, - Delegator, Object>> { + private static class PublicChoiceCodecImpl implements ChoiceCodec, Delegator, Object>> { private final BindingCodec, Object> delegate; @@ -963,7 +1016,7 @@ class LazyGeneratedCodecRegistry implements // try { @SuppressWarnings("unchecked") Class clazz = (Class) classLoadingStrategy - .loadClass(potential); + .loadClass(potential); ChoiceCaseCodecImpl codec = tryToLoadImplementation(clazz); addImplementation(codec); return Optional.of(codec); @@ -1020,69 +1073,138 @@ class LazyGeneratedCodecRegistry implements // } } + /** + * + * Dispatch codec for augmented object, which processes augmentations + *

+ * This codec is used from DataObject codec generated using + * {@link TransformerGenerator#transformerFor(Class)} and is wired + * during {@link LazyGeneratedCodecRegistry#onDataContainerCodecCreated(Class, Class)}. + *

+ * Instance of this codec is associated with class of Binding DTO which + * represents target for augmentations. + * + */ @SuppressWarnings({ "rawtypes", "unchecked" }) static class AugmentableDispatchCodec extends LocationAwareDispatchCodec { private final Class augmentableType; + /** + * Construct augmetable dispatch codec. + * + * @param type Class representing augmentation target + * @param registry Registry with which this codec is associated. + */ public AugmentableDispatchCodec(final Class type, final LazyGeneratedCodecRegistry registry) { super(registry); Preconditions.checkArgument(Augmentable.class.isAssignableFrom(type)); augmentableType = type; } + + + /** + * Serializes object to list of values which needs to be injected + * into resulting DOM Node. Injection of data to parent DOM Node + * is handled by caller (in this case generated codec). + * + * TODO: Deprecate use of augmentation codec without instance + * instance identifier + * + * @return list of nodes, which needs to be added to parent node. + * + */ @Override - // TODO deprecate use without iid public Object serialize(final Object input) { + Preconditions.checkArgument(augmentableType.isInstance(input), "Object %s is not instance of %s ",input,augmentableType); if (input instanceof Augmentable) { - Map augmentations = getAugmentations(input); + Map>, Augmentation> augmentations = getAugmentations(input); return serializeImpl(augmentations); } return null; } - private Map getAugmentations(final Object input) { - Field augmentationField; - try { - augmentationField = input.getClass().getDeclaredField("augmentation"); - augmentationField.setAccessible(true); - Map augMap = (Map) augmentationField.get(input); - return augMap; - } catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException e) { - LOG.debug("Could not read augmentations for {}", input, e); - } - return Collections.emptyMap(); + /** + * + * Extracts augmentation from Binding DTO field using reflection + * + * @param input Instance of DataObject which is augmentable and + * may contain augmentation + * @return Map of augmentations if read was successful, otherwise + * empty map. + */ + private Map>, Augmentation> getAugmentations(final Object input) { + return AUGMENTATION_GETTERS.getUnchecked(input.getClass()).getAugmentations(input); } - @SuppressWarnings("deprecation") - private List serializeImpl(final Map input) { + /** + * + * Serialization of augmentations, returns list of composite nodes, + * which needs to be injected to parent node. + * + * @param input Map of classes to augmentations + * @return List of nodes, which should be added to parent node. + */ + private List serializeImpl(final Map>, Augmentation> input) { List ret = new ArrayList<>(); - for (Entry entry : input.entrySet()) { + for (Entry>, Augmentation> entry : input.entrySet()) { AugmentationCodec codec = getRegistry().getCodecForAugmentation(entry.getKey()); CompositeNode node = codec.serialize(new ValueWithQName(null, entry.getValue())); - ret.addAll(node.getChildren()); + ret.addAll(node.getValue()); } return ret; } + /** + * + * Deserialization of augmentation which is location aware. + * + * Note: In case of composite nodes as an input, each codec + * is invoked since there is no augmentation identifier + * and we need to look for concrete classes. + * FIXME: Maybe faster variation will be by extending + * {@link AugmentationCodecWrapper} to look for particular QNames, + * which will filter incoming set of codecs. + * + * + * @param input Input representation of data + * @param path Wildcarded instance identifier representing location of augmentation parent + * in conceptual schema tree + * @param codecs Set of codecs which are applicable for supplied path, + * selected by caller to be used by deserialization + * + * + */ @Override public Map deserializeImpl(final CompositeNode input, final InstanceIdentifier path, final Iterable codecs) { LOG.trace("{}: Going to deserialize augmentations from {} in location {}. Available codecs {}",this,input,path,codecs); Map ret = new HashMap<>(); for (AugmentationCodecWrapper codec : codecs) { - // We add Augmentation Identifier to path, in order to - // correctly identify children. - Class type = codec.getDataType(); - final InstanceIdentifier augmentPath = path.augmentation(type); - ValueWithQName value = codec.deserialize(input, augmentPath); - if (value != null && value.getValue() != null) { - ret.put(type, (Augmentation) value.getValue()); - } + // We add Augmentation Identifier to path, in order to + // correctly identify children. + Class type = codec.getDataType(); + final InstanceIdentifier augmentPath = path.augmentation(type); + ValueWithQName value = codec.deserialize(input, augmentPath); + if (value != null && value.getValue() != null) { + ret.put(type, (Augmentation) value.getValue()); + } } return ret; } + /** + * + * Tries to load implementation of concrete augmentation codec for supplied type + * + * Loading of codec may fail, because of supplied type may not be visible + * by classloaders known by registry. If class was not found returns {@link Optional#absent()}. + * + * @param potential Augmentation class identifier for which codecs should be loaded. + * @return Optional with codec for supplied type + * + */ protected Optional tryToLoadImplementation(final Type potential) { try { Class> clazz = (Class>) getRegistry().classLoadingStrategy @@ -1200,8 +1322,7 @@ class LazyGeneratedCodecRegistry implements // } @SuppressWarnings("rawtypes") - private static class AugmentationCodecWrapper> implements AugmentationCodec, - Delegator, LocationAwareBindingCodec, ValueWithQName> { + private static class AugmentationCodecWrapper> implements AugmentationCodec, Delegator, LocationAwareBindingCodec, ValueWithQName> { private final BindingCodec delegate; private final QName augmentationQName;