/* Copyright 2008-2010 Gephi Authors : Mathieu Bastian, Cezary Bartosiak Website : http://www.gephi.org This file is part of Gephi. Gephi is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Gephi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with Gephi. If not, see <http://www.gnu.org/licenses/>. */ package org.gephi.data.attributes.type; import java.util.ArrayList; import java.util.Hashtable; import java.util.List; import org.gephi.data.attributes.api.AttributeUtils; import org.gephi.data.attributes.api.Estimator; /** * Complex type for specifying time interval. An, interval is two * <code>double</code> with <code>low</code> inferior or equal to * <code>high</code>. Thus intervals can have got included or excluded * bounds. * * @author Mathieu Bastian, Cezary Bartosiak */ public final class TimeInterval extends DynamicType<Double[]> { /** * Constructs a new {@code DynamicType} instance with no intervals. */ public TimeInterval() { super(); } /** * Constructs a new {@code DynamicType} instance that contains a given * {@code interval}. * * @param low the left endpoint * @param high the right endpoint * @param lopen indicates if the left endpoint is excluded (true in this case) * @param ropen indicates if the right endpoint is excluded (true in this case) */ public TimeInterval(double low, double high, boolean lopen, boolean ropen) { super(new Interval<Double[]>(low, high, lopen, ropen)); } /** * Constructs a new {@code DynamicType} instance that contains a given * {@code interval} [{@code low}, {@code high}]. * * @param low the left endpoint * @param high the right endpoint */ public TimeInterval(double low, double high) { super(new Interval<Double[]>(low, high)); } /** * Constructs a new {@code DynamicType} instance with intervals given by * {@code List<Interval>} in. * * @param in intervals to add (could be null) */ public TimeInterval(List<Interval> in) { super(getList(in)); } /** * Constructs a deep copy of {@code source}. * * @param source an object to copy from (could be null, then completely new * instance is created) */ public TimeInterval(TimeInterval source) { super(source); } /** * Constructs a deep copy of {@code source} that contains a given * {@code interval}. * * @param source an object to copy from (could be null, then completely new * instance is created) * @param low the left endpoint * @param high the right endpoint * @param lopen indicates if the left endpoint is excluded (true in this case) * @param ropen indicates if the right endpoint is excluded (true in this case) */ public TimeInterval(TimeInterval source, double low, double high, boolean lopen, boolean ropen) { super(source, new Interval<Double[]>(low, high, lopen, ropen)); } /** * Constructs a deep copy of {@code source} that contains a given * {@code interval} [{@code low}, {@code high}]. * * @param source an object to copy from (could be null, then completely new * instance is created) * @param low the left endpoint * @param high the right endpoint */ public TimeInterval(TimeInterval source, double low, double high) { super(source, new Interval<Double[]>(low, high)); } /** * Constructs a deep copy of {@code source} that contains a given * {@code interval} [{@code alow}, {@code ahigh}]. Before add it removes * from the newly created object all intervals that overlap with a given * {@code interval} [{@code rlow}, {@code rhigh}]. * * @param source an object to copy from (could be null, then completely new * instance is created) * @param alow the left endpoint of the interval to add * @param ahigh the right endpoint of the interval to add * @param alopen indicates if the left endpoint of the interval to add is excluded (true in this case) * @param aropen indicates if the right endpoint of the interval to add is excluded (true in this case) * @param rlow the left endpoint of the interval to remove * @param rhigh the right endpoint of the interval to remove * @param blopen indicates if the left endpoint of the interval to remove is excluded (true in this case) * @param bropen indicates if the right endpoint of the interval to remove is excluded (true in this case) */ public TimeInterval(TimeInterval source, double alow, double ahigh, boolean alopen, boolean aropen, double rlow, double rhigh, boolean blopen, boolean bropen) { super(source, new Interval<Double[]>(alow, ahigh, alopen, aropen), new Interval<Double[]>(rlow, rhigh, blopen, bropen)); } /** * Constructs a deep copy of {@code source} that contains a given * {@code interval} [{@code alow}, {@code ahigh}]. Before add it removes * from the newly created object all intervals that overlap with a given * {@code interval} [{@code rlow}, {@code rhigh}]. * * @param source an object to copy from (could be null, then completely new * instance is created) * @param alow the left endpoint of the interval to add * @param ahigh the right endpoint of the interval to add * @param rlow the left endpoint of the interval to remove * @param rhigh the right endpoint of the interval to remove */ public TimeInterval(TimeInterval source, double alow, double ahigh, double rlow, double rhigh) { super(source, new Interval<Double[]>(alow, ahigh), new Interval<Double[]>(rlow, rhigh)); } /** * Constructs a deep copy of {@code source} with additional intervals * given by {@code List<Interval>} in. * * @param source an object to copy from (could be null, then completely new * instance is created) * @param in intervals to add (could be null) */ public TimeInterval(TimeInterval source, List<Interval> in) { super(source, getList(in)); } /** * Constructs a deep copy of {@code source} with additional intervals * given by {@code List<Interval>} in. Before add it removes from the * newly created object all intervals that overlap with intervals given by * {@code List<Interval>} out. * * @param source an object to copy from (could be null, then completely new * instance is created) * @param in intervals to add (could be null) * @param out intervals to remove (could be null) */ public TimeInterval(TimeInterval source, List<Interval> in, List<Interval> out) { super(source, getList(in), getList(out)); } private static List<Interval<Double[]>> getList(List<Interval> arg) { if (arg == null) return null; List<Interval<Double[]>> list = new ArrayList<Interval<Double[]>>(); for (Interval item : arg) list.add(new Interval<Double[]>(item.getLow(), item.getHigh(), item.isLowExcluded(), item.isHighExcluded())); return list; } @Override public Double[] getValue(Interval interval, Estimator estimator) { List<Double[]> values = getValues(interval); if (values.isEmpty()) return null; switch (estimator) { case AVERAGE: throw new UnsupportedOperationException( "Not supported estimator"); case MEDIAN: if (values.size() % 2 == 1) return values.get(values.size() / 2); return values.get(values.size() / 2 - 1); case MODE: Hashtable<Integer, Integer> map = new Hashtable<Integer, Integer>(); for (int i = 0; i < values.size(); ++i) { int prev = 0; if (map.containsKey(values.get(i).hashCode())) prev = map.get(values.get(i).hashCode()); map.put(values.get(i).hashCode(), prev + 1); } int max = map.get(values.get(0).hashCode()); int index = 0; for (int i = 1; i < values.size(); ++i) if (max < map.get(values.get(i).hashCode())) { max = map.get(values.get(i).hashCode()); index = i; } return values.get(index); case SUM: throw new UnsupportedOperationException( "Not supported estimator"); case MIN: throw new UnsupportedOperationException( "Not supported estimator"); case MAX: throw new UnsupportedOperationException( "Not supported estimator"); case FIRST: return values.get(0); case LAST: return values.get(values.size() - 1); default: throw new IllegalArgumentException("Unknown estimator."); } } @Override public List<Double[]> getValues(Interval interval) { List<Double[]> result = new ArrayList<Double[]>(); for (Interval<Double[]> i : intervalTree.search(interval)) result.add(new Double[] { i.getLow(), i.getHigh() }); return result; } @Override public Class getUnderlyingType() { return Double[].class; } @Override public String toString(boolean timesAsDoubles) { if (timesAsDoubles) return toString(); return toStringTimesAsDates(); } /** * Returns a string representation of this instance in a format * {@code <[low, high], ..., [low, high]>}. Intervals are * ordered by its left endpoint. * * <p>Times are always shown as dates.</p> * * @return a string representation of this instance. */ public String toStringTimesAsDates() { List<Interval<Double[]>> list = getIntervals(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY); if (!list.isEmpty()) { StringBuilder sb = new StringBuilder("<"); sb.append(list.get(0).isLowExcluded() ? "(" : "[").append(AttributeUtils.getXMLDateStringFromDouble( list.get(0).getLow())).append(", ").append(AttributeUtils.getXMLDateStringFromDouble( list.get(0).getHigh())).append(list.get(0).isHighExcluded() ? ")" : "]"); for (int i = 1; i < list.size(); ++i) sb.append("; ").append(list.get(i).isLowExcluded() ? "(" : "[").append(AttributeUtils. getXMLDateStringFromDouble(list.get(i).getLow())).append(", ").append(AttributeUtils. getXMLDateStringFromDouble(list.get(i).getHigh())).append(list.get(i).isHighExcluded() ? ")" : "]"); sb.append(">"); return sb.toString(); } return "<empty>"; } /** * Returns a string representation of this instance in a format * {@code <[low, high], ..., [low, high]>}. Intervals are * ordered by its left endpoint. * * @return a string representation of this instance. */ @Override public String toString() { List<Interval<Double[]>> list = getIntervals(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY); if (!list.isEmpty()) { StringBuilder sb = new StringBuilder("<"); sb.append(list.get(0).isLowExcluded() ? "(" : "[").append(list.get(0).getLow()).append(", "). append(list.get(0).getHigh()).append(list.get(0).isHighExcluded() ? ")" : "]"); for (int i = 1; i < list.size(); ++i) sb.append("; ").append(list.get(i).isLowExcluded() ? "(" : "[").append(list.get(i).getLow()). append(", ").append(list.get(i).getHigh()).append(list.get(i).isHighExcluded() ? ")" : "]"); sb.append(">"); return sb.toString(); } return "<empty>"; } }