public void clearItems() {
   for (SearchConditionItem i : items) {
     i.removeListener(this);
   }
   this.items.removeAll(items);
   fireSearchConditionOrderChangedEvent();
 }
  public void addItem(SearchConditionItem i, int atIndex) {

    i.addListener(this);
    i.setParent(this);
    items.add(atIndex, i);
    fireSearchConditionOrderChangedEvent();
    fireSearchConditionItemAddedEvent(i);
  }
 public void moveItemToGroupAtIndex(
     SearchConditionItem i, SearchConditionGroupItem g, int newIndex) {
   i.removeListener(this);
   items.remove(i);
   i.setParent(null);
   g.addItem(i);
   g.moveItemToIndex(i, newIndex);
   fireSearchConditionOrderChangedEvent();
 }
  public SearchConditionGroupItem(
      QueryRelation r, SearchConditionItem i, SearchConditionGroupItem parent) {
    super(null, r, parent);

    thisGroupNo = (++groupNo);

    items = new ArrayList<SearchConditionItem>();
    if (i != null) {
      items.add(i);
      i.setParent(this);
      i.addListener(this);
    }
  }
  public void moveItemToGroup(SearchConditionItem i, SearchConditionGroupItem g) {
    i.removeListener(this);
    items.remove(i);
    i.setParent(null);
    g.addItem(i);

    if (items.size() < 1) {
      if (getParent() != null) {
        getParent().removeItem(this);
      }
    } else {
      fireSearchConditionOrderChangedEvent();
    }
  }
 public void createGroupFromItem(SearchConditionItem i) {
   i.removeListener(this);
   int indexOfItem = items.indexOf(i);
   items.remove(i);
   SearchConditionGroupItem g = new SearchConditionGroupItem(QueryRelation.AND, i, this);
   addItem(g, indexOfItem);
 }
  @Override
  public String toString() {
    String s = "";

    for (int i = 0; i < items.size(); i++) {
      SearchConditionItem item = items.get(i);
      if (item instanceof SearchConditionGroupItem) {
        s += "(" + item.toString() + ")";
      } else {
        s += item.toString();
      }
      if (i != items.size()) {
        s += " " + this.getRelation() + " ";
      }
    }

    return s;
  }
  protected String toXML(int indent) {
    String tab = "";
    for (int i = 0; i < indent; ++i) {
      tab += "\t";
    }

    String xml = tab + "<Group ";
    xml += " queryRelation=\"" + escape(getRelation().toString()) + "\"";
    if (getDescription() != null) {
      xml += " description=\"" + escape(getDescription()) + "\"";
    }
    xml += ">\n";

    for (SearchConditionItem sci : items) {
      xml += sci.toXML(indent + 1);
    }
    xml += tab + "</Group>\n";
    return xml;
  }
  public void removeItem(SearchConditionItem i) {
    i.removeListener(this);
    items.remove(i);
    i.setParent(null);

    // the only child is a group
    // Commented out, June 8,2013
    // The group functionality is being used to search Genomic Regions (sets
    // of (chromosome, position) tuples), and it's easier if these always
    // stay grouped.
    /*
            if (items.size() == 1 && items.get(0) instanceof SearchConditionGroupItem) {

                SearchConditionGroupItem child = (SearchConditionGroupItem) items.get(0);
                for (SearchConditionItem c : child.getItems()) {
                    c.removeListener(child);
                    child.items.remove(i);
                    c.setParent(null);

                    c.addListener(this);
                    c.setParent(this);
                    items.add(c);
                }

                child.removeListener(this);
                this.items.remove(child);
                child.setParent(null);

            }
    */
    // remove the group entirely, parent notifies of update
    if (items.isEmpty() && this.getParent() != null) {
      this.getParent().removeItem(this);

      // notify listeners of change
    } else {
      fireSearchConditionOrderChangedEvent();
      fireSearchConditionItemRemovedEvent(i);
    }
  }