2 * Copyright (c) 2019 PANTHEON.tech, s.r.o. and others. All rights reserved.
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
8 package org.opendaylight.mdsal.binding.dom.codec.impl;
10 import static java.util.Objects.requireNonNull;
12 import com.google.common.base.MoreObjects.ToStringHelper;
13 import com.google.common.collect.ImmutableList;
14 import java.lang.reflect.Method;
15 import java.lang.reflect.Modifier;
16 import java.util.List;
17 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
18 import javassist.CannotCompileException;
19 import javassist.CtClass;
20 import javassist.CtField;
21 import javassist.CtMethod;
22 import javassist.NotFoundException;
23 import org.opendaylight.mdsal.binding.dom.codec.loader.CodecClassLoader;
24 import org.opendaylight.mdsal.binding.dom.codec.loader.CodecClassLoader.Customizer;
25 import org.opendaylight.mdsal.binding.dom.codec.loader.StaticClassPool;
26 import org.opendaylight.yangtools.yang.binding.DataObject;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
31 * Private support for generating AbstractDataObject specializations.
33 final class CodecDataObjectCustomizer implements Customizer {
34 private static final Logger LOG = LoggerFactory.getLogger(CodecDataObjectCustomizer.class);
35 private static final CtClass CT_ARFU = StaticClassPool.findClass(AtomicReferenceFieldUpdater.class);
36 private static final CtClass CT_BOOLEAN = StaticClassPool.findClass(boolean.class);
37 private static final CtClass CT_INT = StaticClassPool.findClass(int.class);
38 private static final CtClass CT_OBJECT = StaticClassPool.findClass(Object.class);
39 private static final CtClass CT_HELPER = StaticClassPool.findClass(ToStringHelper.class);
40 private static final CtClass CT_DATAOBJECT = StaticClassPool.findClass(DataObject.class);
41 private static final CtClass[] EMPTY_ARGS = new CtClass[0];
42 private static final CtClass[] EQUALS_ARGS = new CtClass[] { CT_DATAOBJECT };
43 private static final CtClass[] TOSTRING_ARGS = new CtClass[] { CT_HELPER };
45 private final List<Method> properties;
46 private final List<Method> methods;
48 CodecDataObjectCustomizer(final List<Method> properties, final List<Method> methods) {
49 this.properties = requireNonNull(properties);
50 this.methods = requireNonNull(methods);
54 public List<Class<?>> customize(final CodecClassLoader loader, final CtClass bindingClass, final CtClass generated)
55 throws NotFoundException, CannotCompileException {
56 final String classFqn = generated.getName();
57 generated.addInterface(bindingClass);
59 // Generate members for all methods ...
60 LOG.trace("Generating class {}", classFqn);
61 for (Method method : methods) {
62 LOG.trace("Generating for method {}", method);
63 final String methodName = method.getName();
64 final String methodArfu = methodName + "$$$ARFU";
66 // AtomicReferenceFieldUpdater ...
67 final CtField arfuField = new CtField(CT_ARFU, methodArfu, generated);
68 arfuField.setModifiers(Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL);
69 generated.addField(arfuField, new StringBuilder().append(CT_ARFU.getName()).append(".newUpdater(")
70 .append(generated.getName()).append(".class, java.lang.Object.class, \"").append(methodName)
71 .append("\")").toString());
73 // ... corresponding volatile field ...
74 final CtField field = new CtField(CT_OBJECT, methodName, generated);
75 field.setModifiers(Modifier.PRIVATE | Modifier.VOLATILE);
76 generated.addField(field);
79 final Class<?> retType = method.getReturnType();
80 final CtMethod getter = new CtMethod(loader.findClass(retType), methodName, EMPTY_ARGS, generated);
81 final String retName = retType.isArray() ? retType.getSimpleName() : retType.getName();
83 getter.setBody(new StringBuilder()
85 .append("return (").append(retName).append(") codecMember(").append(methodArfu).append(", \"")
86 .append(methodName).append("\");\n")
87 .append('}').toString());
88 getter.setModifiers(Modifier.PUBLIC | Modifier.FINAL);
89 generated.addMethod(getter);
92 // Final bits: codecHashCode() ...
93 final CtMethod codecHashCode = new CtMethod(CT_INT, "codecHashCode", EMPTY_ARGS, generated);
94 codecHashCode.setModifiers(Modifier.PROTECTED | Modifier.FINAL);
95 codecHashCode.setBody(codecHashCodeBody());
96 generated.addMethod(codecHashCode);
99 final CtMethod codecEquals = new CtMethod(CT_BOOLEAN, "codecEquals", EQUALS_ARGS, generated);
100 codecEquals.setModifiers(Modifier.PROTECTED | Modifier.FINAL);
101 codecEquals.setBody(codecEqualsBody(bindingClass.getName()));
102 generated.addMethod(codecEquals);
104 // ... and codecFillToString()
105 final CtMethod codecFillToString = new CtMethod(CT_HELPER, "codecFillToString", TOSTRING_ARGS, generated);
106 codecFillToString.setModifiers(Modifier.PROTECTED | Modifier.FINAL);
107 codecFillToString.setBody(codecFillToStringBody());
108 generated.addMethod(codecFillToString);
110 generated.setModifiers(Modifier.FINAL | Modifier.PUBLIC);
111 return ImmutableList.of();
114 private String codecHashCodeBody() {
115 final StringBuilder sb = new StringBuilder()
117 .append("final int prime = 31;\n")
118 .append("int result = 1;\n");
120 for (Method method : properties) {
121 sb.append("result = prime * result + java.util.").append(utilClass(method)).append(".hashCode(")
122 .append(method.getName()).append("());\n");
125 return sb.append("return result;\n")
126 .append('}').toString();
129 private String codecEqualsBody(final String ifaceName) {
130 final StringBuilder sb = new StringBuilder()
132 .append("final ").append(ifaceName).append(" other = $1;")
133 .append("return true");
135 for (Method method : properties) {
136 final String methodName = method.getName();
137 sb.append("\n&& java.util.").append(utilClass(method)).append(".equals(").append(methodName)
138 .append("(), other.").append(methodName).append("())");
141 return sb.append(";\n")
142 .append('}').toString();
145 private String codecFillToStringBody() {
146 final StringBuilder sb = new StringBuilder()
148 .append("return $1");
149 for (Method method : properties) {
150 final String methodName = method.getName();
151 sb.append("\n.add(\"").append(methodName).append("\", ").append(methodName).append("())");
154 return sb.append(";\n")
155 .append('}').toString();
158 private static String utilClass(final Method method) {
159 // We can either have objects or byte[], we cannot have Object[]
160 return method.getReturnType().isArray() ? "Arrays" : "Objects";