More license header changes to appc-client files
[appc.git] / appc-client / code-generator / src / main / java / org / onap / appc / tools / generator / extensions / YangContextBuilderImpl.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP : APPC
4  * ================================================================================
5  * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Copyright (C) 2017 Amdocs
8  * =============================================================================
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  * 
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  * 
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  * 
21  * ============LICENSE_END=========================================================
22  */
23
24 package org.onap.appc.tools.generator.extensions;
25
26 import com.google.common.base.Optional;
27 import org.onap.appc.tools.generator.api.ContextBuilder;
28 import org.opendaylight.yangtools.yang.model.api.Module;
29 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
30 import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
31 import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException;
32 import org.opendaylight.yangtools.yang.parser.repo.YangTextSchemaContextResolver;
33
34 import java.io.IOException;
35 import java.lang.reflect.Modifier;
36 import java.lang.reflect.Proxy;
37 import java.net.URL;
38 import java.util.Arrays;
39 import java.util.HashMap;
40 import java.util.HashSet;
41 import java.util.Map;
42 import java.util.Set;
43
44 public class YangContextBuilderImpl implements ContextBuilder {
45
46     @Override
47     public Map<String, Object> buildContext(URL sourceYangURL, String contextConf) throws IOException {
48
49
50         Optional<SchemaContext> sc = null;
51         try ( YangTextSchemaContextResolver yangContextResolver =
52               YangTextSchemaContextResolver.create("yang-context-resolver")) {
53             yangContextResolver.registerSource(sourceYangURL);
54             sc = yangContextResolver.getSchemaContext();
55         } catch (SchemaSourceException | IOException | YangSyntaxErrorException e) {
56             throw new IOException(String.format("Exception occurred while processing sourceFileName %s ",sourceYangURL),e);
57         }
58
59         Map<String, Object> map = new HashMap<>();
60         if ( null != sc && sc.isPresent()) {
61             Set<Module> modules = sc.get().getModules();
62             for (final Module module : modules) {
63
64                 Module proxyModule = (new PackagePrivateReflectBug()).buildProxyObjects(module);
65                 map.put("module", proxyModule);
66             }
67         }
68
69         return map;
70     }
71
72
73     /**
74      * The Wrap Proxy uses java Proxy to work around bug JDK-8193172.  The Issue is that if a super class is package
75      * private its public methods cause IllegalAccessException when using java reflect to access those methods from a
76      * subclass which is package public.
77      * <p>
78      * Example exception:
79      * Caused by: java.lang.IllegalAccessException: Class freemarker.ext.beans.BeansWrapper cannot access a
80      * member of class org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective.AbstractEffectiveModule
81      * with modifiers "public final"
82      */
83     private static class  PackagePrivateReflectBug {
84
85         /**
86          * Recursive method that traverses the yang model and wraps objects that extend package private classes
87          * with java proxy objects.
88          * @param candidateObj
89          * @param <T>
90          * @return
91          */
92         private <T> T buildProxyObjects(Object candidateObj) {
93
94             //can't proxy null so just return
95             if (candidateObj == null) {
96                 return null;
97             }
98
99
100             Class<?> candidateClass = candidateObj.getClass();
101
102             //prevent a recursive proxy
103             if (Proxy.isProxyClass(candidateClass)) {
104                 return (T) candidateObj;
105             }
106
107             //Can�t create a proxy an Array,  so replace each
108             //element the array with a proxy if needed.
109             if (candidateClass.isArray()) {
110                 Object[] sourceArray = (Object[]) candidateObj;
111                 for (int i = 0; i < sourceArray.length; i++) {
112                     sourceArray[i] = buildProxyObjects(sourceArray[i]);
113                 }
114                 return (T) candidateObj;
115             }
116
117             //Evaluate if this class must be wrapped in a proxy or not.
118             if (!isCandidateForProxy(candidateClass)) {
119                 return (T) candidateObj;
120             }
121
122             //Collect the interfaces for the proxy.  Proxy only work with classes
123             //that implement interfaces if there are none the obj cannot be wrapped
124             //with a proxy.
125             HashSet<Class<?>> interfaceSet = new HashSet<>();
126             collectInterfaces(candidateClass, interfaceSet);
127             if (interfaceSet.isEmpty()) {
128                 return (T) candidateObj;
129             }
130             Class<?>[] interfaces = new Class<?>[interfaceSet.size()];
131             interfaceSet.toArray(interfaces);
132
133             //wrap the Object in a proxy
134             return (T) Proxy.newProxyInstance(
135                     candidateClass.getClassLoader(),
136                     interfaces,
137                     (proxy, method, args) -> {
138                         Object returnObj = method.invoke(candidateObj, args);
139                         returnObj = buildProxyObjects(returnObj);
140                         return returnObj;
141                     }
142             );
143         }
144
145
146         /**
147          * This method determines if the specified class is a candidate for proxy.
148          *
149          * @param fromClass
150          * @return true - if the specifed class is a Candidate.
151          */
152         private boolean isCandidateForProxy(Class<?> fromClass) {
153
154             //a
155             Class<?>[] includeClasses = {
156                     java.util.Collection.class,
157                     java.util.Map.class,
158                     org.opendaylight.yangtools.yang.model.api.Module.class
159
160             };
161
162             for (Class<?> toClass : includeClasses) {
163                 if (toClass.isAssignableFrom(fromClass)) {
164                     return true;
165                 }
166             }
167
168             if (isExtendsPackagePrivateSuperClass(fromClass)) {
169                 return true;
170             }
171
172             return false;
173         }
174
175         /**
176          * Test if any of the super packages are package private.
177          *
178          * @param clazz
179          * @return
180          */
181         private boolean isExtendsPackagePrivateSuperClass(Class<?> clazz) {
182             if (Object.class.equals(clazz)) {
183                 return false;
184             }
185
186             int classModifiers = clazz.getModifiers();
187             if (Modifier.isPublic(classModifiers)) {
188                 return isExtendsPackagePrivateSuperClass(clazz.getSuperclass());
189             }
190
191             return true;
192         }
193
194         /**
195          * Collect all of the interfaces this class can be cast too.  Tavers the class hierarchy to include interfaces
196          * from the super classes.
197          *
198          * @param clazz
199          * @param classSet
200          */
201         private void collectInterfaces(Class<?> clazz, Set<Class<?>> classSet) {
202             Class<?>[] interfaces = clazz.getInterfaces();
203             classSet.addAll(Arrays.asList(interfaces));
204             Class<?> superClass = clazz.getSuperclass();
205
206     //
207             if (!Object.class.equals(superClass)) {
208                 collectInterfaces(superClass, classSet);
209             }
210         }
211     }
212 }