diff --git a/src/main/java/org/cytoscape/io/internal/cxio/Cx2Importer.java b/src/main/java/org/cytoscape/io/internal/cxio/Cx2Importer.java index e5b21ac..e421f2e 100644 --- a/src/main/java/org/cytoscape/io/internal/cxio/Cx2Importer.java +++ b/src/main/java/org/cytoscape/io/internal/cxio/Cx2Importer.java @@ -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; @@ -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; @@ -107,9 +109,12 @@ public final class Cx2Importer { //CX ID to suid mapping table private Map edgeIdMap; - // node suid to CxNodes mapping table. + // node suid to CxNodes mapping table private Map cxNodes; + // edge suid to CxEdges mapping table + private Map cxEdges; + private CxVisualProperty visualProperties; private List nodeBypasses; @@ -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; @@ -228,7 +234,7 @@ public CyNetwork importNetwork() throws IOException, NdexException { } } - + postProcessTables(); serializeOpaqueAspects(); // create the view @@ -316,7 +322,7 @@ private CyNode createCyNodeByCXId(Long cxNodeId) { nodeIdMap.put(cxNodeId, cyNode.getSUID()); return cyNode; } - + private void createEdge(CxEdge edge) throws NdexException { Map attributeDeclarations = attrDecls.getAttributesInAspect(CxEdge.ASPECT_NAME); @@ -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 { @@ -416,10 +417,64 @@ private void serializeOpaqueAspects() { }); } + + // helper function to get the node name + private String getNodeName(CxNode cxNode) { + if (cxNode == null) { + return ""; + } + Map 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 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 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) { diff --git a/src/main/java/org/cytoscape/io/internal/cxio/CxExporter.java b/src/main/java/org/cytoscape/io/internal/cxio/CxExporter.java index 05c5ad8..e1d10e3 100644 --- a/src/main/java/org/cytoscape/io/internal/cxio/CxExporter.java +++ b/src/main/java/org/cytoscape/io/internal/cxio/CxExporter.java @@ -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); @@ -1046,7 +1045,7 @@ private void writeCx2Edges(CXWriter cx2Writer) throws IOException, NdexException for (Map.Entrye: 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); @@ -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 aspects, final OutputStream out) throws IOException, NdexException { @@ -1921,6 +2026,7 @@ public final void writeNetworkInCX2(Collection aspects, final OutputStre collapsed_groups = expandGroups(); try { + // //Write attribute declarations first if ( !attrDecls.getDeclarations().isEmpty()) @@ -1931,9 +2037,9 @@ public final void writeNetworkInCX2(Collection aspects, final OutputStre writeCx2NetworkAttributes(cx2Writer); } + ignoreTableColumnsInCX2(); //write nodes. TODO: Handles CyGroups and internal nodes/edges writeCx2Nodes(cx2Writer, subNet); - writeCx2Edges(cx2Writer); writeCX2VisualProperties (cx2Writer); writeTableVisualStyles(cx2Writer); @@ -1957,5 +2063,4 @@ public final void writeNetworkInCX2(Collection aspects, final OutputStre cx2Writer.printError(msg); } -} - +} \ No newline at end of file diff --git a/src/main/java/org/cytoscape/io/internal/cxio/Settings.java b/src/main/java/org/cytoscape/io/internal/cxio/Settings.java index 37c0665..6aff596 100644 --- a/src/main/java/org/cytoscape/io/internal/cxio/Settings.java +++ b/src/main/java/org/cytoscape/io/internal/cxio/Settings.java @@ -25,11 +25,9 @@ public final class Settings { public final static Set IGNORE_SINGLE_NETWORK_NODE_ATTRIBUTES = new HashSet<>(); public final static Set IGNORE_SINGLE_NETWORK_NETWORK_ATTRIBUTES = new HashSet<>(); - public final static Set CX2_IGNORE_NODE_ATTRIBUTES = new HashSet<>( - Arrays.asList(CyRootNetwork.SHARED_NAME)); + public final static Set CX2_IGNORE_NODE_ATTRIBUTES = new HashSet<>(); - public final static Set CX2_IGNORE_EDGE_ATTRIBUTES = - new HashSet<>(Arrays.asList(CyNetwork.NAME,CyRootNetwork.SHARED_NAME, CyRootNetwork.SHARED_INTERACTION)); + public final static Set CX2_IGNORE_EDGE_ATTRIBUTES = new HashSet<>(); public final static Set cytoscapeBuiltinEdgeTableAttributes = new HashSet<>(Arrays.asList(CyNetwork.NAME, CyNetwork.SELECTED, CyNetwork.SUID,CxUtil.INTERACTION,