/**
   * 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();
      }
    }
  }
  public String generate(String packageName, String clazzName) {
    File fileCacheDir = RebindUtils.getErraiCacheDir();
    File cacheFile = new File(fileCacheDir.getAbsolutePath() + "/" + clazzName + ".java");

    final Set<Class<? extends Annotation>> annos = new HashSet<Class<? extends Annotation>>();
    annos.add(Portable.class);
    annos.add(ExposeEntity.class);

    String gen;
    if (RebindUtils.hasClasspathChangedForAnnotatedWith(annos) || !cacheFile.exists()) {
      log.info("generating marshalling class...");
      long st = System.currentTimeMillis();
      gen = _generate(packageName, clazzName);
      log.info("generated marshalling class in " + (System.currentTimeMillis() - st) + "ms");

      RebindUtils.writeStringToFile(cacheFile, gen);
    } else {
      gen = RebindUtils.readFileToString(cacheFile);
      log.info("nothing has changed. using cached marshaller factory class.");
    }

    return gen;
  }