private static Wrapper makeWrapper()

in dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/Wrapper.java [128:367]


    private static Wrapper makeWrapper(Class<?> c) {
        if (c.isPrimitive()) {
            throw new IllegalArgumentException("Can not create wrapper for primitive type: " + c);
        }

        String name = c.getName();
        ClassLoader cl = ClassUtils.getClassLoader(c);

        StringBuilder c1 = new StringBuilder("public void setPropertyValue(Object o, String n, Object v){ ");
        StringBuilder c2 = new StringBuilder("public Object getPropertyValue(Object o, String n){ ");
        StringBuilder c3 =
                new StringBuilder("public Object invokeMethod(Object o, String n, Class[] p, Object[] v) throws "
                        + InvocationTargetException.class.getName() + "{ ");

        c1.append(name)
                .append(" w; try{ w = ((")
                .append(name)
                .append(")$1); }catch(Throwable e){ throw new IllegalArgumentException(e); }");
        c2.append(name)
                .append(" w; try{ w = ((")
                .append(name)
                .append(")$1); }catch(Throwable e){ throw new IllegalArgumentException(e); }");
        c3.append(name)
                .append(" w; try{ w = ((")
                .append(name)
                .append(")$1); }catch(Throwable e){ throw new IllegalArgumentException(e); }");

        Map<String, Class<?>> pts = new HashMap<>(); // <property name, property types>
        Map<String, Method> ms = new LinkedHashMap<>(); // <method desc, Method instance>
        List<String> mns = new ArrayList<>(); // method names.
        List<String> dmns = new ArrayList<>(); // declaring method names.

        // get all public field.
        for (Field f : c.getFields()) {
            String fn = f.getName();
            Class<?> ft = f.getType();
            if (Modifier.isStatic(f.getModifiers())
                    || Modifier.isTransient(f.getModifiers())
                    || Modifier.isFinal(f.getModifiers())) {
                continue;
            }

            c1.append(" if( $2.equals(\"")
                    .append(fn)
                    .append("\") ){ ((")
                    .append(f.getDeclaringClass().getName())
                    .append(")w).")
                    .append(fn)
                    .append('=')
                    .append(arg(ft, "$3"))
                    .append("; return; }");
            c2.append(" if( $2.equals(\"")
                    .append(fn)
                    .append("\") ){ return ($w)((")
                    .append(f.getDeclaringClass().getName())
                    .append(")w).")
                    .append(fn)
                    .append("; }");
            pts.put(fn, ft);
        }

        final ClassPool classPool = ClassGenerator.getClassPool(cl);

        List<String> allMethod = new ArrayList<>();
        try {
            final CtMethod[] ctMethods = classPool.get(c.getName()).getMethods();
            for (CtMethod method : ctMethods) {
                allMethod.add(ReflectUtils.getDesc(method));
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        Method[] methods = Arrays.stream(c.getMethods())
                .filter(method -> allMethod.contains(ReflectUtils.getDesc(method)))
                .collect(Collectors.toList())
                .toArray(new Method[] {});
        // get all public method.
        boolean hasMethod = ClassUtils.hasMethods(methods);
        if (hasMethod) {
            Map<String, Integer> sameNameMethodCount = new HashMap<>((int) (methods.length / 0.75f) + 1);
            for (Method m : methods) {
                sameNameMethodCount.compute(m.getName(), (key, oldValue) -> oldValue == null ? 1 : oldValue + 1);
            }

            c3.append(" try{");
            for (Method m : methods) {
                // ignore Object's method.
                if (m.getDeclaringClass() == Object.class) {
                    continue;
                }

                String mn = m.getName();
                c3.append(" if( \"").append(mn).append("\".equals( $2 ) ");
                int len = m.getParameterTypes().length;
                c3.append(" && ").append(" $3.length == ").append(len);

                boolean overload = sameNameMethodCount.get(m.getName()) > 1;
                if (overload) {
                    if (len > 0) {
                        for (int l = 0; l < len; l++) {
                            c3.append(" && ")
                                    .append(" $3[")
                                    .append(l)
                                    .append("].getName().equals(\"")
                                    .append(m.getParameterTypes()[l].getName())
                                    .append("\")");
                        }
                    }
                }

                c3.append(" ) { ");

                if (m.getReturnType() == Void.TYPE) {
                    c3.append(" w.")
                            .append(mn)
                            .append('(')
                            .append(args(m.getParameterTypes(), "$4"))
                            .append(");")
                            .append(" return null;");
                } else {
                    c3.append(" return ($w)w.")
                            .append(mn)
                            .append('(')
                            .append(args(m.getParameterTypes(), "$4"))
                            .append(");");
                }

                c3.append(" }");

                mns.add(mn);
                if (m.getDeclaringClass() == c) {
                    dmns.add(mn);
                }
                ms.put(ReflectUtils.getDesc(m), m);
            }
            c3.append(" } catch(Throwable e) { ");
            c3.append("     throw new java.lang.reflect.InvocationTargetException(e); ");
            c3.append(" }");
        }

        c3.append(" throw new ")
                .append(NoSuchMethodException.class.getName())
                .append("(\"Not found method \\\"\"+$2+\"\\\" in class ")
                .append(c.getName())
                .append(".\"); }");

        // deal with get/set method.
        Matcher matcher;
        for (Map.Entry<String, Method> entry : ms.entrySet()) {
            String md = entry.getKey();
            Method method = entry.getValue();
            if ((matcher = ReflectUtils.GETTER_METHOD_DESC_PATTERN.matcher(md)).matches()) {
                String pn = propertyName(matcher.group(1));
                c2.append(" if( $2.equals(\"")
                        .append(pn)
                        .append("\") ){ return ($w)w.")
                        .append(method.getName())
                        .append("(); }");
                pts.put(pn, method.getReturnType());
            } else if ((matcher = ReflectUtils.IS_HAS_CAN_METHOD_DESC_PATTERN.matcher(md)).matches()) {
                String pn = propertyName(matcher.group(1));
                c2.append(" if( $2.equals(\"")
                        .append(pn)
                        .append("\") ){ return ($w)w.")
                        .append(method.getName())
                        .append("(); }");
                pts.put(pn, method.getReturnType());
            } else if ((matcher = ReflectUtils.SETTER_METHOD_DESC_PATTERN.matcher(md)).matches()) {
                Class<?> pt = method.getParameterTypes()[0];
                String pn = propertyName(matcher.group(1));
                c1.append(" if( $2.equals(\"")
                        .append(pn)
                        .append("\") ){ w.")
                        .append(method.getName())
                        .append('(')
                        .append(arg(pt, "$3"))
                        .append("); return; }");
                pts.put(pn, pt);
            }
        }
        c1.append(" throw new ")
                .append(NoSuchPropertyException.class.getName())
                .append("(\"Not found property \\\"\"+$2+\"\\\" field or setter method in class ")
                .append(c.getName())
                .append(".\"); }");
        c2.append(" throw new ")
                .append(NoSuchPropertyException.class.getName())
                .append("(\"Not found property \\\"\"+$2+\"\\\" field or getter method in class ")
                .append(c.getName())
                .append(".\"); }");

        // make class
        long id = WRAPPER_CLASS_COUNTER.getAndIncrement();
        ClassGenerator cc = ClassGenerator.newInstance(cl);
        cc.setClassName(c.getName() + "DubboWrap" + id);
        cc.setSuperClass(Wrapper.class);

        cc.addDefaultConstructor();
        cc.addField("public static String[] pns;"); // property name array.
        cc.addField("public static " + Map.class.getName() + " pts;"); // property type map.
        cc.addField("public static String[] mns;"); // all method name array.
        cc.addField("public static String[] dmns;"); // declared method name array.
        for (int i = 0, len = ms.size(); i < len; i++) {
            cc.addField("public static Class[] mts" + i + ";");
        }

        cc.addMethod("public String[] getPropertyNames(){ return pns; }");
        cc.addMethod("public boolean hasProperty(String n){ return pts.containsKey($1); }");
        cc.addMethod("public Class getPropertyType(String n){ return (Class)pts.get($1); }");
        cc.addMethod("public String[] getMethodNames(){ return mns; }");
        cc.addMethod("public String[] getDeclaredMethodNames(){ return dmns; }");
        cc.addMethod(c1.toString());
        cc.addMethod(c2.toString());
        cc.addMethod(c3.toString());

        try {
            Class<?> wc = cc.toClass(c);
            // setup static field.
            wc.getField("pts").set(null, pts);
            wc.getField("pns").set(null, pts.keySet().toArray(new String[0]));
            wc.getField("mns").set(null, mns.toArray(new String[0]));
            wc.getField("dmns").set(null, dmns.toArray(new String[0]));
            int ix = 0;
            for (Method m : ms.values()) {
                wc.getField("mts" + ix++).set(null, m.getParameterTypes());
            }
            return (Wrapper) wc.getDeclaredConstructor().newInstance();
        } catch (RuntimeException e) {
            throw e;
        } catch (Throwable e) {
            throw new RuntimeException(e.getMessage(), e);
        } finally {
            cc.release();
            pts.clear();
            ms.clear();
            mns.clear();
            dmns.clear();
        }
    }