Automate Updates to the API Client Library
[appc.git] / appc-client / code-generator / src / main / java / org / onap / appc / tools / generator / extensions / YangContextBuilderImpl.java
index 67055c7..6307d13 100644 (file)
 
 package org.onap.appc.tools.generator.extensions;
 
-import org.onap.appc.tools.generator.api.ContextBuilder;
 import com.google.common.base.Optional;
-
+import org.onap.appc.tools.generator.api.ContextBuilder;
 import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
 import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException;
 import org.opendaylight.yangtools.yang.parser.repo.YangTextSchemaContextResolver;
-import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective.ModuleEffectiveStatementImpl;
 
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
 import java.io.IOException;
-import java.io.InputStream;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Proxy;
 import java.net.URL;
-import java.util.Map;
+import java.util.Arrays;
 import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
 import java.util.Set;
 
 public class YangContextBuilderImpl implements ContextBuilder {
 
     @Override
-    public Map<String, Object> buildContext(String sourceFile, String contextConf) throws FileNotFoundException {
-        try ( InputStream source = new FileInputStream(sourceFile) ) {}
-        catch ( IOException ex) {
-            throw new FileNotFoundException("YANG file <" + sourceFile + ">not found");
-        }
+    public Map<String, Object> buildContext(URL sourceYangURL, String contextConf) throws IOException {
+
 
         Optional<SchemaContext> sc = null;
         try ( YangTextSchemaContextResolver yangContextResolver =
               YangTextSchemaContextResolver.create("yang-context-resolver")) {
-            yangContextResolver.registerSource(new URL("file:///" + sourceFile));
+            yangContextResolver.registerSource(sourceYangURL);
             sc = yangContextResolver.getSchemaContext();
         } catch (SchemaSourceException | IOException | YangSyntaxErrorException e) {
-            e.printStackTrace();
+            throw new IOException(String.format("Exception occurred while processing sourceFileName %s ",sourceYangURL),e);
         }
 
         Map<String, Object> map = new HashMap<>();
         if ( null != sc && sc.isPresent()) {
             Set<Module> modules = sc.get().getModules();
-            for (Module module : modules) {
-                ModuleEffectiveStatementImpl impl = (ModuleEffectiveStatementImpl) module;
-                map.put("module", module);
+            for (final Module module : modules) {
+
+                Module proxyModule = (new PackagePrivateReflectBug()).buildProxyObjects(module);
+                map.put("module", proxyModule);
             }
         }
 
         return map;
     }
 
-    // @Override
-    // public Map<String, Object> buildContext(String sourceFile, String
-    // contextConf) throws FileNotFoundException {
-    // InputStream source = new FileInputStream(sourceFile);
-    // if (source == null) {
-    // throw new FileNotFoundException("YANG file <" + sourceFile + ">not found");
-    // }
-    //
-    // SchemaContext mSchema = parse(Collections.singletonList(source));
-    //
-    // Map<String, Object> map = new HashMap<>();
-    // map.put("module", mSchema.getModules().iterator().next());
-    // return map;
-    // }
-    //
-    // private SchemaContext parse(List<InputStream> sources) {
-    // YangParserImpl parser = new YangParserImpl();
-    // Set<Module> modules = parser.parseYangModelsFromStreams(sources);
-    // return parser.resolveSchemaContext(modules);
-    // }
 
+    /**
+     * The Wrap Proxy uses java Proxy to work around bug JDK-8193172.  The Issue is that if a super class is package
+     * private its public methods cause IllegalAccessException when using java reflect to access those methods from a
+     * subclass which is package public.
+     * <p>
+     * Example exception:
+     * Caused by: java.lang.IllegalAccessException: Class freemarker.ext.beans.BeansWrapper cannot access a
+     * member of class org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective.AbstractEffectiveModule
+     * with modifiers "public final"
+     */
+    private static class  PackagePrivateReflectBug {
+
+        /**
+         * Recursive method that traverses the yang model and wraps objects that extend package private classes
+         * with java proxy objects.
+         * @param candidateObj
+         * @param <T>
+         * @return
+         */
+        private <T> T buildProxyObjects(Object candidateObj) {
+
+            //can't proxy null so just return
+            if (candidateObj == null) {
+                return null;
+            }
+
+
+            Class<?> candidateClass = candidateObj.getClass();
+
+            //prevent a recursive proxy
+            if (Proxy.isProxyClass(candidateClass)) {
+                return (T) candidateObj;
+            }
+
+            //Can�t create a proxy an Array,  so replace each
+            //element the array with a proxy if needed.
+            if (candidateClass.isArray()) {
+                Object[] sourceArray = (Object[]) candidateObj;
+                for (int i = 0; i < sourceArray.length; i++) {
+                    sourceArray[i] = buildProxyObjects(sourceArray[i]);
+                }
+                return (T) candidateObj;
+            }
+
+            //Evaluate if this class must be wrapped in a proxy or not.
+            if (!isCandidateForProxy(candidateClass)) {
+                return (T) candidateObj;
+            }
+
+            //Collect the interfaces for the proxy.  Proxy only work with classes
+            //that implement interfaces if there are none the obj cannot be wrapped
+            //with a proxy.
+            HashSet<Class<?>> interfaceSet = new HashSet<>();
+            collectInterfaces(candidateClass, interfaceSet);
+            if (interfaceSet.isEmpty()) {
+                return (T) candidateObj;
+            }
+            Class<?>[] interfaces = new Class<?>[interfaceSet.size()];
+            interfaceSet.toArray(interfaces);
+
+            //wrap the Object in a proxy
+            return (T) Proxy.newProxyInstance(
+                    candidateClass.getClassLoader(),
+                    interfaces,
+                    (proxy, method, args) -> {
+                        Object returnObj = method.invoke(candidateObj, args);
+                        returnObj = buildProxyObjects(returnObj);
+                        return returnObj;
+                    }
+            );
+        }
+
+
+        /**
+         * This method determines if the specified class is a candidate for proxy.
+         *
+         * @param fromClass
+         * @return true - if the specifed class is a Candidate.
+         */
+        private boolean isCandidateForProxy(Class<?> fromClass) {
+
+            //a
+            Class<?>[] includeClasses = {
+                    java.util.Collection.class,
+                    java.util.Map.class,
+                    org.opendaylight.yangtools.yang.model.api.Module.class
+
+            };
+
+            for (Class<?> toClass : includeClasses) {
+                if (toClass.isAssignableFrom(fromClass)) {
+                    return true;
+                }
+            }
+
+            if (isExtendsPackagePrivateSuperClass(fromClass)) {
+                return true;
+            }
+
+            return false;
+        }
+
+        /**
+         * Test if any of the super packages are package private.
+         *
+         * @param clazz
+         * @return
+         */
+        private boolean isExtendsPackagePrivateSuperClass(Class<?> clazz) {
+            if (Object.class.equals(clazz)) {
+                return false;
+            }
+
+            int classModifiers = clazz.getModifiers();
+            if (Modifier.isPublic(classModifiers)) {
+                return isExtendsPackagePrivateSuperClass(clazz.getSuperclass());
+            }
+
+            return true;
+        }
+
+        /**
+         * Collect all of the interfaces this class can be cast too.  Tavers the class hierarchy to include interfaces
+         * from the super classes.
+         *
+         * @param clazz
+         * @param classSet
+         */
+        private void collectInterfaces(Class<?> clazz, Set<Class<?>> classSet) {
+            Class<?>[] interfaces = clazz.getInterfaces();
+            classSet.addAll(Arrays.asList(interfaces));
+            Class<?> superClass = clazz.getSuperclass();
+
+    //
+            if (!Object.class.equals(superClass)) {
+                collectInterfaces(superClass, classSet);
+            }
+        }
+    }
 }