R/RCy3.R

Defines functions cyPlot findColumnType continuousMapping discreteMapping getEdgeNamesAndSUIDS setEdgePropertyDirect setNodePropertyDirect obtainEveryOtherValue sNotHexColor setVisualProperty getVisualProperty simpleCap is.multiGraph is.classic.graph getNovelEdges rcyEdgeNames multiGraphToNodePairTable classicGraphToNodePairTable sendEdgeAttributesForGraph sendNodeAttributesForGraph initEdgeAttribute initNodeAttribute remove.redundancies.in.undirected.graph remove.redundancies.in.undirected.graph.old makeRandomGraph demoSimpleGraph makeSimpleGraph getAdjacentEdgeNames cy2.edge.names eda noa eda.names noa.names selectEdgesConnectedBySelectedNodes properlyInitializedEdgeAttribute properlyInitializedNodeAttribute check.cytoscape.plugin.version existing.CytoscapeWindow CytoscapeWindow CytoscapeConnection printf BBSOverride

Documented in cy2.edge.names cyPlot CytoscapeConnection CytoscapeWindow demoSimpleGraph eda eda.names existing.CytoscapeWindow getAdjacentEdgeNames initEdgeAttribute initNodeAttribute makeRandomGraph makeSimpleGraph noa noa.names

# RCy3.R
#--------------------------------------------------------------------------------
# this code is for the Bioconductor build system. You should never need to set or
# read these environment variables in ordinary use.
.BBSOverride <- function(host, port) {
    ret <- list()
    if ((Sys.getenv("RCYTOSCAPE3_PORT_OVERRIDE") != "") &&  (Sys.getenv("RCYTOSCAPE3_HOST_OVERRIDE") != "")) {
      host = Sys.getenv("RCYTOSCAPE3_HOST_OVERRIDE")
      port = as(Sys.getenv("RCYTOSCAPE3_PORT_OVERRIDE"),"integer")
      }
    if (.Platform$r_arch == "x64") {
        if ((Sys.getenv("RCYTOSCAPE3_PORT_OVERRIDE_64") != "") &&  (Sys.getenv("RCYTOSCAPE3_HOST_OVERRIDE_64") != "")) {
          host = Sys.getenv("RCYTOSCAPE3_HOST_OVERRIDE_64")
          port = as(Sys.getenv("RCYTOSCAPE3_PORT_OVERRIDE_64"),"integer")
          }
    }
    #cat(paste("Using host", host, "and port", port, "."))

    ret["host"] <- host
    ret["port"] <- port
    ret
}


# ------------------------------------------------------------------------------
printf = function (...) print (noquote (sprintf (...)))
# ------------------------------------------------------------------------------
setClass("CytoscapeConnectionClass", 
	representation = representation (uri="character"), 
	prototype = prototype(uri="http://localhost:1234/v1")
)

# ------------------------------------------------------------------------------
setClass("CytoscapeWindowClass", 
	representation = representation(
		title="character", 
		window.id='character', 
		graph="graphBase", 
		collectTimings="logical",
		suid.name.dict="list",
		edge.suid.name.dict="list",
                view.id='numeric'), 
	contains = 'CytoscapeConnectionClass', 
	prototype = prototype(title="R graph", 
		graph=new("graphNEL", edgemode='directed'), 
		uri="http://localhost:1234/v1", 
		collectTimings=FALSE, 
		suid.name.dict=list(),
		edge.suid.name.dict=list())
)

# ------------------------------------------------------------------------------
setGeneric ('ping', 
	signature='obj', function(obj) standardGeneric('ping'))
setGeneric ('pluginVersion', 
	signature='obj', function(obj) standardGeneric('pluginVersion'))
setGeneric ('getServerStatus',
    signature='obj', function(obj, api.version) standardGeneric('getServerStatus'))
setGeneric ('createWindow', 
	signature='obj', function(obj) standardGeneric('createWindow'))
setGeneric ('createWindowFromSelection', 
	signature='obj', function(obj, new.windowTitle, return.graph) standardGeneric ('createWindowFromSelection'))
setGeneric('copyCytoscapeNetwork',
  signature = 'obj', function(obj, new_title, copy.graph.to.R = FALSE) standardGeneric('copyCytoscapeNetwork'))
setGeneric('renameCytoscapeNetwork',	
  signature = 'obj',function(obj, new_title, copy.graph.to.R = FALSE) standardGeneric('renameCytoscapeNetwork'))
setGeneric ('getWindowCount', 
	signature='obj', function(obj) standardGeneric ('getWindowCount'))
setGeneric ('getWindowList',
	signature='obj', function(obj) standardGeneric ('getWindowList'))
setGeneric ('deleteWindow', 
	signature='obj', function(obj, window.title=NA) standardGeneric ('deleteWindow'))
setGeneric ('deleteAllWindows', 
	signature='obj', function(obj) standardGeneric ('deleteAllWindows'))
setGeneric ('getArrowShapes', 
	signature='obj', function(obj) standardGeneric ('getArrowShapes'))
setGeneric ('getLayoutNames', 
	signature='obj', function(obj) standardGeneric ('getLayoutNames'))
setGeneric ('getLayoutNameMapping',	
	signature='obj', function(obj) standardGeneric ('getLayoutNameMapping'))
setGeneric ('getLayoutPropertyNames',	
	signature='obj', function(obj, layout.name) standardGeneric ('getLayoutPropertyNames'))
setGeneric ('getLayoutPropertyType',	
	signature='obj', function(obj, layout.name, property.name) standardGeneric ('getLayoutPropertyType'))
setGeneric ('getLayoutPropertyValue', 
	signature='obj', function(obj, layout.name, property.name) standardGeneric ('getLayoutPropertyValue'))
setGeneric('getCommandNames', 
           signature='obj',
           function(obj) standardGeneric ('getCommandNames'))
setGeneric('getCommandsWithinNamespace', 
           signature = 'obj',
           function(obj,
                    namespace) standardGeneric('getCommandsWithinNamespace'))
setGeneric('setCommandProperties', 
           signature = 'obj',
           function(obj,
                    command.name,
                    properties.list, 
                    copy.graph.to.R = FALSE) standardGeneric('setCommandProperties')
)
setGeneric ('setLayoutProperties', 
	signature='obj', function(obj, layout.name, properties.list) standardGeneric ('setLayoutProperties'))
setGeneric ('getLineStyles', 
	signature='obj', function(obj) standardGeneric ('getLineStyles'))
setGeneric ('getNodeShapes', 
	signature='obj', function(obj) standardGeneric ('getNodeShapes'))
setGeneric ('getDirectlyModifiableVisualProperties', 
	signature='obj', function(obj, vizmap.style.name="default") standardGeneric ('getDirectlyModifiableVisualProperties'))
setGeneric ('getAttributeClassNames',	
	signature='obj', function(obj) standardGeneric ('getAttributeClassNames'))
setGeneric ('setGraph', 
	signature='obj', function(obj, graph) standardGeneric ('setGraph'))
setGeneric ('getGraph', 
	signature='obj', function(obj) standardGeneric ('getGraph'))
setGeneric ('sendNodes', 
	signature='obj', function(obj) standardGeneric ('sendNodes'))
setGeneric ('sendEdges', 
	signature='obj', function(obj) standardGeneric ('sendEdges'))

setGeneric ('addCyNode', 
	signature='obj', function(obj, nodeName) standardGeneric ('addCyNode'))
setGeneric ('addCyEdge', 
	signature='obj', function(obj, sourceNode, targetNode, edgeType, directed) standardGeneric ('addCyEdge'))
setGeneric ('addGraphToGraph', 
	signature='obj', function(obj, other.graph) standardGeneric ('addGraphToGraph'))

setGeneric ('setNodeAttributes', 
	signature='obj', function(obj, attribute.name) standardGeneric ('setNodeAttributes'))
setGeneric ('setNodeAttributesDirect', 
	signature='obj', function(obj, attribute.name, attribute.type, node.names, values) standardGeneric ('setNodeAttributesDirect'))

setGeneric ('setEdgeAttributes', 
	signature='obj', function(obj, attribute.name) standardGeneric ('setEdgeAttributes'))
setGeneric ('setEdgeAttributesDirect', 
	signature='obj', function(obj, attribute.name, attribute.type, edge.names, values) standardGeneric ('setEdgeAttributesDirect'))

setGeneric ('displayGraph', 
	signature='obj', function(obj) standardGeneric ('displayGraph'))
setGeneric ('predictTimeToDisplayGraph', 
	signature='obj', function(obj) standardGeneric ('predictTimeToDisplayGraph'))
setGeneric ('layoutNetwork', 
	signature='obj', function(obj, layout.name='grid') standardGeneric ('layoutNetwork'))
setGeneric ('saveLayout', 
	signature='obj', function(obj, filename, timestamp.in.filename=FALSE) standardGeneric ('saveLayout'))
setGeneric ('restoreLayout', 
	signature='obj', function(obj, filename) standardGeneric ('restoreLayout'))
setGeneric ('setNodePosition', 
	signature='obj', function (obj, node.names, x.coords, y.coords) standardGeneric ('setNodePosition'))
setGeneric ('getNodePosition',				signature='obj', function (obj, node.names) standardGeneric ('getNodePosition'))
setGeneric ('getNodeSize',					signature='obj', function (obj, node.names) standardGeneric ('getNodeSize'))
setGeneric ('redraw',							signature='obj', function (obj) standardGeneric ('redraw'))
setGeneric ('hidePanel',						signature='obj', function (obj, panelName) standardGeneric ('hidePanel'))
setGeneric ('hideAllPanels',					signature='obj', function (obj) standardGeneric ('hideAllPanels'))
setGeneric ('dockPanel',						signature='obj', function (obj, panelName) standardGeneric ('dockPanel'))
setGeneric ('floatPanel',						signature='obj', function (obj, panelName) standardGeneric ('floatPanel'))

setGeneric ('setTooltipInitialDelay',		signature='obj', function (obj, msecs) standardGeneric ('setTooltipInitialDelay'))
setGeneric ('setTooltipDismissDelay',	signature='obj', function (obj, msecs) standardGeneric ('setTooltipDismissDelay'))

setGeneric ('raiseWindow',					signature='obj', function (obj, window.title=NA) standardGeneric ('raiseWindow'))
setGeneric ('setWindowSize',				signature='obj', function (obj, width, height) standardGeneric ('setWindowSize'))
setGeneric ('showGraphicsDetails',		signature='obj', function (obj, new.value) standardGeneric ('showGraphicsDetails'))
setGeneric ('fitContent',						signature='obj', function (obj) standardGeneric ('fitContent'))
setGeneric ('fitSelectedContent',			signature='obj', function (obj) standardGeneric ('fitSelectedContent'))
setGeneric ('getCenter',						signature='obj', function (obj) standardGeneric ('getCenter'))
setGeneric ('setCenter',						signature='obj', function (obj, x, y) standardGeneric ('setCenter'))
setGeneric ('getZoom',						signature='obj', function (obj) standardGeneric ('getZoom'))
setGeneric ('setZoom',						signature='obj', function (obj, new.level) standardGeneric ('setZoom'))
setGeneric ('getViewCoordinates',		signature='obj', function (obj) standardGeneric ('getViewCoordinates'))

setGeneric ('getDefaultBackgroundColor',	signature='obj',
            function (obj, vizmap.style.name='default') standardGeneric ('getDefaultBackgroundColor'))
setGeneric ('setDefaultBackgroundColor',  signature='obj', 
             function (obj, new.color, vizmap.style.name='default') standardGeneric ('setDefaultBackgroundColor'))

setGeneric ('getDefaultNodeSelectionColor',  signature='obj', 
             function (obj, vizmap.style.name='default') standardGeneric ('getDefaultNodeSelectionColor'))
setGeneric ('setDefaultNodeSelectionColor',  signature='obj', 
             function (obj, new.color, vizmap.style.name='default') standardGeneric ('setDefaultNodeSelectionColor'))

setGeneric ('getDefaultNodeReverseSelectionColor',  signature='obj',
                function (obj, vizmap.style.name='default') standardGeneric ('getDefaultNodeReverseSelectionColor'))
setGeneric ('setDefaultNodeReverseSelectionColor',  signature='obj',
                function (obj, new.color, vizmap.style.name='default') standardGeneric ('setDefaultNodeReverseSelectionColor'))

setGeneric ('getDefaultEdgeSelectionColor',  signature='obj', 
                function (obj, vizmap.style.name='default') standardGeneric ('getDefaultEdgeSelectionColor'))
setGeneric ('setDefaultEdgeSelectionColor',  signature='obj', 
             function (obj, new.color,  vizmap.style.name='default') standardGeneric ('setDefaultEdgeSelectionColor'))

setGeneric ('getDefaultEdgeReverseSelectionColor',  signature='obj',
                function (obj, vizmap.style.name='default') standardGeneric ('getDefaultEdgeReverseSelectionColor'))
setGeneric ('setDefaultEdgeReverseSelectionColor',  signature='obj',
                function (obj, new.color, vizmap.style.name='default') standardGeneric ('setDefaultEdgeReverseSelectionColor'))

setGeneric ('saveImage',                  signature='obj', function (obj, file.name, image.type, h=600) standardGeneric ('saveImage'))
setGeneric ('saveNetwork',                signature='obj', function (obj, file.name, format='cys') standardGeneric ('saveNetwork'))

setGeneric ('setDefaultNodeShape',        signature='obj', function (obj, new.shape, vizmap.style.name='default') standardGeneric ('setDefaultNodeShape'))
setGeneric ('setDefaultNodeSize',         signature='obj', function (obj, new.size, vizmap.style.name='default') standardGeneric ('setDefaultNodeSize'))
setGeneric ('setDefaultNodeColor',        signature='obj', function (obj, new.color, vizmap.style.name='default') standardGeneric ('setDefaultNodeColor'))
setGeneric ('setDefaultNodeBorderColor',  signature='obj', function (obj, new.color, vizmap.style.name='default') standardGeneric ('setDefaultNodeBorderColor'))
setGeneric ('setDefaultNodeBorderWidth',  signature='obj', function (obj, new.width, vizmap.style.name='default') standardGeneric ('setDefaultNodeBorderWidth'))
setGeneric ('setDefaultNodeFontSize',     signature='obj', function (obj, new.size, vizmap.style.name='default') standardGeneric ('setDefaultNodeFontSize'))
setGeneric ('setDefaultNodeLabelColor',   signature='obj', function (obj, new.color, vizmap.style.name='default') standardGeneric ('setDefaultNodeLabelColor'))

setGeneric ('setDefaultEdgeLineWidth',    signature='obj', function (obj, new.width, vizmap.style.name='default') standardGeneric ('setDefaultEdgeLineWidth'))
setGeneric ('setDefaultEdgeColor',        signature='obj', function (obj, new.color, vizmap.style.name='default') standardGeneric ('setDefaultEdgeColor'))
setGeneric ('setDefaultEdgeSourceArrowColor',        signature='obj', function (obj, new.color, vizmap.style.name='default') standardGeneric ('setDefaultEdgeSourceArrowColor'))
setGeneric ('setDefaultEdgeTargetArrowColor',        signature='obj', function (obj, new.color, vizmap.style.name='default') standardGeneric ('setDefaultEdgeTargetArrowColor'))
setGeneric ('setDefaultEdgeFontSize',     signature='obj', function (obj, new.size, vizmap.style.name='default') standardGeneric ('setDefaultEdgeFontSize'))

setGeneric ('setNodeTooltipRule',       signature='obj', function (obj, node.attribute.name) standardGeneric ('setNodeTooltipRule'))
setGeneric ('setEdgeTooltipRule',       signature='obj', function (obj, edge.attribute.name) standardGeneric ('setEdgeTooltipRule'))
setGeneric ('setNodeLabelRule',         signature='obj', function (obj, node.attribute.name) standardGeneric ('setNodeLabelRule'))
setGeneric ('setEdgeLabelRule',         signature='obj', function (obj, edge.attribute.name) standardGeneric ('setEdgeLabelRule'))

setGeneric ('setNodeColorRule',         signature='obj', 
    function (obj, node.attribute.name, control.points, colors, mode, default.color='#FFFFFF') standardGeneric ('setNodeColorRule'))

setGeneric ('setNodeBorderColorRule',   signature='obj', 
    function (obj, node.attribute.name, control.points, colors, mode, default.color='#000000') standardGeneric ('setNodeBorderColorRule'))

setGeneric ('setNodeBorderWidthRule',   signature='obj', 
    function (obj, node.attribute.name, attribute.values, line.widths, default.width=1) standardGeneric ('setNodeBorderWidthRule'))

setGeneric ('setNodeShapeRule',         signature='obj', 
    function (obj, node.attribute.name, attribute.values, node.shapes, default.shape='ELLIPSE') standardGeneric ('setNodeShapeRule'))
setGeneric ('setNodeSizeRule',          signature='obj', 
    function (obj, node.attribute.name, control.points, node.sizes, mode, default.size=40) standardGeneric ('setNodeSizeRule'))

setGeneric ('setNodeOpacityRule',          signature='obj', 
    function (obj, node.attribute.name, control.points, opacities, mode, aspect='all') standardGeneric ('setNodeOpacityRule'))


setGeneric ('setNodeSizeDirect',          signature='obj', function (obj, node.names, new.sizes) standardGeneric ('setNodeSizeDirect'))
setGeneric ('setNodeLabelDirect',         signature='obj', function (obj, node.names, new.labels) standardGeneric ('setNodeLabelDirect'))
setGeneric ('setNodeFontSizeDirect',      signature='obj', function (obj, node.names, new.sizes) standardGeneric ('setNodeFontSizeDirect'))
setGeneric ('setNodeLabelColorDirect',    signature='obj', function (obj, node.names, new.colors) standardGeneric ('setNodeLabelColorDirect'))
setGeneric ('setNodeWidthDirect',         signature='obj', function (obj, node.names, new.widths) standardGeneric ('setNodeWidthDirect'))
setGeneric ('setNodeHeightDirect',        signature='obj', function (obj, node.names, new.heights) standardGeneric ('setNodeHeightDirect'))
setGeneric ('setNodeShapeDirect',         signature='obj', function (obj, node.names, new.shapes) standardGeneric ('setNodeShapeDirect'))
setGeneric ('setNodeImageDirect',         signature='obj', function (obj, node.names, image.positions) standardGeneric ('setNodeImageDirect'))
setGeneric ('setNodeColorDirect',         signature='obj', function (obj, node.names, new.colors) standardGeneric ('setNodeColorDirect'))
setGeneric ('setNodeBorderWidthDirect',   signature='obj', function (obj, node.names, new.sizes) standardGeneric ('setNodeBorderWidthDirect'))
setGeneric ('setNodeBorderColorDirect',   signature='obj', function (obj, node.names, new.colors) standardGeneric ('setNodeBorderColorDirect'))

setGeneric ('setNodeOpacityDirect',       signature='obj', function (obj, node.names, new.values) standardGeneric ('setNodeOpacityDirect'))
setGeneric ('setNodeFillOpacityDirect',   signature='obj', function (obj, node.names, new.values) standardGeneric ('setNodeFillOpacityDirect'))
setGeneric ('setNodeLabelOpacityDirect',  signature='obj', function (obj, node.names, new.values) standardGeneric ('setNodeLabelOpacityDirect'))
setGeneric ('setNodeBorderOpacityDirect', signature='obj', function (obj, node.names, new.values) standardGeneric ('setNodeBorderOpacityDirect'))
setGeneric ('setEdgeOpacityDirect',         signature='obj', function (obj, edge.names, new.values) standardGeneric ('setEdgeOpacityDirect'))

setGeneric ('setEdgeColorDirect', signature='obj', function (obj, edge.names, new.value) standardGeneric ('setEdgeColorDirect'))
setGeneric ('setEdgeLabelDirect', signature='obj', function (obj, edge.names, new.value) standardGeneric ('setEdgeLabelDirect'))
setGeneric ('setEdgeFontFaceDirect', signature='obj', function (obj, edge.names, new.value) standardGeneric ('setEdgeFontFaceDirect'))
setGeneric ('setEdgeFontSizeDirect', signature='obj', function (obj, edge.names, new.value) standardGeneric ('setEdgeFontSizeDirect'))
setGeneric ('setEdgeLabelColorDirect', signature='obj', function (obj, edge.names, new.value) standardGeneric ('setEdgeLabelColorDirect'))
setGeneric ('setEdgeTooltipDirect', signature='obj', function (obj, edge.names, new.values) standardGeneric ('setEdgeTooltipDirect'))
setGeneric ('setEdgeLineWidthDirect', signature='obj', function (obj, edge.names, new.value) standardGeneric ('setEdgeLineWidthDirect'))
setGeneric ('setEdgeLineStyleDirect', signature='obj', function (obj, edge.names, new.values) standardGeneric ('setEdgeLineStyleDirect'))
setGeneric ('setEdgeSourceArrowShapeDirect', signature='obj', function (obj, edge.names, new.values) standardGeneric ('setEdgeSourceArrowShapeDirect'))
setGeneric ('setEdgeTargetArrowShapeDirect', signature='obj', function (obj, edge.names, new.values) standardGeneric ('setEdgeTargetArrowShapeDirect'))
setGeneric ('setEdgeSourceArrowColorDirect', signature='obj', function (obj, edge.names, new.colors) standardGeneric ('setEdgeSourceArrowColorDirect'))
setGeneric ('setEdgeTargetArrowColorDirect', signature='obj', function (obj, edge.names, new.colors) standardGeneric ('setEdgeTargetArrowColorDirect'))
setGeneric ('setEdgeLabelOpacityDirect', signature='obj', function (obj, edge.names, new.value) standardGeneric ('setEdgeLabelOpacityDirect'))
setGeneric ('setEdgeSourceArrowOpacityDirect', signature='obj', function (obj, edge.names, new.values) standardGeneric ('setEdgeSourceArrowOpacityDirect'))
setGeneric ('setEdgeTargetArrowOpacityDirect', signature='obj', function (obj, edge.names, new.values) standardGeneric ('setEdgeTargetArrowOpacityDirect'))
#setGeneric ('setEdgeLabelPositionDirect', signature='obj', function (obj, edge.names, new.value) standardGeneric ('setEdgeLabelPositionDirect'))
#setGeneric ('setEdgeLabelWidthDirect', signature='obj', function (obj, edge.names, new.value) standardGeneric ('setEdgeLabelWidthDirect'))


