public static boolean areDropDownChoicesMergeable(
      SearchableFieldChoiceItem choice1, SearchableFieldChoiceItem choice2) {
    FieldSpec rawSpec1 = choice1.getSpec();
    FieldSpec rawSpec2 = choice2.getSpec();
    if (!rawSpec1.getType().isDropdown()) return false;
    if (!rawSpec2.getType().isDropdown()) return false;
    if (!rawSpec1.getTag().equals(rawSpec2.getTag())) return false;
    if (!rawSpec1.getLabel().equals(rawSpec2.getLabel())) return false;

    DropDownFieldSpec spec1 = (DropDownFieldSpec) rawSpec1;
    DropDownFieldSpec spec2 = (DropDownFieldSpec) rawSpec2;
    return Arrays.equals(spec1.getReusableChoicesCodes(), spec2.getReusableChoicesCodes());
  }
 void mergeSimilarDropdowns() {
   int mergeInto = 0;
   while (mergeInto + 1 < allChoices.size()) {
     int mergeFrom = mergeInto + 1;
     SearchableFieldChoiceItem into = ((SearchableFieldChoiceItem) allChoices.get(mergeInto));
     SearchableFieldChoiceItem from = ((SearchableFieldChoiceItem) allChoices.get(mergeFrom));
     if (into.getSpec().equals(from.getSpec())) {
       allChoices.remove(mergeFrom);
     } else if (areDropDownChoicesMergeable(into, from)) {
       SearchableFieldChoiceItem result = mergeDropDownChoices(into, from);
       allChoices.set(mergeInto, result);
       allChoices.remove(mergeFrom);
     } else {
       ++mergeInto;
     }
   }
 }
  private void sortAllChoicesWithinDropdowns() {
    for (int i = 0; i < allChoices.size(); ++i) {
      SearchableFieldChoiceItem choiceItem = (SearchableFieldChoiceItem) allChoices.get(i);
      FieldSpec spec = choiceItem.getSpec();
      if (!spec.getType().isDropdown()) continue;

      DropDownFieldSpec dropdownSpec = (DropDownFieldSpec) spec;
      dropdownSpec.sortChoicesByLabel();
    }
  }
  private SearchFieldTreeNode[] getIndexesOfReusableDropdownChildren(SearchFieldTreeNode parent) {
    Vector reusableDropdownNodes = new Vector();
    for (int i = 0; i < parent.getChildCount(); ++i) {
      SearchFieldTreeNode child = (SearchFieldTreeNode) parent.getChildAt(i);
      if (!child.isSearchableFieldChoiceItemNode()) continue;
      SearchableFieldChoiceItem choice = child.getChoiceItem();
      FieldSpec spec = choice.getSpec();
      if (spec.getReusableChoicesCodes().length == 0) continue;
      reusableDropdownNodes.add(child);
    }

    return (SearchFieldTreeNode[]) reusableDropdownNodes.toArray(new SearchFieldTreeNode[0]);
  }
 private void differentiateChildNodes(SearchFieldTreeNode parent) {
   SearchFieldTreeNode[] needDifferentiation = getIndexesOfReusableDropdownChildren(parent);
   for (int i = 0; i < needDifferentiation.length; ++i) {
     SearchFieldTreeNode child = needDifferentiation[i];
     SearchableFieldChoiceItem choice = child.getChoiceItem();
     FieldSpec spec = choice.getSpec();
     String reusableListLabels = "";
     for (int level = 0; level < spec.getReusableChoicesCodes().length; ++level) {
       if (level > 0) reusableListLabels += ", ";
       reusableListLabels += spec.getReusableChoicesCodes()[level];
     }
     child.setLabel(child.toString() + " (" + reusableListLabels + ")");
   }
 }
  public static SearchableFieldChoiceItem mergeDropDownChoices(
      SearchableFieldChoiceItem mergeInto, SearchableFieldChoiceItem mergeFrom) {
    if (!areDropDownChoicesMergeable(mergeInto, mergeFrom))
      throw new RuntimeException("Attempted to merge unmergeable fieldspecs");

    DropDownFieldSpec specInto = (DropDownFieldSpec) mergeInto.getSpec();
    DropDownFieldSpec specFrom = (DropDownFieldSpec) mergeFrom.getSpec();

    Vector choices = new Vector(Arrays.asList(specInto.getAllChoices()));
    ChoiceItem[] moreChoices = specFrom.getAllChoices();
    for (int i = 0; i < moreChoices.length; ++i) {
      if (!choices.contains(moreChoices[i])) choices.add(moreChoices[i]);
    }

    CustomDropDownFieldSpec resultSpec = new CustomDropDownFieldSpec();
    resultSpec.setTag(mergeInto.getSpec().getSubFieldTag());
    resultSpec.setLabel(mergeInto.getSpec().getLabel());
    resultSpec.setParent(mergeInto.getSpec().getParent());
    resultSpec.pullDynamicChoiceSettingsFrom(specFrom);
    // NOTE: Must setChoices AFTER pulling dynamic choices
    resultSpec.setChoices((ChoiceItem[]) choices.toArray(new ChoiceItem[0]));
    return new SearchableFieldChoiceItem(mergeInto.getSpecialCode(), resultSpec);
  }