3b3f4190cfb4716a04ecfbc7808e5bbcb06acc95
[controller.git] / opendaylight / md-sal / sal-binding-broker / src / main / java / org / opendaylight / controller / sal / binding / codegen / impl / RuntimeCodeGenerator.xtend
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.controller.sal.binding.codegen.impl
9
10 import javassist.ClassPool
11 import org.opendaylight.yangtools.yang.binding.RpcService
12
13 import javassist.CtClass
14 import static com.google.common.base.Preconditions.*
15
16 import javassist.CtField
17 import javassist.Modifier
18 import javassist.CtMethod
19 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier
20 import org.opendaylight.yangtools.yang.binding.annotations.RoutingContext
21 import org.opendaylight.yangtools.yang.binding.BaseIdentity
22
23 import java.util.Map
24 import java.util.HashMap
25 import javassist.NotFoundException
26 import javassist.LoaderClassPath
27 import org.opendaylight.controller.sal.binding.codegen.impl.JavassistUtils.MethodGenerator
28 import org.opendaylight.controller.sal.binding.codegen.impl.JavassistUtils.ClassGenerator
29 import org.opendaylight.yangtools.yang.binding.NotificationListener
30 import org.opendaylight.yangtools.yang.binding.Notification
31 import java.util.Arrays
32
33 import static extension org.opendaylight.controller.sal.binding.codegen.YangtoolsMappingHelper.*
34 import static extension org.opendaylight.controller.sal.binding.codegen.RuntimeCodeSpecification.*
35
36 class RuntimeCodeGenerator {
37
38     val ClassPool classPool;
39
40     public new(ClassPool pool) {
41         classPool = pool;
42     }
43
44     def <T extends RpcService> Class<? extends T> generateDirectProxy(Class<T> iface) {
45         val supertype = iface.asCtClass
46         val targetCls = createClass(iface.directProxyName, supertype) [
47             field(DELEGATE_FIELD, iface);
48             implementMethodsFrom(supertype) [
49                 body = '''return ($r) «DELEGATE_FIELD».«it.name»($$);'''
50             ]
51         ]
52         return targetCls.toClass(iface.classLoader)
53     }
54
55     def <T extends RpcService> Class<? extends T> generateRouter(Class<T> iface) {
56         val supertype = iface.asCtClass
57         val targetCls = createClass(iface.routerName, supertype) [
58             //field(ROUTING_TABLE_FIELD,Map)
59             field(DELEGATE_FIELD, iface)
60             val contexts = new HashMap<String, Class<? extends BaseIdentity>>();
61             // We search for routing pairs and add fields
62             supertype.methods.filter[declaringClass == supertype && parameterTypes.size === 1].forEach [ method |
63                 val routingPair = method.routingContextInput;
64                 if (routingPair !== null)
65                     contexts.put(routingPair.context.routingTableField, routingPair.context);
66             ]
67             for (ctx : contexts.entrySet) {
68                 field(ctx.key, Map)
69             }
70             implementMethodsFrom(supertype) [
71                 if (parameterTypes.size === 1) {
72                     val routingPair = routingContextInput;
73                     val bodyTmp = '''
74                     {
75                         final «InstanceIdentifier.name» identifier = $1.«routingPair.getter.name»();
76                         «supertype.name» instance = («supertype.name») «routingPair.context.routingTableField».get(identifier);
77                         if(instance == null) {
78                            instance = «DELEGATE_FIELD»;
79                         }
80                         return ($r) instance.«it.name»($$);
81                     }'''
82                     body = bodyTmp
83                 } else if (parameterTypes.size === 0) {
84                     body = '''return ($r) «DELEGATE_FIELD».«it.name»($$);'''
85                 }
86             ]
87         ]
88         return targetCls.toClass(iface.classLoader)
89     }
90
91     def Class<?> generateListenerInvoker(Class<? extends NotificationListener> iface) {
92         val targetCls = createClass(iface.invokerName) [
93             field(DELEGATE_FIELD, iface)
94             it.method(Void, "invoke", Notification) [
95                 val callbacks = iface.methods.filter[notificationCallback]
96                 body = '''
97                     {
98                         «FOR callback : callbacks SEPARATOR " else "»
99                             if($1 instanceof «val cls = callback.parameterTypes.get(0).name») {
100                                 «DELEGATE_FIELD».«callback.name»((«cls») $1);
101                                 return;
102                             }
103                         «ENDFOR»
104                     }
105                 '''
106             ]
107         ]
108         return targetCls.toClass(iface.classLoader);
109     }
110
111     def void method(CtClass it, Class<?> returnType, String name, Class<?> parameter, MethodGenerator function1) {
112         val method = new CtMethod(returnType.asCtClass, name, Arrays.asList(parameter.asCtClass), it);
113         function1.process(method);
114         it.addMethod(method);
115     }
116
117     private def routingContextInput(CtMethod method) {
118         val inputClass = method.parameterTypes.get(0);
119         return inputClass.contextInstance;
120     }
121
122     private def RoutingPair getContextInstance(CtClass dataClass) {
123         for (method : dataClass.methods) {
124             if (method.parameterTypes.size === 0 && method.name.startsWith("get")) {
125                 for (annotation : method.availableAnnotations) {
126                     if (annotation instanceof RoutingContext) {
127                         return new RoutingPair((annotation as RoutingContext).value, method)
128                     }
129                 }
130             }
131         }
132         for (iface : dataClass.interfaces) {
133             val ret = getContextInstance(iface);
134             if (ret != null) return ret;
135         }
136         return null;
137     }
138
139     private def void implementMethodsFrom(CtClass target, CtClass source, MethodGenerator function1) {
140         for (method : source.methods) {
141             if (method.declaringClass == source) {
142                 val redeclaredMethod = new CtMethod(method, target, null);
143                 function1.process(redeclaredMethod);
144                 target.addMethod(redeclaredMethod);
145             }
146         }
147     }
148
149     private def CtClass createClass(String fqn, ClassGenerator cls) {
150         val target = classPool.makeClass(fqn);
151         cls.process(target);
152         return target;
153     }
154
155     private def CtClass createClass(String fqn, CtClass superInterface, ClassGenerator cls) {
156         val target = classPool.makeClass(fqn);
157         target.implementsType(superInterface);
158         cls.process(target);
159         return target;
160     }
161
162     private def void implementsType(CtClass it, CtClass supertype) {
163         checkArgument(supertype.interface, "Supertype must be interface");
164         addInterface(supertype);
165     }
166
167     private def asCtClass(Class<?> class1) {
168         classPool.get(class1);
169     }
170
171     private def CtField field(CtClass it, String name, Class<?> returnValue) {
172         val field = new CtField(returnValue.asCtClass, name, it);
173         field.modifiers = Modifier.PUBLIC
174         addField(field);
175         return field;
176     }
177
178     def get(ClassPool pool, Class<?> cls) {
179         try {
180             return pool.get(cls.name)
181         } catch (NotFoundException e) {
182             pool.appendClassPath(new LoaderClassPath(cls.classLoader));
183             return pool.get(cls.name)
184         }
185     }
186 }