/**
   * Locate each unexpanded Structure|Sequence and: 1. check that none of its fields is referenced
   * => do not expand 2. add all of its fields as leaves Note that #2 may end up adding additional
   * leaf structs &/or seqs
   */
  public void expand() {
    // Create a queue of unprocessed leaf compounds
    Queue<DapVariable> queue = new ArrayDeque<DapVariable>();

    for (int i = 0; i < variables.size(); i++) {
      DapVariable var = variables.get(i);
      if (!var.isTopLevel()) continue;
      // prime the queue
      if (var.getSort() == DapSort.STRUCTURE || var.getSort() == DapSort.SEQUENCE) {
        DapStructure struct = (DapStructure) var; // remember Sequence subclass Structure
        if (expansionCount(struct) == 0) queue.add(var);
      }
    }
    // Process the queue in prefix order
    while (queue.size() > 0) {
      DapVariable vvstruct = queue.remove();
      DapStructure dstruct = (DapStructure) vvstruct;
      for (DapVariable field : dstruct.getFields()) {
        if (findVariableIndex(field) < 0) {
          // Add field as leaf
          this.segments.add(new Segment(field));
          this.variables.add(field);
        }
        if (field.getSort() == DapSort.STRUCTURE || field.getSort() == DapSort.SEQUENCE) {
          if (expansionCount((DapStructure) field) == 0) queue.add(field);
        }
      }
    }
    this.expansion = Expand.EXPANDED;
  }
 /**
  * Count the number of fields of a structure that already in this view.
  *
  * @param struct the dapstructure to check
  * @return # of fields in this view
  * @throws DapException
  */
 protected int expansionCount(DapStructure struct) {
   int count = 0;
   for (DapVariable field : struct.getFields()) {
     if (findVariableIndex(field) >= 0) count++;
   }
   return count;
 }
 public void structure(DapStructure struct) throws DapException {
   List<DapVariable> fields = struct.getFields();
   Odometer odom = null;
   if (struct.getRank() == 0) { // scalar
     odom = new ScalarOdometer();
   } else { // dimensioned
     List<Slice> slices = ce.getConstrainedSlices(struct);
     odom = Odometer.factory(slices, struct.getDimensions(), false);
   }
   while (odom.hasNext()) {
     // generate a value for each field recursively
     for (int i = 0; i < fields.size(); i++) {
       DapVariable field = fields.get(i);
       variable(field);
     }
     odom.next();
   }
 }
 /**
  * Recursive helper for tostring/toConstraintString
  *
  * @param seg
  * @param buf
  * @param forconstraint
  */
 protected void dumpvar(Segment seg, StringBuilder buf, boolean forconstraint) {
   if (seg.var.isTopLevel()) buf.append(seg.var.getFQN());
   else buf.append(seg.var.getShortName());
   List<DapDimension> dimset = seg.var.getDimensions();
   // Add any slices
   List<Slice> slices = seg.slices;
   if (slices == null) dimset = new ArrayList<DapDimension>();
   else assert dimset.size() == slices.size();
   for (int i = 0; i < dimset.size(); i++) {
     Slice slice = slices.get(i);
     DapDimension dim = dimset.get(i);
     try {
       buf.append(forconstraint ? slice.toConstraintString() : slice.toString());
     } catch (DapException de) {
     }
   }
   // if the var is atomic, then we are done
   if (seg.var.getSort() == DapSort.ATOMICVARIABLE) return;
   // If structure and all fields are in the view, then done
   if (seg.var.getSort() == DapSort.STRUCTURE || seg.var.getSort() == DapSort.SEQUENCE) {
     if (!isWholeCompound((DapStructure) seg.var)) {
       // Need to insert {...} and recurse
       buf.append(LBRACE);
       DapStructure struct = (DapStructure) seg.var;
       boolean first = true;
       for (DapVariable field : struct.getFields()) {
         if (!first) buf.append(";");
         first = false;
         Segment fseg = findSegment(field);
         dumpvar(fseg, buf, forconstraint);
       }
       buf.append(RBRACE);
     }
     if (seg.var.getSort() == DapSort.SEQUENCE && seg.filter != null) {
       buf.append("|");
       buf.append(seg.filter.toString());
     }
   }
 }
 /**
  * Recursive helper
  *
  * @param dstruct to contract
  * @param contracted set of already contracted compounds
  * @return true if this structure was contracted, false otherwise
  */
 protected boolean contractR(DapStructure dstruct, Set<DapStructure> contracted) {
   if (contracted.contains(dstruct)) return true;
   int processed = 0;
   List<DapVariable> fields = dstruct.getFields();
   for (DapVariable field : fields) {
     if (findVariableIndex(field) < 0) break; // this compound cannot be contracted
     if ((field.getSort() == DapSort.STRUCTURE || field.getSort() == DapSort.SEQUENCE)
         && !contracted.contains((DapStructure) field)) {
       if (!contractR((DapStructure) field, contracted))
         break; // this compound cannot be contracted
     }
     processed++;
   }
   if (processed < fields.size()) return false;
   contracted.add(dstruct); // all compound fields were successfully contracted.
   return true;
 }
 /**
  * See if a structure is "whole", which means that none of its fields is missing from the
  * constraint, all of fields use default (non-constrained) dimension), and all of its fields are
  * also whole. This must be done recursively.
  *
  * @param dstruct to test
  * @return true if this structure is whole.
  */
 protected boolean isWholeCompound(DapStructure dstruct) {
   int processed = 0;
   List<DapVariable> fields = dstruct.getFields();
   for (DapVariable field : fields) {
     // not contractable if this field has non-original dimensions
     Segment seg = findSegment(field);
     if (seg == null) break; // this compound is not whole
     List<Slice> slices = seg.slices;
     if (slices != null) {
       for (Slice slice : slices) {
         if (slice.isConstrained()) break;
       }
     }
     if (field.getSort() == DapSort.STRUCTURE || field.getSort() == DapSort.SEQUENCE) {
       if (!isWholeCompound((DapStructure) field)) break; // this compound is not whole
     }
     processed++;
   }
   return (processed == fields.size());
 }