All Downloads are FREE. Search and download functionalities are using the official Maven repository.

prerna.reactor.export.GraphFormatter Maven / Gradle / Ivy

The newest version!
package prerna.reactor.export;

import java.awt.Color;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;

import org.apache.commons.lang.ArrayUtils;

import prerna.engine.api.IHeadersDataRow;
import prerna.ui.helpers.TypeColorShapeTable;
import prerna.util.ArrayUtilityMethods;
import prerna.util.Constants;

public class GraphFormatter extends AbstractFormatter {

	// the nodes list to return
	private List nodesMapList;
	// the edges list to return
	private List edgesMapList;

	/*
	 * These are the options that the FE can define for graph output
	 * 1) connectionsMap
	 * 		Example : {upstreamNode1 -> [downstreamNode1 , downstreamnode2, .. etc]
	 * 		This defines the up node to its list of downnodes
	 * 		The same node can be an up node in some situations and down nodes in orders
	 * 
	 * 2) nodePropertiesMap
	 * 		Example : {node -> [prop1, prop2, ... propN]
	 * 		This defines the properties to add into the prop hash for a given node
	 * 
	 * 3) edgePropertiesMap
	 * 		Example: {upNode1.downNode1 -> [prop1, prop2, ... propN]
	 * 		This defines the properties to put inbetween a given relationship that 
	 * 		must already be defined in teh connectionsMap
	 * 
	 */
	private Map> connectionsMap;
	private Map> nodePropertiesMap;
	private Map> edgePropertiesMap;
	private Map colorsMap;
	private List nodeList;
	private Map aliasMap;

	// this is used to make sure we do not add vertices twice
	protected Map> vertLabelUniqueValues;

	private List indexConnections;

	// used for edge map
	public static final String EDGES = "edges";
	private static final String SOURCE = "source";
	private static final String TARGET = "target";

	// used for node map
	public static final String NODES = "nodes";
	private static final String VERTEX_TYPE_PROPERTY = "VERTEX_TYPE_PROPERTY";
	private static final String VERTEX_COLOR_PROPERTY = "VERTEX_COLOR_PROPERTY";
	private static final String VERTEX_LABEL_PROPERTY = "VERTEX_LABEL_PROPERTY";

	private static final String PROP_HASH = "propHash";
	public static final String GRAPH_META = "graphMeta";
	static final String URI = "uri";

	public GraphFormatter() {
		this.nodesMapList = new ArrayList();
		this.edgesMapList = new ArrayList();
		this.vertLabelUniqueValues = new HashMap>();
	}

	@Override
	public void addData(IHeadersDataRow nextData) {
		String[] headers = nextData.getHeaders();
		// also get the raw headers and see if you can try it
		String[] rawHeaders = nextData.getRawHeaders();
		Object[] values = nextData.getValues();
		if (this.indexConnections == null) {
			determineConnectionsIndex(headers, rawHeaders);
		}
		
		// process the nodes
		processNodes(headers, values);
		// process the relationships
		// if no connections, dont do any of this
		if(this.indexConnections != null) {
			processRelationships(headers, values);
		}
	}

	private void processNodes(String[] headers, Object[] values) {
		// add the node information
		
		// If node list is not provided, use all headers as nodes
		if(nodeList == null) {
			nodeList = Arrays.asList(headers);
		}
		for (int i = 0; i < nodeList.size(); i++) {
			String vertexType = nodeList.get(i);
			Object vertexLabel = values[ArrayUtils.indexOf(headers, vertexType)];
			if(vertexLabel == null) {
				continue;
			}
			vertexType = getVertexType(vertexType);
			String uri = vertexType + "/" + vertexLabel;

			// only process new nodes once
			if(alreadyProcessedId(this.nodesMapList, uri)) {
				continue;
			}
			
			// store the meta data around each node
			// and also ensure we do not add nodes twice unnecessarily
			if (this.vertLabelUniqueValues.containsKey(vertexType)) {
				Set processedNodes = (Set) this.vertLabelUniqueValues.get(vertexType);
				if (!processedNodes.contains(vertexLabel.toString())) {
					processedNodes.add(vertexLabel.toString());
				}
			} else {
				Set processedNodes = new HashSet();
				processedNodes.add(vertexLabel.toString());
				this.vertLabelUniqueValues.put(vertexType, processedNodes);
			}

			GraphFormatterMap nodeMap = new GraphFormatterMap();
			Color color = (this.colorsMap != null && this.colorsMap.get(vertexType) != null) 
					? this.colorsMap.get(vertexType)
					: TypeColorShapeTable.getInstance().getColor(vertexType, vertexLabel.toString());
			nodeMap.put(Constants.VERTEX_COLOR, getRgb(color));
			nodeMap.put(Constants.VERTEX_TYPE, vertexType);
			nodeMap.put(Constants.VERTEX_NAME, vertexLabel);
			nodeMap.put(URI, uri);

			Map propHash = new HashMap();
			if (this.nodePropertiesMap != null && !this.nodePropertiesMap.isEmpty()) {
				if (nodePropertiesMap.containsKey(vertexType)) {
					List propertyTypes = nodePropertiesMap.get(vertexType);
					for (String property : propertyTypes) {
						int propertyIndex = ArrayUtilityMethods.arrayContainsValueAtIndex(headers, property);
						if (propertyIndex < values.length) {
							propHash.put(property, values[propertyIndex]);
						}
					}

				}
			}
			nodeMap.put(PROP_HASH, propHash);
			this.nodesMapList.add(nodeMap);
		}
	}

