/** * Generates a new instance of an anonymous inner class that implements the PageNode interface. * * @param pageClass The class providing the widget content for the page. * @param pageName The name of the page (to be used in the URL history fragment). */ private ObjectBuilder generateNewInstanceOfPageImpl(MetaClass pageClass, String pageName) { AnonymousClassStructureBuilder pageImplBuilder = ObjectBuilder.newInstanceOf( MetaClassFactory.parameterizedAs( PageNode.class, MetaClassFactory.typeParametersOf(pageClass))) .extend(); pageImplBuilder .publicMethod(String.class, "name") .append(Stmt.loadLiteral(pageName).returnValue()) .finish() .publicMethod(Class.class, "contentType") .append(Stmt.loadLiteral(pageClass).returnValue()) .finish() .publicMethod( void.class, "produceContent", Parameter.of(CreationalCallback.class, "callback")) .append( Stmt.nestedCall(Refs.get("bm")) .invoke("lookupBean", Stmt.loadLiteral(pageClass)) .invoke("getInstance", Stmt.loadVariable("callback"))) .finish(); appendPageHidingMethod(pageImplBuilder, pageClass); appendPageHiddenMethod(pageImplBuilder, pageClass); appendPageShowingMethod(pageImplBuilder, pageClass); appendPageShownMethod(pageImplBuilder, pageClass); return pageImplBuilder.finish(); }
@Override public Statement getBeanInstance(final InjectableInstance injectableInstance) { final IOCProcessingContext pCtx = injectableInstance.getInjectionContext().getProcessingContext(); pCtx.append(Stmt.declareFinalVariable(varName, proxyClass, newObject(proxyClass))); final MetaClass proxyResolverRef = parameterizedAs(ProxyResolver.class, typeParametersOf(proxiedType)); final BlockBuilder<AnonymousClassStructureBuilder> proxyResolverBody = newObject(proxyResolverRef) .extend().publicOverridesMethod("resolve", Parameter.of(proxiedType, "obj")); final Statement proxyResolver = proxyResolverBody._( ProxyMaker.closeProxy(Refs.get(varName), Refs.get("obj")) ).finish().finish(); proxyResolverBody._(Stmt.loadVariable("context").invoke("addProxyReference", Refs.get(varName), Refs.get("obj"))); pCtx.append(loadVariable("context").invoke("addUnresolvedProxy", proxyResolver, proxiedType, qualifyingMetadata.getQualifiers())); for (final Statement statement : closeStatements) { proxyResolverBody.append(statement); } setRendered(true); return loadVariable(varName); }
/** * Tries to find a reference for an injected {@link AutoBound} data binder. * * @return the data binder reference or null if not found. */ private static DataBinderRef lookupAutoBoundBinder(final InjectableInstance<?> inst) { Statement dataBinderRef = null; MetaClass dataModelType = null; InjectUtil.BeanMetric beanMetric = InjectUtil.getFilteredBeanMetric( inst.getInjectionContext(), inst.getInjector().getInjectedType(), AutoBound.class); final Collection<Object> allInjectors = beanMetric.getAllInjectors(); if (allInjectors.size() > 1) { throw new GenerationException( "Multiple @AutoBound data binders injected in " + inst.getEnclosingType()); } else if (allInjectors.size() == 1) { final Object injectorElement = allInjectors.iterator().next(); if (injectorElement instanceof MetaConstructor || injectorElement instanceof MetaMethod) { final MetaParameter mp = beanMetric.getConsolidatedMetaParameters().iterator().next(); assertTypeIsDataBinder(mp.getType()); dataModelType = (MetaClass) mp.getType().getParameterizedType().getTypeParameters()[0]; dataBinderRef = inst.getInjectionContext().getInlineBeanReference(mp); inst.ensureMemberExposed(); } else { final MetaField field = (MetaField) allInjectors.iterator().next(); assertTypeIsDataBinder(field.getType()); dataModelType = (MetaClass) field.getType().getParameterizedType().getTypeParameters()[0]; dataBinderRef = Stmt.invokeStatic( inst.getInjectionContext().getProcessingContext().getBootstrapClass(), PrivateAccessUtil.getPrivateFieldInjectorName(field), Variable.get(inst.getInjector().getInstanceVarName())); inst.getInjectionContext().addExposedField(field, PrivateAccessType.Both); } } else { final MetaClass declaringClass = inst.getInjector().getInjectedType(); for (final MetaField field : declaringClass.getFields()) { if (field.isAnnotationPresent(AutoBound.class)) { assertTypeIsDataBinder(field.getType()); dataModelType = (MetaClass) field.getType().getParameterizedType().getTypeParameters()[0]; dataBinderRef = Stmt.invokeStatic( inst.getInjectionContext().getProcessingContext().getBootstrapClass(), PrivateAccessUtil.getPrivateFieldInjectorName(field), Variable.get(inst.getInjector().getInstanceVarName())); inst.getInjectionContext().addExposedField(field, PrivateAccessType.Both); break; } } } return (dataBinderRef != null) ? new DataBinderRef(dataModelType, dataBinderRef) : null; }
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); } }
/** * Tests that it is safe to call ensureMemberExposed() on any type of InjectionPoint. (This was an * actual bug that was not caught by the existing integration tests). */ public void testEnsureMemberExposedWithConstructorInjectionPoint() throws Exception { final ClassStructureBuilder<? extends ClassStructureBuilder<?>> structureBuilder = ClassBuilder.define("my.FakeBootstrapper").publicScope().body(); IOCProcessingContext processingContext = IOCProcessingContext.Builder.create() .logger( new TreeLogger() { @Override public TreeLogger branch( Type type, String msg, Throwable caught, HelpInfo helpInfo) { return null; } @Override public boolean isLoggable(Type type) { return false; } @Override public void log(Type type, String msg, Throwable caught, HelpInfo helpInfo) { System.out.println(type.getLabel() + ": " + msg); if (caught != null) { caught.printStackTrace(); } } }) .sourceWriter(new StringSourceWriter()) .context(Context.create()) .bootstrapClassInstance(structureBuilder.getClassDefinition()) .bootstrapBuilder(structureBuilder) .blockBuilder(Stmt.do_()) .packages(Collections.singleton(ConstructorInjectedBean.class.getPackage().getName())) .build(); InjectionContext ctx = InjectionContext.Builder.create().processingContext(processingContext).build(); MetaConstructor constructor = MetaClassFactory.get(ConstructorInjectedBean.class).getConstructor(FooService.class); InjectionPoint<Inject> injectionPoint = new InjectionPoint<Inject>( new Inject() { @Override public Class<? extends Annotation> annotationType() { return Inject.class; } }, TaskType.Parameter, constructor, null, null, null, constructor.getParameters()[0], null, ctx); // holy crap that was a lot of setup. Here comes the actual test: injectionPoint.ensureMemberExposed(); }
/** * Appends the method that calls the {@code @PageHiding} method of the widget. * * @param pageImplBuilder The class builder for the implementation of PageNode we are adding the * method to. * @param pageClass The "content type" (Widget subclass) of the page. This is the type the user * annotated with {@code @Page}. */ private void appendPageHidingMethod( AnonymousClassStructureBuilder pageImplBuilder, MetaClass pageClass) { BlockBuilder<?> method = pageImplBuilder .publicMethod( void.class, createMethodNameFromAnnotation(PageHiding.class), Parameter.of(pageClass, "widget"), Parameter.of(NavigationControl.class, "control")) .body(); final MetaMethod pageHidingMethod = checkMethodAndAddPrivateAccessors( pageImplBuilder, method, pageClass, PageHiding.class, NavigationControl.class, "control"); /* * If the user did not provide a control parameter, we must proceed for them after the method is invoked. */ if (pageHidingMethod == null || pageHidingMethod.getParameters().length != 1) { method.append(Stmt.loadVariable("control").invoke("proceed")); } method.finish(); }
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 Collection<Statement> createAllPropertyBindings(final Statement proxyRef, final Map<String, ProxyProperty> proxyProperties) { final List<Statement> statementList = new ArrayList<Statement>(); for (final Map.Entry<String, ProxyProperty> entry : proxyProperties.entrySet()) { statementList.add(Stmt.nestedCall(proxyRef).invoke("$set_" + entry.getKey(), entry.getValue().getOriginalValueReference())); } return statementList; }
@Test public void testTryCatchFinallyBlock() { String s = StatementBuilder.create() .try_() .append(Stmt.throw_(Exception.class)) .finish() .catch_(Exception.class, "e") .append(Stmt.throw_(RuntimeException.class, Variable.get("e"))) .finish() .finally_() .append(Stmt.load(0).returnValue()) .finish() .toJavaString(); assertEquals("Failed to generate try catch finally block", TRY_CATCH_FINALLY_BLOCK, s); }
/** * Appends the method that calls the {@code @PageHidden} method of the widget. * * @param pageImplBuilder The class builder for the implementation of PageNode we are adding the * method to. * @param pageClass The "content type" (Widget subclass) of the page. This is the type the user * annotated with {@code @Page}. */ private void appendPageHiddenMethod( AnonymousClassStructureBuilder pageImplBuilder, MetaClass pageClass) { BlockBuilder<?> method = pageImplBuilder .publicMethod( void.class, createMethodNameFromAnnotation(PageHidden.class), Parameter.of(pageClass, "widget")) .body(); checkMethodAndAddPrivateAccessors( pageImplBuilder, method, pageClass, PageHidden.class, HistoryToken.class, "state"); if (pageClass.getAnnotation(Singleton.class) == null && pageClass.getAnnotation(ApplicationScoped.class) == null && pageClass.getAnnotation(EntryPoint.class) == null) { method.append(Stmt.loadVariable("bm").invoke("destroyBean", Stmt.loadVariable("widget"))); } method.finish(); }
public void registerWithBeanManager(InjectionContext context, Statement valueRef) { if (InjectUtil.checkIfTypeNeedsAddingToBeanStore(context, this)) { QualifyingMetadata md = delegate.getQualifyingMetadata(); if (md == null) { md = context.getProcessingContext().getQualifyingMetadataFactory().createDefaultMetadata(); } context .getProcessingContext() .appendToEnd( Stmt.loadVariable(context.getProcessingContext().getContextVariableReference()) .invoke( "addBean", type, Refs.get(delegate.getCreationalCallbackVarName()), isSingleton() ? valueRef : null, md.render())); } }
@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(); }
public static Statement closeProxy(final Statement proxyReference, final Statement beanInstance) { return Stmt.nestedCall(proxyReference).invoke(PROXY_BIND_METHOD, beanInstance); }
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(); }
@Override public Statement getBeanInstance(final InjectableInstance injectableInstance) { final MetaClass type; final MetaParameterizedType pType; switch (injectableInstance.getTaskType()) { case Type: return null; case PrivateField: case Field: final MetaField field = injectableInstance.getField(); type = field.getType(); pType = type.getParameterizedType(); break; case Parameter: final MetaParameter parm = injectableInstance.getParm(); type = parm.getType(); pType = type.getParameterizedType(); break; default: throw new RuntimeException("illegal task type: " + injectableInstance.getEnclosingType()); } final MetaType[] typeArgs = pType.getTypeParameters(); final MetaClass[] typeArgsClasses = new MetaClass[typeArgs.length]; for (int i = 0; i < typeArgs.length; i++) { final MetaType argType = typeArgs[i]; if (argType instanceof MetaClass) { typeArgsClasses[i] = (MetaClass) argType; } else if (argType instanceof MetaParameterizedType) { typeArgsClasses[i] = (MetaClass) ((MetaParameterizedType) argType).getRawType(); } } final Annotation[] qualifiers = injectableInstance.getQualifiers(); final BlockBuilder<?> block = injectableInstance.getInjectionContext().getProcessingContext().getBlockBuilder(); final MetaClass providerCreationalCallback = MetaClassFactory.parameterizedAs( CreationalCallback.class, MetaClassFactory.typeParametersOf(providerInjector.getInjectedType())); final String varName = InjectUtil.getVarNameFromType( providerInjector.getConcreteInjectedType(), injectableInstance); final Statement valueRef; if (providerInjector.isSingleton() && providerInjector.isRendered()) { valueRef = Stmt.loadVariable("beanInstance") .invoke("provide", typeArgsClasses, qualifiers.length != 0 ? qualifiers : null); } else { valueRef = Stmt.load(null); } block.append( Stmt.declareFinalVariable( varName, providerCreationalCallback, Stmt.newObject(providerCreationalCallback) .extend() .publicOverridesMethod( "callback", Parameter.of(providerInjector.getInjectedType(), "beanInstance")) .append( Stmt.loadVariable(InjectUtil.getVarNameFromType(type, injectableInstance)) .invoke("callback", valueRef)) .append(Stmt.loadVariable("async").invoke("finish", Refs.get("this"))) .finish() .publicOverridesMethod("toString") .append( Stmt.load(providerInjector.getInjectedType()).invoke("getName").returnValue()) .finish() .finish())); block.append(Stmt.loadVariable("async").invoke("wait", Refs.get(varName))); block.append( Stmt.loadVariable(providerInjector.getCreationalCallbackVarName()) .invoke("getInstance", Refs.get(varName), Refs.get("context"))); return null; }
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(); }
/** * Searches the given class for methods bearing the given annotation. Verifies that such methods * follow the rules (returns void; takes PageState as a parameter (or not); is the only method * with this annotation), creates a private accessor if required, and then appends a call to that * method to the given method builder. * * @param pageImplBuilder the class to add private accessors to if necessary * @param methodToAppendTo the method builder to append to * @param pageClass the class to search for annotated methods in * @param annotation the annotation to search for in pageClass * @param optionalParamType the type of the single method parameter or null if no parameter * @param optionalParamName the name of the variable of the optional parameter or null if no * parameter. * @return The meta-method for which code was generated * @throws UnsupportedOperationException if the annotated methods in pageClass violate any of the * rules */ private MetaMethod checkMethodAndAddPrivateAccessors( AnonymousClassStructureBuilder pageImplBuilder, BlockBuilder<?> methodToAppendTo, MetaClass pageClass, Class<? extends Annotation> annotation, Class<?> optionalParamType, String optionalParamName) { List<MetaMethod> annotatedMethods = pageClass.getMethodsAnnotatedWith(annotation); if (annotatedMethods.size() > 1) { throw new UnsupportedOperationException( "A @Page can have at most 1 " + createAnnotionName(annotation) + " method, but " + pageClass + " has " + annotatedMethods.size()); } for (MetaMethod metaMethod : annotatedMethods) { if (!metaMethod.getReturnType().equals(MetaClassFactory.get(void.class))) { throw new UnsupportedOperationException( createAnnotionName(annotation) + " methods must have a void return type, but " + metaMethod.getDeclaringClass().getFullyQualifiedName() + "." + metaMethod.getName() + " returns " + metaMethod.getReturnType().getFullyQualifiedName()); } Object[] paramValues = new Object[metaMethod.getParameters().length + 1]; paramValues[0] = Stmt.loadVariable("widget"); // assemble parameters for private method invoker (first param is the widget instance) PrivateAccessUtil.addPrivateAccessStubs( "jsni", pageImplBuilder, metaMethod, new Modifier[] {}); if (optionalParamType != null) { for (int i = 1; i < paramValues.length; i++) { MetaParameter paramSpec = metaMethod.getParameters()[i - 1]; if (paramSpec.getType().equals(MetaClassFactory.get(optionalParamType))) { paramValues[i] = Stmt.loadVariable(optionalParamName); } else { throw new UnsupportedOperationException( createAnnotionName(annotation) + " method " + metaMethod.getDeclaringClass().getFullyQualifiedName() + "." + metaMethod.getName() + " has an illegal parameter of type " + paramSpec.getType().getFullyQualifiedName()); } } } else { if (metaMethod.getParameters().length != 0) { throw new UnsupportedOperationException( createAnnotionName(annotation) + " methods cannot take parameters, but " + metaMethod.getDeclaringClass().getFullyQualifiedName() + "." + metaMethod.getName() + " does."); } } methodToAppendTo.append( Stmt.loadVariable("this") .invoke(PrivateAccessUtil.getPrivateMethodName(metaMethod), paramValues)); return annotatedMethods.get(0); } return null; }