private Set<String> getEditorFieldCleanupExpressions() {
    Set<String> result = new LinkedHashSet<>();

    for (JClassType typeCandidate : model.getEditorType().getFlattenedSupertypeHierarchy()) {
      JClassType classType = typeCandidate.isClass();

      if (classType != null) {
        for (JField field : classType.getFields()) {
          JClassType fieldClassOrInterfaceType = field.getType().isClassOrInterface();

          if (fieldClassOrInterfaceType != null
              // field type assignable to HasCleanup ..
              && fieldClassOrInterfaceType.isAssignableTo(hasCleanupType)
              // .. but not assignable to Model
              && !fieldClassOrInterfaceType.isAssignableTo(baseModelType)) {
            result.add(
                String.format(
                    "getEditor().%s", //$NON-NLS-1$
                    field.getName()));
          }
        }
      }
    }

    return result;
  }
  /** Writes the map of the owner Models */
  private void writeOwnerModels() {
    logger.log(Type.DEBUG, "Starting to write OwnerModels"); // $NON-NLS-1$
    sw.println();
    sw.println("@Override"); // $NON-NLS-1$
    sw.println(
        "protected java.util.Map<String, org.ovirt.engine.ui.uicommonweb.models.Model> getOwnerModels() {"); //$NON-NLS-1$
    sw.indent();

    sw.println(
        "java.util.Map<String, org.ovirt.engine.ui.uicommonweb.models.Model> regs = new java.util.HashMap<String, org.ovirt.engine.ui.uicommonweb.models.Model>();"); //$NON-NLS-1$

    logger.log(Type.DEBUG, "Going over Editor Fields"); // $NON-NLS-1$
    for (EditorData editorData : model.getEditorData()) {
      logger.log(Type.DEBUG, "Going over Field: " + editorData); // $NON-NLS-1$
      String path = editorData.getPath();
      if (path.length() == 0) {
        continue;
      }

      JClassType propertyOwnerType = eraseType(editorData.getPropertyOwnerType());

      if (propertyOwnerType == listModelType || propertyOwnerType == entityModelType) {
        logger.log(Type.DEBUG, "Found owner Model Field: " + editorData); // $NON-NLS-1$
        sw.println(
            "regs.put(\"%s\", getObject()%s);",
            path, editorData.getBeanOwnerExpression()); // $NON-NLS-1$
      }
    }
    sw.println("return regs;"); // $NON-NLS-1$
    sw.outdent();
    sw.println("}"); // $NON-NLS-1$
  }
  /** Writes the UiCommonListenerMap for the edited model */
  private void writeListenerMap() {
    logger.log(Type.DEBUG, "Starting to write ListenerMap"); // $NON-NLS-1$
    sw.println();
    sw.println("@Override"); // $NON-NLS-1$
    sw.println(
        "protected "
            + UiCommonListenerMap.class.getName()
            + " getListenerMap() {"); //$NON-NLS-1$ //$NON-NLS-2$
    sw.indent();

    sw.println(
        UiCommonListenerMap.class.getName()
            + " listenerMap = new "
            + UiCommonListenerMap.class.getName() // $NON-NLS-1$
            + "();"); //$NON-NLS-1$
    sw.println();

    logger.log(Type.DEBUG, "Looking for top-level Editor Fields"); // $NON-NLS-1$

    for (EditorData editorData : model.getEditorData()) {
      logger.log(Type.DEBUG, "Going over Field: " + editorData); // $NON-NLS-1$
      String path = editorData.getPath();
      // Change first letter to Upper to comply with UiCommon Property Names
      path = Character.toUpperCase(path.charAt(0)) + path.substring(1, path.length());

      if (path.length() == 0) {
        continue;
      }

      // only relevant for top-level properties
      if (!editorData.isDeclaredPathNested()) {
        logger.log(Type.DEBUG, "Found top-level Field: " + editorData); // $NON-NLS-1$

        sw.println(
            "listenerMap.addListener(\"%s\", \"PropertyChanged\", new org.ovirt.engine.ui.uicompat.IEventListener() {", //$NON-NLS-1$
            path);
        sw.indent();
        sw.println("@Override"); // $NON-NLS-1$
        sw.println(
            "public void eventRaised(org.ovirt.engine.ui.uicompat.Event ev, Object sender, org.ovirt.engine.ui.uicompat.EventArgs args) {"); //$NON-NLS-1$
        sw.indent();
        sw.println(
            "getEditor().%s.setValue(getObject()%s);", //$NON-NLS-1$
            editorData.getExpression(), editorData.getGetterExpression());
        sw.outdent();
        sw.println("}"); // $NON-NLS-1$
        sw.outdent();
        sw.println("});"); // $NON-NLS-1$
        sw.println();
      }
    }

    sw.println("return listenerMap;"); // $NON-NLS-1$
    sw.outdent();
    sw.println("}"); // $NON-NLS-1$
  }
  /** Writes the UiCommonEventMap for the edited model */
  private void writeEventMap() {
    logger.log(Type.DEBUG, "Starting to write EventMap"); // $NON-NLS-1$

    sw.println();
    sw.println("@Override"); // $NON-NLS-1$
    sw.println(
        "protected "
            + UiCommonEventMap.class.getName()
            + " getEventMap() {"); //$NON-NLS-1$ //$NON-NLS-2$
    sw.indent();

    sw.println(
        UiCommonEventMap.class.getName()
            + " eventMap = new "
            + UiCommonEventMap.class.getName()
            + "();"); //$NON-NLS-1$ //$NON-NLS-2$

    logger.log(Type.DEBUG, "Looking for Model Fields"); // $NON-NLS-1$

    for (EditorData editorData : model.getEditorData()) {
      logger.log(Type.DEBUG, "Going over Field: " + editorData); // $NON-NLS-1$

      String path = editorData.getPath();
      if (path.length() == 0) {
        continue;
      }

      JClassType propertyOwnerType = eraseType(editorData.getPropertyOwnerType());

      if (propertyOwnerType == entityModelType) {
        logger.log(Type.DEBUG, "Found EntityModel Field: " + editorData); // $NON-NLS-1$

        sw.println(
            "eventMap.addEvent(\"%s\", \"EntityChanged\", getObject()%s.getEntityChangedEvent());", //$NON-NLS-1$
            path, editorData.getBeanOwnerExpression());

      } else if (propertyOwnerType == listModelType) {
        logger.log(Type.DEBUG, "Found ListModel Field: " + editorData); // $NON-NLS-1$

        sw.println(
            "eventMap.addEvent(\"%s\", \"ItemsChanged\", getObject()%s.getItemsChangedEvent());", //$NON-NLS-1$
            path, editorData.getBeanOwnerExpression());
        sw.println(
            "eventMap.addEvent(\"%s\", \"SelectedItemsChanged\", getObject()%s.getSelectedItemsChangedEvent());", //$NON-NLS-1$
            path, editorData.getBeanOwnerExpression());
        sw.println(
            "eventMap.addEvent(\"%s\", \"SelectedItemChanged\", getObject()%s.getSelectedItemChangedEvent());", //$NON-NLS-1$
            path, editorData.getBeanOwnerExpression());
      }
    }

    sw.println("return eventMap;"); // $NON-NLS-1$
    sw.outdent();
    sw.println("}"); // $NON-NLS-1$
  }
  private Set<String> getModelCleanupExpressions() {
    Set<String> result = new LinkedHashSet<>();

    // top-level Model
    if (model.getProxyType().isAssignableTo(hasCleanupType)) {
      result.add("getObject()"); // $NON-NLS-1$
    }

    // all Models edited through the top-level Model
    for (EditorData editorData : model.getEditorData()) {
      if (editorData.getPropertyOwnerType().isAssignableTo(hasCleanupType)) {
        result.add(
            String.format(
                "getObject()%s", //$NON-NLS-1$
                editorData.getBeanOwnerExpression()));
      }
    }

    return result;
  }
  private void writeCleanup() {
    logger.log(
        Type.DEBUG,
        "Starting to write cleanup impl. for editor " //$NON-NLS-1$
            + model.getEditorType().getQualifiedSourceName());

    sw.println();
    sw.println("@Override"); // $NON-NLS-1$
    sw.println("public void cleanup() {"); // $NON-NLS-1$
    sw.indent();

    // 1. clean up the Editor instance
    Set<String> editorFieldExpressions = getEditorFieldCleanupExpressions();

    for (String expr : editorFieldExpressions) {
      sw.println(String.format("if (%s != null) {", expr)); // $NON-NLS-1$
      sw.indent();

      sw.println(String.format("%s.cleanup();", expr)); // $NON-NLS-1$

      sw.outdent();
      sw.println("}"); // $NON-NLS-1$
    }

    // 2. clean up the edited Model object
    Set<String> modelExpressions = getModelCleanupExpressions();

    if (!modelExpressions.isEmpty()) {
      sw.println("if (getObject() != null) {"); // $NON-NLS-1$
      sw.indent();

      for (String expr : modelExpressions) {
        sw.println(String.format("%s.cleanup();", expr)); // $NON-NLS-1$
      }

      sw.outdent();
      sw.println("}"); // $NON-NLS-1$
    }

    sw.outdent();
    sw.println("}"); // $NON-NLS-1$
  }