/**
* 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.nulls;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import org.openbel.framework.api.internal.KAMCatalogDao.KamInfo;
import org.openbel.framework.api.internal.KAMStoreDaoImpl.TermParameter;
import org.openbel.framework.common.InvalidArgument;
import org.openbel.framework.common.enums.FunctionEnum;
import org.openbel.framework.common.enums.RelationshipType;
/**
* {@link OrthologizedKam} defines a {@link Kam kam} that provides a
* orthologized view using a {@link SpeciesDialect species dialect}.
*
* The {@link OrthologizedKam} orthologization occurs on construction.
* The existing {@link Kam} is cloned to preserve the original {@link Kam}.
*
* Orthologization consists of:
* <ol>
* <li>Removing orthologous edges.</li>
* <li>Reconnect orthologous {@link KamEdge kam edges} to the collapsed
* species nodes.</li>
* <li>Remove ortholog {@link KamNode kam nodes}. This is done at
* construction of {@link OrthologizedKam}.</ol>
*
* @see SpeciesDialect
* @author Anthony Bargnesi <abargnesi@selventa.com>
*/
public class OrthologizedKam implements Kam {
/**
* Copy of the provided {@link Kam kam}.
*/
private final Kam kam;
/**
* Dialect that determines which {@link KamNode kam nodes} to collapse.
*/
private final SpeciesDialect dialect;
/**
* A map of kam node id to the species namespace parameter.
*/
private final Map<Integer, TermParameter> ntp;
/**
* A map of kam edge id to the species namespace parameter.
*/
private final Map<Integer, TermParameter> etp;
/**
* A map of kam node that collapsed to species node.
*/
private final Map<KamNode, KamNode> collapsed;
/**
* Constructs a {@link OrthologizedKam species-specific kam}.
*
* @param kam {@link Kam} to orthologize, which cannot be {@code null}
* @param dialect {@link SpeciesDialect} to control species
* ortholgization, which cannot be {@code null}
* @param ntp {@link Map} of node id to {@link TermParameter}
* @param etp {@link Map} of edge id to {@link TermParameter}
* @param collapsed {@link Map} of collapsed orthologous node to species node
* @throws InvalidArgument Thrown if {@code kam}, {@code speciesDialect},
* or {@code kamStore} is {@code null}
*/
OrthologizedKam(Kam kam, SpeciesDialect dialect,
Map<Integer, TermParameter> ntp, Map<Integer, TermParameter> etp,
Map<KamNode, KamNode> collapsed) throws KAMStoreException {
if (nulls(kam, dialect, ntp, etp, collapsed))
throw new InvalidArgument("parameter(s) are null");
this.kam = kam;
this.dialect = dialect;
this.ntp = ntp;
this.etp = etp;
this.collapsed = collapsed;
}
/**
* Returns the {@link Map} of orthologous {@link KamNode kam nodes} that
* were collapsed to species target {@link KamNode kam nodes}.
*
* @return {@link Map} of K: {@link KamNode}, V: {@link KamNode}
*/
public Map<KamNode, KamNode> getCollapsedNodes() {
return collapsed;
}
/**
* {@inheritDoc}
*/
@Override
public Integer getId() {
return kam.getId();
}
/**
* {@inheritDoc}
*/
@Override
public KamInfo getKamInfo() {
return kam.getKamInfo();
}
/**
* {@inheritDoc}
*/
@Override
public NodeFilter createNodeFilter() {
return kam.createNodeFilter();
}
/**
* {@inheritDoc}
*/
@Override
public EdgeFilter createEdgeFilter() {
return kam.createEdgeFilter();
}
/**
* {@inheritDoc}
*/
@Override
public KamNode findNode(String label) {
return findNode(label, null);
}
/**
* {@inheritDoc}
*/
@Override
public KamNode findNode(String label, NodeFilter filter) {
for (KamNode kamNode : getNodes()) {
boolean passedFilter = (filter == null || filter.accept(kamNode));
if (passedFilter) {
String nodeLabel = kamNode.getLabel();
if (nodeLabel.equalsIgnoreCase(label)) {
return kamNode;
}
}
}
return null;
}
/**
* {@inheritDoc}
*/
@Override
public KamNode findNode(Integer kamNodeId) {
return findNode(kamNodeId, null);
}
/**
* {@inheritDoc}
*/
@Override
public KamNode findNode(Integer kamNodeId, NodeFilter filter) {
return wrapNode(kam.findNode(kamNodeId, filter));
}
/**
* {@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 : getNodes()) {
boolean passedFilter = (filter == null || filter.accept(node));
if (passedFilter && labelPattern.matcher(node.getLabel()).matches()) {
results.add(node);
}
}
return results;
}
/**
* {@inheritDoc}
*/
@Override
public Set<KamNode> getAdjacentNodes(KamNode kamNode) {
return wrapNodes(kam.getAdjacentNodes(kamNode));
}
/**
* {@inheritDoc}
*/
@Override
public Set<KamNode> getAdjacentNodes(KamNode kamNode,
EdgeDirectionType edgeDirection) {
return wrapNodes(kam.getAdjacentNodes(kamNode, edgeDirection));
}
/**
* {@inheritDoc}
*/
@Override
public Set<KamNode> getAdjacentNodes(KamNode kamNode,
EdgeDirectionType edgeDirection, EdgeFilter edgeFilter) {
return wrapNodes(kam.getAdjacentNodes(kamNode, edgeDirection,
edgeFilter));
}
/**
* {@inheritDoc}
*/
@Override
public Set<KamNode> getAdjacentNodes(KamNode kamNode,
EdgeDirectionType edgeDirection, NodeFilter filter) {
return wrapNodes(kam.getAdjacentNodes(kamNode, edgeDirection, filter));
}
/**
* {@inheritDoc}
*/
@Override
public Set<KamNode> getAdjacentNodes(KamNode kamNode,
EdgeFilter edgeFilter, NodeFilter nodeFilter) {
return wrapNodes(kam.getAdjacentNodes(kamNode, edgeFilter, nodeFilter));
}
/**
* {@inheritDoc}
*/
@Override
public Set<KamNode> getAdjacentNodes(KamNode kamNode,
EdgeDirectionType edgeDirection, EdgeFilter edgeFilter,
NodeFilter nodeFilter) {
return wrapNodes(kam.getAdjacentNodes(kamNode, edgeDirection,
edgeFilter, nodeFilter));
}
/**
* {@inheritDoc}
*/
@Override
public Set<KamEdge> getAdjacentEdges(KamNode kamNode) {
return wrapEdges(kam.getAdjacentEdges(kamNode));
}
/**
* {@inheritDoc}
*/
@Override
public Set<KamEdge> getAdjacentEdges(KamNode kamNode, EdgeFilter filter) {
return wrapEdges(kam.getAdjacentEdges(kamNode, filter));
}
/**
* {@inheritDoc}
*/
@Override
public Set<KamEdge> getAdjacentEdges(KamNode kamNode,
EdgeDirectionType edgeDirection) {
return wrapEdges(kam.getAdjacentEdges(kamNode, edgeDirection));
}
/**
* {@inheritDoc}
*/
@Override
public Set<KamEdge> getAdjacentEdges(KamNode kamNode,
EdgeDirectionType edgeDirection, EdgeFilter filter) {
return wrapEdges(kam.getAdjacentEdges(kamNode, edgeDirection, filter));
}
/**
* {@inheritDoc}
*/
@Override
public Set<KamEdge> getEdges(KamNode sourceNode, KamNode targetNode) {
return wrapEdges(kam.getEdges(sourceNode, targetNode));
}
/**
* {@inheritDoc}
*/
@Override
public Set<KamEdge> getEdges(KamNode sourceNode, KamNode targetNode,
EdgeFilter filter) {
return wrapEdges(kam.getEdges(sourceNode, targetNode, filter));
}
/**
* {@inheritDoc}
*/
@Override
public KamEdge findEdge(Integer kamEdgeId) {
return wrapEdge(kam.findEdge(kamEdgeId));
}
/**
* {@inheritDoc}
*/
@Override
public KamEdge findEdge(KamNode sourceNode,
RelationshipType relationshipType, KamNode targetNode)
throws InvalidArgument {
return wrapEdge(kam.findEdge(sourceNode, relationshipType, targetNode));
}
/**
* {@inheritDoc}
*/
@Override
public boolean contains(KamNode kamNode) {
return kam.contains(kamNode);
}
/**
* {@inheritDoc}
*/
@Override
public boolean contains(KamEdge kamEdge) {
return kam.contains(kamEdge);
}
/**
* {@inheritDoc}
*/
@Override
public Collection<KamNode> getNodes() {
return wrapNodes(kam.getNodes());
}
/**
* {@inheritDoc}
*/
@Override
public Collection<KamNode> getNodes(NodeFilter filter) {
return wrapNodes(kam.getNodes(filter));
}
/**
* {@inheritDoc}
*/
@Override
public Collection<KamEdge> getEdges() {
return wrapEdges(kam.getEdges());
}
/**
* {@inheritDoc}
*/
@Override
public Collection<KamEdge> getEdges(EdgeFilter filter) {
return wrapEdges(kam.getEdges(filter));
}
/**
* {@inheritDoc}
*/
@Override
public void union(Collection<KamEdge> kamEdges) throws InvalidArgument {
// TODO does this need to be implemented?
throw new UnsupportedOperationException(
"union() is not supported by DialectKam");
}
/**
* {@inheritDoc}
*/
@Override
public KamEdge createEdge(Integer kamEdgeId, KamNode sourceNode,
RelationshipType relationshipType, KamNode targetNode)
throws InvalidArgument {
return kam.createEdge(kamEdgeId, sourceNode, relationshipType,
targetNode);
}
/**
* {@inheritDoc}
*/
@Override
public void removeEdge(KamEdge kamEdge) {
kam.removeEdge(kamEdge);
}
/**
* {@inheritDoc}
*/
@Override
public KamEdge replaceEdge(KamEdge kamEdge, FunctionEnum sourceFunction,
String sourceLabel, RelationshipType relationship,
FunctionEnum targetFunction, String targetLabel) {
return kam.replaceEdge(kamEdge, sourceFunction, sourceLabel,
relationship, targetFunction, targetLabel);
}
/**
* {@inheritDoc}
*/
@Override
public KamEdge replaceEdge(KamEdge kamEdge, KamEdge replacement) {
return kam.replaceEdge(kamEdge, replacement);
}
/**
* {@inheritDoc}
*/
@Override
public KamNode createNode(Integer id, FunctionEnum functionType,
String label) throws InvalidArgument {
return kam.createNode(id, functionType, label);
}
/**
* {@inheritDoc}
*/
@Override
public void removeNode(KamNode kamNode) {
kam.removeNode(kamNode);
}
/**
* {@inheritDoc}
*/
@Override
public KamNode replaceNode(KamNode kamNode, FunctionEnum function,
String label) {
return kam.replaceNode(kamNode, function, label);
}
/**
* {@inheritDoc}
*/
@Override
public KamNode replaceNode(KamNode kamNode, KamNode replacement) {
return kam.replaceNode(kamNode, replacement);
}
/**
* {@inheritDoc}
*/
@Override
public void collapseNode(KamNode from, KamNode to) {
kam.collapseNode(from, to);
}
private Set<KamNode> wrapNodes(Collection<KamNode> nodes) {
Set<KamNode> ret = new LinkedHashSet<KamNode>(nodes.size());
for (KamNode n : nodes) {
KamNode w = wrapNode(n);
if (w != null) {
ret.add(w);
}
}
return ret;
}
private Set<KamEdge> wrapEdges(Collection<KamEdge> edges) {
Set<KamEdge> ret = new LinkedHashSet<KamEdge>(edges.size());
for (KamEdge e : edges) {
KamEdge w = wrapEdge(e);
if (w != null) {
ret.add(w);
}
}
return ret;
}
/**
* Wrap a {@link KamNode} as an {@link OrthologousNode} to allow conversion
* of the node label by the {@link SpeciesDialect}.
*
* @param kamNode {@link KamNode}
* @return the wrapped kam node,
* <ol>
* <li>{@code null} if {@code kamNode} input is {@code null}</li>
* <li>{@link OrthologousNode} if {@code kamNode} has orthologized</li>
* <li>the original {@code kamNode} input if it has not orthologized</li>
* </ol>
*/
private KamNode wrapNode(KamNode kamNode) {
if (kamNode == null) {
return null;
}
TermParameter param = ntp.get(kamNode.getId());
if (param != null) {
return new OrthologousNode(kamNode, param);
}
return kamNode;
}
/**
* Wrap a {@link KamEdge} as an {@link OrthologousEdge} to allow conversion
* of the edge label by the {@link SpeciesDialect}. The edge's
* {@link KamNode}s are also wrapped.
*
* @param kamEdge {@link KamEdge}
* @return the wrapped kam edge,
* <ol>
* <li>{@code null} if {@code kamEdge} input is {@code null}</li>
* <li>{@link OrthologousEdge} if {@code kamEdge} has orthologized</li>
* <li>the original {@code kamEdge} input if it has not orthologized</li>
* </ol>
*/
private KamEdge wrapEdge(KamEdge kamEdge) {
if (kamEdge == null) {
return null;
}
TermParameter param = etp.get(kamEdge.getId());
if (param != null) {
return new OrthologousEdge(kamEdge, param);
}
return kamEdge;
}
protected final class OrthologousNode implements KamNode {
private final KamNode node;
private final TermParameter speciesParameter;
private OrthologousNode(final KamNode node,
final TermParameter speciesParameter) {
this.node = node;
this.speciesParameter = speciesParameter;
}
@Override
public Kam getKam() {
return node.getKam();
}
@Override
public Integer getId() {
return node.getId();
}
@Override
public FunctionEnum getFunctionType() {
return node.getFunctionType();
}
@Override
public String getLabel() {
return dialect.getLabel(node, speciesParameter);
}
public TermParameter getSpeciesParameter() {
return speciesParameter;
}
/**
* Delegate to original {@link KamNode} to ensure an
* {@link OrthologousNode} can be used as a parameter for methods where
* map lookups are performed.
*/
@Override
public int hashCode() {
return node.hashCode();
}
/**
* Delegate to original {@link KamNode} to ensure an
* {@link OrthologousNode} can be used as a parameter for methods where
* map lookups are performed.
*/
@Override
public boolean equals(Object obj) {
return node.equals(obj);
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return getLabel();
}
}
private final class OrthologousEdge implements KamEdge {
private final KamEdge kamEdge;
private final TermParameter speciesParameter;
protected OrthologousEdge(final KamEdge kamEdge,
final TermParameter speciesParameter) {
this.kamEdge = kamEdge;
this.speciesParameter = speciesParameter;
}
@Override
public Kam getKam() {
return kamEdge.getKam();
}
@Override
public Integer getId() {
return kamEdge.getId();
}
@Override
public KamNode getSourceNode() {
return new OrthologousNode(kamEdge.getSourceNode(),
speciesParameter);
}
@Override
public KamNode getTargetNode() {
return new OrthologousNode(kamEdge.getTargetNode(),
speciesParameter);
}
@Override
public RelationshipType getRelationshipType() {
return kamEdge.getRelationshipType();
}
/**
* Delegate to original {@link KamEdge} to ensure an
* {@link OrthologousEdge} can be used as a parameter for methods where
* map lookups are performed.
*/
@Override
public int hashCode() {
return kamEdge.hashCode();
}
/**
* Delegate to original {@link KamEdge} to ensure an
* {@link OrthologousEdge} can be used as a parameter for methods where
* map lookups are performed.
*/
@Override
public boolean equals(Object obj) {
return kamEdge.equals(obj);
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(getSourceNode().toString());
sb.append(" ");
sb.append(getRelationshipType().getDisplayValue());
sb.append(" ");
sb.append(getTargetNode().toString());
return sb.toString();
}
}
}