	private void processRelationships(String[] headers, Object[] values) {
		// add the relationship information

		// we will use the index connections to determine the header locations
		// instead of calculating this every time
		if (this.indexConnections != null && !this.indexConnections.isEmpty()) {
			for (Integer[] index : indexConnections) {
				GraphFormatterMap edgeMap = new GraphFormatterMap();
				int upHeaderIndex = index[0];
				int downHeaderIndex = index[1];
				if (upHeaderIndex >= 0 && downHeaderIndex >= 0) {
					Object sValue = values[upHeaderIndex];
					if(sValue == null) {
						continue;
					}
					Object tValue = values[downHeaderIndex];
					if(tValue == null) {
						continue;
					}
					String source = getVertexType(headers[upHeaderIndex]) + "/" + sValue;
					String target = getVertexType(headers[downHeaderIndex]) + "/" + tValue;
					String uri = source + ":" + target;
					edgeMap.put(SOURCE, source);
					edgeMap.put(TARGET, target);
					edgeMap.put(URI, uri);
					
					// only process new edges
					if(alreadyProcessedId(this.edgesMapList, uri)) {
						continue;
					}

					// Add relationship properties col.col = ["col"]
					Map propHash = new HashMap();
					if (edgePropertiesMap != null && !edgePropertiesMap.isEmpty()) {
						for (String edgeLabel : edgePropertiesMap.keySet()) {
							// validate syntax col.col
							if (edgeLabel.contains(".")) {
								String[] split = edgeLabel.split("\\.");
								if (split.length > 0) {
									String startNode = split[0];
									String endNode = split[1];
									// check if edge exists in connections
									int startNodeIndex = ArrayUtilityMethods.arrayContainsValueAtIndex(headers, startNode);
									int endNodeIndex = ArrayUtilityMethods.arrayContainsValueAtIndex(headers, endNode);
									if (validEdgeLabel(startNodeIndex, endNodeIndex)) {
										List properties = edgePropertiesMap.get(edgeLabel);
										for (String property : properties) {
											int propertyIndex = ArrayUtilityMethods.arrayContainsValueAtIndex(headers, property);
											propHash.put(property, values[propertyIndex]);
										}
									}

								}
							}

						}
					}

					edgeMap.put(PROP_HASH, propHash);
					this.edgesMapList.add(edgeMap);
				}
			}
		}
	}

	private boolean validEdgeLabel(int startNodeIndex, int endNodeIndex) {
		if (startNodeIndex >= 0 && endNodeIndex >= 0) {
			for (Integer[] edge : indexConnections) {
				int edgeStartIndex = edge[0].intValue();
				int edgeEndIndex = edge[1].intValue();
				if (edgeStartIndex == startNodeIndex && edgeEndIndex == endNodeIndex) {
					return true;
				}
			}
		}
		return false;
	}
	
	private String getVertexType(String vertexType) {
		if(this.aliasMap != null && this.aliasMap.containsKey(vertexType)) {
			return this.aliasMap.get(vertexType);
		}
		// cant find it, return the original
		return vertexType;
	}

	/**
	 * Gets graph metadata
	 * "col" : unique instance count
	 */
	protected HashMap getGraphMeta() {
		HashMap meta = new HashMap();
		for(String vertexType : vertLabelUniqueValues.keySet()) {
			Set values = vertLabelUniqueValues.get(vertexType);
			meta.put(vertexType, values.size());
		}
		return meta;
	}

	private void determineConnectionsIndex(String[] headers, String [] rawHeaders) {
		// loop through and find the indices to grab for each connection we want
		this.indexConnections = new ArrayList();
		if (connectionsMap != null && !this.connectionsMap.isEmpty()) {
			for (String upstreamHeader : this.connectionsMap.keySet()) {
				// find the up header index
				int upHeaderIndex = ArrayUtilityMethods.arrayContainsValueAtIndex(headers, upstreamHeader);
				if(upHeaderIndex < 0)
					upHeaderIndex = ArrayUtilityMethods.arrayContainsValueAtIndex(rawHeaders, upstreamHeader);
				List downstreamHeaderList = this.connectionsMap.get(upstreamHeader);
				for (String downstreamHeader : downstreamHeaderList) {
					// find the down header index
					int downHeaderIndex = ArrayUtilityMethods.arrayContainsValueAtIndex(headers, downstreamHeader);
					// try it in raw headers as well
					if(downHeaderIndex < 0)
						downHeaderIndex = ArrayUtilityMethods.arrayContainsValueAtIndex(rawHeaders, downstreamHeader);

					this.indexConnections.add(new Integer[] { upHeaderIndex, downHeaderIndex });
				}
			}
		}
	}

