/**
* 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.internal;
import static java.lang.String.format;
import static java.sql.ResultSet.CONCUR_READ_ONLY;
import static java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE;
import static java.util.Collections.emptyList;
import static org.openbel.framework.common.BELUtilities.noLength;
import static org.openbel.framework.common.BELUtilities.quoteParameter;
import static org.openbel.framework.common.BELUtilities.sizedHashMap;
import static org.openbel.framework.common.BELUtilities.sizedHashSet;
import static org.openbel.framework.common.BELUtilities.substringEquals;
import static org.openbel.framework.common.enums.RelationshipType.fromValue;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.openbel.framework.api.AllocatingIterator;
import org.openbel.framework.api.AnnotationFilterCriteria;
import org.openbel.framework.api.BelDocumentFilterCriteria;
import org.openbel.framework.api.CitationFilterCriteria;
import org.openbel.framework.api.FilterCriteria;
import org.openbel.framework.api.Kam;
import org.openbel.framework.api.Kam.KamEdge;
import org.openbel.framework.api.Kam.KamNode;
import org.openbel.framework.api.KamElementImpl;
import org.openbel.framework.api.KamStoreObjectImpl;
import org.openbel.framework.api.NamespaceFilterCriteria;
import org.openbel.framework.api.RelationshipTypeFilterCriteria;
import org.openbel.framework.api.SimpleKAMEdge;
import org.openbel.framework.api.SimpleKAMNode;
import org.openbel.framework.api.internal.KAMCatalogDao.AnnotationFilter;
import org.openbel.framework.api.internal.KAMCatalogDao.KamFilter;
import org.openbel.framework.api.internal.KAMCatalogDao.KamInfo;
import org.openbel.framework.api.internal.KAMCatalogDao.NamespaceFilter;
import org.openbel.framework.common.AnnotationDefinitionResolutionException;
import org.openbel.framework.common.BELUtilities;
import org.openbel.framework.common.InvalidArgument;
import org.openbel.framework.common.bel.parser.BELParser;
import org.openbel.framework.common.enums.CitationType;
import org.openbel.framework.common.enums.FunctionEnum;
import org.openbel.framework.common.enums.RelationshipType;
import org.openbel.framework.common.model.AnnotationDefinition;
import org.openbel.framework.common.model.CitationAuthorsAnnotationDefinition;
import org.openbel.framework.common.model.CitationCommentAnnotationDefinition;
import org.openbel.framework.common.model.CitationDateAnnotationDefinition;
import org.openbel.framework.common.model.CitationNameAnnotationDefinition;
import org.openbel.framework.common.model.CitationReferenceAnnotationDefinition;
import org.openbel.framework.common.model.CitationTypeAnnotationDefinition;
import org.openbel.framework.common.model.Parameter;
import org.openbel.framework.common.model.Term;
import org.openbel.framework.common.protonetwork.model.SkinnyUUID;
import org.openbel.framework.common.util.PackUtils;
import org.openbel.framework.common.util.Pair;
import org.openbel.framework.core.df.AbstractJdbcDAO;
import org.openbel.framework.core.df.DBConnection;
import org.openbel.framework.core.df.external.CacheableAnnotationDefinitionService;
import org.openbel.framework.core.df.external.CacheableAnnotationDefinitionServiceImpl;
import org.openbel.framework.core.df.external.ExternalResourceException;
/**
*
* @author Julian Ray {@code jray@selventa.com}
*
*/
public final class KAMStoreDaoImpl extends AbstractJdbcDAO implements
KAMStoreDao {
private static final String SELECT_COUNT_NODES =
"SELECT COUNT(*) FROM @.kam_node kn";
private static final String SELECT_COUNT_EDGES =
"SELECT COUNT(*) FROM @.kam_edge ke";
private static final String SELECT_PROTO_NODES_SQL =
"SELECT kn.kam_node_id, kn.function_type_id, kn.node_label_oid FROM @.kam_node kn";
private static final String SELECT_PROTO_EDGES_SQL =
"SELECT ke.kam_edge_id, ke.kam_source_node_id, ke.relationship_type_id, ke.kam_target_node_id FROM @.kam_edge ke";
private static final String SELECT_OBJECTS_VALUE_SQL =
"SELECT type_id, varchar_value, objects_text_id FROM @.objects WHERE objects_id = ?";
private static final String SELECT_OBJECTS_TEXT_SQL =
"SELECT text_value FROM @.objects_text WHERE objects_text_id = ?";
private static final String SELECT_OBJECTS_ID_SQL =
"SELECT objects_id from @.objects WHERE varchar_value = ?";
private static final String SELECT_STATEMENTS_BY_EDGE_SQL =
"select "
+
"s.statement_id, document_id, subject_term_id, relationship_type_id, object_term_id, nested_subject_id, nested_relationship_type_id, nested_object_id "
+
"FROM @.statement s, @.kam_edge_statement_map kesm WHERE s.statement_id = kesm.statement_id and kesm.kam_edge_id = ?";
private static final String SELECT_TERM_BY_ID_SQL =
"SELECT term_label_oid FROM @.term WHERE term_id = ?";
private static final String SELECT_KAM_NODE_ID_BY_TERM_ID_SQL =
"SELECT kam_node_id FROM @.term WHERE term_id = ?";
private static final String SELECT_DOCUMENT_BY_ID_SQL =
"SELECT document_id, name, description, version, copyright, disclaimer, contact_information, license_information, authors FROM @.document_header_information WHERE document_id = ?";
private static final String SELECT_ANNOTATION_BY_ID_SQL =
"SELECT value_oid, annotation_definition_id FROM @.annotation WHERE annotation_id = ?";
private static final String SELECT_ANNOTATIONS_BY_STATEMENT_ID_SQL =
"SELECT b.annotation_id FROM @.statement_annotation_map a LEFT JOIN @.annotation b ON a.annotation_id = b.annotation_id WHERE a.statement_id = ?";
private static final String SELECT_ANNOTATION_TYPE_BY_ID_SQL =
"SELECT annotation_definition_id, name, description, annotation_usage, annotation_definition_type_id FROM @.annotation_definition WHERE annotation_definition_id = ?";
private static final String SELECT_ANNOTATION_TYPES_SQL =
"SELECT annotation_definition_id, name, description, annotation_usage, annotation_definition_type_id FROM @.annotation_definition";
private static final String SELECT_ANNOTATION_TYPES_BY_DOCUMENT_ID_SQL =
"SELECT annotation_definition_id FROM @.document_annotation_def_map WHERE document_id = ?";
private static final String SELECT_ANNOTATION_TYPE_DOMAIN_VALUE_SQL =
"SELECT domain_value_oid, annotation_definition_type_id FROM @.annotation_definition WHERE annotation_definition_id = ?";
private static final String SELECT_KAM_NODE_PARAMETERS_PREFIX_SQL =
"SELECT knp.kam_node_id, knp.kam_global_parameter_id FROM @.kam_node_parameter knp";
private static final String SELECT_KAM_NODE_PARAMETERS_ORDER_SQL =
" ORDER BY knp.kam_node_id, knp.ordinal";
private static final String SELECT_NAMESPACES_BY_DOCUMENT_ID_SQL =
"SELECT namespace_id FROM @.document_namespace_map WHERE document_id = ?";
private static final String SELECT_NAMESPACE_BY_PREFIX_SQL =
"SELECT namespace_id, prefix, resource_location_oid FROM @.namespace WHERE prefix = ?";
private static final String SELECT_DOCUMENTS_SQL =
"SELECT document_id, name, description, version, copyright, disclaimer, contact_information, license_information, authors FROM @.document_header_information";
private static final String SELECT_NAMESPACES_SQL =
"SELECT namespace_id, prefix, resource_location_oid FROM @.namespace";
private static final String SELECT_NAMESPACE_BY_ID_SQL =
"SELECT namespace_id, prefix, resource_location_oid FROM @.namespace WHERE namespace_id = ?";
private static final String SELECT_TERM_PARAMETERS_BY_TERM_ID_SQL =
"SELECT term_parameter_id, namespace_id, parameter_value_oid FROM @.term_parameter WHERE term_id = ? ORDER BY ordinal";
private static final String SELECT_KAM_NODE_IDS_FOR_PARAMETER_FUNCTION_SQL =
"SELECT DISTINCT(k.kam_node_id) FROM @.kam_node k LEFT JOIN @.term t ON k.kam_node_id = t.kam_node_id LEFT JOIN @.term_parameter tp ON t.term_id = tp.term_id WHERE k.function_type_id = ? AND (tp.namespace_id = ? OR (tp.namespace_id IS NULL AND ? IS NULL)) AND tp.parameter_value_oid = ?";
private static final String SELECT_KAM_NODE_IDS_FOR_PARAMETER_SQL =
"SELECT DISTINCT(k.kam_node_id) FROM @.kam_node k LEFT JOIN @.term t ON k.kam_node_id = t.kam_node_id LEFT JOIN @.term_parameter tp ON t.term_id = tp.term_id WHERE (tp.namespace_id = ? OR (tp.namespace_id IS NULL AND ? IS NULL)) AND tp.parameter_value_oid = ?";
private static final String SELECT_KAM_NODE_IDS_FOR_UUID_FUNCTION_SQL =
"SELECT k.kam_node_id FROM @.kam_node k INNER JOIN @.kam_node_parameter p ON k.kam_node_id = p.kam_node_id INNER JOIN @.kam_parameter_uuid u ON p.kam_global_parameter_id = u.kam_global_parameter_id WHERE k.function_type_id = ? AND u.most_significant_bits = ? AND u.least_significant_bits = ?";
private static final String SELECT_KAM_NODE_IDS_FOR_UUID_SQL =
"SELECT DISTINCT(p.kam_node_id) FROM @.kam_node_parameter p LEFT JOIN @.kam_parameter_uuid u ON p.kam_global_parameter_id = u.kam_global_parameter_id WHERE u.most_significant_bits = ? AND u.least_significant_bits = ?";
private static final String SELECT_STATEMENT_BY_ID_SQL =
"SELECT statement_id, document_id, subject_term_id, relationship_type_id, object_term_id, nested_subject_id, nested_relationship_type_id, nested_object_id FROM @.statement WHERE statement_id = ?";
private static final String SELECT_TERMS_IDS_BY_NODE_ID_SQL =
"SELECT term_id FROM @.term WHERE kam_node_id = ?";
private static final String SELECT_KAM_NODES_CONTAINING_KAM_NODE_PARAMETER_SQL =
"SELECT DISTINCT(knp2.kam_node_id) FROM @.kam_node_parameter knp1, @.kam_node_parameter knp2 where knp1.kam_node_id = ? AND knp1.kam_global_parameter_id = knp2.kam_global_parameter_id AND knp2.kam_node_id != ?";
private static final String SELECT_CITATION_ANNOTATIONS_SQL =
"SELECT sam.annotation_id, sam.statement_id, s.document_id FROM @.statement s LEFT JOIN @.statement_annotation_map sam ON s.statement_id = sam.statement_id LEFT JOIN @.annotation a ON a.annotation_id = sam.annotation_id LEFT JOIN @.annotation_definition ad ON ad.annotation_definition_id = a.annotation_definition_id WHERE ad.name IN ('"
+ StringUtils
.join(KAMStoreConstants.CITATION_ANNOTATION_DEFINITION_IDS,
"', '") + "') order by sam.statement_id";
private static final String SELECT_TERM_ID_BY_PARAMETERS_SQL =
"SELECT term_id FROM @.term_parameter WHERE (namespace_id = ? OR (namespace_id IS NULL AND ? IS NULL)) AND parameter_value_oid = ? AND ordinal = ?";
private static final String SELECT_KAM_NODE_UUIDS_SQL =
"SELECT kn.kam_node_id, most_significant_bits, least_significant_bits " +
"FROM @.kam_node kn, @.kam_node_parameter knp, @.kam_parameter_uuid kpu " +
"WHERE kn.kam_node_id = knp.kam_node_id AND " +
"knp.kam_global_parameter_id = kpu.kam_global_parameter_id " +
"ORDER BY kn.kam_node_id";
private static final String SELECT_KAM_NODE_BY_LABEL_SQL =
"SELECT kn.kam_node_id " +
"FROM @.kam_node kn, @.objects o " +
"WHERE kn.node_label_oid = o.objects_id and o.varchar_value = ?";
private static final String $TERM_PARAMETER_TABLE = "@.term_parameter tp%d";
private static final String $KAM_PARAMETER_UUID_TABLE = "@.kam_parameter_uuid kpu%d";
private static final String $TERM_ID_JOIN = "tp%d.term_id = t.term_id ";
private static final String $ORDINAL_JOIN = "tp%d.ordinal = %d ";
private static final String $KAM_UUID_JOIN = "tp%d.kam_global_parameter_id = kpu%d.kam_global_parameter_id ";
private static final String $UUID_JOIN = "(kpu%d.most_significant_bits = ? AND kpu%d.least_significant_bits = ?) ";
private static final String SELECT_KAM_NODE_BY_TERM_UUIDS =
"SELECT kn.kam_node_id " +
"FROM @.kam_node kn, @.term t, @.objects ot, %s, %s " +
"WHERE kn.function_type_id = ? AND t.kam_node_id = kn.kam_node_id AND t.term_label_oid = ot.objects_id AND ot.varchar_value = ? AND " +
"%s AND %s AND %s AND ( %s )";
private static final String ANY_NUMBER_PLACEHOLDER = "#";
private static final int ANY_NUMBER_PLACEHOLDER_LENGTH =
ANY_NUMBER_PLACEHOLDER.length();
private static final Pattern NUMBER_REGEX_PATTERN = Pattern.compile("\\d+");
private static final Pattern ANY_NUMBER_REGEX_PATTERN = Pattern
.compile("\\d+|" + Pattern.quote(ANY_NUMBER_PLACEHOLDER));
private static final String CITATION = "Citation";
private static final Set<String> citationTypes = getCitationDefinitions();
// Caches
private Map<Integer, BelTerm> termCache =
new ConcurrentHashMap<Integer, BelTerm>();
private Map<Integer, String> objectValueCache =
new ConcurrentHashMap<Integer, String>();
private Map<String, Integer> objectValueReverseCache =
new ConcurrentHashMap<String, Integer>();
private Map<Integer, Namespace> namespaceCache =
new ConcurrentHashMap<Integer, Namespace>();
private Map<String, Namespace> namespacePrefixCache =
new ConcurrentHashMap<String, Namespace>();
private Map<Integer, AnnotationType> annotationTypeCache =
new ConcurrentHashMap<Integer, AnnotationType>();
private Map<Integer, List<BelStatement>> supportingEvidenceCache =
new ConcurrentHashMap<Integer, List<BelStatement>>();
private Map<Integer, List<BelTerm>> supportingTermCache =
new ConcurrentHashMap<Integer, List<BelTerm>>();
private Map<String, Integer> supportingTermLabelReverseCache =
new ConcurrentHashMap<String, Integer>();
private Map<Integer, Integer> kamNodeTermCache =
new ConcurrentHashMap<Integer, Integer>();
private Map<Integer, BelStatement> statementCache =
new ConcurrentHashMap<Integer, BelStatement>();
private Map<Integer, BelDocumentInfo> documentCache =
new ConcurrentHashMap<Integer, BelDocumentInfo>();
private Map<Integer, List<TermParameter>> termParameterCache =
new ConcurrentHashMap<Integer, List<TermParameter>>();
private Map<Integer, Annotation> annotationCache =
new ConcurrentHashMap<Integer, KAMStoreDaoImpl.Annotation>();
private Map<Integer, List<Annotation>> statementAnnotationCache =
new ConcurrentHashMap<Integer, List<Annotation>>();
private Map<Integer, List<String>> annotationTypeValueCache =
new ConcurrentHashMap<Integer, List<String>>();
private Map<Integer, List<Integer>> nodeExampleMatchCache =
new ConcurrentHashMap<Integer, List<Integer>>();
//citation caches
private Map<String, Citation> citationMap = null;
private Map<Integer, List<Citation>> belDocumentCitationsMap = null;
private CacheableAnnotationDefinitionService cacheableAnnotationDefinitionService;
private static SimpleDateFormat dateFormat = new SimpleDateFormat(
KAMStoreConstants.DATE_FORMAT);
/**
* Creates a KAMStoreDaoImpl from the Jdbc {@link Connection} that will
* be used to load the KAM.
*
* @param dbc {@link Connection}, the database connection which should be
* non-null and already open for sql execution.
* @throws InvalidArgument - Thrown if {@code dbc} is null or the sql
* connection is already closed.
* @throws SQLException - Thrown if a sql error occurred while loading
* the KAM.
*/
public KAMStoreDaoImpl(String schemaName, DBConnection dbc)
throws SQLException {
super(dbc, schemaName);
if (dbc == null) {
throw new InvalidArgument("dbc is null");
}
if (dbc.getConnection().isClosed()) {
throw new InvalidArgument("dbc is closed and cannot be used");
}
this.cacheableAnnotationDefinitionService =
new CacheableAnnotationDefinitionServiceImpl();
}
@Override
public List<BelDocumentInfo> getBelDocumentInfos() throws SQLException {
List<BelDocumentInfo> list = new ArrayList<BelDocumentInfo>();
ResultSet rset = null;
try {
PreparedStatement ps = getPreparedStatement(SELECT_DOCUMENTS_SQL);
rset = ps.executeQuery();
while (rset.next()) {
BelDocumentInfo belDocumentInfo = getBelDocumentInfo(rset);
list.add(belDocumentInfo);
// This might save us time later on
if (!documentCache.containsKey(belDocumentInfo.getId())) {
documentCache.put(belDocumentInfo.getId(), belDocumentInfo);
}
}
} finally {
close(rset);
}
return list;
}
@Override
public List<Namespace> getNamespaces() throws SQLException {
List<Namespace> list = new ArrayList<Namespace>();
ResultSet rset = null;
try {
PreparedStatement ps = getPreparedStatement(SELECT_NAMESPACES_SQL);
rset = ps.executeQuery();
while (rset.next()) {
Namespace namespace = getNamespace(rset);
list.add(namespace);
// This might save us time later on
if (!namespaceCache.containsKey(namespace.getId())) {
namespaceCache.put(namespace.getId(), namespace);
}
if (!namespacePrefixCache.containsKey(namespace.getPrefix())) {
namespacePrefixCache.put(namespace.getPrefix(), namespace);
}
}
} finally {
close(rset);
}
return list;
}
@Override
public BelStatement getBelStatement(Integer belStatementId)
throws SQLException {
// See if this statement is already cached
if (statementCache.containsKey(belStatementId)) {
return statementCache.get(belStatementId);
}
BelStatement belStatement = null;
ResultSet rset = null;
PreparedStatement ps = null;
try {
ps = getPreparedStatement(SELECT_STATEMENT_BY_ID_SQL);
ps.setInt(1, belStatementId);
rset = ps.executeQuery();
if (rset.next()) {
belStatement = getStatement(rset);
}
} finally {
close(rset);
close(ps);
}
// put this list into the evidence cache
statementCache.put(belStatementId, belStatement);
return belStatement;
}
@Override
public List<BelStatement> getSupportingEvidence(KamEdge kamEdge)
throws SQLException {
return getSupportingEvidence(kamEdge.getId());
}
@Override
public List<BelStatement> getSupportingEvidence(KamEdge kamEdge,
AnnotationFilter filter) throws SQLException {
final List<BelStatement> stmts = getSupportingEvidence(kamEdge);
if (filter == null) {
return stmts;
}
final List<FilterCriteria> criteria = filter.getFilterCriteria();
final Map<AnnotationType, AnnotationFilterCriteria> amap =
sizedHashMap(criteria.size());
for (final FilterCriteria c : criteria) {
final AnnotationFilterCriteria afc = (AnnotationFilterCriteria) c;
amap.put(afc.getAnnotationType(), afc);
}
final Iterator<BelStatement> stmtIt = stmts.iterator();
while (stmtIt.hasNext()) {
final BelStatement stmt = stmtIt.next();
final List<Annotation> annotations = stmt.getAnnotationList();
for (final FilterCriteria c : criteria) {
// criteria is invalid, continue
if (c == null) {
continue;
}
final AnnotationFilterCriteria afc =
(AnnotationFilterCriteria) c;
// criteria's annotation type is invalid, continue
if (afc.getAnnotationType() == null) {
continue;
}
Annotation matchedAnnotation = null;
for (final Annotation annotation : annotations) {
if (annotation.getAnnotationType() == afc
.getAnnotationType()) {
matchedAnnotation = annotation;
}
}
if (matchedAnnotation == null) {
if (c.isInclude()) {
stmtIt.remove();
}
} else {
boolean valueMatch = afc.getValues().contains(
matchedAnnotation.getValue());
if (valueMatch && !c.isInclude()) {
stmtIt.remove();
}
}
}
}
return stmts;
}
/**
*
* @param kamEdgeId
* @return
* @throws SQLException
*/
@Override
public List<BelStatement> getSupportingEvidence(Integer kamEdgeId)
throws SQLException {
if (kamEdgeId == null) {
throw new IllegalArgumentException("KAM edge ID cannot be null.");
}
// See if this evidence is already cached
if (supportingEvidenceCache.containsKey(kamEdgeId)) {
return supportingEvidenceCache.get(kamEdgeId);
}
List<BelStatement> list = new ArrayList<BelStatement>();
ResultSet rset = null;
try {
PreparedStatement ps =
getPreparedStatement(SELECT_STATEMENTS_BY_EDGE_SQL);
ps.setInt(1, kamEdgeId);
rset = ps.executeQuery();
while (rset.next()) {
list.add(getStatement(rset));
}
} finally {
close(rset);
}
// put this list into the evidence cache
supportingEvidenceCache.put(kamEdgeId, list);
return list;
}
@Override
public List<BelTerm> getSupportingTerms(KamNode kamNode,
NamespaceFilter namespaceFilter) throws SQLException {
List<BelTerm> termList = getSupportingTerms(kamNode);
if (namespaceFilter != null) {
for (FilterCriteria criterion : namespaceFilter.getFilterCriteria()) {
NamespaceFilterCriteria nfc =
(NamespaceFilterCriteria) criterion;
Set<Integer> targetNamespaceIds = new HashSet<Integer>();
for (Namespace ns : nfc.getValues()) {
targetNamespaceIds.add(ns.getId());
}
List<BelTerm> matchedTerms = new ArrayList<BelTerm>();
for (BelTerm term : termList) {
List<TermParameter> params = getTermParameters(term);
for (TermParameter param : params) {
if (param.namespace != null
&& targetNamespaceIds.contains(param.namespace
.getId())) {
matchedTerms.add(term);
break;
}
}
}
if (criterion.isInclude()) {
termList.retainAll(matchedTerms);
} else {
// must be exclude
termList.removeAll(matchedTerms);
}
}
}
return termList;
}
@Override
public List<BelTerm> getSupportingTerms(KamNode kamNode)
throws SQLException {
return getSupportingTerms(kamNode.getId());
}
private List<BelTerm> getSupportingTerms(Integer kamNodeId)
throws SQLException {
// See if this evidence is already cached
if (supportingTermCache.containsKey(kamNodeId)) {
return supportingTermCache.get(kamNodeId);
}
List<BelTerm> list = new ArrayList<BelTerm>();
ResultSet rset = null;
try {
PreparedStatement ps =
getPreparedStatement(SELECT_TERMS_IDS_BY_NODE_ID_SQL);
ps.setInt(1, kamNodeId);
rset = ps.executeQuery();
while (rset.next()) {
list.add(getBelTermById(rset.getInt(1)));
}
} finally {
close(rset);
}
// put this list into the evidence cache
supportingTermCache.put(kamNodeId, list);
for (BelTerm belTerm : list) {
supportingTermLabelReverseCache.put(belTerm.getLabel(), kamNodeId);
}
return list;
}
@SuppressWarnings("unchecked")
@Override
public Integer getKamNodeId(String belTerm) throws SQLException {
if (belTerm == null) throw new NullPointerException();
// See if the bel term is already mapped
if (supportingTermLabelReverseCache.containsKey(belTerm)) {
return supportingTermLabelReverseCache.get(belTerm);
}
// parse the BelTerm
Term t;
try {
t = BELParser.parseTerm(belTerm);
} catch (Exception e) {
// invalid BEL
return null;
}
// convert to short form
String shortForm = t.toBELShortForm();
// 1: short circuit; try by kam node label
PreparedStatement ps = getPreparedStatement(SELECT_KAM_NODE_BY_LABEL_SQL);
ResultSet rset = null;
try {
ps.setString(1, shortForm);
rset = ps.executeQuery();
if (rset.next()) {
int kamNodeId = rset.getInt(1);
supportingTermLabelReverseCache.put(belTerm, kamNodeId);
return kamNodeId;
}
} finally {
close(rset);
}
// 2: try by bel terms
Collection<Integer> possibleTermIds = null;
int ordinal = 0;
for (Parameter param : t.getAllParametersLeftToRight()) {
Integer namespaceId = null;
if (param.getNamespace() != null
&& StringUtils.isNotBlank(param.getNamespace().getPrefix())) {
Namespace ns =
getNamespaceByPrefix(param.getNamespace().getPrefix());
if (ns != null) {
namespaceId = ns.getId();
}
}
String paramValue = param.getValue();
if (paramValue.startsWith("\"") && paramValue.endsWith("\"")) {
paramValue = paramValue.substring(1, paramValue.length() - 1);
}
Integer valueOid = getObjectIdByValue(paramValue);
if (valueOid == null) {
// could not find label for param
if (possibleTermIds != null) {
possibleTermIds.clear();
}
break;
}
rset = null;
try {
ps = getPreparedStatement(SELECT_TERM_ID_BY_PARAMETERS_SQL);
// set twice to handle is null, see http://stackoverflow.com/questions/4215135/
if (namespaceId == null) {
ps.setNull(1, Types.INTEGER);
ps.setNull(2, Types.INTEGER);
} else {
ps.setInt(1, namespaceId);
ps.setInt(2, namespaceId);
}
ps.setInt(3, valueOid);
ps.setInt(4, ordinal);
rset = ps.executeQuery();
Set<Integer> termIdsWithParam = new HashSet<Integer>();
while (rset.next()) {
termIdsWithParam.add(rset.getInt(1));
}
if (possibleTermIds == null) {
// first param
possibleTermIds = termIdsWithParam;
} else {
possibleTermIds =
CollectionUtils.intersection(possibleTermIds,
termIdsWithParam);
}
} finally {
close(rset);
}
// no need to continue to next param if possibleTermIds is empty
if (possibleTermIds.isEmpty()) {
break;
}
ordinal++;
}
Integer kamNodeId = null;
// iterate over all possible terms and check for label matches
if (possibleTermIds != null) {
for (Integer termId : possibleTermIds) {
BelTerm term = getBelTermById(termId);
if (term.getLabel().equals(shortForm)) {
kamNodeId = getKamNodeId(term);
break;
}
}
}
if (kamNodeId != null) {
supportingTermLabelReverseCache.put(belTerm, kamNodeId);
}
return kamNodeId;
}
/**
*
* @param belTerm
* @return
* @throws SQLException
*/
@Override
public Integer getKamNodeId(BelTerm belTerm) throws SQLException {
// See if the bel term is already mapped
if (kamNodeTermCache.containsKey(belTerm.getId())) {
return kamNodeTermCache.get(belTerm.getId());
}
ResultSet rset = null;
Integer kamNodeId = null;
try {
PreparedStatement ps =
getPreparedStatement(SELECT_KAM_NODE_ID_BY_TERM_ID_SQL);
ps.setInt(1, belTerm.getId());
rset = ps.executeQuery();
if (rset.next()) {
kamNodeId = rset.getInt(1);
}
} finally {
close(rset);
}
// Add to the cache
kamNodeTermCache.put(belTerm.getId(), kamNodeId);
return kamNodeId;
}
/**
*
*/
@Override
public List<TermParameter> getTermParameters(BelTerm belTerm)
throws SQLException {
return getTermParameters(belTerm.getId());
}
/**
*
* @param belTermId
* @return
* @throws SQLException
*/
private List<TermParameter> getTermParameters(Integer belTermId)
throws SQLException {
// See if this evidence is already cached
if (termParameterCache.containsKey(belTermId)) {
return termParameterCache.get(belTermId);
}
List<TermParameter> list = new ArrayList<TermParameter>();
ResultSet rset = null;
try {
PreparedStatement ps =
getPreparedStatement(SELECT_TERM_PARAMETERS_BY_TERM_ID_SQL);
ps.setInt(1, belTermId);
rset = ps.executeQuery();
while (rset.next()) {
Integer termParameterId = rset.getInt(1);
Namespace namespace = getNamespaceById(rset.getInt(2));
String parameterValue = getObjectValueById(rset.getInt(3));
list.add(new TermParameter(termParameterId, namespace,
parameterValue));
}
} finally {
close(rset);
}
// put this list into the evidence cache
termParameterCache.put(belTermId, list);
return list;
}
/**
* {@inheritDoc}
*/
@Override
public List<Integer> getKamNodeCandidates(FunctionEnum functionType,
Namespace namespace, String parameterValue) throws SQLException {
// if function is null then delegate to overloaded sibling
if (functionType == null) {
return getKamNodeCandidates(namespace, parameterValue);
}
// guard against blank parameter value
if (noLength(parameterValue)) {
throw new InvalidArgument("parameterValue is blank");
}
final Integer objectId = getParameterObjectValueId(parameterValue);
if (objectId == null) {
// we couldn't find a string for parameterValue, so return no matches
return new ArrayList<Integer>();
}
PreparedStatement ps =
getPreparedStatement(SELECT_KAM_NODE_IDS_FOR_PARAMETER_FUNCTION_SQL);
ps.setInt(1, functionType.getValue());
// set namespace to search on, which may be null
if (namespace == null || namespace.getId() == null) {
ps.setNull(2, Types.INTEGER);
ps.setNull(3, Types.INTEGER);
} else {
final int nid = namespace.getId();
ps.setInt(2, nid);
ps.setInt(3, nid);
}
ps.setInt(4, objectId);
return queryForKamNodeCandidates(ps);
}
/**
* {@inheritDoc}
*/
@Override
public List<Integer> getKamNodeCandidates(Namespace namespace,
String parameterValue) throws SQLException {
// guard against blank parameter value
if (noLength(parameterValue)) {
throw new InvalidArgument("parameterValue is blank");
}
final Integer objectId = getParameterObjectValueId(parameterValue);
if (objectId == null) {
// we couldn't find a string for parameterValue, so return no matches
return new ArrayList<Integer>();
}
PreparedStatement ps =
getPreparedStatement(SELECT_KAM_NODE_IDS_FOR_PARAMETER_SQL);
// set namespace to search on, which may be null
if (namespace == null || namespace.getId() == null) {
ps.setNull(1, Types.INTEGER);
ps.setNull(2, Types.INTEGER);
} else {
final int nid = namespace.getId();
ps.setInt(1, nid);
ps.setInt(2, nid);
}
ps.setInt(3, objectId);
return queryForKamNodeCandidates(ps);
}
/**
* {@inheritDoc}
*/
@Override
public List<Integer> getKamNodeCandidates(SkinnyUUID uuid)
throws SQLException {
if (uuid == null) {
throw new InvalidArgument("uuid", uuid);
}
PreparedStatement ps =
getPreparedStatement(SELECT_KAM_NODE_IDS_FOR_UUID_SQL);
ps.setLong(1, uuid.getMostSignificantBits());
ps.setLong(2, uuid.getLeastSignificantBits());
return queryForKamNodeCandidates(ps);
}
/**
* {@inheritDoc}
*/
@Override
public List<Integer> getKamNodeCandidates(FunctionEnum functionType,
SkinnyUUID uuid) throws SQLException {
if (functionType == null) {
return getKamNodeCandidates(uuid);
}
if (uuid == null) {
throw new InvalidArgument("uuid", null);
}
PreparedStatement ps =
getPreparedStatement(SELECT_KAM_NODE_IDS_FOR_UUID_FUNCTION_SQL);
ps.setInt(1, functionType.getValue());
ps.setLong(2, uuid.getMostSignificantBits());
ps.setLong(3, uuid.getLeastSignificantBits());
return queryForKamNodeCandidates(ps);
}
/**
* {@inheritDoc}
*/
@Override
public List<Integer> getKamNodeCandidates(KamNode example)
throws SQLException {
if (example == null || example.getId() == null) {
throw new InvalidArgument("example", example);
}
List<Integer> kamNodeIds = nodeExampleMatchCache.get(example.getId());
if (kamNodeIds != null) {
return kamNodeIds;
}
PreparedStatement ps =
getPreparedStatement(SELECT_KAM_NODES_CONTAINING_KAM_NODE_PARAMETER_SQL);
ps.setInt(1, example.getId());
ps.setInt(2, example.getId());
kamNodeIds = queryForKamNodeCandidates(ps);
nodeExampleMatchCache.put(example.getId(), kamNodeIds);
return kamNodeIds;
}
/**
* {@inheritDoc}
*/
@Override
public Map<Integer, Set<SkinnyUUID>> getKamNodeUUIDs() throws SQLException {
PreparedStatement ps = getPreparedStatement(SELECT_KAM_NODE_UUIDS_SQL);
Map<Integer, Set<SkinnyUUID>> uuidmap =
new HashMap<Integer, Set<SkinnyUUID>>();
ResultSet rset = null;
try {
rset = ps.executeQuery();
while (rset.next()) {
Integer knid = rset.getInt(1);
Long msb = rset.getLong(2);
Long lsb = rset.getLong(3);
Set<SkinnyUUID> uuids = uuidmap.get(knid);
if (uuids == null) {
uuids = new HashSet<SkinnyUUID>();
uuidmap.put(knid, uuids);
}
uuids.add(new SkinnyUUID(msb, lsb));
}
} finally {
close(rset);
}
return uuidmap;
}
/**
* {@inheritDoc}
*/
@Override
public Integer getKamNodeForTerm(String term, FunctionEnum fx,
SkinnyUUID[] u) throws SQLException {
if (term == null) throw new InvalidArgument("term is null");
if (fx == null) throw new InvalidArgument("fx is null");
if (u == null) throw new InvalidArgument("u is null");
if (u.length == 0) throw new InvalidArgument("u is empty");
// build sql by concatenation
String sql = SELECT_KAM_NODE_BY_TERM_UUIDS;
String tpt = "";
String kput = "";
String tidj = "";
String oj = "";
String kuj = "";
String uj = "";
for (int i = 0; i < u.length; i++) {
if (i < (u.length - 1)) {
tpt = tpt + format($TERM_PARAMETER_TABLE, i) + ",";
kput = kput + format($KAM_PARAMETER_UUID_TABLE, i) + ",";
tidj = tidj + format($TERM_ID_JOIN, i) + " AND ";
oj = oj + format($ORDINAL_JOIN, i, i) + " AND ";
kuj = kuj + format($KAM_UUID_JOIN, i, i) + " AND ";
uj = uj + format($UUID_JOIN, i, i) + " AND ";
} else {
tpt = tpt + format($TERM_PARAMETER_TABLE, i);
kput = kput + format($KAM_PARAMETER_UUID_TABLE, i);
tidj = tidj + format($TERM_ID_JOIN, i);
oj = oj + format($ORDINAL_JOIN, i, i);
kuj = kuj + format($KAM_UUID_JOIN, i, i);
uj = uj + format($UUID_JOIN, i, i);
}
}
sql = format(sql, tpt, kput, tidj, oj, kuj, uj);
PreparedStatement ps = getPreparedStatement(sql);
ps.setInt(1, fx.getValue());
ps.setString(2, term);
int index = 3;
for (int i = 0; i < u.length; i++) {
SkinnyUUID item = u[i];
ps.setLong(index, item.getMostSignificantBits());
ps.setLong(index + 1, item.getLeastSignificantBits());
index += 2;
}
ResultSet rset = null;
try {
rset = ps.executeQuery();
if (rset.next()) {
int kamNodeId = rset.getInt(1);
return kamNodeId;
}
return null;
} finally {
close(rset);
}
}
private List<Integer> queryForKamNodeCandidates(final PreparedStatement ps)
throws SQLException {
List<Integer> kamNodeIdList = new ArrayList<Integer>();
ResultSet rset = null;
try {
rset = ps.executeQuery();
while (rset.next()) {
Integer kamNodeId = rset.getInt(1);
kamNodeIdList.add(kamNodeId);
}
} finally {
close(rset);
}
return kamNodeIdList;
}
private Integer getParameterObjectValueId(String parameterValue)
throws SQLException {
// remove quoted parameter values, set to null if only ""
if (parameterValue.startsWith("\"") && parameterValue.endsWith("\"")) {
parameterValue = parameterValue.length() == 2 ? null :
parameterValue.substring(1, parameterValue.length() - 1);
}
final Integer objectId = getObjectIdByValue(parameterValue);
return objectId;
}
/**
*
* @param belTermId
* @return
* @throws SQLException
*/
private BelTerm getBelTermById(Integer belTermId) throws SQLException {
// See if the term is cached
if (termCache.containsKey(belTermId)) {
return termCache.get(belTermId);
}
BelTerm belTerm = null;
ResultSet rset = null;
try {
PreparedStatement ps = getPreparedStatement(SELECT_TERM_BY_ID_SQL);
ps.setInt(1, belTermId);
rset = ps.executeQuery();
if (rset.next()) {
String label = getObjectValueById(rset.getInt(1));
// Get the term parameters for this term and reconstruct the
// label based on its original encoding
List<TermParameter> tparams = getTermParameters(belTermId);
for (TermParameter termParameter : tparams) {
String nsprefix = termParameter.namespace != null ?
termParameter.namespace.prefix : null;
// quote parameter if necessary
String paramValue = termParameter.parameterValue;
paramValue = quoteParameter(paramValue);
final String termParam;
if (nsprefix != null) {
termParam = nsprefix + ":" + paramValue;
} else {
termParam = paramValue;
}
label = label.replaceFirst("#", termParam);
}
belTerm = new BelTerm(belTermId, label);
}
} finally {
close(rset);
}
// Insert this term into the cache
termCache.put(belTermId, belTerm);
return belTerm;
}
/**
*
* @param namespaceId
* @return
* @throws SQLException
*/
private Namespace getNamespaceById(Integer namespaceId) throws SQLException {
// See if the term is cached
if (namespaceCache.containsKey(namespaceId)) {
return namespaceCache.get(namespaceId);
}
Namespace namespace = null;
ResultSet rset = null;
try {
PreparedStatement ps =
getPreparedStatement(SELECT_NAMESPACE_BY_ID_SQL);
ps.setInt(1, namespaceId);
rset = ps.executeQuery();
if (rset.next()) {
namespace = getNamespace(rset);
}
} finally {
close(rset);
}
// Insert this term into the cache
if (namespace != null) {
namespaceCache.put(namespaceId, namespace);
namespacePrefixCache.put(namespace.getPrefix(), namespace);
}
return namespace;
}
/**
*
* @param belDocumentId
* @return
* @throws SQLException
*/
private BelDocumentInfo getBelDocumentInfoById(Integer belDocumentId)
throws SQLException {
// See if the document is cached
if (documentCache.containsKey(belDocumentId)) {
return documentCache.get(belDocumentId);
}
ResultSet rset = null;
BelDocumentInfo belDocumentInfo = null;
try {
PreparedStatement ps =
getPreparedStatement(SELECT_DOCUMENT_BY_ID_SQL);
ps.setInt(1, belDocumentId);
rset = ps.executeQuery();
if (rset.next()) {
belDocumentInfo = getBelDocumentInfo(rset);
}
} finally {
close(rset);
}
// Insert this document into the cache
documentCache.put(belDocumentId, belDocumentInfo);
return belDocumentInfo;
}
/**
*
* @param belDocumentId
* @return
* @throws SQLException
*/
private List<Namespace> getNamespacesByDocumentId(Integer belDocumentId)
throws SQLException {
ResultSet rset = null;
List<Namespace> list = new ArrayList<Namespace>();
try {
PreparedStatement ps =
getPreparedStatement(SELECT_NAMESPACES_BY_DOCUMENT_ID_SQL);
ps.setInt(1, belDocumentId);
rset = ps.executeQuery();
while (rset.next()) {
Integer namespaceId = rset.getInt(1);
list.add(getNamespaceById(namespaceId));
}
} finally {
close(rset);
}
return list;
}
/**
* Obtain a namespace by prefix
* @param prefix
* @return
* @throws SQLException
*/
private Namespace getNamespaceByPrefix(String prefix) throws SQLException {
if (namespacePrefixCache.containsKey(prefix)) {
return namespacePrefixCache.get(prefix);
}
ResultSet rset = null;
Namespace ns = null;
try {
PreparedStatement ps =
getPreparedStatement(SELECT_NAMESPACE_BY_PREFIX_SQL);
ps.setString(1, prefix);
rset = ps.executeQuery();
if (rset.next()) {
ns = getNamespace(rset);
}
} finally {
close(rset);
}
if (ns != null) {
namespacePrefixCache.put(prefix, ns);
namespaceCache.put(ns.getId(), ns);
}
return ns;
}
/**
*
* @param belDocumentId
* @return
* @throws SQLException
*/
private List<AnnotationType> getAnnotationTypesByDocumentId(
Integer belDocumentId) throws SQLException {
ResultSet rset = null;
List<AnnotationType> list = new ArrayList<AnnotationType>();
try {
PreparedStatement ps =
getPreparedStatement(SELECT_ANNOTATION_TYPES_BY_DOCUMENT_ID_SQL);
ps.setInt(1, belDocumentId);
rset = ps.executeQuery();
while (rset.next()) {
Integer annotationTypeId = rset.getInt(1);
AnnotationType type = getAnnotationTypeById(annotationTypeId);
addAnnotationType(list, type);
}
} finally {
close(rset);
}
return list;
}
/**
* Adds the {@link AnnotationType type} to the {@link List list} based on:
* <ul>
* <li>if {@link CitationNameAnnotationDefinition#ANNOTATION_DEFINITION_ID}
* is the annotation then add a Citation {@link AnnotationType annotation type}</li>
* <li>if another Citation field is the annotation then ignore it</li>
* <li>any other annotation is added</li>
* </ul>
*
* @param list the {@link List annotation type list}
* @param type the {@link AnnotationType annotation type} to add
*/
private void addAnnotationType(List<AnnotationType> list,
AnnotationType type) {
boolean isCitation = citationTypes.contains(type.getName());
if (isCitation) {
// add Citation is we have seen a CitationName annotation, throw away the others
boolean isCitationName =
CitationNameAnnotationDefinition.ANNOTATION_DEFINITION_ID
.equals(type.getName());
if (isCitationName) {
list.add(createCitationType(type));
}
} else {
list.add(type);
}
}
/**
*
* @param statementId
* @return
* @throws SQLException
*/
private List<Annotation> getAnnotationsByStatementId(Integer statementId)
throws SQLException {
// See if the annotation set for this statement is already cached
if (statementAnnotationCache.containsKey(statementId)) {
return statementAnnotationCache.get(statementId);
}
ResultSet rset = null;
List<Annotation> list = new ArrayList<Annotation>();
try {
PreparedStatement ps =
getPreparedStatement(SELECT_ANNOTATIONS_BY_STATEMENT_ID_SQL);
ps.setInt(1, statementId);
rset = ps.executeQuery();
while (rset.next()) {
Integer annotationId = rset.getInt(1);
list.add(getAnnotation(annotationId));
}
} finally {
close(rset);
}
// Add this to the cache
statementAnnotationCache.put(statementId, list);
return list;
}
/**
* Builds an SQL snippet to help select KAM edges that match <code>kamFilter</code>.
*
* The returned SQL snippet is a SELECT statement that queries the {@code kam_edge_id}'s of
* the KAM edges that satisfy the provided KAM filter. Callers may INNER JOIN with the snippet
* to select more fields of the {@code kam_edge} table.
*
* @param kamFilter A selection of KAM filter criteria to apply in including or excluding edges.
* <code>kamFilter</code> can not be null.
* @return A pair of a SQL snippet and a list of all parameters to bind when using the
* generated SQL in a PreparedStatement.
*/
private Pair<String, List<String>> getFilteredSelectProtoEdgesSql(
KamFilter kamFilter) {
List<FilterCriteria> criteria = kamFilter.getFilterCriteria();
// Create a StringBuilder for each part of the SQL query.
StringBuilder baseQuery =
new StringBuilder("SELECT ke.kam_edge_id FROM @.kam_edge ke");
StringBuilder citationJoins = new StringBuilder();
StringBuilder whereClause = new StringBuilder(" WHERE TRUE");
StringBuilder havingClause = new StringBuilder();
boolean joinedStatements = false, joinedAnnotations = false, groupedByEdge =
false;
// Create lists to contain the Strings that will need to
// be bound in the PreparedStatement
ArrayList<String> annotationParameters = new ArrayList<String>(), citationParameters =
new ArrayList<String>();
int uniqueSubselectId = 0;
// Matching some of the types of FilterCriteria require an SQL aggregate function
// that computes boolean AND or OR over a group.
// Use MAX(CASE WHEN some_boolean THEN 1 ELSE 0 END)=1 for a boolean OR aggregate function.
// Use MIN(CASE WHEN some_boolean THEN 1 ELSE 0 END)=1 for a boolean AND aggregate function.
for (FilterCriteria criterion : criteria) {
boolean include = criterion.isInclude();
// The filter criteria are ANDed together.
if (criterion instanceof RelationshipTypeFilterCriteria) {
Set<RelationshipType> relationships =
((RelationshipTypeFilterCriteria) criterion)
.getValues();
int size = relationships.size();
if (size == 0) {
// There is nothing on which to match.
continue;
}
whereClause.append(" AND ");
// An include filter matches the edges that have at least one of the provided
// relationship types (i.e. the tests of equality of the edge relationship type to each
// of the provided relationship types are ORed).
// An exclude filter matches the edges that have none of the provided relationship types
// (i.e. the tests of equality of the edge relationship type to each of the provided
// relationship types are ORed, then finally complemented (NOT)).
if (!include) {
whereClause.append("NOT ");
}
whereClause.append("(");
int count = 0;
for (RelationshipType relationship : relationships) {
whereClause.append("ke.relationship_type_id=");
whereClause.append(relationship.getValue());
if (++count < size) {
whereClause.append(" OR ");
}
}
whereClause.append(")");
} else if (criterion instanceof BelDocumentFilterCriteria) {
Set<BelDocumentInfo> documents =
((BelDocumentFilterCriteria) criterion).getValues();
int size = documents.size();
if (size == 0) {
// There is nothing on which to match.
continue;
}
// The provided documents are matched against the documents associated with the
// supporting evidence for the KAM edges, that is the statements that are associated
// with the edge. This filter requires joining with the <code>statement</code> table.
if (!joinedStatements) {
baseQuery
.append(" LEFT OUTER JOIN @.kam_edge_statement_map kesm ON ke.kam_edge_id=kesm.kam_edge_id");
baseQuery
.append(" LEFT OUTER JOIN @.statement s ON kesm.statement_id=s.statement_id");
joinedStatements = true;
}
// An include filter matches only the edges that have supporting evidence statements that
// have a document that is one of the provided documents. A predicate expression
// for "being one of the provided documents" must be evaluated for all documents
// of supporting statements. If that predicate is true for any document then the edge
// passes the filter (i.e. a boolean OR).
// An exclude filter matches only the edges all of whose supporting evidence statements do
// not have documents that are one of the provided documents. The predicate expression
// of "being one of the provided documents" must be false for all documents of all
// supporting statements for an edge to pass an "exclude" filter (i.e. NOT OR).
// The query will need to group by edge and use a HAVING clause to match the predicate.
if (!groupedByEdge) {
havingClause = new StringBuilder(" HAVING TRUE");
groupedByEdge = true;
}
havingClause.append(" AND");
if (!include) {
havingClause.append(" NOT");
}
havingClause.append(" MAX(CASE WHEN (");
int count = 0;
for (BelDocumentInfo doc : documents) {
havingClause.append("s.document_id=");
havingClause.append(doc.getId());
if (++count < size) {
havingClause.append(" OR ");
}
}
havingClause.append(") THEN 1 ELSE 0 END)=1");
} else if (criterion instanceof AnnotationFilterCriteria) {
AnnotationFilterCriteria ac =
(AnnotationFilterCriteria) criterion;
Integer type = ac.getAnnotationType().getId();
Set<String> annotations = ac.getValues();
int size = annotations.size();
if (size == 0) {
continue;
}
// This case is very similar to the BelDocumentFilterCriteria case, except
// that the match is performed on non-citation annotations.
// The query will need to join on the <code>statement</code> table.
if (!joinedStatements) {
baseQuery
.append(" LEFT OUTER JOIN @.kam_edge_statement_map kesm ON ke.kam_edge_id=kesm.kam_edge_id");
baseQuery
.append(" LEFT OUTER JOIN @.statement s ON kesm.statement_id=s.statement_id");
joinedStatements = true;
}
// The query will need to group by edge.
if (!groupedByEdge) {
havingClause = new StringBuilder(" HAVING TRUE");
groupedByEdge = true;
}
// The query will also need to join the <code>annotations</code> table.
if (!joinedAnnotations) {
baseQuery
.append(" LEFT OUTER JOIN @.statement_annotation_map sam ON s.statement_id=sam.statement_id");
baseQuery
.append(" LEFT OUTER JOIN @.annotation a ON sam.annotation_id=a.annotation_id");
baseQuery
.append(" LEFT OUTER JOIN @.annotation_definition ad ON a.annotation_definition_id=ad.annotation_definition_id");
baseQuery
.append(" LEFT OUTER JOIN @.objects o ON a.value_oid=o.objects_id");
baseQuery
.append(" LEFT OUTER JOIN @.objects_text ot ON o.objects_text_id=ot.objects_text_id");
joinedAnnotations = true;
}
havingClause.append(" AND");
if (!include) {
havingClause.append(" NOT");
}
havingClause.append(" MAX(CASE WHEN (");
int count = 0;
for (String annotation : annotations) {
annotationParameters.add(annotation);
havingClause.append("(a.annotation_definition_id=");
havingClause.append(type);
havingClause
.append(" AND ((o.varchar_value IS NOT NULL AND o.varchar_value=?)");
havingClause.append(" OR (o.varchar_value IS NULL AND ");
if (dbConnection.isDerby()) {
// Apache Derby does not support comparing CLOBs so cast
// to the largest VARCHAR type for the comparison.
havingClause
.append("CAST(ot.text_value AS VARCHAR(32672))");
} else {
havingClause.append("ot.text_value");
}
havingClause.append("=?)))");
if (++count < size) {
havingClause.append(" OR ");
}
}
havingClause.append(") THEN 1 ELSE 0 END)=1");
} else if (criterion instanceof CitationFilterCriteria) {
Set<Citation> citations =
((CitationFilterCriteria) criterion).getValues();
int size = citations.size();
if (size == 0) {
continue;
}
// This case is similar to the AnnotationFilterCriteria, except that the annotations
// used in the match are the predefined citation annotations
// (authors, comment, date, name, reference, and type) and the predicate
// to determine whether the citation of a supporting evidence statement
// matches a provided citation is computed in a subselect.
// The query will need to join on the <code>statement</code> table.
if (!joinedStatements) {
baseQuery
.append(" LEFT OUTER JOIN @.kam_edge_statement_map kesm ON ke.kam_edge_id=kesm.kam_edge_id");
baseQuery
.append(" LEFT OUTER JOIN @.statement s ON kesm.statement_id=s.statement_id");
joinedStatements = true;
}
whereClause.append(" AND");
// An include filter matches an edge only if one of the supporting statements has one
// of the provided citations (i.e. boolean OR).
// An exclude filter matches an edge only if none of the supporting statements has one
// of the provided citations (i.e. NOT OR).
if (!include) {
whereClause.append(" NOT");
}
whereClause.append(" (FALSE");
for (Citation citation : citations) {
final String id = citation.getId();
final String name = citation.getName();
final String comment = citation.getComment();
final CitationType citationType =
citation.getCitationType();
final String ctype = citationType != null ? citationType
.getDisplayValue() : null;
// Pack the authors string exactly as done in
// CitationDataConverter.convert(Citation, Map<String, BELAnnotationDefinition>).
String author = null;
final List<String> authors = citation.getAuthors();
if (BELUtilities.hasItems(authors)) {
author = StringUtils.left(PackUtils.packValues(authors), 4000);
}
final Date date = citation.getPublicationDate();
final String publicationDate = (date != null ? dateFormat
.format(date) : null);
citationJoins.append(" LEFT OUTER JOIN (");
citationJoins
.append("SELECT s.statement_id statement_id, SUM(CASE WHEN (FALSE");
int countNulls = 0;
for (String type : KAMStoreConstants.CITATION_ANNOTATION_DEFINITION_IDS) {
String value = null;
if (type == CitationAuthorsAnnotationDefinition.ANNOTATION_DEFINITION_ID) {
value = author;
} else if (type == CitationDateAnnotationDefinition.ANNOTATION_DEFINITION_ID) {
value = publicationDate;
} else if (type == CitationNameAnnotationDefinition.ANNOTATION_DEFINITION_ID) {
value = name;
} else if (type == CitationTypeAnnotationDefinition.ANNOTATION_DEFINITION_ID) {
value = ctype;
} else if (type == CitationCommentAnnotationDefinition.ANNOTATION_DEFINITION_ID) {
value = comment;
} else if (type == CitationReferenceAnnotationDefinition.ANNOTATION_DEFINITION_ID) {
value = id;
}
if (noLength(value)) {
// For example the citation date can be null, in which case the
// supporting statements are not checked for the citation date
// annotation.
++countNulls;
continue;
}
citationParameters.add(value);
citationJoins.append(" OR (ad.name='");
citationJoins.append(type);
citationJoins
.append("' AND ((o.varchar_value IS NOT NULL AND o.varchar_value=?)");
citationJoins
.append(" OR (o.varchar_value IS NULL AND ");
if (dbConnection.isDerby()) {
// Apache Derby does not support comparing CLOBs so cast
// to the largest VARCHAR type for the comparison.
// https://db.apache.org/derby/docs/10.7/ref/rrefjdbc96386.html
citationJoins
.append("CAST(ot.text_value AS VARCHAR(32672))");
} else {
citationJoins.append("ot.text_value");
}
citationJoins.append("=?)))");
}
citationJoins.append(") THEN 1 ELSE 0 END) citations");
citationJoins.append(" FROM @.statement s");
citationJoins
.append(" LEFT OUTER JOIN @.statement_annotation_map sam ON s.statement_id=sam.statement_id");
citationJoins
.append(" LEFT OUTER JOIN @.annotation a ON sam.annotation_id=a.annotation_id");
citationJoins
.append(" LEFT OUTER JOIN @.annotation_definition ad ON a.annotation_definition_id=ad.annotation_definition_id");
citationJoins
.append(" LEFT OUTER JOIN @.objects o ON a.value_oid=o.objects_id");
citationJoins
.append(" LEFT OUTER JOIN @.objects_text ot ON o.objects_text_id=ot.objects_text_id");
citationJoins.append(" GROUP BY s.statement_id) t");
citationJoins.append(uniqueSubselectId);
citationJoins.append(" ON s.statement_id=t");
citationJoins.append(uniqueSubselectId);
citationJoins.append(".statement_id");
// The subselect above stores whether the supporting statements of each edge have the
// provided citation in the "has_citation" field.
whereClause.append(" OR (t");
whereClause.append(uniqueSubselectId);
whereClause.append(".citations<>0 AND MOD(t");
whereClause.append(uniqueSubselectId);
whereClause.append(".citations,");
whereClause
.append(KAMStoreConstants.CITATION_ANNOTATION_DEFINITION_IDS.length
- countNulls);
whereClause.append(")=0)");
++uniqueSubselectId;
}
whereClause.append(")");
}
}
// Prepare the final SQL query.
baseQuery.append(citationJoins);
baseQuery.append(whereClause);
if (groupedByEdge) {
baseQuery.append(" GROUP BY ke.kam_edge_id");
baseQuery.append(havingClause);
}
// Create a pair of the SQL query string and a list of the string parameters that
// need to be bound, in order, in any PreparedStatement that uses the query string.
final String sql = baseQuery.toString();
final int size =
2 * (citationParameters.size() + annotationParameters.size());
final List<String> bindings = new ArrayList<String>(size);
for (String param : citationParameters) {
bindings.add(param);
bindings.add(param);
}
for (String param : annotationParameters) {
bindings.add(param);
bindings.add(param);
}
return new Pair<String, List<String>>(sql, bindings);
}
/**
* {@inheritDoc}
*/
@Override
public AllocatingIterator<SimpleKAMNode> iterateNodes() throws SQLException {
PreparedStatement ps1, ps2;
ps1 = getPreparedStatement(SELECT_PROTO_NODES_SQL);
ps2 = getPreparedStatement(SELECT_KAM_NODE_PARAMETERS_PREFIX_SQL
+ SELECT_KAM_NODE_PARAMETERS_ORDER_SQL,
TYPE_SCROLL_INSENSITIVE, CONCUR_READ_ONLY);
final ResultSet nodeRS = ps1.executeQuery();
final ResultSet paramRS = ps2.executeQuery();
class Iter implements AllocatingIterator<SimpleKAMNode> {
private boolean closed = false;
@Override
public boolean hasNext() {
if (closed) throw new IllegalStateException("closed");
try {
return nodeRS.next();
} catch (SQLException e) {
e.printStackTrace();
close();
return false;
}
}
@Override
public SimpleKAMNode next() {
if (closed) throw new IllegalStateException("closed");
KamProtoNode kpn;
try {
kpn = getKamProtoNode(nodeRS, paramRS);
} catch (SQLException e) {
e.printStackTrace();
close();
return null;
}
int id = kpn.getId();
FunctionEnum fx = kpn.getFunctionType();
String lbl = kpn.getLabel();
_KamNode node = new _KamNode(id, fx, lbl);
return node;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
@Override
public void close() {
closed = true;
try {
nodeRS.close();
paramRS.close();
} catch (SQLException e) {
// ignore it
}
}
@Override
public void finalize() {
close();
}
}
return new Iter();
}
/**
* {@inheritDoc}
*/
@Override
public AllocatingIterator<SimpleKAMEdge> iterateEdges() throws SQLException {
PreparedStatement ps = getPreparedStatement(SELECT_PROTO_EDGES_SQL);
final ResultSet edgeRS = ps.executeQuery();
class Iter implements AllocatingIterator<SimpleKAMEdge> {
private boolean closed = false;
@Override
public boolean hasNext() {
if (closed) throw new IllegalStateException("closed");
try {
return edgeRS.next();
} catch (SQLException e) {
e.printStackTrace();
close();
return false;
}
}
@Override
public SimpleKAMEdge next() {
if (closed) throw new IllegalStateException("closed");
int id, rel, src, tgt;
try {
id = edgeRS.getInt(1);
src = edgeRS.getInt(2);
rel = edgeRS.getInt(3);
tgt = edgeRS.getInt(4);
} catch (SQLException e) {
e.printStackTrace();
close();
return null;
}
return new _KamEdge(id, fromValue(rel), src, tgt);
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
@Override
public void close() {
closed = true;
try {
edgeRS.close();
} catch (SQLException e) {
// ignore it
}
}
@Override
public void finalize() {
close();
}
}
return new Iter();
}
/**
* {@inheritDoc}
*/
@Override
public int countNodes() throws SQLException {
PreparedStatement ps = getPreparedStatement(SELECT_COUNT_NODES);
ResultSet rs = ps.executeQuery();
if (rs.next()) return rs.getInt(1);
return 0;
}
/**
* {@inheritDoc}
*/
@Override
public int countEdges() throws SQLException {
PreparedStatement ps = getPreparedStatement(SELECT_COUNT_EDGES);
ResultSet rs = ps.executeQuery();
if (rs.next()) return rs.getInt(1);
return 0;
}
/**
* {@inheritDoc}
*/
@Override
public KamProtoNodesAndEdges getKamProtoNodesAndEdges(KamInfo kamInfo)
throws SQLException {
Map<Integer, KamProtoNode> nodes = new HashMap<Integer, KamProtoNode>();
Map<Integer, KamProtoEdge> edges = new HashMap<Integer, KamProtoEdge>();
ResultSet nodesRs = null;
ResultSet paramsRs = null;
try {
PreparedStatement nps =
getPreparedStatement(SELECT_PROTO_NODES_SQL);
nodesRs = nps.executeQuery();
PreparedStatement pps = getPreparedStatement(
SELECT_KAM_NODE_PARAMETERS_PREFIX_SQL
+ SELECT_KAM_NODE_PARAMETERS_ORDER_SQL,
TYPE_SCROLL_INSENSITIVE, CONCUR_READ_ONLY);
paramsRs = pps.executeQuery();
while (nodesRs.next()) {
KamProtoNode kamProtoNode = getKamProtoNode(nodesRs, paramsRs);
nodes.put(kamProtoNode.getId(), kamProtoNode);
}
} finally {
close(nodesRs);
close(paramsRs);
}
try {
PreparedStatement ps = getPreparedStatement(SELECT_PROTO_EDGES_SQL);
nodesRs = ps.executeQuery();
while (nodesRs.next()) {
Integer kamEdgeId = nodesRs.getInt(1);
KamProtoNode sourceKamProtoNode = nodes.get(nodesRs.getInt(2));
Integer relationshipTypeId = nodesRs.getInt(3);
KamProtoNode targetKamProtoNode = nodes.get(nodesRs.getInt(4));
// Sanity checks
if (null == sourceKamProtoNode) {
throw new SQLException(String.format(
"Source node for edge %d is missing.", kamEdgeId));
}
if (null == targetKamProtoNode) {
throw new SQLException(String.format(
"Target node for edge %d is missing.", kamEdgeId));
}
edges.put(kamEdgeId,
new KamProtoEdge(kamEdgeId, sourceKamProtoNode,
fromValue(relationshipTypeId),
targetKamProtoNode));
}
} finally {
close(nodesRs);
close(paramsRs);
}
return new KamProtoNodesAndEdges(nodes, edges);
}
/**
* {@inheritDoc}
*/
@Override
public KamProtoNodesAndEdges getKamProtoNodesAndEdges(KamInfo kamInfo,
KamFilter kamFilter) throws SQLException {
if (kamFilter == null) {
// The result will be the same as the result of continuing in this method,
// but getKamProtoNodesAndEdges(KamInfo) can better handle the query.
return getKamProtoNodesAndEdges(kamInfo);
}
Map<Integer, KamProtoNode> nodes = new HashMap<Integer, KamProtoNode>();
Map<Integer, KamProtoEdge> edges = new HashMap<Integer, KamProtoEdge>();
ResultSet nodesRs = null;
ResultSet paramsRs = null;
// Get an SQL snippet to help query the KAM nodes and edges.
final Pair<String, List<String>> pair =
getFilteredSelectProtoEdgesSql(kamFilter);
final String snippet = pair.getFirst();
final List<String> bindings = pair.getSecond();
// Query the KAM nodes that are either a source or target node of
// a KAM edge that satisfies the KAM filter.
StringBuilder nodesSql = new StringBuilder(SELECT_PROTO_NODES_SQL);
nodesSql.append(
" INNER JOIN (SELECT ke.kam_source_node_id, ke.kam_target_node_id FROM @.kam_edge ke INNER JOIN (")
.append(snippet)
.append(") fke ON ke.kam_edge_id=fke.kam_edge_id")
.append(") ke ON kn.kam_node_id=ke.kam_source_node_id OR kn.kam_node_id=ke.kam_target_node_id");
// Same for global parameters
StringBuilder paramSql =
new StringBuilder(SELECT_KAM_NODE_PARAMETERS_PREFIX_SQL)
.append(" INNER JOIN (SELECT ke.kam_source_node_id, ke.kam_target_node_id FROM @.kam_edge ke INNER JOIN (")
.append(snippet)
.append(") fke ON ke.kam_edge_id=fke.kam_edge_id")
.append(") ke ON knp.kam_node_id=ke.kam_source_node_id OR knp.kam_node_id=ke.kam_target_node_id")
.append(SELECT_KAM_NODE_PARAMETERS_ORDER_SQL);
try {
PreparedStatement nps = getPreparedStatement(nodesSql.toString());
int count = 0;
for (String param : bindings) {
nps.setString(++count, param);
}
nodesRs = nps.executeQuery();
PreparedStatement pps = getPreparedStatement(paramSql.toString(),
TYPE_SCROLL_INSENSITIVE, CONCUR_READ_ONLY);
count = 0;
for (String param : bindings) {
pps.setString(++count, param);
}
paramsRs = pps.executeQuery();
while (nodesRs.next()) {
KamProtoNode kamProtoNode = getKamProtoNode(nodesRs, paramsRs);
nodes.put(kamProtoNode.getId(), kamProtoNode);
}
} finally {
close(nodesRs);
}
// Then query the KAM edges.
StringBuilder sql2 = new StringBuilder(SELECT_PROTO_EDGES_SQL);
sql2.append(" INNER JOIN (")
.append(snippet)
.append(") fke ON ke.kam_edge_id=fke.kam_edge_id");
try {
PreparedStatement ps = getPreparedStatement(sql2.toString());
int count = 0;
for (String param : bindings) {
ps.setString(++count, param);
}
nodesRs = ps.executeQuery();
while (nodesRs.next()) {
Integer kamEdgeId = nodesRs.getInt(1);
KamProtoNode sourceKamProtoNode = nodes.get(nodesRs.getInt(2));
Integer relationshipTypeId = nodesRs.getInt(3);
KamProtoNode targetKamProtoNode = nodes.get(nodesRs.getInt(4));
// Sanity checks
if (null == sourceKamProtoNode) {
throw new SQLException(String.format(
"Source node for edge %d is missing.", kamEdgeId));
}
if (null == targetKamProtoNode) {
throw new SQLException(String.format(
"Target node for edge %d is missing.", kamEdgeId));
}
edges.put(kamEdgeId,
new KamProtoEdge(kamEdgeId, sourceKamProtoNode,
fromValue(relationshipTypeId),
targetKamProtoNode));
}
} finally {
close(nodesRs);
}
return new KamProtoNodesAndEdges(nodes, edges);
}
/**
*
* @return
* @throws SQLException
*/
protected Collection<Citation> getAllCitations() throws SQLException {
ResultSet rset = null;
Map<String, Citation> citations = new HashMap<String, Citation>();
try {
PreparedStatement ps =
getPreparedStatement(SELECT_CITATION_ANNOTATIONS_SQL);
rset = ps.executeQuery();
List<Annotation> annotations = new ArrayList<Annotation>();
Integer lastStatementId = null;
//annotations ordered by statement ID
while (rset.next()) {
Integer statementId = rset.getInt(4);
//Integer documentId = rset.getInt(5);
if (lastStatementId == null) {
lastStatementId = statementId;
}
if (!lastStatementId.equals(statementId)) {
//new citation
Citation citation = new Citation(annotations);
String key = makeCitationKey(citation);
if (!citations.containsKey(key)) {
citations.put(key, citation);
}
annotations.clear();
}
annotations.add(getAnnotation(rset));
lastStatementId = statementId;
}
Citation citation = new Citation(annotations);
String key = makeCitationKey(citation);
citations.put(key, citation);
} finally {
close(rset);
}
return citations.values();
}
//temporary key for determining citation equivalence
private String makeCitationKey(Citation citation) {
return citation.getName() + "," + citation.getId() + ","
+ citation.getCitationType();
}
private synchronized void initializeCitationMaps() throws SQLException {
if (belDocumentCitationsMap == null || citationMap == null) {
belDocumentCitationsMap = new HashMap<Integer, List<Citation>>();
citationMap = new HashMap<String, Citation>();
ResultSet rset = null;
Map<String, Citation> citations = new HashMap<String, Citation>();
try {
PreparedStatement ps =
getPreparedStatement(SELECT_CITATION_ANNOTATIONS_SQL);
rset = ps.executeQuery();
List<Annotation> annotations = new ArrayList<Annotation>();
Integer lastStatementId = 1;
Integer lastDocumentId = null;
//annotations ordered by statement ID
while (rset.next()) {
Integer annotationId = rset.getInt(1);
Integer statementId = rset.getInt(2);
Integer documentId = rset.getInt(3);
if (!lastStatementId.equals(statementId)) {
//new citation
Citation citation = new Citation(annotations);
String key = makeCitationKey(citation);
if (!citations.containsKey(key)) {
citations.put(key, citation);
processCitationMapEntry(citation, documentId);
}
annotations.clear();
}
annotations.add(getAnnotation(annotationId));
lastStatementId = statementId;
lastDocumentId = documentId;
}
Citation citation = new Citation(annotations);
String key = makeCitationKey(citation);
if (!citations.containsKey(key)) {
citations.put(key, citation);
processCitationMapEntry(citation, lastDocumentId);
}
} finally {
close(rset);
}
}
}
private void processCitationMapEntry(Citation citation, Integer documentId) {
citationMap.put(citation.getId(), citation);
List<Citation> documentCitations =
belDocumentCitationsMap.get(documentId);
if (documentCitations == null) {
documentCitations = new ArrayList<Citation>();
belDocumentCitationsMap.put(documentId, documentCitations);
}
documentCitations.add(citation);
}
@Override
public List<Citation> getCitations(BelDocumentInfo belDocumentInfo)
throws SQLException {
List<Citation> citations = new ArrayList<Citation>();
if (belDocumentCitationsMap == null) {
initializeCitationMaps();
}
List<Citation> matches =
belDocumentCitationsMap.get(belDocumentInfo.getId());
if (matches != null) {
citations.addAll(matches);
}
return citations;
}
@Override
public List<Citation> getCitations(BelDocumentInfo belDocumentInfo,
CitationType citationType) throws SQLException {
List<Citation> citations = new ArrayList<Citation>();
for (Citation citation : getCitations(belDocumentInfo)) {
if (citation.getCitationType() == citationType) {
citations.add(citation);
}
}
return citations;
}
@Override
public List<Citation> getCitations(CitationType citationType)
throws SQLException {
if (citationMap == null) {
initializeCitationMaps();
}
List<Citation> citations = new ArrayList<Citation>();
for (Citation citation : citationMap.values()) {
if (citation.getCitationType() == citationType) {
citations.add(citation);
}
}
return citations;
}
@Override
public List<Citation> getCitations(CitationType citationType,
String... referenceIds) throws SQLException {
if (referenceIds == null) {
return emptyList();
}
if (citationMap == null) {
initializeCitationMaps();
}
List<Citation> citations = new ArrayList<Citation>();
for (int i = 0; i < referenceIds.length; i++) {
Citation citation = citationMap.get(referenceIds[i]);
if (citation != null && citation.getCitationType() == citationType) {
citations.add(citation);
}
}
return citations;
}
/**
*
* @param annotationTypeId
* @return
* @throws SQLException
*/
private AnnotationType getAnnotationTypeById(Integer annotationTypeId)
throws SQLException {
// See if the term is cached
if (annotationTypeCache.containsKey(annotationTypeId)) {
return annotationTypeCache.get(annotationTypeId);
}
AnnotationType annotationType = null;
ResultSet rset = null;
try {
PreparedStatement ps =
getPreparedStatement(SELECT_ANNOTATION_TYPE_BY_ID_SQL);
ps.setInt(1, annotationTypeId);
rset = ps.executeQuery();
if (rset.next()) {
annotationType = getAnnotationType(rset);
}
} finally {
close(rset);
}
annotationTypeCache.put(annotationTypeId, annotationType);
return annotationType;
}
@Override
public List<String> getAnnotationTypeDomainValues(
AnnotationType annotationType) throws SQLException,
ExternalResourceException {
if (annotationType == null) {
throw new InvalidArgument("annotationType", annotationType);
}
if (CITATION.equals(annotationType.getName())) {
return Arrays.asList(".*");
}
if (annotationTypeValueCache.containsKey(annotationType.getId())) {
return annotationTypeValueCache.get(annotationType.getId());
}
List<String> domainValues = new ArrayList<String>();
ResultSet rset = null;
try {
PreparedStatement ps =
getPreparedStatement(SELECT_ANNOTATION_TYPE_DOMAIN_VALUE_SQL);
ps.setInt(1, annotationType.getId());
rset = ps.executeQuery();
if (rset.next()) {
String value = getObjectValueById(rset.getInt(1));
switch (annotationType.getAnnotationDefinitionType()) {
case ENUMERATION:
for (String domainValue : StringUtils.split(value, "|")) {
domainValues.add(domainValue);
}
break;
case REGULAR_EXPRESSION:
domainValues.add(value);
break;
case URL:
//retrieve external resource
try {
AnnotationDefinition ad =
cacheableAnnotationDefinitionService
.resolveAnnotationDefinition(value);
if (BELUtilities.hasItems(ad.getEnums())) {
//external enumerations
domainValues.addAll(ad.getEnums());
} else {
//pattern
domainValues.add(ad.getValue());
}
} catch (AnnotationDefinitionResolutionException e) {
throw new ExternalResourceException(e.getName(),
e.getMessage(), e);
}
break;
}
}
} finally {
close(rset);
}
// Add this list to the cache
annotationTypeValueCache.put(annotationType.getId(), domainValues);
return domainValues;
}
@Override
public List<AnnotationType> getAnnotationTypes() throws SQLException {
List<AnnotationType> list = new ArrayList<AnnotationType>();
ResultSet rset = null;
try {
PreparedStatement ps =
getPreparedStatement(SELECT_ANNOTATION_TYPES_SQL);
rset = ps.executeQuery();
while (rset.next()) {
AnnotationType annotationType = getAnnotationType(rset);
addAnnotationType(list, annotationType);
// This might save us time later on
if (!annotationTypeCache.containsKey(annotationType.getId())) {
annotationTypeCache.put(annotationType.getId(),
annotationType);
}
}
} finally {
close(rset);
}
return list;
}
private AnnotationType createCitationType(AnnotationType annotationType) {
return new AnnotationType(
annotationType.getId(),
CITATION,
"The citation that supports one or more statements.",
"Use this annotation to link statements to a citation.",
AnnotationDefinitionType.REGULAR_EXPRESSION);
}
/**
*
* @param rset
* @return
* @throws SQLException
*/
private AnnotationType getAnnotationType(ResultSet rset)
throws SQLException {
Integer annotationTypeId = rset.getInt(1);
if (annotationTypeCache.containsKey(annotationTypeId)) {
return annotationTypeCache.get(annotationTypeId);
}
String name = rset.getString(2);
String description = rset.getString(3);
String usage = rset.getString(4);
Integer annotationDefinitionTypeId = rset.getInt(5);
AnnotationDefinitionType annotationDefinitionType =
AnnotationDefinitionType.fromValue(annotationDefinitionTypeId);
final AnnotationType annotationType;
if (annotationDefinitionType.equals(AnnotationDefinitionType.URL)) {
ResultSet dvrset = null;
try {
PreparedStatement dvps =
getPreparedStatement(SELECT_ANNOTATION_TYPE_DOMAIN_VALUE_SQL);
dvps.setInt(1, annotationTypeId);
dvrset = dvps.executeQuery();
String url = null;
if (dvrset.next()) {
url = getObjectValueById(dvrset.getInt(1));
}
annotationType =
new AnnotationType(annotationTypeId, name, description,
usage, annotationDefinitionType, url);
} finally {
close(dvrset);
}
} else {
annotationType =
new AnnotationType(annotationTypeId, name, description,
usage, annotationDefinitionType);
}
return annotationType;
}
/**
*
* @param nodes
* @param params A {@link ResultSet} from a SELECT on the {@code kam_node_parameter}
* table.
* @return
* @throws SQLException
*/
private KamProtoNode getKamProtoNode(ResultSet nodes, ResultSet params)
throws SQLException {
Integer kamNodeId = nodes.getInt(1);
Integer functionTypeId = nodes.getInt(2);
String label = getObjectValueById(nodes.getInt(3));
List<Integer> nodeParameterIds = new ArrayList<Integer>();
while (params.next()) {
if (params.getInt(1) != kamNodeId.intValue()) {
params.previous(); // cursor poised for proper next() call
break;
}
nodeParameterIds.add(params.getInt(2));
}
for (Integer nodeParameterId : nodeParameterIds) {
label =
label.replaceFirst(ANY_NUMBER_PLACEHOLDER,
nodeParameterId.toString());
}
return new KamProtoNode(kamNodeId,
FunctionEnum.fromValue(functionTypeId), label);
}
/**
*
* @param rset
* @return
* @throws SQLException
*/
private Annotation getAnnotation(ResultSet rset) throws SQLException {
Integer annotationId = rset.getInt(1);
String label = getObjectValueById(rset.getInt(2));
Integer annotationTypeId = rset.getInt(3);
return new Annotation(annotationId,
getAnnotationTypeById(annotationTypeId), label);
}
private Annotation getAnnotation(final Integer annotationId)
throws SQLException {
Annotation annotation = annotationCache.get(annotationId);
if (annotation != null) {
return annotation;
}
ResultSet rset = null;
try {
PreparedStatement ps =
getPreparedStatement(SELECT_ANNOTATION_BY_ID_SQL);
ps.setInt(1, annotationId);
rset = ps.executeQuery();
if (rset.next()) {
String label = getObjectValueById(rset.getInt(1));
Integer annotationTypeId = rset.getInt(2);
AnnotationType annotationType =
getAnnotationTypeById(annotationTypeId);
annotation =
new Annotation(annotationId, annotationType, label);
annotationCache.put(annotationId, annotation);
return annotation;
}
} finally {
close(rset);
}
return null;
}
/**
* @param rset
* @return
* @throws SQLException
*/
private Namespace getNamespace(ResultSet rset) throws SQLException {
Integer namespaceId = rset.getInt(1);
String prefix = rset.getString(2);
String resourceLocation = getObjectValueById(rset.getInt(3));
return new Namespace(namespaceId, prefix, resourceLocation);
}
/**
*
* @param rset
* @return
* @throws SQLException
*/
private BelStatement getStatement(ResultSet rset) throws SQLException {
Integer statementId = rset.getInt(1);
Integer documentId = rset.getInt(2);
Integer subjectTermId = rset.getInt(3);
Integer relationshipTypeId = rset.getInt(4);
boolean definitional = rset.wasNull();
Integer objectTermId = rset.getInt(5);
Integer nestedSubjectId = rset.getInt(6);
Integer nestedRelationship = rset.getInt(7);
Integer nestedObjectId = rset.getInt(8);
// Get the document info for this statement
BelDocumentInfo belDocumentInfo = getBelDocumentInfoById(documentId);
// Lastly, get the annotations
List<Annotation> annotations = getAnnotationsByStatementId(statementId);
BelTerm subject = getBelTermById(subjectTermId);
BelElement object = null;
if (definitional) {
return new BelStatement(statementId, subject, belDocumentInfo,
annotations);
} else if (null != objectTermId && !objectTermId.equals(0)) {
object = getBelTermById(objectTermId);
return new BelStatement(statementId, subject,
fromValue(relationshipTypeId), object,
belDocumentInfo, annotations);
} else {
BelTerm nestedSubject = getBelTermById(nestedSubjectId);
BelTerm nestedObject = getBelTermById(nestedObjectId);
BelStatement nested =
new BelStatement(statementId, nestedSubject,
fromValue(nestedRelationship),
nestedObject,
belDocumentInfo, annotations);
return new BelStatement(statementId, subject,
fromValue(relationshipTypeId), nested,
belDocumentInfo, annotations);
}
}
/**
*
* @param rset
* @return
* @throws SQLException
*/
private BelDocumentInfo getBelDocumentInfo(ResultSet rset)
throws SQLException {
Integer belDocumentId = rset.getInt(1);
String name = rset.getString(2);
String description = rset.getString(3);
String version = rset.getString(4);
String copyright = rset.getString(5);
String disclaimer = rset.getString(6);
String contactInfo = rset.getString(7);
String licenseInfo = rset.getString(8);
String authors = rset.getString(9);
List<AnnotationType> annotationTypes =
getAnnotationTypesByDocumentId(belDocumentId);
List<Namespace> namespaces = getNamespacesByDocumentId(belDocumentId);
return new BelDocumentInfo(belDocumentId, name, description, version,
copyright, disclaimer, contactInfo, licenseInfo, authors,
annotationTypes, namespaces);
}
/**
* @param objectsTextId
* @return
* @throws SQLException
*/
private String getObjectsTextById(Integer objectsTextId)
throws SQLException {
PreparedStatement ps = null;
ResultSet rset = null;
String value = null;
try {
ps = getPreparedStatement(SELECT_OBJECTS_TEXT_SQL);
ps.setInt(1, objectsTextId);
rset = ps.executeQuery();
if (rset.next()) {
value = rset.getString(1);
}
} finally {
close(rset);
}
return value;
}
/**
* {@inheritDoc}
*/
private String getObjectValueById(Integer objectId) throws SQLException {
// See if the object is cached
if (objectValueCache.containsKey(objectId)) {
return objectValueCache.get(objectId);
}
String value = null;
//Integer typeId = null;
PreparedStatement ps = null;
ResultSet rset = null;
int objectsTextId = 0;
try {
ps = getPreparedStatement(SELECT_OBJECTS_VALUE_SQL);
ps.setInt(1, objectId);
rset = ps.executeQuery();
if (rset.next()) {
//typeId = rset.getInt(1);
value = rset.getString(2);
objectsTextId = rset.getInt(3);
}
if (value == null && objectsTextId != 0) { //value is in objects_text table
value = getObjectsTextById(objectsTextId);
}
if (value == null) {
value = "";
}
} finally {
close(rset);
}
// Push into the cache
objectValueCache.put(objectId, value);
objectValueReverseCache.put(value, objectId);
return value;
}
/**
* TODO: This will fail if the object value is stored in the text table
*
* @param value
* @return
* @throws SQLException
*/
private Integer getObjectIdByValue(final String value)
throws SQLException {
if (value == null) {
return null;
}
// Check to see if the objectValue has already been seen
if (objectValueReverseCache.containsKey(value)) {
return objectValueReverseCache.get(value);
}
ResultSet rset = null;
Integer objectId = null;
try {
PreparedStatement ps = getPreparedStatement(SELECT_OBJECTS_ID_SQL);
ps.setString(1, value);
rset = ps.executeQuery();
if (rset.next()) {
objectId = rset.getInt(1);
}
} finally {
close(rset);
}
// Push into the cache
if (objectId != null) {
objectValueCache.put(objectId, value);
objectValueReverseCache.put(value, objectId);
}
return objectId;
}
/**
* Retrieves a {@link Set set} of all {@link String citation definitions}.
*
* @return the {@link Set set}
*/
private static Set<String> getCitationDefinitions() {
final Set<String> citationKeys =
sizedHashSet(KAMStoreConstants.CITATION_ANNOTATION_DEFINITION_IDS.length);
citationKeys.addAll(Arrays
.asList(KAMStoreConstants.CITATION_ANNOTATION_DEFINITION_IDS));
return citationKeys;
}
/**
*
* @author julianjray
*/
public final static class TermParameter extends KamElementImpl {
private final Namespace namespace;
private final String parameterValue;
protected TermParameter(Integer id, Namespace namespace,
String parameterValue) {
super(null, id);
this.namespace = namespace;
this.parameterValue = parameterValue;
}
public Namespace getNamespace() {
return namespace;
}
public String getParameterValue() {
return parameterValue;
}
}
/**
* {@link KamProtoNode} represents a {@link Kam kam} node retrieved from
* a KAM schema.
*
* @author julianjray
*/
public final static class KamProtoNode extends KamElementImpl {
private final FunctionEnum functionType;
private final String label;
private int hash;
/**
* Constructs the {@link KamProtoNode kam proto node}.
*
* <p>
* The hashcode is precomputed since {@link KamProtoNode} is immutable.
* </p>
*
* @param id the database {@link Integer id} of the {@link Kam kam}
* node.
* @param functionType the {@link Kam kam} node
* {@link FunctionEnum function}
* @param label the node {@link String label}
* @throws InvalidArgument Thrown if {@code functionType} or
* {@code label} is {@code null}
*/
private KamProtoNode(final Integer id, final FunctionEnum functionType,
final String label) {
super(null, id);
if (functionType == null) {
throw new InvalidArgument("functionType", functionType);
}
if (label == null) {
throw new InvalidArgument("label", label);
}
this.functionType = functionType;
this.label = label;
hash = computeHash();
}
/**
* Retrieves the {@link FunctionEnum function type}.
*
* @return the {@link FunctionEnum function type}, which will be
* {@code non-null}
*/
public FunctionEnum getFunctionType() {
return functionType;
}
/**
* Retrieves the {@link String label}.
*
* @return the {@link String label}, which will be {@code non-null}
*/
public String getLabel() {
return label;
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object o) {
if (o == null || !(o instanceof KamProtoNode)) {
return false;
}
KamProtoNode other = (KamProtoNode) o;
if (!(this.functionType.equals(other.functionType))) {
return false;
}
// The label may contain '#' instead of a string of numbers, so this method
// needs to check number-patterns separately. '#' matches '#' or any
// number, but otherwise the numbers and non-number substrings are matched
// exactly.
final Matcher thisMatcher =
ANY_NUMBER_REGEX_PATTERN.matcher(this.label), otherMatcher =
ANY_NUMBER_REGEX_PATTERN.matcher(other.label);
int i = 0, j = 0;
final int iMax = this.label.length(), jMax = other.label.length();
while (true) {
final boolean b1 = thisMatcher.find(i);
final boolean b2 = otherMatcher.find(j);
if (b1 != b2) {
// One label has more instances of the pattern than the other,
// so they can not be equal.
return false;
} else if (!b1) {
// There are no more number patterns in either this.label or
// other.label, so simply compare the rest of the labels.
return substringEquals(this.label, i, iMax, other.label, j,
jMax);
} else if (!substringEquals(this.label, i, thisMatcher.start(),
other.label, j, otherMatcher.start())) {
// The substring before the next pattern match are not equal,
// so the labels can not be equal.
return false;
} else if (substringEquals(this.label, thisMatcher.start(),
thisMatcher.end(),
ANY_NUMBER_PLACEHOLDER, 0,
ANY_NUMBER_PLACEHOLDER_LENGTH)
||
substringEquals(other.label, otherMatcher.start(),
otherMatcher.end(),
ANY_NUMBER_PLACEHOLDER, 0,
ANY_NUMBER_PLACEHOLDER_LENGTH)
||
substringEquals(this.label, thisMatcher.start(),
thisMatcher.end(),
other.label, otherMatcher.start(),
otherMatcher.end())) {
// this.label and other.label have matching patterns, so continue the search
// after incrementing i and j.
i = thisMatcher.end();
j = otherMatcher.end();
} else {
// The numbers do not match, so the labels are not equal.
return false;
}
}
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
return hash;
}
private int computeHash() {
// The label may contain '#' instead of a number. Compute the hash
// as if all numbers in the label have been replaced with '#'.
final int labelHashCode = NUMBER_REGEX_PATTERN.matcher(this.label)
.replaceAll(ANY_NUMBER_PLACEHOLDER).hashCode();
// TODO improve implementation
return this.functionType.hashCode() ^ labelHashCode;
}
}
/**
* {@link KamProtoEdge} represents a {@link Kam kam} edge retrieved from
* a KAM schema.
*
* @author julianjray
*/
public final static class KamProtoEdge extends KamElementImpl {
private final KamProtoNode sourceNode;
private final KamProtoNode targetNode;
private final RelationshipType relationshipType;
private int hash;
/**
* Constructs the {@link KamProtoEdge kam proto edge}.
*
* <p>
* The hashcode is precomputed since {@link KamProtoEdge} is immutable.
* </p>
*
* @param kamEdgeId the database {@link Integer id} of the
* {@link Kam kam} node.
* @param sourceNode the source {@link KamProtoNode node} for this edge
* @param relationshipType the {@link RelationshipType relationship}
* for this edge
* @param targetNode the target {@link KamProtoNode node} for this edge
* @throws InvalidArgument Thrown if {@code sourceNode},
* {@code RelationshipType}, or {@code targetNode} is {@code null}
*/
private KamProtoEdge(Integer kamEdgeId, KamProtoNode sourceNode,
RelationshipType relationshipType, KamProtoNode targetNode) {
super(null, kamEdgeId);
if (sourceNode == null) {
throw new InvalidArgument("sourceNode", sourceNode);
}
if (relationshipType == null) {
throw new InvalidArgument("relationshipType", relationshipType);
}
if (targetNode == null) {
throw new InvalidArgument("targetNode", targetNode);
}
this.sourceNode = sourceNode;
this.relationshipType = relationshipType;
this.targetNode = targetNode;
this.hash = computeHash();
}
/**
* Retrieves the source {@link KamProtoNode node}.
*
* @return the source {@link KamProtoNode node}, which will be
* {@code non-null}
*/
public KamProtoNode getSourceNode() {
return sourceNode;
}
/**
* Retrieves the target {@link KamProtoNode node}.
*
* @return the target {@link KamProtoNode node}, which will be
* {@code non-null}
*/
public KamProtoNode getTargetNode() {
return targetNode;
}
/**
* Retrieves the {@link RelationshipType relationship}.
*
* @return the edge's {@link RelationshipType relationship}, which will
* be {@code non-null}
*/
public RelationshipType getRelationship() {
return relationshipType;
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object o) {
if ((o == null) || !(o instanceof KamProtoEdge)) {
return false;
}
KamProtoEdge other = (KamProtoEdge) o;
return (this.sourceNode.equals(other.sourceNode) &&
this.targetNode.equals(other.targetNode) && this.relationshipType
.equals(other.relationshipType));
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
return hash;
}
private int computeHash() {
// TODO improve implementation
return this.sourceNode.hashCode() ^ this.targetNode.hashCode()
^ this.relationshipType.hashCode();
}
}
/**
*
* @author julianjray
*/
public final static class Namespace extends KamStoreObjectImpl {
private final String prefix;
private final String resourceLocation;
protected Namespace(Integer namespaceId, String prefix,
String resourceLocation) {
super(namespaceId);
this.prefix = prefix;
this.resourceLocation = resourceLocation;
}
public String getPrefix() {
return prefix;
}
public String getResourceLocation() {
return resourceLocation;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else if (obj == null) {
return false;
} else if (!(obj instanceof Namespace)) {
return false;
} else {
Namespace other = (Namespace) obj;
return (BELUtilities.equals(getId(), other.getId()) &&
BELUtilities.equals(prefix, other.prefix) && BELUtilities
.equals(resourceLocation, other.resourceLocation));
}
}
@Override
public int hashCode() {
final int prime = 31;
int hash = 0;
final Integer id = getId();
hash += (id != null ? id.hashCode() : 0);
hash *= prime;
hash += (prefix != null ? prefix.hashCode() : 0);
hash *= prime;
hash +=
(resourceLocation != null ? resourceLocation.hashCode() : 0);
return hash;
}
}
/**
*
* @author julianjray
*/
public static final class BelDocumentInfo extends KamStoreObjectImpl {
private final String name;
private final String description;
private final String version;
private final String copyright;
private final String disclaimer;
private final String contactInfo;
private final String licenseInfo;
private final String authors;
private final List<AnnotationType> annotationTypes;
private final List<Namespace> namespaces;
public BelDocumentInfo(Integer belDocumentId, String name,
String description, String version, String copyright,
String disclaimer, String contactInfo, String licenseInfo,
String authors, List<AnnotationType> annotationTypes,
List<Namespace> namespaces) {
super(belDocumentId);
this.name = name;
this.description = description;
this.version = version;
this.copyright = copyright;
this.disclaimer = disclaimer;
this.contactInfo = contactInfo;
this.licenseInfo = licenseInfo;
this.authors = authors;
this.annotationTypes = new ArrayList<AnnotationType>();
this.annotationTypes.addAll(annotationTypes);
this.namespaces = new ArrayList<Namespace>();
this.namespaces.addAll(namespaces);
}
public List<AnnotationType> getAnnotationTypes() {
List<AnnotationType> list = new ArrayList<AnnotationType>();
list.addAll(this.annotationTypes);
return list;
}
public List<Namespace> getNamespaces() {
List<Namespace> list = new ArrayList<Namespace>();
list.addAll(this.namespaces);
return list;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public String getVersion() {
return version;
}
public String getCopyright() {
return copyright;
}
public String getDisclaimer() {
return disclaimer;
}
public String getContactInfo() {
return contactInfo;
}
public String getLicenseInfo() {
return licenseInfo;
}
public String getAuthors() {
return authors;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else if (obj == null) {
return false;
} else if (!(obj instanceof BelDocumentInfo)) {
return false;
} else {
BelDocumentInfo other = (BelDocumentInfo) obj;
return (BELUtilities.equals(getId(), other.getId())
&&
BELUtilities.equals(name, other.name)
&&
BELUtilities.equals(description, other.description)
&&
BELUtilities.equals(version, other.version)
&&
BELUtilities.equals(copyright, other.copyright)
&&
BELUtilities.equals(disclaimer, other.disclaimer)
&&
BELUtilities.equals(contactInfo, other.contactInfo)
&&
BELUtilities.equals(licenseInfo, other.licenseInfo)
&&
BELUtilities.equals(authors, other.authors)
&&
BELUtilities.equals(annotationTypes,
other.annotationTypes) && BELUtilities.equals(
namespaces, other.namespaces));
}
}
@Override
public int hashCode() {
final int prime = 31;
int hash = 0, annotationTypesHash = 0, namespacesHash = 0;
if (annotationTypes != null) {
for (AnnotationType annotationType : annotationTypes) {
annotationTypesHash ^= annotationType.hashCode();
}
}
if (namespaces != null) {
for (Namespace namespace : namespaces) {
namespacesHash ^= namespace.hashCode();
}
}
final Integer id = getId();
hash += (id != null ? id.hashCode() : 0);
hash *= prime;
hash += (name != null ? name.hashCode() : 0);
hash *= prime;
hash += (description != null ? description.hashCode() : 0);
hash *= prime;
hash += (version != null ? version.hashCode() : 0);
hash *= prime;
hash += (copyright != null ? copyright.hashCode() : 0);
hash *= prime;
hash += (disclaimer != null ? disclaimer.hashCode() : 0);
hash *= prime;
hash += (contactInfo != null ? contactInfo.hashCode() : 0);
hash *= prime;
hash += (licenseInfo != null ? licenseInfo.hashCode() : 0);
hash *= prime;
hash += (authors != null ? authors.hashCode() : 0);
hash *= prime;
hash += annotationTypesHash;
hash *= prime;
hash += namespacesHash;
return hash;
}
}
/**
*
* @author julianjray
*/
public static final class Citation {
private final String name;
private final String id;
private final String comment;
private final Date publicationDate;
private final List<String> authors;
private final CitationType citationType;
public Citation(String name, String id, String comment,
Date publicationDate, List<String> authors,
CitationType citationType) {
this.name = name;
this.id = id;
this.comment = comment;
this.publicationDate = publicationDate;
this.authors = authors;
this.citationType = citationType;
}
private Citation(List<Annotation> annotationList) {
String name = null;
String id = null;
String comment = null;
Date publicationDate = null;
List<String> authors = null;
CitationType citationType = null;
for (Annotation annotation : annotationList) {
if (CitationReferenceAnnotationDefinition.ANNOTATION_DEFINITION_ID
.equals(annotation.getAnnotationType().getName())) {
//citation reference id
id = annotation.getValue();
} else if (CitationDateAnnotationDefinition.ANNOTATION_DEFINITION_ID
.equals(annotation.getAnnotationType().getName())) {
if (annotation.getValue() != null) {
try {
publicationDate =
dateFormat.parse(annotation.getValue());
} catch (ParseException e) {
publicationDate = null;
}
}
} else if (CitationNameAnnotationDefinition.ANNOTATION_DEFINITION_ID
.equals(annotation.getAnnotationType().getName())) {
name = annotation.getValue();
} else if (CitationCommentAnnotationDefinition.ANNOTATION_DEFINITION_ID
.equals(annotation.getAnnotationType().getName())) {
comment = annotation.getValue();
} else if (CitationTypeAnnotationDefinition.ANNOTATION_DEFINITION_ID
.equals(annotation.getAnnotationType().getName())) {
citationType =
CitationType.getCitationType(annotation.getValue());
} else if (CitationAuthorsAnnotationDefinition.ANNOTATION_DEFINITION_ID
.equals(annotation.getAnnotationType().getName())) {
if (annotation.getValue() != null) {
authors = PackUtils.unpackValues(annotation.getValue());
}
}
}
this.name = name;
this.id = id;
this.comment = comment;
this.publicationDate = publicationDate;
this.authors = authors;
this.citationType = citationType;
}
public String getName() {
return name;
}
public String getId() {
return id;
}
public String getComment() {
return comment;
}
public Date getPublicationDate() {
return publicationDate;
}
public List<String> getAuthors() {
return authors;
}
public CitationType getCitationType() {
return citationType;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
} else if (!(obj instanceof Citation)) {
return false;
} else {
Citation other = (Citation) obj;
return (BELUtilities.equals(name, other.name)
&&
BELUtilities.equals(id, other.id)
&&
BELUtilities.equals(comment, other.comment)
&&
BELUtilities.equals(publicationDate,
other.publicationDate) &&
BELUtilities.equals(authors, other.authors) && BELUtilities
.equals(citationType, other.citationType));
}
}
@Override
public int hashCode() {
final int prime = 31;
int hash = 0;
int authorsHash = 0;
if (authors != null) {
for (String author : authors) {
authorsHash ^= author.hashCode();
}
}
hash *= prime;
hash += (name != null ? name.hashCode() : 0);
hash *= prime;
hash += (id != null ? id.hashCode() : 0);
hash *= prime;
hash += (comment != null ? comment.hashCode() : 0);
hash *= prime;
hash += (publicationDate != null ? publicationDate.hashCode() : 0);
hash *= prime;
hash += authorsHash;
hash *= prime;
hash += (citationType != null ? citationType.hashCode() : 0);
return hash;
}
}
/**
*
* @author julianjray
*/
public final static class BelStatement extends BelElement {
private final BelDocumentInfo belDocumentInfo;
private final BelTerm subject;
private final RelationshipType relationshipType;
private final BelElement object;
private final List<Annotation> annotationList;
private final Citation citation;
private BelStatement(Integer belStatementId, BelTerm subject,
BelDocumentInfo belDocumentInfo, List<Annotation> annotationList) {
super(belStatementId);
this.subject = subject;
this.relationshipType = null;
this.object = null;
this.belDocumentInfo = belDocumentInfo;
this.annotationList = new ArrayList<Annotation>();
for (Annotation annotation : annotationList) {
if (!ArrayUtils.contains(
KAMStoreConstants.CITATION_ANNOTATION_DEFINITION_IDS,
annotation.getAnnotationType().getName())) {
//non citation annotations
this.annotationList.add(annotation);
}
}
this.citation = new Citation(annotationList);
}
private BelStatement(Integer belStatementId, BelTerm subject,
RelationshipType relationshipType, BelElement object,
BelDocumentInfo belDocumentInfo, List<Annotation> annotationList) {
super(belStatementId);
this.subject = subject;
this.relationshipType = relationshipType;
this.object = object;
this.belDocumentInfo = belDocumentInfo;
this.annotationList = new ArrayList<Annotation>();
for (Annotation annotation : annotationList) {
if (!ArrayUtils.contains(
KAMStoreConstants.CITATION_ANNOTATION_DEFINITION_IDS,
annotation.getAnnotationType().getName())) {
//non citation annotations
this.annotationList.add(annotation);
}
}
this.citation = new Citation(annotationList);
}
/**
* returns a list which immutable with respect to the statement
* @return
*/
public List<Annotation> getAnnotationList() {
List<Annotation> list = new ArrayList<Annotation>();
list.addAll(this.annotationList);
return list;
}
/**
* returns a citation object associated with this statement
* @return
*/
public Citation getCitation() {
return citation;
}
public BelDocumentInfo getBelDocumentInfo() {
return belDocumentInfo;
}
public BelTerm getSubject() {
return subject;
}
public RelationshipType getRelationshipType() {
return relationshipType;
}
public BelElement getObject() {
return object;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(subject.toString());
if (relationshipType == null) {
// definitional statement
return sb.toString();
}
sb.append(" ");
sb.append(relationshipType.getDisplayValue());
if (object instanceof BelStatement) {
// nested statement
sb.append(" (");
sb.append(object.toString());
sb.append(" )");
return sb.toString();
}
// simple statement
sb.append(" ");
sb.append(object.toString());
return sb.toString();
}
}
/**
*
* @author julianjray
*/
public final static class BelTerm extends BelElement {
private final String label;
protected BelTerm(Integer belTermId, String label) {
super(belTermId);
this.label = label;
}
public String getLabel() {
return label;
}
@Override
public String toString() {
return label;
}
}
/**
*
* @author julianjray
*/
public final static class Annotation extends KamStoreObjectImpl {
private final AnnotationType annotationType;
private final String value;
private Annotation(Integer annotationId, AnnotationType annotationType,
String value) {
super(annotationId);
this.value = value;
this.annotationType = annotationType;
}
public AnnotationType getAnnotationType() {
return annotationType;
}
public String getValue() {
return value;
}
}
/**
*
* @author julianjray
*
*/
public static final class AnnotationType extends KamStoreObjectImpl {
private final String name;
private final String description;
private final String usage;
private final AnnotationDefinitionType annotationDefinitionType;
private final String url;
public AnnotationType(Integer annotationTypeId, String name,
String description, String usage,
AnnotationDefinitionType annotationDefinitionType) {
super(annotationTypeId);
this.name = name;
this.description = description;
this.usage = usage;
this.annotationDefinitionType = annotationDefinitionType;
this.url = null;
}
private AnnotationType(Integer annotationTypeId, String name,
String description, String usage,
AnnotationDefinitionType annotationDefinitionType, String url) {
super(annotationTypeId);
this.name = name;
this.description = description;
this.usage = usage;
this.annotationDefinitionType = annotationDefinitionType;
this.url = url;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public String getUsage() {
return usage;
}
public AnnotationDefinitionType getAnnotationDefinitionType() {
return annotationDefinitionType;
}
/**
* Returns the url of this annotation type.
*
* @return {@link String} the url where the annotation is defined,
* which may be <tt>null</tt>
*/
public String getUrl() {
return url;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else if (obj == null) {
return false;
} else if (!(obj instanceof AnnotationType)) {
return false;
} else {
AnnotationType other = (AnnotationType) obj;
return (BELUtilities.equals(getId(), other.getId())
&&
BELUtilities.equals(name, other.name)
&&
BELUtilities.equals(description, other.description)
&&
BELUtilities.equals(usage, other.usage)
&&
BELUtilities.equals(annotationDefinitionType,
other.annotationDefinitionType) && BELUtilities
.equals(url, other.url));
}
}
@Override
public int hashCode() {
final int prime = 31;
int hash = 0;
final Integer id = getId();
hash += (id != null ? id.hashCode() : 0);
hash *= prime;
hash += (name != null ? name.hashCode() : 0);
hash *= prime;
hash += (description != null ? description.hashCode() : 0);
hash *= prime;
hash += (usage != null ? usage.hashCode() : 0);
hash *= prime;
hash +=
(annotationDefinitionType != null ? annotationDefinitionType
.hashCode()
: 0);
hash *= prime;
hash += (url != null ? url.hashCode() : 0);
return hash;
}
}
/**
* Annotation definition type
*
*/
public enum AnnotationDefinitionType {
/**
* An annotation whose value must match one from an enumerated list.
*/
ENUMERATION(0, "listAnnotation"),
/**
* An annotation whose value must match a regular expression.
*/
REGULAR_EXPRESSION(1, "patternAnnotation"),
/**
* An annotation whose value is specified by a URL
*/
URL(2, "urlAnnotation");
/**
* Value unique to each enumeration.
*/
private final Integer value;
/**
* Enumeration display value.
*/
private final String displayValue;
/**
* Constructor for setting enum and display value.
*
* @param value Enum value
* @param displayValue Display value
*/
private AnnotationDefinitionType(Integer value, String displayValue) {
this.value = value;
this.displayValue = displayValue;
}
/**
* Returns the annotation type by its integer value.
* @param value
* @return
*/
public static AnnotationDefinitionType fromValue(final Integer value) {
AnnotationDefinitionType type = null;
if (value != null) {
for (AnnotationDefinitionType adt : AnnotationDefinitionType
.values()) {
if (value.equals(adt.value)) {
type = adt;
break;
}
}
}
return type;
}
public Integer getValue() {
return value;
}
public String getDisplayValue() {
return displayValue;
}
}
private class _KamEdge implements SimpleKAMEdge {
private final int id;
private final int source;
private final int target;
private final RelationshipType relationship;
_KamEdge(int id, RelationshipType r, int srcID, int tgtID) {
this.id = id;
this.relationship = r;
this.source = srcID;
this.target = tgtID;
}
@Override
public int getID() {
return id;
}
@Override
public int getSourceID() {
return source;
}
@Override
public int getTargetID() {
return target;
}
@Override
public RelationshipType getRelationship() {
return relationship;
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result *= prime;
result += id;
result *= prime;
result += relationship.hashCode();
result *= prime;
result += source;
result *= prime;
result += target;
return result;
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (!(obj instanceof _KamEdge)) return false;
_KamEdge other = (_KamEdge) obj;
if (id != other.id) return false;
if (relationship != other.relationship) return false;
if (source != other.source) return false;
if (target != other.target) return false;
return true;
}
}
private class _KamNode implements SimpleKAMNode {
private final int id;
private final FunctionEnum function;
private final String label;
_KamNode(int id, FunctionEnum fx, String label) {
this.id = id;
this.function = fx;
this.label = label;
}
@Override
public int getID() {
return id;
}
@Override
public FunctionEnum getFunction() {
return function;
}
@Override
public String getLabel() {
return label;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result *= prime;
result += function.hashCode();
result *= prime;
result += id;
result *= prime;
result += label.hashCode();
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (!(obj instanceof _KamNode)) return false;
_KamNode other = (_KamNode) obj;
if (function != other.function) return false;
if (id != other.id) return false;
if (!label.equals(other.label)) return false;
return true;
}
}
}