/**
* 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.model;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.openbel.framework.common.InvalidArgument;
import org.openbel.framework.common.enums.RelationshipType;
/**
* A statement is a specific assertion of a fact in some context: it is
* implicitly based on some source of knowledge, implicitly true in at least one
* specific situation.
* <p>
* BEL statements primarily represent relationships between {@link Term terms}.
* As such, statements are term-iterable:
* <pre>
* <code>
* for (final Term term : statement) {
* // ...
* }
* </code>
* </pre>
* </p>
*
* @author Anthony Bargnesi {@code <abargnesi@selventa.com>}
*/
public class Statement implements BELObject, Iterable<Term> {
private static final long serialVersionUID = -4295358543715077939L;
private String comment;
private AnnotationGroup annotationGroup;
private final Term subject;
private Object object;
private RelationshipType relationshipType;
/**
* Creates a statement with the required subject property.
*
* @param subject Subject
* @throws InvalidArgument Thrown if {@code subject} is null
*/
public Statement(Term subject) {
if (subject == null) throw new InvalidArgument("subject is null");
this.subject = subject;
}
/**
* Creates a statement with the required subject and optional properties.
*
* @param subject Subject
* @param comment Comment
* @param ag Annotation group
* @param object Object
* @param r Relationship
* @throws InvalidArgument Thrown if {@code subject} is null
*/
public Statement(Term subject, String comment, AnnotationGroup ag,
Object object, RelationshipType r) {
if (subject == null) throw new InvalidArgument("subject is null");
this.subject = subject;
this.comment = comment;
this.annotationGroup = ag;
this.object = object;
this.relationshipType = r;
}
/**
* Returns the statement's comment.
*
* @return String, which may be null
*/
public String getComment() {
return comment;
}
/**
* Sets the statement's comment.
*
* @param comment Statement's comment
*/
public void setComment(String comment) {
this.comment = comment;
}
/**
* Returns the statement's relationship.
*
* @return Relationship, which may be null
*/
public RelationshipType getRelationshipType() {
return relationshipType;
}
/**
* Sets the statement's relationship type.
*
* @param relationshipType Statement's relationship type
*/
public void setRelationshipType(RelationshipType relationshipType) {
this.relationshipType = relationshipType;
}
/**
* Returns the statement's annotation group.
*
* @return AnnotationGroup, which may be null
*/
public AnnotationGroup getAnnotationGroup() {
return annotationGroup;
}
/**
* Sets the statement's annotation group.
*
* @param annotationGroup Annotation group
*/
public void setAnnotationGroup(AnnotationGroup annotationGroup) {
this.annotationGroup = annotationGroup;
}
/**
* Returns the statement's object.
*
* @return Object, which may be null
*/
public Object getObject() {
return object;
}
/**
* Sets the statement's object.
*
* @param object Object
*/
public void setObject(Object object) {
this.object = object;
}
/**
* Returns the statement's subject.
*
* @return Non-null term
*/
public Term getSubject() {
return subject;
}
/**
* Returns a list of all parameters contained by this statement and object.
*
* @return List of parameters
*/
public List<Parameter> getAllParameters() {
List<Parameter> ret = new ArrayList<Parameter>();
ret.addAll(subject.getAllParameters());
if (object != null) {
if (object.getStatement() != null)
ret.addAll(object.getStatement()
.getAllParameters());
else
ret.addAll(object.getTerm().getAllParameters());
}
return ret;
}
/**
* Returns a list of all terms contained by this statement's subject and
* object.
*
* @return List of terms
*/
public List<Term> getAllTerms() {
List<Term> ret = new ArrayList<Term>();
ret.add(subject);
List<Term> subjectTerms = subject.getTerms();
if (subjectTerms != null) {
ret.addAll(subjectTerms);
for (final Term term : subjectTerms) {
ret.addAll(term.getAllTerms());
}
}
if (object != null) {
ret.addAll(object.getAllTerms());
}
return ret;
}
/**
* Returns an {@link Iterator iterator} over the document's {@link Term
* terms}.
* <p>
* The following code is guaranteed to be safe (no
* {@link NullPointerException null pointer exceptions} will be thrown):
*
* <pre>
* <code>
* for (final Term term : statement) {
* }
* </code>
* </pre>
*
* </p>
*
* @return {@link Iterator} of {@link Term terms}
*/
@Override
public Iterator<Term> iterator() {
return getAllTerms().iterator();
}
/**
* Returns {@code true} if this statement only contains a
* {@link Term subject term}, {@code false} otherwise.
*
* <p>
* "In its simplest form, a BEL Statement can be used to establish that a
* BEL Term has been observed in the context of the BEL Statement. A
* typical example of such a statement would be one that contains a
* molecular complex term. Such a BEL Statement would assert that the
* complex has been observed."
* </p>
*
* <p>
* For further reference, see <i>BEL Language Overview</i>, BEL Statements
* (BEL Overview).
* </p>
*
* @see Statement#hasNestedStatement()
* @see Statement#isStatementTriple()
* @return {@code true} if this statement only contains a
* {@link Term subject term}, {@code false} otherwise
*/
public boolean isSubjectOnly() {
return object == null;
}
/**
* Returns {@code true} if this statement is a triple consisting of
* <p>
* <tt>TERM RELATIONSHIP TERM</tt>
* </p>
*
* <p>
* Otherwise return {@code false}.
* </p>
*
* <p>
* <blockquote>
* "Most BEL Statements represent relationships between one BEL Term and
* another BEL Term or BEL Statement. This type of BEL Statement encodes a
* semantic triple (subject, relationship type, object), which represents
* an assertion of a relationship between the subject and object."
* </blockquote>
* </p>
*
* <p>
* For further reference, see <i>BEL Language Overview</i>, BEL Statements
* (BEL Overview).
* </p>
*
* @see Statement#hasNestedStatement()
* @see Statement#isSubjectOnly()
* @return {@code true} if this statement is a term-rel-term triple,
* {@code false} otherwise
*/
public boolean isStatementTriple() {
if (object != null && object.getTerm() != null) {
return true;
}
return false;
}
/**
* Returns {@code true} if this statement contains a nested statement of
* the form:
*
* <p>
* <tt>TERM RELATIONSHIP STATEMENT</tt>
* </p>
*
* <p>
* <blockquote>"If the object of a BEL statement is another BEL statement,
* the BEL statement is said to be nested and the relationship type is
* constrained to the set of causal relationship types."</blockquote>
* </p>
*
* <p>
* For further reference, see <i>BEL Language Overview</i>, BEL Statements
* (BEL Overview).
* </p>
*
* @see Statement#isStatementTriple()
* @see Statement#isSubjectOnly()
* @return {@code true} if this statement contains a nested statement,
* {@code false} otherwise
*/
public boolean hasNestedStatement() {
if (object != null && object.getStatement() != null) {
return true;
}
return false;
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("Statement [");
if (comment != null) {
builder.append("comment=");
builder.append(comment);
builder.append(", ");
}
if (annotationGroup != null) {
builder.append("annotationGroup=");
builder.append(annotationGroup);
builder.append(", ");
}
// subject is non-null by contract
builder.append("subject=");
builder.append(subject);
builder.append(", ");
if (object != null) {
builder.append("object=");
builder.append(object);
builder.append(", ");
}
if (relationshipType != null) {
builder.append("relationshipType=");
builder.append(relationshipType);
builder.append(", ");
}
builder.append("]");
return builder.toString();
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result *= prime;
if (comment != null) result += comment.hashCode();
result *= prime;
if (annotationGroup != null) result += annotationGroup.hashCode();
result *= prime;
// subject is non-null by contract
result += subject.hashCode();
result *= prime;
if (object != null) result += object.hashCode();
result *= prime;
if (relationshipType != null) result += relationshipType.hashCode();
return result;
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(java.lang.Object o) {
if (this == o) return true;
if (!(o instanceof Statement)) return false;
final Statement s = (Statement) o;
if (comment == null) {
if (s.comment != null) return false;
} else if (!comment.equals(s.comment)) return false;
if (annotationGroup == null) {
if (s.annotationGroup != null) return false;
} else if (!annotationGroup.equals(s.annotationGroup)) return false;
// subject is non-null by contract
if (!subject.equals(s.subject)) return false;
if (object == null) {
if (s.object != null) return false;
} else if (!object.equals(s.object)) return false;
if (relationshipType != s.relationshipType) return false;
return true;
}
/**
* {@inheritDoc}
*/
@Override
public String toBELLongForm() {
return toBEL(false);
}
/**
* {@inheritDoc}
*/
@Override
public String toBELShortForm() {
return toBEL(true);
}
private String toBEL(boolean shortForm) {
final StringBuilder sb = new StringBuilder();
if (shortForm) {
sb.append(subject.toBELShortForm());
} else {
sb.append(subject.toBELLongForm());
}
if (object != null) {
sb.append(" ");
if (shortForm) {
String abbr = relationshipType.getAbbreviation();
sb.append(abbr == null ? relationshipType.getDisplayValue()
: abbr);
} else {
sb.append(relationshipType.getDisplayValue());
}
sb.append(" ");
if (object.getStatement() != null) {
sb.append("(");
if (shortForm) {
sb.append(object.getStatement().toBELShortForm());
} else {
sb.append(object.getStatement().toBELLongForm());
}
sb.append(")");
} else {
if (shortForm) {
sb.append(object.getTerm().toBELShortForm());
} else {
sb.append(object.getTerm().toBELLongForm());
}
}
}
return sb.toString();
}
/**
* {@inheritDoc}
*/
@Override
public Statement clone() {
Term subject2 = subject.clone();
Object object2 = null;
if (object != null) {
object2 = object.clone();
}
AnnotationGroup annotationGroup2 = null;
if (annotationGroup != null) {
annotationGroup2 = annotationGroup.clone();
}
return new Statement(subject2, comment, annotationGroup2, object2,
relationshipType);
}
/**
* Defines the object of a statement.
*/
public static class Object implements BELObject {
private static final long serialVersionUID = 7947363393651852678L;
private Term term;
private Statement statement;
/**
* Creates an object.
* <p>
* Objects require either a term or a statement.
* </p>
*
* @param t Term
* @throws InvalidArgument Thrown if {@code t} is null
*/
public Object(Term t) {
if (t == null) throw new InvalidArgument("term is null");
this.term = t;
}
/**
* Creates an object.
* <p>
* Objects require either a term or a statement.
* </p>
*
* @param s Statement
* @throws InvalidArgument Thrown if {@code statement} is null
*/
public Object(Statement s) {
if (s == null) throw new InvalidArgument("statement is null");
this.statement = s;
}
/**
* Returns the term of the object.
*
* @return Term, may be null, in which case, statement is non-null
*/
public Term getTerm() {
return term;
}
/**
* Returns the statement of the object.
*
* @return Statement, may be null, in which case, term is non-null
*/
public Statement getStatement() {
return statement;
}
/**
* Returns this object's terms or all the terms contained by the nested
* statement.
*
* @return List of terms
*/
public List<Term> getAllTerms() {
if (term != null) {
final List<Term> ret = new ArrayList<Term>(1);
ret.add(term);
ret.addAll(term.getAllTerms());
return ret;
}
return statement.getAllTerms();
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result *= prime;
if (statement == null)
result += term.hashCode();
result *= prime;
if (term == null)
result += statement.hashCode();
return result;
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(java.lang.Object o) {
if (this == o) return true;
if (!(o instanceof Object)) return false;
final Object o2 = (Object) o;
if (statement == null) {
if (o2.statement != null) return false;
} else if (!statement.equals(o2.statement)) return false;
if (term == null) {
if (o2.term != null) return false;
} else if (!term.equals(o2.term)) return false;
return true;
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
builder.append("Object [");
if (term != null) {
builder.append("term=");
builder.append(term);
builder.append(", ");
}
if (statement != null) {
builder.append("statement=");
builder.append(statement);
}
builder.append("]");
return builder.toString();
}
/**
* {@inheritDoc}
*/
@Override
public String toBELLongForm() {
return term != null
? term.toBELLongForm()
: statement.toBELLongForm();
}
@Override
public String toBELShortForm() {
return term != null
? term.toBELShortForm()
: statement.toBELShortForm();
}
/**
* {@inheritDoc}
*/
@Override
public Object clone() {
if (term != null)
return new Object(term.clone());
return new Object(statement.clone());
}
}
}