@Override
 public JSON getDescriptor(ModuleMetaModel metaModel) {
   JSON json = new JSON();
   for (ApplicationMetaModel application : metaModel.getChildren(ApplicationMetaModel.class)) {
     json.set(application.getHandle().getPackage().toString(), new JSON());
   }
   return json;
 }
 @Override
 public void processAnnotationRemoved(
     ModuleMetaModel metaModel, AnnotationKey key, AnnotationState removed) {
   if (key.getType().equals(APPLICATION)) {
     ElementHandle.Package pkg = (ElementHandle.Package) key.getElement();
     ApplicationMetaModel mm = metaModel.getChild(Key.of(pkg, ApplicationMetaModel.class));
     if (mm != null) {
       context.remove(mm);
       mm.remove();
     }
   }
 }
 @Override
 public void processAnnotationUpdated(
     ModuleMetaModel metaModel,
     AnnotationKey key,
     AnnotationState removed,
     AnnotationState added) {
   if (key.getType().equals(APPLICATION)) {
     ElementHandle.Package pkg = (ElementHandle.Package) key.getElement();
     ApplicationMetaModel application =
         metaModel.getChild(Key.of(pkg, ApplicationMetaModel.class));
     application.modified = true;
   }
 }
  @Override
  public void postProcessAnnotations(ModuleMetaModel metaModel) {

    // Resolve applications
    for (ApplicationMetaModel application : metaModel.getChildren(ApplicationMetaModel.class)) {
      if (application.modified) {
        metaModel.queue(MetaModelEvent.createUpdated(application));
        application.modified = false;
      }
    }

    //
    context.postProcessAnnotations();
  }
  void emitApplication(ProcessingContext env, ApplicationMetaModel application)
      throws ProcessingException {
    PackageElement elt = env.get(application.getHandle());
    FQN fqn = new FQN(application.getName(), "Application");

    //
    Writer writer = null;
    try {
      JavaFileObject applicationFile = env.createSourceFile(fqn, elt);
      writer = applicationFile.openWriter();

      writer.append("package ").append(fqn.getPackageName()).append(";\n");

      // Imports
      writer.append("import ").append(Tools.getImport(ApplicationDescriptor.class)).append(";\n");

      // Open class
      writer.append("public class ").append(fqn.getSimpleName()).append(" {\n");

      // Field
      writer.append("private final ").append(APPLICATION_DESCRIPTOR).append(" descriptor;\n");

      // Constructor
      writer.append("public ").append(fqn.getSimpleName()).append("() throws Exception {\n");
      writer
          .append("this.descriptor = ")
          .append(APPLICATION_DESCRIPTOR)
          .append(".create(getClass());\n");
      writer.append("}\n");

      // Getter
      writer.append("public ").append(APPLICATION_DESCRIPTOR).append(" getDescriptor() {\n");
      writer.append("return descriptor;\n");
      writer.append("}\n");

      // Close class
      writer.append("}\n");

      //
      env.log("Generated application " + fqn.getName() + " as " + applicationFile.toUri());
    } catch (IOException e) {
      throw TemplateMetaModel.CANNOT_WRITE_APPLICATION.failure(e, elt, application.getName());
    } finally {
      Tools.safeClose(writer);
    }
  }
  private void emitConfig(ModuleMetaModel metaModel) {
    JSON descriptor = new JSON();

    // Application configs
    for (ApplicationMetaModel application : metaModel.getChildren(ApplicationMetaModel.class)) {

      //
      metaModel.processingContext.log(
          "Emitting application " + application.getHandle() + " config");

      // Recycle
      descriptor.clear();

      // Emit config
      for (ApplicationMetaModelPlugin plugin : context.getPlugins()) {
        JSON pluginDescriptor = plugin.getDescriptor(application);
        if (pluginDescriptor != null) {
          descriptor.set(plugin.getName(), pluginDescriptor);
        }
      }

      //
      Writer writer = null;
      try {
        FileObject fo =
            metaModel.processingContext.createResource(
                StandardLocation.CLASS_OUTPUT, application.getName(), "config.json");
        writer = fo.openWriter();
        descriptor.toString(writer, 2);
      } catch (IOException e) {
        throw ApplicationMetaModel.CANNOT_WRITE_APPLICATION_CONFIG.failure(
            e, metaModel.processingContext.get(application.getHandle()), application.getName());
      } finally {
        Tools.safeClose(writer);
      }
    }
  }