public boolean expand(Writer output, Serializable object, RuleOptions options)
      throws IOException, RuleException {
    boolean expanded = false;
    boolean prefixPrinted = false;
    boolean atLeastOneChildPrinted = false;

    if (object == null) {
      if (Debug.isDebug()) {
        throw new NullPointerException(); // disclose programming error
        // in debug..
      } else {
        return false;
      }
    } // end if

    if (!(object instanceof DbObject)) {
      return false;
    } // end if

    DbObject dbObject = (DbObject) object;
    MetaRelationship metaRelation = getMetaRelation(dbObject);
    MetaClass childrenMetaClass = getChildrenMetaClass();

    try {
      // get metaRelation from its string representation
      if (metaRelation == null)
        metaRelation = (MetaRelationship) getMetaField(dbObject, sConnector);

      boolean state[] = {prefixPrinted, atLeastOneChildPrinted};

      if (metaRelation instanceof MetaRelation1) {
        MetaRelation1 metaRelation1 = (MetaRelation1) metaRelation;
        expanded |=
            expandMetaRelation1(output, dbObject, metaRelation1, state, childrenMetaClass, options);
      } else if (metaRelation instanceof MetaRelationN) {
        expanded |=
            expandMetaRelationN(output, dbObject, metaRelation, state, childrenMetaClass, options);
      } else if (metaRelation instanceof MetaChoice) {
        MetaChoice choice = (MetaChoice) metaRelation;
        expanded |= expandMetaChoice(output, dbObject, choice, state, childrenMetaClass, options);
      } else {
        // TODO: throw 'meta-relationship not supported'
      }

      super.terminate(output, options);
    } catch (DbException ex) {
      String msg = ex.getMessage();
      throw new RuleException(msg);
    } catch (RuntimeException ex) {
      String conn = m_connector.getGUIName();
      String objectKind = dbObject.getMetaClass().getGUIName();
      String msg = InvalidConnectorRuleException.buildMessage(m_ruleName, conn, objectKind);
      throw new InvalidConnectorRuleException(msg);
    }

    return expanded;
  }
  // set actual actual subrules from its string rule;
  public void setActualSubRules(Hashtable table) throws RuleException {
    // find childRule from stringRule
    childRule = (Rule) table.get(m_childStrRule);
    if (m_oneChildStrRule != null) {
      oneChildRule = (Rule) table.get(m_oneChildStrRule);
    }

    // change modifiers with their actual rules
    super.setActualModifiers(table);
  }
  public boolean expandChild(
      Writer output,
      DbObject parent,
      DbObject child,
      int nbChildren,
      boolean state[], // for {prefixPrinted,
      // atLeastOneChildPrinted}
      RuleOptions options)
      throws DbException, IOException, RuleException {
    boolean expanded = false;
    boolean prefixPrinted = state[0];
    boolean atLeastOneChildPrinted = state[1];

    // If no 'WHEN MODIFIER' defined, or one defined that returns true
    // if ((m_whenClause == null) ||
    boolean condition = true;
    if (m_booleanModifier != null) {
      condition = m_booleanModifier.evaluate(child);
    } // end if

    if (condition) {
      // Make sure a child has been defined
      if (childRule == null) {
        String msg = NoChildRuleException.buildMessage(m_ruleName, m_childStrRule);
        throw new NoChildRuleException(msg);
      }

      // create a string buffer
      StringWriter sw = new StringWriter();

      // take CHILD rule, except if there is one element and ONECHILD rule
      // is defined
      Rule ruleToUse = childRule;
      if ((nbChildren == 1) && (oneChildRule != null)) {
        ruleToUse = oneChildRule;
      }

      // expand the child in the string buffer
      boolean childExpanded = ruleToUse.expand(sw, child, options);

      if (childExpanded) {
        // if there is a prefix to print
        if ((prefixModifier != null) && (!prefixPrinted)) {
          expanded |= prefixModifier.expand(output, parent, options);
          prefixPrinted = true;
        } else { // prefix already printed, or no prefix
          if (atLeastOneChildPrinted) {
            if (separatorModifier != null) {
              // expand the SEParator right before to print the
              // child
              expanded |= separatorModifier.expand(output, parent, options);
            } // end if
          } // end if
        } // end if

        // copy the string buffer into the output stream
        output.write(sw.toString());
      } // end if

      expanded |= childExpanded;
      if (childExpanded) {
        atLeastOneChildPrinted = true;
      }
    } // end if

    state[0] = prefixPrinted;
    state[1] = atLeastOneChildPrinted;
    return expanded;
  }