setGeneric ('setEdgeLineStyleRule',     signature='obj', 
    function (obj, edge.attribute.name, attribute.values, line.styles, default.style='SOLID') standardGeneric ('setEdgeLineStyleRule'))

setGeneric ('setEdgeLineWidthRule', signature='obj', 
    function (obj, edge.attribute.name, attribute.values, line.widths, default.width='1') standardGeneric ('setEdgeLineWidthRule'))

setGeneric ('setEdgeTargetArrowRule',   signature='obj', 
    function (obj, edge.attribute.name, attribute.values, arrows, default='ARROW') standardGeneric ('setEdgeTargetArrowRule'))
setGeneric ('setEdgeSourceArrowRule',   signature='obj', 
    function (obj, edge.attribute.name, attribute.values, arrows, default='ARROW') standardGeneric ('setEdgeSourceArrowRule'))

setGeneric ('setEdgeTargetArrowColorRule',   signature='obj', 
    function (obj, edge.attribute.name, control.points, colors, mode="interpolate", default.color='#000000') standardGeneric ('setEdgeTargetArrowColorRule'))
setGeneric ('setEdgeSourceArrowColorRule',   signature='obj', 
    function (obj, edge.attribute.name, control.points, colors, mode="interpolate", default.color='#000000') standardGeneric ('setEdgeSourceArrowColorRule'))

setGeneric ('setEdgeColorRule',         signature='obj',
    function (obj, edge.attribute.name, control.points, colors, mode="interpolate", default.color='#FFFFFF') standardGeneric ('setEdgeColorRule'))

setGeneric ('setEdgeOpacityRule',          signature='obj', 
    function (obj, edge.attribute.name, control.points, opacities, mode) standardGeneric ('setEdgeOpacityRule'))


setGeneric ('getNodeCount',             signature='obj', function (obj) standardGeneric ('getNodeCount'))
setGeneric ('getEdgeCount',             signature='obj', function (obj) standardGeneric ('getEdgeCount'))
setGeneric ('getNodeAttribute',         signature='obj', function (obj, node.name, attribute.name) standardGeneric ('getNodeAttribute'))
setGeneric ('getNodeAttributeType',     signature='obj', function (obj, attribute.name) standardGeneric ('getNodeAttributeType'))
setGeneric ('getAllNodeAttributes',     signature='obj', function (obj, onlySelectedNodes=FALSE) standardGeneric ('getAllNodeAttributes'))
setGeneric ('getEdgeAttribute',         signature='obj', function (obj, edge.name, attribute.name) standardGeneric ('getEdgeAttribute'))
setGeneric ('getEdgeAttributeType',     signature='obj', function (obj, attribute.name) standardGeneric ('getEdgeAttributeType'))
setGeneric ('getAllEdgeAttributes',     signature='obj', function (obj, onlySelectedEdges=FALSE) standardGeneric ('getAllEdgeAttributes'))
setGeneric ('getNodeAttributeNames',    signature='obj', function (obj) standardGeneric ('getNodeAttributeNames'))
setGeneric ('getEdgeAttributeNames',    signature='obj', function (obj) standardGeneric ('getEdgeAttributeNames'))
setGeneric ('deleteNodeAttribute',      signature='obj', function (obj, attribute.name) standardGeneric ('deleteNodeAttribute'))
setGeneric ('deleteEdgeAttribute',      signature='obj', function (obj, attribute.name) standardGeneric ('deleteEdgeAttribute'))
setGeneric ('getAllNodes',              signature='obj', function (obj) standardGeneric ('getAllNodes'))
setGeneric ('getAllEdges',              signature='obj', function (obj) standardGeneric ('getAllEdges'))
setGeneric ('selectNodes',              signature='obj', function (obj, node.names, preserve.current.selection=TRUE) standardGeneric ('selectNodes'))
setGeneric('selectAllNodes',            signature = 'obj',function(obj) standardGeneric('selectAllNodes'))
setGeneric ('getSelectedNodes',         signature='obj', function (obj) standardGeneric ('getSelectedNodes'))
setGeneric ('clearSelection',           signature='obj', function (obj) standardGeneric ('clearSelection'))
setGeneric ('getSelectedNodeCount',     signature='obj', function (obj) standardGeneric ('getSelectedNodeCount'))
setGeneric ('hideNodes',                signature='obj', function (obj, node.names) standardGeneric ('hideNodes'))
setGeneric ('unhideNodes',              signature='obj', function (obj, node.names) standardGeneric ('unhideNodes'))
setGeneric ('hideSelectedNodes',        signature='obj', function (obj) standardGeneric ('hideSelectedNodes'))
setGeneric ('invertNodeSelection',      signature='obj', function (obj) standardGeneric ('invertNodeSelection'))
setGeneric ('deleteSelectedNodes',      signature='obj', function (obj) standardGeneric ('deleteSelectedNodes'))
setGeneric ('selectEdges',              signature='obj', function (obj, edge.names, preserve.current.selection=TRUE) standardGeneric ('selectEdges'))
setGeneric('selectAllEdges',            signature = 'obj', function(obj) standardGeneric('selectAllEdges'))
setGeneric ('invertEdgeSelection',      signature='obj', function (obj) standardGeneric ('invertEdgeSelection'))
setGeneric ('deleteSelectedEdges',      signature='obj', function (obj) standardGeneric ('deleteSelectedEdges'))
setGeneric ('getSelectedEdges',         signature='obj', function (obj) standardGeneric ('getSelectedEdges'))
setGeneric ('clearSelection',           signature='obj', function (obj) standardGeneric ('clearSelection'))
setGeneric ('getSelectedEdgeCount',     signature='obj', function (obj) standardGeneric ('getSelectedEdgeCount'))
setGeneric ('hideSelectedEdges',        signature='obj', function (obj) standardGeneric ('hideSelectedEdges'))

setGeneric ('unhideAll',                signature='obj', function (obj) standardGeneric ('unhideAll'))

setGeneric ('getFirstNeighbors',        signature='obj', function (obj, node.names, as.nested.list=FALSE) standardGeneric ('getFirstNeighbors'))
setGeneric ('selectFirstNeighborsOfSelectedNodes',
                                        signature='obj', function (obj) standardGeneric ('selectFirstNeighborsOfSelectedNodes'))
setGeneric ('sfn',                      signature='obj', function (obj) standardGeneric ('sfn'))
setGeneric ('selectEdgesConnectedBySelectedNodes', 
            signature='obj', function(obj) standardGeneric ('selectEdgesConnectedBySelectedNodes'))

#-----------------------------------------------------------
# methods related to transmitting data from Cytoscape to R
#-----------------------------------------------------------
setGeneric ('getWindowID',                   signature='obj', function (obj, window.title) standardGeneric ('getWindowID'))
setGeneric ('haveNodeAttribute',             signature='obj', function (obj, node.names, attribute.name) standardGeneric ('haveNodeAttribute'))
setGeneric ('haveEdgeAttribute',             signature='obj', function (obj, edge.names, attribute.name) standardGeneric ('haveEdgeAttribute'))
setGeneric ('copyNodeAttributesFromCyGraph', signature='obj', function (obj, window.id, existing.graph) standardGeneric ('copyNodeAttributesFromCyGraph'))
setGeneric ('copyEdgeAttributesFromCyGraph', signature='obj', function (obj, window.id, existing.graph) standardGeneric ('copyEdgeAttributesFromCyGraph'))
setGeneric ('getGraphFromCyWindow',          signature='obj', function (obj, window.title) standardGeneric ('getGraphFromCyWindow'))
setGeneric('connectToNewestCyWindow', 
           signature = 'obj',
           function(obj,
                    copyToR = FALSE) standardGeneric('connectToNewestCyWindow')
)

#-----------------------------------------------------------
# methods related to visual styles
#-----------------------------------------------------------
setGeneric ('getVisualStyleNames',    signature='obj', function (obj) standardGeneric ('getVisualStyleNames'))
setGeneric ('copyVisualStyle',        signature='obj', function (obj, from.style, to.style) standardGeneric ('copyVisualStyle'))
setGeneric ('setVisualStyle',         signature='obj', function (obj, new.style.name) standardGeneric ('setVisualStyle'))
setGeneric ('lockNodeDimensions',     signature='obj', function (obj, new.state, visual.style.name='default') standardGeneric ('lockNodeDimensions'))

#-----------------------------------------------------------
# private methods, for internal use only
#-----------------------------------------------------------
setGeneric ('.addNodes',
            signature='obj', function (obj, other.graph) standardGeneric ('.addNodes'))
setGeneric ('.addEdges', 
            signature='obj', function (obj, other.graph) standardGeneric ('.addEdges'))
setGeneric ('.getWindowNameFromSUID', 
            signature='obj', function (obj, win.suid) standardGeneric ('.getWindowNameFromSUID'))
setGeneric ('.getNetworkViews', 
            signature='obj', function (obj) standardGeneric ('.getNetworkViews'))
setGeneric ('.nodeNameToNodeSUID',
            signature='obj', function (obj, node.names) standardGeneric ('.nodeNameToNodeSUID'))
setGeneric ('.nodeSUIDToNodeName', 
            signature='obj', function (obj, node.suids) standardGeneric ('.nodeSUIDToNodeName'))
setGeneric ('.edgeNameToEdgeSUID',
            signature='obj', function (obj, edge.names) standardGeneric ('.edgeNameToEdgeSUID'))
setGeneric ('.edgeSUIDToEdgeName', 
            signature='obj', function (obj, edge.suids) standardGeneric ('.edgeSUIDToEdgeName'))
setGeneric ('cyPlot', function (node.df, edge.df) standardGeneric('cyPlot'))

# ------------------------------------------------------------------------------
setValidity("CytoscapeWindowClass", function(object) {
    if (length(object@title) != 1){
        "'title' is not a single string" 
    }
    else if (!nzchar(object@title)){
        "'title' is an empty string"
    }
    validObject(object@graph)
}) # END setValidity

# ------------------------------------------------------------------------------
CytoscapeConnection = function(host='localhost', port=1234) {

    res <- .BBSOverride(host, port)
    host = res$host
    port = res$port
    uri = sprintf('http://%s:%s', host, port)
    cc = new('CytoscapeConnectionClass', uri = uri)
    if (!url.exists(uri)){
        write(sprintf('Connection failed.'), stderr())
        write(sprintf('To troubleshoot: 1) Please ensure that you have Cytoscape open'), stderr())
        write(sprintf('2) that the latest version of CyREST is installed.'), stderr())
        write(sprintf('3) that Cytoscape uses Java 8 (not 7 or below).'), stderr())
        write(sprintf('To help troubleshooting, please check:'), stderr())
        write(sprintf('http://www.cytoscape.org/troubleshooting.html'), stderr())
        return()
    }
    return(cc)
} # END CytoscapeConnection

# ------------------------------------------------------------------------------
# the 'CytoscapeWindow' class constructor, defined as a simple function
CytoscapeWindow = function(title, graph=new('graphNEL', edgemode='directed'), host='localhost', 
                           port=1234, create.window=TRUE, overwriteWindow=FALSE, collectTimings=FALSE){
    res <- .BBSOverride(host, port)
    host = res$host
    port = res$port
	uri = sprintf('http://%s:%s', host, port)
	
    # new 'CytoscapeConnectionClass' object
	cy.conn = CytoscapeConnection(host, port)
	if (is.null(cy.conn)){
	    return()
	}
	check.cytoscape.plugin.version(cy.conn)
	# if the user has specified, delete already existing window(s) with the same title
	if (overwriteWindow) {
        if (title %in% as.character(getWindowList(cy.conn))) {
		    deleteWindow(cy.conn, title)
		}
	}
	
	if (!is.na(getWindowID(cy.conn, title))) {
		write(sprintf('There is already a window in Cytoscape named "%s".', title), stderr())
		write(sprintf('Please use a unique name, or set "overwriteWindow=TRUE".'), stderr())
		stop()
	}
    
    # add a label to each node if not already present. default label is the node name, the node ID    	
    if (is.classic.graph(graph)){
        if (edgemode(graph) == 'undirected') {
            graph = remove.redundancies.in.undirected.graph(graph)
        }
    }
	# are all node attributes properly initialized?
	node.attributes = noa.names(graph)
	if (length(node.attributes) > 0) {
		check.list = list()
		for (node.attribute in node.attributes) {
			check.list[[node.attribute]] = properlyInitializedNodeAttribute(graph, node.attribute)
		}
		uninitialized.attributes = which(check.list == FALSE)
		if (length(uninitialized.attributes) > 0) {
			write(sprintf("%d uninitialized node attribute/s", length(uninitialized.attributes)), stderr())
			return()
		}
	} # if node.attributes

	# are all edge attributes properly initialized?
	edge.attributes = eda.names(graph)
	if (length(edge.attributes) > 0) {
		check.list = list()
		for (edge.attribute in edge.attributes) {
			check.list[[edge.attribute]] = properlyInitializedEdgeAttribute(graph, edge.attribute)
		}
		uninitialized.attributes = which(check.list == FALSE)
		if (length(uninitialized.attributes) > 0) {
			write(sprintf("%d uninitialized edge attribute/s", length(uninitialized.attributes)), stderr())
			return()
		}
	} # if edge.attributes
	
	if (!'label' %in% noa.names(graph)) {
		write('nodes have no label attribute -- adding default labels', stderr())
		graph = initNodeAttribute(graph, 'label', 'char', 'noLabel')
		if (length(nodes(graph) > 0)) {
			nodeData(graph, nodes(graph), 'label') = nodes(graph) # nodes(graph) returns strings
		}
	}
    
	# create new 'CytoscapeWindow' object
	cw = new('CytoscapeWindowClass', title=title, graph=graph, uri=uri, 
					 collectTimings=collectTimings, suid.name.dict = list(), edge.suid.name.dict=list())
	
	if (create.window) {
		cw@window.id = createWindow(cw)
	}
	cw@collectTimings = collectTimings
	
	# let user know that a new window was created
	write(sprintf('New window named "%s" was created in Cytoscape.', title), stderr())
	
	return (cw)

} # END 'CytsoscapeWindow' constructor

# ------------------------------------------------------------------------------
existing.CytoscapeWindow = 
    function(title, host='localhost', port=1234, copy.graph.from.cytoscape.to.R=FALSE) 
        {
        res <- .BBSOverride(host, port)
        host <- res$host
        port <- res$port
        
		uri <- sprintf('http://%s:%s', host, port)
		
        # establish a connection to Cytoscape
        cy.conn <- CytoscapeConnection(host, port)
		
        if (is.null(cy.conn)) {
            write(sprintf("ERROR in existing.CytoscapeWindow():\n\t Cytoscape connection could not be established >> NULL returned"), stderr())
            
            return()
        }
        # ensure the script is using the latest cyREST plugin version 
        check.cytoscape.plugin.version(cy.conn)
        
		existing.window.id = as.character(getWindowID(cy.conn, title))
		
		# inform user if the window does not exist
        if (is.na(existing.window.id)) {
            write(sprintf("ERROR in RCy3::existing.CytoscapeWindow():\n\t no window named '%s' exists in Cytoscape >> choose from the following titles: ", title), stderr())
			write(as.character(getWindowList(cy.conn)), stderr())
            return(NA)
		}
		
		# get graph from Cytoscape
        cy.window <- new('CytoscapeWindowClass', title=title, window.id=existing.window.id, uri=uri)

        if (copy.graph.from.cytoscape.to.R) {
            # copy over graph
            g.cy <- getGraphFromCyWindow(cy.window, title)
            cy.window <- setGraph(cy.window, g.cy)

            # copy over [email protected]
            resource.uri <- paste(cy.window@uri, pluginVersion(cy.window), "networks", as.character(cy.window@window.id), sep="/")
            request.res <- GET(url=resource.uri)
            request.res <- fromJSON(rawToChar(request.res$content))

            if (length(request.res$elements$nodes) != 0){
                cy.window@suid.name.dict = lapply(request.res$elements$nodes, function(n) { 
                    list(name=n$data$name, SUID=n$data$SUID) })
            }
            if (length(request.res$elements$edges) != 0){
                cy.window@edge.suid.name.dict = lapply(request.res$elements$edges, function(e) {
                    list(name=e$data$name, SUID=e$data$SUID) })
            }
        }
        return (cy.window)
}
## END existing.CytsoscapeWindow


# ------------------------------------------------------------------------------
check.cytoscape.plugin.version = function(cyCon) 
{
	plugin.version.string = pluginVersion(cyCon)
	string.tmp1 = strsplit(plugin.version.string, ' ')[[1]][1]
	string.tmp2 = gsub('[a-z]', '', string.tmp1)
	string.tmp3 = gsub('[A-Z]', '', string.tmp2)
	plugin.version = as.numeric(string.tmp3)
	
	expected.version = 1
	
	if(plugin.version < expected.version) { 
		write(' ', stderr())
		write(sprintf('This version of the RCy3 package requires CyREST plugin version %s or greater.', expected.version), 
					stderr ())
		write(sprintf('However, you are using version %s. You must upgrade.', plugin.version), stderr ())
		write('Please visit the plugins page at http://www.cytoscape.org.', stderr ())
		write(' ', stderr())
		stop('Wrong CyREST version.')
	}
} # END check.cytoscape.plugin.version

#------------------------------------------------------------------------------------------------------------------------
setMethod('ping', signature = 'CytoscapeConnectionClass',
	function(obj) {
		conn.str <- paste(obj@uri, pluginVersion(obj), sep="/")
		res <- GET(conn.str)
		apiVersion <- fromJSON(rawToChar(res$content))$apiVersion
		
		if(length(apiVersion) > 0) {
			return("It works!")
		} else {
			write(sprintf('CyREST connection problem. RCy3 exits!'), stderr())
			stop()
		}
	}) # END ping
#------------------------------------------------------------------------------------------------------------------------
setMethod('pluginVersion', 'CytoscapeConnectionClass', 
	function(obj) {
		res <- GET(obj@uri)
		# get vector with available plugin versions
		available.api.versions <- fromJSON(rawToChar(res$content))$availableApiVersion
		
		api.version <- character(0)
		
		# loop through the vector and check which is the correct plugin version
		for(i in 1:length(available.api.versions)) {
		    server.status = getServerStatus(obj, available.api.versions[i])
		    
		    if(server.status$status_code == 200) {
		        api.version = fromJSON(rawToChar(server.status$content))$apiVersion
		    }
		}
		# current api.version will be the highest/latest version
		return(api.version)
	}) # END pluginVersion

# ------------------------------------------------------------------------------
setMethod('getServerStatus', 'CytoscapeConnectionClass',
          function(obj, api.version) {
              request.uri = paste(obj@uri, api.version, sep="/")
              request.res = GET(url=request.uri)
              return(request.res)
          }) # END getServerStatus

# ------------------------------------------------------------------------------
setMethod('createWindow', 'CytoscapeWindowClass', 
	function(obj) {
		obj@graph@graphData$name <- obj@title
		graph.attributes <- obj@graph@graphData
		graph.elements = list(nodes = list(), edges = list())
		
		cygraph <- toJSON(list(data = graph.attributes, elements = graph.elements))
		resource.uri <- paste(obj@uri, pluginVersion(obj), "networks", sep="/")
		request.res <- POST(url = resource.uri, body = cygraph, encode = "json")
		window.id <- unname(fromJSON(rawToChar(request.res$content)))
		
		return(as.character(window.id))
})

