/*
* Copyright 2008-2010 Gephi
* Authors : 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.dynamic;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.GregorianCalendar;
import java.util.List;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import org.gephi.data.attributes.api.AttributeType;
import org.gephi.data.attributes.api.Estimator;
import org.gephi.data.attributes.type.DynamicBigDecimal;
import org.gephi.data.attributes.type.DynamicBigInteger;
import org.gephi.data.attributes.type.DynamicBoolean;
import org.gephi.data.attributes.type.DynamicByte;
import org.gephi.data.attributes.type.DynamicCharacter;
import org.gephi.data.attributes.type.DynamicDouble;
import org.gephi.data.attributes.type.DynamicFloat;
import org.gephi.data.attributes.type.DynamicInteger;
import org.gephi.data.attributes.type.DynamicLong;
import org.gephi.data.attributes.type.DynamicShort;
import org.gephi.data.attributes.type.DynamicString;
import org.gephi.data.attributes.type.DynamicType;
import org.gephi.data.attributes.type.Interval;
import org.gephi.data.attributes.type.TimeInterval;
import org.gephi.dynamic.api.DynamicModel;
import org.openide.util.Exceptions;
/**
* Contains only static, and toolkit functions, like type conversion
* for the needs of dynamic stuff.
*
* @author Cezary Bartosiak
*/
public final class DynamicUtilities {
/**
* Used for import (parses XML date strings).
*
* @param str a string to parse from
*
* @return date as a double.
*
* @throws IllegalArgumentException if {@code str} is not a valid {@code XMLGregorianCalendar}.
* @throws NullPointerException if {@code str} is null.
*/
public static double getDoubleFromXMLDateString(String str) {
try {
DatatypeFactory dateFactory = DatatypeFactory.newInstance();
return dateFactory.newXMLGregorianCalendar(str.length() > 23 ? str.substring(0, 23) : str).
toGregorianCalendar().getTimeInMillis();
} catch (DatatypeConfigurationException ex) {
Exceptions.printStackTrace(ex);
return 0.0;
}
}
/**
* Used for export (writes XML date strings).
*
* @param d a double to convert from
*
* @return an XML date string.
*
* @throws IllegalArgumentException if {@code d} is infinite.
*/
public static String getXMLDateStringFromDouble(double d) {
try {
DatatypeFactory dateFactory = DatatypeFactory.newInstance();
if (Double.isInfinite(d)) {
throw new IllegalArgumentException("The passed double cannot be infinite.");
}
GregorianCalendar gc = new GregorianCalendar();
gc.setTimeInMillis((long) d);
return dateFactory.newXMLGregorianCalendar(gc).toXMLFormat().substring(0, 23);
} catch (DatatypeConfigurationException ex) {
Exceptions.printStackTrace(ex);
return "";
}
}
/**
* Returns a new {@code DynamicType} instance that contains a given
* {@code Interval} in.
*
* @param in interval to add (could be null)
*
* @return a new {@code DynamicType} instance that contains a given
* {@code Interval} in.
*/
public static DynamicType createDynamicObject(AttributeType type, Interval in) {
return createDynamicObject(type, null, in);
}
/**
* Returns a new {@code DynamicType} instance with intervals given by
* {@code List<Interval>} in.
*
* @param in intervals to add (could be null)
*
* @return a new {@code DynamicType} instance with intervals given by
* {@code List<Interval>} in.
*/
public static DynamicType createDynamicObject(AttributeType type, List<Interval> in) {
return createDynamicObject(type, null, in);
}
/**
* Returns a deep copy of {@code source}.
*
* @param source an object to copy from (could be null, then completely new
* instance is created)
*
* @return a deep copy of {@code source}.
*/
public static DynamicType createDynamicObject(AttributeType type, DynamicType source) {
return createDynamicObject(type, source, (Interval) null, (Interval) null);
}
/**
* Returns a deep copy of {@code source} that contains a given
* {@code Interval} in.
*
* @param source an object to copy from (could be null, then completely new
* instance is created)
* @param in interval to add (could be null)
*
* @return a deep copy of {@code source} that contains a given
* {@code Interval} in.
*/
public static DynamicType createDynamicObject(AttributeType type, DynamicType source, Interval in) {
return createDynamicObject(type, source, in, null);
}
/**
* Returns a deep copy of {@code source} that contains a given
* {@code Interval} in. Before add it removes from the newly created
* object all intervals that overlap with a given {@code Interval} out.
*
* @param source an object to copy from (could be null, then completely new
* instance is created)
* @param in interval to add (could be null)
* @param out interval to remove (could be null)
*
* @return a deep copy of {@code source} that contains a given
* {@code Interval} in. Before add it removes from the newly created
* object all intervals that overlap with a given {@code Interval} out.
*/
public static DynamicType createDynamicObject(AttributeType type, DynamicType source, Interval in, Interval out) {
ArrayList<Interval> lin = null;
ArrayList<Interval> lout = null;
if (in != null) {
lin = new ArrayList<Interval>();
lin.add(in);
}
if (out != null) {
lout = new ArrayList<Interval>();
lout.add(out);
}
return createDynamicObject(type, source, lin, lout);
}
/**
* Returns 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)
*
* @return a deep copy of {@code source} with additional intervals
* given by {@code List<Interval>} in.
*/
public static DynamicType createDynamicObject(AttributeType type, DynamicType source, List<Interval> in) {
return createDynamicObject(type, source, in, null);
}
/**
* Returns 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.
* <p>
* It can return {@code null} if type is not dynamic.
*
* @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)
*
* @return 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. It can return {@code null} if type
* is not dynamic.
*/
public static DynamicType createDynamicObject(AttributeType type, DynamicType source, List<Interval> in,
List<Interval> out) {
if (!type.isDynamicType()) {
return null;
}
switch (type) {
case DYNAMIC_BYTE: {
ArrayList<Interval<Byte>> lin = null;
if (in != null) {
lin = new ArrayList<Interval<Byte>>();
for (Interval interval : in) {
lin.add(new Interval<Byte>(interval.getLow(), interval.getHigh(),
interval.isLowExcluded(), interval.isHighExcluded(), (Byte) interval.getValue()));
}
}
ArrayList<Interval<Byte>> lout = null;
if (out != null) {
lout = new ArrayList<Interval<Byte>>();
for (Interval interval : out) {
lout.add(new Interval<Byte>(interval.getLow(), interval.getHigh(),
interval.isLowExcluded(), interval.isHighExcluded(), (Byte) interval.getValue()));
}
}
return new DynamicByte((DynamicByte) source, lin, lout);
}
case DYNAMIC_SHORT: {
ArrayList<Interval<Short>> lin = null;
if (in != null) {
lin = new ArrayList<Interval<Short>>();
for (Interval interval : in) {
lin.add(new Interval<Short>(interval.getLow(), interval.getHigh(),
interval.isLowExcluded(), interval.isHighExcluded(), (Short) interval.getValue()));
}
}
ArrayList<Interval<Short>> lout = null;
if (out != null) {
lout = new ArrayList<Interval<Short>>();
for (Interval interval : out) {
lout.add(new Interval<Short>(interval.getLow(), interval.getHigh(),
interval.isLowExcluded(), interval.isHighExcluded(), (Short) interval.getValue()));
}
}
return new DynamicShort((DynamicShort) source, lin, lout);
}
case DYNAMIC_INT: {
ArrayList<Interval<Integer>> lin = null;
if (in != null) {
lin = new ArrayList<Interval<Integer>>();
for (Interval interval : in) {
lin.add(new Interval<Integer>(interval.getLow(), interval.getHigh(),
interval.isLowExcluded(), interval.isHighExcluded(), (Integer) interval.getValue()));
}
}
ArrayList<Interval<Integer>> lout = null;
if (out != null) {
lout = new ArrayList<Interval<Integer>>();
for (Interval interval : out) {
lout.add(new Interval<Integer>(interval.getLow(), interval.getHigh(),
interval.isLowExcluded(), interval.isHighExcluded(), (Integer) interval.getValue()));
}
}
return new DynamicInteger((DynamicInteger) source, lin, lout);
}
case DYNAMIC_LONG: {
ArrayList<Interval<Long>> lin = null;
if (in != null) {
lin = new ArrayList<Interval<Long>>();
for (Interval interval : in) {
lin.add(new Interval<Long>(interval.getLow(), interval.getHigh(),
interval.isLowExcluded(), interval.isHighExcluded(), (Long) interval.getValue()));
}
}
ArrayList<Interval<Long>> lout = null;
if (out != null) {
lout = new ArrayList<Interval<Long>>();
for (Interval interval : out) {
lout.add(new Interval<Long>(interval.getLow(), interval.getHigh(),
interval.isLowExcluded(), interval.isHighExcluded(), (Long) interval.getValue()));
}
}
return new DynamicLong((DynamicLong) source, lin, lout);
}
case DYNAMIC_FLOAT: {
ArrayList<Interval<Float>> lin = null;
if (in != null) {
lin = new ArrayList<Interval<Float>>();
for (Interval interval : in) {
lin.add(new Interval<Float>(interval.getLow(), interval.getHigh(),
interval.isLowExcluded(), interval.isHighExcluded(), (Float) interval.getValue()));
}
}
ArrayList<Interval<Float>> lout = null;
if (out != null) {
lout = new ArrayList<Interval<Float>>();
for (Interval interval : out) {
lout.add(new Interval<Float>(interval.getLow(), interval.getHigh(),
interval.isLowExcluded(), interval.isHighExcluded(), (Float) interval.getValue()));
}
}
return new DynamicFloat((DynamicFloat) source, lin, lout);
}
case DYNAMIC_DOUBLE: {
ArrayList<Interval<Double>> lin = null;
if (in != null) {
lin = new ArrayList<Interval<Double>>();
for (Interval interval : in) {
lin.add(new Interval<Double>(interval.getLow(), interval.getHigh(),
interval.isLowExcluded(), interval.isHighExcluded(), (Double) interval.getValue()));
}
}
ArrayList<Interval<Double>> lout = null;
if (out != null) {
lout = new ArrayList<Interval<Double>>();
for (Interval interval : out) {
lout.add(new Interval<Double>(interval.getLow(), interval.getHigh(),
interval.isLowExcluded(), interval.isHighExcluded(), (Double) interval.getValue()));
}
}
return new DynamicDouble((DynamicDouble) source, lin, lout);
}
case DYNAMIC_BOOLEAN: {
ArrayList<Interval<Boolean>> lin = null;
if (in != null) {
lin = new ArrayList<Interval<Boolean>>();
for (Interval interval : in) {
lin.add(new Interval<Boolean>(interval.getLow(), interval.getHigh(),
interval.isLowExcluded(), interval.isHighExcluded(), (Boolean) interval.getValue()));
}
}
ArrayList<Interval<Boolean>> lout = null;
if (out != null) {
lout = new ArrayList<Interval<Boolean>>();
for (Interval interval : out) {
lout.add(new Interval<Boolean>(interval.getLow(), interval.getHigh(),
interval.isLowExcluded(), interval.isHighExcluded(), (Boolean) interval.getValue()));
}
}
return new DynamicBoolean((DynamicBoolean) source, lin, lout);
}
case DYNAMIC_CHAR: {
ArrayList<Interval<Character>> lin = null;
if (in != null) {
lin = new ArrayList<Interval<Character>>();
for (Interval interval : in) {
lin.add(new Interval<Character>(interval.getLow(), interval.getHigh(),
interval.isLowExcluded(), interval.isHighExcluded(), (Character) interval.getValue()));
}
}
ArrayList<Interval<Character>> lout = null;
if (out != null) {
lout = new ArrayList<Interval<Character>>();
for (Interval interval : out) {
lout.add(new Interval<Character>(interval.getLow(), interval.getHigh(),
interval.isLowExcluded(), interval.isHighExcluded(), (Character) interval.getValue()));
}
}
return new DynamicCharacter((DynamicCharacter) source, lin, lout);
}
case DYNAMIC_STRING: {
ArrayList<Interval<String>> lin = null;
if (in != null) {
lin = new ArrayList<Interval<String>>();
for (Interval interval : in) {
lin.add(new Interval<String>(interval.getLow(), interval.getHigh(),
interval.isLowExcluded(), interval.isHighExcluded(), (String) interval.getValue()));
}
}
ArrayList<Interval<String>> lout = null;
if (out != null) {
lout = new ArrayList<Interval<String>>();
for (Interval interval : out) {
lout.add(new Interval<String>(interval.getLow(), interval.getHigh(),
interval.isLowExcluded(), interval.isHighExcluded(), (String) interval.getValue()));
}
}
return new DynamicString((DynamicString) source, lin, lout);
}
case DYNAMIC_BIGINTEGER: {
ArrayList<Interval<BigInteger>> lin = null;
if (in != null) {
lin = new ArrayList<Interval<BigInteger>>();
for (Interval interval : in) {
lin.add(new Interval<BigInteger>(interval.getLow(), interval.getHigh(),
interval.isLowExcluded(), interval.isHighExcluded(), (BigInteger) interval.getValue()));
}
}
ArrayList<Interval<BigInteger>> lout = null;
if (out != null) {
lout = new ArrayList<Interval<BigInteger>>();
for (Interval interval : out) {
lout.add(new Interval<BigInteger>(interval.getLow(), interval.getHigh(),
interval.isLowExcluded(), interval.isHighExcluded(), (BigInteger) interval.getValue()));
}
}
return new DynamicBigInteger((DynamicBigInteger) source, lin, lout);
}
case DYNAMIC_BIGDECIMAL: {
ArrayList<Interval<BigDecimal>> lin = null;
if (in != null) {
lin = new ArrayList<Interval<BigDecimal>>();
for (Interval interval : in) {
lin.add(new Interval<BigDecimal>(interval.getLow(), interval.getHigh(),
interval.isLowExcluded(), interval.isHighExcluded(), (BigDecimal) interval.getValue()));
}
}
ArrayList<Interval<BigDecimal>> lout = null;
if (out != null) {
lout = new ArrayList<Interval<BigDecimal>>();
for (Interval interval : out) {
lout.add(new Interval<BigDecimal>(interval.getLow(), interval.getHigh(),
interval.isLowExcluded(), interval.isHighExcluded(), (BigDecimal) interval.getValue()));
}
}
return new DynamicBigDecimal((DynamicBigDecimal) source, lin, lout);
}
case TIME_INTERVAL: {
ArrayList<Interval> lin = null;
if (in != null) {
lin = new ArrayList<Interval>();
for (Interval interval : in) {
lin.add(new Interval(interval.getLow(), interval.getHigh(),
interval.isLowExcluded(), interval.isHighExcluded()));
}
}
ArrayList<Interval> lout = null;
if (out != null) {
lout = new ArrayList<Interval>();
for (Interval interval : out) {
lout.add(new Interval(interval.getLow(), interval.getHigh(),
interval.isLowExcluded(), interval.isHighExcluded()));
}
}
return new TimeInterval((TimeInterval) source, lin, lout);
}
default:
return null;
}
}
/**
* It checks intervals of the {@code source} and make it fit to the given interval,
* possibly removing intervals out of the window and
* changing low or high of intervals to fit.
*
* @param source a {@code DynamicType} to be performed
* @param interval a given interval
*
* @return a fitted {@code DynamicType} instance.
*
* @throws NullPointerException if {@code source} is null.
*/
public static DynamicType fitToInterval(DynamicType source, Interval interval) {
if (source == null) {
throw new NullPointerException("The source cannot be null.");
}
List<Interval> sIntervals = source.getIntervals(interval);
List<Interval> tIntervals = new ArrayList<Interval>();
for (Interval i : sIntervals) {
double iLow = i.getLow();
double iHigh = i.getHigh();
boolean ilopen = i.isLowExcluded();
boolean iropen = i.isHighExcluded();
if (i.getLow() < interval.getLow()) {
iLow = interval.getLow();
}
if (i.getHigh() > interval.getHigh()) {
iHigh = interval.getHigh();
}
if (interval.isLowExcluded()) {
ilopen = true;
}
if (interval.isHighExcluded()) {
iropen = true;
}
tIntervals.add(new Interval(iLow, iHigh, ilopen, iropen, i.getValue()));
}
return createDynamicObject(AttributeType.parse(source), tIntervals);
}
/**
* It checks intervals of the {@code source} and make it fit to the given interval
* [{@code low}, {@code high}], possibly removing intervals out of the window and
* changing low or high of intervals to fit.
*
* @param source a {@code DynamicType} to be performed
* @param low the left endpoint
* @param high the right endpoint
*
* @return a fitted {@code DynamicType} instance.
*
* @throws NullPointerException if {@code source} is null.
* @throws IllegalArgumentException if {@code low} > {@code high}.
*/
public static DynamicType fitToInterval(DynamicType source, double low, double high) {
return fitToInterval(source, new Interval(low, high));
}
/**
* Returns the visible time interval of <code>dynamicModel</code> if it is not
* [-inf, +inf]. Returns <code>null</null> in other cases.
*
* @param dynamicModel the dynamic model
*
* @return the valid visible interval, or <code>null</code>.
*/
public static TimeInterval getVisibleInterval(DynamicModel dynamicModel) {
if (dynamicModel != null) {
TimeInterval ti = dynamicModel.getVisibleInterval();
if (ti != null && !(Double.isInfinite(ti.getLow()) && Double.isInfinite(ti.getHigh()))) {
return ti;
}
}
return null;
}
public static Object getDynamicValue(Object value, double low, double high) {
if (value != null && value instanceof DynamicType) {
DynamicType dynamicType = (DynamicType) value;
Estimator estimator = Estimator.FIRST;
if (Number.class.isAssignableFrom(dynamicType.getUnderlyingType())) {
estimator = Estimator.AVERAGE;
}
return dynamicType.getValue(low, high, estimator);
}
return value;
}
public static DynamicType removeOverlapping(DynamicType dynamicType) {
Comparator<Interval> comparator = new Comparator<Interval>() {
@Override
public int compare(Interval o1, Interval o2) {
if (o1.getLow() < o2.getLow()) {
return -1;
}
if (o2.getLow() < o1.getLow()) {
return 1;
}
if (o1.getHigh() < o2.getHigh()) {
return -1;
}
if (o2.getHigh() < o1.getHigh()) {
return 1;
}
return 0;
}
};
List<Interval> intervals = dynamicType.getIntervals(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
Collections.sort(intervals, comparator);
boolean overlap = true;
while (overlap) {
overlap = false;
for (int i = 0; i < intervals.size() - 1; i++) {
Interval interval = intervals.get(i);
Interval next = intervals.get(i + 1);
if (interval.getLow() == next.getLow()) {
intervals.set(i + 1, createInterval(dynamicType, interval.getHigh(), next.getHigh(), true,
next.isHighExcluded(), next.getValue()));
overlap = true;
break;
}
if (interval.getHigh() == next.getHigh()) {
intervals.set(i, createInterval(dynamicType, interval.getLow(), next.getLow(),
interval.isLowExcluded(), true, interval.getValue()));
overlap = true;
break;
}
if (next.getLow() < interval.getLow() && next.getHigh() > interval.getHigh()) {
intervals.set(i + 1, createInterval(dynamicType, interval.getHigh(), next.getHigh(), true,
next.isHighExcluded(), interval.getValue()));
overlap = true;
break;
}
if ((next.getLow() < interval.getHigh() || (next.getLow() == interval.getHigh()
&& !interval.isHighExcluded())) && next.getHigh() < interval.getHigh()) {
intervals.set(i, createInterval(dynamicType, interval.getLow(), next.getLow(),
interval.isLowExcluded(), true, interval.getValue()));
intervals.add(i + 2, createInterval(dynamicType, next.getHigh(), interval.getHigh(), true,
interval.isHighExcluded(), interval.getValue()));
overlap = true;
break;
}
if (next.getLow() == interval.getHigh() && !interval.isHighExcluded() && !next.isLowExcluded()) {
intervals.set(i, createInterval(dynamicType, interval.getLow(), interval.getHigh(),
interval.isLowExcluded(), true, interval.getValue()));
overlap = true;
break;
}
if (next.getLow() < interval.getHigh()) {
intervals.set(i, createInterval(dynamicType, interval.getLow(), next.getHigh(),
interval.isLowExcluded(), true, interval.getValue()));
overlap = true;
break;
}
}
}
return createDynamicObject(AttributeType.parse(dynamicType), intervals);
}
public static Interval createInterval(DynamicType dynamicType, double low, double high, boolean lopen,
boolean ropen, Object value) {
if (dynamicType instanceof TimeInterval) {
return new Interval<Double[]>(low, high, lopen, ropen, new Double[]{low, high});
}
if (dynamicType instanceof DynamicBigDecimal) {
return new Interval<BigDecimal>(low, high, lopen, ropen, (BigDecimal) value);
}
if (dynamicType instanceof DynamicBigInteger) {
return new Interval<BigInteger>(low, high, lopen, ropen, (BigInteger) value);
}
if (dynamicType instanceof DynamicBoolean) {
return new Interval<Boolean>(low, high, lopen, ropen, (Boolean) value);
}
if (dynamicType instanceof DynamicByte) {
return new Interval<Byte>(low, high, lopen, ropen, (Byte) value);
}
if (dynamicType instanceof DynamicCharacter) {
return new Interval<Character>(low, high, lopen, ropen, (Character) value);
}
if (dynamicType instanceof DynamicDouble) {
return new Interval<Double>(low, high, lopen, ropen, (Double) value);
}
if (dynamicType instanceof DynamicFloat) {
return new Interval<Float>(low, high, lopen, ropen, (Float) value);
}
if (dynamicType instanceof DynamicInteger) {
return new Interval<Integer>(low, high, lopen, ropen, (Integer) value);
}
if (dynamicType instanceof DynamicLong) {
return new Interval<Long>(low, high, lopen, ropen, (Long) value);
}
if (dynamicType instanceof DynamicShort) {
return new Interval<Short>(low, high, lopen, ropen, (Short) value);
}
if (dynamicType instanceof DynamicString) {
return new Interval<String>(low, high, lopen, ropen, (String) value);
}
return null;
}
}