/** * 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; }
/** Walk all the included declarations and accumulate the set of referenced groups */ protected void computegroups() { // 1. variables for (int i = 0; i < variables.size(); i++) { DapVariable var = variables.get(i); List<DapGroup> path = var.getGroupPath(); for (DapGroup group : path) { if (!this.groups.contains(group)) this.groups.add(group); } } // 2. Dimensions for (DapDimension dim : this.dimrefs) { if (!dim.isShared()) continue; List<DapGroup> path = dim.getGroupPath(); for (DapGroup group : path) { if (!this.groups.contains(group)) this.groups.add(group); } } // 2. enumerations for (DapEnum en : this.enums) { List<DapGroup> path = en.getGroupPath(); for (DapGroup group : path) { if (!this.groups.contains(group)) this.groups.add(group); } } }
/** Walk all the included variables and accumulate the referenced enums */ protected void computeenums() { for (int i = 0; i < variables.size(); i++) { DapVariable var = variables.get(i); if (var.getSort() != DapSort.ATOMICVARIABLE) continue; DapType daptype = var.getBaseType(); if (!daptype.isEnumType()) continue; if (!this.enums.contains((DapEnum) daptype)) this.enums.add((DapEnum) daptype); } }
/** * Convert the view to a constraint string suitable for use in a URL, except not URL encoded. * * @return constraint string */ public String toConstraintString() { StringBuilder buf = new StringBuilder(); boolean first = true; for (int i = 0; i < segments.size(); i++) { Segment seg = segments.get(i); if (!seg.var.isTopLevel()) continue; if (!first) buf.append(";"); first = false; dumpvar(seg, buf, true); } return buf.toString(); }
/** * Locate each Structure|Sequence and: 1. check that all of its fields are referenced recursively * and not constrained, otherwise ignore 2. contract by removing all of the fields of the * Structure or Sequence. This is intended to be (not quite) the dual of expand(); */ public void contract() { // Create a set of contracted compounds Set<DapStructure> contracted = new HashSet<>(); for (int i = 0; i < variables.size(); i++) { DapVariable var = variables.get(i); if (var.isTopLevel()) { if (var.getSort() == DapSort.STRUCTURE || var.getSort() == DapSort.SEQUENCE) { contractR((DapStructure) var, contracted); } } } this.expansion = Expand.CONTRACTED; }
/** * 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; }
/** * Compute dimension related information using slicing and redef info. In effect, this is where * projection constraints are applied * * <p>Assume that the constraint compiler has given us the following info: * * <ol> * <li>A list of the variables to include. * <li>A pair (DapDimension,Slice) for each redef * <li>For each variable in #1, a list of slices taken from the constraint expression * </ol> * * <p>Two products will be produced. * * <ol> * <li>The variables map will be modified so that the slices properly reflect any original or * redef dimensions. * <li>A set, dimrefs, of all referenced original dimensions. * </ol> * * <p>The processing is as follows * * <ol> * <li>For each redef create a new redef dimension * <li>For each variable: * <ol> * <li>if the variable is scalar, do nothing. * <li>if the variable has no associated slices, then make its new dimensions be the * original dimensions. * <li>otherwise, walk the slices and create new dimensions from them; use redefs where * indicated * <li> * </ol> * </ol> */ protected void computedimensions() throws DapException { // Build the redefmap for (DapDimension key : redefslice.keySet()) { Slice slice = redefslice.get(key); DapDimension newdim = (DapDimension) key.clone(); newdim.setSize(slice.getCount()); redef.put(key, newdim); } // Process each variable for (int i = 0; i < segments.size(); i++) { Segment seg = segments.get(i); if (seg.var.getRank() == 0) continue; List<Slice> slices = seg.slices; List<DapDimension> orig = seg.var.getDimensions(); List<DapDimension> newdims = new ArrayList<>(); // If the slice list is short then pad it with // default slices if (slices == null) slices = new ArrayList<Slice>(); while (slices.size() < orig.size()) // pad { slices.add(new Slice().setConstrained(false)); } assert (slices != null && slices.size() == orig.size()); for (int j = 0; j < slices.size(); j++) { Slice slice = slices.get(j); DapDimension dim0 = orig.get(j); DapDimension newdim = redef.get(dim0); if (newdim == null) newdim = dim0; // fill in the undefined last value slice.setMaxSize(newdim.getSize()); slice.finish(); Slice newslice = null; if (slice.isConstrained()) { // Construct an anonymous dimension for this slice newdim = new DapDimension(slice.getCount()); } else { // replace with a new slice from the dim newslice = new Slice(newdim); if (newslice != null) { // track set of referenced non-anonymous dimensions if (!dimrefs.contains(dim0)) dimrefs.add(dim0); slices.set(j, newslice); } } // record the dimension per variable newdims.add(newdim); } seg.setDimset(newdims); } }
/** * 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()); }
protected Segment findSegment(DapVariable var) { for (int i = 0; i < segments.size(); i++) { if (segments.get(i).var == var) return segments.get(i); } return null; }
/* Search the set of variables */ protected int findVariableIndex(DapVariable var) { for (int i = 0; i < variables.size(); i++) { if (variables.get(i) == var) return i; } return -1; }