package org.cytoscape.rest.internal.datamapper;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import javax.ws.rs.core.Response;
import org.cytoscape.model.CyEdge;
import org.cytoscape.model.CyIdentifiable;
import org.cytoscape.model.CyNetwork;
import org.cytoscape.model.CyNode;
import org.cytoscape.rest.internal.MappingFactoryManager;
import org.cytoscape.view.model.View;
import org.cytoscape.view.model.VisualLexicon;
import org.cytoscape.view.model.VisualProperty;
import org.cytoscape.view.vizmap.VisualMappingFunction;
import org.cytoscape.view.vizmap.VisualMappingFunctionFactory;
import org.cytoscape.view.vizmap.VisualPropertyDependency;
import org.cytoscape.view.vizmap.VisualStyle;
import org.cytoscape.view.vizmap.VisualStyleFactory;
import org.cytoscape.view.vizmap.mappings.BoundaryRangeValues;
import org.cytoscape.view.vizmap.mappings.ContinuousMapping;
import org.cytoscape.view.vizmap.mappings.DiscreteMapping;
import org.cytoscape.view.vizmap.mappings.PassthroughMapping;
import com.fasterxml.jackson.databind.JsonNode;
import com.qmino.miredot.annotations.MireDotIgnore;
@MireDotIgnore
public class VisualStyleMapper {
private static final String TITLE = "title";
private static final String MAPPINGS = "mappings";
private static final String DEFAULTS = "defaults";
public static final String MAPPING_TYPE = "mappingType";
private static final String MAPPING_DISCRETE = "discrete";
private static final String MAPPING_PASSTHROUGH = "passthrough";
private static final String MAPPING_CONTINUOUS = "continuous";
public static final String MAPPING_COLUMN = "mappingColumn";
public static final String MAPPING_COLUMN_TYPE = "mappingColumnType";
public static final String MAPPING_VP = "visualProperty";
private static final String MAPPING_DISCRETE_MAP = "map";
private static final String MAPPING_DISCRETE_KEY = "key";
private static final String MAPPING_DISCRETE_VALUE = "value";
public static final String VP_DEPENDENCY = "visualPropertyDependency";
public static final String VP_DEPENDENCY_ENABLED = "enabled";
public VisualStyle buildVisualStyle(final MappingFactoryManager factoryManager, final VisualStyleFactory factory,
final VisualLexicon lexicon, final JsonNode rootNode) {
final JsonNode title = rootNode.get(TITLE);
final VisualStyle style = factory.createVisualStyle(title.textValue());
final JsonNode defaults = rootNode.get(DEFAULTS);
final JsonNode mappings = rootNode.get(MAPPINGS);
if(defaults != null) {
parseDefaults(defaults, style, lexicon);
}
if(mappings != null) {
parseMappings(mappings, style, lexicon, factoryManager);
}
return style;
}
public void buildMappings(final VisualStyle style, final MappingFactoryManager factoryManager,
final VisualLexicon lexicon, final JsonNode mappings) {
parseMappings(mappings, style, lexicon, factoryManager);
}
public void updateStyleName(final VisualStyle style,
final VisualLexicon lexicon, final JsonNode rootNode) {
final String newTitle = rootNode.get(TITLE).textValue();
style.setTitle(newTitle);
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private final void parseDefaults(final JsonNode defaults, final VisualStyle style, final VisualLexicon lexicon) {
for (final JsonNode vpNode : defaults) {
String vpName = vpNode.get(MAPPING_VP).textValue();
final VisualProperty vp = getVisualProperty(vpName, lexicon);
final JsonNode value = vpNode.get("value");
if (vp == null || value == null ) {
continue;
}
Object parsedValue = null;
if(value.isTextual()) {
parsedValue = vp.parseSerializableString(value.asText());
} else {
parsedValue = vp.parseSerializableString(value.toString());
}
style.setDefaultValue(vp, parsedValue);
}
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private final void parseMappings(JsonNode mappings, VisualStyle style, VisualLexicon lexicon,
MappingFactoryManager factoryManager) {
for (final JsonNode mapping : mappings) {
final String type = mapping.get(MAPPING_TYPE).textValue();
final String column = mapping.get(MAPPING_COLUMN).textValue();
final String colType = mapping.get(MAPPING_COLUMN_TYPE).textValue();
final String vpName = mapping.get(MAPPING_VP).textValue();
final VisualProperty vp = getVisualProperty(vpName, lexicon);
final Class<?> columnType = MapperUtil.getColumnClass(colType);
if (vp == null || columnType == null) {
continue;
}
VisualMappingFunction newMapping = null;
if (type.equals(MAPPING_DISCRETE)) {
final VisualMappingFunctionFactory factory = factoryManager.getFactory(DiscreteMapping.class);
newMapping = parseDiscrete(column, columnType, vp, factory, mapping.get(MAPPING_DISCRETE_MAP));
} else if (type.equals(MAPPING_CONTINUOUS)) {
final VisualMappingFunctionFactory factory = factoryManager.getFactory(ContinuousMapping.class);
newMapping = parseContinuous(column, columnType, vp, factory, mapping);
} else if (type.equals(MAPPING_PASSTHROUGH)) {
final VisualMappingFunctionFactory factory = factoryManager.getFactory(PassthroughMapping.class);
newMapping = parsePassthrough(column, columnType, vp, factory);
}
if (newMapping != null) {
if(style.getVisualMappingFunction(vp) != null) {
style.removeVisualMappingFunction(vp);
}
style.addVisualMappingFunction(newMapping);
}
}
}
@SuppressWarnings("rawtypes")
private final VisualProperty getVisualProperty(String vpName, VisualLexicon lexicon) {
VisualProperty vp = null;
if (vpName.startsWith("NODE")) {
vp = lexicon.lookup(CyNode.class, vpName);
} else if (vpName.startsWith("EDGE")) {
vp = lexicon.lookup(CyEdge.class, vpName);
} else if (vpName.startsWith("NETWORK")) {
vp = lexicon.lookup(CyNetwork.class, vpName);
}
return vp;
}
private final Object parseKeyValue(final Class<?> type, final String value) {
if (type == Double.class) {
return Double.parseDouble(value);
} else if (type == Long.class) {
return Long.parseLong(value);
} else if (type == Integer.class) {
return Integer.parseInt(value);
} else if (type == Float.class) {
return Float.parseFloat(value);
} else if (type == Boolean.class) {
return Boolean.parseBoolean(value);
} else if (type == String.class) {
return value;
} else {
return null;
}
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private final DiscreteMapping parseDiscrete(String columnName, Class<?> type, VisualProperty<?> vp,
VisualMappingFunctionFactory factory, JsonNode discreteMapping) {
DiscreteMapping mapping = (DiscreteMapping) factory.createVisualMappingFunction(columnName, type, vp);
final Map map = new HashMap();
for (JsonNode pair : discreteMapping) {
final Object key = parseKeyValue(type, pair.get(MAPPING_DISCRETE_KEY).textValue());
if (key != null) {
map.put(key, vp.parseSerializableString(pair.get(MAPPING_DISCRETE_VALUE).textValue()));
}
}
mapping.putAll(map);
return mapping;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private final ContinuousMapping parseContinuous(String columnName, Class<?> type, VisualProperty<?> vp,
VisualMappingFunctionFactory factory, JsonNode mappingNode) {
final ContinuousMapping mapping = (ContinuousMapping) factory.createVisualMappingFunction(columnName, type, vp);
for(JsonNode point:mappingNode.get("points")) {
JsonNode val = point.get("value");
JsonNode lesser = point.get("lesser");
JsonNode equal = point.get("equal");
JsonNode greater = point.get("greater");
final BoundaryRangeValues newPoint =
new BoundaryRangeValues(vp.parseSerializableString(lesser.asText()),
vp.parseSerializableString(equal.asText()),
vp.parseSerializableString(greater.asText()));
mapping.addPoint(val.asDouble(), newPoint);
}
return mapping;
}
@SuppressWarnings("rawtypes")
private final PassthroughMapping parsePassthrough(String columnName, Class<?> type, VisualProperty<?> vp,
VisualMappingFunctionFactory factory) {
return (PassthroughMapping) factory.createVisualMappingFunction(columnName, type, vp);
}
/**
*
* Directly update view object.
*
* @param view
* @param rootNode
* @param lexicon
*/
public Response updateView(final View<? extends CyIdentifiable> view, final JsonNode rootNode, final VisualLexicon lexicon) {
for (final JsonNode vpNode : rootNode) {
String vpName = vpNode.get(MAPPING_VP).textValue();
final VisualProperty<?> vp = getVisualProperty(vpName, lexicon);
final JsonNode value = vpNode.get(MAPPING_DISCRETE_VALUE);
if (vp == null || value == null ) {
continue;
}
Object parsedValue = null;
if(value.isTextual()) {
parsedValue = vp.parseSerializableString(value.asText());
} else {
parsedValue = vp.parseSerializableString(value.toString());
}
view.setVisualProperty(vp, parsedValue);
}
return Response.ok().build();
}
public void updateDependencies(final VisualStyle style, final JsonNode rootNode) {
final Set<VisualPropertyDependency<?>> deps = style.getAllVisualPropertyDependencies();
final Map<String, VisualPropertyDependency<?>> names = new HashMap<>();
for(final VisualPropertyDependency<?> dep:deps) {
names.put(dep.getIdString(), dep);
}
for (final JsonNode depNode : rootNode) {
String depId = depNode.get(VisualStyleMapper.VP_DEPENDENCY).textValue();
final VisualPropertyDependency<?> dep = names.get(depId);
if(dep == null) {
continue;
}
final JsonNode enabled = depNode.get("enabled");
if (enabled == null) {
continue;
}
boolean value = enabled.asBoolean();
dep.setDependency(value);
}
}
}