private int addEditor(
      String name,
      Object obj,
      Field field,
      int index,
      Class<?> type,
      Object value,
      List<Field> parentList,
      List<Integer> parentIndices,
      boolean readonly)
      throws IllegalArgumentException, IllegalAccessException {
    Calculated calculated = field.getAnnotation(Calculated.class);
    if (calculated != null) {
      return 0;
    }
    List<Field> parList = new ArrayList<>(parentList);
    parList.add(field);

    List<Integer> parIndices = new ArrayList<>(parentIndices);
    parIndices.add(index);
    Internal inter = field.getAnnotation(Internal.class);
    if (inter != null) {
      return 0;
    }
    SWFType swfType = field.getAnnotation(SWFType.class);
    Multiline multiline = field.getAnnotation(Multiline.class);
    Component editor;
    if (type.equals(int.class)
        || type.equals(Integer.class)
        || type.equals(short.class)
        || type.equals(Short.class)
        || type.equals(long.class)
        || type.equals(Long.class)
        || type.equals(double.class)
        || type.equals(Double.class)
        || type.equals(float.class)
        || type.equals(Float.class)) {
      editor = new NumberEditor(name, obj, field, index, type, swfType);
    } else if (type.equals(boolean.class) || type.equals(Boolean.class)) {
      editor = new BooleanEditor(name, obj, field, index, type);
    } else if (type.equals(String.class)) {
      editor = new StringEditor(name, obj, field, index, type, multiline != null);
    } else if (type.equals(RGB.class) || type.equals(RGBA.class) || type.equals(ARGB.class)) {
      editor = new ColorEditor(name, obj, field, index, type);
    } else if (type.equals(ByteArrayRange.class)) {
      editor = new BinaryDataEditor(mainPanel, name, obj, field, index, type);
    } else if (type.equals(Amf3Value.class)) {
      editor = new Amf3ValueEditor(name, obj, field, index, type);
    } else {
      if (value == null) {
        if (readonly) {
          return 0;
        }
        Optional opt = field.getAnnotation(Optional.class);
        if (opt == null) {
          try {
            value = ReflectionTools.newInstanceOf(field.getType());
            field.set(obj, value);
          } catch (InstantiationException | IllegalAccessException ex) {
            logger.log(Level.SEVERE, null, ex);
            return 0;
          }
        } else {
          return 0;
        }
      }
      return generateEditControlsRecursive(value, name + ".", parList, parIndices, readonly);
    }
    if (editor instanceof GenericTagEditor) {
      GenericTagEditor ce = (GenericTagEditor) editor;
      ce.addChangeListener(this);
      editors.put(name, ce);
      fieldPaths.put(name, parList);
      fieldIndices.put(name, parIndices);
      addRow(name, editor, field);

      ce.added();
    }
    return 1;
  }
  private int generateEditControlsRecursive(
      final Object obj,
      String parent,
      List<Field> parentFields,
      List<Integer> parentIndices,
      boolean readonly) {
    if (obj == null) {
      return 0;
    }
    Field[] fields = obj.getClass().getDeclaredFields();
    int propCount = 0;
    for (final Field field : fields) {
      try {
        if (Modifier.isStatic(field.getModifiers())) {
          continue;
        }
        field.setAccessible(true);
        String name = parent + field.getName();
        final Object value = field.get(obj);
        if (List.class.isAssignableFrom(field.getType())) {
          if (value != null) {
            int i = 0;
            for (Object obj1 : (Iterable) value) {
              final String subname = name + "[" + i + "]";
              propCount +=
                  addEditor(
                      subname,
                      obj,
                      field,
                      i,
                      obj1.getClass(),
                      obj1,
                      parentFields,
                      parentIndices,
                      readonly);
              final int fi = i;
              i++;
              JButton removeButton = new JButton(View.getIcon("close16"));
              removeButton.addActionListener(
                  new ActionListener() {

                    @Override
                    public void actionPerformed(ActionEvent e) {
                      removeItem(obj, field, fi);
                    }
                  });
              removeButtons.put(subname, removeButton);
            }
          }
        } else if (field.getType().isArray()) {
          if (value != null) {
            for (int i = 0; i < Array.getLength(value); i++) {
              Object item = Array.get(value, i);
              String subname = name + "[" + i + "]";
              propCount +=
                  addEditor(
                      subname,
                      obj,
                      field,
                      i,
                      item.getClass(),
                      item,
                      parentFields,
                      parentIndices,
                      readonly);
              final int fi = i;
              JButton removeButton = new JButton(View.getIcon("close16"));
              removeButton.addActionListener(
                  new ActionListener() {

                    @Override
                    public void actionPerformed(ActionEvent e) {
                      removeItem(obj, field, fi);
                    }
                  });
              removeButtons.put(subname, removeButton);
            }
          }
        } else {
          propCount +=
              addEditor(
                  name,
                  obj,
                  field,
                  0,
                  field.getType(),
                  value,
                  parentFields,
                  parentIndices,
                  readonly);
        }
        if (ReflectionTools.needsIndex(field)
            && !readonly
            && !field.getName().equals("clipActionRecords")) { // No clip actions, sorry
          JButton addButton = new JButton(View.getIcon("add16"));
          addButton.addActionListener(
              new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                  addItem(obj, field);
                }
              });
          name += "[]";

          List<Field> parList = new ArrayList<>(parentFields);
          parList.add(field);
          fieldPaths.put(name, parList);

          List<Integer> parIndices = new ArrayList<>(parentIndices);
          parIndices.add(0);
          fieldIndices.put(name, parIndices);

          addRow(name, addButton, field);
          addKeys.add(name);
          addButtons.put(name, addButton);
        }
      } catch (IllegalArgumentException | IllegalAccessException ex) {
        logger.log(Level.SEVERE, null, ex);
      }
    }
    return propCount;
  }