package org.cytoscape.rest.internal.resource;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import javax.inject.Singleton;
import javax.validation.constraints.NotNull;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.InternalServerErrorException;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import org.cytoscape.application.swing.CySwingApplication;
import org.cytoscape.application.swing.CytoPanel;
import org.cytoscape.application.swing.CytoPanelName;
import org.cytoscape.application.swing.CytoPanelState;
import org.cytoscape.rest.internal.CyActivator.LevelOfDetails;
import org.cytoscape.work.TaskIterator;
import org.cytoscape.work.TaskMonitor;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.sym.Name;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
@Singleton
@Path("/v1/ui")
public class UIResource extends AbstractResource {
@Context
@NotNull
protected CySwingApplication desktop;
@Context
@NotNull
protected LevelOfDetails detailsTF;
@Context
@NotNull
private TaskMonitor headlessTaskMonitor;
/**
*
* @summary Get status of Desktop
*
* @return An object with isDesktopAvailable field.
* This value is true if Cytoscape Desktop is available.
* And it is false if Cytoscape is running in headless mode (not available yet).
*
*/
@GET
@Path("/")
@Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
public Map<String, Boolean> getDesktop() {
final Map<String, Boolean> status = new HashMap<>();
boolean desktopAvailable = false;
if(desktop != null) {
desktopAvailable = true;
}
status.put("isDesktopAvailable", desktopAvailable);
return status;
}
/**
*
* Switch between full graphics details <---> fast rendering mode.
*
* @summary Toggle level of graphics details (LoD).
*
* @return Success message.
*/
@PUT
@Path("/lod")
@Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
public Response updateLodState() {
final TaskIterator lod = detailsTF.getLodTF().createTaskIterator(null);
try {
lod.next().run(headlessTaskMonitor);
} catch (Exception e) {
throw getError("Could not toggle LOD.", e,
Response.Status.INTERNAL_SERVER_ERROR);
}
return Response.status(Response.Status.OK)
.type(MediaType.APPLICATION_JSON)
.entity("{\"message\":\"Toggled Graphics level of details.\"}").build();
}
/**
* The return value will includes status of all CytoPanels.
* Each entry includes:
*
* <ul>
* <li>
* name: Official name of the CytoPanel:
* <ul>
* <li>SOUTH</li>
* <li>EAST</li>
* <li>WEST</li>
* <li>SOUTH_WEST</li>
* </ul>
* </li>
* <li>
* state: State of the CytoPanel:
* <ul>
* <li>FLOAT</li>
* <li>DOCK</li>
* <li>HIDE</li>
* </ul>
* </li>
* </ul>
*
* @summary Get status of all CytoPanels
*
* @return Panel status as an array
*/
@GET
@Path("/panels")
@Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
public List<Map<String, String>> getAllPanelStatus() {
try {
return Arrays.asList(CytoPanelName.values()).stream()
.map(panelName->desktop.getCytoPanel(panelName))
.map(panel->getMap(panel))
.collect(Collectors.toList());
} catch(Exception ex) {
throw getError("Could not getpanel status", ex, Status.INTERNAL_SERVER_ERROR);
}
}
private final Map<String, String> getMap(final CytoPanel panel) {
final Map<String, String> values = new HashMap<>();
values.put("name", panel.getCytoPanelName().name());
values.put("state", panel.getState().name());
return values;
}
/**
*
* @summary Get status of a CytoPanel
*
* @param panelName official name of the CytroPanel
*
* @return Status of the CytoPanel (name-state pair)
*/
@GET
@Path("/panels/{panelName}")
@Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
public Response getPanelStatus(@PathParam("panelName") String panelName) {
final CytoPanelName panel = CytoPanelName.valueOf(panelName);
System.out.println(panel);
if(panel == null) {
return Response.status(Response.Status.NOT_FOUND).build();
}
final CytoPanel panelObject = desktop.getCytoPanel(panel);
return Response.ok(getMap(panelObject)).build();
}
/**
*
* You can update multiple panel states at once.
* Body of your request should have same format as the return value of GET method.
*
* @summary Update CytoPanel states
*
* @return Response 200 if success
*/
@PUT
@Path("/panels")
@Consumes(MediaType.APPLICATION_JSON)
public Response updatePanelStatus(final InputStream is) {
final ObjectMapper objMapper = new ObjectMapper();
JsonNode rootNode = null;
try {
rootNode = objMapper.readValue(is, JsonNode.class);
} catch (IOException e) {
throw new InternalServerErrorException("Could not parse input JSON.", e);
}
for (final JsonNode entry : rootNode) {
final JsonNode panelName = entry.get("name");
final JsonNode panelStatus = entry.get("state");
if(panelName == null || panelStatus == null) {
throw new IllegalArgumentException("Imput parameters are missing.");
}
final CytoPanelName panel = CytoPanelName.valueOf(panelName.asText());
if(panel == null) {
throw new IllegalArgumentException("Could not find panel: " + panelName.asText());
}
final CytoPanelState state = CytoPanelState.valueOf(panelStatus.asText());
if(state == null) {
throw new IllegalArgumentException("Invalid Panel State: " + panelStatus.asText());
}
final CytoPanel panelObject = desktop.getCytoPanel(panel);
panelObject.setState(state);
}
return Response.ok().build();
}
}