refactor javassist compiler: extract class CtClassBuilder (#3424)
authorwanghbxxxx <wanghbxxxx@gmail.com>
Mon, 11 Feb 2019 08:44:41 +0000 (16:44 +0800)
committerIan Luo <ian.luo@gmail.com>
Mon, 11 Feb 2019 08:44:41 +0000 (16:44 +0800)
* refactor JavassistCompiler

* rename variable names

* reformat code

* refactor: prepend modifier of constructor, field and method outside the
JavassistClassInfo

* add null for ClassUtils.getSimpleClassName

* rename JavassistClassInfo to CtClassBuilder

dubbo-common/src/main/java/org/apache/dubbo/common/compiler/support/ClassUtils.java
dubbo-common/src/main/java/org/apache/dubbo/common/compiler/support/CtClassBuilder.java [new file with mode: 0644]
dubbo-common/src/main/java/org/apache/dubbo/common/compiler/support/JavassistCompiler.java
dubbo-common/src/test/java/org/apache/dubbo/common/compiler/support/ClassUtilsTest.java

index 5d3cdad..365cb7f 100644 (file)
@@ -431,5 +431,17 @@ public class ClassUtils {
         }\r
         return map;\r
     }\r
+    \r
+    /**\r
+     * get simple class name from qualified class name\r
+     */\r
+    public static String getSimpleClassName(String qualifiedName) {\r
+        if (null == qualifiedName) {\r
+            return null;\r
+        }\r
+        \r
+        int i = qualifiedName.lastIndexOf('.');\r
+        return i < 0 ? qualifiedName : qualifiedName.substring(i + 1);\r
+    }\r
 \r
 }\r
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/compiler/support/CtClassBuilder.java b/dubbo-common/src/main/java/org/apache/dubbo/common/compiler/support/CtClassBuilder.java
new file mode 100644 (file)
index 0000000..0dc8f88
--- /dev/null
@@ -0,0 +1,174 @@
+/*\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements.  See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License.  You may obtain a copy of the License at\r
+ *\r
+ *     http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package org.apache.dubbo.common.compiler.support;\r
+\r
+import java.util.ArrayList;\r
+import java.util.HashMap;\r
+import java.util.List;\r
+import java.util.Map;\r
+\r
+import javassist.CannotCompileException;\r
+import javassist.ClassPool;\r
+import javassist.CtClass;\r
+import javassist.CtField;\r
+import javassist.CtNewConstructor;\r
+import javassist.CtNewMethod;\r
+import javassist.LoaderClassPath;\r
+import javassist.NotFoundException;\r
+\r
+/**\r
+ * CtClassBuilder is builder for CtClass\r
+ * <p>\r
+ * contains all the information, including:\r
+ * <p>\r
+ * class name, imported packages, super class name, implemented interfaces, constructors, fields, methods.\r
+ */\r
+public class CtClassBuilder {\r
+\r
+    private String className;\r
+\r
+    private String superClassName = "java.lang.Object";\r
+\r
+    private List<String> imports = new ArrayList<>();\r
+\r
+    private Map<String, String> fullNames = new HashMap<>();\r
+\r
+    private List<String> ifaces = new ArrayList<>();\r
+\r
+    private List<String> constructors = new ArrayList<>();\r
+\r
+    private List<String> fields = new ArrayList<>();\r
+\r
+    private List<String> methods = new ArrayList<>();\r
+\r
+    public String getClassName() {\r
+        return className;\r
+    }\r
+\r
+    public void setClassName(String className) {\r
+        this.className = className;\r
+    }\r
+\r
+    public String getSuperClassName() {\r
+        return superClassName;\r
+    }\r
+\r
+    public void setSuperClassName(String superClassName) {\r
+        this.superClassName = getQualifiedClassName(superClassName);\r
+    }\r
+\r
+    public List<String> getImports() {\r
+        return imports;\r
+    }\r
+\r
+    public void addImports(String pkg) {\r
+        int pi = pkg.lastIndexOf('.');\r
+        if (pi > 0) {\r
+            String pkgName = pkg.substring(0, pi);\r
+            this.imports.add(pkgName);\r
+            if (!pkg.endsWith(".*")) {\r
+                fullNames.put(pkg.substring(pi + 1), pkg);\r
+            }\r
+        }\r
+    }\r
+\r
+    public List<String> getInterfaces() {\r
+        return ifaces;\r
+    }\r
+\r
+    public void addInterface(String iface) {\r
+        this.ifaces.add(getQualifiedClassName(iface));\r
+    }\r
+\r
+    public List<String> getConstructors() {\r
+        return constructors;\r
+    }\r
+\r
+    public void addConstructor(String constructor) {\r
+        this.constructors.add(constructor);\r
+    }\r
+\r
+    public List<String> getFields() {\r
+        return fields;\r
+    }\r
+\r
+    public void addField(String field) {\r
+        this.fields.add(field);\r
+    }\r
+\r
+    public List<String> getMethods() {\r
+        return methods;\r
+    }\r
+\r
+    public void addMethod(String method) {\r
+        this.methods.add(method);\r
+    }\r
+\r
+    /**\r
+     * get full qualified class name\r
+     * \r
+     * @param className super class name, maybe qualified or not\r
+     */\r
+    protected String getQualifiedClassName(String className) {\r
+        if (className.contains(".")) {\r
+            return className;\r
+        }\r
+\r
+        if (fullNames.containsKey(className)) {\r
+            return fullNames.get(className);\r
+        }\r
+\r
+        return ClassUtils.forName(imports.toArray(new String[0]), className).getName();\r
+    }\r
+\r
+    /**\r
+     * build CtClass object\r
+     */\r
+    public CtClass build(ClassLoader classLoader) throws NotFoundException, CannotCompileException {\r
+        ClassPool pool = new ClassPool(true);\r
+        pool.appendClassPath(new LoaderClassPath(classLoader));\r
+        \r
+        // create class\r
+        CtClass ctClass = pool.makeClass(className, pool.get(superClassName));\r
+\r
+        // add imported packages\r
+        imports.stream().forEach(pool::importPackage);\r
+\r
+        // add implemented interfaces\r
+        for (String iface : ifaces) {\r
+            ctClass.addInterface(pool.get(iface));\r
+        }\r
+\r
+        // add constructors\r
+        for (String constructor : constructors) {\r
+            ctClass.addConstructor(CtNewConstructor.make(constructor, ctClass));\r
+        }\r
+\r
+        // add fields\r
+        for (String field : fields) {\r
+            ctClass.addField(CtField.make(field, ctClass));\r
+        }\r
+\r
+        // add methods\r
+        for (String method : methods) {\r
+            ctClass.addMethod(CtNewMethod.make(method, ctClass));\r
+        }\r
+\r
+        return ctClass;\r
+    }\r
+\r
+}\r
index 5beb8e4..11f76b1 100644 (file)
@@ -18,17 +18,9 @@ package org.apache.dubbo.common.compiler.support;
 \r
 import org.apache.dubbo.common.utils.ClassHelper;\r
 \r
