/**
* Copyright (C) 2012-2013 Selventa, Inc.
*
* This file is part of the OpenBEL Framework.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The OpenBEL Framework is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the OpenBEL Framework. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms under LGPL v3:
*
* This license does not authorize you and you are prohibited from using the
* name, trademarks, service marks, logos or similar indicia of Selventa, Inc.,
* or, in the discretion of other licensors or authors of the program, the
* name, trademarks, service marks, logos or similar indicia of such authors or
* licensors, in any marketing or advertising materials relating to your
* distribution of the program or any covered product. This restriction does
* not waive or limit your obligation to keep intact all copyright notices set
* forth in the program as delivered to you.
*
* If you distribute the program in whole or in part, or any modified version
* of the program, and you assume contractual liability to the recipient with
* respect to the program or modified version, then you will indemnify the
* authors and licensors of the program for any liabilities that these
* contractual assumptions directly impose on those licensors and authors.
*/
package org.openbel.framework.common.protonetwork.model;
import static org.openbel.framework.common.BELUtilities.entries;
import static org.openbel.framework.common.BELUtilities.index;
import static org.openbel.framework.common.BELUtilities.sizedHashMap;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.openbel.framework.common.InvalidArgument;
import org.openbel.framework.common.enums.AnnotationType;
import org.openbel.framework.common.external.ExternalType;
import org.openbel.framework.common.external.ReadCache;
import org.openbel.framework.common.external.WriteCache;
import org.openbel.framework.common.model.AnnotationDefinition;
/**
* AnnotationDefinitionTable holds the document annotation definitions. This
* class manages the annotation definitions through the
* {@link #addAnnotationDefinition(TableAnnotationDefinition, int)} operation.
*
* @author Anthony Bargnesi {@code <abargnesi@selventa.com>}
* @version 1.3 Derives from {@link ExternalType}
*/
public class AnnotationDefinitionTable extends ExternalType {
private static final long serialVersionUID = -6486699009265767011L;
public static final Integer URL_ANNOTATION_TYPE_ID = 2;
public static final String URL_ANNOTATION_TYPE = "urlAnnotation";
/**
* Defines a unique collection to hold annotation definition values. Note:
* This collection is backed by {@link LinkedHashSet} which is not
* thread-safe.
*/
private Set<TableAnnotationDefinition> annotationDefinitions =
new LinkedHashSet<AnnotationDefinitionTable.TableAnnotationDefinition>();
/**
* Defines a map from {@link TableAnnotationDefinition} to an index
* representing insertion order in {@code annotationDefinitions}. Note: This
* collection is backed by {@link HashMap} which is not thread-safe.
*/
private Map<TableAnnotationDefinition, Integer> definitionIndex =
new HashMap<TableAnnotationDefinition, Integer>();
/**
* Defines a map from the index to the {@link TableAnnotationDefinition}.
* Note: This collection is backed by {@link HashMap} which is not
* thread-safe.
*/
private Map<Integer, TableAnnotationDefinition> indexDefinition =
new HashMap<Integer, AnnotationDefinitionTable.TableAnnotationDefinition>();
/**
* Defines a map from document index to a {@link Set} of annotation
* definition index. Note: This collection is backed by
* {@code LinkedHashMap} which is not threadsafe.
*/
private Map<Integer, Set<Integer>> documentAnnotationDefinitions =
new LinkedHashMap<Integer, Set<Integer>>();
/**
* Adds an annotation definition to the {@code annotationDefinitions} set.
* The insertion index is captured by {@code definitionIndex}.
*
* @param annotationDefinition {@link TableAnnotationDefinition}, the
* annotation definition to add, which cannot be null
* @return {@code int}, the index of the added annotation definition, which
* must be >= 0.
* @throws InvalidArgument Thrown if the {@code annotationDefinition}
* argument is null
*/
public int addAnnotationDefinition(
TableAnnotationDefinition annotationDefinition, int did) {
if (annotationDefinition == null) {
throw new InvalidArgument("annotation definition is null");
}
if (annotationDefinitions.add(annotationDefinition)) {
int nextIndex = annotationDefinitions.size() - 1;
definitionIndex.put(annotationDefinition, nextIndex);
indexDefinition.put(nextIndex, annotationDefinition);
}
Integer adi = definitionIndex.get(annotationDefinition);
Set<Integer> dadi = documentAnnotationDefinitions.get(did);
if (dadi == null) {
dadi = new LinkedHashSet<Integer>();
documentAnnotationDefinitions.put(did, dadi);
}
dadi.add(adi);
return adi;
}
/**
* Returns the annotation definition table's {@code annotationDefinitions}
* set. This set is unmodifiable to preserve the state of the annotation
* definition table.
*
* @return {@link Set}, which cannot be null or modified
*/
public Set<TableAnnotationDefinition> getAnnotationDefinitions() {
return Collections.unmodifiableSet(annotationDefinitions);
}
/**
* Returns the map of {@link TableAnnotationDefinition} to index. This map
* is unmodifiable to preserve the state of the annotation definition table.
*
* @return {@link Map}, which cannot be null or modified
*/
public Map<TableAnnotationDefinition, Integer> getDefinitionIndex() {
return Collections.unmodifiableMap(definitionIndex);
}
/**
* Returns the map of index to {@link TableAnnotationDefinition}. This map
* is unmodifiable to preserve the state of the annotation definition table.
*
* @return {@link Map}, which cannot be null or modified
*/
public Map<Integer, TableAnnotationDefinition> getIndexDefinition() {
return Collections.unmodifiableMap(indexDefinition);
}
/**
* Returns the map of document index to a {@link Set} of annotation
* definition index. This map is unmodifiable to preserve the state of the
* annotation definition table.
*
* @return {@link Map}, which cannot be null or modified
*/
public Map<Integer, Set<Integer>> getDocumentAnnotationDefinitions() {
return documentAnnotationDefinitions;
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime
* result
+ ((annotationDefinitions == null) ? 0 : annotationDefinitions
.hashCode());
result = prime * result
+ ((definitionIndex == null) ? 0 : definitionIndex.hashCode());
result = prime
* result
+ ((documentAnnotationDefinitions == null) ? 0
: documentAnnotationDefinitions.hashCode());
result = prime * result
+ ((indexDefinition == null) ? 0 : indexDefinition.hashCode());
return result;
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
AnnotationDefinitionTable other = (AnnotationDefinitionTable) obj;
if (annotationDefinitions == null) {
if (other.annotationDefinitions != null)
return false;
} else if (!annotationDefinitions.equals(other.annotationDefinitions))
return false;
if (definitionIndex == null) {
if (other.definitionIndex != null)
return false;
} else if (!definitionIndex.equals(other.definitionIndex))
return false;
if (documentAnnotationDefinitions == null) {
if (other.documentAnnotationDefinitions != null)
return false;
} else if (!documentAnnotationDefinitions
.equals(other.documentAnnotationDefinitions))
return false;
if (indexDefinition == null) {
if (other.indexDefinition != null)
return false;
} else if (!indexDefinition.equals(other.indexDefinition))
return false;
return true;
}
/**
* {@inheritDoc}
*/
@Override
protected void _from(ObjectInput in) throws IOException,
ClassNotFoundException {
// 1: read index def map size
final int size = in.readInt();
// size map accordingly
indexDefinition = sizedHashMap(size);
TableAnnotationDefinition tad;
for (int i = 0; i < size; ++i) {
tad = new TableAnnotationDefinition();
// 1: read the table annotation definition
tad.readExternal(in);
// 2: read the number of documents
final int documentsSize = in.readInt();
for (int j = 0; j < documentsSize; ++j) {
// 1: read each documents index
addAnnotationDefinition(tad, in.readInt());
}
}
}
/**
* {@inheritDoc}
*/
@Override
protected void _to(ObjectOutput out) throws IOException {
TableAnnotationDefinition[] tads =
index(TableAnnotationDefinition.class, indexDefinition);
// 1: write index def map size
out.writeInt(tads.length);
for (int i = 0; i < tads.length; i++) {
// 1: write the table annotation definition
tads[i].writeExternal(out);
final List<Integer> documents = new ArrayList<Integer>();
Set<Entry<Integer, Set<Integer>>> e2 =
entries(documentAnnotationDefinitions);
for (Map.Entry<Integer, Set<Integer>> e : e2) {
if (e.getValue().contains(i)) {
documents.add(e.getKey());
}
}
// 2: write the number of documents
out.writeInt(documents.size());
for (Integer document : documents) {
// 1: write each documents index
out.writeInt(document.intValue());
}
}
}
/**
* {@inheritDoc}
*/
@Override
protected void _from(ObjectInput in, ReadCache cache) throws IOException,
ClassNotFoundException {
throw new UnsupportedOperationException();
}
/**
* {@inheritDoc}
*/
@Override
protected void _to(ObjectOutput out, WriteCache cache) throws IOException {
throw new UnsupportedOperationException();
}
/**
* TableAnnotationDefinition encapsulates a document's annotation
* definition.
*
* @author Anthony Bargnesi {@code <abargnesi@selventa.com>}
* @version 1.3 Derives from {@link ExternalType}
*/
public static class TableAnnotationDefinition extends ExternalType {
private static final long serialVersionUID = -6486699009265767012L;
/**
* Defines the table annotation definition name.
*/
private/* final */String name;
/**
* Defines the table annotation definition description.
*/
private/* final */String description;
/**
* Defines the table annotation definition usage text.
*/
private/* final */String usage;
/**
* Defines the table annotation definition type as an {@code int} value.
* This value maps to {@link AnnotationType#getValue()}.
*/
private/* final */int annotationType;
/**
* Defines the annotation domain value for this table annotation
* definition. The domain value is dependent on the
* {@code annotationType}:
* <ul>
* <li>{@link AnnotationType#REGULAR_EXPRESSION} : a regular expression
* string</li>
* <li>{@link AnnotationType#ENUMERATION} : a packed string containing
* values separated by a {@code |} character.</li>
* <li>{@link AnnotationType#EXTERNAL} : an external annotation url
* where the domain values are listed</li>
* </ul>
*/
private/* final */String annotationDomain;
/**
* Defines the hash for {@link TableAnnotationDefinition this table
* annotation definition}.
*/
private/* final */int hash;
/**
* Creates the TableAnnotationDefinition based on the BEL common model
* {@link AnnotationDefinition}. <br/>
* <br/>
* If the annotation definition:
* <ul>
* <li>supplies a list annotation domain : set domain to the packed list
* separated by {@link ProtoNetwork#VALUE_SEPARATOR}</li>
* <li>supplies a pattern or external annotation domain : set domain to
* the pattern or external url value</li>
* </ul>
*
* @param belAnnotationDefinition {@link AnnotationDefinition}, the bel
* annotation definition, which cannot be nul
* @throws InvalidArgument - Thrown if {@code belAnnotationDefinition}
* is null or {@link AnnotationDefinition#getType()} is {@code null}
*/
public TableAnnotationDefinition(
final AnnotationDefinition belAnnotationDefinition) {
if (belAnnotationDefinition == null) {
throw new InvalidArgument(
"bel annotation definition cannot be null.");
}
this.name = belAnnotationDefinition.getId();
if (belAnnotationDefinition.getURL() != null) {
// externally defined
this.description = null;
this.usage = null;
this.annotationType = URL_ANNOTATION_TYPE_ID;
this.annotationDomain = belAnnotationDefinition.getURL();
} else {
// defined in-line
this.description = belAnnotationDefinition.getDescription();
this.usage = belAnnotationDefinition.getUsage();
AnnotationType type = belAnnotationDefinition.getType();
if (type == null) {
throw new InvalidArgument("annotation type is null");
}
this.annotationType =
belAnnotationDefinition.getType().getValue();
switch (type) {
case ENUMERATION:
final StringBuilder bldr = new StringBuilder();
for (final String s : belAnnotationDefinition.getEnums()) {
bldr.append(s);
bldr.append(ProtoNetwork.VALUE_SEPARATOR);
}
if (bldr.length() > 0) {
this.annotationDomain =
bldr.substring(0, bldr.length() - 1);
} else {
this.annotationDomain = "";
}
break;
case REGULAR_EXPRESSION:
this.annotationDomain = belAnnotationDefinition.getValue();
break;
default:
this.annotationDomain = null;
}
}
this.hash = computeHash();
}
/**
* Creates the TableAnnotationDefinition This public, no-argument
* constructor is required when implementing Externalizable but it is
* not meant to be used for anything else.
*/
public TableAnnotationDefinition() {
}
/**
* Returns the table annotation definition name.
*
* @return {@link String}, the table annotation definition name, which
* cannot be null
*/
public String getName() {
return name;
}
/**
* Returns the table annotation definition description.
*
* @return {@link String}, the annotation definition description, which
* may be null, if the definition is externally defined
*/
public String getDescription() {
return description;
}
/**
* Returns the table annotation definition usage.
*
* @return {@link String}, the annotation definition usage, which may be
* null, if the definition is externally defined
*/
public String getUsage() {
return usage;
}
/**
* Returns the annotation type {@code int} value. This value maps to
* {@link AnnotationType#getValue()}.
*
* @return {@code int}, the annotation type value
*/
public int getAnnotationType() {
return annotationType;
}
/**
* Returns the table annotation definition's domain value. The domain
* value is dependent on the annotation type value:
* <ul>
* <li>{@value AnnotationType#ENUMERATION} : a packed string containing
* values separated by a {@code |} character.</li>
* <li>{@link AnnotationType#REGULAR_EXPRESSION} : a regular expression
* string</li>
* <li>{@link AnnotationDefinitionTable#URL_ANNOTATION_TYPE_ID} : a url
* where the annotation is externally defined</li>
* </ul>
*
* @return {@link String}, the annotation domain, which cannot be null
*/
public String getAnnotationDomain() {
return annotationDomain;
}
/**
* Compute the hash for {@link TableAnnotationDefinition this table
* annotation definition}.
*
* @return the hash
*/
private int computeHash() {
final int prime = 31;
int result = 1;
if (annotationDomain != null) {
result = prime * result;
result += annotationDomain.hashCode();
}
result = prime * result + annotationType;
if (description != null) {
result = prime * result;
result += description.hashCode();
}
if (name != null) {
result = prime * result;
result += name.hashCode();
}
if (usage != null) {
result = prime * result;
result += usage.hashCode();
}
return result;
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
return hash;
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
TableAnnotationDefinition other = (TableAnnotationDefinition) obj;
if (annotationDomain == null) {
if (other.annotationDomain != null) {
return false;
}
} else if (!annotationDomain.equals(other.annotationDomain)) {
return false;
}
if (annotationType != other.annotationType) {
return false;
}
if (description == null) {
if (other.description != null) {
return false;
}
} else if (!description.equals(other.description)) {
return false;
}
if (name == null) {
if (other.name != null) {
return false;
}
} else if (!name.equals(other.name)) {
return false;
}
if (usage == null) {
if (other.usage != null) {
return false;
}
} else if (!usage.equals(other.usage)) {
return false;
}
return true;
}
/**
* {@inheritDoc}
*/
@Override
protected void _from(ObjectInput in) throws IOException,
ClassNotFoundException {
name = readString(in);
description = readString(in);
usage = readString(in);
annotationType = readInteger(in);
annotationDomain = readString(in);
hash = computeHash();
}
/**
* {@inheritDoc}
*/
@Override
protected void _to(ObjectOutput out) throws IOException {
out.writeObject(name);
out.writeObject(description);
out.writeObject(usage);
writeInteger(out, annotationType);
out.writeObject(annotationDomain);
}
/**
* {@inheritDoc}
*/
@Override
protected void _from(ObjectInput in, ReadCache cache)
throws IOException,
ClassNotFoundException {
throw new UnsupportedOperationException();
}
/**
* {@inheritDoc}
*/
@Override
protected void _to(ObjectOutput out, WriteCache cache)
throws IOException {
throw new UnsupportedOperationException();
}
}
}