#------------------------------------------------------------------------------------------------------------------------
setMethod ('createWindowFromSelection', 'CytoscapeWindowClass',

    function (obj, new.windowTitle, return.graph=FALSE) {
        if (getSelectedNodeCount (obj) == 0) {
            write (noquote ('RCy3::createWindowFromSelection error:  no nodes are selected'), stderr ())
            return (NA)
        }

        if (new.windowTitle %in% as.character (getWindowList (obj))) {
            msg <- sprintf ('RCy3::createWindowFromSelection error:  window "%s" already exists', new.windowTitle)
            write (noquote (msg), stderr ())
            return (NA)
        }
        
        # create new window
        cy.window <- CytoscapeWindow (new.windowTitle)
        
        net.SUID <- as.character(cy.window@window.id)
        version <- pluginVersion(obj)
        
        
        # copy nodes over
        selected.nodes <- getSelectedNodes(obj)
        resource.uri <- paste(obj@uri, version, "networks", net.SUID, "nodes", sep="/")
        new.nodes.JSON <- toJSON(selected.nodes)
        request.res <- POST(url=resource.uri, body=new.nodes.JSON, encode="json")
        new.node.SUIDs <- unname(fromJSON(rawToChar(request.res$content)))
        new.node.names <- do.call(rbind, lapply(new.node.SUIDs, data.frame, stringsAsFactors=FALSE))
        invisible(request.res)
        
        
        # copy node attributes over
        node.attribute.names <- noa.names(obj@graph)
        for (attribute.name in node.attribute.names) {
            printf('sending node attribute "%s"', attribute.name)
            caller.specified.attribute.class <- attr(nodeDataDefaults(obj@graph, attribute.name), 'class')
            values <- noa(obj@graph, attribute.name)[selected.nodes]
            values <- data.frame(values)
            values["name"] <- rownames(values)
            node.name.suid.value.df <- merge(new.node.names, values, by='name')
            
            # converts the above data frame data in the cyREST [SUID:value]-pairs format
            node.SUID.value.pairs <- 
                apply(node.name.suid.value.df[,c('SUID','values')], 1, function(x) {list(SUID=unname(x[1]), value=unname(x[2]))})
            apply(data.frame(new.node.SUIDs, values), 1, function(x) {list(SUID=unname(x[1]), value=unname(x[2]))})
            node.SUID.value.pairs.JSON = toJSON(node.SUID.value.pairs)
            resource.uri <- 
                paste(obj@uri, version, "networks", net.SUID, "tables/defaultnode/columns", attribute.name, sep="/")
            request.res <- PUT(url=resource.uri, body=node.SUID.value.pairs.JSON, encode="json")
            invisible(request.res)
        }
        
        
        # copy edges over
        if (is.classic.graph(obj@graph)) {
            tbl.edges <- .classicGraphToNodePairTable(obj@graph)
        } else if (is.multiGraph(obj@graph)) {
            tbl.edges <- .multiGraphToNodePairTable(obj@graph)
        }
        new.tbl.edges <- tbl.edges[tbl.edges$source %in% selected.nodes & tbl.edges$target %in% selected.nodes,]
        num.edges.to.copy <- nrow(new.tbl.edges)
        if (num.edges.to.copy > 0) {
            directed <- rep(TRUE, nrow(new.tbl.edges)) #TODO enable undirected?
            
            if (num.edges.to.copy == 1) {
                # get the SUIDs of the source nodes for the new edges
                source.node.SUIDs <- new.node.names$SUID[new.node.names$name==new.tbl.edges$source]
                # get the SUIDs of the target nodes for the new edges
                target.node.SUIDs <- new.node.names$SUID[new.node.names$name==new.tbl.edges$target]
            }else{
                source.node.SUIDs <- new.node.names$SUID[match(new.tbl.edges$source, new.node.names$name)]
                target.node.SUIDs <- new.node.names$SUID[match(new.tbl.edges$target, new.node.names$name)]
            }
            # format the new edges data for sending to Cytoscape
            edge.tbl.records = 
                apply(cbind(source.node.SUIDs, target.node.SUIDs, directed, new.tbl.edges$edgeType), MARGIN=1,
                      FUN=function(r) {list(source=unname(r[[1]]), target=unname(r[[2]]), directed=unname(r[[3]]), interaction=unname(r[[4]]))})
            edge.tbl.records.JSON = toJSON(edge.tbl.records)
            resource.uri = paste(cy.window@uri, version, "networks", net.SUID, "edges", sep="/")
            request.res = POST(url=resource.uri, body=edge.tbl.records.JSON, encode="json")
            new.edge.SUIDs <- unname(fromJSON(rawToChar(request.res$content)))
            invisible(request.res)
        }
        
        
        # copy edge attributes over
        edge.attribute.names = eda.names(obj@graph)
        for (attribute.name in edge.attribute.names) {
            printf('sending edge attribute "%s"', attribute.name)
            values <- eda(obj@graph, attribute.name)

            if (num.edges.to.copy == 1) {
                # add edge name to the dataframe
                edge.name.suid.value.df <- data.frame(rbind(unlist(new.edge.SUIDs)))
                edge.name.suid.value.df$edgeName <- paste0(new.node.names$name[new.node.names$SUID==edge.name.suid.value.df$source], "|",
                                                           new.node.names$name[new.node.names$SUID==edge.name.suid.value.df$target])
                edge.name.suid.value.df$edgeValue <- unname(values)[(names(values)==edge.name.suid.value.df$edgeName)]
            }else{
                edge.name.suid.value.df <- data.frame(matrix(unlist(new.edge.SUIDs), nrow=3, byrow = TRUE))
                names(edge.name.suid.value.df) <-  names(data.frame(rbind(unlist(new.edge.SUIDs[[1]]))))
                edge.name.suid.value.df$edgeName <- paste0(new.node.names$name[match(edge.name.suid.value.df$source, new.node.names$SUID)], "|",
                                                           new.node.names$name[match(edge.name.suid.value.df$target, new.node.names$SUID)])
                edge.name.suid.value.df$edgeValue <- unname(values)[match(edge.name.suid.value.df$edgeName, names(values))]
            }
            edge.SUID.value.pairs <- 
                apply(edge.name.suid.value.df[,c('SUID','edgeValue')], 1, function(x) {list(SUID=unname(x[1]), value=unname(x[2]))})
            edge.SUID.value.pairs.JSON = toJSON(edge.SUID.value.pairs)
            resource.uri <- 
                paste(cy.window@uri, version, "networks", net.SUID, "tables/defaultedge/columns", attribute.name, sep="/")
            request.res <- PUT(url=resource.uri, body=edge.SUID.value.pairs.JSON, encode="json")
            invisible(request.res)
        }
        
        return (existing.CytoscapeWindow (new.windowTitle, copy.graph.from.cytoscape.to.R = return.graph))
    }) # createWindowFromSelection

#' Copy a Cytoscape Network 
#'
#' Makes a copy of a Cytoscape Network with all of its edges and nodes 
#'
#' @param obj Cytoscape network 
#' @param new_title New name for the copy
#' @param copy.graph.to.R Logical whether to copy the graph to a new object in R 
#' 
#' @return Connection to new copy of network. 
#'
#' @examples \dontrun{
#' cw <- CytoscapeWindow('new.demo', new('graphNEL'))
#' copy_of_your_net <- copyCytoscapeNetwork(cw, "new_copy")
#' }
#'
#' @author Julia Gustavsen, \email{[email protected]@gmail.com}
#' @seealso \code{\link{createWindowFromSelection}}, \code{\link{existing.CytoscapeWindow}}, \code{\link{renameCytoscapeNetwork}}
#' 
#' @concept RCy3
#' @export
#' 
#' @importFrom methods setGeneric
setMethod('copyCytoscapeNetwork',
          'CytoscapeWindowClass', 
          function(obj,
                   new_title,
                   copy.graph.to.R = FALSE) {
            if (obj@title == new_title){
              print("Copy not made. The titles of the original window and its copy are the same. Please pick a new name for the copy.")
              stderr()
            }
            else{
              selectAllNodes(obj)
              selectAllEdges(obj)
              request.uri <- paste(obj@uri,
                                   pluginVersion(obj),
                                   "networks",
                                   obj@window.id,
                                   sep = "/")
              
              request.res <- POST(url = request.uri,
                                  query = list(title = new_title))
              
              invisible(request.res)
              
              if (copy.graph.to.R){
                connect_window <- existing.CytoscapeWindow(new_title,
                                                           copy.graph.from.cytoscape.to.R = TRUE)
                print(paste("Cytoscape window",
                            obj@title,
                            "successfully copied to",
                            connect_window@title,
                            "and the graph was copied to R."))
              } 
              else {
                connect_window <- existing.CytoscapeWindow(new_title,
                                                           copy.graph.from.cytoscape.to.R = FALSE) 
                print(paste("Cytoscape window",
                            obj@title,
                            "successfully copied to",
                            connect_window@title,
                            "and the graph was not copied to the R session."))
              }
              
              return(connect_window)
            }
            
          })

#' Rename a network 
#'
#' Renames a Cytoscape Network. 
#'
#' @param object Cytoscape network 
#' @param new_title New name for the copy
#' @param copy.graph.to.R Logical whether to copy the graph to a new object in R 
#' 
#' @return Connection to the renamed network. 
#'
#' @author Julia Gustavsen, \email{[email protected]@gmail.com}
#' @seealso \code{\link{createWindowFromSelection}}, \code{\link{existing.CytoscapeWindow}}, \code{\link{copyCytoscapeNetwork}}
#'
#' @examples \dontrun{
#' cw <- CytoscapeWindow('new.demo', new('graphNEL'))
#' renamed_net <- renameCytoscapeNetwork(cw, "renamed_network")
#' }
#' 
#' @concept RCy3
#' @export
#' 
#' @importFrom methods setGeneric
setMethod('renameCytoscapeNetwork',
          'CytoscapeWindowClass', 
          function(obj,
                   new_title,
                   copy.graph.to.R = FALSE) {
            new_net <- copyCytoscapeNetwork(obj,
                                            new_title)  
            deleteWindow(obj,
                         obj@title)
            return(new_net)
          })

# ------------------------------------------------------------------------------
setMethod('getWindowCount', 'CytoscapeConnectionClass',
	function(obj) {
		resource.uri <- paste(obj@uri, pluginVersion(obj), "networks/count", sep="/")
		res <- GET(url=resource.uri)
		num.cytoscape.windows <- unname(fromJSON(rawToChar(res$content)))
		return(as.integer(num.cytoscape.windows))
}) # END getWindowCount

# ------------------------------------------------------------------------------
setMethod('getWindowID', 'CytoscapeConnectionClass', 
	function(obj, window.title) {
		# get all window suids and associates names
		resource.uri <- paste(obj@uri, pluginVersion(obj), "networks", sep="/")
		request.res <- GET(resource.uri)
		# SUIDs list of the existing Cytoscape networks	
		cy.networks.SUIDs <- fromJSON(rawToChar(request.res$content))
		# names list of the existing Cytoscape networks
		cy.networks.names = c()
		
		for(net.SUID in cy.networks.SUIDs)	{
			 res.uri <- paste(obj@uri, pluginVersion(obj), "networks", as.character(net.SUID), sep="/")
			 result <- GET(res.uri)
			 net.name <- fromJSON(rawToChar(result$content))$data$name
			 cy.networks.names <- c(cy.networks.names, net.name)
		}

		if(!window.title %in% as.character(cy.networks.names)) {
			write(sprintf("Cytoscape window named '%s' does not exist yet", window.title), stderr())
			return (NA)
		} # if unrecognized window.title
		
		window.entry = which(as.character(cy.networks.names) == window.title)
		window.id = as.character(cy.networks.SUIDs[window.entry])
		
		return(window.id)
})

# ------------------------------------------------------------------------------
setMethod('getWindowList', 'CytoscapeConnectionClass', 
	function(obj) {
		if(getWindowCount(obj) == 0) {
			return(c())
		}
		resource.uri <- paste(obj@uri, pluginVersion(obj), "networks", sep="/")
		request.res <- GET(resource.uri)
		# SUIDs list of the existing Cytoscape networks	
		cy.networks.SUIDs <- fromJSON(rawToChar(request.res$content))
		# names list of the existing Cytoscape networks
		cy.networks.names = c()
		
		for(net.SUID in cy.networks.SUIDs)	{
			res.uri <- paste(obj@uri, pluginVersion(obj), "networks", as.character(net.SUID), sep="/")
			result <- GET(res.uri)
			net.name <- fromJSON(rawToChar(result$content))$data$name
			cy.networks.names <- c(cy.networks.names, net.name)
		}
		
		return(cy.networks.names)
})

