/** * Returns the matching interval. * * @param index * @param type * @param tValue * @return */ @SuppressWarnings("unchecked") private Interval<T> getIntervalUpperSnap( IndexNode index, DataTypeWithRatioScale<T> type, T tValue) { // Find interval double shift = Math.floor( type.ratio(type.subtract(tValue, index.min), type.subtract(index.max, index.min))); T offset = type.multiply(type.subtract(index.max, index.min), shift); T value = type.subtract(tValue, offset); Interval<T> interval = null; for (int j = 0; j < intervals.size(); j++) { Interval<T> i = intervals.get(j); if (type.compare(i.min, value) <= 0 && type.compare(i.max, value) > 0) { // If on lower bound, use next-lower interval if (type.compare(value, i.min) == 0) { if (j > 0) { // Simply use the next one interval = intervals.get(j - 1); break; } else { // Wrap around interval = intervals.get(intervals.size() - 1); offset = type.multiply(type.subtract(index.max, index.min), shift - 1); break; } } else { interval = i; break; } } } if (interval == null && intervals.size() == 1) { interval = intervals.get(0); } // Check if (interval == null) { throw new IllegalStateException("Internal error. Sorry for that!"); } // Create first result interval T lower = type.add(interval.min, offset); T upper = type.add(interval.max, offset); return new Interval<T>(this, (DataType<T>) type, lower, upper, interval.function); }
/** * Returns the matching interval. * * @param index * @param type * @param tValue * @return */ @SuppressWarnings("unchecked") private Interval<T> getInterval(IndexNode index, DataTypeWithRatioScale<T> type, T tValue) { // Find interval int shift = (int) Math.floor( type.ratio(type.subtract(tValue, index.min), type.subtract(index.max, index.min))); T offset = type.multiply(type.subtract(index.max, index.min), shift); Interval<T> interval = getInterval(index, type.subtract(tValue, offset)); // Check if (interval == null) { throw new IllegalStateException("No interval found for: " + type.format(tValue)); } // Create first result interval T lower = type.add(interval.min, offset); T upper = type.add(interval.max, offset); return new Interval<T>(this, (DataType<T>) type, lower, upper, interval.function); }
@Override @SuppressWarnings("unchecked") protected AbstractGroup[][] prepareGroups() { // Check String valid = isValid(); if (valid != null) { throw new IllegalArgumentException(valid); } // Create adjustments Range<T>[] ranges = getAdjustedRanges(); Range<T> tempLower = ranges[0]; Range<T> tempUpper = ranges[1]; // Build leaf level index ArrayList<IndexNode> nodes = new ArrayList<IndexNode>(); for (int i = 0, len = intervals.size(); i < len; i += INDEX_FANOUT) { int min = i; int max = Math.min(i + INDEX_FANOUT - 1, len - 1); List<Interval<T>> leafs = new ArrayList<Interval<T>>(); for (int j = min; j <= max; j++) { leafs.add(intervals.get(j)); } nodes.add( new IndexNode( intervals.get(min).min, intervals.get(max).max, leafs.toArray(new Interval[leafs.size()]))); } // Builder inner nodes while (nodes.size() > 1) { List<IndexNode> current = (List<IndexNode>) nodes.clone(); nodes.clear(); for (int i = 0, len = current.size(); i < len; i += INDEX_FANOUT) { int min = i; int max = Math.min(i + INDEX_FANOUT - 1, len - 1); List<IndexNode> temp = new ArrayList<IndexNode>(); for (int j = min; j <= max; j++) { temp.add(current.get(j)); } nodes.add( new IndexNode( current.get(min).min, current.get(max).max, temp.toArray(new HierarchyBuilderIntervalBased.IndexNode[temp.size()]))); } } // Prepare String[] data = getData(); List<AbstractGroup[]> result = new ArrayList<AbstractGroup[]>(); IndexNode index = nodes.get(0); // Prepare DataTypeWithRatioScale<T> type = (DataTypeWithRatioScale<T>) getDataType(); Map<AbstractGroup, AbstractGroup> cache = new HashMap<AbstractGroup, AbstractGroup>(); // Create snap intervals Interval<T> lowerSnap = getInterval(index, type, tempLower.repeatBound); lowerSnap = new Interval<T>( this, getDataType(), tempLower.snapBound, lowerSnap.max, lowerSnap.function); Interval<T> upperSnap = getIntervalUpperSnap(index, type, tempUpper.repeatBound); upperSnap = new Interval<T>( this, getDataType(), upperSnap.min, tempUpper.snapBound, upperSnap.function); // Overlapping snaps -> one interval if (type.compare(lowerSnap.max, upperSnap.min) > 0) { // We could use lowerSnap.function or upperSnap.function lowerSnap = new Interval<T>(this, getDataType(), lowerSnap.min, upperSnap.max, lowerSnap.function); upperSnap = lowerSnap; } // Create first column AbstractGroup[] first = new AbstractGroup[data.length]; for (int i = 0; i < data.length; i++) { T value = type.parse(data[i]); Interval<T> interval; if (value == null) { interval = new Interval<T>(this); } else if (type.compare(value, tempLower.labelBound) < 0) { throw new IllegalArgumentException(type.format(value) + " is < lower label bound"); } else if (type.compare(value, tempLower.snapBound) < 0) { interval = new Interval<T>(this, true, tempLower.snapBound); } else if (type.compare(value, tempUpper.labelBound) >= 0) { throw new IllegalArgumentException(type.format(value) + " is >= upper label bound"); } else if (type.compare(value, tempUpper.snapBound) >= 0) { interval = new Interval<T>(this, false, tempUpper.snapBound); } else { interval = getInterval(index, type, value); } if (interval.min != null && interval.max != null) { if (type.compare(interval.min, lowerSnap.max) < 0) { interval = lowerSnap; } else if (type.compare(interval.max, upperSnap.min) > 0) { interval = upperSnap; } } first[i] = getGroup(cache, interval); } result.add(first); // Clean index = null; // Create other columns List<Group<T>> groups = new ArrayList<Group<T>>(); if (!super.getLevels().isEmpty()) groups = super.getLevels().get(0).getGroups(); if (cache.size() > 1 && !groups.isEmpty()) { // Prepare List<Interval<T>> newIntervals = new ArrayList<Interval<T>>(); int intervalIndex = 0; int multiplier = 0; T width = type.subtract(intervals.get(intervals.size() - 1).max, intervals.get(0).min); // Merge intervals for (Group<T> group : groups) { // Find min and max T min = null; T max = null; for (int i = 0; i < group.getSize(); i++) { Interval<T> current = intervals.get(intervalIndex++); T offset = type.multiply(width, multiplier); T cMin = type.add(current.min, offset); T cMax = type.add(current.max, offset); if (min == null || type.compare(min, cMin) > 0) { min = cMin; } if (max == null || type.compare(max, cMax) < 0) { max = cMax; } if (intervalIndex == intervals.size()) { intervalIndex = 0; multiplier++; } } // Add interval newIntervals.add(new Interval<T>(this, getDataType(), min, max, group.getFunction())); } // Compute next column HierarchyBuilderIntervalBased<T> builder = new HierarchyBuilderIntervalBased<T>(getDataType(), tempLower, tempUpper); for (Interval<T> interval : newIntervals) { builder.addInterval(interval.min, interval.max, interval.function); } for (int i = 1; i < super.getLevels().size(); i++) { for (Group<T> sgroup : super.getLevel(i).getGroups()) { builder.getLevel(i - 1).addGroup(sgroup.getSize(), sgroup.getFunction()); } } // Copy data builder.prepare(data); AbstractGroup[][] columns = builder.getPreparedGroups(); for (AbstractGroup[] column : columns) { result.add(column); } } else { if (cache.size() > 1) { AbstractGroup[] column = new AbstractGroup[data.length]; @SuppressWarnings("serial") AbstractGroup element = new AbstractGroup(DataType.ANY_VALUE) {}; for (int i = 0; i < column.length; i++) { column[i] = element; } result.add(column); } } // Return return result.toArray(new AbstractGroup[0][0]); }