	@Override
	public Object getFormattedData() {
		Map formattedData = new HashMap();
		formattedData.put(NODES, nodesMapList);
		formattedData.put(EDGES, edgesMapList);
		formattedData.put(GRAPH_META, getGraphMeta());
		return formattedData;
	}

	@Override
	public void clear() {
		this.nodesMapList.clear();
		this.edgesMapList.clear();
	}

	@Override
	public String getFormatType() {
		return "GRAPH";
	}

	@SuppressWarnings("unchecked")
	@Override
	public void setOptionsMap(Map optionsMap) {
		super.setOptionsMap(optionsMap);
		String connections = (String) this.optionsMap.get("connections");
		if (connections != null && !connections.isEmpty()) {
			this.connectionsMap = generateEdgeHashFromStr(connections);
		}
		String nodeProperties = (String) this.optionsMap.get("nodeProperties");
		if (nodeProperties != null && !nodeProperties.isEmpty()) {
			this.nodePropertiesMap = generateEdgeHashFromStr(nodeProperties);
		}
		String edgeProperties = (String) this.optionsMap.get("edgeProperties");
		if (edgeProperties != null && !edgeProperties.isEmpty()) {
			this.edgePropertiesMap = generateEdgeHashFromStr(edgeProperties);
		}
		String alias = (String) this.optionsMap.get("alias");
		if(alias != null && !alias.isEmpty()) {
			this.aliasMap = generateAliasMapFromStr(alias);
		}
		String nodes = (String) this.optionsMap.get("nodes");
		if (nodes != null && !nodes.isEmpty()) {
			this.nodeList = generateNodeListFromStr(nodes);
		}
		Object colors = this.optionsMap.get("colors");
		if (colors != null) {
			this.colorsMap = (Map) colors;
		}
	}
	
	private List generateNodeListFromStr(String nodes)
	{
		//Generates list of nodes from string option
		List nList = new ArrayList();
		nList = Arrays.asList(nodes.split(";"));
		return nList;
	}
	
	private Map generateAliasMapFromStr(String aliasStr) {
		Map aliasMap = new Hashtable();
		// example string is UpSys.System;DownSys.System
		// we split on ";"
		// [UpSys.System   ,   DownSys.System]
		// then we split on "."
		// and we know the matching is
		// UpSys -> System
		// and
		// DownSys -> System
		String[] aliasArr = aliasStr.split(";");
		for(String aliasPair : aliasArr) {
			if(aliasPair.contains(".")) {
				String[] aliasPairArr = aliasPair.split("\\.");
				aliasMap.put(aliasPairArr[0], aliasPairArr[1]);
			}
		}
		return aliasMap;
	}

	public static Map> generateEdgeHashFromStr(String edgeHashStr) {
		Map> edgeHash = new Hashtable>();
		// each path is separated by a semicolon
		String[] paths = edgeHashStr.split(";");
		for(String path : paths) {
			if(path.contains(".")) {
				String[] pathVertex = path.split("\\.");
				// we start at index 1 and take the index prior for ease of looping
				for(int i = 1; i < pathVertex.length; i++) {
					String startNode = pathVertex[i-1];
					if(startNode.contains("__")) {
						startNode = startNode.split("__")[1];
					}
					String endNode = pathVertex[i];
					if(endNode.contains("__")) {
						endNode = endNode.split("__")[1];
					}
					
					// update the edge hash correctly
					Set downstreamNodes = null;
					Vector list = new Vector();

					if (edgeHash.containsKey(startNode)) {
						downstreamNodes = new HashSet(edgeHash.get(startNode));
						downstreamNodes.add(endNode);
					} else {
						downstreamNodes = new HashSet();
						downstreamNodes.add(endNode);
					}
					list.addAll(downstreamNodes);
					edgeHash.put(startNode, list);
				}
			} else {
				// ugh... when would this happen?
			}
		}
		return edgeHash;
	}
	
	private String getRgb(Color c) {
		return c.getRed() + "," + c.getGreen() + "," +c.getBlue();
	}
	
	private boolean alreadyProcessedId(List list, String newId) {
		int size = list.size();
		for(int i = 0; i < size; i++) {
			if(list.get(i).equals(newId)) {
				return true;
			}
		}
		return false;
	}
}

// so we can compare the string uri to the map we are passing in
class GraphFormatterMap extends HashMap {
	
	@Override
	public boolean equals(Object o) {
		if(o instanceof String) {
			if(this.containsKey(GraphFormatter.URI)) {
				if(o.equals(this.get(GraphFormatter.URI))) {
					return true;
				} else {
					return false;
				}
			}
		}
		return super.equals(o);
	}
	
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy