public static MetaMethod findCaseInsensitiveMatch( final MetaClass retType, final MetaClass clazz, final String name, final MetaClass... parms) { MetaClass c = clazz; do { Outer: for (final MetaMethod method : c.getDeclaredMethods()) { if (name.equalsIgnoreCase(method.getName())) { if (parms.length != method.getParameters().length) continue; final MetaParameter[] mps = method.getParameters(); for (int i = 0; i < parms.length; i++) { if (!parms[i] .getFullyQualifiedName() .equals(mps[i].getType().getFullyQualifiedName())) { continue Outer; } } if (retType != null && !retType .getFullyQualifiedName() .equals(method.getReturnType().getFullyQualifiedName())) { continue; } return method; } } } while ((c = c.getSuperClass()) != null); return null; }
/** * Ensures the provided type is a {@link DataBinder} and throws a {@link GenerationException} in * case it's not. * * @param type the type to check */ private static void assertTypeIsDataBinder(MetaClass type) { final MetaClass databinderMetaClass = MetaClassFactory.get(DataBinder.class); if (!databinderMetaClass.isAssignableFrom(type)) { throw new GenerationException( "Type of @AutoBound element must be " + DataBinder.class.getName() + " but is: " + type.getFullyQualifiedName()); } }
private void createValidationError(Collection<MetaClass> pages, Class<?> role) { StringBuilder builder = new StringBuilder(); for (MetaClass mc : pages) { builder.append("\n ").append(mc.getFullyQualifiedName()); } throw new GenerationException( "Found more than one @Page with role = '" + role + "': " + builder + "\nExactly one @Page class must be designated with this unique role."); }
public static void assertAssignableTypes( final Context context, final MetaClass from, final MetaClass to) { if (!to.asBoxed().isAssignableFrom(from.asBoxed())) { if (to.isArray() && from.isArray() && GenUtil.getArrayDimensions(to) == GenUtil.getArrayDimensions(from) && to.getOuterComponentType().isAssignableFrom(from.getOuterComponentType())) { return; } if (!context.isPermissiveMode()) { if (classAliases.contains(from.getFullyQualifiedName()) && classAliases.contains(to.getFullyQualifiedName())) { // handle convertibility between MetaClass API and java Class reference. return; } throw new InvalidTypeException( to.getFullyQualifiedName() + " is not assignable from " + from.getFullyQualifiedName()); } } }
private Statement defaultValueStatement(MetaClass type) { if (type.isPrimitive()) { if (type.asBoxed().isAssignableTo(Number.class)) { return Stmt.castTo(type, Stmt.load(0)); } else if (type.isAssignableTo(boolean.class)) { return Stmt.load(false); } else { throw new UnsupportedOperationException( "Don't know how to make a default value for @PageState field of type " + type.getFullyQualifiedName()); } } else { return Stmt.load(null); } }
public static boolean isPrimitiveWrapper(final MetaClass clazz) { return Integer.class.getName().equals(clazz.getFullyQualifiedName()) || Boolean.class.getName().equals(clazz.getFullyQualifiedName()) || Long.class.getName().equals(clazz.getFullyQualifiedName()) || Double.class.getName().equals(clazz.getFullyQualifiedName()) || Float.class.getName().equals(clazz.getFullyQualifiedName()) || Short.class.getName().equals(clazz.getFullyQualifiedName()) || Character.class.getName().equals(clazz.getFullyQualifiedName()) || Byte.class.getName().equals(clazz.getFullyQualifiedName()); }
public static Statement convert(final Context context, Object input, final MetaClass targetType) { try { if (input instanceof Statement) { if (input instanceof LiteralValue<?>) { input = ((LiteralValue<?>) input).getValue(); } else { if ("null".equals(((Statement) input).generate(context))) { return (Statement) input; } assertAssignableTypes(context, ((Statement) input).getType(), targetType); return (Statement) input; } } if (input != null && MetaClassFactory.get(input.getClass()) .getOuterComponentType() .getFullyQualifiedName() .equals(MetaClass.class.getName())) { return generate(context, input); } if (Object.class.getName().equals(targetType.getFullyQualifiedName())) { return generate(context, input); } Class<?> inputClass = input == null ? Object.class : input.getClass(); if (MetaClass.class.isAssignableFrom(inputClass)) { inputClass = Class.class; } final Class<?> targetClass = targetType.asBoxed().asClass(); if (NullType.class.getName().equals(targetClass.getName())) { return generate(context, input); } if (!targetClass.isAssignableFrom(inputClass) && DataConversion.canConvert(targetClass, inputClass)) { return generate(context, DataConversion.convert(input, targetClass)); } else { return generate(context, input); } } catch (NumberFormatException nfe) { throw new InvalidTypeException(nfe); } }
private static Statement paramFromStringStatement(MetaClass toType, Statement stringValue) { // make sure it's really a string stringValue = Stmt.castTo(String.class, stringValue); if (toType.isAssignableTo(String.class)) { return stringValue; } else if (toType.asBoxed().isAssignableTo(Number.class)) { return Stmt.invokeStatic(toType.asBoxed(), "valueOf", stringValue); } else if (toType.asBoxed().isAssignableTo(Boolean.class)) { return Stmt.invokeStatic(Boolean.class, "valueOf", stringValue); } else { throw new UnsupportedOperationException( "@PageState fields of type " + toType.getFullyQualifiedName() + " are not supported"); } }
public static MetaClass getUnboxedFromWrapper(final MetaClass clazz) { if (Integer.class.getName().equals(clazz.getFullyQualifiedName())) { return MetaClassFactory.get(int.class); } else if (Boolean.class.getName().equals(clazz.getFullyQualifiedName())) { return MetaClassFactory.get(boolean.class); } else if (Long.class.getName().equals(clazz.getFullyQualifiedName())) { return MetaClassFactory.get(long.class); } else if (Double.class.getName().equals(clazz.getFullyQualifiedName())) { return MetaClassFactory.get(double.class); } else if (Float.class.getName().equals(clazz.getFullyQualifiedName())) { return MetaClassFactory.get(float.class); } else if (Short.class.getName().equals(clazz.getFullyQualifiedName())) { return MetaClassFactory.get(short.class); } else if (Character.class.getName().equals(clazz.getFullyQualifiedName())) { return MetaClassFactory.get(char.class); } else if (Byte.class.getName().equals(clazz.getFullyQualifiedName())) { return MetaClassFactory.get(byte.class); } return clazz; }
@Override public MetaClassMember getReadingMember() { if (readingMember != null) { return readingMember; } MetaMethod meth = toMap.getMethod(getterMethod, new MetaClass[0]); meth.asMethod().setAccessible(true); readingMember = meth; if (readingMember == null) { throw new RuntimeException( "no such getter method: " + toMap.getFullyQualifiedName() + "." + getterMethod); } return readingMember; }
BuildMetaClass make(final String proxyClassName, final MetaClass toProxy, final String privateAccessorType) { final ClassStructureBuilder builder; final boolean renderEqualsAndHash; if (!toProxy.isInterface()) { renderEqualsAndHash = true; if (toProxy.isFinal()) { throw new UnproxyableClassException(toProxy, toProxy.getFullyQualifiedName() + " is an unproxiable class because it is final"); } if (!toProxy.isDefaultInstantiable()) { throw new UnproxyableClassException(toProxy, toProxy.getFullyQualifiedName() + " must have a default " + "no-arg constructor"); } builder = ClassBuilder.define(proxyClassName, toProxy).publicScope().body(); } else { renderEqualsAndHash = false; builder = ClassBuilder.define(proxyClassName).publicScope().implementsInterface(toProxy).body(); } final String proxyVar = "$$_proxy_$$"; final Set<String> renderedMethods = new HashSet<String>(); final Map<String, MetaType> typeVariableMap = new HashMap<String, MetaType>(); final MetaParameterizedType metaParameterizedType = toProxy.getParameterizedType(); if (metaParameterizedType != null) { int i = 0; for (final MetaTypeVariable metaTypeVariable : toProxy.getTypeParameters()) { typeVariableMap.put(metaTypeVariable.getName(), metaParameterizedType.getTypeParameters()[i++]); } } builder.privateField(proxyVar, toProxy).finish(); final Set<Map.Entry<String, ProxyProperty>> entries = proxyProperties.entrySet(); for (final Map.Entry<String, ProxyProperty> entry : entries) { builder.privateField(entry.getValue().getEncodedProperty(), entry.getValue().getType()).finish(); builder.packageMethod(void.class, "$set_" + entry.getKey(), Parameter.of(entry.getValue().getType(), "o")) .append(Stmt.loadVariable(entry.getValue().getEncodedProperty()).assignValue(Refs.get("o"))) .finish(); } for (final MetaMethod method : toProxy.getMethods()) { final String methodString = GenUtil.getMethodString(method); if (renderedMethods.contains(methodString) || method.getName().equals("hashCode") || (method.getName().equals("equals") && method.getParameters().length == 1 && method.getParameters()[0].getType().getFullyQualifiedName().equals(Object.class.getName()))) continue; renderedMethods.add(methodString); if ((!method.isPublic() && !method.isProtected()) || method.isSynthetic() || method.isFinal() || method.isStatic() || method.getDeclaringClass().getFullyQualifiedName().equals(Object.class.getName())) continue; final List<Parameter> methodParms = new ArrayList<Parameter>(); final MetaParameter[] parameters = method.getParameters(); for (int i = 0; i < parameters.length; i++) { methodParms.add(Parameter.of(parameters[i].getType().getErased(), "a" + i)); } final DefParameters defParameters = DefParameters.fromParameters(methodParms); final BlockBuilder methBody = builder.publicMethod(method.getReturnType(), method.getName()) .annotatedWith(OVERRIDE_ANNOTATION) .parameters(defParameters) .throws_(method.getCheckedExceptions()); methBody.appendAll(getAroundInvokeStatements(method)); methBody.appendAll(getBeforeStatements(method)); final List<Parameter> parms = defParameters.getParameters(); final Statement[] statementVars = new Statement[parms.size()]; for (int i = 0; i < parms.size(); i++) { statementVars[i] = loadVariable(parms.get(i).getName()); } if (!method.isPublic()) { PrivateAccessUtil.addPrivateAccessStubs(privateAccessorType, builder, method, new Modifier[0]); final Statement[] privateAccessStmts = new Statement[statementVars.length + 1]; privateAccessStmts[0] = Refs.get(proxyVar); System.arraycopy(statementVars, 0, privateAccessStmts, 1, statementVars.length); if (method.getReturnType().isVoid()) { methBody._(loadVariable("this").invoke(PrivateAccessUtil.getPrivateMethodName(method), privateAccessStmts)); } else { methBody._(loadVariable("this").invoke(PrivateAccessUtil.getPrivateMethodName(method), privateAccessStmts).returnValue()); } } else { if (method.getReturnType().isVoid()) { methBody._(loadVariable(proxyVar).invoke(method, statementVars)); } else { methBody._(loadVariable(proxyVar).invoke(method, statementVars).returnValue()); } } methBody.appendAll(getAfterStatements(method)); methBody.appendAll(getAroundInvokeStatements(method)); methBody.finish(); } if (renderEqualsAndHash) { // implement hashCode() builder.publicMethod(int.class, "hashCode") .annotatedWith(OVERRIDE_ANNOTATION) .body() ._( If.isNull(loadVariable(proxyVar)) ._(throw_(IllegalStateException.class, "call to hashCode() on an unclosed proxy.")) .finish() .else_() ._(Stmt.loadVariable(proxyVar).invoke("hashCode").returnValue()) .finish() ) .finish(); // implements equals() builder.publicMethod(boolean.class, "equals", Parameter.of(Object.class, "o")) .annotatedWith(OVERRIDE_ANNOTATION) .body() ._( If.isNull(loadVariable(proxyVar)) ._(throw_(IllegalStateException.class, "call to equals() on an unclosed proxy.")) .finish() .else_() ._(Stmt.loadVariable(proxyVar).invoke("equals", Refs.get("o")).returnValue()) .finish() ) .finish(); } builder.publicMethod(void.class, PROXY_BIND_METHOD).parameters(DefParameters.of(Parameter.of(toProxy, "proxy"))) ._(loadVariable(proxyVar).assignValue(loadVariable("proxy"))).finish(); return builder.getClassDefinition(); }
public static ReachableTypes getAllReachableClasses(final GeneratorContext context) { if (System.getProperty(SYSPROP_USE_REACHABILITY_ANALYSIS) == null || !Boolean.getBoolean(SYSPROP_USE_REACHABILITY_ANALYSIS)) { log.warn("reachability analysis disabled. errai may generate unnecessary code."); log.warn( "enable reachability analysis with -D" + SYSPROP_USE_REACHABILITY_ANALYSIS + "=true"); return ReachableTypes.EVERYTHING_REACHABLE_INSTANCE; } ReachabilityCache cache; if (reachabilityCache == null || (cache = reachabilityCache.get()) == null) { reachabilityCache = new SoftReference<ReachabilityCache>(cache = new ReachabilityCache()); } if (cache.isCacheValid(context)) { return cache.getCache(context); } final EnvironmentConfig config = getEnvironmentConfig(); long time = System.currentTimeMillis(); final Set<String> packages = new HashSet<String>(); if (isJUnitTest()) { packages.addAll(RebindUtils.findTranslatablePackagesInModule(context)); } else { packages.addAll(RebindUtils.getOuterTranslatablePackages(context)); } class Reachability { private final Set<String> packages; private final Set<String> negativeHits = new HashSet<String>(); Reachability(final Set<String> packages) { this.packages = new HashSet<String>(packages); } public boolean isReachablePackage(final String pkg) { if (pkg == null || packages.contains(pkg)) { return true; } if (negativeHits.contains(pkg)) { return false; } for (final String p : packages) { if (pkg.startsWith(p)) { packages.add(pkg); return true; } } negativeHits.add(pkg); return false; } } final Set<String> allDependencies = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>(100)); final Collection<MetaClass> allCachedClasses = MetaClassFactory.getAllCachedClasses(); final ClassLoader classLoader = EnvUtil.class.getClassLoader(); final ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); final Reachability reachability = new Reachability(packages); try { for (final MetaClass mc : allCachedClasses) { String fullyQualifiedName = mc.getFullyQualifiedName(); int splitPoint; while ((splitPoint = fullyQualifiedName.lastIndexOf('$')) != -1) { fullyQualifiedName = fullyQualifiedName.substring(0, splitPoint); } if (mc.isPrimitive() || mc.isArray()) { continue; } else if (isReachabilityExcluded(mc.getPackageName())) { continue; } else if (!config.getExplicitTypes().contains(fullyQualifiedName) && !reachability.isReachablePackage(mc.getPackageName())) { continue; } final URL resource = classLoader.getResource(fullyQualifiedName.replaceAll("\\.", "/") + ".java"); if (resource != null) { InputStream stream = null; try { stream = new BufferedInputStream(resource.openStream()); final byte[] readBuffer = new byte[stream.available()]; stream.read(readBuffer); if (log.isDebugEnabled()) { log.debug("scanning " + fullyQualifiedName + " for reachable types ..."); } executor.execute(new ReachabilityRunnable(readBuffer, allDependencies)); } catch (IOException e) { log.warn("could not open resource: " + resource.getFile()); } finally { if (stream != null) { stream.close(); } } } else { log.warn("source for " + fullyQualifiedName + " is missing."); } } } catch (Throwable e) { e.printStackTrace(); } try { executor.shutdown(); executor.awaitTermination(60, TimeUnit.MINUTES); } catch (InterruptedException e) { log.warn("the reachability analysis was interrupted", e); cache.putCache(context, ReachableTypes.EVERYTHING_REACHABLE_INSTANCE); return ReachableTypes.EVERYTHING_REACHABLE_INSTANCE; } if (log.isDebugEnabled()) { log.debug("*** REACHABILITY ANALYSIS (production mode: " + EnvUtil.isProdMode() + ") ***"); for (final String s : allDependencies) { log.debug(" -> " + s); } time = System.currentTimeMillis() - time; log.debug("*** END OF REACHABILITY ANALYSIS (" + time + "ms) *** "); } final ReachableTypes reachableTypes = new ReachableTypes(allDependencies, true); cache.putCache(context, reachableTypes); return reachableTypes; }
@Override public String toString() { return type.getFullyQualifiedName(); }
private void appendPageShowMethod( AnonymousClassStructureBuilder pageImplBuilder, MetaClass pageClass, Class<? extends Annotation> annotation, boolean addPrivateAccessors) { BlockBuilder<?> method = pageImplBuilder .publicMethod( void.class, createMethodNameFromAnnotation(annotation), Parameter.of(pageClass, "widget"), Parameter.of(HistoryToken.class, "state")) .body(); int idx = 0; method.append(Stmt.declareFinalVariable("pageState", Map.class, new HashMap<String, Object>())); for (MetaField field : pageClass.getFieldsAnnotatedWith(PageState.class)) { PageState psAnno = field.getAnnotation(PageState.class); String fieldName = field.getName(); String queryParamName = psAnno.value(); if (queryParamName == null || queryParamName.trim().isEmpty()) { queryParamName = fieldName; } if (addPrivateAccessors) { PrivateAccessUtil.addPrivateAccessStubs( PrivateAccessType.Write, "jsni", pageImplBuilder, field, new Modifier[] {}); } String injectorName = PrivateAccessUtil.getPrivateFieldInjectorName(field); MetaClass erasedFieldType = field.getType().getErased(); if (erasedFieldType.isAssignableTo(Collection.class)) { MetaClass elementType = MarshallingGenUtil.getConcreteCollectionElementType(field.getType()); if (elementType == null) { throw new UnsupportedOperationException( "Found a @PageState field with a Collection type but without a concrete type parameter. " + "Collection-typed @PageState fields must specify a concrete type parameter."); } if (erasedFieldType.equals(MetaClassFactory.get(Set.class))) { method.append(Stmt.declareVariable(fieldName, Stmt.newObject(HashSet.class))); } else if (erasedFieldType.equals(MetaClassFactory.get(List.class)) || erasedFieldType.equals(MetaClassFactory.get(Collection.class))) { method.append(Stmt.declareVariable(fieldName, Stmt.newObject(ArrayList.class))); } else { throw new UnsupportedOperationException( "Found a @PageState field which is a collection of type " + erasedFieldType.getFullyQualifiedName() + ". For collection-valued fields, only the exact types java.util.Collection, java.util.Set, and " + "java.util.List are supported at this time."); } // for (String fv{idx} : state.get({fieldName})) method.append( Stmt.loadVariable("state") .invoke("getState") .invoke("get", queryParamName) .foreach("elem", Object.class) .append( Stmt.declareVariable( "fv" + idx, Stmt.castTo(String.class, Stmt.loadVariable("elem")))) .append( Stmt.loadVariable(fieldName) .invoke( "add", paramFromStringStatement(elementType, Stmt.loadVariable("fv" + idx)))) .append( Stmt.loadVariable("pageState") .invoke("put", fieldName, Stmt.loadVariable(fieldName))) .finish()); method.append( Stmt.loadVariable("this") .invoke(injectorName, Stmt.loadVariable("widget"), Stmt.loadVariable(fieldName))); } else { method.append( Stmt.declareFinalVariable( "fv" + idx, Collection.class, Stmt.loadVariable("state").invoke("getState").invoke("get", queryParamName))); method.append( If.cond( Bool.or( Bool.isNull(Stmt.loadVariable("fv" + idx)), Stmt.loadVariable("fv" + idx).invoke("isEmpty"))) .append( Stmt.loadVariable("this") .invoke( injectorName, Stmt.loadVariable("widget"), defaultValueStatement(erasedFieldType))) .finish() .else_() .append( Stmt.loadVariable("this") .invoke( injectorName, Stmt.loadVariable("widget"), paramFromStringStatement( erasedFieldType, Stmt.loadVariable("fv" + idx).invoke("iterator").invoke("next")))) .append( Stmt.loadVariable("pageState") .invoke( "put", fieldName, Stmt.loadVariable("fv" + idx).invoke("iterator").invoke("next"))) .finish()); } idx++; } if (addPrivateAccessors) { method.append( Stmt.invokeStatic( CDI.class, "fireEvent", Stmt.newObject(NavigationEvent.class) .withParameters( Stmt.newObject(PageRequest.class) .withParameters( getPageName(pageClass), Stmt.loadVariable("pageState"))))); } checkMethodAndAddPrivateAccessors( pageImplBuilder, method, pageClass, annotation, HistoryToken.class, "state"); method.finish(); }
@Override protected String generate(TreeLogger logger, GeneratorContext context) { final ClassStructureBuilder<?> classBuilder = Implementations.extend(NavigationGraph.class, GENERATED_CLASS_NAME); // accumulation of (name, pageclass) mappings for dupe detection and dot file generation BiMap<String, MetaClass> pageNames = HashBiMap.create(); // accumulation UniquePageRoles for ensuring there is exactly one. Multimap<Class<?>, MetaClass> pageRoles = ArrayListMultimap.create(); ConstructorBlockBuilder<?> ctor = classBuilder.publicConstructor(); final Collection<MetaClass> pages = ClassScanner.getTypesAnnotatedWith(Page.class, context); for (MetaClass pageClass : pages) { if (!pageClass.isAssignableTo(IsWidget.class)) { throw new GenerationException( "Class " + pageClass.getFullyQualifiedName() + " is annotated with @Page, so it must implement IsWidget"); } Page annotation = pageClass.getAnnotation(Page.class); String pageName = getPageName(pageClass); List<Class<? extends PageRole>> annotatedPageRoles = Arrays.asList(annotation.role()); MetaClass prevPageWithThisName = pageNames.put(pageName, pageClass); if (prevPageWithThisName != null) { throw new GenerationException( "Page names must be unique, but " + prevPageWithThisName + " and " + pageClass + " are both named [" + pageName + "]"); } Statement pageImplStmt = generateNewInstanceOfPageImpl(pageClass, pageName); if (annotatedPageRoles.contains(DefaultPage.class)) { // need to assign the page impl to a variable and add it to the map twice ctor.append(Stmt.declareFinalVariable("defaultPage", PageNode.class, pageImplStmt)); pageImplStmt = Variable.get("defaultPage"); ctor.append(Stmt.nestedCall(Refs.get("pagesByName")).invoke("put", "", pageImplStmt)); ctor.append( Stmt.nestedCall(Refs.get("pagesByRole")) .invoke("put", DefaultPage.class, pageImplStmt)); } else if (pageName.equals("")) { throw new GenerationException( "Page " + pageClass.getFullyQualifiedName() + " has an empty path. Only the" + " page with startingPage=true is permitted to have an empty path."); } final String fieldName = StringUtils.uncapitalize(pageClass.getName()); ctor.append(Stmt.declareFinalVariable(fieldName, PageNode.class, pageImplStmt)); ctor.append( Stmt.nestedCall(Refs.get("pagesByName")).invoke("put", pageName, Refs.get(fieldName))); for (Class<? extends PageRole> annotatedPageRole : annotatedPageRoles) { pageRoles.put(annotatedPageRole, pageClass); // DefaultPage is already added above. if (!annotatedPageRole.equals(DefaultPage.class)) ctor.append( Stmt.nestedCall(Refs.get("pagesByRole")) .invoke("put", annotatedPageRole, Refs.get(fieldName))); } } ctor.finish(); renderNavigationToDotFile(pageNames); validateDefaultPagePresent(pages, pageRoles); validateUnique(pageRoles); return classBuilder.toJavaString(); }