/**
* 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.index;
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.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.openbel.framework.common.InvalidArgument;
import org.openbel.framework.common.enums.FunctionEnum;
import org.openbel.framework.common.enums.RelationshipType;
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.model.AnnotationGroup;
import org.openbel.framework.common.model.BELObject;
import org.openbel.framework.common.model.Namespace;
import org.openbel.framework.common.model.Parameter;
import org.openbel.framework.common.model.Statement;
import org.openbel.framework.common.model.Term;
/**
* StatementTable holds the statement values. This class manages the statements
* through the {@link #addStatement(TableStatement, Statement, int)} operation.
*
* @author Anthony Bargnesi {@code <abargnesi@selventa.com>}
* @version 1.3 Derives from {@link ExternalType}
*/
public class StatementTable extends ExternalType {
private static final long serialVersionUID = -6486699009265767001L;
/**
* Defines an ordered list to hold statement values. Note: This collection
* is backed by {@link ArrayList} which is not thread-safe.
*/
private List<TableStatement> statements = new ArrayList<TableStatement>();
/**
* Defines a map from statement index to global index. Note: This collection
* is backed by {@link HashMap} which is not thread-safe.
*/
private Map<Integer, Integer> globalStatementIndex =
new HashMap<Integer, Integer>();
/**
* Defines a map from document index to a {@link List} of statement index.
* Note: This collection is backed by {@code LinkedHashMap} which is not
* threadsafe.
*/
private Map<Integer, Integer> statementDocumentMap =
new LinkedHashMap<Integer, Integer>();
/**
* Defines a map of {@link TableStatement} to {@link Integer} index. Note:
* This collection is backed by {@link HashMap} which is not thread-safe.
*/
private Map<TableStatement, Integer> tableStatementIndex =
new HashMap<StatementTable.TableStatement, Integer>();
/**
* Defines a map of {@link Statement} to {@link Integer} index. This
* collection stores the index of the common-model statement object for
* duplication checking. Note: This collection is backed by {@link HashMap}
* which is not thread-safe.
*/
private Map<Statement, Integer> visitedStatements =
new HashMap<Statement, Integer>();
/**
* Defines a map of {@link Integer} index to {@link Statement}. This
* collection stores the common-model statement for duplication checking.
* Note: This collection is backed by {@link HashMap} which is not
* thread-safe.
*/
private Map<Integer, Statement> indexedStatements =
new HashMap<Integer, Statement>();
/**
* These objects should only be constructed by {@link ProtoNetwork}.
*/
StatementTable() {
}
/**
* Adds a statement to the {@code statements} set. The insertion index and
* statement occurrences are also indexed in the {@code index} and
* {@code count} map. <br/>
* <br/>
* Uses statement ids as 1-based to support nested statements as an
* <tt>int</tt>.
*
* @param tblStatement {@link TableStatement}, the statement to be added to
* the {@code statements} set
* @param did Document index
* @return int, the index of the added statement, which is 'int' datatype
* since it cannot be null
* @throws InvalidArgument Thrown if {@code statement} is null
*/
public int addStatement(TableStatement tblStatement, Statement statement,
int did) {
if (tblStatement == null) {
throw new InvalidArgument("statement is null");
}
// duplicate statement check
Integer existingIndex = visitedStatements.get(statement);
if (existingIndex != null) {
return existingIndex;
}
int nsid = statements.size();
statements.add(tblStatement);
visitedStatements.put(statement, nsid);
indexedStatements.put(nsid, statement);
globalStatementIndex.put(nsid, nsid);
statementDocumentMap.put(nsid, did);
tableStatementIndex.put(tblStatement, nsid);
return nsid;
}
/**
* Returns the statement table's <tt>statements</tt> list. This list is
* unmodifiable to preserve the state of the statement table.
*
* @return {@link List}, which cannot be null or modified
*/
public List<TableStatement> getStatements() {
return Collections.unmodifiableList(statements);
}
/**
* Returns the statement table's global index {@link Map}. This map is
* unmodifiable to preserve the state of the statement table.
*
* @return {@link Map} of {@link Integer} statement index to {@link Integer}
* global index, which cannot be null
*/
public Map<Integer, Integer> getGlobalStatementIndex() {
return globalStatementIndex;
}
/**
* Returns the statement table's table statement index {@link Map}. This map
* is unmodifiable to preserve the state of the statement table.
*
* @return {@link Map} of {@link TableStatement} to {@link Integer}
* statement index, which cannot be null or modified
*/
public Map<TableStatement, Integer> getTableStatementIndex() {
return Collections.unmodifiableMap(tableStatementIndex);
}
/**
* Returns the map of statement index to document index. This map is
* unmodifiable to preserve the state of the statement table.
*
* @return {@link Map}, which cannot be null or modified
*/
public Map<Integer, Integer> getStatementDocument() {
return statementDocumentMap;
}
/**
* Returns the map of common-model {@link Statement} to {@link Integer}
* statement index. This map it unmodifiable to preserve the state of the
* statement table.
*
* @return {@link Map} of {@link Statement} to {@link Integer} index, which
* cannot be null or modified
*/
public Map<Statement, Integer> getVisitedStatements() {
return Collections.unmodifiableMap(visitedStatements);
}
/**
* Returns the map of {@link Integer} index to common-model
* {@link Statement}. This map it unmodifiable to preserve the state of the
* statement table.
*
* @return {@link Map} of {@link Integer} index to {@link Statement} index,
* which cannot be null or modified
*/
public Map<Integer, Statement> getIndexedStatements() {
return Collections.unmodifiableMap(indexedStatements);
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result =
prime * result
+ ((statements == null) ? 0 : statements.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;
StatementTable other = (StatementTable) obj;
if (statements == null) {
if (other.statements != null)
return false;
} else if (!statements.equals(other.statements))
return false;
return true;
}
/**
* {@inheritDoc}
*/
@Override
protected void _from(ObjectInput in) throws IOException,
ClassNotFoundException {
// 1: read number of stmts
final int stmts = in.readInt();
statements = sizedArrayList(stmts);
for (int i = 0; i < stmts; i++) {
TableStatement ts = new TableStatement();
// 1: read each table statement
ts.readExternal(in);
// 2: read the relevant document index
final int did = in.readInt();
// 3: read the statement
final Statement statement = readStatement(in);
addStatement(ts, statement, did);
}
}
/**
* {@inheritDoc}
*/
@Override
protected void _to(ObjectOutput out) throws IOException {
final int stmts = statements.size();
// 1: write number of stmts
out.writeInt(stmts);
Integer[] stmtdocs = index(Integer.class, statementDocumentMap);
Statement[] stmtidxs = index(Statement.class, indexedStatements);
for (int i = 0; i < stmts; i++) {
TableStatement ts = statements.get(i);
// 1: write each table statement
ts.writeExternal(out);
// 2: write the relevant document index
out.writeInt(stmtdocs[i]);
// 3: write statement
writeStatement(out, stmtidxs[i]);
}
}
static void writeRelationship(ObjectOutput out, RelationshipType rel)
throws IOException {
if (rel == null) {
out.writeByte(0);
} else {
out.writeByte(1);
out.writeInt(rel.ordinal());
}
}
static RelationshipType readRelationship(ObjectInput in)
throws IOException {
RelationshipType ret = null;
byte obyte = in.readByte();
if (obyte == 1) {
int relOrdinal = in.readInt();
ret = RelationshipType.values()[relOrdinal];
}
return ret;
}
static void writeStatement(ObjectOutput out, Statement stmt)
throws IOException {
// 1: comment, may be null
String comment = stmt.getComment();
writeObject(out, comment);
// 2: annotation group, may be null
AnnotationGroup ag = stmt.getAnnotationGroup();
writeObject(out, ag);
// 3: subject term, never null
Term subject = stmt.getSubject();
writeTerm(out, subject);
// 4: statement object, may be null
Statement.Object object = stmt.getObject();
if (object == null) {
out.writeByte(0);
} else if (object.getTerm() != null) {
out.writeByte(1);
writeTerm(out, object.getTerm());
} else if (object.getStatement() != null) {
out.writeByte(2);
writeStatement(out, object.getStatement());
}
// 5: relationship type, may be null
writeRelationship(out, stmt.getRelationshipType());
}
static Statement readStatement(ObjectInput in) throws IOException,
ClassNotFoundException {
// 1: comment
String comment = (String) readObject(in);
// 2: annotation group
AnnotationGroup ag = (AnnotationGroup) readObject(in);
// 3: subject term
Term subject = readTerm(in);
// 4: statement object
Statement.Object object;
byte obyte = in.readByte();
if (obyte == 1) {
object = new Statement.Object(readTerm(in));
} else if (obyte == 2) {
object = new Statement.Object(readStatement(in));
} else {
object = null;
}
// 5: relationship type
RelationshipType rel = readRelationship(in);
return new Statement(subject, comment, ag, object, rel);
}
static void writeTerm(ObjectOutput out, Term term) throws IOException {
// 1: function enum, never null
FunctionEnum fx = term.getFunctionEnum();
out.writeObject(fx);
// 2: function arguments, may be null
List<BELObject> fxargs = term.getFunctionArguments();
if (fxargs == null) {
out.writeByte(0);
} else {
out.writeByte(1);
out.writeInt(fxargs.size());
for (final BELObject bo : fxargs) {
if (bo instanceof Parameter) {
out.writeByte(0);
writeParameter(out, (Parameter) bo);
} else if (bo instanceof Term) {
out.writeByte(1);
writeTerm(out, (Term) bo);
}
}
}
}
static Term readTerm(ObjectInput in) throws IOException,
ClassNotFoundException {
// 1: function enum
FunctionEnum fx = (FunctionEnum) in.readObject();
// 2: functions arguments
List<BELObject> fxargs = null;
byte obyte = in.readByte();
if (obyte == 1) {
int size = in.readInt();
fxargs = sizedArrayList(size);
for (int i = 0; i < size; i++) {
obyte = in.readByte();
if (obyte == 0) {
fxargs.add(readParameter(in));
} else if (obyte == 1) {
fxargs.add(readTerm(in));
}
}
}
return new Term(fx, fxargs);
}
static void writeParameter(ObjectOutput out, Parameter param)
throws IOException {
writeObject(out, param.getNamespace());
writeObject(out, param.getValue());
}
static Parameter readParameter(ObjectInput in) throws IOException,
ClassNotFoundException {
Namespace ns = (Namespace) readObject(in);
String value = (String) readObject(in);
return new Parameter(ns, value);
}
/**
* {@inheritDoc}
*/
@Override
protected void _from(ObjectInput in, ReadCache cache) throws IOException {
throw new UnsupportedOperationException();
}
/**
* {@inheritDoc}
*/
@Override
protected void _to(ObjectOutput out, WriteCache cache) throws IOException {
throw new UnsupportedOperationException();
}
/**
* TableStatement holds the statement values.
*
* @author Anthony Bargnesi {@code <abargnesi@selventa.com>}
* @version 1.3 Derives from {@link ExternalType}
*/
public static class TableStatement extends ExternalType {
private static final long serialVersionUID = -6486699009265767002L;
/**
* Defines the subject term index for this table statement.
*/
private/* final */Integer subjectTerm;
/**
* Defines the relationship name for this table statement.
*/
private/* final */String relationship;
/**
* Defines the object index for this table statement.
*/
private/* final */Integer objectTerm;
/**
* Defines the nested subject index for this table statement.
*/
private/* final */Integer nestedSubject;
/**
* Defines the nested relationship for this table statement.
*/
private/* final */String nestedRelationship;
/**
* Defines the nested object index for this table statement.
*/
private/* final */Integer nestedObject;
/**
* Defines the hash for {@link TableStatement this table statement}.
*/
private/* final */int hash;
/**
* Create a definitional {@link TableStatement statement}.
*
* @param subjectTerm the subject term index
* @throws InvalidArgument Thrown if the {@code subjectTerm} is
* {@code null} is null
*/
public TableStatement(Integer subjectTerm) {
if (subjectTerm == null) {
throw new InvalidArgument("subjectTerm", subjectTerm);
}
this.subjectTerm = subjectTerm;
this.relationship = null;
this.objectTerm = null;
this.nestedSubject = null;
this.nestedRelationship = null;
this.nestedObject = null;
this.hash = computeHash();
}
/**
* Creates a simple statement from a
*
* <pre>
* subject term, relationship, object term
* </pre>
*
* @param subjectTerm {@link Integer}, the subject term id, which cannot
* be null
* @param relationship {@link String}, the relationship name, which can
* be null
* @param objectTerm {@link Integer}, the object term index
* @throws InvalidArgument Thrown if the {@code subjectTerm} is
* {@code null}
*/
public TableStatement(Integer subjectTerm, String relationship,
Integer objectTerm) {
if (subjectTerm == null) {
throw new InvalidArgument("subjectTerm", subjectTerm);
}
this.subjectTerm = subjectTerm;
this.relationship = relationship;
this.objectTerm = objectTerm;
this.nestedSubject = null;
this.nestedRelationship = null;
this.nestedObject = null;
this.hash = computeHash();
}
/**
* Creates a nested statement from a
*
* <pre>
* subject term, relationship,
* nested subject term, nested relationship, nested object term
* </pre>
*
* @param subjectTerm the subject term index
* @param relationship the relationship name
* @param nestedSubject the nested subject term index
* @param nestedRelationship the nested relationship name
* @param nestedObject the nested object term index
* @throws InvalidArgument Thrown if the {@code subjectTerm} is
* {@code null}
*/
public TableStatement(Integer subjectTerm, String relationship,
Integer nestedSubject, String nestedRelationship,
Integer nestedObject) {
if (subjectTerm == null) {
throw new InvalidArgument("subjectTerm", subjectTerm);
}
this.subjectTerm = subjectTerm;
this.relationship = relationship;
this.objectTerm = null;
this.nestedSubject = nestedSubject;
this.nestedRelationship = nestedRelationship;
this.nestedObject = nestedObject;
this.hash = computeHash();
}
/**
* These objects should only be constructed by {@link ProtoNetwork}.
*/
TableStatement() {
}
/**
* Return the subject term id.
*
* @return {@link Integer}, the subject term id, which cannot be null
*/
public Integer getSubjectTermId() {
return subjectTerm;
}
/**
* Return the relationship name.
*
* @return {@link String}, the relationship name, which can be null
*/
public String getRelationshipName() {
return relationship;
}
/**
* Return the object term index.
*
* @return the {@link Integer object term index}
*/
public Integer getObjectTermId() {
return objectTerm;
}
/**
* Return the nested subject term index.
*
* @return the {@link Integer nested subject term index}
*/
public Integer getNestedSubject() {
return nestedSubject;
}
/**
* Return the {@link RelationshipType nested relationship}.
*
* @return the {@link Integer nested subject term index}
*/
public String getNestedRelationship() {
return nestedRelationship;
}
/**
* Return the {@link Integer nested object term index}.
*
* @return the {@link Integer nested object term index}
*/
public Integer getNestedObject() {
return nestedObject;
}
/**
* Compute the hashCode of {@link TableStatement this table statement}.
*
* @return the hashCode
*/
private int computeHash() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((subjectTerm == null) ? 0 : subjectTerm.hashCode());
result = prime * result
+ ((relationship == null) ? 0 : relationship.hashCode());
result = prime * result
+ ((objectTerm == null) ? 0 : objectTerm.hashCode());
result = prime * result
+ ((nestedSubject == null) ? 0 : nestedSubject.hashCode());
result = prime
* result
+ ((nestedRelationship == null) ? 0 : nestedRelationship
.hashCode());
result = prime * result
+ ((nestedObject == null) ? 0 : nestedObject.hashCode());
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;
TableStatement other = (TableStatement) obj;
if (subjectTerm == null) {
if (other.subjectTerm != null)
return false;
} else if (!subjectTerm.equals(other.subjectTerm))
return false;
if (relationship == null) {
if (other.relationship != null)
return false;
} else if (!relationship.equals(other.relationship))
return false;
if (objectTerm == null) {
if (other.objectTerm != null)
return false;
} else if (!objectTerm.equals(other.objectTerm))
return false;
if (nestedSubject == null) {
if (other.nestedSubject != null)
return false;
} else if (!nestedSubject.equals(other.nestedSubject))
return false;
if (nestedRelationship == null) {
if (other.nestedRelationship != null)
return false;
} else if (!nestedRelationship.equals(other.nestedRelationship))
return false;
if (nestedObject == null) {
if (other.nestedObject != null)
return false;
} else if (!nestedObject.equals(other.nestedObject))
return false;
return true;
}
/**
* {@inheritDoc}
*/
@Override
protected void _from(ObjectInput in) throws IOException,
ClassNotFoundException {
// subject term is guaranteed non-null
subjectTerm = in.readInt();
relationship = null;
RelationshipType rel = readRelationship(in);
if (rel != null) {
relationship = rel.getDisplayValue();
}
objectTerm = readInteger(in);
nestedSubject = readInteger(in);
nestedRelationship = readString(in);
nestedObject = readInteger(in);
hash = computeHash();
}
/**
* {@inheritDoc}
*/
@Override
protected void _to(ObjectOutput out) throws IOException {
// subject term is guaranteed non-null
out.writeInt(subjectTerm);
RelationshipType rel = RelationshipType.fromString(relationship);
writeRelationship(out, rel);
writeInteger(out, objectTerm);
writeInteger(out, nestedSubject);
out.writeObject(nestedRelationship);
writeInteger(out, nestedObject);
}
/**
* {@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();
}
}
}