Search in sources :

Example 1 with Service

use of com.didi.drouter.annotation.Service in project DRouter by didi.

the class ServiceCollect method generate.

@Override
public void generate(File routerDir) throws Exception {
    CtClass ctClass = pool.makeClass(getPackageName() + ".ServiceLoader");
    CtClass superClass = pool.get("com.didi.drouter.store.MetaLoader");
    ctClass.setSuperclass(superClass);
    StringBuilder builder = new StringBuilder();
    builder.append("public void load(java.util.Map data) {\n");
    CtClass featureInterface = pool.get("com.didi.drouter.service.IFeatureMatcher");
    for (CtClass serviceCc : serviceClass.values()) {
        try {
            if (isNonStaticInnerClass(serviceCc)) {
                throw new Exception("Annotation can not use non static inner class");
            }
            Annotation annotation = getAnnotation(serviceCc, Service.class);
            ArrayMemberValue functionValue = (ArrayMemberValue) annotation.getMemberValue("function");
            String[] aliasValue = ((Service) serviceCc.getAnnotation(Service.class)).alias();
            ArrayMemberValue featureValue = (ArrayMemberValue) annotation.getMemberValue("feature");
            int priority = ((Service) serviceCc.getAnnotation(Service.class)).priority();
            int cacheValue = ((Service) serviceCc.getAnnotation(Service.class)).cache();
            boolean anyAbility = false;
            List<CtClass> functionCcList = new ArrayList<>();
            for (int i = 0; i < functionValue.getValue().length; i++) {
                ClassMemberValue functionCmv = (ClassMemberValue) functionValue.getValue()[i];
                if ("com.didi.drouter.service.AnyAbility".equals(functionCmv.getValue())) {
                    anyAbility = true;
                } else {
                    CtClass functionCc = getCtClass(functionCmv.getValue());
                    functionCcList.add(functionCc);
                    String[] superClassNames;
                    if ("com.didi.drouter.service.ICallService".equals(functionCmv.getValue())) {
                        superClassNames = new String[11];
                        for (int j = 0; j < 10; j++) {
                            superClassNames[j] = "com.didi.drouter.service.ICallService" + j;
                        }
                        superClassNames[10] = "com.didi.drouter.service.ICallServiceN";
                    } else {
                        superClassNames = new String[] { functionCc.getName() };
                    }
                    // ICallService should use unique alias, for using alias to determine which service
                    if (functionCmv.getValue().startsWith("com.didi.drouter.service.ICallService")) {
                        if (i <= aliasValue.length - 1) {
                            String duplicate = StoreUtil.insertCallAlias(aliasValue[i], serviceCc);
                            if (duplicate != null) {
                                throw new Exception("ICallService can't use the same alias with" + duplicate);
                            }
                        }
                    }
                    if (!checkSuper(serviceCc, superClassNames)) {
                        throw new Exception("@Service with function does not match interface");
                    }
                }
            }
            if (anyAbility) {
                functionCcList.clear();
                functionCcList.addAll(collectSuper(serviceCc));
                if (aliasValue.length > 1) {
                    throw new Exception("only use one alias at most to match AnyAbility");
                }
                if (featureValue != null && featureValue.getValue().length > 1) {
                    throw new Exception("only use one feature at most to match AnyAbility");
                }
            }
            // traverse all the function argument, one function corresponding one function->(impl,feature) data
            for (int i = 0; i < functionCcList.size(); i++) {
                CtClass functionCc = functionCcList.get(i);
                String alias = "";
                if (aliasValue.length == 1) {
                    alias = aliasValue[0];
                } else if (i < aliasValue.length) {
                    // affirm no AnyAbility
                    alias = aliasValue[i];
                }
                // one feature generator one feature matcher class
                CtClass featureCc = null;
                CtClass featureMatchCc = null;
                if (featureValue != null) {
                    if (featureValue.getValue().length == 1) {
                        ClassMemberValue featureCmv = (ClassMemberValue) featureValue.getValue()[0];
                        featureCc = pool.get(featureCmv.getValue());
                    } else if (i < featureValue.getValue().length) {
                        // affirm no AnyAbility
                        ClassMemberValue featureCmv = (ClassMemberValue) featureValue.getValue()[i];
                        featureCc = pool.get(featureCmv.getValue());
                    }
                }
                if (featureCc != null) {
                    // avoid class duplication
                    String featureMatcher = MATCH + serviceCc.getName().replace(".", "_") + "__" + featureCc.getSimpleName();
                    featureMatchCc = pool.getOrNull(featureMatcher);
                    if (featureMatchCc == null) {
                        featureMatchCc = pool.makeClass(featureMatcher);
                        featureMatchCc.addInterface(featureInterface);
                        StringBuilder featureBuilder = new StringBuilder();
                        featureBuilder.append("\npublic boolean match(Object obj) {");
                        featureBuilder.append("\n    return obj instanceof ");
                        featureBuilder.append(featureCc.getName());
                        completeMatchMethod(serviceCc, featureCc, featureBuilder);
                        featureBuilder.append(";\n}");
                        // Logger.d(featureBuilder.toString());
                        generatorClass(routerDir, featureMatchCc, featureBuilder.toString());
                    }
                }
                CtClass methodProxyCc = null;
                String constructorMethod = null;
                String executeMethod = null;
                try {
                    CtConstructor constructor = serviceCc.getDeclaredConstructor(null);
                    if (constructor != null) {
                        constructorMethod = String.format("public java.lang.Object newInstance(android.content.Context context) {" + "{  return new %s();} }", serviceCc.getName());
                    }
                } catch (NotFoundException ignore) {
                }
                CtMethod[] ctMethods = serviceCc.getMethods();
                if (ctMethods != null) {
                    StringBuilder allIfStr = new StringBuilder();
                    Set<String> methodNames = new HashSet<>();
                    for (CtMethod method : ctMethods) {
                        boolean add = methodNames.add(method.getName() + "_$$_" + method.getParameterTypes().length);
                        Remote remote = (Remote) method.getAnnotation(Remote.class);
                        if (remote != null) {
                            if (!add) {
                                throw new Exception(String.format("The method \"%s\" with @Remote " + "can't be same name and same parameter count", method.getName()));
                            }
                            CtClass returnCc = method.getReturnType();
                            checkPrimitiveType(method.getName(), returnCc);
                            CtClass[] paraTypeCts = method.getParameterTypes();
                            StringBuilder para = new StringBuilder();
                            if (paraTypeCts != null) {
                                // argument type
                                for (int j = 0; j < paraTypeCts.length; j++) {
                                    checkPrimitiveType(method.getName(), paraTypeCts[j]);
                                    para.append(String.format("(%s) (args[%s])", paraTypeCts[j].getName(), j));
                                    if (j != paraTypeCts.length - 1) {
                                        para.append(",");
                                    }
                                }
                            }
                            // return type
                            if (!"void".equals(returnCc.getName())) {
                                allIfStr.append(String.format("if (\"%s\".equals(methodName)) { return ((%s)instance).%s(%s); }", method.getName() + "_$$_" + method.getParameterTypes().length, serviceCc.getName(), method.getName(), para));
                            } else {
                                allIfStr.append(String.format("if (\"%s\".equals(methodName)) { ((%s)instance).%s(%s); return null; }", method.getName() + "_$$_" + method.getParameterTypes().length, serviceCc.getName(), method.getName(), para));
                            }
                        }
                    }
                    executeMethod = String.format("public java.lang.Object execute(Object instance, String methodName, Object[] " + "args) {" + "%s" + "throw " + "new com.didi.drouter.store.IRouterProxy.RemoteMethodMatchException();" + "}", allIfStr);
                }
                if (constructorMethod != null || executeMethod != null) {
                    CtClass proxyInterface = pool.get("com.didi.drouter.store.IRouterProxy");
                    String path = PROXY + serviceCc.getName().replace(".", "_");
                    methodProxyCc = pool.getOrNull(path);
                    if (methodProxyCc == null) {
                        methodProxyCc = pool.makeClass(path);
                        methodProxyCc.addInterface(proxyInterface);
                        generatorClass(routerDir, methodProxyCc, constructorMethod == null ? METHOD1 : constructorMethod, executeMethod == null ? METHOD2 : executeMethod);
                    }
                }
                StringBuilder itemBuilder = new StringBuilder();
                itemBuilder.append("    put(");
                itemBuilder.append(functionCc.getName());
                itemBuilder.append(".class, com.didi.drouter.store.RouterMeta.build(");
                itemBuilder.append("com.didi.drouter.store.RouterMeta.SERVICE)");
                itemBuilder.append(".assembleService(");
                itemBuilder.append(serviceCc.getName());
                itemBuilder.append(".class, ");
                itemBuilder.append(methodProxyCc != null ? "new " + methodProxyCc.getName() + "()" : "null");
                itemBuilder.append(", \"");
                itemBuilder.append(alias);
                itemBuilder.append("\",");
                itemBuilder.append(featureMatchCc != null ? "new " + featureMatchCc.getName() + "()" : "null");
                itemBuilder.append(",");
                itemBuilder.append(priority);
                itemBuilder.append(",");
                itemBuilder.append(cacheValue);
                itemBuilder.append(")");
                itemBuilder.append(", data);\n");
                items.add(itemBuilder.toString());
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception("Class: === " + serviceCc.getName() + " ===\nCause: " + e.getMessage());
        }
    }
    Collections.sort(items);
    for (String item : items) {
        builder.append(item);
    }
    builder.append("}");
    Logger.d("\nclass ServiceLoader" + "\n" + builder.toString());
    generatorClass(routerDir, ctClass, builder.toString());
}
Also used : ArrayList(java.util.ArrayList) Service(com.didi.drouter.annotation.Service) NotFoundException(javassist.NotFoundException) Remote(com.didi.drouter.annotation.Remote) NotFoundException(javassist.NotFoundException) Annotation(javassist.bytecode.annotation.Annotation) ClassMemberValue(javassist.bytecode.annotation.ClassMemberValue) CtConstructor(javassist.CtConstructor) CtClass(javassist.CtClass) ArrayMemberValue(javassist.bytecode.annotation.ArrayMemberValue) CtMethod(javassist.CtMethod) HashSet(java.util.HashSet)

Aggregations

Remote (com.didi.drouter.annotation.Remote)1 Service (com.didi.drouter.annotation.Service)1 ArrayList (java.util.ArrayList)1 HashSet (java.util.HashSet)1 CtClass (javassist.CtClass)1 CtConstructor (javassist.CtConstructor)1 CtMethod (javassist.CtMethod)1 NotFoundException (javassist.NotFoundException)1 Annotation (javassist.bytecode.annotation.Annotation)1 ArrayMemberValue (javassist.bytecode.annotation.ArrayMemberValue)1 ClassMemberValue (javassist.bytecode.annotation.ClassMemberValue)1