/** * 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.common.protonetwork.model; import static org.openbel.framework.common.BELUtilities.sizedArrayList; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.openbel.framework.common.InvalidArgument; import org.openbel.framework.common.external.ExternalType; import org.openbel.framework.common.external.ReadCache; import org.openbel.framework.common.external.WriteCache; import org.openbel.framework.common.protonetwork.model.StatementTable.TableStatement; /** * {@link ProtoEdgeTable} defines a symbol table to hold proto edges for the * {@link ProtoNetwork proto network}. The proto edge is composed of the triple: * * <pre> * source node, relationship, target node * </pre> * <p> * This symbol table holds the association * </p> * * @author Anthony Bargnesi {@code <abargnesi@selventa.com>} * @version 1.3 Derives from {@link ExternalType} */ public class ProtoEdgeTable extends ExternalType { private static final long serialVersionUID = -6486699009265767016L; /** * Defines a {@link List list} to hold the {@link TableProtoEdge proto * edges}. This {@link Collection collection} is not thread-safe. */ private List<TableProtoEdge> protoEdges = new ArrayList<ProtoEdgeTable.TableProtoEdge>(); /** * Defines a {@link Map map} with {@link Integer statement index} key and * {@link List set of proto edge indexes} as the value. This * {@link Collection collection} is not thread-safe. */ //XXX could we replace int to int maps with lists? private final Map<Integer, Set<Integer>> stmtEdges = new HashMap<Integer, Set<Integer>>(); /** * Defines a {@link Map map} with {@link Integer edge index} key and * {@link List set of statement index} as the value. This {@link Collection * collection} is not thread-safe. */ private final Map<Integer, Set<Integer>> edgeStmts = new HashMap<Integer, Set<Integer>>(); /** * Defines a {@link Map map} with {@link TableProtoEdge proto edges} key and * {@link Integer proto edge index} as the value. This {@link Collection * collection} is not thread-safe. */ private final Map<TableProtoEdge, Integer> visited = new HashMap<ProtoEdgeTable.TableProtoEdge, Integer>(); private Map<Integer, Integer> equivalences = new HashMap<Integer, Integer>(); /** * Adds 1 or more {@link TableProtoEdge proto edges} to the table and * associates them with the supporting {@link TableStatement statement} * index. * <p> * There must be at least one {@link TableProtoEdge proto edge} to * associate to a statement otherwise an exception is thrown. * </p> * * @param stmt the {@link TableStatement statement} index to associate the * {@link TableProtoEdge proto edges} to, which cannot be null * @param newEdges the {@link TableProtoEdge proto edges} array to add to * the table, which cannot be null or have a length less than 1 * @throws InvalidArgument May be thrown if <tt>stmt</tt> is <tt>null</tt>, * <tt>newEdges</tt> is null, or <tt>newEdges</tt> is <tt> <= 0</tt> */ public void addEdges(final Integer stmt, final TableProtoEdge... newEdges) { if (stmt == null) { throw new InvalidArgument("stmt", stmt); } if (newEdges == null || newEdges.length <= 0) { throw new InvalidArgument("newEdges", newEdges); } Set<Integer> edges = stmtEdges.get(stmt); if (edges == null) { edges = new HashSet<Integer>(); stmtEdges.put(stmt, edges); } for (final TableProtoEdge newEdge : newEdges) { int idx = protoEdges.size(); protoEdges.add(newEdge); Integer existingEdge = visited.get(newEdge); if (existingEdge != null) { getEquivalences().put(idx, existingEdge); } else { visited.put(newEdge, idx); getEquivalences().put(idx, getEquivalences().size()); } edges.add(idx); Set<Integer> stmts = new LinkedHashSet<Integer>(); stmts.add(stmt); edgeStmts.put(idx, stmts); } } /** * Return the {@link List proto edges list}. This list is modifiable to * allow the edge equivalencing process to filter duplicates. * * @return the {@link List proto edges list} */ public List<TableProtoEdge> getProtoEdges() { return protoEdges; } /** * Return the {@link Map statement to edges associations map}. This map is * modifiable to allow the edge equivalencing process to filter duplicates. * * @return the {@link Map statement to edges associations map} */ public Map<Integer, Set<Integer>> getStatementEdges() { return stmtEdges; } /** * Return the {@link Map edge to statements associations map}. This map is * modifiable to allow the edge equivalencing process to filter duplicates. * * @return the {@link Map edge to statements associations map} */ public Map<Integer, Set<Integer>> getEdgeStatements() { return edgeStmts; } /** * Return the {@link Map proto edges to index map}. This map is unmodifiable * to prevent the visited state from being changed. * * @return the {@link Map edge to statements associations map} */ public Map<TableProtoEdge, Integer> getVisitedEdges() { return Collections.unmodifiableMap(visited); } public Map<Integer, Integer> getEquivalences() { return equivalences; } /** * {@inheritDoc} */ @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((protoEdges == null) ? 0 : protoEdges.hashCode()); return result; } /** * {@inheritDoc} */ @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; ProtoEdgeTable other = (ProtoEdgeTable) obj; if (protoEdges == null) { if (other.protoEdges != null) return false; } else if (!protoEdges.equals(other.protoEdges)) return false; return true; } /** * {@inheritDoc} */ @Override @SuppressWarnings("unchecked") protected void _from(ObjectInput in) throws IOException, ClassNotFoundException { final int sizeProtoEdges = in.readInt(); protoEdges = sizedArrayList(sizeProtoEdges); for (int i = 0; i < sizeProtoEdges; i++) { TableProtoEdge tpe = new TableProtoEdge(); tpe.readExternal(in); protoEdges.add(tpe); } final int sizeStmtEdges = in.readInt(); for (int i = 0; i < sizeStmtEdges; i++) { final int key = in.readInt(); final int valueSize = in.readInt(); final Set<Integer> value = new HashSet<Integer>(); for (int j = 0; j < valueSize; j++) { value.add(in.readInt()); } stmtEdges.put(key, value); } final int sizeEdgeStmts = in.readInt(); for (int i = 0; i < sizeEdgeStmts; i++) { final int key = in.readInt(); final int valueSize = in.readInt(); final Set<Integer> value = new HashSet<Integer>(); for (int j = 0; j < valueSize; j++) { value.add(in.readInt()); } edgeStmts.put(key, value); } final int sizeVisited = in.readInt(); for (int i = 0; i < sizeVisited; i++) { TableProtoEdge key = (TableProtoEdge) in.readObject(); final int value = in.readInt(); visited.put(key, value); } equivalences = (Map<Integer, Integer>) in.readObject(); } /** * {@inheritDoc} */ @Override protected void _to(ObjectOutput out) throws IOException { final int sizeProtoEdges = protoEdges.size(); out.writeInt(sizeProtoEdges); for (int i = 0; i < sizeProtoEdges; i++) { TableProtoEdge tpe = protoEdges.get(i); tpe.writeExternal(out); } final int sizeStmtEdges = stmtEdges.size(); out.writeInt(sizeStmtEdges); for (Map.Entry<Integer, Set<Integer>> entry : stmtEdges.entrySet()) { out.writeInt(entry.getKey().intValue()); final Set<Integer> value = entry.getValue(); out.writeInt(value.size()); for (Integer j : value) { out.writeInt(j.intValue()); } } final int sizeEdgeStmts = edgeStmts.size(); out.writeInt(sizeEdgeStmts); for (Map.Entry<Integer, Set<Integer>> entry : edgeStmts.entrySet()) { out.writeInt(entry.getKey().intValue()); final Set<Integer> value = entry.getValue(); out.writeInt(value.size()); for (Integer j : value) { out.writeInt(j.intValue()); } } final int sizeVisited = visited.size(); out.writeInt(sizeVisited); for (Map.Entry<TableProtoEdge, Integer> entry : visited.entrySet()) { out.writeObject(entry.getKey()); out.writeInt(entry.getValue().intValue()); } out.writeObject(equivalences); } /** * {@inheritDoc} */ @Override protected void _from(ObjectInput in, ReadCache cache) throws IOException, ClassNotFoundException { throw new UnsupportedOperationException(); } /** * {@inheritDoc} */ @Override protected void _to(ObjectOutput out, WriteCache cache) throws IOException { throw new UnsupportedOperationException(); } /** * {@link TableProtoEdge} defines the container object for a proto network * edge. The {@link TableProtoEdge proto network edge} is made up of: * * <pre> * source (int), rel (RelationshipType), target (int) * </pre> * * @author Anthony Bargnesi {@code <abargnesi@selventa.com>} * @version 1.3 Derives from {@link ExternalType} */ public static final class TableProtoEdge extends ExternalType { private static final long serialVersionUID = -6486699009265767015L; /** * The source node index. */ private/* final */int source; /** * The {@link String relationship type}. */ private/* final */String rel; /** * The target node index. */ private/* final */int target; /** * This object's hashCode which is computed on construction. */ private/* final */int hash; /** * Create the {@link TableProtoEdge proto edge} with the triple: * * <pre> * source, rel, target * </pre> * * @param source the {@code int} source index * @param relName the {@link String relationship name} * @param target the {@code int} target index */ // FIXME: replace string relName with enum public TableProtoEdge(final int source, final String relName, final int target) { this.source = source; this.rel = relName; this.target = target; this.hash = computeHash(); } /** * Creates a {@link TableProtoEdge proto edge}. This public, no-argument * constructor is required when implementing Externalizable but it is * not meant to be used for anything else. */ public TableProtoEdge() { } /** * Return the source. * * @return the source */ public int getSource() { return source; } /** * Return the {@link String relationship name}. * * @return the {@link String relationship name} */ public String getRel() { return rel; } /** * Return the target. * * @return the target */ public int getTarget() { return target; } /** * Compute the hash since {@link TableProtoEdge this} is immutable. * * @return the computed hash {@code int} */ private int computeHash() { final int prime = 31; int result = 1; result = prime * result + ((rel == null) ? 0 : rel.hashCode()); result = prime * result + source; result = prime * result + target; return result; } /** * {@inheritDoc} */ @Override public int hashCode() { return hash; } /** * {@inheritDoc} */ @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; TableProtoEdge other = (TableProtoEdge) obj; if (!rel.equals(other.rel)) return false; if (source != other.source) return false; if (target != other.target) return false; return true; } /** * {@inheritDoc} */ @Override protected void _from(ObjectInput in) throws IOException, ClassNotFoundException { source = in.readInt(); rel = readString(in); target = in.readInt(); hash = computeHash(); } /** * {@inheritDoc} */ @Override protected void _to(ObjectOutput out) throws IOException { out.writeInt(source); out.writeObject(rel); out.writeInt(target); } /** * {@inheritDoc} */ @Override protected void _from(ObjectInput in, ReadCache cache) throws IOException, ClassNotFoundException { throw new UnsupportedOperationException(); } /** * {@inheritDoc} */ @Override protected void _to(ObjectOutput out, WriteCache cache) throws IOException { throw new UnsupportedOperationException(); } } }