/* Copyright 2008-2010 Gephi Authors : Mathieu Bastian <mathieu.bastian@gephi.org> 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.partition.impl; import com.google.common.collect.ArrayListMultimap; import java.awt.Color; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Random; import java.util.Set; import org.gephi.data.attributes.api.AttributeColumn; import org.gephi.data.attributes.api.AttributeType; import org.gephi.data.attributes.api.Estimator; import org.gephi.data.attributes.type.DynamicType; import org.gephi.data.attributes.type.TimeInterval; import org.gephi.graph.api.Edge; import org.gephi.graph.api.EdgeData; import org.gephi.graph.api.Graph; import org.gephi.graph.api.Node; import org.gephi.graph.api.NodeData; import org.gephi.partition.api.EdgePartition; import org.gephi.partition.api.NodePartition; import org.gephi.partition.api.Part; import org.gephi.partition.api.Partition; /** * * @author Mathieu Bastian */ public class PartitionFactory { public static boolean isPartitionColumn(AttributeColumn column) { return column.getType().equals(AttributeType.STRING) || column.getType().equals(AttributeType.BOOLEAN) || column.getType().equals(AttributeType.INT) || column.getType().equals(AttributeType.SHORT); } public static boolean isDynamicPartitionColumn(AttributeColumn column) { return column.getType().equals(AttributeType.DYNAMIC_STRING) || column.getType().equals(AttributeType.DYNAMIC_BOOLEAN) || column.getType().equals(AttributeType.DYNAMIC_INT) || column.getType().equals(AttributeType.DYNAMIC_SHORT); } public static boolean isNodePartitionColumn(AttributeColumn column, Graph graph) { Set values = new HashSet(); int nonNullvalues = 0; for (Node n : graph.getNodes()) { Object value = n.getNodeData().getAttributes().getValue(column.getIndex()); if (value != null) { nonNullvalues++; } values.add(value); } if (values.size() < 9f / 10f * nonNullvalues) { //If #different values is < 9:10 of total non-null values return true; } return false; } public static boolean isEdgePartitionColumn(AttributeColumn column, Graph graph) { Set values = new HashSet(); int nonNullvalues = 0; for (Edge n : graph.getEdges()) { Object value = n.getEdgeData().getAttributes().getValue(column.getIndex()); if (value != null) { nonNullvalues++; } values.add(value); } if (values.size() < 9f / 10f * nonNullvalues) { //If #different values is < 9:10 of total non-null values return true; } return false; } public static boolean isDynamicNodePartitionColumn(AttributeColumn column, Graph graph, TimeInterval timeInterval, Estimator estimator) { Set values = new HashSet(); int nonNullvalues = 0; for (Node n : graph.getNodes()) { Object value = n.getNodeData().getAttributes().getValue(column.getIndex()); value = getDynamicValue(value, timeInterval, estimator); if (value != null) { nonNullvalues++; } values.add(value); } if (values.size() < 9f / 10f * nonNullvalues) { //If #different values is < 9:10 of total non-null values return true; } return false; } public static boolean isDynamicEdgePartitionColumn(AttributeColumn column, Graph graph, TimeInterval timeInterval, Estimator estimator) { Set values = new HashSet(); int nonNullvalues = 0; for (Edge n : graph.getEdges()) { Object value = n.getEdgeData().getAttributes().getValue(column.getIndex()); value = getDynamicValue(value, timeInterval, estimator); if (value != null) { nonNullvalues++; } values.add(value); } if (values.size() < 9f / 10f * nonNullvalues) { //If #different values is < 9:10 of total non-null values return true; } return false; } private static Object getDynamicValue(Object object, TimeInterval timeInterval, Estimator estimator) { if (object != null && object instanceof DynamicType) { DynamicType dynamicType = (DynamicType) object; return dynamicType.getValue(timeInterval == null ? Double.NEGATIVE_INFINITY : timeInterval.getLow(), timeInterval == null ? Double.POSITIVE_INFINITY : timeInterval.getHigh(), estimator); } return object; } public static NodePartition createNodePartition(AttributeColumn column) { return new NodePartitionImpl(column); } public static EdgePartition createEdgePartition(AttributeColumn column) { return new EdgePartitionImpl(column); } public static boolean isPartitionBuilt(Partition partition) { return partition.getParts().length > 0; } public static void buildNodePartition(NodePartition partition, Graph graph) { buildNodePartition(partition, graph, null, null); } public static void buildNodePartition(NodePartition partition, Graph graph, TimeInterval timeInterval, Estimator estimator) { NodePartitionImpl partitionImpl = (NodePartitionImpl) partition; ArrayListMultimap<Object, Node> multimap = ArrayListMultimap.create(); for (Node n : graph.getNodes()) { Object value = n.getNodeData().getAttributes().getValue(partitionImpl.column.getIndex()); value = getDynamicValue(value, timeInterval, estimator); multimap.put(value, n); } PartImpl<Node>[] parts = new PartImpl[multimap.keySet().size()]; Map<Object, Collection<Node>> map = multimap.asMap(); int i = 0; for (Entry<Object, Collection<Node>> entry : map.entrySet()) { PartImpl<Node> part = new PartImpl<Node>(partition, entry.getKey(), entry.getValue().toArray(new Node[0])); parts[i] = part; i++; } partitionImpl.setParts(parts); } public static void buildEdgePartition(EdgePartition partition, Graph graph) { buildEdgePartition(partition, graph, null, null); } public static void buildEdgePartition(EdgePartition partition, Graph graph, TimeInterval timeInterval, Estimator estimator) { EdgePartitionImpl partitionImpl = (EdgePartitionImpl) partition; ArrayListMultimap<Object, Edge> multimap = ArrayListMultimap.create(); for (Edge n : graph.getEdges()) { Object value = n.getEdgeData().getAttributes().getValue(partitionImpl.column.getIndex()); value = getDynamicValue(value, timeInterval, estimator); multimap.put(value, n); } PartImpl<Edge>[] parts = new PartImpl[multimap.keySet().size()]; Map<Object, Collection<Edge>> map = multimap.asMap(); int i = 0; for (Entry<Object, Collection<Edge>> entry : map.entrySet()) { PartImpl<Edge> part = new PartImpl<Edge>(partition, entry.getKey(), entry.getValue().toArray(new Edge[0])); parts[i] = part; i++; } partitionImpl.setParts(parts); } private static class NodePartitionImpl implements NodePartition { private HashMap<NodeData, Part<Node>> nodeMap; private HashMap<Object, Part<Node>> valueMap; private PartImpl<Node>[] parts; private AttributeColumn column; public NodePartitionImpl(AttributeColumn column) { this.column = column; nodeMap = new HashMap<NodeData, Part<Node>>(); valueMap = new HashMap<Object, Part<Node>>(); parts = new PartImpl[0]; } public int getPartsCount() { return parts.length; } public Part<Node> getPartFromValue(Object value) { return valueMap.get(value); } public Part<Node>[] getParts() { return parts; } public Map<NodeData, Part<Node>> getMap() { return nodeMap; } public Part<Node> getPart(Node element) { return nodeMap.get(element.getNodeData()); } public void setParts(PartImpl<Node>[] parts) { this.parts = parts; List<Color> colors = getSequenceColors(parts.length); int i = 0; for (PartImpl<Node> p : parts) { for (Node n : p.objects) { nodeMap.put(n.getNodeData(), p); } p.setColor(colors.get(i)); valueMap.put(p.getValue(), p); i++; } } public AttributeColumn getColumn() { return column; } @Override public String toString() { return column.getTitle(); } public int getElementsCount() { return nodeMap.size(); } } private static class EdgePartitionImpl implements EdgePartition { private HashMap<EdgeData, Part<Edge>> edgeMap; private PartImpl<Edge>[] parts; private HashMap<Object, Part<Edge>> valueMap; private AttributeColumn column; public EdgePartitionImpl(AttributeColumn column) { this.column = column; edgeMap = new HashMap<EdgeData, Part<Edge>>(); valueMap = new HashMap<Object, Part<Edge>>(); parts = new PartImpl[0]; } public int getPartsCount() { return parts.length; } public Part<Edge>[] getParts() { return parts; } public Part<Edge> getPartFromValue(Object value) { return valueMap.get(value); } public Map<EdgeData, Part<Edge>> getMap() { return edgeMap; } public Part<Edge> getPart(Edge element) { return edgeMap.get(element.getEdgeData()); } public void setParts(PartImpl<Edge>[] parts) { this.parts = parts; List<Color> colors = getSequenceColors(parts.length); int i = 0; for (PartImpl<Edge> p : parts) { for (Edge e : p.objects) { edgeMap.put(e.getEdgeData(), p); } p.setColor(colors.get(i)); valueMap.put(p.getValue(), p); i++; } } public AttributeColumn getColumn() { return column; } @Override public String toString() { return column.getTitle(); } public int getElementsCount() { return edgeMap.size(); } } private static class PartImpl<Element> implements Part<Element> { private static final String NULL = "null"; private Partition<Element> partition; private Element[] objects; private Object value; private Color color; public PartImpl(Partition<Element> partition, Object value, Element[] objects) { this.partition = partition; this.value = value; this.objects = objects; } public Element[] getObjects() { return objects; } public Object getValue() { return value; } public String getDisplayName() { return value != null ? value.toString() : NULL; } public boolean isInPart(Element element) { return partition.getPart(element) == this; } public void setColor(Color color) { this.color = color; } public Color getColor() { return color; } public float getPercentage() { return objects.length / (float) partition.getElementsCount(); } public Partition getPartition() { return partition; } @Override public String toString() { return getDisplayName(); } public int compareTo(Object o) { int thisCount = objects.length; int theirCount = ((PartImpl) o).objects.length; return thisCount == theirCount ? 0 : thisCount > theirCount ? 1 : -1; } @Override public boolean equals(Object obj) { if (obj != null && obj instanceof PartImpl) { if (value == null && ((PartImpl) obj).value == null) { return true; } else if (((PartImpl) obj).value != null && value != null) { return ((PartImpl) obj).value.equals(value); } } return false; } @Override public int hashCode() { int hash = 7; hash = 53 * hash + (this.value != null ? this.value.hashCode() : 0); return hash; } } public static List<Color> getSequenceColors(int num) { List<Color> colors = new LinkedList<Color>(); //On choisit H et S au random Random random = new Random(); float B = random.nextFloat() * 2 / 5f + 0.6f; // 0.6 <= B < 1 float S = random.nextFloat() * 2 / 5f + 0.6f; // 0.6 <= S < 1 //System.out.println("B : "+B+" S : "+S); for (int i = 1; i <= num; i++) { float H = i / (float) num; //System.out.println(H); Color c = Color.getHSBColor(H, S, B); colors.add(c); } Collections.shuffle(colors); return colors; } }