public boolean equals(Object obj) {

    // if both have id, the comparision is done based on ids
    if (!super.equals(obj)) return false;

    if (!(obj instanceof SubmissionSourceOther)) return false;

    final SubmissionSourceOther that = (SubmissionSourceOther) obj;

    // this is used only if both are new
    return StringUtils.compareStrings(this.location, that.location)
        && StringUtils.compareStrings(this.title, that.title)
        && StringUtils.compareStrings(this.classNumber, that.classNumber)
        && StringUtils.compareStrings(this.folioOrPage, that.folioOrPage);
  }
  public void encode(
      EditorComponent editor,
      String editorId,
      UIForm form,
      FacesContext context,
      Schema schema,
      FieldValue value)
      throws IOException {

    // type check
    if (!(value instanceof FieldValueDropdowns))
      throw new RuntimeException("FieldSchemaDropdowns expected FieldValueDropdowns");

    // cast
    FieldValueDropdowns valueDropdowns = (FieldValueDropdowns) value;

    // get writer
    ResponseWriter writer = context.getResponseWriter();

    // number of lists
    int listCount = listIds != null ? listIds.length : 0;

    // hidden field with the number of lists
    String countFieldName =
        FieldValueDropdowns.getHtmlCountHiddenFieldName(editor, context, getName());
    JsfUtils.encodeHiddenInput(editor, writer, countFieldName, String.valueOf(listCount));

    // selects
    String parentValue = null;
    for (int i = 0; i < listCount; i++) {

      String selectName = FieldValueDropdowns.getHtmlSelectName(editor, context, getName(), i);
      ListItem[] list = schema.getListById(listIds[i]);
      String selectedValue = valueDropdowns.getValue(i);

      String onChange =
          "RecordEditorGlobals.dropdownValueChanged("
              + "'"
              + editorId
              + "',"
              + "'"
              + getName()
              + "',"
              + +i
              + ")";

      writer.startElement("div", editor);

      writer.startElement("select", editor);
      writer.writeAttribute("name", selectName, null);
      writer.writeAttribute("onchange", onChange, null);
      if (!StringUtils.isNullOrEmpty(cssClass)) writer.writeAttribute("class", cssClass, null);

      for (int j = 0; j < list.length; j++) {
        ListItem item = list[j];
        if (i == 0
            || (item.getParentValue() == null)
            || (parentValue == null && item.getValue() == null)
            || (parentValue != null && parentValue.equals(item.getParentValue()))) {
          writer.startElement("option", editor);
          if (StringUtils.compareStrings(item.getValue(), selectedValue))
            writer.writeAttribute("selected", "selected", null);
          writer.writeAttribute("value", item.getValue(), null);
          writer.write(StringUtils.coalesce(item.getText(), ""));
          writer.endElement("option");
        }
      }

      writer.endElement("select");
      writer.endElement("div");

      parentValue = selectedValue;
    }
  }