*/
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;
import org.opendaylight.yangtools.yang.binding.Augmentation;
import org.opendaylight.yangtools.yang.binding.BaseIdentity;
import org.opendaylight.yangtools.yang.binding.BindingCodec;
+import org.opendaylight.yangtools.yang.binding.BindingMapping;
import org.opendaylight.yangtools.yang.binding.DataContainer;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.Identifier;
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);
private final AbstractTransformerGenerator generator;
private final SchemaLock lock;
+ private static final LoadingCache<Class<?>, AugmentationFieldGetter> AUGMENTATION_GETTERS =
+ CacheBuilder.newBuilder().weakKeys().softValues().build(new AugmentationGetterLoader());
+
// FIXME: how is this protected?
private SchemaContext currentSchema;
@Override
public Class<?> getClassForPath(final List<QName> 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);
@SuppressWarnings("rawtypes")
final WeakReference<Class> 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
WeakReference<Class> weakRef = new WeakReference<>(cls);
typeToClass.put(typeRef, weakRef);
if (Augmentation.class.isAssignableFrom(cls)) {
-
+ // Intentionally NOOP
} else if (DataObject.class.isAssignableFrom(cls)) {
getCodecForDataObject((Class<? extends DataObject>) cls);
}
}
private DataSchemaNode searchInChoices(final DataNodeContainer node, final QName arg) {
- Set<DataSchemaNode> 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);
for (Map.Entry<Type, AugmentationSchema> entry : bimap.entrySet()) {
Type key = entry.getKey();
AugmentationSchema value = entry.getValue();
- Set<DataSchemaNode> augmentedNodes = value.getChildNodes();
- if (augmentedNodes != null && !(augmentedNodes.isEmpty())) {
+ Collection<DataSchemaNode> augmentedNodes = value.getChildNodes();
+ if (augmentedNodes != null && !augmentedNodes.isEmpty()) {
typeToAugment.put(key, value);
}
}
return ret;
}
- private static abstract class IntermediateCodec<T> implements //
- DomCodec<T>, Delegator<BindingCodec<Map<QName, Object>, Object>> {
+ private static final class AugmentationGetterLoader extends CacheLoader<Class<?>, AugmentationFieldGetter> {
+ private static final AugmentationFieldGetter DUMMY = new AugmentationFieldGetter() {
+ @Override
+ Map<Class<? extends Augmentation<?>>, 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<Class<? extends Augmentation<?>>, 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<Class<? extends Augmentation<?>>, Augmentation<?>> getAugmentations(final Object input) {
+ try {
+ return (Map<Class<? extends Augmentation<?>>, Augmentation<?>>) augmentationField.get(input);
+ } catch (IllegalArgumentException | IllegalAccessException e) {
+ throw new IllegalStateException("Failed to access augmentation field", e);
+ }
+ }
+ }
+
+ private static abstract class IntermediateCodec<T> implements DomCodec<T>, Delegator<BindingCodec<Map<QName, Object>, Object>> {
private final BindingCodec<Map<QName, Object>, Object> delegate;
}
- private static class IdentifierCodecImpl<T extends Identifier<?>> //
- extends IntermediateCodec<T> //
- implements IdentifierCodec<T> {
+ private static class IdentifierCodecImpl<T extends Identifier<?>> extends IntermediateCodec<T> implements IdentifierCodec<T> {
public IdentifierCodecImpl(final BindingCodec<Map<QName, Object>, Object> delegate) {
super(delegate);
}
}
- private static class DataContainerCodecImpl<T extends DataContainer> //
- extends IntermediateCodec<T> //
- implements DataContainerCodec<T> {
+ private static class DataContainerCodecImpl<T extends DataContainer> extends IntermediateCodec<T> implements DataContainerCodec<T> {
public DataContainerCodecImpl(final BindingCodec<Map<QName, Object>, Object> delegate) {
super(delegate);
/* 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;
}
@SuppressWarnings("rawtypes")
private static class ChoiceCaseCodecImpl<T extends DataContainer> implements ChoiceCaseCodec<T>, //
- Delegator<BindingCodec>, LocationAwareBindingCodec<Node<?>, ValueWithQName<T>> {
+ Delegator<BindingCodec>, LocationAwareBindingCodec<Node<?>, ValueWithQName<T>> {
private final BindingCodec delegate;
private final ChoiceCaseNode schema;
private final Map<InstanceIdentifier<?>, ChoiceCaseNode> instantiatedLocations;
}
}
- private static class PublicChoiceCodecImpl<T> implements ChoiceCodec<T>,
- Delegator<BindingCodec<Map<QName, Object>, Object>> {
+ private static class PublicChoiceCodecImpl<T> implements ChoiceCodec<T>, Delegator<BindingCodec<Map<QName, Object>, Object>> {
private final BindingCodec<Map<QName, Object>, Object> delegate;
try {
@SuppressWarnings("unchecked")
Class<? extends DataContainer> clazz = (Class<? extends DataContainer>) classLoadingStrategy
- .loadClass(potential);
+ .loadClass(potential);
ChoiceCaseCodecImpl codec = tryToLoadImplementation(clazz);
addImplementation(codec);
return Optional.of(codec);
}
}
+ /**
+ *
+ * Dispatch codec for augmented object, which processes augmentations
+ * <p>
+ * This codec is used from DataObject codec generated using
+ * {@link TransformerGenerator#transformerFor(Class)} and is wired
+ * during {@link LazyGeneratedCodecRegistry#onDataContainerCodecCreated(Class, Class)}.
+ * <p>
+ * Instance of this codec is associated with class of Binding DTO which
+ * represents target for augmentations.
+ *
+ */
@SuppressWarnings({ "rawtypes", "unchecked" })
static class AugmentableDispatchCodec extends LocationAwareDispatchCodec<AugmentationCodecWrapper> {
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<Class, Augmentation> augmentations = getAugmentations(input);
+ Map<Class<? extends Augmentation<?>>, Augmentation<?>> augmentations = getAugmentations(input);
return serializeImpl(augmentations);
}
return null;
}
- private Map<Class, Augmentation> getAugmentations(final Object input) {
- Field augmentationField;
- try {
- augmentationField = input.getClass().getDeclaredField("augmentation");
- augmentationField.setAccessible(true);
- Map<Class, Augmentation> augMap = (Map<Class, Augmentation>) 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<Class<? extends Augmentation<?>>, Augmentation<?>> getAugmentations(final Object input) {
+ return AUGMENTATION_GETTERS.getUnchecked(input.getClass()).getAugmentations(input);
}
- @SuppressWarnings("deprecation")
- private List serializeImpl(final Map<Class, Augmentation> 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<Class<? extends Augmentation<?>>, Augmentation<?>> input) {
List ret = new ArrayList<>();
- for (Entry<Class, Augmentation> entry : input.entrySet()) {
+ for (Entry<Class<? extends Augmentation<?>>, 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 <code>path</code>,
+ * selected by caller to be used by deserialization
+ *
+ *
+ */
@Override
public Map<Class, Augmentation> deserializeImpl(final CompositeNode input, final InstanceIdentifier<?> path,
final Iterable<AugmentationCodecWrapper> codecs) {
LOG.trace("{}: Going to deserialize augmentations from {} in location {}. Available codecs {}",this,input,path,codecs);
Map<Class, Augmentation> 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<AugmentationCodecWrapper> tryToLoadImplementation(final Type potential) {
try {
Class<? extends Augmentation<?>> clazz = (Class<? extends Augmentation<?>>) getRegistry().classLoadingStrategy
}
@SuppressWarnings("rawtypes")
- private static class AugmentationCodecWrapper<T extends Augmentation<?>> implements AugmentationCodec<T>,
- Delegator<BindingCodec>, LocationAwareBindingCodec<Node<?>, ValueWithQName<T>> {
+ private static class AugmentationCodecWrapper<T extends Augmentation<?>> implements AugmentationCodec<T>, Delegator<BindingCodec>, LocationAwareBindingCodec<Node<?>, ValueWithQName<T>> {
private final BindingCodec delegate;
private final QName augmentationQName;
@Override
public Class<?> deserialize(final QName input) {
+ if(input == null) {
+ return null;
+ }
Type type = qnamesToIdentityMap.get(input);
if (type == null) {
- throw new IllegalArgumentException( "Invalid value \"" + input + "\"." );
+ String packageName = BindingMapping.getRootPackageName(input);
+ String className = BindingMapping.getClassName(input);
+ type = new ReferencedTypeImpl(packageName, className);
}
ReferencedTypeImpl typeref = new ReferencedTypeImpl(type.getPackageName(), type.getName());
WeakReference<Class> softref = typeToClass.get(typeref);
if (qname != null) {
return qname;
}
- ConcreteType typeref = Types.typeForClass(input);
- qname = typeToQname.get(typeref);
+ qname = BindingReflections.findQName(input);
if (qname != null) {
identityQNames.put(input, qname);
}