/** * Renders the page-to-page navigation graph into the file {@code navgraph.gv} in the {@code * .errai} cache directory. */ private void renderNavigationToDotFile(BiMap<String, MetaClass> pages) { final File dotFile = new File(RebindUtils.getErraiCacheDir().getAbsolutePath(), "navgraph.gv"); PrintWriter out = null; try { out = new PrintWriter(dotFile); out.println("digraph Navigation {"); final MetaClass transitionToType = MetaClassFactory.get(TransitionTo.class); final MetaClass transitionAnchorType = MetaClassFactory.get(TransitionAnchor.class); final MetaClass transitionAnchorFactoryType = MetaClassFactory.get(TransitionAnchorFactory.class); for (Map.Entry<String, MetaClass> entry : pages.entrySet()) { String pageName = entry.getKey(); MetaClass pageClass = entry.getValue(); // entry for the node itself out.print("\"" + pageName + "\""); Page pageAnnotation = pageClass.getAnnotation(Page.class); List<Class<? extends PageRole>> roles = Arrays.asList(pageAnnotation.role()); if (roles.contains(DefaultPage.class)) { out.print(" [penwidth=3]"); } out.println(); for (MetaField field : getAllFields(pageClass)) { if (field.getType().getErased().equals(transitionToType) || field.getType().getErased().equals(transitionAnchorType) || field.getType().getErased().equals(transitionAnchorFactoryType)) { MetaType targetPageType = field.getType().getParameterizedType().getTypeParameters()[0]; String targetPageName = pages.inverse().get(targetPageType); // entry for the link between nodes out.println( "\"" + pageName + "\" -> \"" + targetPageName + "\" [label=\"" + field.getName() + "\"]"); } } } out.println("}"); } catch (FileNotFoundException e) { throw new RuntimeException(e); } finally { if (out != null) { out.close(); } } }
@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(); }