# ------------------------------------------------------------------------------
setMethod('deleteWindow', 'CytoscapeConnectionClass',
	function (obj, window.title=NA) {
		if(!is.na(window.title))
			window.id = getWindowID(obj, window.title)
		else if(class(obj) == 'CytoscapeWindowClass')
			window.id = as.character(obj@window.id)
		else {
			write(sprintf('RCy::deleteWindow error. You must provide a valid 
										CytoscapeWindow object, or a CytoscapeConnection object and 
										a window title'), stderr())
			return()
		}
		resource.uri = paste(obj@uri, pluginVersion(obj), "networks", window.id, sep="/")
		request.res = DELETE(url=resource.uri)
		invisible(request.res)
})

#------------------------------------------------------------------------------------------------------------------------
setMethod ('deleteAllWindows',	'CytoscapeConnectionClass', function (obj) {
    # deletes all networks and associated windows in Cytoscape
    resource.uri <- paste(obj@uri, pluginVersion(obj), "networks", sep="/")
    request.res <- DELETE(resource.uri)
    invisible(request.res)
    })
#------------------------------------------------------------------------------------------------------------------------
setMethod ('getNodeShapes', 'CytoscapeConnectionClass', function (obj) {
    resource.uri <- paste(obj@uri, pluginVersion(obj), "styles/visualproperties/NODE_SHAPE/values", sep="/")
    request.res <- GET(resource.uri)
    request.res <- fromJSON(rawToChar(request.res$content))
    return(request.res$values)
    })

#------------------------------------------------------------------------------------------------------------------------
setMethod ('getDirectlyModifiableVisualProperties', 'CytoscapeConnectionClass',
    function (obj, vizmap.style.name="default") {
        resource.uri = paste(obj@uri, pluginVersion(obj), "styles", as.character(vizmap.style.name), "defaults", sep="/")
        request.res = GET(url=resource.uri)
        visual.properties <- unname(fromJSON(rawToChar(request.res$content))[[1]])
        visual.properties <- sapply(visual.properties, '[[', 1)
        return(visual.properties)
     })

#------------------------------------------------------------------------------------------------------------------------
setMethod ('getAttributeClassNames', 'CytoscapeConnectionClass',
# retrieve the names of the recognized and supported names for the class of any node or edge attribute.
	function (obj) {
		 return (c ('floating|numeric|double', 'integer|int', 'string|char|character'))
		 })

#------------------------------------------------------------------------------------------------------------------------
setMethod ('getLineStyles', 'CytoscapeConnectionClass', function (obj) {
    resource.uri <- paste(obj@uri, pluginVersion(obj), "styles/visualproperties/EDGE_LINE_TYPE/values", sep="/")
	request.res <- GET(resource.uri)
    request.res <- fromJSON(rawToChar(request.res$content))
    return(request.res$values)
    })

# ------------------------------------------------------------------------------
setMethod('getArrowShapes', 'CytoscapeConnectionClass', 
    function(obj) {
        version <- pluginVersion(obj)
        
        resource.uri <- paste(obj@uri, version, "styles/visualproperties/EDGE_TARGET_ARROW_SHAPE/values", sep="/")
        # TanjaM: EDGE_SOURCE_ARROW_SHAPE rather than TARGET returns the same results as of April 2015
        request.res <- GET(resource.uri)
        request.res <- fromJSON(rawToChar(request.res$content))
        return(request.res$values)
})
## END getArrowShapes

# ------------------------------------------------------------------------------
setMethod('getLayoutNames', 'CytoscapeConnectionClass', 
	function(obj) {
        request.uri <- paste(obj@uri, pluginVersion(obj), "apply/layouts", sep="/")
        request.res <- GET(url=request.uri)
        
        available.layouts <- unname(fromJSON(rawToChar(request.res$content)))
        return(available.layouts)
}) 
## END getLayoutNames

# ------------------------------------------------------------------------------
setMethod('getLayoutNameMapping', 'CytoscapeConnectionClass', 
    function(obj) {
        layout.names <- getLayoutNames(obj)
        layout.full.names <- c()
        
        # get the English/full name of a layout
        for (layout.name in layout.names){
            request.uri <- paste(obj@uri, pluginVersion(obj), "apply/layouts", as.character(layout.name), sep="/")
            request.res <- GET(url=request.uri)
            
            layout.property.names <- unname(fromJSON(rawToChar(request.res$content)))
            layout.full.names <- c(layout.full.names, layout.property.names[[4]])
        }
        names(layout.names) <- layout.full.names
        
        return(layout.names)
})
## END getLayoutNameMapping

# ------------------------------------------------------------------------------
setMethod('getLayoutPropertyNames', 'CytoscapeConnectionClass', 
    function(obj, layout.name) {
        request.uri <- paste(obj@uri, pluginVersion(obj), "apply/layouts", as.character(layout.name), "parameters/", sep="/")
        request.res <- GET(url=request.uri)
        
        layout.property.names <- unname(fromJSON(rawToChar(request.res$content)))
        return(sapply(layout.property.names, '[[', 1))
})
## END getLayoutPropertyNames

# ------------------------------------------------------------------------------
setMethod('getLayoutPropertyType', 'CytoscapeConnectionClass', 
    function(obj, layout.name, property.name) {
        request.uri <- paste(obj@uri, pluginVersion(obj), "apply/layouts", as.character(layout.name), "parameters/", sep="/")
        request.res <- GET(url=request.uri)
        
        layout.property.list <- unname(fromJSON(rawToChar(request.res$content)))
        layout.property.names <- sapply(layout.property.list, '[[', 1)
        position <- layout.property.names == property.name
        return(sapply(layout.property.list, '[[', 3)[position])
}) 
## END getLayoutPropertyType

#------------------------------------------------------------------------------------------------------------------------
setMethod ('getLayoutPropertyValue', 'CytoscapeConnectionClass', 

   function (obj, layout.name, property.name) {
       request.uri <- paste(obj@uri, pluginVersion(obj), "apply/layouts", as.character(layout.name), "parameters/", sep="/")
       request.res <- GET(url=request.uri)
       
       layout.property.list <- unname(fromJSON(rawToChar(request.res$content)))
       layout.property.names <- sapply(layout.property.list, '[[', 1)
       position <- layout.property.names == property.name
       return(sapply(layout.property.list, '[[', 4)[position])
     }) # getLayoutPropertyValue

#------------------------------------------------------------------------------------------------------------------------
#' Gets commands available from within cytoscape from 
#' functions within cytoscape and from installed plugins.
#'
#' @param obj Cytoscape network where commands are fetched via RCy3 
#' @return Vector of available commands from all namespaces (e.g. functions and plugins) 
#'
#' @concept RCy3
#' @export
#' 
#' @importFrom methods setGeneric
setMethod('getCommandNames','CytoscapeConnectionClass',
          function(obj) { 
            request.uri <- paste(obj@uri,
                                 pluginVersion(obj),
                                 "commands",
                                 sep="/")
            request.res <- GET(url=request.uri)
            
            available.commands <- unlist(strsplit(rawToChar(request.res$content),
                                                  split="\n\\s*"))
            ## to remove "Available namespaces" title
            ## remove the first value
            available.commands <- available.commands[-1]
            return(available.commands) 
          })
# END getCommandNames

#' Gets commands available from within a namespace in Cytoscape from 
#' functions within cytoscape and from installed plugins.
#'
#' @param obj Cytoscape network where commands are fetched via RCy3 
#' @param namespace Cytoscape function (e.g. layout or network settings) or Cytoscape plugin function
#' 
#' @return Vector of available commands from a specific plugin or Cytoscape function (e.g. namespace)
#'
#' @concept RCy3
#' @export
#' 
setMethod('getCommandsWithinNamespace','CytoscapeConnectionClass',
          function(obj,
                   namespace) { 
            request.uri <- paste(obj@uri,
                                 pluginVersion(obj),
                                 "commands",
                                 namespace,
                                 sep = "/")
            request.res <- GET(url = request.uri)
            
            available.commands <- unlist(strsplit(rawToChar(request.res$content),
                                                  split = "\n\\s*"))
            ## remove "Available commands" title
            available.commands <- available.commands[-1]
            return(available.commands) })

# END getCommandsWithinNamespace

#' Runs a Cytoscape command (for example from a plugin) with a list of parameters and creates a connection to the network (if a new one is created) so that it can be further manipulated from R. 
#'
#' @param obj Cytoscape network where command is run via RCy3 
#' @param command.name Need more info here - how to specify..
#' @param properties.list Parameters (e.g. files, p-values, etc) to be used to set to run the command
#' @param copy.graph.to.R If true this copies the graph information to R. This step can be quite slow. Default is false. 
#' 
#' @return Runs in Cytoscape and creates a connection to the Cytoscape window so that it can be further manipulated from R 
#' 
#' @examples \dontrun{
#' cw <- CytoscapeWindow('new.demo', new('graphNEL'))
#' selectAllNodes(cw)
#' }
#'
#' @concept RCy3
#' @export
#' 
#' @importFrom methods setGeneric
setMethod('setCommandProperties','CytoscapeConnectionClass', 
          function(obj,
                   command.name,
                   properties.list, 
                   copy.graph.to.R = FALSE) {
            all.possible.props <- getCommandsWithinNamespace(obj,
                                                             command.name)
            if (all(names(properties.list) %in% all.possible.props) == FALSE) {
              print('You have included a name which is not in the commands')
              stderr ()
            } else {
              request.uri <- paste(obj@uri,
                                   pluginVersion(obj),
                                   "commands",
                                   as.character(command.name),
                                   sep = "/")
              
              request.res <- GET(url = request.uri,
                                 query = properties.list)
              if (request.res$status == 200){
                print("Successfully built the EnrichmentMap.")
                stdout ()
                resource.uri <- paste(obj@uri,
                                      pluginVersion(obj),
                                      "networks",
                                      sep = "/")
                request.res <- GET(resource.uri)
                # SUIDs list of the existing Cytoscape networks	
                cy.networks.SUIDs <- fromJSON(rawToChar(request.res$content))
                # most recently made enrichment map will have the highest SUID
                cy.networks.SUIDs.last <- max(cy.networks.SUIDs)
                
                res.uri.last <- paste(obj@uri,
                                      pluginVersion(obj),
                                      "networks",
                                      as.character(cy.networks.SUIDs.last),
                                      sep = "/")
                result <- GET(res.uri.last)
                net.name <- fromJSON(rawToChar(result$content))$data$name
                
                if (copy.graph.to.R){
                  connect_window_to_R_session <- existing.CytoscapeWindow(net.name,
                                                                          copy.graph.from.cytoscape.to.R = TRUE)
                  print(paste0("Cytoscape window",
                               net.name,
                               " successfully connected to R session and graph copied to R."))
                } 
                else {
                  connect_window_to_R_session <- existing.CytoscapeWindow(net.name,
                                                                          copy.graph.from.cytoscape.to.R = FALSE) 
                  print(paste0("Cytoscape window ",
                               net.name,
                               " successfully connected to R session."))
                }
                
                
              } else {
                print("Something went wrong. Unable to run command.")
                stderr ()
              }
              invisible(request.res)
            }
            return(connect_window_to_R_session)
          }) 

# END setCommandProperties

setMethod ('setLayoutProperties', 'CytoscapeConnectionClass', 

    function (obj, layout.name, properties.list) {
        all.possible.props <- getLayoutPropertyNames (obj, layout.name)
        
        # set properties iteratively, this could have been done with a single API call
        for (prop in names (properties.list)) {
            if (!prop %in% all.possible.props) {
                write (sprintf ('%s is not a property in layout %s', prop, layout.name), stderr ())
            } else {
                new.value <- properties.list [[prop]]
                new.property.value.list <- list("name"=prop, "value"=new.value)
                new.property.value.list.JSON <- toJSON(list(new.property.value.list))
                
                request.uri <- paste(obj@uri, pluginVersion(obj), "apply/layouts", as.character(layout.name), "parameters/", sep="/")
                request.res <- PUT(url=request.uri, body= new.property.value.list.JSON, encode="json")
                if (request.res$status == 200){
                    write (sprintf ("Successfully updated the property '%s'.", prop), stdout ())
                } else {
                    write (sprintf ("Something went wrong. Unable to update property '%s'.", prop), stderr ())
                }
                invisible(request.res)
            }
        } # for prop
     }) # setLayoutProperties

# ------------------------------------------------------------------------------
setMethod ('setGraph', 'CytoscapeWindowClass', function(obj, graph) {
    # copy the graph over
    loc.obj <- obj
    if (edgemode(graph) == 'undirected'){
        graph = remove.redundancies.in.undirected.graph (graph)
    }
    
    loc.obj@graph = graph
    
    eval.parent(substitute(obj <- loc.obj))
})

# ------------------------------------------------------------------------------
setMethod('getGraph', 'CytoscapeWindowClass', 
  function(obj) {
    return(obj@graph)
})

#------------------------------------------------------------------------------------------------------------------------
# in Cytoscape, node attributes are administered on a global level.  In addition, and in contrast to R, not all nodes in a graph
# will have a specific attribute define on it.  (In R, every node has every attribute)
# this function returns a list of nodes for which the specified attribute has a value in the corresponding Cytoscape network

setMethod ('haveNodeAttribute', 'CytoscapeConnectionClass',
    function(obj, node.names, attribute.name) {
    
        net.SUID = as.character(obj@window.id)
        version = pluginVersion(obj)
        # check the attribute exists
        if (attribute.name %in% getNodeAttributeNames(obj)) {
        # get the node SUIDs
            node.SUIDs = .nodeNameToNodeSUID(obj, node.names)
            nodes.that.have.attribute = c()
            
            for (i in 1:length(node.SUIDs)) {
                resource.uri = paste(obj@uri, version, "networks", net.SUID, "tables/defaultnode/rows", as.character(node.SUIDs[i]), attribute.name, sep="/")
                request.res = GET(url=resource.uri)
                node.attribute.value = rawToChar(request.res$content)
                
                if(nchar(node.attribute.value) > 0) {
                    nodes.that.have.attribute = c(nodes.that.have.attribute, node.SUIDs[i])
                }
            }
            
            return (as.character(.nodeSUIDToNodeName(obj, nodes.that.have.attribute)))
            } else {
                write(sprintf("Error: '%s' is not an existing node attribute name", attribute.name), stderr())
        }
    })
## END haveNodeAttribute

#------------------------------------------------------------------------------------------------------------------------
# in Cytoscape, attributes are administered on a global level.  In addition, and in contrast to R, not all nodes in a graph
# will have a specific attribute define on it.  (In R, every node has every attribute)
# this function returns a list of nodes for which the specified attribute has a value in the corresponding Cytoscape network

setMethod ('haveEdgeAttribute', 'CytoscapeConnectionClass',

    function (obj, edge.names, attribute.name) {
        net.SUID = as.character(obj@window.id)
        version = pluginVersion(obj)
        
        if(attribute.name %in% getEdgeAttributeNames(obj)) {
            edge.SUIDs = .edgeNameToEdgeSUID(obj, edge.names)
            edges.that.have.attribute = c()
            
            for(i in 1:length(edge.SUIDs)) {
                resource.uri = paste(obj@uri, version, "networks", net.SUID, "tables/defaultedge/rows", as.character(edge.SUIDs[i]), attribute.name, sep="/")
                request.res = GET(url=resource.uri)
                edge.attribute.value = rawToChar(request.res$content)
                
                if(nchar(edge.attribute.value) > 0) {
                    edges.that.have.attribute = c(edges.that.have.attribute, edge.SUIDs[i])
                }
            }
            
            return(as.character(.edgeSUIDToEdgeName(obj, edges.that.have.attribute)))
        } else {
            write(sprintf("Error: '%s' is no an existing edge attribute name", attribute.name), stderr())
        }
    })

#------------------------------------------------------------------------------------------------------------------------
setMethod ('copyNodeAttributesFromCyGraph', 'CytoscapeConnectionClass',

    function (obj, window.id, existing.graph) {
        net.SUID = as.character(obj@window.id)
        version = pluginVersion(obj)
        
        node.attribute.names = getNodeAttributeNames(obj)
        
        for(attribute.name in node.attribute.names) {
            known.node.names = sapply(obj@suid.name.dict, function(n) { n$name })
            # nodes that store values for this attribute (meaning the value is not empty)
            nodes.with.attribute = haveNodeAttribute(obj, known.node.names, attribute.name)
            if(length(nodes.with.attribute) > 0) {
                attribute.type = getNodeAttributeType(obj, attribute.name)
                
                write(sprintf("\t retrieving attribute '%s' values for %d nodes", attribute.name, length(nodes.with.attribute)), stderr())
                # write(sprintf("\t retrieving %s '%s' attribute for %d nodes", attribute.type, attribute.name, length(nodes.with.attribute)), stderr())
                if(attribute.type == 'Integer') {
                    attribute.type = 'integer'
                    default.value = as.integer(0)
                } else if(attribute.type == 'String') {
                    attribute.type = 'char'
                    default.value = as.character('unassigned')
                } else if(attribute.type == 'Double') {
                    attribute.type = 'numeric'
                    default.value = as.numeric(0.0)
                } else if(attribute.type == 'Boolean') {
                    attribute.value = 'boolean'
                    default.value = as.logical(FALSE)
                } else {
                    write(sprintf('RCy3::copyNodeAttributesFromCyGraph, no support yet for attributes of type %s', attribute.type), stderr())
                    next()
                }
                existing.graph = 
                    initNodeAttribute(existing.graph, attribute.name, attribute.type, default.value)
                
                attribute.values = c()
                
                for(i in 1:length(nodes.with.attribute)) {
                    attribute.values = c(attribute.values, getNodeAttribute(obj, nodes.with.attribute[i], attribute.name))
                }
                nodeData(existing.graph, nodes.with.attribute, attribute.name) = attribute.values
            } ## END if there are nodes that have values for the attribute
        } ## END for loop : looping through each node attribute
        return(existing.graph)
    })
## END copyNodeAttributesFromCyGraph

#------------------------------------------------------------------------------------------------------------------------
setMethod ('copyEdgeAttributesFromCyGraph', 'CytoscapeConnectionClass',

    function (obj, window.id, existing.graph) {
        net.SUID = as.character(obj@window.id)
        version = pluginVersion(obj)
        
        edge.attribute.names = getEdgeAttributeNames(obj)
        
        cy2.edgenames = as.character(cy2.edge.names(existing.graph)) # < 2 seconds for > 9000 edges
        
        for(attribute.name in edge.attribute.names) {
            edges.with.attribute = haveEdgeAttribute(obj, cy2.edgenames, attribute.name)
            
            if(length(edges.with.attribute) > 0) {
                attribute.type = getEdgeAttributeType(obj, attribute.name) 
                
                write(sprintf("\t retrieving attribute '%s' values for %d edges", attribute.name, length(edges.with.attribute)), stderr())
                if(attribute.type == 'Integer') {
                    attribute.type = 'integer' 
                    default.value = 0
                } else if(attribute.type == 'String') {
                    attribute.type = 'char'
                    default.value = 'unassigned'
                } else if(attribute.type == 'Double') {
                    attribute.type = 'numeric' 
                    default.value = as.numeric(0.0)
                } else {
                    write(sprintf('RCy3::copyEdgeAttributesFromCyGraph, no support yet for attributes of type %s', attribute.type), stderr())
                    next()
                }
                existing.graph = 
                    initEdgeAttribute(existing.graph, attribute.name, attribute.type, default.value)
                eda.values = c()
                
                for(i in 1:length(edges.with.attribute)) {
                    eda.values = c(eda.values, getEdgeAttribute(obj, edges.with.attribute[i], attribute.name))
                }
                
                regex = ' *[\\(|\\)] *'
                edges.tokens = strsplit(edges.with.attribute, regex)
                source.nodes = unlist(lapply(edges.tokens, function(tokens) tokens[1]))
                target.nodes = unlist(lapply(edges.tokens, function(tokens) tokens[3]))
                edge.types = unlist(lapply(edges.tokens, function(tokens) tokens[2])) 
                
                edgeData(existing.graph, source.nodes, target.nodes, attribute.name) = eda.values
                
                # for(i in 1:length(edgeData(existing.graph, from=source.nodes, to=target.nodes, attr=attribute.name))) {
                #     attr(edgeData(existing.graph, from=source.nodes, to=target.nodes, attr=attribute.name)[[i]], 'class') = 
                #         getEdgeAttributeType(obj, attribute.name)
                # }
            } ## END if
        } ## END for
        
        return(existing.graph)
    }) # END copyEdgeAttributesFromCyGraph

#------------------------------------------------------------------------------------------------------------------------
setMethod ('getGraphFromCyWindow', 'CytoscapeConnectionClass',

    function (obj, window.title) {
        window.id = NULL
        # handles the case when 'obj' is 'CytoscapeConnectionClass', instead of 'CytoscapeWindowClass' 
        if (class(obj) == "CytoscapeConnectionClass") {
            window.id = as.character(getWindowID(obj, window.title))
            loc.obj = 
                new('CytoscapeWindowClass', title=window.title, window.id=window.id, uri=obj@uri)
        } else {
            loc.obj = obj
        }
        # network id and cyREST plugin version
        net.SUID = as.character(loc.obj@window.id)
        version = pluginVersion(loc.obj)
        
        if (!is.na(net.SUID)) {
            # get the graph from Cytoscape
            resource.uri = paste(loc.obj@uri, version, "networks", net.SUID, sep="/")
            request.res = GET(url=resource.uri)
            request.res = fromJSON(rawToChar(request.res$content))
            
            g = new("graphNEL", edgemode='directed') # create graph object
            
            # GET GRAPH NODES
            g.nodes = request.res$elements$nodes
            # if there are no nodes in the graph received from Cytoscape, return an empty 'graphNEL' object
            if(length(g.nodes) == 0) {
                write(sprintf("NOTICE in RCy3::getGraphFromCyWindow():\n\t returning an empty 'graphNEL'"), stderr())
                return(g)
            }
            
            # else get the node names and add them to the R graph
            loc.obj@suid.name.dict = lapply(g.nodes, function(n) { 
            list(name=n$data$name, SUID=n$data$SUID) })
            g.node.names = sapply(loc.obj@suid.name.dict, function(n) { n$name })
            write(sprintf("\t received %d NODES from '%s'", length(g.nodes), window.title), stderr())
            g = graph::addNode(g.node.names, g)
            write(sprintf("\t - added %d nodes to the returned graph\n", length(g.node.names)), stderr())
            
            # GET NODE ATTRIBUTES (if any)
            g = copyNodeAttributesFromCyGraph(loc.obj, net.SUID, g)
            
            # Bioconductor's 'graph' edges require the 'edgeType' attribute, so its default value is assigned
            g = initEdgeAttribute (g, 'edgeType', 'char', 'assoc')
            
            # GET GRAPH EDGES
            g.edges = request.res$elements$edges
            
            if (length(g.edges) > 0) {
                regex = ' *[\\(|\\)] *'
                write(sprintf("\n\t received %d EDGES from '%s'", length(g.edges), window.title), stderr())
                
                loc.obj@edge.suid.name.dict = lapply(g.edges, function(e) {
                    list(name=e$data$name, SUID=e$data$SUID) })
                g.edge.names = sapply(loc.obj@edge.suid.name.dict, function(e) { e$name })
                edges.tokens = strsplit(g.edge.names, regex)
                source.nodes = unlist(lapply(edges.tokens, function(tokens) tokens[1]))
                target.nodes = unlist(lapply(edges.tokens, function(tokens) tokens[3]))
                edge.types = unlist(lapply(edges.tokens, function(tokens) tokens[2]))
                write(sprintf('\t - adding %d edges to the returned graph\n', length(edges.tokens)), stderr())
               
                tryCatch({
                    g = addEdge(source.nodes, target.nodes, g)
                    edgeData(g, source.nodes, target.nodes, 'edgeType') = edge.types
                    
                    # GET EDGE ATTRIBUTES (if any)
                    g = copyEdgeAttributesFromCyGraph(loc.obj, window.id, g)
                },
                error = function(cond){
                    write(sprintf("ERROR in RCy3::getGraphFromCyWindow(): Node names cannot contain parentheses.", window.title), stderr())
                    return(NA)
                })
                

            }
          
        } else {
            write(sprintf("ERROR in RCy3::getGraphFromCyWindow():\n\t there is no graph with name '%s' in Cytoscape", window.title), stderr())
            return(NA)
        }
        
        return(g)
  })
## END getGraphFromCyWindow

#' Creates a connection to the newest Cytoscape window so that it can be further manipulated from R.
#'
#' @param obj Cytoscape network where command is run via RCy3 
#' @param copyToR If true this copies the graph information to R. This step can be quite slow. Default is false. 
#' 
#' @return Creates a connection to the newest Cytoscape window so that it can be further manipulated from R 
#' 
#' @concept RCy3
#' @export
#' 
#' @importFrom methods setGeneric
setMethod('connectToNewestCyWindow',
          'CytoscapeConnectionClass',
          function(obj,
                                    copyToR = FALSE) {
  resource.uri <- paste(obj@uri,
                        pluginVersion(obj),
                        "networks",
                        sep = "/")
  request.res <- GET(resource.uri)
  # SUIDs list of the existing Cytoscape networks
  cy.networks.SUIDs <- fromJSON(rawToChar(request.res$content))
  # most recently made enrichment map will have the highest SUID
  cy.networks.SUIDs.last <- max(cy.networks.SUIDs)
  
  res.uri.last <- paste(obj@uri,
                        pluginVersion(obj),
                        "networks",
                        as.character(cy.networks.SUIDs.last),
                        sep = "/")
  result <- GET(res.uri.last)
  net.name <- fromJSON(rawToChar(result$content))$data$name
  
  ## to get edges request.res$elements$edges
  newest_CyWindow <- existing.CytoscapeWindow(net.name,
                                              copy.graph.from.cytoscape.to.R = copyToR) 
  return(newest_CyWindow)
})


# ------------------------------------------------------------------------------
setMethod('sendNodes', 'CytoscapeWindowClass', function(obj) {
    loc.obj <- obj
    # returns the nodes currently stored in the graph object
    graph.network.nodes = nodes(loc.obj@graph)
    # returns the nodes currently displayed in Cytoscape
    current.cytoscape.nodes = sapply(loc.obj@suid.name.dict, function(n) n$name)
    
    node.suid.name.dict <- (0)
    
    diff.nodes = setdiff(graph.network.nodes, current.cytoscape.nodes)
    # if new nodes need to be added
    if(length(diff.nodes) > 0) {
        net.SUID = as.character(loc.obj@window.id)
        version = pluginVersion(loc.obj)
        
        resource.uri = paste(loc.obj@uri, version, "networks", net.SUID, "nodes", sep="/")
        diff.nodes.JSON = toJSON(diff.nodes)
        
        write(sprintf('sending %d node(s)', length(diff.nodes)), stderr())
        
        request.res = POST(url=resource.uri, body=diff.nodes.JSON, encode="json")
        new.node.SUIDs = unname(fromJSON(rawToChar(request.res$content)))
        
        for(i in 1:length(new.node.SUIDs)) {
            loc.obj@suid.name.dict[[length(loc.obj@suid.name.dict)+1]] = new.node.SUIDs[[i]]
        }
    } else {
        write('CytoscapeWindow.sendNodes(), no new nodes to send, returning', stderr())
        return()
    }
    
    write('sendNodes completed', stderr())
    # needed for 'pass-by-reference' R functionality 
    eval.parent(substitute(obj <- loc.obj))
    })

#------------------------------------------------------------------------------------------------------------------------
setMethod ('.addNodes', signature (obj='CytoscapeWindowClass'),

    function (obj, other.graph) {
        loc.obj <- obj
        if(length(nodes(other.graph)) == 0) {
            write("NOTICE in RCy3::.addNodes():\n\t no nodes in other.graph >> function returns", stderr())
            return()
        }
        # new.nodes = setdiff(nodes(other.graph), getAllNodes(loc.obj))
        new.node.indices = which(!nodes(other.graph) %in% getAllNodes(loc.obj))
        
        new.nodes = nodes(other.graph)[new.node.indices]
        
        if(length(new.node.indices) > 0) {
            net.SUID = as.character(loc.obj@window.id)
            version = pluginVersion(loc.obj)
            
            resource.uri = paste(loc.obj@uri, version, "networks", net.SUID, "nodes", sep="/")
            new.nodes.JSON = toJSON(new.nodes)
            
            request.res = POST(url=resource.uri, body=new.nodes.JSON, encode="json")
            new.node.SUIDs = unname(fromJSON(rawToChar(request.res$content)))
            
            for(i in 1:length(new.node.SUIDs)) {
                loc.obj@suid.name.dict[[length(loc.obj@suid.name.dict)+1]] = new.node.SUIDs[[i]]
            }
        } else {
            write(sprintf("NOTICE in RCy3::.addNodes():\n\t all %d nodes already exist in Cytoscape - nothing new to add >> function returns", length(nodes(other.graph))), stderr())
            return()
        }
        # needed for 'pass-by-reference' R functionality
        eval.parent(substitute(obj <- loc.obj))
        
        return(new.node.indices)
    }) # END .addNodes
#------------------------------------------------------------------------------------------------------------------------
setMethod ('.addEdges', signature (obj='CytoscapeWindowClass'),

    function (obj, other.graph) {
        loc.obj <- obj
        net.SUID = as.character(loc.obj@window.id)
        version = pluginVersion(loc.obj)
        
        if(length(edgeNames(other.graph)) == 0) {
            write("NOTICE in RCy3::.addEdges():\n\t no edges in graph >> function returns", stderr())
            return()
        }
        
        if(is.classic.graph(other.graph)) {
            tbl.edges = .classicGraphToNodePairTable(other.graph)
        } else if(is.multiGraph(other.graph)) {
            tbl.edges = .multiGraphToNodePairTable(other.graph)
        }
        # get the 'other.graph' edge names
        other.graph.edge.names = unname(cy2.edge.names(other.graph))
        
        cytoscape.existing.edge.names = 
            sapply(loc.obj@edge.suid.name.dict, function(e) {return(e$name)})
        new.edge.indices = which(!other.graph.edge.names %in% cytoscape.existing.edge.names)
        
        if(length(new.edge.indices) > 0) {
            # source nodes vector
            source.nodes = tbl.edges$source[new.edge.indices]
            # target nodes vector
            target.nodes = tbl.edges$target[new.edge.indices]
            # edge types vector
            edge.type = tbl.edges$edgeType[new.edge.indices]
            directed = rep(TRUE, length(source.nodes))
            
            # convert the [node.SUID, node.name] dict(list) to data frame object
            suid.name.dict.df = 
                data.frame(matrix(unlist(loc.obj@suid.name.dict), nrow=length(loc.obj@suid.name.dict), byrow=TRUE), stringsAsFactors=FALSE)
            colnames(suid.name.dict.df) <- c("name", "SUID")
            
            # get the SUIDs of the source nodes for the new edges
            source.node.SUIDs = .nodeNameToNodeSUID(loc.obj, source.nodes)
            # get the SUIDs of the target nodes for the new edges
            target.node.SUIDs = .nodeNameToNodeSUID(loc.obj, target.nodes)
            
            # format the new edges data for sending to Cytoscape
            edge.tbl.records = 
                apply(cbind(source.node.SUIDs, target.node.SUIDs, directed, edge.type), MARGIN=1,
                      FUN=function(r) {list(source=unname(r[[1]]), target=unname(r[[2]]), directed=unname(r[[3]]), interaction=unname(r[[4]]))})
            edge.tbl.records.JSON = toJSON(edge.tbl.records)
            resource.uri = paste(loc.obj@uri, pluginVersion(loc.obj), "networks", net.SUID, "edges", sep="/")
            request.res = POST(url=resource.uri, body=edge.tbl.records.JSON, encode="json")
            
            # request.res.edge.SUIDs contains 
            # [edge.SUID, source.node.SUID, targetn.node.SUID] for each edge
            request.res.edge.data = fromJSON(rawToChar(request.res$content))
            
            new.edge.names = cy2.edge.names(other.graph)[new.edge.indices]
            # ctreates matrix of the format : 
            # note: column 1 contains edge.SUIDs, and columns 3 & 4 contain node.SUIDs
            #      [,1]   [,2]                     [,3]   [,4]
            # [1,] "412"  "A (phosphorylates) B"   "413"  "404"
            # [2,] "406"  "B (synthetic lethal C"  "407"  "408"
            # [3,] "407"  "C (undefined) A"        "408"  "406"
            edge.names.tbl.records = 
                apply(unname(cbind(unname(t(sapply(request.res.edge.data, unlist))), new.edge.names)), 
                      MARGIN=1, 
                      FUN=function(r) {list(SUID=as.numeric(unname(r[[1]])), value=unname(r[[4]]), 
                                            source.node=as.numeric(unname(r[[2]])), 
                                            target.node=as.numeric(unname(r[[3]])))})
            # CREATES DICT ENTRIES for the new edges in the following format :
            # [edge.SUID, edge.name, source.node.SUID, target.node.SUID]
            for(i in 1:length(edge.names.tbl.records)) {
                loc.obj@edge.suid.name.dict[[length(loc.obj@edge.suid.name.dict)+1]] = 
                    list(SUID=edge.names.tbl.records[[i]]$SUID, name=edge.names.tbl.records[[i]]$value, 
                         source.node=edge.names.tbl.records[[i]]$source.node, 
                         target.node=edge.names.tbl.records[[i]]$target.node)
            }
            
            # invisible(request.res)
        } else {
            write(sprintf("NOTICE in RCy3::.addEdges():\n\t all %d edges already exists in Cytoscape - nothing new to add >> function returns", length(other.graph.edge.names)), stderr())
            return()
        }
        
        eval.parent(substitute(obj <- loc.obj))
        
        return(new.edge.indices)
    }) # END .addEdges

# ------------------------------------------------------------------------------
# helper function: returns the name of the Cytoscape window based on its SUID
setMethod('.getWindowNameFromSUID', 'CytoscapeConnectionClass', 
    function(obj, win.suid) {
        suid <- as.character(win.suid)
        resource.uri <- paste(obj@uri, pluginVersion(obj), "networks", suid, sep="/")
        request.res <- GET(url=resource.uri)
        win.name <- fromJSON(rawToChar(request.res$content))$data$name
        return(win.name)
}) 
## END .getWindowNameFromSUID

# ------------------------------------------------------------------------------
# helper function: returns the SUIDs of all views belonging to specific network
setMethod('.getNetworkViews', 'CytoscapeConnectionClass', 
    function(obj) {
        net.SUID <- as.character(obj@window.id)
        
        resource.uri <- paste(obj@uri, pluginVersion(obj), "networks", net.SUID, "views", sep="/")
        request.res <- GET(url=resource.uri)
        network.view.SUIDs <- unname(fromJSON(rawToChar(request.res$content)))
        return(network.view.SUIDs)
}) 
## END .getNetworkViews

# ------------------------------------------------------------------------------
setMethod('.nodeNameToNodeSUID', 'CytoscapeConnectionClass', 
    function(obj, node.names) {
        # initial source used 'which', but it did not return SUIDs in the input names order  
        # dict.indices = which(node.names %in% sapply([email protected], function(n) { n$name}))
        # 'match' achieves this desired behavior
        dict.node.names <- sapply(obj@suid.name.dict, function(n) {n$name})
        dict.indices <- match(node.names, dict.node.names)
        
        # [dict.indices[!is.na(dict.indices)]] is used to clean any 'NAs' from the vector 
        node.SUIDs <- sapply(obj@suid.name.dict[dict.indices[!is.na(dict.indices)]], function(i) {i$SUID})
        return(node.SUIDs)
}) 
## END .nodeNamesToNodeSUID

# ------------------------------------------------------------------------------
setMethod('.nodeSUIDToNodeName', 'CytoscapeConnectionClass', 
    function(obj, node.suids) {
        dict.node.SUIDs <- sapply(obj@suid.name.dict, function(s) {s$SUID})
        dict.indices <- match(node.suids, dict.node.SUIDs)
        
        # [dict.indices[!is.na(dict.indices)]] is used to clean any 'NAs' from the vector
        node.names <- sapply(obj@suid.name.dict[dict.indices[!is.na(dict.indices)]], function(n) {n$name})
        return(node.names)
}) 
## END .nodeSUIDToNodeName

# ------------------------------------------------------------------------------
setMethod('.edgeNameToEdgeSUID', 'CytoscapeConnectionClass', 
    function(obj, edge.names) {
        dict.edge.names <- sapply(obj@edge.suid.name.dict, function(e) {e$name})
        dict.indices <- match(edge.names, dict.edge.names)
        
        # [dict.indices[!is.na(dict.indices)]] is used to clean any 'NAs' from the vector
        edge.SUIDs <- sapply(obj@edge.suid.name.dict[dict.indices[!is.na(dict.indices)]], function(i){ i$SUID })
        return(edge.SUIDs)
}) 
## END .edgeNamesToEdgeSUID

# ------------------------------------------------------------------------------
setMethod('.edgeSUIDToEdgeName', 'CytoscapeConnectionClass', 
    function(obj, edge.suids) {
        dict.edge.SUIDs = sapply(obj@edge.suid.name.dict, function(s) {s$SUID})
        dict.indices = match(edge.suids, dict.edge.SUIDs)
        
        # [dict.indices[!is.na(dict.indices)]] is used to clean any 'NAs' from the vector
        edge.names = sapply(obj@edge.suid.name.dict[dict.indices[!is.na(dict.indices)]], function(e) {e$name})
        return(edge.names)
}) 
## END .edgeSUIDToEdgeName

# ------------------------------------------------------------------------------
setMethod('addCyNode', 'CytoscapeWindowClass', function(obj, nodeName) {
    loc.obj <- obj

    if(nodeName %in% getAllNodes(loc.obj)) {
        write(sprintf('RCy3::addCyNode, node "%s" already present in Cytoscape graph', nodeName), stderr())
        return()
    }
    
    # get the network suid
    net.suid <- as.character(loc.obj@window.id)
    resource.uri <- paste(loc.obj@uri, pluginVersion(loc.obj), "networks", net.suid, "nodes", sep="/")
    nodename.json = toJSON(c(nodeName))
    
    # add the node to the Cytoscape graph
    new.cynode.res <- POST(url=resource.uri, body=nodename.json, encode="json")
    new.cynode.suid.name <- unname(fromJSON(rawToChar(new.cynode.res$content)))
    
    # add the new node to the [email protected]
    loc.obj@suid.name.dict[[length(loc.obj@suid.name.dict)+1]] <- 
        list(name=new.cynode.suid.name[[1]]$name, SUID=new.cynode.suid.name[[1]]$SUID)
    
    # add the node to the R graph object
    loc.obj@graph <- addNode(nodeName, loc.obj@graph)

    eval.parent(substitute(obj <- loc.obj))
}) # addCyNode

# ------------------------------------------------------------------------------
setMethod('addCyEdge', 'CytoscapeWindowClass', 
  function (obj, sourceNode, targetNode, edgeType, directed) {
    loc.obj <- obj
    
    good.args = TRUE
    # confirm that the user has provided exactly one source and one target nodes
    if(length(sourceNode) > 1 || length(targetNode) > 1) {
      good.args = FALSE
      write(sprintf('RCy3::addEdge can have only one source and one target nodes'), stderr())
    }
    
    if(!sourceNode %in% getAllNodes(loc.obj)) {
      good.args = FALSE
      write(sprintf('RCy3::addEdge. Error: source node %s does not exist in the Cytoscape graph. Edge cannot be created.', sourceNode), stderr())
    }
    if(!targetNode %in% getAllNodes(loc.obj)) {
      good.args = FALSE
      write(sprintf('RCy3::addEdge. Error: source node %s does not exist in the Cytoscape graph. Edge cannot be created.', targetNode), stderr())
    }
    if(!good.args) {
      return()
    }
    
    net.suid <- as.character(loc.obj@window.id)
    resource.uri <- paste(loc.obj@uri, pluginVersion(loc.obj), "networks", net.suid, "edges", sep="/")
    
    node.names.vec <- sapply(loc.obj@suid.name.dict, "[[", 1)
    edge.data <- list(source = loc.obj@suid.name.dict[[which(node.names.vec %in% sourceNode)]]$SUID, 
                      target = loc.obj@suid.name.dict[[which(node.names.vec %in% targetNode)]]$SUID, 
                      directed = directed, interaction = edgeType)
    
    edge.data.JSON <- toJSON(list(edge.data))
    
    new.cyedge.res <- POST(url=resource.uri, body=edge.data.JSON, encode='json')
    invisible(new.cyedge.res)
    
    # add the edge to the R graph object
    loc.obj@graph <- addEdge(sourceNode, targetNode, loc.obj@graph)
    
    eval.parent(substitute(obj <- loc.obj))
}) # addCyEdge

#------------------------------------------------------------------------------------------------------------------------
# This method adds a new graph to an existing graph.
# First the new nodes, then the new edges, then node attributes, then edge attributes
setMethod ('addGraphToGraph', 'CytoscapeWindowClass',

    function (obj, other.graph) {
        loc.obj <- obj
        # RCy3 keeps a dictionary of the network nodes 
        # the below vector stores the indices of the newly added nodes in this dictionary
        new.node.indices = .addNodes(loc.obj, other.graph)
        
        # RCy3 keeps a dictionary of the network edges
        # the below vector stores the indices of the newly added edges to this dictionary
        new.edge.indices = .addEdges(loc.obj, other.graph)
        
        node.attribute.names = noa.names(other.graph)
        
        for (attribute.name in node.attribute.names) {
            printf('sending noa %s', attribute.name)
            .sendNodeAttributesForGraph(loc.obj, other.graph, attribute.name, new.node.indices)
        }
        
        edge.attribute.names = eda.names(other.graph)
        for (attribute.name in edge.attribute.names) {
            printf('sending eda %s', attribute.name)
            .sendEdgeAttributesForGraph(loc.obj, other.graph, attribute.name, new.edge.indices)
        }
        
        # needed for 'pass-by-reference' R functionality
        eval.parent(substitute(obj <- loc.obj))
    }) # END addGraphToGraph

#------------------------------------------------------------------------------------------------------------------------
setMethod('sendEdges', 'CytoscapeWindowClass',
  function(obj) {
      loc.obj <- obj
      net.SUID = as.character(loc.obj@window.id)
      version = pluginVersion(loc.obj)
      # check that there are edges in the graph
      if(length(edgeNames(loc.obj@graph)) == 0) {
          write('NOTICE in RCy3::sendEdges():\n\t no edges in graph >> function returns', stderr())
          return()
      }
      
      write(sprintf('transforming (%d) graph edges to nodePairTable', length(edgeNames(loc.obj@graph))), stderr())
      if(loc.obj@collectTimings) {
          start.time = Sys.time()
      }
      
      if(is.classic.graph(loc.obj@graph)) {
          tbl.edges = .classicGraphToNodePairTable(loc.obj@graph)
      }
      else if(is.multiGraph(loc.obj@graph)) {
          tbl.edges = .multiGraphToNodePairTable(loc.obj@graph)
      }
      
      if (loc.obj@collectTimings){
          write (sprintf(' *** create node pair table: %f secs', difftime (Sys.time(), start.time, units='secs')), stderr ())
      }
      
      # get the list of edges to be send to Cytoscape
      in.graph.edge.names = unname(cy2.edge.names(loc.obj@graph))
      # get the list of currently existing esges (from dict)
      existing.edge.names = 
          sapply(loc.obj@edge.suid.name.dict, function(n) {return(n$name)})
      
      diff.edges = setdiff(in.graph.edge.names, existing.edge.names)
      # in new edges need to be send to the network
      if(length(diff.edges) > 0) {
          write (sprintf('sending %d edges', nrow(tbl.edges)), stderr())
          # source nodes vector
          source.nodes = tbl.edges$source
          # target nodes vector
          target.nodes = tbl.edges$target
          # edge types vector
          edge.type = tbl.edges$edgeType
          directed = rep(TRUE, length(source.nodes))
          
          # convert the [node.SUID, node.name] dict(list) to data frame object 
          suid.name.dict.df = 
              data.frame(matrix(unlist(loc.obj@suid.name.dict), nrow=length(loc.obj@suid.name.dict), byrow=TRUE), stringsAsFactors=FALSE)
          colnames(suid.name.dict.df) <- c("name", "SUID")
          # get the SUIDs of the source nodes for the new edges
          source.node.SUIDs = .nodeNameToNodeSUID(loc.obj, source.nodes)
          # get the SUIDs of the target nodes for the new edges
          target.node.SUIDs = .nodeNameToNodeSUID(loc.obj, target.nodes)
          
          # format the new edges data for sending to Cytoscape
          edge.tbl.records = 
              apply(cbind(source.node.SUIDs, target.node.SUIDs, directed, edge.type), MARGIN=1,
                    FUN=function(r) {list(source=unname(r[[1]]), target=unname(r[[2]]), directed=unname(r[[3]]), interaction=unname(r[[4]]))})
          edge.tbl.records.JSON = toJSON(edge.tbl.records)
          resource.uri = paste(loc.obj@uri, pluginVersion(loc.obj), "networks", net.SUID, "edges", sep="/")
          request.res = POST(url=resource.uri, body=edge.tbl.records.JSON, encode="json")
          
          # request.res.edge.SUIDs contains 
          # [edge.SUID, source.node.SUID, targetn.node.SUID] for each edge
          request.res.edge.data = fromJSON(rawToChar(request.res$content))
          
          edge.names = cy2.edge.names(obj@graph)
          # ctreates matrix of the format : 
          # note: column 1 contains edge.SUIDs, and columns 3 & 4 contain node.SUIDs
          #      [,1]   [,2]                     [,3]   [,4]
          # [1,] "412"  "A (phosphorylates) B"   "413"  "404"
          # [2,] "406"  "B (synthetic lethal C"  "407"  "408"
          # [3,] "407"  "C (undefined) A"        "408"  "406"
          edge.names.tbl.records = 
              apply(unname(cbind(unname(t(sapply(request.res.edge.data, unlist))), edge.names)), 
                    MARGIN=1, 
                    FUN=function(r) {list(SUID=as.numeric(unname(r[[1]])), value=unname(r[[4]]), 
                                          source.node=as.numeric(unname(r[[2]])), 
                                          target.node=as.numeric(unname(r[[3]])))})
          # CREATES DICT ENTRIES for the new edges in the following format :
          # [edge.SUID, edge.name, source.node.SUID, target.node.SUID]
          for(i in 1:length(edge.names.tbl.records)) {
              loc.obj@edge.suid.name.dict[[length(loc.obj@edge.suid.name.dict)+1]] = 
                  list(SUID=edge.names.tbl.records[[i]]$SUID, name=edge.names.tbl.records[[i]]$value, 
                       source.node=edge.names.tbl.records[[i]]$source.node, 
                       target.node=edge.names.tbl.records[[i]]$target.node)
          }
          invisible(request.res)
      } else {
          write(sprintf("NOTICE in RCy3::sendEdges():\n\t all %d edges already exist in Cytoscape - nothing new to add >> function returns", length(in.graph.edge.names)), stderr())
          return()
      }
      # simulate 'pass-by-reference' in R
      eval.parent(substitute(obj <- loc.obj))
}) # sendEdges

# ------------------------------------------------------------------------------
setMethod('layoutNetwork', 'CytoscapeWindowClass', 
    function(obj, layout.name = 'grid') {
        if(!layout.name %in% getLayoutNames(obj)) {
            write(sprintf("layout.name '%s' is not recognized; call getLayoutNames(<CytoscapeWindow>) to see those which are supported", layout.name), stderr())
    }
    id = as.character(obj@window.id)
    
    api.str <- paste(obj@uri, pluginVersion(obj), "apply/layouts", layout.name, id, sep = "/")
    
    res <- GET(api.str)
    invisible(res)
  }) # layoutNetwork

#------------------------------------------------------------------------------------------------------------------------
setMethod ('saveLayout', 'CytoscapeWindowClass',

  function (obj, filename, timestamp.in.filename=FALSE) {

     custom.layout <- getNodePosition (obj,  getAllNodes (obj))
     if (timestamp.in.filename) {
        dateString <- format (Sys.time (), "%a.%b.%d.%Y-%H.%M.%S")
        stem <- strsplit (filename, '\\.RData')[[1]]
        filename <- sprintf ('%s.%s.RData', stem, dateString)
        write (sprintf ('saving layout to %s\n', filename), stderr ())
     }
     save (custom.layout, file=filename)
    }) # save.layout

#------------------------------------------------------------------------------------------------------------------------
setMethod ('restoreLayout', 'CytoscapeWindowClass',

  function (obj, filename) {
     custom.layout <- local({x=load(filename); get(x)})
     node.names <- names (custom.layout)
     node.names.filtered <- intersect (node.names, getAllNodes (obj))
     x <- as.integer (sapply (node.names.filtered, function (node.name) return (custom.layout [[node.name]]$x)))
     y <- as.integer (sapply (node.names.filtered, function (node.name) return (custom.layout [[node.name]]$y)))
     setNodePosition (obj, node.names.filtered, x, y)
    }) # restoreLayout

#------------------------------------------------------------------------------------------------------------------------
setMethod ('setNodePosition', 'CytoscapeWindowClass',

    function (obj, node.names, x.coords, y.coords) {
        unknown.nodes <- setdiff (node.names, getAllNodes (obj))
        recognized.nodes <- intersect(node.names, getAllNodes(obj))
        
        # ensure that nodes were provided
        if (length (node.names) == 0){
            return ()
        }

        # throw error if nodes in node.names don't exist in the network
        if (length (unknown.nodes) > 0) {
            node.names = intersect (node.names, nodes (obj@graph))
            write (sprintf ("Error! Unknown nodes in RCy3::setNodePosition"), stderr ())
            for (i in 1:length (unknown.nodes)){
                write (sprintf ("     %s", unknown.nodes [i]), stderr ())
            } # end for
            return ()
        } # end if 

        # ensure that node.names, x.coords, y.coords are of the same length
        if (length(unique(c(length(node.names), length(x.coords), length(y.coords))))>1){
            write (sprintf ("Error! RCy3::setNodePosition: The node names vector has to be the same length as each of the x and y coordiante vectors."), stderr ())
            return()
        }
        
        # check if the coordinates are valid numbers
        if (!any(is.numeric(c(x.coords, y.coords)))){
            write (sprintf ("Error! RCy3::setNodePosition: x and y coordiante vectors must be numeric."), stderr ())
            return()
        }
            
        # set x position
        setNodePropertyDirect(obj, node.names, x.coords, "NODE_X_LOCATION")
        
        # set y position
        setNodePropertyDirect(obj, node.names, y.coords, "NODE_Y_LOCATION")
        
    }) # setNodePosition

#------------------------------------------------------------------------------------------------------------------------
setMethod ('getNodePosition', 'CytoscapeWindowClass',
  function (obj, node.names) {
      net.suid = as.character(obj@window.id)
      # cyREST API version
      version = pluginVersion(obj)
      # get the views for the given network model
      resource.uri <- paste(obj@uri, version, "networks", net.suid, "views", sep="/")
      request.res <- GET(resource.uri)
      net.views.SUIDs <- fromJSON(rawToChar(request.res$content))
      
      view.SUID <- as.character(net.views.SUIDs[[1]])
      
      # if multiple views are found, inform the user about it
      if(length(net.views.SUIDs) > 1) {
          write(sprintf("RCy3::getNodePosition() - %d views found... getting node coordinates of the first one", length(net.views.SUIDs)), stderr())
      }
      
      coordinates.list <- list()
      # get node position for each node
      for (node.name in node.names){
          # convert node name into node SUID
          dict.indices = which(sapply(obj@suid.name.dict, function(s) { s$name }) %in% node.name)
          query.node = sapply(obj@suid.name.dict[dict.indices], function(i) {i$SUID})
          
          # get node x coordinate
          resource.uri <- paste(obj@uri, version, "networks", net.suid, "views", view.SUID, "nodes", as.character(query.node) ,"NODE_X_LOCATION", sep="/")
          request.res <- GET(resource.uri)
          node.x.position <- fromJSON(rawToChar(request.res$content))
          node.x.position <- node.x.position[[2]]
          
          # get node y coordinate
          resource.uri <- paste(obj@uri, version, "networks", net.suid, "views", view.SUID, "nodes", as.character(query.node) ,"NODE_Y_LOCATION", sep="/")
          request.res <- GET(resource.uri)
          node.y.position <- fromJSON(rawToChar(request.res$content))
          node.y.position <- node.y.position[[2]]
          
          # add x and y coordinates to the coordinates list
          coordinates.list[[node.name]] <- list(x= node.x.position, y=node.y.position)
      }
      return(coordinates.list)
    }) # getNodePosition

# ------------------------------------------------------------------------------
setMethod ('getNodeSize', 'CytoscapeWindowClass',

  function (obj, node.names) {
     # get network ID and version
     net.SUID = as.character(obj@window.id)
     version = pluginVersion(obj)
     
     # get the views for the given network model
     resource.uri <- paste(obj@uri, version, "networks", net.SUID, "views", sep="/")
     request.res <- GET(resource.uri)
     net.views.SUIDs <- fromJSON(rawToChar(request.res$content))
     view.SUID <- as.character(net.views.SUIDs[[1]])
     
     widths <- c()
     heights <- c()
     
     for (pos in seq(node.names)){
        node.name <- node.names[pos]
        
        # map node name to node SUID
        dict.indices <- which(sapply(obj@suid.name.dict, function(s) { s$name }) %in% node.name)
        node.SUID <- sapply(obj@suid.name.dict[dict.indices], function(i) {i$SUID})
        
        # request 
        resource.uri <- paste(obj@uri, version, "networks", net.SUID, "views", view.SUID, "nodes", as.character(node.SUID), sep="/")
    
        # request result
        request.res <- GET(resource.uri)
        request.res <- fromJSON(rawToChar(request.res$content))
        visual.properties <- sapply(request.res, '[[', "visualProperty")
        visual.values <- sapply(request.res, '[[', "value")
        widths <- c(widths, as.integer(visual.values[which(visual.properties =="NODE_WIDTH")]))
        heights <- c(heights, as.integer(visual.values[which(visual.properties =="NODE_HEIGHT")]))         
     } # end for (node.name in node.names)
     
     invisible(request.res)
     return (list (width=widths, height=heights))
    }) # getNodeSize

#------------------------------------------------------------------------------------------------------------------------
properlyInitializedNodeAttribute = function (graph, attribute.name) {

 if (length (nodes (graph)) == 0)
   return (TRUE)

 caller.specified.attribute.class = attr (nodeDataDefaults (graph, attribute.name), 'class')

 if (is.null (caller.specified.attribute.class)) {
    msg1 = sprintf ('Error!  Node attribute not initialized "%s"', attribute.name)
    msg2 = sprintf ('        You should call:')
    msg3 = sprintf ('        initNodeAttribute (graph, attribute.name, attribute.type, default.value)')
    msg4 = sprintf ('        where attribute type is one of "char", "integer", or "numeric".')
    msg5 = sprintf ('        example:  g <- initNodeAttribute (g, "nodeType", "char", "molecule")')
    msg6 = sprintf ('             or:  g <- initNodeAttribute (g, "pValue", "numeric", 1.0)')
    write (msg1, stderr ())
    write (msg2, stderr ())
    write (msg3, stderr ())
    write (msg4, stderr ())
    write (msg5, stderr ())
    write (msg6, stderr ())
    return (FALSE)
    }
  return (TRUE)

} # properlyInitializedNodeAttribute
#------------------------------------------------------------------------------------------------------------------------
properlyInitializedEdgeAttribute = function (graph, attribute.name) {

 if (length (edgeNames (graph)) == 0)
   return (TRUE)

 caller.specified.attribute.class = attr (edgeDataDefaults (graph, attribute.name), 'class')

 if (is.null (caller.specified.attribute.class)) {
    msg1 = sprintf ('Error!  "%s" edge attribute not initialized.', attribute.name)
    msg2 = sprintf ('        You should call:')
    msg3 = sprintf ('        initEdgeAttribute (graph, attribute.name, attribute.type, default.value)')
    msg4 = sprintf ('        where attribute type is one of "char", "integer", or "numeric".')
    msg5 = sprintf ('        example:  g <- initEdgeAttribute (g, "edgeType", "char", "molecule")')
    msg6 = sprintf ('             or:  g <- initEdgeAttribute (g, "pValue", "numeric", 1.0)')
    write (msg1, stderr ())
    write (msg2, stderr ())
    write (msg3, stderr ())
    write (msg4, stderr ())
    write (msg5, stderr ())
    write (msg6, stderr ())
    return (FALSE)
    }
  return (TRUE)

} # properlyInitializedEdgeAttribute

# ------------------------------------------------------------------------------
setMethod('setNodeAttributes', 'CytoscapeWindowClass', 
    function(obj, attribute.name) { 
        # it might be the case that '[email protected]' contains nodes that do NOT exist in Cytoscape
        # the below line identifies the indices of those graph nodes, which DO exist in Cytoscape
        node.indices = which(nodes(obj@graph) %in% getAllNodes(obj))
        
        if(length(node.indices) > 0) {
            node.names = nodes(obj@graph)[node.indices]
            
            values = noa(obj@graph, attribute.name)[node.indices]
            
            caller.specified.attribute.class = 
                attr(nodeDataDefaults(obj@graph, attribute.name), 'class')
            invisible(setNodeAttributesDirect(obj, attribute.name, caller.specified.attribute.class, node.names, values))
        } else {
            write(sprintf("WARNING in RCy3::setNodeAttributes():\n\t before setting node attributes, please first send the graph nodes to Cytoscape >> function aborted"), stderr())
        }
})
## END setNodeAttributes

# ------------------------------------------------------------------------------
setMethod('setNodeAttributesDirect', 'CytoscapeWindowClass', 
    function(obj, attribute.name, attribute.type, node.names, values) {
        net.SUID = as.character(obj@window.id)
        version = pluginVersion(obj)
        
        caller.specified.attribute.class = tolower(attribute.type)
        # the switch-block ensures the attribute values have the correct data type
        switch(caller.specified.attribute.class,
               "floating"=,
               "numeric"=,
               "double"={
                   caller.specified.attribute.class = 'Double'
                   values = as.numeric(values)
               },
               "integer"=,
               "int"={
                   caller.specified.attribute.class = "Integer"
                   values = as.integer(values)
               },
               "boolean"={
                   caller.specified.attribute.class = "Boolean"
                   values = as.logical(values)
               },{
                   caller.specified.attribute.class = "String"
                   values = as.character(values)
               }
        )
        
        # CREATES NEW COLUMN (IF NEEDED)
        if(!attribute.name %in% getNodeAttributeNames(obj)) {
            # create new table column in Cytoscape 'Node Table' to store the attribute values
            tbl.col = list(name=attribute.name, type=caller.specified.attribute.class)
            tbl.col.JSON = toJSON(tbl.col)
            resource.uri = 
                paste(obj@uri, version, "networks", net.SUID, "tables/defaultnode/columns", sep="/")
            request.res = POST(url=resource.uri, body=tbl.col.JSON, encode="json")
        }
        
        if(length(node.names) > 0) {
            if(length(node.names) != length(values)) {
                write(sprintf("ERROR in RCy3::setNodeAttributesDirect():\n\t the number of values(%d) for attribute '%s' must equal the number of nodes(%d) >> function aborted", 
                              length(values), attribute.name, length(node.names)), stderr())
            } else {
                node.SUIDs = .nodeNameToNodeSUID(obj, node.names)
                node.name.suid.value.df = data.frame(node.names, node.SUIDs, values)
                
                # converts the above data frame data in the cyREST [SUID:value]-pairs format
                node.SUID.value.pairs = 
                    apply(node.name.suid.value.df[,c('node.SUIDs','values')], 1, function(x) {list(SUID=unname(x[1]), value=unname(x[2]))})
                node.SUID.value.pairs.JSON = toJSON(node.SUID.value.pairs)
                
                resource.uri = 
                    paste(obj@uri, version, "networks", net.SUID, "tables/defaultnode/columns", attribute.name, sep="/")
                request.res = PUT(url=resource.uri, body=node.SUID.value.pairs.JSON, encode="json")
                invisible(request.res)
            }
        }
})
## END setNodeAttributesDirect

# ------------------------------------------------------------------------------
setMethod('setEdgeAttributes', 'CytoscapeWindowClass', 
    function(obj, attribute.name) {
        cyrest.edge.names = as.character(cy2.edge.names(obj@graph))
        # user might have entered the names of edges that do NOT exist
        # the below line will return the indices of the nodes that DO exist
        edge.indices = which(cyrest.edge.names %in% getAllEdges(obj))
        
        if(length(edge.indices) > 0) {
            edge.names = cyrest.edge.names[edge.indices]
            edge.names.tilde = names(cy2.edge.names(obj@graph)[edge.indices])
            edge.names.with.bars = gsub('~', '|', edge.names.tilde)
            
            values = eda(obj@graph, attribute.name)[edge.names.with.bars]
            
            caller.specified.attribute.class = attr(edgeDataDefaults(obj@graph, attribute.name), 'class')
            
            invisible(setEdgeAttributesDirect(obj, attribute.name, caller.specified.attribute.class, edge.names, values))
        } else {
            write(sprintf("WARNING in RCy3::setEdgeAttributes():\n\t before setting edge attributes, please first send the graph edges to Cytoscape >> function aborted"), stderr())
        }        
}) 
## END setEdgeAttributes

# ------------------------------------------------------------------------------
setMethod('setEdgeAttributesDirect', 'CytoscapeWindowClass', 
    function(obj, attribute.name, attribute.type, edge.names, values) {
        net.SUID = as.character(obj@window.id)
        version = pluginVersion(obj)
        
        if(length(edge.names) > 0) {
            if(length(edge.names) != length(values)) {
                write(sprintf("ERROR in RCy3::setEdgeAttributesDirect():\n\t the number of values(%d) for attribute '%s' must equal the number of edges(%d) >> function aborted", length(values), attribute.name, length(edge.names)), stderr())
                
            } else {
                caller.specified.attribute.class = tolower(attribute.type)
                # the switch-block ensures the attribute values have the correct data type
                switch(caller.specified.attribute.class,
                       "floating"=,
                       "numeric"=,
                       "double"={
                           caller.specified.attribute.class = 'Double'
                           values = as.numeric(values)
                       },
                       "integer"=,
                       "int"={
                           caller.specified.attribute.class = "Integer"
                           values = as.integer(values)
                       },
                       "boolean"={
                           caller.specified.attribute.class = "Boolean"
                           values = as.logical(values)
                       },{
                           caller.specified.attribute.class = "String"
                           values = as.character(values)
                       }
                )
                
                if(!attribute.name %in% getEdgeAttributeNames(obj)) {
                    tbl.col = list(name=attribute.name, type=caller.specified.attribute.class)
                    tbl.col.JSON = toJSON(tbl.col)
                    resource.uri = 
                        paste(obj@uri, version, "networks", net.SUID, "tables/defaultedge/columns", sep="/")
                    request.res = POST(url=resource.uri, body=tbl.col.JSON, encode="json")
                }
                
                edge.SUIDs = .edgeNameToEdgeSUID(obj, edge.names)
                edge.name.suid.value.df = data.frame(edge.names, edge.SUIDs, values)
                
                edge.SUID.value.pairs = 
                    apply(edge.name.suid.value.df[,c('edge.SUIDs','values')], 1, function(x) {list(SUID=unname(x[1]), value=unname(x[2]))})
                edge.SUID.value.pairs.JSON = toJSON(edge.SUID.value.pairs)
                resource.uri = 
                    paste(obj@uri, version, "networks", net.SUID, "tables/defaultedge/columns", attribute.name, sep="/")
                request.res = PUT(url=resource.uri, body=edge.SUID.value.pairs.JSON, encode="json")
                invisible(request.res)        
            }
        }
}) 
## END setEdgeAttributesDirect

# ------------------------------------------------------------------------------
setMethod('displayGraph', 'CytoscapeWindowClass', function(obj) {
    # needed to simulate 'pass-by-reference' behavior in R
    loc.obj <- obj
    
    if(length(nodes(loc.obj@graph)) == 0) {
        write('RCy3::displayGraph, cannot display empty(0 nodes) graph, returning...', stderr())
        return()
    }
    
    node.count = length(nodes(loc.obj@graph))
    edge.count = length(edgeNames(loc.obj@graph))
    node.attribute.count = length(noa.names(loc.obj@graph)) * node.count
    edge.attribute.count = length(eda.names(loc.obj@graph)) * edge.count
    
    estimated.time = predictTimeToDisplayGraph(loc.obj)
    # if (execution)time measurement option is turned on, save the current time
    if (loc.obj@collectTimings) {
        method.start.time = Sys.time()
        # start time (for sending nodes to Cytoscape) 
        stepwise.start.time = Sys.time()
    }
    
    write(sprintf('estimated displayGraph time: %8.1f seconds', estimated.time), stderr())
    write(sprintf('adding %d nodes...', length(nodes(obj@graph))), stderr())
    
    sendNodes(loc.obj)
    
    if(loc.obj@collectTimings) {
        current.step.exec.time = difftime(Sys.time(), stepwise.start.time, units='secs')
        write(sprintf(' *** sendNodes: %f secs', current.step.exec.time, stderr()))
        # start time (for sending node attributes to Cytoscape)
        stepwise.start.time = Sys.time()
    }
    
    # sends edges to Cytoscape
    write (sprintf ('adding %d edges...', length (edgeNames (loc.obj@graph))), stderr ())
    sendEdges (loc.obj)
    
    if (obj@collectTimings) {
        write (sprintf (' *** sendEdges: %f secs', difftime (Sys.time (), stepwise.start.time, units='secs')), stderr ())
        stepwise.start.time = Sys.time ()
    }
    
    # sending node attributes
    write ('adding node attributes...', stderr ())
    
    # send node attributes from R to Cytoscape
    sapply (noa.names (loc.obj@graph), function (name) {print (name); setNodeAttributes (loc.obj, name)})
    
    if (obj@collectTimings) {
        write (sprintf (' *** send node attributes: %f secs', difftime (Sys.time (), stepwise.start.time, units='secs')), stderr ())
        stepwise.start.time = Sys.time ()
    }
    
    # send edge attributes
    write ('adding edge attributes...', stderr ())
    edgeAttributeNames = eda.names (loc.obj@graph)
    sapply (eda.names (loc.obj@graph), function (name) {print (name); setEdgeAttributes (loc.obj, name)})
    
    if (obj@collectTimings) {
        write (sprintf (' *** send edge attributes: %f secs', difftime (Sys.time (), stepwise.start.time, units='secs')), stderr ())
        stepwise.start.time = Sys.time ()
        actual.time = difftime (Sys.time (), method.start.time, units='secs')
        write (sprintf (' *** leaving displayGraph, predicted duration %f secs,  actual %f secs', as.integer (round (estimated.time)),
                        as.integer (round (actual.time))), stderr ())
    } # if collectTimings
    
    # pseudo R 'pass-by-reference': cw now contains the [node suid,node name] pairs
    eval.parent(substitute(obj <- loc.obj))
}) 
## END displayGraph

# ------------------------------------------------------------------------------
setMethod('predictTimeToDisplayGraph', 'CytoscapeWindowClass', 
    function(obj) {
        g = obj@graph
        node.count = length(nodes(g))
        edge.count = length(edgeNames(g))
        noa.count = length(noa.names(g)) * node.count
        eda.count = length(eda.names(g)) * edge.count
        prediction = (0.002 * node.count) + (0.010 * edge.count) + (0.001 * noa.count) + (0.001 * eda.count)
        return (prediction)
})
## END predictTimeToDisplayGraph

# ------------------------------------------------------------------------------
setMethod('redraw', 'CytoscapeWindowClass', 
    function(obj) {
        net.SUID <- as.character(obj@window.id)
        version <- pluginVersion(obj)
        
        resource.uri <- paste(obj@uri, version, "apply/styles", "default", net.SUID, sep = "/")
        request.res <- GET(url=resource.uri)
        invisible(request.res)
}) 
## END redraw

# ------------------------------------------------------------------------------
setMethod('setWindowSize', 'CytoscapeWindowClass', 
    function(obj, width, height) {
        write(sprintf("WARNING: Method RCy3::setWindowSize() is not implemented in RCy3!"), stderr())
        
        return(FALSE)
})
## END setWindowSize

#------------------------------------------------------------------------------------------------------------------------
setMethod ('setTooltipInitialDelay', 'CytoscapeConnectionClass',

   function (obj, msecs) {
       write(sprintf("WARNING: Method RCy3::setTooltipInitialDelay() is not implemented in RCy3!"), stderr())
       return(FALSE)
       
#     invisible (xml.rpc ([email protected], 'Cytoscape.setToolTipInitialDelay', as.integer (msecs)))
     })

#------------------------------------------------------------------------------------------------------------------------
setMethod ('setTooltipDismissDelay', 'CytoscapeConnectionClass',

   function (obj, msecs) {
       write(sprintf("WARNING: Method RCy3::setTooltipDismissDelay() is not implemented in RCy3!"), stderr())
       return(FALSE)
       
#     invisible (xml.rpc ([email protected], 'Cytoscape.setToolTipDismissDelay', as.integer (msecs)))
     })

# ------------------------------------------------------------------------------
setMethod('raiseWindow', 'CytoscapeConnectionClass', 
    function(obj, window.title = NA) {
        write(sprintf("WARNING: Method RCy3::raiseWindow() is not implemented in RCy3!"), stderr())
        return(FALSE)
        
#         if (is.na(window.title)) {
#             if(class(obj) == 'CytoscapeWindowClass') {
#                 window.id = [email protected]
#             } else {
#                 write(sprintf('error in RCy3::raiseWindow(), no window title provided'), stderr())
#                 return()
#             }
#         } # no window title
#         
#         # if window title was provided
#         if(!is.na(window.title)) {
#             window.id = getWindowID(obj, window.title)
#             
#             if(is.na(window.id)) {
#                 write(sprintf('error in RCy3::raiseWindow(), unrecognized window title: %s', window.title), stderr ())
#                 return()
#             }
#             # TO DO: call to raise the view
#         } # window title was provided
    }) # raiseWindow

#------------------------------------------------------------------------------------------------------------------------
setMethod ('showGraphicsDetails', 'CytoscapeConnectionClass',

    function (obj, new.value) {
        resource.uri <- paste(obj@uri, pluginVersion(obj), "ui/lod/", sep="/")
        request.res <- PUT(resource.uri)
        invisible (request.res)
        if (class (obj) == 'CytoscapeWindowClass'){
            redraw (obj)
        }
        write(sprintf('RCy3::showGraphicsDetails(), Switching between show and hide full graphics details.'), stdout())
        
    })

# ------------------------------------------------------------------------------
# display the graph using all of the available window space (the Cytoscape drawing canvas)
setMethod('fitContent', 'CytoscapeWindowClass', 
    function(obj) {
        net.SUID <- as.character(obj@window.id)
        resource.uri <- paste(obj@uri, pluginVersion(obj), "apply/fit", net.SUID, sep="/")
        request.res <- GET(url=resource.uri)
        invisible(request.res)
})
## END fitContent

# ------------------------------------------------------------------------------
setMethod('fitSelectedContent', 'CytoscapeWindowClass', 
    function(obj) {
        write(sprintf("WARNING: Method RCy3::fitSelectedContent() is not implemented in RCy3!"), stderr())
        
        return(FALSE)
})
## END fitSelectedContent

# ------------------------------------------------------------------------------
setMethod('getCenter', 'CytoscapeWindowClass', 
    function(obj) {
        net.SUID <- as.character(obj@window.id)
        version <- pluginVersion(obj)
        
        # get all Cytoscape views belonging to that network
        net.views.SUIDs <- .getNetworkViews(obj)
        view.SUID <- as.character(net.views.SUIDs[[1]])
        
        # if multiple views are found, inform the user about it
        if(length(net.views.SUIDs) > 1) {
            write(sprintf("RCy3::getCenter() - %d views found... returning coordinates of the first one", length(net.views.SUIDs)), stderr())
        }
        # get the X-coordinate
        resource.uri <- 
            paste(obj@uri, version, "networks", net.SUID, "views", view.SUID, "network/NETWORK_CENTER_X_LOCATION", sep="/")
        request.res <- GET(resource.uri)
        x.coordinate <- fromJSON(rawToChar(request.res$content))$value[[1]]
        # get the Y-coordinate
        resource.uri <- 
            paste(obj@uri, version, "networks", net.SUID, "views", view.SUID, "network/NETWORK_CENTER_Y_LOCATION", sep="/")
        request.res <- GET(resource.uri)
        y.coordinate <- fromJSON(rawToChar(request.res$content))$value[[1]]
        
        return(list(x = x.coordinate, y = y.coordinate))
})
## END getCenter

# ------------------------------------------------------------------------------
# this method could be used to pan and scroll the Cytoscape canvas, which is adjusted(moved) 
# so that the specified x and y coordinates are at the center of the visible window.
setMethod('setCenter', 'CytoscapeWindowClass', 
    function(obj, x, y) {
        net.SUID <- as.character(obj@window.id)
        version <- pluginVersion(obj)
        
        net.views.SUIDs <- .getNetworkViews(obj)
        view.SUID <- as.character(net.views.SUIDs[[1]])
        
        # if multiple views are found, inform the user about it
        if(length(net.views.SUIDs) > 1) {
            write(sprintf("RCy3::setCenter() - %d views found... setting coordinates of the first one", length(net.views.SUIDs)), stderr())
        }
        
        # set the X-coordinate
        resource.uri <- 
            paste(obj@uri, version, "networks", net.SUID, "views", view.SUID, "network", sep="/")
        new.x.coordinate.JSON <- toJSON(list(list(visualProperty="NETWORK_CENTER_X_LOCATION", value=x)))
        request.res <- PUT(resource.uri, body=new.x.coordinate.JSON, encode="json")
        # set the Y-coordinate
        resource.uri <- 
            paste(obj@uri, version, "networks", net.SUID, "views", view.SUID, "network", sep="/")
        new.y.coordinate.JSON <- toJSON(list(list(visualProperty="NETWORK_CENTER_Y_LOCATION", value=y)))
        request.res <- PUT(resource.uri, body=new.y.coordinate.JSON, encode="json")
        invisible(request.res)
})
## END setCenter

# ------------------------------------------------------------------------------
setMethod('getZoom', 'CytoscapeWindowClass', 
    function(obj) {
        net.SUID <- as.character(obj@window.id)
        version <- pluginVersion(obj)
        
        # get the existing views for the given network model
        net.views.SUIDs <- .getNetworkViews(obj)
        view.SUID <- as.character(net.views.SUIDs[[1]])
        
        # if multiple views are found, inform the user about it
        if(length(net.views.SUIDs) > 1) {
            write(sprintf("RCy3::getZoom() - %d views found... returning coordinates of the first one", length(net.views.SUIDs)), stderr())
        }
        
        resource.uri <- 
            paste(obj@uri, version, "networks", net.SUID, "views", view.SUID, "network/NETWORK_SCALE_FACTOR", sep="/")
        request.res <- GET(resource.uri)
        zoom.level <- fromJSON(rawToChar(request.res$content))$value[[1]]
        
        return(zoom.level)
})
## END getZoom

# ------------------------------------------------------------------------------
setMethod('setZoom', 'CytoscapeWindowClass', 
    function(obj, new.level) {
        net.SUID <- as.character(obj@window.id)
        version <- pluginVersion(obj)
        
        net.views.SUIDs <- .getNetworkViews(obj)
        view.SUID <- as.character(net.views.SUIDs[[1]])
        
        # if multiple views are found, inform the user about it
        if(length(net.views.SUIDs) > 1) {
            write(sprintf("RCy3::getZoom() - %d views found... returning coordinates of the first view", length(net.views.SUIDs)), stderr())
        }
        
        view.zoom.value <- list(visualProperty='NETWORK_SCALE_FACTOR', value=new.level)
        view.zoom.value.JSON <- toJSON(list(view.zoom.value))
        
        resource.uri <- paste(obj@uri, version, "networks", net.SUID, "views", view.SUID, "network", sep="/")
        request.res <- PUT(url=resource.uri, body=view.zoom.value.JSON, encode="json")
        
        invisible(request.res)
})
## END setZoom

# ------------------------------------------------------------------------------
setMethod('getViewCoordinates', 'CytoscapeWindowClass', 
    function(obj) {
        write(sprintf("WARNING: Method RCy3::getViewCoordinates() is not implemented in RCy3!"), stderr())
        
        return(FALSE)
})
## END getViewCoordinates

# ------------------------------------------------------------------------------
setMethod('hidePanel', 'CytoscapeConnectionClass', 
    function(obj, panelName) {
        version <- pluginVersion(obj)
        
        if (tolower(panelName) %in% c('data panel', 'd', 'data', 'da')){
            panelName <- 'SOUTH'
        }else if (tolower(panelName) %in% c('control panel', 'control', 'c', 'co')){
            panelName <- 'WEST'
        }else if (!(panelName %in% c('WEST', 'EAST', 'SOUTH', 'SOUTH_WEST'))){
            write (sprintf ('ERROR! Define a valid panel name.'), stderr ())
            return(NA)
        }
        
        panel.name.state = list(name=panelName, state='HIDE')
        
        resource.uri <- paste(obj@uri, version, "ui/panels", sep="/")
        request.res <- PUT(url=resource.uri, body=toJSON(list(panel.name.state)), encoding="json")
        
        invisible(request.res)
})
## END hidePanel

# ------------------------------------------------------------------------------
setMethod('hideAllPanels', 'CytoscapeConnectionClass', 
    function(obj) {
        hidePanel(obj, "SOUTH")
        hidePanel(obj, "EAST")
        hidePanel(obj, "WEST")
        hidePanel(obj, "SOUTH_WEST")
})
## END hideAllPanels

# ------------------------------------------------------------------------------
setMethod('dockPanel', 'CytoscapeConnectionClass', 
    function(obj, panelName) {
        version <- pluginVersion(obj)

        if (tolower(panelName) %in% c('data panel', 'd', 'data', 'da')){
            panelName <- 'SOUTH'
        }else if (tolower(panelName) %in% c('control panel', 'control', 'c', 'co')){
            panelName <- 'WEST'
        }else if (!(panelName %in% c('WEST', 'EAST', 'SOUTH', 'SOUTH_WEST'))){
            write (sprintf ('ERROR! Define a valid panel name.'), stderr ())
            return(NA)
        }
        
        panel.name.state = list(name=panelName, state='DOCK')
        
        resource.uri <- paste(obj@uri, version, "ui/panels", sep="/")
        request.res <- PUT(url=resource.uri, body=toJSON(list(panel.name.state)), encoding="json")
        
        invisible(request.res)
})
## END dockPanel

# ------------------------------------------------------------------------------
setMethod('floatPanel', 'CytoscapeConnectionClass', 
    function(obj, panelName) {
        version <- pluginVersion(obj)
        
        if (tolower(panelName) %in% c('data panel', 'd', 'data', 'da')){
            panelName <- 'SOUTH'
        }else if (tolower(panelName) %in% c('control panel', 'control', 'c', 'co')){
            panelName <- 'WEST'
        }else if (!(panelName %in% c('WEST', 'EAST', 'SOUTH', 'SOUTH_WEST'))){
            write (sprintf ('ERROR! Define a valid panel name.'), stderr ())
            return(NA)
        }
        
        panel.name.state = list(name=panelName, state='FLOAT')
        
        resource.uri <- paste(obj@uri, version, "ui/panels", sep="/")
        request.res <- PUT(url=resource.uri, body=toJSON(list(panel.name.state)), encoding="json")
        
        invisible(request.res)
})
## END floatPanel

#------------------------------------------------------------------------------------------------------------------------
setMethod ('setNodeTooltipRule', 'CytoscapeWindowClass',

      function (obj, node.attribute.name) {
          id <- as.character (obj@window.id)
          viz.style.name = 'default'
          if (!node.attribute.name %in% noa.names (obj@graph)) {
              write (sprintf ('Warning! RCy3::setNodeTooltipRule: passed non-existent node attribute: %s', node.attribute.name), stderr ())
              return ()
          }
          attribute.values = noa (obj@graph, node.attribute.name)
          
          # set default tooltip
          default.tooltip <- list(visualProperty = "NODE_TOOLTIP", value = "")
          setVisualProperty(obj, default.tooltip, viz.style.name)
          
          # define the column type
          sample.node.attribute <- getNodeAttribute (obj, getAllNodes(obj)[1], node.attribute.name)
          columnType <- findColumnType(typeof(sample.node.attribute))

          # discrete mapping
          discreteMapping(obj, node.attribute.name, attribute.values, attribute.values,
                          visual.property="NODE_TOOLTIP", columnType=columnType, style=viz.style.name)
          
    })  # END setNodeTooltipRule

#------------------------------------------------------------------------------------------------------------------------
setMethod ('setEdgeTooltipRule', 'CytoscapeWindowClass',

    function (obj, edge.attribute.name) {
        id = as.character (obj@window.id)
        viz.style.name = 'default'
        if (!edge.attribute.name %in% eda.names (obj@graph)) {
            write (sprintf ('warning!  setEdgeTooltipRule passed non-existent edge attribute: %s', edge.attribute.name), stderr ())
            return ()
        }
        attribute.values = as.character (eda (obj@graph, edge.attribute.name))
        
        # set default tooltip
        default.tooltip <- list(visualProperty = "EDGE_TOOLTIP", value = "")
        setVisualProperty(obj, default.tooltip, viz.style.name)
        
        # define the column type
        columnType <- findColumnType(typeof(attribute.values[1]))
        
        # discrete mapping
        discreteMapping(obj, edge.attribute.name, attribute.values, attribute.values,
                        visual.property="EDGE_TOOLTIP", columnType=columnType, style=viz.style.name)

    })  # setEdgeTooltipRule

#------------------------------------------------------------------------------------------------------------------------
setMethod ('setNodeLabelRule', 'CytoscapeWindowClass',
    function (obj, node.attribute.name) {
        id = as.character (obj@window.id)
        vizmap.style.name = 'default'
        if (!node.attribute.name %in% noa.names (obj@graph)) {
            write (sprintf ('warning!  setNodeLabelRule passed non-existent node attribute: %s', node.attribute.name), stderr ())
            return ()
        }
        attribute.values = as.character (noa (obj@graph, node.attribute.name))
        
        # set default label
        default.label <- list(visualProperty = "NODE_LABEL", value = "")
        setVisualProperty(obj, default.label, vizmap.style.name)
        
        # define the column type
        columnType <- findColumnType(typeof(attribute.values[1]))
        
        # discrete mapping
        discreteMapping(obj, node.attribute.name, attribute.values, attribute.values,
                        visual.property="NODE_LABEL", columnType=columnType, style=vizmap.style.name)
    })  # setNodeLabelRule

#------------------------------------------------------------------------------------------------------------------------
setMethod ('setEdgeLabelRule', 'CytoscapeWindowClass',

    function (obj, edge.attribute.name) {
        id = as.character (obj@window.id)
        vizmap.style.name = 'default'
        if (!edge.attribute.name %in% eda.names (obj@graph)) {
            write (sprintf ('warning!  setEdgeLabelRule passed non-existent edge attribute: %s', edge.attribute.name), stderr ())
            return ()
        }
        attribute.values = as.character (eda (obj@graph, edge.attribute.name))
        
        # set default label
        default.label <- list(visualProperty = "EDGE_LABEL", value = "")
        setVisualProperty(obj, default.label, vizmap.style.name)
        
        # define the column type
        columnType <- findColumnType(typeof(attribute.values[1]))
        
        # discrete mapping
        discreteMapping(obj, edge.attribute.name, attribute.values, attribute.values,
                        visual.property="EDGE_LABEL", columnType=columnType, style=vizmap.style.name)
    })  # setEdgeLabelRule

#------------------------------------------------------------------------------------------------------------------------
setMethod ('setNodeColorRule', 'CytoscapeWindowClass',
           
           function (obj, node.attribute.name, control.points, colors, mode, default.color='#FFFFFF') {
               if (!mode %in% c ('interpolate', 'lookup')) {
                   write ("Error! RCy3:setNodeColorRule. Mode must be 'interpolate' (the default) or 'lookup'.", stderr ())
                   return ()
               }
               
               # check if colors are formatted correctly
               for (color in colors){
                   if (.isNotHexColor(color)){
                       return()
                   } 
               }
               
               #TODO Comment TanjaM we should give the user the option to choose the style as an input parameter which defaults to default.
               vizmap.style.name = 'default'
               
               #set default
               setDefaultNodeColor (obj, default.color, vizmap.style.name)
               
               # define the column type
               columnType <- findColumnType(typeof(control.points[1]))

               # interpolate
               if (mode=='interpolate') {  # need a 'below' color and an 'above' color.  so there should be two more colors than control.points 
                   if (length (control.points) == length (colors)) { # caller did not supply 'below' and 'above' values; manufacture them
                       colors = c (colors [1], colors, colors [length (colors)])
                       write ("RCy3::setNodeColorRule, no 'below' or 'above' colors specified.  Inferred from supplied colors.", stderr ());
                } #
                good.args = length (control.points) == (length (colors) - 2)
                if (!good.args) {
                    write (sprintf ('cp: %d', length (control.points)), stderr ())
                    write (sprintf ('co: %d', length (colors)), stderr ())
                    write ("Error! RCy3:setNodeColorRule, interpolate mode.", stderr ())
                    write ("Expecting 1 color for each control.point, one for 'above' color, one for 'below' color.", stderr ())
                    return ()
                }
                
                continuousMapping (obj, node.attribute.name, control.points, colors, visual.property="NODE_FILL_COLOR", columnType=columnType, style=vizmap.style.name)
                 
                } # if mode==interpolate
                else { # use a discrete rule, with no interpolation, mode==lookup
                   good.args = length (control.points) == length (colors)
                   if (!good.args) {
                       write (sprintf ('control points: %d', length (control.points)), stderr ())
                       write (sprintf ('colors: %d', length (colors)), stderr ())
                       write ("Error! RCy3:setNodeColorRule.  Expecting exactly as many colors as control.points in lookup mode.", stderr ())
                       return ()
                }
                   
                discreteMapping(obj, node.attribute.name, control.points, colors, visual.property="NODE_FILL_COLOR", columnType=columnType, style=vizmap.style.name)    
              
                } # else: !interpolate, aka lookup
     }) # setNodeColorRule

#------------------------------------------------------------------------------------------------------------------------
# Cytoscape distinguishes between Node Opacity, Node Border Opacity, and Node Label Opacity.  we call this 'aspect' here.

setMethod ('setNodeOpacityRule', 'CytoscapeWindowClass',

    function (obj, node.attribute.name, control.points, opacities, mode, aspect='all') {
        if (!mode %in% c ('interpolate', 'lookup')) {
            write ("Error! RCy3:setNodeOpacityRule.  mode must be 'interpolate' (the default) or 'lookup'.", stderr ())
            return ()
        }
        
        #TODO Comment TanjaM we should give the user the option to choose the style 
        # as an input parameter which defaults to default.
        vizmap.style.name = 'default'

        # define the column type
        columnType <- findColumnType(typeof(control.points[1]))
        
        # set default # Comment TanjaM: Current version does not set default
        #setDefaultNodeOpacity (obj, default.opacity, vizmap.style.name)
        
        aspect.all = length (grep ('all', aspect))  > 0
        aspect.fill = length (grep ('fill', aspect)) > 0
        aspect.border = length (grep ('border', aspect)) > 0
        aspect.label = length (grep ('label', aspect)) > 0
        
        if (aspect.all) {
            aspect.fill = TRUE
            aspect.border = TRUE
            aspect.label = TRUE
        }
        
        if (aspect.fill == FALSE && aspect.border == FALSE && aspect.label == FALSE) {
            specific.options = 'fill, border, label'
            msg.1 = "Error! RCy3:setNodeOpacityRule. Aspect must be 'all' (the default) "
            msg.2 = sprintf ("or some combination, in any order, of %s", specific.options)
            write (msg.1, stderr ())
            write (msg.2, stderr ())
            return ()
        }

        if (mode=='interpolate') {  # need a 'below' opacity and an 'above' opacity.  so there should be two more opacities than control.points 
            if (length (control.points) == length (opacities)) { # caller did not supply 'below' and 'above' values; manufacture them
                opacities = c (opacities [1], opacities, opacities [length (opacities)])
                write ("RCy3::setNodeOpacityRule, no 'below' or 'above' opacities specified.  Inferred from supplied opacities.", stderr ());
            }
            
            good.args = length (control.points) == (length (opacities) - 2)
            if (!good.args) {
                write (sprintf ('cp: %d', length (control.points)), stderr ())
                write (sprintf ('co: %d', length (opacities)), stderr ())
                write ("Error! RCy3:setNodeOpacityRule, interpolate mode.", stderr ())
                write ("Expecting 1 opacity for each control.point, one for 'above' opacity, one for 'below' opacity.", stderr ())
                return ()
            }
            
            if (aspect.fill){
                continuousMapping (obj, node.attribute.name, control.points, opacities,
                                   visual.property="NODE_TRANSPARENCY",
                                   columnType=columnType, style=vizmap.style.name)
            }
            if (aspect.border){
                continuousMapping (obj, node.attribute.name, control.points, opacities,
                                   visual.property="NODE_BORDER_TRANSPARENCY",
                                   columnType=columnType, style=vizmap.style.name)
            }
            if (aspect.label){
                continuousMapping (obj, node.attribute.name, control.points, opacities,
                                   visual.property="NODE_LABEL_TRANSPARENCY",
                                   columnType=columnType, style=vizmap.style.name)
            }
        } # if mode==interpolate
        
        else { # mode==lookup, use a discrete rule, with no interpolation
            good.args = length (control.points) == length (opacities)
            if (!good.args) {
                write (sprintf ('cp: %d', length (control.points)), stderr ())
                write (sprintf ('co: %d', length (opacities)), stderr ())
                write ("Error! RCy3:setNodeOpacityRule.  Expecting exactly as many opacities as control.points in lookup mode.", stderr ())
                return ()
            }
            
            default.opacity = 255;
            if (length (control.points) == 1) {   # code around the requirement that one-element lists are turned into scalars
                control.points = rep (control.points, 2)
                opacities = rep (opacities, 2)
            }
            
            if (aspect.fill){
                discreteMapping(obj, node.attribute.name, control.points, opacities,
                                visual.property="NODE_TRANSPARENCY",
                                columnType=columnType, style=vizmap.style.name)
            }
            
            if (aspect.border){
                discreteMapping(obj, node.attribute.name, control.points, opacities,
                                visual.property="NODE_BORDER_TRANSPARENCY",
                                columnType=columnType, style=vizmap.style.name)
            }
            
            if (aspect.label){
                discreteMapping(obj, node.attribute.name, control.points, opacities,
                                visual.property="NODE_LABEL_TRANSPARENCY",
                                columnType=columnType, style=vizmap.style.name)
            }
        } # else: !interpolate
     }) # setNodeOpacityRule

#------------------------------------------------------------------------------------------------------------------------
setMethod ('setNodeBorderColorRule', 'CytoscapeWindowClass',

    function (obj, node.attribute.name, control.points, colors, mode, default.color='#000000') {
        if (!mode %in% c ('interpolate', 'lookup')) {
            write ("Error! RCy3:setNodeBorderColorRule. Mode must be 'interpolate' or 'lookup'.", stderr ())
            return ()
        }
        
        # check if colors are formatted correctly
        for (color in colors){
            if (.isNotHexColor(color)){
                return()
            } 
        }
        
        #TODO Comment TanjaM we should give the user the option to choose the style as an input parameter which defaults to default.
        vizmap.style.name = 'default'
        
        # set default
        setDefaultNodeBorderColor (obj, default.color, vizmap.style.name)
        
        # define the column type
        columnType <- findColumnType(typeof(control.points[1]))
        
        # mode==interpolate
        if (mode=='interpolate') {  # need a 'below' color and an 'above' color.  so there should be two more colors than control.points 
            if (length (control.points) == length (colors)){  # caller did not supply 'below' and 'above' values; manufacture them
                colors = c (default.color, colors, default.color)
            }
            good.args = length (control.points) == (length (colors) - 2)
            if (!good.args) {
                write (sprintf ('cp: %d', length (control.points)), stderr ())
                write (sprintf ('co: %d', length (colors)), stderr ())
                write ("Error! RCy3:setNodeBorderColorRule, interpolate mode.", stderr ())
                write ("Expecting 1 color for each control.point, one for 'above' color, one for 'below' color.", stderr ())
                return ()
            }
            # continous mapping
            continuousMapping (obj, node.attribute.name, control.points, colors, visual.property="NODE_BORDER_PAINT", columnType=columnType, style=vizmap.style.name)

        } # if mode==interpolate
        else { # use a discrete rule, with no interpolation
            good.args = length (control.points) == length (colors)
            if (!good.args) {
                write (sprintf ('cp: %d', length (control.points)), stderr ())
                write (sprintf ('co: %d', length (colors)), stderr ())
                write ("Error! RCy3:setNodeBorderColorRule.  Expecting exactly as many colors as control.points in lookup mode.", stderr ())
                return ()
            }
            discreteMapping(obj, node.attribute.name, control.points, colors, visual.property="NODE_BORDER_PAINT", columnType=columnType, style=vizmap.style.name)
        } # else: !interpolate
     }) # setNodeBorderColorRule

#------------------------------------------------------------------------------------------------------------------------
setMethod ('setNodeBorderWidthRule', 'CytoscapeWindowClass',

   function (obj, node.attribute.name, attribute.values, line.widths, default.width=1) {
       id = as.character (obj@window.id)
       #TODO the style should be passed as a parameter
       vizmap.style.name = 'default'
       #TODO we should add interpolate as mode in the function
       mode = "lookup"
       if (!node.attribute.name %in% noa.names (obj@graph)) {
           write (sprintf ('warning!  setNodeBorderWidthRule passed non-existent node attribute: %s', node.attribute.name), stderr ())
           return ()
       }
       
       # set default
       setDefaultNodeBorderWidth(obj, default.width, vizmap.style.name)
       
       # define the column type
       columnType <- "String" #findColumnType(typeof(line.widths[1]))
       # discrete mapping
       if (mode=="lookup"){
           discreteMapping (obj, node.attribute.name, attribute.values, line.widths,
                       visual.property="NODE_BORDER_WIDTH", columnType=columnType, style=vizmap.style.name)
       } else{
           # continuous mapping
           # TODO need to check here if 2 more values were passed in for width
           continuousMapping (obj, node.attribute.name, attribute.values, line.widths, visual.property="NODE_BORDER_WIDTH", columnType=columnType, style=vizmap.style.name)
       }
     })

# ------------------------------------------------------------------------------
setMethod('setDefaultNodeShape', 'CytoscapeConnectionClass', 
  function(obj, new.shape, vizmap.style.name='default') {
      new.shape <- toupper(new.shape)
      if (new.shape %in% getNodeShapes(obj)){
          style = list(visualProperty = "NODE_SHAPE", value = new.shape)
          setVisualProperty(obj, style, vizmap.style.name)
      }else{
          write (sprintf ('%s is not a valid shape. Use getNodeShapes() to find valid values.', new.shape), stderr ())
      }
})

# ------------------------------------------------------------------------------
setMethod('setDefaultNodeSize', 'CytoscapeConnectionClass', 
    function(obj, new.size, vizmap.style.name='default') {
        # lock node dimensions
        lockNodeDimensions (obj, TRUE)
        
        style <- list(visualProperty = "NODE_SIZE", value = new.size)
        setVisualProperty(obj, style, vizmap.style.name)
})

# ------------------------------------------------------------------------------
setMethod('setDefaultNodeColor', 'CytoscapeConnectionClass', 
  function(obj, new.color, vizmap.style.name='default') {
    if (.isNotHexColor(new.color)){
        return()
    }
    style = list(visualProperty = "NODE_FILL_COLOR", value = new.color)
    setVisualProperty(obj, style, vizmap.style.name)
})

# ------------------------------------------------------------------------------
setMethod('setDefaultNodeBorderColor', 'CytoscapeConnectionClass', 
  function(obj, new.color, vizmap.style.name='default') {
    if (.isNotHexColor(new.color)){
      return()
    }
    style = list(visualProperty = "NODE_BORDER_PAINT", value = new.color)
    setVisualProperty(obj, style, vizmap.style.name)
})

# ------------------------------------------------------------------------------
setMethod ('setDefaultNodeBorderWidth', 'CytoscapeConnectionClass', 
  function(obj, new.width, vizmap.style.name='default') {
    style = list(visualProperty = "NODE_BORDER_WIDTH", value = new.width) 
    setVisualProperty(obj, style, vizmap.style.name)
})

# ------------------------------------------------------------------------------
setMethod('setDefaultNodeFontSize', 'CytoscapeConnectionClass', 
  function(obj, new.size, vizmap.style.name='default') {
    style = list(visualProperty = "NODE_LABEL_FONT_SIZE", value = new.size)
    setVisualProperty(obj, style, vizmap.style.name)
})

# ------------------------------------------------------------------------------
setMethod('setDefaultNodeLabelColor', 'CytoscapeConnectionClass', 
  function(obj, new.color, vizmap.style.name='default') {
    if (.isNotHexColor(new.color)){
      return()
    }      
    style = list(visualProperty = "NODE_LABEL_COLOR", value = new.color)
    setVisualProperty(obj, style, vizmap.style.name)
})

# ------------------------------------------------------------------------------
setMethod('setDefaultEdgeLineWidth', 'CytoscapeConnectionClass', 
  function(obj, new.width, vizmap.style.name='default') {
    style = list(visualProperty = "EDGE_WIDTH", value = new.width) 
    setVisualProperty(obj, style, vizmap.style.name)
})

# ------------------------------------------------------------------------------
setMethod('setDefaultEdgeColor', 'CytoscapeConnectionClass', 
  function(obj, new.color, vizmap.style.name='default') {
    if (.isNotHexColor(new.color)){
      return()
    }
     # TODO Comment Tanja: maybe change to EDGE_UNSELECTED_PAINT
    style = list(visualProperty = "EDGE_STROKE_UNSELECTED_PAINT", value = new.color) 
    setVisualProperty(obj, style, vizmap.style.name)
})

setMethod('setDefaultEdgeSourceArrowColor', 'CytoscapeConnectionClass', 
          function(obj, new.color, vizmap.style.name='default') {
              if (.isNotHexColor(new.color)){
                  return()
              }
              style = list(visualProperty = "EDGE_SOURCE_ARROW_UNSELECTED_PAINT", value = new.color) 
              setVisualProperty(obj, style, vizmap.style.name)
          })

setMethod('setDefaultEdgeTargetArrowColor', 'CytoscapeConnectionClass', 
          function(obj, new.color, vizmap.style.name='default') {
              if (.isNotHexColor(new.color)){
                  return()
              }
              style = list(visualProperty = "EDGE_TARGET_ARROW_UNSELECTED_PAINT", value = new.color) 
              setVisualProperty(obj, style, vizmap.style.name)
          })
# ------------------------------------------------------------------------------
setMethod('setDefaultEdgeFontSize', 'CytoscapeConnectionClass', 
  function(obj, new.size, vizmap.style.name='default') {
    style = list(visualProperty = "EDGE_LABEL_FONT_SIZE", value = new.size)
    setVisualProperty(obj, style, vizmap.style.name)
})

#------------------------------------------------------------------------------------------------------------------------
setMethod ('setNodeShapeRule', 'CytoscapeWindowClass',

    function (obj, node.attribute.name, attribute.values, node.shapes, default.shape='ELLIPSE') {
        id = as.character (obj@window.id)
        
        #TODO the style should be passed as a parameter
        vizmap.style.name = 'default'

        if (!node.attribute.name %in% noa.names (obj@graph)) {
            write (sprintf ('Error in RCy3::setNodeShapeRule. Passed non-existent node attribute: %s', node.attribute.name), stderr ())
            return ()
        }
        
        # ensure correct node shapes
        node.shapes <- toupper(node.shapes)
        unique.node.shapes <- unique(node.shapes)
        wrong.node.shape <- sapply(unique.node.shapes, function(x) !(x %in% getNodeShapes(obj)))
        if (any(wrong.node.shape)){
            write (sprintf('ERROR in RCy3::setNodeShapeRule. You tried to use invalid node shapes. For valid ones use getNodeShapes'), stderr())
            return(NA)
        }

        # set default
        setDefaultNodeShape (obj, default.shape, vizmap.style.name)
        
        # define the column type
        columnType <- findColumnType(typeof(node.shapes[1]))
        
        # discrete mapping
        discreteMapping (obj, node.attribute.name, attribute.values, node.shapes,
                             visual.property="NODE_SHAPE", columnType=columnType, style=vizmap.style.name)
     }) # setNodeShapeRule

#------------------------------------------------------------------------------------------------------------------------
setMethod ('setNodeSizeRule', 'CytoscapeWindowClass',

    function (obj, node.attribute.name, control.points, node.sizes, mode, default.size=40) {
        if (!mode %in% c ('interpolate', 'lookup')) {
            write ("Error! RCy3:setNodeSizeRule.  mode must be 'interpolate' (the default) or 'lookup'.", stderr ())
            return ()
        }

        #TODO Comment TanjaM we should give the user the option to choose the style 
        # as an input parameter which defaults to default.
        vizmap.style.name = 'default'
        
        # define the column type
        columnType <- findColumnType(typeof(control.points[1]))

        # lock node dimensions
        lockNodeDimensions (obj, TRUE)
        
        # set default
        setDefaultNodeSize (obj, default.size, vizmap.style.name)
        
        if (mode=='interpolate') {  # need a 'below' size and an 'above' size.  so there should be two more colors than control.points
            if (length (control.points) == length (node.sizes)) { # caller did not supply 'below' and 'above' values; manufacture them
                node.sizes = c (node.sizes [1], node.sizes, node.sizes [length (node.sizes)])
                write ("RCy3::setNodeSizeRule, no 'below' or 'above' sizes specified.  Inferred from node.sizes.", stderr ())
            }

            good.args = length (control.points) == (length (node.sizes) - 2)
            if (!good.args) {
                write (sprintf ('cp: %d', length (control.points)), stderr ())
                write (sprintf ('co: %d', length (node.sizes)), stderr ())
                write ("Error! RCy3:setNodeSizeRule, interpolate mode.", stderr ())
                write ("Expecting 1 node.size for each control.point, one for 'above' size, one for 'below' size.", stderr ())
                return ()
            }
            continuousMapping (obj, node.attribute.name, control.points, node.sizes,
                               visual.property="NODE_SIZE",
                               columnType=columnType, style=vizmap.style.name)
            
        } # if mode==interpolate

        else { # use a discrete rule, with no interpolation
            good.args = length (control.points) == length (node.sizes)
            if (!good.args) {
                write (sprintf ('cp: %d', length (control.points)), stderr ())
                write (sprintf ('co: %d', length (node.sizes)), stderr ())
                write ("Error! RCy3:setNodeSizeRule. Expecting exactly as many node.sizes as control.points in lookup mode.", stderr ())
                return ()
            }
            discreteMapping(obj, node.attribute.name, control.points, node.sizes,
                            visual.property="NODE_SIZE",
                            columnType=columnType, style=vizmap.style.name)
        } # else: !interpolate, aka lookup
        
    }) # setNodeSizeRule
#
#------------------------------------------------------------------------------------------------------------------------
setMethod ('setEdgeColorRule', 'CytoscapeWindowClass',

    function (obj, edge.attribute.name, control.points, colors, mode="interpolate", default.color='#FFFFFF') {
        if (!mode %in% c ('interpolate', 'lookup')) {
            write ("Error! RCy3:setEdgeColorRule.  mode must be 'interpolate' (the default) or 'lookup'.", stderr ())
            return ()
        }
        
        # check if colors are formatted correctly
        for (color in colors){
            if (.isNotHexColor(color)){
                return()
            } 
        }
        
        #TODO Comment TanjaM we should give the user the option to choose the style 
        # as an input parameter which defaults to default.
        vizmap.style.name = 'default'
        
        #set default
        setDefaultEdgeColor (obj, default.color, vizmap.style.name)
        
        # define the column type
        columnType <- findColumnType(typeof(control.points[1]))
        
        if (mode=='interpolate') {  # need a 'below' color and an 'above' color.  so there should be two more colors than control.points
            if (length (control.points) == length (colors)) { # caller did not supply 'below' and 'above' values; manufacture them
                colors = c (colors [1], colors, colors [length (colors)])
                write ("RCy3::setEdgeColorRule, no 'below' or 'above' colors specified.  Inferred from supplied colors.", stderr ());
            } 
        good.args = length (control.points) == (length (colors) - 2)
        if (!good.args) {
            write (sprintf ('cp: %d', length (control.points)), stderr ())
            write (sprintf ('co: %d', length (colors)), stderr ())
            write ("Error! RCy3:setEdgeColorRule, interpolate mode.", stderr ())
            write ("Expecting 1 color for each control.point, one for 'above' color, one for 'below' color.", stderr ())
            return ()
            }
        
        continuousMapping (obj, edge.attribute.name, control.points, colors,
                           visual.property="EDGE_STROKE_UNSELECTED_PAINT",
                           columnType=columnType, style=vizmap.style.name)
        } # if mode==interpolate
        else { # use a discrete rule, with no interpolation, mode==lookup
        good.args = length (control.points) == length (colors)
        if (!good.args) {
            write (sprintf ('cp: %d', length (control.points)), stderr ())
            write (sprintf ('co: %d', length (colors)), stderr ())
            write ("Error! RCy3:setEdgeColorRule.  Expecting exactly as many colors as control.points in lookup mode.", stderr ())
            return ()
        }
        
        discreteMapping(obj, edge.attribute.name, control.points, colors,
                        visual.property="EDGE_STROKE_UNSELECTED_PAINT",
                        columnType=columnType, style=vizmap.style.name)
        } # else: !interpolate, aka lookup
     }) # setEdgeColorRule

#------------------------------------------------------------------------------------------------------------------------
setMethod ('setEdgeOpacityRule', 'CytoscapeWindowClass',

    function (obj, edge.attribute.name, control.points, opacities, mode) {
        if (!mode %in% c ('interpolate', 'lookup')) {
            write ("Error! RCy3:setEdgeOpacityRule.  mode must be 'interpolate' (the default) or 'lookup'.", stderr ())
            return ()
        }
        
        #TODO Comment TanjaM we should give the user the option to choose the style 
        # as an input parameter which defaults to default.
        vizmap.style.name = 'default'
        
        # set default # Comment TanjaM: Current version does not set default
        #setDefaultEdgeOpacity (obj, default.opacity, vizmap.style.name)
        
        # define the column type
        columnType <- findColumnType(typeof(control.points[1]))
        
        # in a previous Cytoscape version the three elements were set seperately
        #aspects = c ('Edge Opacity', 'Edge Target Arrow Opacity', 'Edge Source Arrow Opacity')

        if (mode=='interpolate') {  # need a 'below' opacity and an 'above' opacity.  so there should be two more opacities than control.points 
            if (length (control.points) == length (opacities)) { # caller did not supply 'below' and 'above' values; manufacture them
                opacities = c (opacities [1], opacities, opacities [length (opacities)])
                write ("RCy3::setEdgeOpacityRule, no 'below' or 'above' colors specified.  Inferred from supplied colors.", stderr ());
            } 
            good.args = length (control.points) == (length (opacities) - 2)
            if (!good.args) {
                write (sprintf ('cp: %d', length (control.points)), stderr ())
                write (sprintf ('co: %d', length (opacities)), stderr ())
                write ("Error! RCy3:setEdgeOpacityRule, interpolate mode.", stderr ())
                write ("Expecting 1 opacity value for each control.point, one for 'above' opacity, one for 'below' opacity.", stderr ())
                return ()
            }
            continuousMapping (obj, edge.attribute.name, control.points, opacities,
                                   visual.property="EDGE_TRANSPARENCY",
                                   columnType=columnType, style=vizmap.style.name)
        } # if mode==interpolate
        else { # use a discrete rule, with no interpolation
            good.args = length (control.points) == length (opacities)
            if (!good.args) {
                write (sprintf ('cp: %d', length (control.points)), stderr ())
                write (sprintf ('co: %d', length (opacities)), stderr ())
                write ("Error! RCy3:setEdgeColorRule.  Expecting exactly as many opacities as control.points in lookup mode.", stderr ())
                return ()
            }
            discreteMapping(obj, edge.attribute.name, control.points, opacities,
                            visual.property="EDGE_TRANSPARENCY",
                            columnType=columnType, style=vizmap.style.name)
        } # else: !interpolate
     }) # setEdgeColorRule

#------------------------------------------------------------------------------------------------------------------------
setMethod ('setEdgeLineStyleRule', 'CytoscapeWindowClass',

    function (obj, edge.attribute.name, attribute.values, line.styles, default.style='SOLID') {
        id = as.character (obj@window.id)
        #TODO the style should be passed as a parameter
        vizmap.style.name = 'default'
        
        if (!edge.attribute.name %in% eda.names (obj@graph)) {
            write (sprintf ('warning!  setEdgeLineStyleRule passed non-existent node attribute: %s', edge.attribute.name), stderr ())
            return ()
        }
        
        # ensure correct values
        line.styles <- toupper(line.styles)
        unique.values <- unique(line.styles)
        wrong.values <- sapply(unique.values, function(x) !(x %in% getLineStyles(obj)))
        if (any(wrong.values)){
            write (sprintf ('ERROR in RCy3::setEdgeLineStyleRule. Invalid value. For valid values use getLineStyles'), stderr ())
            return(NA)
        }
        
        # set default
        default.style.list <- list(visualProperty = "EDGE_LINE_TYPE", value = default.style)
        setVisualProperty(obj, default.style.list, vizmap.style.name)
        
        # define the column type
        columnType <- findColumnType(typeof(line.styles[1]))
        
        # discrete mapping
        discreteMapping (obj, edge.attribute.name, attribute.values, line.styles,
                         visual.property="EDGE_LINE_TYPE", columnType=columnType, style=vizmap.style.name)
     }) # setEdgeLineStyleRule

#------------------------------------------------------------------------------------------------------------------------
setMethod ('setEdgeLineWidthRule', 'CytoscapeWindowClass',

    function (obj, edge.attribute.name, attribute.values, line.widths, default.width=1) {
        id = as.character (obj@window.id)
        #TODO the style should be passed as a parameter
        vizmap.style.name = 'default'
        
        if (!edge.attribute.name %in% eda.names (obj@graph)) {
            write (sprintf ('Warning! setEdgeLineWidthRule passed non-existent node attribute: %s', edge.attribute.name), stderr ())
            return ()
        }
        
        # set default
        default.width.list <- list(visualProperty = "EDGE_WIDTH", value = default.width)
        setVisualProperty(obj, default.width.list, vizmap.style.name)
        
        # define the column type
        columnType <- findColumnType(typeof(line.widths[1]))
        #columnType <- 'String'
        
        # discrete mapping
        discreteMapping (obj, edge.attribute.name, attribute.values, line.widths,
                         visual.property="EDGE_WIDTH", columnType=columnType, style=vizmap.style.name)
     })

#------------------------------------------------------------------------------------------------------------------------
setMethod ('setEdgeTargetArrowRule', 'CytoscapeWindowClass', 

    function (obj, edge.attribute.name, attribute.values, arrows, default='ARROW') {
        id = as.character (obj@window.id)
        #TODO the style should be passed as a parameter
        vizmap.style.name = 'default'
        
        if (!edge.attribute.name %in% eda.names (obj@graph)) {
            write (sprintf ('Warning! setEdgeTargetArrowRule passed non-existent node attribute: %s', edge.attribute.name), stderr ())
            return ()
        }
        
        # set default
        default.style.list <- list(visualProperty = "EDGE_TARGET_ARROW_SHAPE", value = default)
        setVisualProperty(obj, default.style.list, vizmap.style.name)
        
        # define the column type
        columnType <- findColumnType(typeof(arrows[1]))
        
        # discrete mapping
        discreteMapping (obj, edge.attribute.name, attribute.values, arrows,
                         visual.property="EDGE_TARGET_ARROW_SHAPE", columnType=columnType, style=vizmap.style.name)
     }) # setTargetArrowRule

#------------------------------------------------------------------------------------------------------------------------
setMethod ('setEdgeSourceArrowRule', 'CytoscapeWindowClass', 

    function (obj, edge.attribute.name, attribute.values, arrows, default='ARROW') {
        id = as.character (obj@window.id)
        #TODO the style should be passed as a parameter
        vizmap.style.name = 'default'
        
        if (!edge.attribute.name %in% eda.names (obj@graph)) {
            write (sprintf ('warning!  setEdgeSourceArrowRule passed non-existent node attribute: %s', edge.attribute.name), stderr ())
            return ()
        }
        
        # set default
        default.style.list <- list(visualProperty = "EDGE_SOURCE_ARROW_SHAPE", value = default)
        setVisualProperty(obj, default.style.list, vizmap.style.name)
        
        # define the column type
        columnType <- findColumnType(typeof(arrows[1]))
        
        # discrete mapping
        discreteMapping (obj, edge.attribute.name, attribute.values, arrows,
                         visual.property="EDGE_SOURCE_ARROW_SHAPE", columnType=columnType, style=vizmap.style.name)
    }) # setTargetArrowRule