-import javassist.ClassPool;\r
 import javassist.CtClass;\r
-import javassist.CtField;\r
-import javassist.CtNewConstructor;\r
-import javassist.CtNewMethod;\r
-import javassist.LoaderClassPath;\r
 \r
-import java.util.ArrayList;\r
-import java.util.HashMap;\r
-import java.util.List;\r
-import java.util.Map;\r
+import java.util.Arrays;\r
 import java.util.regex.Matcher;\r
 import java.util.regex.Pattern;\r
 \r
@@ -49,77 +41,46 @@ public class JavassistCompiler extends AbstractCompiler {
 \r
     @Override\r
     public Class<?> doCompile(String name, String source) throws Throwable {\r
-        int i = name.lastIndexOf('.');\r
-        String className = i < 0 ? name : name.substring(i + 1);\r
-        ClassPool pool = new ClassPool(true);\r
-        pool.appendClassPath(new LoaderClassPath(ClassHelper.getCallerClassLoader(getClass())));\r
+        CtClassBuilder builder = new CtClassBuilder();\r
+        builder.setClassName(name);\r
+\r
+        // process imported classes\r
         Matcher matcher = IMPORT_PATTERN.matcher(source);\r
-        List<String> importPackages = new ArrayList<String>();\r
-        Map<String, String> fullNames = new HashMap<String, String>();\r
         while (matcher.find()) {\r
-            String pkg = matcher.group(1);\r
-            if (pkg.endsWith(".*")) {\r
-                String pkgName = pkg.substring(0, pkg.length() - 2);\r
-                pool.importPackage(pkgName);\r
-                importPackages.add(pkgName);\r
-            } else {\r
-                int pi = pkg.lastIndexOf('.');\r
-                if (pi > 0) {\r
-                    String pkgName = pkg.substring(0, pi);\r
-                    pool.importPackage(pkgName);\r
-                    importPackages.add(pkgName);\r
-                    fullNames.put(pkg.substring(pi + 1), pkg);\r
-                }\r
-            }\r
+            builder.addImports(matcher.group(1).trim());\r
         }\r
-        String[] packages = importPackages.toArray(new String[0]);\r
+        \r
+        // process extended super class\r
         matcher = EXTENDS_PATTERN.matcher(source);\r
-        CtClass cls;\r
         if (matcher.find()) {\r
-            String extend = matcher.group(1).trim();\r
-            String extendClass;\r
-            if (extend.contains(".")) {\r
-                extendClass = extend;\r
-            } else if (fullNames.containsKey(extend)) {\r
-                extendClass = fullNames.get(extend);\r
-            } else {\r
-                extendClass = ClassUtils.forName(packages, extend).getName();\r
-            }\r
-            cls = pool.makeClass(name, pool.get(extendClass));\r
-        } else {\r
-            cls = pool.makeClass(name);\r
+            builder.setSuperClassName(matcher.group(1).trim());\r
         }\r
+        \r
+        // process implemented interfaces\r
         matcher = IMPLEMENTS_PATTERN.matcher(source);\r
         if (matcher.find()) {\r
             String[] ifaces = matcher.group(1).trim().split("\\,");\r
-            for (String iface : ifaces) {\r
-                iface = iface.trim();\r
-                String ifaceClass;\r
-                if (iface.contains(".")) {\r
-                    ifaceClass = iface;\r
-                } else if (fullNames.containsKey(iface)) {\r
-                    ifaceClass = fullNames.get(iface);\r
-                } else {\r
-                    ifaceClass = ClassUtils.forName(packages, iface).getName();\r
-                }\r
-                cls.addInterface(pool.get(ifaceClass));\r
-            }\r
+            Arrays.stream(ifaces).forEach(i -> builder.addInterface(i.trim()));\r
         }\r
-        String body = source.substring(source.indexOf("{") + 1, source.length() - 1);\r
+        \r
+        // process constructors, fields, methods\r
+        String body = source.substring(source.indexOf('{') + 1, source.length() - 1);\r
         String[] methods = METHODS_PATTERN.split(body);\r
-        for (String method : methods) {\r
-            method = method.trim();\r
-            if (method.length() > 0) {\r
-                if (method.startsWith(className)) {\r
-                    cls.addConstructor(CtNewConstructor.make("public " + method, cls));\r
-                } else if (FIELD_PATTERN.matcher(method).matches()) {\r
-                    cls.addField(CtField.make("private " + method, cls));\r
-                } else {\r
-                    cls.addMethod(CtNewMethod.make("public " + method, cls));\r
-                }\r
+        String className = ClassUtils.getSimpleClassName(name);\r
+        Arrays.stream(methods).map(String::trim).filter(m -> !m.isEmpty()).forEach(method-> {\r
+            if (method.startsWith(className)) {\r
+                builder.addConstructor("public " + method);\r
+            } else if (FIELD_PATTERN.matcher(method).matches()) {\r
+                builder.addField("private " + method);\r
+            } else {\r
+                builder.addMethod("public " + method);\r
             }\r
-        }\r
-        return cls.toClass(ClassHelper.getCallerClassLoader(getClass()), JavassistCompiler.class.getProtectionDomain());\r
+        });\r
+        \r
+        // compile\r
+        ClassLoader classLoader = ClassHelper.getCallerClassLoader(getClass());\r
+        CtClass cls = builder.build(classLoader);\r
+        return cls.toClass(classLoader, JavassistCompiler.class.getProtectionDomain());\r
     }\r
 \r
 }\r
index 2ebf668..8acf00b 100644 (file)
@@ -146,6 +146,13 @@ public class ClassUtilsTest {
     public void testGetSizeMethod() {
         Assertions.assertEquals("getLength()", ClassUtils.getSizeMethod(GenericClass3.class));
     }
+    
+    @Test
+    public void testGetSimpleClassName() {
+        Assertions.assertNull(ClassUtils.getSimpleClassName(null));
+        Assertions.assertEquals("Map", ClassUtils.getSimpleClassName(Map.class.getName()));
+        Assertions.assertEquals("Map", ClassUtils.getSimpleClassName(Map.class.getSimpleName()));
+    }
 
     private interface GenericInterface<T> {
     }