/*
Copyright 2008-2010 Gephi
Authors : Mathieu Bastian <mathieu.bastian@gephi.org>
Website : http://www.gephi.org
This file is part of Gephi.
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
Copyright 2011 Gephi Consortium. All rights reserved.
The contents of this file are subject to the terms of either the GNU
General Public License Version 3 only ("GPL") or the Common
Development and Distribution License("CDDL") (collectively, the
"License"). You may not use this file except in compliance with the
License. You can obtain a copy of the License at
http://gephi.org/about/legal/license-notice/
or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the
specific language governing permissions and limitations under the
License. When distributing the software, include this License Header
Notice in each file and include the License files at
/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the
License Header, with the fields enclosed by brackets [] replaced by
your own identifying information:
"Portions Copyrighted [year] [name of copyright owner]"
If you wish your version of this file to be governed by only the CDDL
or only the GPL Version 3, indicate your decision by adding
"[Contributor] elects to include this software in this distribution
under the [CDDL or GPL Version 3] license." If you do not indicate a
single choice of license, a recipient has the option to distribute
your version of this file under either the CDDL, the GPL Version 3 or
to extend the choice of license to its licensees as provided above.
However, if you add GPL Version 3 code and therefore, elected the GPL
Version 3 license, then the option applies only if the new code is
made subject to such option by the copyright holder.
Contributor(s):
Portions Copyrighted 2011 Gephi Consortium.
*/
package org.gephi.io.importer.impl;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import it.unimi.dsi.fastutil.objects.ObjectList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import org.gephi.graph.api.AttributeUtils;
import org.gephi.graph.api.Interval;
import org.gephi.graph.api.TimeFormat;
import org.gephi.graph.api.TimeRepresentation;
import org.gephi.graph.api.types.TimeSet;
import org.gephi.io.importer.api.ColumnDraft;
import org.gephi.io.importer.api.Container;
import org.gephi.io.importer.api.ContainerLoader;
import org.gephi.io.importer.api.ContainerUnloader;
import org.gephi.io.importer.api.EdgeDirection;
import org.gephi.io.importer.api.EdgeDirectionDefault;
import org.gephi.io.importer.api.EdgeDraft;
import org.gephi.io.importer.api.EdgeMergeStrategy;
import org.gephi.io.importer.api.ElementDraft;
import org.gephi.io.importer.api.ElementIdType;
import org.gephi.io.importer.api.Issue;
import org.gephi.io.importer.api.Issue.Level;
import org.gephi.io.importer.api.NodeDraft;
import org.gephi.io.importer.api.Report;
import org.joda.time.DateTimeZone;
import org.openide.util.NbBundle;
/**
*
* @author Mathieu Bastian
*/
public class ImportContainerImpl implements Container, ContainerLoader, ContainerUnloader {
protected static final int NULL_INDEX = -1;
//MetaData
private String source;
//Factory
private final ElementFactoryImpl factory;
//Parameters
private final ImportContainerParameters parameters;
//Maps and Data
private final ObjectList<NodeDraftImpl> nodeList;
private final ObjectList<EdgeDraftImpl> edgeList;
private final Object2IntMap<String> nodeMap;
private final Object2IntMap<String> edgeMap;
private final Object2IntMap edgeTypeMap;
private Class lastEdgeType;
private Long2ObjectMap<int[]>[] edgeTypeSets;
private EdgeDirectionDefault edgeDefault = EdgeDirectionDefault.MIXED;
private final Object2ObjectMap<String, ColumnDraft> nodeColumns;
private final Object2ObjectMap<String, ColumnDraft> edgeColumns;
//Config
private ElementIdType elementIdType = ElementIdType.STRING;
//Management
private boolean dynamicGraph = false;
private boolean dynamicAttributes = false;
private Report report;
//Counting
private int directedEdgesCount = 0;
private int undirectedEdgesCount = 0;
private int selfLoops = 0;
//Dynamic
private TimeFormat timeFormat = TimeFormat.DOUBLE;
private TimeRepresentation timeRepresentation = TimeRepresentation.INTERVAL;
private DateTimeZone timeZone = DateTimeZone.getDefault();
private Double timestamp;
private Interval interval;
//Report flag
private boolean reportedUnknownNode;
private boolean reportedParallelEdges;
public ImportContainerImpl() {
parameters = new ImportContainerParameters();
nodeMap = new Object2IntOpenHashMap<>();
edgeMap = new Object2IntOpenHashMap<>();
nodeMap.defaultReturnValue(NULL_INDEX);
edgeMap.defaultReturnValue(NULL_INDEX);
nodeList = new ObjectArrayList<>();
edgeList = new ObjectArrayList<>();
edgeTypeMap = new Object2IntOpenHashMap();
edgeTypeSets = new Long2ObjectMap[0];
factory = new ElementFactoryImpl(this);
nodeColumns = new Object2ObjectOpenHashMap<>();
edgeColumns = new Object2ObjectOpenHashMap<>();
}
@Override
public ContainerLoader getLoader() {
return this;
}
@Override
public synchronized ContainerUnloader getUnloader() {
return this;
}
@Override
public ElementFactoryImpl factory() {
return factory;
}
@Override
public void setSource(String source) {
this.source = source;
}
@Override
public String getSource() {
return source;
}
@Override
public void addNode(NodeDraft nodeDraft) {
checkElementDraftImpl(nodeDraft);
NodeDraftImpl nodeDraftImpl = (NodeDraftImpl) nodeDraft;
if (nodeMap.containsKey(nodeDraftImpl.getId())) {
String message = NbBundle.getMessage(ImportContainerImpl.class, "ImportContainerException_nodeExist", nodeDraftImpl.getId());
report.logIssue(new Issue(message, Level.WARNING));
return;
}
int index = nodeList.size();
nodeList.add(nodeDraftImpl);
nodeMap.put(nodeDraftImpl.getId(), index);
}
@Override
public NodeDraftImpl getNode(String id) {
checkId(id);
int index = nodeMap.getInt(id);
NodeDraftImpl node = null;
if (index == NULL_INDEX) {
if (parameters.isAutoNode()) {
//Creates the missing node
node = factory.newNodeDraft(id);
addNode(node);
node.setCreatedAuto(true);
if (!reportedUnknownNode) {
String message = NbBundle.getMessage(ImportContainerImpl.class, "ImportContainerException_AutoNodeCreated");
report.logIssue(new Issue(message, Level.INFO));
reportedUnknownNode = true;
}
} else {
String message = NbBundle.getMessage(ImportContainerImpl.class, "ImportContainerException_UnknowNodeId", id);
report.logIssue(new Issue(message, Level.SEVERE));
}
} else {
node = nodeList.get(index);
}
return node;
}
@Override
public boolean nodeExists(String id) {
checkId(id);
return nodeMap.containsKey(id);
}
@Override
public boolean edgeExists(String source, String target) {
checkId(source);
checkId(target);
NodeDraftImpl sourceNode = getNode(source);
NodeDraftImpl targetNode = getNode(target);
if (sourceNode != null && targetNode != null) {
boolean undirected = edgeDefault.equals(EdgeDirectionDefault.UNDIRECTED) || (undirectedEdgesCount > 0 && directedEdgesCount == 0);
long edgeId = getLongId(sourceNode, targetNode, !undirected);
for (Long2ObjectMap l : edgeTypeSets) {
if (l != null) {
if (l.containsKey(edgeId)) {
return true;
}
}
}
}
return false;
}
@Override
public void addEdge(EdgeDraft edgeDraft) {
checkElementDraftImpl(edgeDraft);
EdgeDraftImpl edgeDraftImpl = (EdgeDraftImpl) edgeDraft;
if (edgeDraftImpl.getSource() == null) {
String message = NbBundle.getMessage(ImportContainerImpl.class, "ImportContainerException_MissingNodeSource");
report.logIssue(new Issue(message, Level.SEVERE));
return;
}
if (edgeDraftImpl.getTarget() == null) {
String message = NbBundle.getMessage(ImportContainerImpl.class, "ImportContainerException_MissingNodeTarget");
report.logIssue(new Issue(message, Level.SEVERE));
return;
}
//Check if already exists
if (edgeMap.containsKey(edgeDraftImpl.getId())) {
String message = NbBundle.getMessage(ImportContainerImpl.class, "ImportContainerException_edgeExist", edgeDraftImpl.getId());
report.logIssue(new Issue(message, Level.WARNING));
return;
}
//Self loop
if (edgeDraftImpl.isSelfLoop() && !parameters.isSelfLoops()) {
String message = NbBundle.getMessage(ImportContainerImpl.class, "ImportContainerException_SelfLoop");
report.logIssue(new Issue(message, Level.SEVERE));
return;
}
//Check direction and default type
if (edgeDraftImpl.getDirection() != null) {
//Test if the given type match with parameters
switch (edgeDefault) {
case DIRECTED:
if (edgeDraftImpl.getDirection().equals(EdgeDirection.UNDIRECTED)) {
report.logIssue(new Issue(NbBundle.getMessage(ImportContainerImpl.class, "ImportContainerException_Bad_Edge_Type", edgeDefault, edgeDraftImpl.getId()), Level.SEVERE));
return;
}
break;
case UNDIRECTED:
if (edgeDraftImpl.getDirection().equals(EdgeDirection.DIRECTED)) {
report.logIssue(new Issue(NbBundle.getMessage(ImportContainerImpl.class, "ImportContainerException_Bad_Edge_Type", edgeDefault, edgeDraftImpl.getId()), Level.SEVERE));
return;
}
break;
}
}
//Get index
int index = edgeList.size();
//Type
int edgeType = getEdgeType(edgeDraftImpl.getType());
long sourceTargetLong = getLongId(edgeDraftImpl);
ensureLongSetArraySize(edgeType);
Long2ObjectMap<int[]> edgeTypeSet = edgeTypeSets[edgeType];
if (edgeTypeSet.containsKey(sourceTargetLong)) {
if (!parameters.isParallelEdges()) {
report.logIssue(new Issue(NbBundle.getMessage(ImportContainerImpl.class, "ImportContainerException_Parallel_Edge_Forbidden", edgeDraftImpl.getId()), Level.SEVERE));
return;
} else {
int[] edges = edgeTypeSet.get(sourceTargetLong);
int[] newEdges = new int[edges.length + 1];
newEdges[edges.length] = index;
System.arraycopy(edges, 0, newEdges, 0, edges.length);
edgeTypeSet.put(sourceTargetLong, newEdges);
if (!reportedParallelEdges) {
report.logIssue(new Issue(NbBundle.getMessage(ImportContainerImpl.class, "ImportContainerException_Parallel_Edge_Merged", edgeDraftImpl.getId()), Level.INFO));
reportedParallelEdges = true;
}
}
} else {
edgeTypeSet.put(sourceTargetLong, new int[]{index});
}
//Self loop
if (edgeDraftImpl.isSelfLoop()) {
selfLoops++;
}
//Direction
EdgeDirection direction = edgeDraftImpl.getDirection();
if (direction != null) {
//Counting
switch (direction) {
case DIRECTED:
directedEdgesCount++;
break;
case UNDIRECTED:
undirectedEdgesCount++;
break;
}
}
//Adding
edgeList.add(edgeDraftImpl);
edgeMap.put(edgeDraft.getId(), index);
}
@Override
public void removeEdge(EdgeDraft edgeDraft) {
checkElementDraftImpl(edgeDraft);
EdgeDraftImpl edgeDraftImpl = (EdgeDraftImpl) edgeDraft;
String id = edgeDraftImpl.getId();
if (!edgeMap.containsKey(id)) {
return;
}
if (edgeDraftImpl.getDirection() != null) {
//UnCounting
switch (edgeDraftImpl.getDirection()) {
case DIRECTED:
directedEdgesCount--;
break;
case UNDIRECTED:
undirectedEdgesCount--;
break;
}
}
if (edgeDraftImpl.isSelfLoop()) {
selfLoops--;
}
int edgeType = getEdgeType(edgeDraftImpl.getType());
long sourceTargetLong = getLongId(edgeDraftImpl);
ensureLongSetArraySize(edgeType);
Long2ObjectMap<int[]> edgeTypeSet = edgeTypeSets[edgeType];
//Get index
int index = edgeMap.remove(id);
//Update edgeType set
int[] edges = edgeTypeSet.remove(sourceTargetLong);
if (edges.length > 1) {
int[] newEdges = new int[edges.length - 1];
int i = 0;
for (int e : edges) {
if (e != index) {
newEdges[i++] = e;
}
}
edgeTypeSet.put(sourceTargetLong, newEdges);
}
//Remove edge
edgeList.set(index, null);
}
@Override
public boolean edgeExists(String id) {
checkId(id);
return edgeMap.containsKey(id);
}
@Override
public EdgeDraft getEdge(String id) {
checkId(id);
int index = edgeMap.getInt(id);
if (index == NULL_INDEX) {
return null;
}
return edgeList.get(index);
}
@Override
public Iterable<NodeDraft> getNodes() {
return new NullFilterIterable<>(nodeList);
}
@Override
public int getNodeCount() {
return nodeMap.size();
}
@Override
public Iterable<EdgeDraft> getEdges() {
return new NullFilterIterable<>(edgeList);
}
@Override
public int getEdgeCount() {
return edgeMap.size();
}
@Override
public TimeFormat getTimeFormat() {
return timeFormat;
}
@Override
public void setTimeFormat(TimeFormat timeFormat) {
this.timeFormat = timeFormat;
}
@Override
public TimeRepresentation getTimeRepresentation() {
return timeRepresentation;
}
@Override
public void setTimeRepresentation(TimeRepresentation timeRepresentation) {
this.timeRepresentation = timeRepresentation;
}
@Override
public DateTimeZone getTimeZone() {
return timeZone;
}
@Override
public void setTimeZone(DateTimeZone timeZone) {
this.timeZone = timeZone;
}
@Override
public ColumnDraft addNodeColumn(String key, Class typeClass) {
if (AttributeUtils.isDynamicType(typeClass)) {
if (TimeSet.class.isAssignableFrom(typeClass)) {
return addNodeColumn(key, typeClass, true);
} else {
return addNodeColumn(key, AttributeUtils.getStaticType(typeClass), true);
}
} else {
return addNodeColumn(key, typeClass, false);
}
}
@Override
public ColumnDraft addNodeColumn(String key, Class typeClass, boolean dynamic) {
key = key.toLowerCase().trim();
ColumnDraft column = nodeColumns.get(key);
typeClass = AttributeUtils.getStandardizedType(typeClass);
if (column == null) {
int index = nodeColumns.size();
column = new ColumnDraftImpl(key, index, dynamic, typeClass);
nodeColumns.put(key, column);
if (dynamic) {
report.log(NbBundle.getMessage(ImportContainerImpl.class, "ImportContainerLog.AddDynamicNodeColumn", key, typeClass.getSimpleName()));
} else {
report.log(NbBundle.getMessage(ImportContainerImpl.class, "ImportContainerLog.AddNodeColumn", key, typeClass.getSimpleName()));
}
} else if (!column.getTypeClass().equals(typeClass)) {
report.logIssue(new Issue(NbBundle.getMessage(ImportContainerImpl.class, "ImportContainerException_Column_Type_Mismatch", key, column.getTypeClass()), Level.SEVERE));
}
return column;
}
@Override
public ColumnDraft addEdgeColumn(String key, Class typeClass) {
if (AttributeUtils.isDynamicType(typeClass)) {
if (TimeSet.class.isAssignableFrom(typeClass)) {
return addEdgeColumn(key, typeClass, true);
} else {
return addEdgeColumn(key, AttributeUtils.getStaticType(typeClass), true);
}
} else {
return addEdgeColumn(key, typeClass, false);
}
}
@Override
public ColumnDraft addEdgeColumn(String key, Class typeClass, boolean dynamic) {
key = key.toLowerCase().trim();
ColumnDraft column = edgeColumns.get(key);
typeClass = AttributeUtils.getStandardizedType(typeClass);
if (column == null) {
int index = edgeColumns.size();
column = new ColumnDraftImpl(key, index, dynamic, typeClass);
edgeColumns.put(key, column);
if (dynamic) {
report.log(NbBundle.getMessage(ImportContainerImpl.class, "ImportContainerLog.AddDynamicEdgeColumn", key, typeClass.getSimpleName()));
} else {
report.log(NbBundle.getMessage(ImportContainerImpl.class, "ImportContainerLog.AddEdgeColumn", key, typeClass.getSimpleName()));
}
} else if (!column.getTypeClass().equals(typeClass)) {
report.logIssue(new Issue(NbBundle.getMessage(ImportContainerImpl.class, "ImportContainerException_Column_Type_Mismatch", key, column.getTypeClass()), Level.SEVERE));
}
return column;
}
@Override
public ColumnDraft getNodeColumn(String key) {
return nodeColumns.get(key.toLowerCase());
}
@Override
public boolean hasNodeColumn(String key) {
return nodeColumns.containsKey(key.toLowerCase());
}
@Override
public ColumnDraft getEdgeColumn(String key) {
return edgeColumns.get(key.toLowerCase());
}
@Override
public boolean hasEdgeColumn(String key) {
return edgeColumns.containsKey(key.toLowerCase());
}
@Override
public Iterable<ColumnDraft> getNodeColumns() {
return nodeColumns.values();
}
@Override
public Iterable<ColumnDraft> getEdgeColumns() {
return edgeColumns.values();
}
@Override
public void setTimestamp(String timestamp) {
try {
double t = timeFormat.equals(TimeFormat.DOUBLE) ? Double.parseDouble(timestamp) : AttributeUtils.parseDateTime(timestamp);
this.timestamp = t;
} catch (Exception e) {
report.logIssue(new Issue(NbBundle.getMessage(ImportContainerImpl.class, "ImportContainerException_Timestamp_Parse_Error", timestamp), Level.SEVERE));
return;
}
report.log(NbBundle.getMessage(ImportContainerImpl.class, "ImportContainerLog.GraphTimestamp", timestamp));
}
@Override
public void setInterval(String startDateTime, String endDateTime) {
try {
double start, end;
if (startDateTime == null || startDateTime.trim().isEmpty() || "-inf".equalsIgnoreCase(startDateTime) || "-infinity".equalsIgnoreCase(startDateTime)) {
start = Double.NEGATIVE_INFINITY;
} else {
start = timeFormat.equals(TimeFormat.DOUBLE) ? Double.parseDouble(startDateTime) : AttributeUtils.parseDateTime(startDateTime);
}
if (endDateTime == null || endDateTime.trim().isEmpty() || "inf".equalsIgnoreCase(endDateTime) || "infinity".equalsIgnoreCase(endDateTime)) {
end = Double.POSITIVE_INFINITY;
} else {
end = timeFormat.equals(TimeFormat.DOUBLE) ? Double.parseDouble(endDateTime) : AttributeUtils.parseDateTime(endDateTime);
}
this.interval = new Interval(start, end);
} catch (Exception e) {
report.logIssue(new Issue(NbBundle.getMessage(ImportContainerImpl.class, "ImportContainerException_Interval_Parse_Error", "[" + startDateTime + "," + endDateTime + "]"), Level.SEVERE));
return;
}
report.log(NbBundle.getMessage(ImportContainerImpl.class, "ImportContainerLog.GraphInterval", "[" + startDateTime + "," + endDateTime + "]"));
}
@Override
public Interval getInterval() {
return interval;
}
@Override
public Double getTimestamp() {
return timestamp;
}
@Override
public void setElementIdType(ElementIdType type) {
if (this.elementIdType != type) {
this.elementIdType = type;
report.log(NbBundle.getMessage(ImportContainerImpl.class, "ImportContainerLog.ElementIdType", elementIdType.toString()));
}
}
@Override
public ElementIdType getElementIdType() {
return elementIdType;
}
@Override
public boolean verify() {
//Edge weight zero or negative
for (EdgeDraftImpl edge : new NullFilterIterable<EdgeDraftImpl>(edgeList)) {
String id = edge.getId();
if (edge.getWeight() < 0f) {
report.logIssue(new Issue(NbBundle.getMessage(ImportContainerImpl.class, "ImportContainerException_Negative_Weight", id), Level.WARNING));
} else if (edge.getWeight() == 0) {
removeEdge(edge);
report.logIssue(new Issue(NbBundle.getMessage(ImportContainerImpl.class, "ImportContainerException_Weight_Zero_Ignored", id), Level.SEVERE));
}
}
//Graph EdgeDefault
if (directedEdgesCount > 0 && undirectedEdgesCount == 0) {
setEdgeDefault(EdgeDirectionDefault.DIRECTED);
} else if (directedEdgesCount == 0 && undirectedEdgesCount > 0) {
setEdgeDefault(EdgeDirectionDefault.UNDIRECTED);
} else if (directedEdgesCount > 0 && undirectedEdgesCount > 0) {
setEdgeDefault(EdgeDirectionDefault.MIXED);
}
//IdType
if (elementIdType.equals(ElementIdType.INTEGER) || elementIdType.equals(ElementIdType.LONG)) {
try {
for (NodeDraftImpl node : nodeList) {
if (elementIdType.equals(ElementIdType.INTEGER)) {
Integer.parseInt(node.getId());
} else if (elementIdType.equals(ElementIdType.LONG)) {
Long.parseLong(node.getId());
}
}
for (EdgeDraftImpl edge : edgeList) {
if (elementIdType.equals(ElementIdType.INTEGER)) {
Integer.parseInt(edge.getId());
} else if (elementIdType.equals(ElementIdType.LONG)) {
Long.parseLong(edge.getId());
}
}
} catch (NumberFormatException e) {
report.logIssue(new Issue(NbBundle.getMessage(ImportContainerImpl.class, "ImportContainerException_ElementIdType_Parse_Error", elementIdType), Level.WARNING));
elementIdType = ElementIdType.STRING;
}
}
//Is dynamic graph
for (NodeDraftImpl node : nodeList) {
if (node != null) {
if (node.isDynamic()) {
dynamicGraph = true;
}
if (node.hasDynamicAttributes()) {
dynamicAttributes = true;
}
}
}
for (EdgeDraftImpl edge : edgeList) {
if (edge != null) {
if (edge.isDynamic()) {
dynamicGraph = true;
}
if (edge.hasDynamicAttributes()) {
dynamicAttributes = true;
}
}
}
//Print time interval values to report
// if (timeIntervalMin != null || timeIntervalMax != null) {
// if (timeFormat.equals(TimeFormat.DATE) || timeFormat.equals(TimeFormat.DATETIME)) {
// try {
// String message = "[" + (timeIntervalMin != null ? DynamicUtilities.getXMLDateStringFromDouble(timeIntervalMin) : "-inf") + ",";
// message += (timeIntervalMax != null ? DynamicUtilities.getXMLDateStringFromDouble(timeIntervalMax) : "+inf") + "]";
// report.log(NbBundle.getMessage(ImportContainerImpl.class, "ImportContainerLog.TimeInterval", message));
// } catch (Exception e) {
// }
// } else {
// String message = "[" + (timeIntervalMin != null ? timeIntervalMin.toString() : "-inf") + ",";
// message += (timeIntervalMax != null ? timeIntervalMax.toString() : "+inf") + "]";
// report.log(NbBundle.getMessage(ImportContainerImpl.class, "ImportContainerLog.TimeInterval", message));
// }
// }
//
//Print TimeFormat
if (dynamicGraph) {
report.log(NbBundle.getMessage(ImportContainerImpl.class, "ImportContainerLog.TimeFormat", timeFormat.toString()));
report.log(NbBundle.getMessage(ImportContainerImpl.class, "ImportContainerLog.TimeRepresentation", timeRepresentation.toString()));
report.log(NbBundle.getMessage(ImportContainerImpl.class, "ImportContainerLog.TimeZone", timeZone.toString()));
}
//Print edge label type
if (lastEdgeType != null) {
report.log(NbBundle.getMessage(ImportContainerImpl.class, "ImportContainerLog.EdgeLabelType", lastEdgeType.getSimpleName()));
}
//Print edge types
if (isMultiGraph()) {
report.log(NbBundle.getMessage(ImportContainerImpl.class, "ImportContainerLog.MultiGraphCount", edgeTypeMap.size() - 1));
}
return true;
}
@Override
public void closeLoader() {
//Remove self-loops
if (!parameters.isSelfLoops() && selfLoops > 0) {
List<EdgeDraftImpl> l = new ArrayList<>();
for (EdgeDraftImpl e : edgeList) {
if (e != null && e.isSelfLoop()) {
l.add(e);
}
}
for (EdgeDraftImpl e : l) {
removeEdge(e);
}
}
if (directedEdgesCount > 0 && edgeDefault.equals(EdgeDirectionDefault.UNDIRECTED)) {
//Force undirected
for (EdgeDraftImpl edge : edgeList.toArray(new EdgeDraftImpl[0])) {
if (edge != null && edge.getDirection().equals(EdgeDirection.DIRECTED)) {
EdgeDraftImpl opposite = getOpposite(edge);
if (opposite != null) {
int oppositeIndex = edgeMap.getInt(opposite.getId());
mergeDirectedEdges(opposite, edge);
edgeMap.removeInt(opposite.getId());
edgeList.set(oppositeIndex, null);
}
}
}
}
//TODO check when mixed is forced
//Clean autoNode
if (!allowAutoNode()) {
for (NodeDraftImpl node : nodeList) {
if (node != null && node.isCreatedAuto()) {
int index = nodeMap.removeInt(node.getId());
nodeList.set(index, null);
}
}
for (EdgeDraftImpl edge : edgeList) {
if (edge != null && (edge.getSource().isCreatedAuto() || edge.getTarget().isCreatedAuto())) {
int index = edgeMap.remove(edge.getId());
edgeList.set(index, null);
}
}
}
//Sort nodes by height
if (parameters.isSortNodesBySize()) {
Collections.sort(nodeList, new Comparator<NodeDraftImpl>() {
@Override
public int compare(NodeDraftImpl o1, NodeDraftImpl o2) {
return new Float(o2 != null ? o2.getSize() : 0f).compareTo(o1 != null ? o1.getSize() : 0f);
}
});
}
//Set id as label for nodes that miss label
if (parameters.isFillLabelWithId()) {
for (NodeDraftImpl node : nodeList) {
if (node != null && node.getLabel() == null) {
node.setLabel(node.getId());
}
}
}
//Set random position
boolean customPosition = false;
for (NodeDraftImpl node : nodeList) {
if (node != null) {
if (Float.isNaN(node.getX())) {
node.setX(0);
}
if (Float.isNaN(node.getY())) {
node.setY(0);
}
if (Float.isNaN(node.getZ())) {
node.setZ(0);
}
if (node.getX() != 0f || node.getY() != 0f) {
customPosition = true;
}
}
}
if (!customPosition) {
for (NodeDraftImpl node : nodeList) {
if (node != null) {
node.setX((float) ((0.01 + Math.random()) * 1000) - 500);
node.setY((float) ((0.01 + Math.random()) * 1000) - 500);
}
}
}
//MANAGEMENT
}
protected void mergeDirectedEdges(EdgeDraftImpl source, EdgeDraftImpl dest) {
EdgeMergeStrategy mergeStrategy = parameters.getEdgesMergeStrategy();
double result = dest.getWeight();
switch (mergeStrategy) {
case AVG:
result = (source.getWeight() + dest.getWeight()) / 2.0;
break;
case MAX:
result = Math.max(source.getWeight(), dest.getWeight());
break;
case MIN:
result = Math.min(source.getWeight(), dest.getWeight());
break;
case SUM:
result = source.getWeight() + dest.getWeight();
break;
case FIRST:
result = dest.getWeight();
break;
case LAST:
result = source.getWeight();
break;
default:
break;
}
dest.setWeight(result);
}
@Override
public boolean isDynamicGraph() {
return dynamicGraph;
}
@Override
public boolean hasDynamicAttributes() {
return dynamicAttributes;
}
//REPORT
@Override
public Report getReport() {
return report;
}
@Override
public void setReport(Report report) {
this.report = report;
}
//PARAMS GETTER
@Override
public boolean allowAutoNode() {
return parameters.isAutoNode();
}
@Override
public boolean allowParallelEdges() {
return parameters.isParallelEdges();
}
@Override
public boolean allowSelfLoop() {
return parameters.isSelfLoops();
}
@Override
public boolean isFillLabelWithId() {
return parameters.isFillLabelWithId();
}
@Override
public EdgeMergeStrategy getEdgesMergeStrategy() {
return parameters.getEdgesMergeStrategy();
}
@Override
public EdgeDirectionDefault getEdgeDefault() {
return edgeDefault;
}
@Override
public boolean isMultiGraph() {
return edgeTypeMap.size() > 1;
}
@Override
public boolean hasSelfLoops() {
return selfLoops > 0;
}
//PARAMS
@Override
public void setAllowAutoNode(boolean value) {
parameters.setAutoNode(value);
}
@Override
public void setAllowParallelEdge(boolean value) {
parameters.setParallelEdges(value);
}
@Override
public void setAllowSelfLoop(boolean value) {
parameters.setSelfLoops(value);
}
@Override
public void setFillLabelWithId(boolean value) {
parameters.setFillLabelWithId(value);
}
@Override
public void setEdgeDefault(EdgeDirectionDefault edgeDefault) {
if (this.edgeDefault != edgeDefault) {
this.edgeDefault = edgeDefault;
report.log(NbBundle.getMessage(ImportContainerImpl.class, "ImportContainerException_Set_EdgeDefault", edgeDefault.toString()));
}
}
@Override
public boolean isAutoScale() {
return parameters.isAutoScale();
}
@Override
public void setAutoScale(boolean autoscale) {
parameters.setAutoScale(autoscale);
}
@Override
public void setEdgesMergeStrategy(EdgeMergeStrategy edgesMergeStrategy) {
parameters.setEdgesMergeStrategy(edgesMergeStrategy);
}
@Override
public Class getEdgeTypeLabelClass() {
return lastEdgeType;
}
//Utility
private int getEdgeType(Object type) {
//Verify
if (type != null) {
Class cl = type.getClass();
if (!(cl.equals(Integer.class)
|| cl.equals(String.class)
|| cl.equals(Float.class)
|| cl.equals(Double.class)
|| cl.equals(Short.class)
|| cl.equals(Byte.class)
|| cl.equals(Long.class)
|| cl.equals(Character.class)
|| cl.equals(Boolean.class))) {
report.logIssue(new Issue(NbBundle.getMessage(ImportContainerImpl.class, "ImportContainerException_Unsupported_Edge_type"), Level.SEVERE));
type = null;
}
if (type != null && lastEdgeType != null && !lastEdgeType.equals(type.getClass())) {
report.logIssue(new Issue(NbBundle.getMessage(ImportContainerImpl.class, "ImportContainerException_Unsupported_Edge_type_Conflict", type.getClass().getSimpleName(), lastEdgeType.getSimpleName()), Level.SEVERE));
type = null;
}
}
if (type != null) {
lastEdgeType = type.getClass();
}
if (edgeTypeMap.containsKey(type)) {
return edgeTypeMap.getInt(type);
}
int id = edgeTypeMap.size();
edgeTypeMap.put(type, id);
return id;
}
private void ensureLongSetArraySize(int type) {
if (edgeTypeSets.length <= type) {
Long2ObjectMap[] l = new Long2ObjectMap[type + 1];
System.arraycopy(edgeTypeSets, 0, l, 0, edgeTypeSets.length);
edgeTypeSets = l;
edgeTypeSets[type] = new Long2ObjectOpenHashMap<>();
}
}
private long getLongId(EdgeDraftImpl edge) {
EdgeDirection direction = edge.getDirection();
boolean directed = edgeDefault.equals(EdgeDirectionDefault.DIRECTED)
|| (edgeDefault.equals(EdgeDirectionDefault.MIXED) && direction != EdgeDirection.UNDIRECTED);
return getLongId(edge.getSource(), edge.getTarget(), directed);
}
private long getLongId(NodeDraftImpl source, NodeDraftImpl target, boolean directed) {
if (directed) {
long edgeId = ((long) source.hashCode()) << 32;
edgeId = edgeId | (long) (target.hashCode());
return edgeId;
} else {
long edgeId = ((long) (source.hashCode() > target.hashCode() ? source.hashCode() : target.hashCode())) << 32;
edgeId = edgeId | (long) (source.hashCode() > target.hashCode() ? target.hashCode() : source.hashCode());
return edgeId;
}
}
private EdgeDraftImpl getOpposite(EdgeDraftImpl edge) {
Long2ObjectMap<int[]> typeSet = edgeTypeSets[getEdgeType(edge.getType())];
long longId = getLongId(edge.getTarget(), edge.getSource(), true);
int[] opposites = typeSet.get(longId);
if (opposites != null && opposites.length > 0) {
return edgeList.get(opposites[0]);
}
return null;
}
private void checkElementDraftImpl(ElementDraft elmt) {
if (elmt == null) {
throw new NullPointerException();
}
if (!(elmt instanceof ElementDraftImpl)) {
throw new ClassCastException();
}
}
private void checkId(String id) {
if (id == null) {
throw new NullPointerException();
}
if (id.trim().isEmpty()) {
throw new IllegalArgumentException("The id can't be empty");
}
}
//UTILITY ITERATOR
private static class NullFilterIterable<T extends ElementDraft> implements Iterable<T> {
private final Collection<T> collection;
public NullFilterIterable(Collection elementCollection) {
this.collection = elementCollection;
}
@Override
public Iterator<T> iterator() {
return new NullFilterIterator<>(collection);
}
}
private static class NullFilterIterator<T extends ElementDraft> implements Iterator<T> {
private T pointer;
private final Iterator<T> itr;
public NullFilterIterator(Collection<T> elementCollection) {
this.itr = elementCollection.iterator();
}
@Override
public boolean hasNext() {
while (itr.hasNext()) {
pointer = itr.next();
if (pointer != null) {
return true;
}
}
return false;
}
@Override
public T next() {
return pointer;
}
@Override
public void remove() {
throw new UnsupportedOperationException("Not supported.");
}
}
}