Revert "BUG-1196: fixed bug in choice case codec."
[yangtools.git] / code-generator / binding-generator-impl / src / main / java / org / opendaylight / yangtools / sal / binding / generator / impl / LazyGeneratedCodecRegistry.java
index 767405bd8b2650887af58c4a4252583c40cb6a44..e62f28e6469f2ad1f9a2af9e319b7ca92650ec4a 100644 (file)
@@ -81,7 +81,7 @@ import com.google.common.collect.HashMultimap;
 import com.google.common.collect.Multimap;
 import com.google.common.collect.Multimaps;
 
-public class LazyGeneratedCodecRegistry implements //
+class LazyGeneratedCodecRegistry implements //
         CodecRegistry, //
         SchemaContextListener, //
         GeneratorListener {
@@ -89,11 +89,6 @@ public class LazyGeneratedCodecRegistry implements //
     private static final Logger LOG = LoggerFactory.getLogger(LazyGeneratedCodecRegistry.class);
     private static final LateMixinCodec NOT_READY_CODEC = new LateMixinCodec();
 
-    private final InstanceIdentifierCodec instanceIdentifierCodec = new InstanceIdentifierCodecImpl(this);
-    private final IdentityCompositeCodec identityRefCodec = new IdentityCompositeCodec();
-
-    private TransformerGenerator generator;
-
     // Concrete class to codecs
     private static final Map<Class<?>, DataContainerCodec<?>> containerCodecs = Collections
             .synchronizedMap(new WeakHashMap<Class<?>, DataContainerCodec<?>>());
@@ -117,10 +112,7 @@ public class LazyGeneratedCodecRegistry implements //
     @SuppressWarnings("rawtypes")
     private static final ConcurrentMap<Type, ChoiceCaseCodecImpl> typeToCaseCodecs = new ConcurrentHashMap<>();
 
-    private final CaseClassMapFacade classToCaseRawCodec = new CaseClassMapFacade();
-
-    private static final Map<SchemaPath, InstanceIdentifier<?>> pathToBindingIdentifier = new ConcurrentHashMap<>();
-    private static final Map<SchemaPath, GeneratedTypeBuilder> pathToType = new ConcurrentHashMap<>();
+    private static final Map<SchemaPath, Type> pathToType = new ConcurrentHashMap<>();
     private static final Map<List<QName>, Type> pathToInstantiatedType = new ConcurrentHashMap<>();
     private static final Map<Type, QName> typeToQname = new ConcurrentHashMap<>();
     private static final BiMap<Type, AugmentationSchema> typeToAugment = HashBiMap
@@ -131,29 +123,27 @@ public class LazyGeneratedCodecRegistry implements //
     private static final Multimap<Type, Type> choiceToCases = Multimaps.synchronizedMultimap(HashMultimap
             .<Type, Type> create());
 
+    private final InstanceIdentifierCodec instanceIdentifierCodec = new InstanceIdentifierCodecImpl(this);
+    private final CaseClassMapFacade classToCaseRawCodec = new CaseClassMapFacade();
+    private final IdentityCompositeCodec identityRefCodec = new IdentityCompositeCodec();
+    private final ClassLoadingStrategy classLoadingStrategy;
+    private final AbstractTransformerGenerator generator;
     private final SchemaLock lock;
 
+    // FIXME: how is this protected?
     private SchemaContext currentSchema;
 
-    private final ClassLoadingStrategy classLoadingStrategy;
-
-    LazyGeneratedCodecRegistry(final SchemaLock lock, final ClassLoadingStrategy identityClassLoadingStrategy) {
+    LazyGeneratedCodecRegistry(final SchemaLock lock, final AbstractTransformerGenerator generator,
+            final ClassLoadingStrategy classLoadingStrategy) {
         this.lock = Preconditions.checkNotNull(lock);
-        this.classLoadingStrategy = identityClassLoadingStrategy;
+        this.classLoadingStrategy = Preconditions.checkNotNull(classLoadingStrategy);
+        this.generator = Preconditions.checkNotNull(generator);
     }
 
     public SchemaLock getLock() {
         return lock;
     }
 
-    public TransformerGenerator getGenerator() {
-        return generator;
-    }
-
-    public void setGenerator(final TransformerGenerator generator) {
-        this.generator = generator;
-    }
-
     @Override
     public InstanceIdentifierCodec getInstanceIdentifierCodec() {
         return instanceIdentifierCodec;
@@ -161,35 +151,36 @@ public class LazyGeneratedCodecRegistry implements //
 
     @SuppressWarnings("unchecked")
     @Override
-    public <T extends Augmentation<?>> AugmentationCodecWrapper<T> getCodecForAugmentation(final Class<T> object) {
+    public <T extends Augmentation<?>> AugmentationCodecWrapper<T> getCodecForAugmentation(final Class<T> augClass) {
         AugmentationCodecWrapper<T> codec = null;
         @SuppressWarnings("rawtypes")
-        AugmentationCodecWrapper potentialCodec = augmentationCodecs.get(object);
+        AugmentationCodecWrapper potentialCodec = augmentationCodecs.get(augClass);
         if (potentialCodec != null) {
             codec = potentialCodec;
         } else {
-            try {
-                lock.waitForSchema(object);
-                Class<? extends BindingCodec<Map<QName, Object>, Object>> augmentRawCodec = generator
-                        .augmentationTransformerFor(object);
-                BindingCodec<Map<QName, Object>, Object> rawCodec = augmentRawCodec.newInstance();
-
-                codec = new AugmentationCodecWrapper<T>(rawCodec, null, object);
-                augmentationCodecs.put(object, codec);
-            } catch (InstantiationException e) {
-                LOG.error("Can not instantiate raw augmentation codec {}", object.getSimpleName(), e);
-            } catch (IllegalAccessException e) {
-                LOG.debug(
-                        "Run-time consistency issue: constructor {} is not available. This indicates either a code generation bug or a misconfiguration of JVM.",
-                        object.getSimpleName(), e);
-            }
+            lock.waitForSchema(augClass);
+            Class<? extends BindingCodec<Map<QName, Object>, Object>> augmentRawCodec = generator
+                    .augmentationTransformerFor(augClass);
+
+            BindingCodec<Map<QName, Object>, Object> rawCodec = newInstanceOf(augmentRawCodec);
+            codec = new AugmentationCodecWrapper<T>(rawCodec, augClass);
+            augmentationCodecs.put(augClass, codec);
         }
-        Class<? extends Augmentable<?>> objectSupertype = getAugmentableArgumentFrom(object);
-        if (objectSupertype != null) {
-            getAugmentableCodec(objectSupertype).addImplementation(codec);
-        } else {
-            LOG.warn("Could not find augmentation target for augmentation {}", object);
+
+        final Class<? extends Augmentable<?>> objectSupertype;
+        try {
+            objectSupertype = BindingReflections.findAugmentationTarget(augClass);
+        } catch (Exception e) {
+            LOG.warn("Failed to find target for augmentation {}, ignoring it", augClass, e);
+            return codec;
         }
+
+        if (objectSupertype == null) {
+            LOG.warn("Augmentation target for {} not found, ignoring it", augClass);
+            return codec;
+        }
+
+        getAugmentableCodec(objectSupertype).addImplementation(codec);
         return codec;
     }
 
@@ -200,60 +191,35 @@ public class LazyGeneratedCodecRegistry implements //
         return getCodecForAugmentation((Class<? extends Augmentation<?>>) cls).getAugmentationQName();
     }
 
-    private static Class<? extends Augmentable<?>> getAugmentableArgumentFrom(
-            final Class<? extends Augmentation<?>> augmentation) {
-        try {
-            Class<? extends Augmentable<?>> ret = BindingReflections.findAugmentationTarget(augmentation);
-            return ret;
-
-        } catch (Exception e) {
-            LOG.debug("Could not find augmentable for {} using {}", augmentation, augmentation.getClassLoader(), e);
-            return null;
-        }
-    }
-
     @Override
     public Class<?> getClassForPath(final List<QName> names) {
-        DataSchemaNode node = getSchemaNode(names);
-        SchemaPath path = node.getPath();
-        Type type = pathToType.get(path);
-        if (type != null) {
-            type = new ReferencedTypeImpl(type.getPackageName(), type.getName());
+        final DataSchemaNode node = getSchemaNode(names);
+        final SchemaPath path = node.getPath();
+        final Type t = pathToType.get(path);
+
+        final Type type;
+        if (t != null) {
+            type = new ReferencedTypeImpl(t.getPackageName(), t.getName());
         } else {
             type = pathToInstantiatedType.get(names);
-        }
-        @SuppressWarnings("rawtypes")
-        WeakReference<Class> weakRef = typeToClass.get(type);
-        if (weakRef == null) {
-            LOG.error("Could not find loaded class for path: {} and type: {}", path, type.getFullyQualifiedName());
+            Preconditions.checkState(type != null, "Failed to lookup instantiated type for path %s", 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();
     }
 
     @Override
     public void putPathToClass(final List<QName> names, final Class<?> cls) {
-        Type reference = Types.typeForClass(cls);
+        final Type reference = Types.typeForClass(cls);
         pathToInstantiatedType.put(names, reference);
+        LOG.trace("Path {} attached to class {} reference {}", names, cls, reference);
         bindingClassEncountered(cls);
     }
 
-    public InstanceIdentifier<?> getBindingIdentifierByPath(final SchemaPath path) {
-        return pathToBindingIdentifier.get(path);
-    }
-
-    public void putPathToBindingIdentifier(final SchemaPath path, final InstanceIdentifier<?> bindingIdentifier) {
-        pathToBindingIdentifier.put(path, bindingIdentifier);
-    }
-
-    public InstanceIdentifier<?> putPathToBindingIdentifier(final SchemaPath path,
-            final InstanceIdentifier<?> bindingIdentifier, final Class<?> childClass) {
-        @SuppressWarnings({ "unchecked", "rawtypes" })
-        InstanceIdentifier<?> newId = bindingIdentifier.builder().child((Class) childClass).build();
-        pathToBindingIdentifier.put(path, newId);
-        return newId;
-    }
-
     @Override
     public IdentifierCodec<?> getKeyCodecForPath(final List<QName> names) {
         @SuppressWarnings("unchecked")
@@ -310,6 +276,8 @@ public class LazyGeneratedCodecRegistry implements //
         QName firstNode = path.get(0);
         DataNodeContainer previous = currentSchema.findModuleByNamespaceAndRevision(firstNode.getNamespace(),
                 firstNode.getRevision());
+        Preconditions.checkArgument(previous != null, "Failed to find module %s for path %s", firstNode, path);
+
         Iterator<QName> iterator = path.iterator();
         while (iterator.hasNext()) {
             QName arg = iterator.next();
@@ -352,15 +320,19 @@ public class LazyGeneratedCodecRegistry implements //
         return null;
     }
 
-    private <T> T newInstanceOf(final Class<?> newType) {
+    private static <T> T newInstanceOf(final Class<?> cls) {
         try {
             @SuppressWarnings("unchecked")
-            T ret = (T) newType.newInstance();
+            T ret = (T) cls.newInstance();
             return ret;
         } catch (InstantiationException e) {
-            throw new IllegalStateException(e);
+            LOG.error("Failed to instantiate codec {}", cls.getSimpleName(), e);
+            throw new IllegalStateException(String.format("Failed to instantiate codec %s", cls), e);
         } catch (IllegalAccessException e) {
-            throw new IllegalStateException(e);
+            LOG.debug(
+                    "Run-time consistency issue: constructor for {} is not available. This indicates either a code generation bug or a misconfiguration of JVM.",
+                    cls.getSimpleName(), e);
+            throw new IllegalStateException(String.format("Cannot access contructor of %s", cls), e);
         }
     }
 
@@ -426,7 +398,6 @@ public class LazyGeneratedCodecRegistry implements //
                 caseClass.getName());
         Preconditions.checkState(caseCodec.getSchema() != null, "Case schema is not available for %s",
                 caseClass.getName());
-        @SuppressWarnings("unchecked")
         Class<? extends BindingCodec> newCodec = generator.caseCodecFor(caseClass, caseCodec.getSchema());
         BindingCodec newInstance = newInstanceOf(newCodec);
         caseCodec.setDelegate(newInstance);
@@ -460,10 +431,10 @@ public class LazyGeneratedCodecRegistry implements //
                     identity.getKey());
         }
 
-        synchronized(augmentableToAugmentations) {
+        synchronized (augmentableToAugmentations) {
             augmentableToAugmentations.putAll(context.getAugmentableToAugmentations());
         }
-        synchronized(choiceToCases)  {
+        synchronized (choiceToCases) {
             choiceToCases.putAll(context.getChoiceToCases());
         }
         captureCases(context.getCases(), schemaContext);
@@ -525,7 +496,7 @@ public class LazyGeneratedCodecRegistry implements //
             }
             SchemaPath path = caseNode.getPath();
 
-            GeneratedTypeBuilder type;
+            Type type;
             if (path != null && (type = pathToType.get(path)) != null) {
                 ReferencedTypeImpl typeref = new ReferencedTypeImpl(type.getPackageName(), type.getName());
                 @SuppressWarnings("rawtypes")
@@ -562,7 +533,7 @@ public class LazyGeneratedCodecRegistry implements //
         }
     }
 
-    public AugmentableDispatchCodec getAugmentableCodec(final Class<?> dataClass) {
+    public synchronized AugmentableDispatchCodec getAugmentableCodec(final Class<?> dataClass) {
         AugmentableDispatchCodec ret = augmentableCodecs.get(dataClass);
         if (ret != null) {
             return ret;
@@ -707,18 +678,49 @@ public class LazyGeneratedCodecRegistry implements //
             return implementation;
         }
 
-        protected final void adaptForPath(final InstanceIdentifier<?> path) {
+        protected final synchronized void adaptForPath(final InstanceIdentifier<?> path) {
             if (adaptedForPaths.contains(path)) {
                 return;
             }
-            Optional<DataNodeContainer> contextNode = BindingSchemaContextUtils.findDataNodeContainer(currentSchema, path);
+            /**
+             * We search in schema context if the use of this location aware
+             * codec (augmentable codec, case codec) makes sense on provided
+             * location (path)
+             *
+             */
+            Optional<DataNodeContainer> contextNode = BindingSchemaContextUtils.findDataNodeContainer(currentSchema,
+                    path);
+            /**
+             * If context node is present, this codec makes sense on provided
+             * location.
+             *
+             */
             if (contextNode.isPresent()) {
                 synchronized (this) {
+                    /**
+                     *
+                     * We adapt (turn on / off) possible implementations of
+                     * child codecs (augmentations, cases) based on this
+                     * location.
+                     *
+                     *
+                     */
+
                     adaptForPathImpl(path, contextNode.get());
+                    try {
+                        /**
+                         * We trigger serialization of instance identifier, to
+                         * make sure instance identifier codec is aware of
+                         * combination of this path / augmentation / case
+                         */
+                        instanceIdentifierCodec.serialize(path);
+                    } catch (Exception e) {
+                        LOG.warn("Exception during preparation of instance identifier codec for  path {}.", path, e);
+                    }
                     adaptedForPaths.add(path);
                 }
             } else {
-                LOG.debug("Context node (parent node) not found for {}",path);
+                LOG.debug("Context node (parent node) not found for {}", path);
             }
         }
 
@@ -842,7 +844,8 @@ public class LazyGeneratedCodecRegistry implements //
         }
     }
 
-    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;
 
@@ -887,7 +890,8 @@ public class LazyGeneratedCodecRegistry implements //
     private class DispatchChoiceCodecImpl extends LocationAwareDispatchCodec<ChoiceCaseCodecImpl<?>> {
 
         @Override
-        public Object deserialize(final Object input, @SuppressWarnings("rawtypes") final InstanceIdentifier bindingIdentifier) {
+        public Object deserialize(final Object input,
+                @SuppressWarnings("rawtypes") final InstanceIdentifier bindingIdentifier) {
             // TODO Auto-generated method stub
             return null;
         }
@@ -1043,7 +1047,7 @@ public class LazyGeneratedCodecRegistry implements //
     }
 
     @SuppressWarnings({ "rawtypes", "unchecked" })
-    private class AugmentableDispatchCodec extends LocationAwareDispatchCodec<AugmentationCodecWrapper> {
+    class AugmentableDispatchCodec extends LocationAwareDispatchCodec<AugmentationCodecWrapper> {
 
         private final Class augmentableType;
 
@@ -1115,7 +1119,7 @@ public class LazyGeneratedCodecRegistry implements //
                         .loadClass(potential);
                 return Optional.of(tryToLoadImplementation(clazz));
             } catch (ClassNotFoundException e) {
-                LOG.warn("Failed to find class for augmentation of {}, reason: {}", potential, e.toString());
+                LOG.warn("Failed to find class for augmentation of {}", potential, e);
             }
             return Optional.absent();
         }
@@ -1125,59 +1129,93 @@ public class LazyGeneratedCodecRegistry implements //
             AugmentationCodecWrapper<? extends Augmentation<?>> potentialImpl = getCodecForAugmentation(inputType);
             addImplementation(potentialImpl);
             return potentialImpl;
-
         }
 
         @Override
         protected void tryToLoadImplementations() {
             Type type = referencedType(augmentableType);
             Collection<Type> potentialAugmentations;
-            synchronized(augmentableToAugmentations) {
+            synchronized (augmentableToAugmentations) {
                 potentialAugmentations = new ArrayList(augmentableToAugmentations.get(type));
             }
             for (Type potential : potentialAugmentations) {
                 try {
                     tryToLoadImplementation(potential);
                 } catch (CodeGenerationException e) {
-                    LOG.warn("Failed to proactively generate augment coded for {}, reason: {}", type, e.toString());
+                    LOG.warn("Failed to proactively generate augment code for {}", type, e);
                 }
             }
-
         }
 
         @Override
-        protected void adaptForPathImpl(final InstanceIdentifier<?> path, final DataNodeContainer ctxNode) {
+        protected void adaptForPathImpl(final InstanceIdentifier<?> augTarget, final DataNodeContainer ctxNode) {
             if (ctxNode instanceof AugmentationTarget) {
                 Set<AugmentationSchema> availableAugmentations = ((AugmentationTarget) ctxNode)
                         .getAvailableAugmentations();
                 if (!availableAugmentations.isEmpty()) {
-                    updateAugmentationMapping(path,availableAugmentations);
+                    updateAugmentationMapping(augTarget, availableAugmentations);
                 }
-
             }
         }
 
-        private void updateAugmentationMapping(final InstanceIdentifier<?> path, final Set<AugmentationSchema> availableAugmentations) {
+        /**
+         *
+         * Adapts augmentation codec for specific provider location (target)
+         *
+         * Since augmentation are not forward-referencing and may be discovered
+         * during runtime, we need to adapt {@link AugmentableDispatchCodec},
+         * {@link AugmentationCodecWrapper} and {@link InstanceIdentifierCodec}
+         * for this newly discovered location where augmentation may be used.
+         *
+         * Adaptation consists of:
+         * <ol>
+         * <li> scan of available (valid) augmentations for
+         * current location
+         * <li>lookup for Java classes derived from this augmentations
+         * <li>generation of missing codecs
+         * <li>updating Augmentation codecs to work with new location
+         * <li>updating Instance Identifier to work with new location
+         *
+         */
+        private void updateAugmentationMapping(final InstanceIdentifier<?> augTarget,
+                final Set<AugmentationSchema> availableAugmentations) {
             for (AugmentationSchema aug : availableAugmentations) {
 
                 Type potentialType = getTypeForAugmentation(aug);
                 if (potentialType != null) {
                     Optional<AugmentationCodecWrapper> potentialImpl = tryToLoadImplementation(potentialType);
                     if (potentialImpl.isPresent()) {
-                        potentialImpl.get().addApplicableFor(path,aug);
+                        potentialImpl.get().addApplicableFor(augTarget, aug);
+                        Class augType = potentialImpl.get().getDataType();
+                        InstanceIdentifier augPath = augTarget.augmentation(augType);
+                        try {
+
+                            org.opendaylight.yangtools.yang.data.api.InstanceIdentifier domPath = getInstanceIdentifierCodec().serialize(augPath);
+                            if(domPath == null) {
+                                LOG.error("Unable to serialize instance identifier for {}",augPath);
+                            }
+                        } catch (Exception e) {
+                            LOG.error("Unable to serialize instance identifiers for {}",augPath,e);
+                        }
+
                     }
                 } else {
-                    LOG.warn("Could not find generated type for augmentation {} with childs {}.",aug,aug.getChildNodes());
+                    // Omits warning for empty augmentations since they are not represented in data
+                    if(!aug.getChildNodes().isEmpty()) {
+                        LOG.warn("Could not find generated type for augmentation {} with children {}", aug,
+                            aug.getChildNodes());
+                    }
                 }
             }
-            availableAugmentations.toString();
         }
 
+
+
         private Type getTypeForAugmentation(final AugmentationSchema aug) {
             Optional<AugmentationSchema> currentAug = Optional.of(aug);
-            while(currentAug.isPresent()) {
+            while (currentAug.isPresent()) {
                 Type potentialType = typeToAugment.inverse().get(currentAug.get());
-                if(potentialType != null) {
+                if (potentialType != null) {
                     return potentialType;
                 }
                 currentAug = currentAug.get().getOriginalDefinition();
@@ -1222,20 +1260,20 @@ public class LazyGeneratedCodecRegistry implements //
 
         private final BindingCodec delegate;
         private final QName augmentationQName;
-        private final Multimap<InstanceIdentifier<?>,QName> validAugmentationTargets;
+        private final Multimap<InstanceIdentifier<?>, QName> validAugmentationTargets;
         private final Class<?> augmentationType;
 
-        public AugmentationCodecWrapper(final BindingCodec<Map<QName, Object>, Object> rawCodec,
-                final InstanceIdentifier<?> targetId, final Class<?> dataType) {
+        public AugmentationCodecWrapper(final BindingCodec<Map<QName, Object>, Object> rawCodec, final Class<?> dataType) {
             this.delegate = rawCodec;
             this.augmentationType = dataType;
             this.augmentationQName = BindingReflections.findQName(rawCodec.getClass());
-            this.validAugmentationTargets = Multimaps.synchronizedSetMultimap(HashMultimap.<InstanceIdentifier<?>,QName>create());
+            this.validAugmentationTargets = Multimaps.synchronizedSetMultimap(HashMultimap
+                    .<InstanceIdentifier<?>, QName> create());
         }
 
         public void addApplicableFor(final InstanceIdentifier<?> path, final AugmentationSchema aug) {
-            for(DataSchemaNode child : aug.getChildNodes()) {
-                validAugmentationTargets.put(path,child.getQName());
+            for (DataSchemaNode child : aug.getChildNodes()) {
+                validAugmentationTargets.put(path, child.getQName());
             }
         }
 
@@ -1331,7 +1369,7 @@ public class LazyGeneratedCodecRegistry implements //
         }
 
         @Override
-        public Object deserialize(final Object input,final InstanceIdentifier bindingIdentifier) {
+        public Object deserialize(final Object input, final InstanceIdentifier bindingIdentifier) {
             Type type = qnamesToIdentityMap.get(input);
             if (type == null) {
                 return null;
@@ -1379,29 +1417,7 @@ public class LazyGeneratedCodecRegistry implements //
 
     }
 
-    public boolean isCodecAvailable(final Class<? extends DataContainer> cls) {
-        if (containerCodecs.containsKey(cls)) {
-            return true;
-        }
-        if (identifierCodecs.containsKey(cls)) {
-            return true;
-        }
-        if (choiceCodecs.containsKey(cls)) {
-            return true;
-        }
-        if (caseCodecs.containsKey(cls)) {
-            return true;
-        }
-        if (augmentableCodecs.containsKey(cls)) {
-            return true;
-        }
-        if (augmentationCodecs.containsKey(cls)) {
-            return true;
-        }
-        return false;
-    }
-
-    public static final Type referencedType(final Class<?> augmentableType) {
+    private static final Type referencedType(final Class<?> augmentableType) {
         return new ReferencedTypeImpl(augmentableType.getPackage().getName(), augmentableType.getSimpleName());
     }
 }