/** * Copyright (C) 2012-2013 Selventa, Inc. * * This file is part of the OpenBEL Framework. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * The OpenBEL Framework is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the OpenBEL Framework. If not, see <http://www.gnu.org/licenses/>. * * Additional Terms under LGPL v3: * * This license does not authorize you and you are prohibited from using the * name, trademarks, service marks, logos or similar indicia of Selventa, Inc., * or, in the discretion of other licensors or authors of the program, the * name, trademarks, service marks, logos or similar indicia of such authors or * licensors, in any marketing or advertising materials relating to your * distribution of the program or any covered product. This restriction does * not waive or limit your obligation to keep intact all copyright notices set * forth in the program as delivered to you. * * If you distribute the program in whole or in part, or any modified version * of the program, and you assume contractual liability to the recipient with * respect to the program or modified version, then you will indemnify the * authors and licensors of the program for any liabilities that these * contractual assumptions directly impose on those licensors and authors. */ package org.openbel.framework.api; import static org.openbel.framework.common.BELUtilities.hasItems; import java.util.Collection; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; import java.util.regex.Pattern; import org.openbel.framework.api.Kam.KamEdge; import org.openbel.framework.api.Kam.KamNode; import org.openbel.framework.api.internal.KAMCatalogDao.KamInfo; import org.openbel.framework.api.internal.KAMStoreDaoImpl.KamProtoEdge; import org.openbel.framework.api.internal.KAMStoreDaoImpl.KamProtoNode; import org.openbel.framework.common.InvalidArgument; import org.openbel.framework.common.enums.FunctionEnum; import org.openbel.framework.common.enums.RelationshipType; /** * * @author julianjray * */ public final class KamImpl extends KamStoreObjectImpl implements Kam { private final KamInfo kamInfo; // Stores each edge by its identifier private Map<Integer, KamEdge> idEdgeMap = new LinkedHashMap<Integer, KamEdge>(); private Map<KamEdge, Integer> edgeIdMap = new LinkedHashMap<KamEdge, Integer>(); // Stores each vertex by its identifier private Map<Integer, KamNode> idNodeMap = new LinkedHashMap<Integer, KamNode>(); private Map<KamNode, Integer> nodeIdMap = new LinkedHashMap<KamNode, Integer>(); // Stores a primary map of edges incident to each source node. private Map<KamNode, Set<KamEdge>> nodeSourceMap = new LinkedHashMap<KamNode, Set<KamEdge>>(); // Stores a primary map of edges incident to each source node. private Map<KamNode, Set<KamEdge>> nodeTargetMap = new LinkedHashMap<KamNode, Set<KamEdge>>(); /** * Defines the hashCode that is calculated when the {@link Kam} is mutated. * The cost of calculating the hash code will be paid while mutating the * {@link Kam} so that we can optimize the analysis of a {@link Kam}. * * <p> * The following mutating operations will rebuild the hashCode: * <ul> * <li>{@link Kam#addNode(KamNode)}</li> * <li>{@link Kam#addEdge(KamEdge)}</li> * <li>{@link Kam#removeNode(KamNode)}</li> * <li>{@link Kam#removeEdge(KamEdge)}</li> * </ul> * </p> */ private int hashCode; /** * * @param kamId * @param kamInfo * @param kamProtoNodeList * @param kamProtoEdgeList */ public KamImpl(KamInfo kamInfo, Collection<KamProtoNode> kamProtoNodes, Collection<KamProtoEdge> kamProtoEdges) throws InvalidArgument { super(kamInfo.getId()); this.kamInfo = kamInfo; if (kamProtoNodes == null) { throw new InvalidArgument("Node list cannot be null"); } if (kamProtoEdges == null) { throw new InvalidArgument("Edge list cannot be null"); } for (KamProtoNode kamProtoNode : kamProtoNodes) { createNode(kamProtoNode.getId(), kamProtoNode.getFunctionType(), kamProtoNode.getLabel()); } // Figure out which nodes we want to create. We only create ones which are attached to edges // and as edges might have been filtered, we need to weed out those nodes which are no longer // connected for (KamProtoEdge kamProtoEdge : kamProtoEdges) { KamNode sourceNode = findNode(kamProtoEdge.getSourceNode().getId()); if (null == sourceNode) { throw new InvalidArgument("Can't find source node."); //sourceNode = createNode(kamProtoEdge.getSourceNode().getId(), kamProtoEdge.getSourceNode().getFunctionType(), kamProtoEdge.getSourceNode().getLabel()); } KamNode targetNode = findNode(kamProtoEdge.getTargetNode().getId()); if (null == targetNode) { throw new InvalidArgument("Can't find target node."); //targetNode = createNode(kamProtoEdge.getTargetNode().getId(), kamProtoEdge.getTargetNode().getFunctionType(), kamProtoEdge.getTargetNode().getLabel()); } createEdge(kamProtoEdge.getId(), sourceNode, kamProtoEdge.getRelationship(), targetNode); } } /** * Returns an empty version of a typed Kam. Will throw an NPE if the kaminfo is null. * * @param kamInfo */ public KamImpl(KamInfo kamInfo) { super(kamInfo.getId()); this.kamInfo = kamInfo; } /** * {@inheritDoc} */ @Override public KamInfo getKamInfo() { return kamInfo; } /** * {@inheritDoc} */ @Override public NodeFilter createNodeFilter() { return new NodeFilter(this.kamInfo); } /** * {@inheritDoc} */ @Override public EdgeFilter createEdgeFilter() { return new EdgeFilter(this.kamInfo); } /** * {@inheritDoc} */ @Override public KamNode findNode(String label) { return findNode(label, null); } /** * {@inheritDoc} */ @Override public KamNode findNode(String label, NodeFilter filter) { for (KamNode kamNode : idNodeMap.values()) { String nodeLabel = kamNode.getLabel(); if (nodeLabel.equalsIgnoreCase(label)) { if (filter != null) { if (!filter.accept(kamNode)) { return null; } } return kamNode; } } return null; } /** * {@inheritDoc} */ @Override public KamNode findNode(Integer kamNodeId) { return findNode(kamNodeId, null); } /** * {@inheritDoc} */ @Override public KamNode findNode(Integer kamNodeId, NodeFilter filter) { final KamNode kamNode = idNodeMap.get(kamNodeId); if (kamNode == null) { return null; } if (filter != null) { if (!filter.accept(kamNode)) { return null; } } return kamNode; } /** * {@inheritDoc} */ @Override public Set<KamNode> findNode(Pattern labelPattern) { return findNode(labelPattern, null); } /** * {@inheritDoc} */ @Override public Set<KamNode> findNode(Pattern labelPattern, NodeFilter filter) { Set<KamNode> results = new LinkedHashSet<KamNode>(); for (KamNode node : idNodeMap.values()) { if (labelPattern.matcher(node.getLabel()).matches()) { // Check for a node filter if (null != filter) { if (!filter.accept(node)) { continue; } } results.add(node); } } return results; } /** * {@inheritDoc} */ @Override public Set<KamNode> getAdjacentNodes(KamNode kamNode) { return getAdjacentNodes(kamNode, EdgeDirectionType.BOTH, null, null); } /** * {@inheritDoc} */ @Override public Set<KamNode> getAdjacentNodes(KamNode kamNode, EdgeDirectionType edgeDirection) { return getAdjacentNodes(kamNode, edgeDirection, null, null); } /** * {@inheritDoc} */ @Override public Set<KamNode> getAdjacentNodes(KamNode kamNode, EdgeDirectionType edgeDirection, EdgeFilter edgeFilter) { return getAdjacentNodes(kamNode, edgeDirection, edgeFilter, null); } /** * {@inheritDoc} */ @Override public Set<KamNode> getAdjacentNodes(KamNode kamNode, EdgeDirectionType edgeDirection, NodeFilter filter) { return getAdjacentNodes(kamNode, edgeDirection, null, filter); } /** * {@inheritDoc} */ @Override public Set<KamNode> getAdjacentNodes(KamNode kamNode, EdgeFilter edgeFilter, NodeFilter nodeFilter) { return getAdjacentNodes(kamNode, EdgeDirectionType.BOTH, edgeFilter, nodeFilter); } /** * {@inheritDoc} */ @Override public Set<KamNode> getAdjacentNodes(KamNode kamNode, EdgeDirectionType edgeDirection, EdgeFilter edgeFilter, NodeFilter nodeFilter) { Set<KamNode> adjacentNodes = new LinkedHashSet<KamNode>(); KamNode node = null; if (EdgeDirectionType.FORWARD == edgeDirection || EdgeDirectionType.BOTH == edgeDirection) { final Set<KamEdge> sources = nodeSourceMap.get(kamNode); if (hasItems(sources)) { for (KamEdge kamEdge : sources) { // Check for an edge filter if (null != edgeFilter) { if (!edgeFilter.accept(kamEdge)) { continue; } } node = kamEdge.getTargetNode(); // Check for a node filter if (null != nodeFilter) { if (!nodeFilter.accept(node)) { continue; } } adjacentNodes.add(node); } } } if (EdgeDirectionType.REVERSE == edgeDirection || EdgeDirectionType.BOTH == edgeDirection) { final Set<KamEdge> targets = nodeTargetMap.get(kamNode); if (hasItems(targets)) { for (KamEdge kamEdge : targets) { // Check for an edge filter if (null != edgeFilter) { if (!edgeFilter.accept(kamEdge)) { continue; } } node = kamEdge.getSourceNode(); // Check for a node filter if (null != nodeFilter) { if (!nodeFilter.accept(node)) { continue; } } adjacentNodes.add(node); } } } return adjacentNodes; } /** * {@inheritDoc} */ @Override public Set<KamEdge> getAdjacentEdges(KamNode kamNode) { return getAdjacentEdges(kamNode, EdgeDirectionType.BOTH, null); } /** * {@inheritDoc} */ @Override public Set<KamEdge> getAdjacentEdges(KamNode kamNode, EdgeFilter filter) { return getAdjacentEdges(kamNode, EdgeDirectionType.BOTH, filter); } /** * {@inheritDoc} */ @Override public Set<KamEdge> getAdjacentEdges(KamNode kamNode, EdgeDirectionType edgeDirection) { return getAdjacentEdges(kamNode, edgeDirection, null); } /** * {@inheritDoc} */ @Override public Set<KamEdge> getAdjacentEdges(KamNode kamNode, EdgeDirectionType edgeDirection, EdgeFilter filter) { Set<KamEdge> adjacentEdges = new LinkedHashSet<KamEdge>(); if (EdgeDirectionType.FORWARD == edgeDirection || EdgeDirectionType.BOTH == edgeDirection) { final Set<KamEdge> sources = nodeSourceMap.get(kamNode); if (hasItems(sources)) { for (KamEdge kamEdge : sources) { // Check for an edge filter if (null != filter) { if (!filter.accept(kamEdge)) { continue; } } adjacentEdges.add(kamEdge); } } } if (EdgeDirectionType.REVERSE == edgeDirection || EdgeDirectionType.BOTH == edgeDirection) { final Set<KamEdge> targets = nodeTargetMap.get(kamNode); if (hasItems(targets)) { for (KamEdge kamEdge : targets) { // Check for an edge filter if (null != filter) { if (!filter.accept(kamEdge)) { continue; } } adjacentEdges.add(kamEdge); } } } return adjacentEdges; } /** * {@inheritDoc} */ @Override public Set<KamEdge> getEdges(KamNode sourceNode, KamNode targetNode) { return getEdges(sourceNode, targetNode, null); } /** * {@inheritDoc} */ @Override public Set<KamEdge> getEdges(KamNode sourceNode, KamNode targetNode, EdgeFilter filter) { Set<KamEdge> edgeSet = new LinkedHashSet<KamEdge>(); // Scan the source node and find all edges which reference the target node for (KamEdge kamEdge : nodeSourceMap.get(sourceNode)) { if (kamEdge.getTargetNode().equals(targetNode)) { // Check for an edge filter if (null != filter) { if (!filter.accept(kamEdge)) { continue; } } edgeSet.add(kamEdge); } } return edgeSet; } /** * {@inheritDoc} */ @Override public KamEdge findEdge(Integer kamEdgeId) { return idEdgeMap.get(kamEdgeId); } /** * {@inheritDoc} */ @Override public KamEdge findEdge(KamNode sourceNode, RelationshipType relationshipType, KamNode targetNode) throws InvalidArgument { // Make sure the nodes are valid for this graph if (!contains(sourceNode)) { throw new InvalidArgument("source Node is not in graph"); } if (!contains(targetNode)) { throw new InvalidArgument("target Node is not in graph"); } // See if the edge already exists. The hash ignores the edgeId KamEdge kamEdge = new KamEdgeImpl(this, -1, sourceNode, relationshipType, targetNode); if (edgeIdMap.keySet().contains(kamEdge)) { Integer kamEdgeId = edgeIdMap.get(kamEdge); kamEdge = idEdgeMap.get(kamEdgeId); } else { // Can't find it in the graph kamEdge = null; } return kamEdge; } /** * {@inheritDoc} */ @Override public boolean contains(KamNode kamNode) { return nodeIdMap.containsKey(kamNode); } /** * {@inheritDoc} */ @Override public boolean contains(KamEdge kamEdge) { return edgeIdMap.containsKey(kamEdge); } /** * {@inheritDoc} */ @Override public KamEdge createEdge(Integer kamEdgeId, KamNode sourceNode, RelationshipType relationshipType, KamNode targetNode) throws InvalidArgument { // Make sure the nodes are valid for this graph if (!contains(sourceNode)) { throw new InvalidArgument("source Node is not in graph"); } if (!contains(targetNode)) { throw new InvalidArgument("target Node is not in graph"); } // See if the edge already exists KamEdge kamEdge = new KamEdgeImpl(this, kamEdgeId, sourceNode, relationshipType, targetNode); if (!edgeIdMap.containsKey(kamEdge)) { // add this edge into the graph addEdge(kamEdge); } return kamEdge; } /** * {@inheritDoc} */ @Override public void removeEdge(final KamEdge kamEdge) { // remove storage of kam edge idEdgeMap.remove(kamEdge.getId()); edgeIdMap.remove(kamEdge); // break edge association with source and target nodes nodeSourceMap.get(kamEdge.getSourceNode()).remove(kamEdge); nodeTargetMap.get(kamEdge.getTargetNode()).remove(kamEdge); // rebuild hashcode this.hashCode = generateHashCode(); } @Override public KamEdge replaceEdge(final KamEdge kamEdge, final KamEdge replacement) { // replace source node final int sourceNodeId = kamEdge.getSourceNode().getId(); // establish id to replacement node idNodeMap.put(sourceNodeId, replacement.getSourceNode()); nodeIdMap.put(replacement.getSourceNode(), sourceNodeId); //replace target node final int targetNodeId = kamEdge.getTargetNode().getId(); // establish id to replacement node idNodeMap.put(targetNodeId, replacement.getTargetNode()); nodeIdMap.put(replacement.getTargetNode(), targetNodeId); final int edgeId = kamEdge.getId(); // establish id to replacement edge idEdgeMap.put(edgeId, replacement); edgeIdMap.put(replacement, edgeId); // reconnect network Set<KamEdge> sourceOutgoing = nodeSourceMap.remove(kamEdge.getSourceNode()); sourceOutgoing.remove(kamEdge); sourceOutgoing.add(replacement); nodeSourceMap.put(replacement.getSourceNode(), sourceOutgoing); Set<KamEdge> targetIncoming = nodeTargetMap.remove(kamEdge.getTargetNode()); targetIncoming.remove(kamEdge); targetIncoming.add(replacement); nodeTargetMap.put(replacement.getTargetNode(), targetIncoming); Set<KamEdge> sourceIncoming = nodeTargetMap.remove(kamEdge.getSourceNode()); nodeTargetMap.put(replacement.getSourceNode(), sourceIncoming); Set<KamEdge> targetOutgoing = nodeSourceMap.remove(kamEdge.getTargetNode()); nodeSourceMap.put(replacement.getTargetNode(), targetOutgoing); return replacement; } /** * {@inheritDoc} */ @Override public KamEdge replaceEdge(KamEdge kamEdge, FunctionEnum sourceFunction, String sourceLabel, RelationshipType relationship, FunctionEnum targetFunction, String targetLabel) { // replace source node final int sourceNodeId = kamEdge.getSourceNode().getId(); final KamNode sourceReplacement = new KamNodeImpl(this, sourceNodeId, sourceFunction, sourceLabel); // establish id to replacement node idNodeMap.put(sourceNodeId, sourceReplacement); nodeIdMap.put(sourceReplacement, sourceNodeId); //replace target node final int targetNodeId = kamEdge.getTargetNode().getId(); final KamNode targetReplacement = new KamNodeImpl(this, targetNodeId, targetFunction, targetLabel); // establish id to replacement node idNodeMap.put(targetNodeId, targetReplacement); nodeIdMap.put(targetReplacement, targetNodeId); final int edgeId = kamEdge.getId(); final KamEdge replacement = new KamEdgeImpl(this, edgeId, sourceReplacement, relationship, targetReplacement); // establish id to replacement edge idEdgeMap.put(edgeId, replacement); edgeIdMap.put(replacement, edgeId); // reconnect network Set<KamEdge> sourceOutgoing = nodeSourceMap.remove(kamEdge.getSourceNode()); sourceOutgoing.remove(kamEdge); sourceOutgoing.add(replacement); nodeSourceMap.put(sourceReplacement, sourceOutgoing); Set<KamEdge> targetIncoming = nodeTargetMap.remove(kamEdge.getTargetNode()); targetIncoming.remove(kamEdge); targetIncoming.add(replacement); nodeTargetMap.put(targetReplacement, targetIncoming); Set<KamEdge> sourceIncoming = nodeTargetMap.remove(kamEdge.getSourceNode()); nodeTargetMap.put(sourceReplacement, sourceIncoming); Set<KamEdge> targetOutgoing = nodeSourceMap.remove(kamEdge.getTargetNode()); nodeSourceMap.put(targetReplacement, targetOutgoing); return replacement; } private void addEdge(KamEdge kamEdge) { // Save the edge idEdgeMap.put(kamEdge.getId(), kamEdge); edgeIdMap.put(kamEdge, kamEdge.getId()); // Index the edge into the node maps nodeSourceMap.get(kamEdge.getSourceNode()).add(kamEdge); nodeTargetMap.get(kamEdge.getTargetNode()).add(kamEdge); // rebuild hashcode this.hashCode = generateHashCode(); } /** * {@inheritDoc} */ @Override public KamNode createNode(Integer id, FunctionEnum functionType, String label) throws InvalidArgument { // See if the node already exists KamNode kamNode = findNode(id); if (null == kamNode) { kamNode = new KamNodeImpl(this, id, functionType, label); // add this node into the graph addNode(kamNode); } else { throw new InvalidArgument("node with id " + id + " already exists in the graph."); } return kamNode; } /** * {@inheritDoc} */ @Override public void removeNode(KamNode kamNode) { idNodeMap.remove(kamNode.getId()); nodeIdMap.remove(kamNode); nodeSourceMap.remove(kamNode); nodeTargetMap.remove(kamNode); // rebuild hashcode this.hashCode = generateHashCode(); } /** * {@inheritDoc} */ @Override public KamNode replaceNode(KamNode kamNode, FunctionEnum function, String label) { final int nodeId = kamNode.getId(); final KamNode replacement = new KamNodeImpl(this, nodeId, function, label); return replaceNode(kamNode, replacement); } /** * {@inheritDoc} */ @Override public KamNode replaceNode(KamNode kamNode, KamNode replacement) { final int nodeId = kamNode.getId(); // establish id to replacement node idNodeMap.put(nodeId, replacement); nodeIdMap.put(replacement, nodeId); // reconnect network Set<KamEdge> egress = nodeSourceMap.remove(kamNode); nodeSourceMap.put(replacement, egress); Set<KamEdge> ingress = nodeTargetMap.remove(kamNode); nodeTargetMap.put(replacement, ingress); this.hashCode = generateHashCode(); return replacement; } /** * {@inheritDoc} */ @Override public void collapseNode(final KamNode from, KamNode to) { // redirect FROM's outgoing edges to TO Set<KamEdge> edges = nodeSourceMap.get(from); Set<KamEdge> dest = nodeSourceMap.get(to); for (final KamEdge e : edges) { ((KamEdgeImpl) e).setSourceNode(to); edges.remove(e); dest.add(e); } // redirect FROM's incoming edges to TO edges = nodeTargetMap.get(from); dest = nodeTargetMap.get(to); for (final KamEdge e : edges) { ((KamEdgeImpl) e).setTargetNode(to); edges.remove(e); dest.add(e); } // remove the FROM node removeNode(from); } private void addNode(KamNode kamNode) { // Add it to the id index idNodeMap.put(kamNode.getId(), kamNode); nodeIdMap.put(kamNode, kamNode.getId()); // Add the node to the primary node-edge map. This stores edges adjacent to the node // where the node is the source node for the edge nodeSourceMap.put(kamNode, new LinkedHashSet<KamEdge>()); // Add the node to the secondary index where the node is the target node for // cached edges nodeTargetMap.put(kamNode, new LinkedHashSet<KamEdge>()); // rebuild hashcode this.hashCode = generateHashCode(); } /** * {@inheritDoc} */ @Override public Collection<KamEdge> getEdges() { return getEdges(null); } /** * {@inheritDoc} */ @Override public Collection<KamNode> getNodes() { return getNodes(null); } /** * {@inheritDoc} */ @Override public Collection<KamNode> getNodes(NodeFilter filter) { Set<KamNode> kamNodes = new LinkedHashSet<KamNode>(); for (KamNode kamNode : idNodeMap.values()) { // Check for a node filter if (null != filter) { if (!filter.accept(kamNode)) { continue; } } kamNodes.add(kamNode); } return kamNodes; } /** * {@inheritDoc} */ @Override public Collection<KamEdge> getEdges(EdgeFilter filter) { Set<KamEdge> kamEdges = new LinkedHashSet<KamEdge>(); for (KamEdge kamEdge : edgeIdMap.keySet()) { // Check for an edge filter if (null != filter) { if (!filter.accept(kamEdge)) { continue; } } kamEdges.add(kamEdge); } return kamEdges; } /** * {@inheritDoc} */ @Override public void union(Collection<KamEdge> kamEdges) throws InvalidArgument { for (KamEdge kamEdge : kamEdges) { if (!this.getKamInfo().equals(kamEdge.getKam().getKamInfo())) { throw new InvalidArgument("Edge does not belong to the KAM"); } // See if the edge already exists if (!edgeIdMap.containsKey(kamEdge)) { // Make sure the nodes are valid for this kam, if not then add them if (!contains(kamEdge.getSourceNode())) { addNode(kamEdge.getSourceNode()); } if (!contains(kamEdge.getTargetNode())) { addNode(kamEdge.getTargetNode()); } // add this edge into the graph addEdge(kamEdge); } } } private int generateHashCode() { final int prime = 31; int result = 1; result = prime * result + ((kamInfo == null) ? 0 : kamInfo.hashCode()); return result; } /** * {@inheritDoc} */ @Override public int hashCode() { return this.hashCode != 0 ? hashCode : generateHashCode(); } /** * {@inheritDoc} */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (!(Kam.class.isAssignableFrom(obj.getClass()))) { return false; } Kam other = (Kam) obj; if (kamInfo == null) { if (other.getKamInfo() != null) { return false; } } else if (!kamInfo.equals(other.getKamInfo())) { return false; } return true; } /** * {@link KamEdge} represents an edge / assertion in the {@link Kam kam} * and is represented by the triple: * * <pre> * [source node, relationship, object node] * </pre> * * @author julianjray */ public final class KamEdgeImpl extends KamElementImpl implements KamEdge { private KamNode sourceNode; private KamNode targetNode; private RelationshipType relationshipType; /** * Precalculate the kam edge hash code since the object is immutable. */ private int hashCode; /** * Precalculate the {@link KamEdge kam edge} label since the object * is immutable. */ private String label; /** * Private constructor to create a {@link KamEdge kam edge}. * * @param kam {@link Kam}, the kam that contains this edge * @param kamEdgeId {@link Integer}, the kam edge id * @param sourceNode {@link KamNode}, the kam source node of this edge * @param relationshipType {@link RelationshipType}, the relationship * of this edge * @param targetNode {@link KamNode}, the kam target node of this edge */ private KamEdgeImpl(Kam kam, Integer kamEdgeId, KamNode sourceNode, RelationshipType relationshipType, KamNode targetNode) { super(kam, kamEdgeId); this.sourceNode = sourceNode; this.relationshipType = relationshipType; this.targetNode = targetNode; this.hashCode = generateHashCode(); this.label = generateLabel(); } /** * Retrieve the {@link KamNode source node} of this edge. * * @return the {@link KamNode source node} */ @Override public KamNode getSourceNode() { return sourceNode; } /** * Changes the {@link KamNode source node} for this * {@link KamEdge edge}. The {@link KamEdgeImpl#hashCode} and * {@link KamEdgeImpl#label} is then regenerated to preserve the * identity. * * @param sourceNode {@link KamNode} new node */ private void setSourceNode(final KamNode sourceNode) { this.sourceNode = sourceNode; this.hashCode = generateHashCode(); this.label = generateLabel(); } /** * Retrieve the {@link KamNode target node} of this edge. * * @return the {@link KamNode target node} */ @Override public KamNode getTargetNode() { return targetNode; } /** * Changes the {@link KamNode target node} for this * {@link KamEdge edge}. The {@link KamEdgeImpl#hashCode} and * {@link KamEdgeImpl#label} is then regenerated to preserve the * identity. * * @param targetNode {@link KamNode} new node */ private void setTargetNode(final KamNode targetNode) { this.targetNode = targetNode; this.hashCode = generateHashCode(); this.label = generateLabel(); } /** * Retrieve the {@link RelationshipType relationship} of this edge. * * @return the {@link RelationshipType relationship} */ @Override public RelationshipType getRelationshipType() { return relationshipType; } /** * Generates the <tt>hashCode</tt> value for this immutable edge. * * <p> * This is used to calculate the hashCode once on construction. * </p> * * @return the <tt>int</tt> hashCode */ private int generateHashCode() { final int prime = 31; int result = 1; result = prime * result + getKam().hashCode(); result = prime * result + ((relationshipType == null) ? 0 : relationshipType .hashCode()); result = prime * result + ((sourceNode == null) ? 0 : sourceNode.hashCode()); result = prime * result + ((targetNode == null) ? 0 : targetNode.hashCode()); return result; } /** * Generates the {@link String label} of this edge. * * <p> * This is used to calculate the label once on construction. * </p> * * @return the {@link String label} */ private String generateLabel() { StringBuilder sb = new StringBuilder(); sb.append(sourceNode.toString()); sb.append(" "); sb.append(relationshipType.getDisplayValue()); sb.append(" "); sb.append(targetNode.toString()); return sb.toString(); } /** * {@inheritDoc} */ @Override public int hashCode() { return hashCode; } /** * {@inheritDoc} */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (!(KamEdge.class.isAssignableFrom(obj.getClass()))) { return false; } KamEdge other = (KamEdge) obj; if (!getKam().equals(other.getKam())) { return false; } if (relationshipType != other.getRelationshipType()) { return false; } if (sourceNode == null) { if (other.getSourceNode() != null) { return false; } } else if (!sourceNode.equals(other.getSourceNode())) { return false; } if (targetNode == null) { if (other.getTargetNode() != null) { return false; } } else if (!targetNode.equals(other.getTargetNode())) { return false; } return true; } /** * {@inheritDoc} */ @Override public String toString() { return label; } } /** * {@link KamNode} represents a node in the {@link Kam kam}. * * @author julianjray */ public final class KamNodeImpl extends KamElementImpl implements KamNode { private final FunctionEnum functionType; private final String label; /** * Precalculate the kam node hash code since the object is immutable. */ private final int hashCode; /** * * @param id * @param functionType * @param label */ public KamNodeImpl(Kam kam, Integer id, FunctionEnum functionType, String label) { super(kam, id); this.functionType = functionType; this.label = label; this.hashCode = generateHashCode(); } /** * * @return {@link FunctionEnum} */ @Override public final FunctionEnum getFunctionType() { return functionType; } /** * * @return {@link String} */ @Override public final String getLabel() { return label; } /** * Generates the <tt>hashCode</tt> value for this immutable node. * * <p> * This is used to calculate the hashCode once on construction. * </p> * * @return the <tt>int</tt> hashCode */ private final int generateHashCode() { final int prime = 31; int result = 1; result = prime * result + getKam().hashCode(); result = prime * result + ((functionType == null) ? 0 : functionType.hashCode()); result = prime * result + ((label == null) ? 0 : label.hashCode()); return result; } /** * {@inheritDoc} */ @Override public final int hashCode() { return hashCode; } /** * {@inheritDoc} */ @Override public final boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (!KamNode.class.isAssignableFrom(obj.getClass())) { return false; } KamNode other = (KamNode) obj; if (!getKam().equals(other.getKam())) { return false; } if (getId() == null) { if (other.getId() != null) { return false; } } else if (!getId().equals(other.getId())) { return false; } return true; } /** * {@inheritDoc} */ @Override public final String toString() { return label; } } }