Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve the cx2 export and import for edge columns: 'name', 'shared name' and 'shared interaction' #32

Open
wants to merge 6 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 64 additions & 9 deletions src/main/java/org/cytoscape/io/internal/cxio/Cx2Importer.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
Expand All @@ -17,6 +18,7 @@
import org.cytoscape.io.internal.CxPreferences;
import org.cytoscape.io.internal.CyServiceModule;
import org.cytoscape.io.internal.cx_reader.ViewMaker;
import org.cytoscape.io.internal.nicecy.NiceCyNetwork;
import org.cytoscape.io.internal.nicecy.NiceCyRootNetwork;
import org.cytoscape.model.CyEdge;
import org.cytoscape.model.CyIdentifiable;
Expand Down Expand Up @@ -107,9 +109,12 @@ public final class Cx2Importer {
//CX ID to suid mapping table
private Map<Long,Long> edgeIdMap;

// node suid to CxNodes mapping table.
// node suid to CxNodes mapping table
private Map<Long, CxNode> cxNodes;

// edge suid to CxEdges mapping table
private Map<Long, CxEdge> cxEdges;

private CxVisualProperty visualProperties;

private List<CxNodeBypass> nodeBypasses;
Expand Down Expand Up @@ -137,6 +142,7 @@ public Cx2Importer(InputStream in, boolean createView) {
nodeIdMap = new TreeMap<>();
edgeIdMap = new TreeMap<>();
cxNodes = new TreeMap<>();
cxEdges = new TreeMap<>();
hasLayout = false;

base = null;
Expand Down Expand Up @@ -228,7 +234,7 @@ public CyNetwork importNetwork() throws IOException, NdexException {
}

}
postProcessTables();
serializeOpaqueAspects();

// create the view
Expand Down Expand Up @@ -316,7 +322,7 @@ private CyNode createCyNodeByCXId(Long cxNodeId) {
nodeIdMap.put(cxNodeId, cyNode.getSUID());
return cyNode;
}

private void createEdge(CxEdge edge) throws NdexException {

Map<String,DeclarationEntry> attributeDeclarations = attrDecls.getAttributesInAspect(CxEdge.ASPECT_NAME);
Expand Down Expand Up @@ -354,12 +360,7 @@ private void createEdge(CxEdge edge) throws NdexException {
throw new NdexException("Edge attribute " + e.getKey() + " is not declared.");
}

if ( attributeDeclarations!=null && (! attributeDeclarations.containsKey(CxUtil.SHARED_INTERACTION) &&
attributeDeclarations.containsKey(CxUtil.INTERACTION))) {
Object v = edge.getAttributes().get(CxUtil.INTERACTION);
localRow.set(CxUtil.SHARED_INTERACTION,v);
}

cxEdges.put(cyEdge.getSUID(),edge);
}

private void createNetworkAttribute(CxNetworkAttribute netAttrs) throws NdexException {
Expand Down Expand Up @@ -416,10 +417,64 @@ private void serializeOpaqueAspects() {

});
}

// helper function to get the node name
private String getNodeName(CxNode cxNode) {
if (cxNode == null) {
return "";
}

Map<String, Object> attributes = cxNode.getAttributes();
Object name = attributes.getOrDefault(CyRootNetwork.SHARED_NAME, attributes.get(CyNetwork.NAME));

return name != null ? name.toString() : "";
}

// post-process after iteration through the Cx2 file
private void postProcessTables() {
// auto-fill 'shared name' column in node table if it is empty
for(final CyNode cyNode : base.getNodeList()) {
CyRow row = baseNodeTable.getRow(cyNode.getSUID());
Set<String> allAttrNames = row.getAllValues().keySet();
Object nodeName = row.get(CyNetwork.NAME,String.class);
Object sharedName = row.get(CyRootNetwork.SHARED_NAME,String.class);
if(nodeName!= null && (!allAttrNames.contains(CyRootNetwork.SHARED_NAME) || sharedName == null)) {
row.set(CyRootNetwork.SHARED_NAME,nodeName);
}
}
// auto-fill specific columns("name", "shared name" and "shared interaction") in edge table if they are empty
for (final CyEdge cyEdge : base.getEdgeList()) {
CxEdge cxEdge = cxEdges.get(cyEdge.getSUID());
CyRow row = baseEdgeTable.getRow(cyEdge.getSUID());
Set<String> allAttrNames = row.getAllValues().keySet();
Object v = row.get(CxUtil.INTERACTION,String.class);
if ( cxEdge != null && v != null && allAttrNames.contains(CxUtil.INTERACTION)) {

if((!allAttrNames.contains(CxUtil.SHARED_INTERACTION)) ||
row.get(CxUtil.SHARED_INTERACTION,String.class) == null) {
row.set(CxUtil.SHARED_INTERACTION,v);
}

CxNode srcNode = cxNodes.get(nodeIdMap.get(cxEdge.getSource()));
CxNode tgtNode = cxNodes.get(nodeIdMap.get(cxEdge.getTarget()));
String defaultFormattedName = getNodeName(srcNode) + " (" + v.toString() + ") " + getNodeName(tgtNode);

if((!allAttrNames.contains(CyNetwork.NAME)) ||
row.get(CyNetwork.NAME,String.class) == null) {
row.set(CyNetwork.NAME,defaultFormattedName);
}
if((!allAttrNames.contains(CyRootNetwork.SHARED_NAME)) ||
row.get(CyRootNetwork.SHARED_NAME,String.class) == null) {
row.set(CyRootNetwork.SHARED_NAME,defaultFormattedName);
}
}
}
}

public String getNetworkName() {
return name;
}


public CyNetworkView createView() throws Exception {
if ( createView) {
Expand Down
119 changes: 112 additions & 7 deletions src/main/java/org/cytoscape/io/internal/cxio/CxExporter.java
Original file line number Diff line number Diff line change
Expand Up @@ -612,8 +612,7 @@ private void writeCx2NetworkAttributes(CXWriter cx2writer) throws JsonGeneration
}

}



private void writeTableVisualStyles(CXWriter cx2Writer) throws IOException, NdexException {
var appManager = CyServiceModule.getService(CyApplicationManager.class);
var tableViewManager = CyServiceModule.getService(CyTableViewManager.class);
Expand Down Expand Up @@ -1046,7 +1045,7 @@ private void writeCx2Edges(CXWriter cx2Writer) throws IOException, NdexException
for (Map.Entry<String,Object>e: row.getAllValues().entrySet()) {
String name = e.getKey();
Object value = e.getValue();
if (isNotNullandFinite(value) && !Settings.isIgnore(name, Settings.IGNORE_NODE_ATTRIBUTES, value) &&
if (isNotNullandFinite(value) && !Settings.isIgnore(name, Settings.IGNORE_EDGE_ATTRIBUTES, value) &&
(edgeColumns == null || edgeColumns.contains(name)) &&
!name.startsWith( CxUtil.sourceNodeMappingPrefix) && !name.startsWith(CxUtil.targetNodeMappingPrefix)) {
edgeAttrs.put(name, value);
Expand Down Expand Up @@ -1852,7 +1851,113 @@ private CxAttributeDeclaration getAttributeDeclarations() {
return result;

}


// Helper method to get the node (shared) name
private String getNodeName(CyRow nodeRow) {
if (nodeRow == null) {
return "";
}

String nodeName = nodeRow.get(CyRootNetwork.SHARED_NAME, String.class);
if (nodeName == null || nodeName.equals("")) {
nodeName = nodeRow.get(CyNetwork.NAME, String.class);
}
return nodeName != null ? nodeName : "";
}

// Helper method to construct expected default edge (shared)name
private final String getDefaultName(CyEdge edge, String interactionVal, CySubNetwork subnet) {
String sourceNodeName = getNodeName(subnet.getRow(edge.getSource(), CyNetwork.DEFAULT_ATTRS));
String targetNodeName = getNodeName(subnet.getRow(edge.getTarget(), CyNetwork.DEFAULT_ATTRS));

return sourceNodeName + " (" + interactionVal + ") " + targetNodeName;
}

// Update edge columns based on flags
private void updateTableColumns(boolean interactionColsMatch, boolean nameColsMatch, boolean isDefaultSharedName, boolean isDefaultName, boolean nodeNameColsMatch) {
if (interactionColsMatch) {
edgeColumns.remove(CyRootNetwork.SHARED_INTERACTION);
}
if (nameColsMatch || isDefaultSharedName) {
edgeColumns.remove(CyRootNetwork.SHARED_NAME);
}
if (isDefaultName) {
edgeColumns.remove(CyNetwork.NAME);
}
if (nodeNameColsMatch) {
nodeColumns.remove(CyRootNetwork.SHARED_NAME);
}
}

// Handler function to determine the export of the following 3 columns in the edge table:
// "interaction", "name", and "shared name"
private void ignoreTableColumnsInCX2() throws NdexException{

boolean interactionColsMatch = true;
boolean nameColsMatch = true;
boolean isDefaultSharedName = true;
boolean isDefaultName = true;
boolean nodeNameColsMatch = true;
VisualMappingManager vmm = CyServiceModule.getService(VisualMappingManager.class);
VisualStyle current_visual_style = vmm.getVisualStyle(view);
CyTable table = view.getModel().getTable(CyEdge.class, CyNetwork.DEFAULT_ATTRS);

// Check if any mappings exist on the "interaction", "name", or "shared name" columns
for(final VisualProperty<?> visual_property : CxUtil.getLexicon(view).getAllVisualProperties()) {
VisualPropertyMapping cx2Mapping = VisualPropertiesGatherer.getCX2Mapping(current_visual_style, visual_property, table, taskMonitor);
if(cx2Mapping!=null && ATTRIBUTE_DATA_TYPE.STRING == cx2Mapping.getMappingDef().getAttributeType()) {
String attName = cx2Mapping.getMappingDef().getAttributeName();
if (visual_property.getTargetDataType() == CyEdge.class) {
switch (attName) {
case CyRootNetwork.SHARED_INTERACTION:
interactionColsMatch = false;
break;
case CyRootNetwork.SHARED_NAME:
nameColsMatch = false;
isDefaultSharedName = false;
break;
case CyNetwork.NAME:
isDefaultName = false;
break;
default:
// No action needed for other columns
break;
}

}else if(visual_property.getTargetDataType() == CyNode.class && CyRootNetwork.SHARED_NAME.equals(attName)) {
nodeNameColsMatch = false;
}
}
}

// Iterate through each subnetwork to determine whether to export "interaction", "name", or "shared name" columns
for (final CySubNetwork subnet : subnetworks) {
for (final CyEdge cyEdge : subnet.getEdgeList()) {
CyRow row = subnet.getRow(cyEdge, CyNetwork.DEFAULT_ATTRS);
String interactionVal = row.get(CxUtil.INTERACTION, String.class);
String sharedInteractionVal = row.get(CyRootNetwork.SHARED_INTERACTION, String.class);
String nameVal = row.get(CyNetwork.NAME, String.class);
String sharedNameVal = row.get(CyRootNetwork.SHARED_NAME, String.class);
String defaultFormattedName = getDefaultName(cyEdge, interactionVal,subnet);
// Update column match flags
interactionColsMatch &= (interactionVal == null ? sharedInteractionVal == null : interactionVal.equals(sharedInteractionVal));
nameColsMatch &= (nameVal == null ? sharedNameVal == null : nameVal.equals(sharedNameVal));
isDefaultSharedName &= (sharedNameVal != null && sharedNameVal.equals(defaultFormattedName));
isDefaultName &= (nameVal != null && nameVal.equals(defaultFormattedName));
}

for (final CyNode cyNode:subnet.getNodeList()) {
CyRow row = subnet.getRow(cyNode,CyNetwork.DEFAULT_ATTRS);
String nameVal = row.get(CyNetwork.NAME, String.class);
String sharedNameVal = row.get(CyRootNetwork.SHARED_NAME, String.class);
nodeNameColsMatch &= (nameVal == null ? sharedNameVal == null : nameVal.equals(sharedNameVal));
}
}

// Remove columns based on match flags
updateTableColumns(interactionColsMatch, nameColsMatch, isDefaultSharedName, isDefaultName, nodeNameColsMatch);
}

public final void writeNetworkInCX2(Collection<String> aspects, final OutputStream out) throws IOException, NdexException {


Expand Down Expand Up @@ -1921,6 +2026,7 @@ public final void writeNetworkInCX2(Collection<String> aspects, final OutputStre
collapsed_groups = expandGroups();

try {
//

//Write attribute declarations first
if ( !attrDecls.getDeclarations().isEmpty())
Expand All @@ -1931,9 +2037,9 @@ public final void writeNetworkInCX2(Collection<String> aspects, final OutputStre
writeCx2NetworkAttributes(cx2Writer);
}

ignoreTableColumnsInCX2();
//write nodes. TODO: Handles CyGroups and internal nodes/edges
writeCx2Nodes(cx2Writer, subNet);

writeCx2Edges(cx2Writer);
writeCX2VisualProperties (cx2Writer);
writeTableVisualStyles(cx2Writer);
Expand All @@ -1957,5 +2063,4 @@ public final void writeNetworkInCX2(Collection<String> aspects, final OutputStre
cx2Writer.printError(msg);
}

}

}
6 changes: 2 additions & 4 deletions src/main/java/org/cytoscape/io/internal/cxio/Settings.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,9 @@ public final class Settings {
public final static Set<String> IGNORE_SINGLE_NETWORK_NODE_ATTRIBUTES = new HashSet<>();
public final static Set<String> IGNORE_SINGLE_NETWORK_NETWORK_ATTRIBUTES = new HashSet<>();

public final static Set<String> CX2_IGNORE_NODE_ATTRIBUTES = new HashSet<>(
Arrays.asList(CyRootNetwork.SHARED_NAME));
public final static Set<String> CX2_IGNORE_NODE_ATTRIBUTES = new HashSet<>();

public final static Set<String> CX2_IGNORE_EDGE_ATTRIBUTES =
new HashSet<>(Arrays.asList(CyNetwork.NAME,CyRootNetwork.SHARED_NAME, CyRootNetwork.SHARED_INTERACTION));
public final static Set<String> CX2_IGNORE_EDGE_ATTRIBUTES = new HashSet<>();

public final static Set<String> cytoscapeBuiltinEdgeTableAttributes =
new HashSet<>(Arrays.asList(CyNetwork.NAME, CyNetwork.SELECTED, CyNetwork.SUID,CxUtil.INTERACTION,
Expand Down