/* * EuroCarbDB, a framework for carbohydrate bioinformatics * * Copyright (c) 2006-2009, Eurocarb project, or third-party contributors as * indicated by the @author tags or express copyright attribution * statements applied by the authors. * * This copyrighted material is made available to anyone wishing to use, modify, * copy, or redistribute it subject to the terms and conditions of the GNU * Lesser General Public License, as published by the Free Software Foundation. * A copy of this license accompanies this distribution in the file LICENSE.txt. * * This program 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. * * Last commit: $Rev: 1940 $ by $Author: khaleefah $ on $Date:: 2010-08-10 #$ */ package org.eurocarbdb.dataaccess.core; // stdlib imports import java.math.BigDecimal; import java.util.*; import java.io.Serializable; import java.security.*; // 3rd party imports import org.apache.log4j.Logger; import org.hibernate.Query; // eurocarb imports import org.eurocarbdb.sugar.Sugar; import org.eurocarbdb.sugar.Residue; import org.eurocarbdb.sugar.Linkage; import org.eurocarbdb.sugar.Basetype; import org.eurocarbdb.sugar.Substituent; import org.eurocarbdb.sugar.Monosaccharide; import org.eurocarbdb.sugar.GlycosidicLinkage; import org.eurocarbdb.sugar.SugarSequence; import org.eurocarbdb.sugar.SequenceFormat; import org.eurocarbdb.sugar.PotentiallyIndefinite; import org.eurocarbdb.dataaccess.Contributed; import org.eurocarbdb.dataaccess.EntityManager; import org.eurocarbdb.dataaccess.EurocarbObject; import org.eurocarbdb.dataaccess.BasicEurocarbObject; import org.eurocarbdb.dataaccess.core.Contributor; import org.eurocarbdb.dataaccess.core.seq.GlycanResidue; import org.eurocarbdb.dataaccess.exception.EurocarbException; import org.eurocarbdb.dataaccess.exception.InvalidAssociationException; import org.eurocarbdb.application.glycanbuilder.Glycan; import org.eurocarbdb.application.glycoworkbench.GlycanWorkspace; import org.eurocarbdb.util.graph.Graph; import org.eurocarbdb.util.graph.Vertex; import org.eurocarbdb.util.graph.Edge; import org.eurocarbdb.MolecularFramework.io.SugarImporterException; import org.eurocarbdb.MolecularFramework.io.GlycoCT.*; import org.eurocarbdb.MolecularFramework.io.namespace.*; import org.eurocarbdb.MolecularFramework.util.visitor.GlycoVisitorException; // static imports import static java.util.Collections.emptySet; import static java.util.Collections.emptyList; import static java.util.Collections.unmodifiableSet; import static java.util.Collections.unmodifiableList; import static org.eurocarbdb.dataaccess.Eurocarb.getEntityManager; import static org.eurocarbdb.util.JavaUtils.checkNotNull; import static org.eurocarbdb.util.StringUtils.join; import static org.eurocarbdb.sugar.SequenceFormat.Iupac; import static org.eurocarbdb.sugar.SequenceFormat.Glycoct; /** * GlycanSequence is a data-access object implementing Eurocarb * glycan sequence entries. * * @author mjh * @version $Rev: 1940 $ */ public class GlycanSequence extends BasicEurocarbObject implements Serializable, Contributed, PotentiallyIndefinite { //~~~~~~~~~~~~~~~~~~~~~~ STATIC FIELDS ~~~~~~~~~~~~~~~~~~~~~~~~// static { GlycanWorkspace theWorkspace = new GlycanWorkspace(null,false); } private static final Logger log = Logger.getLogger( GlycanSequence.class ); /** Standard string prefix for GlycanSequence queries */ private static final String Q = "org.eurocarbdb.dataaccess.core.GlycanSequence."; //~~~~~~~~~~~~~~~~~~~~~~~~~~~ FIELDS ~~~~~~~~~~~~~~~~~~~~~~~~~~// /** Unique numeric identifier for this glycan sequence. GlycanSequences * with the same glycan sequence have the same glycanSequenceId. */ private int glycanSequenceId; /** The original (first) contributor of this glycan sequence. */ private Contributor contributor; /** Numeric count of the number of Residues in this glycan sequence. */ private int residueCount; /** Monoisotopic mass */ private BigDecimal massMonoisotopic; /** Average mass */ private BigDecimal massAverage; /** The date/time on which this sequence was entered into Eurocarb. */ private Date dateEntered; /** Unsure - needs clarification? */ private Date dateContributed; /** The set of {@link BiologicalContext}s associated with * this sequence, represented as a set of {@link GlycanSequenceContext}s. */ private Set<GlycanSequenceContext> glycanContexts = new HashSet<GlycanSequenceContext>(0); /** The set of {@link Reference}s associated with this sequence, * represented as a set of {@link GlycanSequenceReference}s. */ private Set<GlycanSequenceReference> glycanReferences = new HashSet<GlycanSequenceReference>(0); /** The set of {@link Evidence} associated with this sequence, * represented as a set of {@link GlycanSequenceEvidence}. */ private Set<GlycanSequenceEvidence> glycanEvidence = new HashSet<GlycanSequenceEvidence>(0); /** Set of individual residues in the sugar sequence encapsulated * by this GlycanSequence. Each GlycanResidue object captures info about * parent/child residues. */ private Set<GlycanResidue> glycanResidues = new HashSet<GlycanResidue>(); /** Contains the actual sequence of the glycan represented by this {@link GlycanSequence} */ private SugarSequence sequence; /** sequence in iupac format. @see SequenceFormat#Iupac */ private String sequenceIupac; /** sequence in glycoct format. @see SequenceFormat#Glycoct */ private String sequenceCt; /** sequence in stalliano (GWS) format. @see SequenceFormat#GWS */ private String sequenceGWS; /** True if the {@link SugarSequence} of this GlycanSequence * contains no unknown elements. */ private Boolean isDefinite; //~~~~~~~~~~~~~~~~~~~~~~~~ CONSTRUCTORS ~~~~~~~~~~~~~~~~~~~~~~~// /** * Creates a new, uninitialised {@link GlycanSequence}. * * @see #setSugarSequence * @see Contributor#getCurrentContributor() */ // note: null constructor needed for hibernate public GlycanSequence() { } /** @deprecated use other contructors */ @Deprecated public GlycanSequence( String raw_sequence ) { // TODO: parse sequence - but let's just assume // it's a valid glycoCT sequence for now... setContributor( Contributor.getCurrentContributor() ); initSequenceGWS(raw_sequence); sequenceCt = raw_sequence; sequenceIupac = raw_sequence; // <- temporary hack // init SugarSequence getSugarSequence(); } /** * Creates a new {@link GlycanSequence} initialised to * the given {@link SugarSequence} and the current {@link Contributor}. * * @see #setSugarSequence * @see Contributor#getCurrentContributor() */ public GlycanSequence( SugarSequence ss ) { setSugarSequence( ss ); setContributor( Contributor.getCurrentContributor() ); } //~~~~~~~~~~~~~~~~~~~~~~ STATIC METHODS ~~~~~~~~~~~~~~~~~~~~~~~// /* countSequences *//****************************************** * * Returns the total number of unique {@link GlycanSequence}s * in the current data store. * @deprecated use getEntityManager().countAll( GlycanSequence.class ) */ @Deprecated public static int countSequences() { return getEntityManager().countAll( GlycanSequence.class ); } /* lookupOrCreateNew *//*************************************** * * Main method for adding sequence information to EurocarbDB, * taking a {@link SugarSequence} argument and returning either * a new or existing GlycanSequence object depending on the existence * of the given SugarSequence in the current data store. * * @see SugarSequence * @see SequenceFormat */ public static GlycanSequence lookupOrCreateNew( SugarSequence ss ) { checkNotNull( ss ); log.debug("checking if given SugarSequence already exists in data store..."); GlycanSequence gs = lookupByExactSequence( ss ); if ( gs == null ) { log.debug( "SugarSequence does not exist in data store, " + "returning new GlycanSequence"); return new GlycanSequence( ss ); } else { if ( log.isDebugEnabled() ) log.debug( "returning existing GlycanSequence (id=" + gs.getGlycanSequenceId() + ")" ); return gs; } } /** * Looks up a {@link GlycanSequence} by an external reference name * and id, for example ("carbbank", 12345) to find the GlycanSequence * corresponding to Carbbank entry 12345. Returns null if no such * sequence exists. * @see Reference */ public static GlycanSequence lookupByExternalRef( String ref_name, int ref_id ) { Object result = getEntityManager() .getQuery( Q + "BY_EXTERNAL_REFERENCE" ) .setString( "ext_ref_name", ref_name ) .setString( "ext_ref_id", Integer.toString(ref_id) ) .uniqueResult(); return ( result != null ) ? (GlycanSequence) result : null; } /* lookupByExactSequence *//*********************************** * * Returns a non-null GlycanSequence if the passed {@link SugarSequence} * exists in the current data store, or null if it does not. Note * that the sequence format of the passed SugarSequence will be * normalised to the default {@link SequenceFormat} before being * looked up. The general idiom for getting a SugarSequence from * a String is (assuming a Glycoct sequence): *<pre> * String my_seq = ... * SugarSequence sseq = new SugarSequence( my_seq, SequenceFormat.Glycoct ); *</pre> * * @throws IllegalArgumentException * if SugarSequence argument is null, or contains a null or * zero-length string sequence. * @see SequenceFormat * @author mjh */ public static GlycanSequence lookupByExactSequence( SugarSequence ss ) { checkNotNull( ss ); String seq = ss.toString(); if ( seq == null || seq.length() == 0 ) throw new IllegalArgumentException( "passed SugarSequence returned a null or zero-length sequence"); log.debug("attempting to lookup GlycanSequence by an exact sequence"); GlycanSequence g = (GlycanSequence) getEntityManager() .getQuery( Q + "BY_EXACT_SEQUENCE" ) .setParameter("seq", seq ) .uniqueResult(); return g; } public static GlycanSequence lookupByExactSequenceString( String ss ) { checkNotNull( ss ); String seq = ss; if ( seq == null || seq.length() == 0 ) throw new IllegalArgumentException( "passed SugarSequence returned a null or zero-length sequence"); log.debug("attempting to lookup GlycanSequence by an exact sequence"); GlycanSequence g = (GlycanSequence) getEntityManager() .getQuery( Q + "BY_EXACT_SEQUENCE" ) .setParameter("seq", seq ) .uniqueResult(); return g; } //~~~~~~~~~~~~~~~~~~~~~~~~~~ METHODS ~~~~~~~~~~~~~~~~~~~~~~~~~~// /** * Associates this glycan sequence with a new {@link BiologicalContext}, * returning a new {@link GlycanSequenceContext} object that encapsulates * this association. */ public GlycanSequenceContext addBiologicalContext( BiologicalContext new_bc ) { assert new_bc != null; log.info("adding association between " + this + " and " + new_bc ); GlycanSequenceContext gsc = new GlycanSequenceContext(); gsc.setBiologicalContext( new_bc ); gsc.setGlycanSequence( this ); // init collection, but add to glycanContext set directly // because accessor for glycanContexts returns unmodifiable set. this.getGlycanSequenceContexts(); glycanContexts.add( gsc ); new_bc.getGlycanSequenceContexts().add( gsc ); return gsc; } /** * Adds a new {@link Evidence} association to this GlycanSequence entry. * @return an object representing this association. */ public GlycanSequenceEvidence addEvidence( Evidence new_evidence ) { assert new_evidence != null; log.info("adding association between " + this + " and " + new_evidence ); GlycanSequenceEvidence gs_ev = new GlycanSequenceEvidence(); gs_ev.setEvidence( new_evidence ); gs_ev.setGlycanSequence( this ); this.getGlycanSequenceEvidence().add( gs_ev ); new_evidence.getGlycanSequenceEvidence().add( gs_ev ); return gs_ev; } /** * Removes an existing {@link Reference} from this GlycanSequence entry. * @return an object representing the previous association */ public GlycanSequenceReference deleteReference( Reference reference ) { assert reference != null; GlycanSequenceReference gs2r = new GlycanSequenceReference(); gs2r.setReference( reference ); gs2r.setGlycanSequence( this ); ArrayList<GlycanSequenceReference> gs2rs = new ArrayList<GlycanSequenceReference>(this.getGlycanSequenceReferences()); gs2r = gs2rs.get(gs2rs.indexOf( gs2r )); this.glycanReferences.remove( gs2r ); getEntityManager().remove(gs2r); return gs2r; } /** * Adds a new {@link Reference} to this GlycanSequence entry. * @return an object representing this association, or null if there * is already an association between these 2 objects. */ public GlycanSequenceReference addReference( Reference new_reference ) { assert new_reference != null; GlycanSequenceReference gs2r = new GlycanSequenceReference(); gs2r.setReference( new_reference ); gs2r.setGlycanSequence( this ); log.info( "adding association between " + this + " and [Reference=" + new_reference.getReferenceId() + "]"); boolean both_added = this.getGlycanSequenceReferences().add( gs2r ); both_added &= new_reference.addGlycanSequenceReference( gs2r ); if ( both_added ) { if ( log.isDebugEnabled() ) { log.debug("added new glycan sequence to reference association between " + this + " and " + new_reference ); } return gs2r; } else { if ( log.isDebugEnabled() ) { log.debug("did not add new glycan sequence to reference association between " + this + " and " + new_reference + " because association already exists" ); } return null; } } /** Returns unique glycan sequence id. */ public int getGlycanSequenceId() { return this.glycanSequenceId; } /** Sets unique glycan sequence id; Internal use only! */ void setGlycanSequenceId( int glycanSequenceId ) { this.glycanSequenceId = glycanSequenceId; } /** * Returns the {@link Contributor} who initially contributed * this glycan sequence. */ public Contributor getContributor() { if ( contributor == null ) contributor = Contributor.getCurrentContributor(); return this.contributor; } /** * Sets the first {@link Contributor} of this glycan sequence. * Note that many other people may contribute evidence for * this structure; this method is only intended to be used * to set the first contributor of a glycan sequence. */ public void setContributor( Contributor contributor ) { this.contributor = contributor; } /** * Returns a {@link Set} of all the {@link Contributor}s who have * contributed something to this sequence, specifically, they * were the original contributor (see {@link #getContributor}), * or they added a {@link BiologicalContext}, {@link Evidence}, * or {@link Reference} link to this sequence. */ public Set<Contributor> getContributors() { Set<Contributor> contribs = new HashSet<Contributor>(); contribs.add( this.getContributor() ); for ( BiologicalContext c : this.getBiologicalContexts() ) for( BiologicalContextContributor cc : (Set<BiologicalContextContributor>) c.getBiologicalContextContributors()) contribs.add(cc.getContributor()); for ( Contributed c : this.getEvidence() ) contribs.add( c.getContributor() ); for ( Contributed c : this.getReferences() ) contribs.add( c.getContributor() ); return contribs; } /** * Returns {@link getSugarSequence()}.toString(), ie: this sequence * in the default {@link SequenceFormat}. */ public String getName() { return this.getSugarSequence().toString(); } /** * Convenience method that returns the sequence of this * glycan sequence in {@link IupacSequenceFormat * Iupac sequence format}. Returns null if an iupac sequence * cannot be generated for this sequence entry. * * @see SequenceFormat * @see SugarSequence */ public String getSequenceIupac() { // mjh: this is a temporary implementation, it's going to change // once iupac seq generation is finalised. if ( sequenceIupac == null || sequenceIupac.startsWith("RES") ) { try { String seq = getSugarSequence().toString( Iupac ); if ( seq != null ) { this.sequenceIupac = seq; return seq; } } catch ( Exception ex ) { log.warn( "Couldn't get a iupac sequence for " + this , ex ); return null; } } return this.sequenceIupac; } /** * Sets the {@link SequenceFormat#Iupac Iupac} sequence * of this {@link GlycanSequence} entry. Note that this *only* * sets the iupac sequence, it doesn't affect the sequence * representations of other formats. */ void setSequenceIupac( String iupac ) { assert iupac != null; this.sequenceIupac = iupac; } /** * Shortcut method for retrieving this glycan sequence in * {@link SequenceFormat#Glycoct Glycoct condensed format}. */ public String getSequenceCt() { return this.sequenceCt; } /** * Sets the {@link SequenceFormat#Glycoct Glycoct} sequence * of this {@link GlycanSequence} entry. Note that this *only* * sets the Glycoct sequence, it doesn't affect the sequence * representations of other formats. */ void setSequenceCt( String glycoct_condensed ) { assert glycoct_condensed != null; this.sequenceCt = glycoct_condensed; } /** * Returns an object that is the gateway to finding various sequence * associations and relations of this {@link GlycanSequence}. */ public GlycanSequenceRelations getRelations() { return GlycanSequenceRelations.of( this ); } /** * Returns a {@link Sugar} object that represents the glycan * structure encapsulated by this {@link GlycanSequence}. */ public Sugar getSugar() { return getSugarSequence().getSugar(); } /** * Returns the (sequence format agnostic) {@link SugarSequence} * of this GlycanSequence. * * @see #getSequenceCt * @see SequenceFormat#Glycoct */ public SugarSequence getSugarSequence() { if ( sequence == null ) { String seq = this.getSequenceCt(); if ( seq == null || seq.length() == 0 ) return null; sequence = new SugarSequence( seq ); } return sequence; } /** * Preferred method for setting the glycan sequence. * Internally handles any/all interconversion between sequence * formats. * * @throws NullPointerException * if passed {@link SugarSequence} is null. */ public void setSugarSequence( SugarSequence sseq ) throws NullPointerException { String seq_glycoct = sseq.toString( Glycoct ); initSequenceGWS( seq_glycoct ); this.sequenceCt = seq_glycoct; try { this.sequenceIupac = sseq.toString( Iupac ); } catch ( RuntimeException ex ) { log.warn("Caught exception converting sequence to Iupac", ex ); // next line is a necessary hack until iupac column is made nullable this.sequenceIupac = seq_glycoct; } this.sequence = sseq; } @Deprecated private void initSequenceGWS( String sequence_ct_cond ) { // ensure backward compatibility Glycan ret = Glycan.fromGlycoCTCondensed(sequence_ct_cond,true); sequenceGWS = ( ret == null ) ? "" :ret.toString(); } /** * Shortcut method for retrieving this glycan sequence in * {@link SequenceFormat#GWS Stalliano/GWS format}. * This is a eurocarb-internal format. */ public String getSequenceGWS() { return sequenceGWS; } void setSequenceGWS( String gws ) { assert gws != null; this.sequenceGWS = gws; } /** * Returns the number of {@link Residue}s in the {@link Sugar} * represented by this GlycanSequence; residues are defined * in terms of Eurocarbdb {@link Monosaccharide}s: * {@link Basetype}s + {@link Substituent}s. * * @see #getGlycanResidues */ public int getResidueCount() { return this.residueCount; } /** todo: don't use. */ public BigDecimal getMassMonoisotopic() { return this.massMonoisotopic; } /** todo: don't use. */ public BigDecimal getMassAverage() { return this.massAverage; } /** * Returns a simple {@link Map} of residue name to residue count, * using normalised residue names. This method is likely to change * in the near future, so marking as deprecated. * * @deprecated the signature of this method is very likely to * change in the near future. */ @Deprecated public Map<String,Integer> getComposition() { Map<String,Integer> map = new HashMap<String,Integer>(); try { Sugar s = getSugarSequence().getSugar(); for ( Residue r : s ) { String name = r.getName(); // log.debug("residue: " + r + ", name: " + name ); Integer count = map.get( name ); if ( count == null || count == 0 ) { map.put( name, 1 ); } else { map.put( name, count + 1 ); } } // log.debug("returning composition: " + map ); } catch ( Exception ex ) { log.warn("Caught exception while trying to derive composition", ex ); return Collections.emptyMap(); } return map; } /** {@inheritDoc} @see Contributed#getDateEntered */ public Date getDateEntered() { return this.dateEntered; } /** {@inheritDoc} @see Contributed#getDateEntered */ public Date getDateContributed() { return this.dateContributed; } /* getEvidence *//********************************************* * * Returns an unmodifiable list of all {@link Evidence} that is * recorded in the current data store for the current glycan sequence. * @see #addEvidence */ @SuppressWarnings("unchecked") public List<Evidence> getEvidence() { int seq_id = this.getGlycanSequenceId(); if ( seq_id <= 0 ) { return Collections.emptyList(); } if ( log.isDebugEnabled() ) log.debug("looking up all Evidence for GlycanSequence=" + seq_id ); List<Evidence> evidence = (List<Evidence>) getEntityManager() .getQuery( Q + "GET_EVIDENCE_FOR_SEQUENCE" ) .setParameter("sequence_id", seq_id ) .list(); //return (List<Evidence>) evidence; /* mjh: note: the below implementation will not work for subclasses of Evidence * List<Evidence> evs = new ArrayList<Evidence>(); for ( GlycanSequenceEvidence gste : getGlycanSequenceEvidence() ) { evs.add(gste.getEvidence()); } */ if ( evidence == null ) return Collections.emptyList(); return unmodifiableList( evidence ); } /** * Returns the number of separate pieces of {@link Evidence} * for this {@link GlycanSequence}. */ public int getEvidenceCount() { return this.getGlycanSequenceEvidence().size(); } /* getBiologicalContexts *//*********************************** * * Returns an unmodifiable list of all biological contexts in * which the current glycan sequence has been observed. * @see #addBiologicalContext */ @SuppressWarnings("unchecked") public List<BiologicalContext> getBiologicalContexts() { /* int seq_id = this.getGlycanSequenceId(); assert seq_id > 0; if (this.getGlycanSequenceContexts() == null) { return new ArrayList<BiologicalContext>(); } List bc_list = getEntityManager() .getQuery( QUERY_ALL_BCS_FOR_SEQUENCE ) .setParameter("sequence_id", seq_id ) .list(); return (List<BiologicalContext>) bc_list; */ Set<GlycanSequenceContext> gsbc_list = this.getGlycanSequenceContexts(); if ( gsbc_list == null || gsbc_list.size() == 0 ) return emptyList(); List<BiologicalContext> bc_list = new ArrayList<BiologicalContext>( gsbc_list.size() ); for ( GlycanSequenceContext c : gsbc_list ) bc_list.add( c.getBiologicalContext()); return unmodifiableList( bc_list ); } /* getUniqueBiologicalContexts *//*********************************** * * Returns an unmodifiable list of all unique {@link BiologicalContext}s in * which the current glycan sequence has been observed. This differs from * method {@link #getBiologicalContexts}, which may return contexts that * are equal by value, but which have different BC ids. * @see #addBiologicalContext */ @SuppressWarnings("unchecked") public List<BiologicalContext> getUniqueBiologicalContexts() { /* TODO: replace this impl with a named query */ Set<GlycanSequenceContext> gsbc_list = this.getGlycanSequenceContexts(); if ( gsbc_list == null || gsbc_list.size() == 0 ) return emptyList(); List<BiologicalContext> bc_list = new ArrayList<BiologicalContext>( gsbc_list.size() ); for ( GlycanSequenceContext c : gsbc_list ) { boolean found = false; for ( BiologicalContext bc : bc_list ) { if ( BiologicalContext.haveSameContent(bc,c.getBiologicalContext()) ) { found = true; break; } } if ( ! found ) bc_list.add(c.getBiologicalContext()); } return unmodifiableList( bc_list ); } /** * Returns an unmodifiable {@link Set} of all associations of * {@link BiologicalContext} and this {@link GlycanSequence}. * @see #addBiologicalContext */ public Set<GlycanSequenceContext> getGlycanSequenceContexts() { return unmodifiableSet( this.glycanContexts ); } /* getTaxonomies *//******************************************* * * Returns an unmodifiable {@link List} of {@link Taxonomy}s in * which this {@link GlycanSequence} has been found. Note that * this List will contain multiples of the same Taxonomy if this * {@link GlycanSequence} has been associated to the same Taxonomy * more than once. * * @see #getUniqueTaxonomies * @see #addBiologicalContext * @see #getBiologicalContexts * @see #getGlycanSequenceContexts */ public List<Taxonomy> getTaxonomies() { return (List<Taxonomy>) getEntityManager() .getQuery(Q + "GET_TAXONOMIES") .setParameter("id", this.glycanSequenceId) .list(); } /** * Return the {@link Set} of the unique {@link Taxonomy}s that * have been associated with this GlycanSequence * * @see #getTaxonomies */ public Set<Taxonomy> getUniqueTaxonomies() { return new HashSet<Taxonomy>(getTaxonomies()); } /* getDiseases *//******************************************* * * Returns an unmodifiable {@link List} of {@link Disease}s in * which this {@link GlycanSequence} has been found. Note that * this List will contain multiples of the same Disease if this * {@link GlycanSequence} has been associated to the same Disease * more than once. * * @see #getUniqueDiseases * @see #addBiologicalContext * @see #getBiologicalContexts * @see #getGlycanSequenceContexts */ public List<Disease> getDiseases() { return (List<Disease>) getEntityManager() .getQuery(Q + "GET_DISEASES") .setParameter("id", this.glycanSequenceId) .list(); } /** * Returns the {@link Set} of unique {@link Disease}s associated * with this GlycanSequence. * * @see #getDiseases * @see #getBiologicalContexts */ public Set<Disease> getUniqueDiseases() { return new HashSet<Disease>( getDiseases() ); } /* getTissues *//******************************************* * * Returns an unmodifiable {@link List} of {@link TissueTaxonomy}s in * which this {@link GlycanSequence} has been found. Note that * this List will contain multiples of the same tissue if this * {@link GlycanSequence} has been associated to the same tissue * more than once. * * @see #getUniqueTissues * @see #addBiologicalContext * @see #getBiologicalContexts * @see #getGlycanSequenceContexts */ public List<TissueTaxonomy> getTissues() { return (List<TissueTaxonomy>) getEntityManager() .getQuery(Q + "GET_TISSUES") .setParameter("id", this.glycanSequenceId) .list(); } /** * Return the {@link Set} of unique {@link Tissue}s associated * with this GlycanSequence * * @see #getTissues * @see #getBiologicalContexts */ public Set<TissueTaxonomy> getUniqueTissues() { return new HashSet<TissueTaxonomy>( getTissues() ); } /* getReferences *//******************************************* * * Returns an unmodifiable {@link List} of all {@link Reference}s * that are linked to this {@link GlycanSequence}, returning an * empty list if there are none. Note that this method returns * all References that reference this {@link GlycanSequence}, * including subclasses of {@link Reference}, such as journal * articles, represented as {@link JournalReference}s. * * @see #addReference * @see Reference * @see JournalReference */ @SuppressWarnings("unchecked") public List<Reference> getReferences() { // must perform a HQL lookup for associations that are polymorphic. int id = this.getGlycanSequenceId(); if ( log.isDebugEnabled() ) log.debug("looking up all References for GlycanSequence=" + id ); assert id > 0; List<Reference> reflist = (List<Reference>) getEntityManager() .getQuery( Q + "GET_REFERENCES_FOR_SEQUENCE") .setParameter("sequence_id", id ) .list(); if ( reflist == null ) return emptyList(); return unmodifiableList( reflist ); } /** * Returns all {@link Reference}s for this {@link GlycanSequence} * that have been contributed by the given {@link Contributor}. * Otherwise similar to {@link #getReferences()}. * * @see Contributed */ public List<Reference> getReferences( Contributor c ) { // must perform a HQL lookup for associations that are polymorphic. int id = this.getGlycanSequenceId(); if ( log.isDebugEnabled() ) log.debug("looking up all References for GlycanSequence=" + id ); assert id > 0; int contributorId = c.getContributorId(); List<Reference> reflist = (List<Reference>) getEntityManager() .getQuery( Q + "GET_REFERENCES_FOR_SEQUENCE_AND_CONTRIBUTOR") .setParameter("sequence_id", id ) .setParameter("contributor_id", contributorId) .list(); if ( reflist == null ) return emptyList(); return unmodifiableList( reflist ); } /* hasEvidence *//********************************************* * * Returns true if this {@link GlycanSequence} has at least * 1 piece of associated {@link Evidence}. */ public boolean hasEvidence() { Set<GlycanSequenceEvidence> gse_set = this.getGlycanSequenceEvidence(); return ( gse_set!=null && gse_set.size() > 0 ); } /* hasMSEvidence *//******************************************* * * Returns true if this {@link GlycanSequence} has at least * 1 piece of associated Mass Spec {@link Evidence}. * @see org.eurocarbdb.dataaccess.ms.Acquisition */ public boolean hasMSEvidence() { Set<GlycanSequenceEvidence> gse_set = this.getGlycanSequenceEvidence(); if( gse_set==null ) return false; for( GlycanSequenceEvidence gse : gse_set ) { if( gse.getEvidence().getTechnique().isMS() ) return true; } return false; } /* hasHPLCEvidence *//***************************************** * * Returns true if this {@link GlycanSequence} has at least * 1 piece of associated HPLC {@link Evidence}. * * @see org.eurocarbdb.dataaccess.hplc.DigestProfile */ public boolean hasHPLCEvidence() { Set<GlycanSequenceEvidence> gse_set = this.getGlycanSequenceEvidence(); if( gse_set==null ) return false; for( GlycanSequenceEvidence gse : gse_set ) { if( gse.getEvidence().getTechnique().isHPLC() ) return true; } return false; } /* hasNMREvidence *//****************************************** * * Returns true if this {@link GlycanSequence} has at least * 1 piece of associated NMR {@link Evidence}. * * @see org.eurocarbdb.dataaccess.nmr.NmrEvidence */ public boolean hasNMREvidence() { Set<GlycanSequenceEvidence> gse_set = this.getGlycanSequenceEvidence(); if( gse_set==null ) return false; for( GlycanSequenceEvidence gse : gse_set ) { if( gse.getEvidence().getTechnique().isNMR() ) return true; } return false; } /** * Returns true if the {@link Sugar} returned by {@link #getSugar} * contains one or more unknown (indefinite) elements. * * @see Sugar#isDefinite */ public boolean isDefinite() { if ( isDefinite == null ) { try { isDefinite = getSugar().isDefinite(); } catch ( Exception ex ) { log.warn("Caught exception while determining if definite", ex ); return false; // assume false... } } return isDefinite; } /** *<p> * Translates the current {@link Sugar} (ie: {@link Graph} of * {@link Linkage}s and {@link Residue}s) contained within * this {@link GlycanSequence}, into a {@link Set} of * {@link GlycanResidue}s, which represent the tree structure * of that Sugar. This is to enable sub-structure searches in the DB. *</p> *<p> * Note: current code/DB implementation DOES NOT HANDLE Sugars that * are not trees (ie: if they contain cycles). We will handle full graph * searches in a different way, since sugar trees will be the general case. *</p> *<p> * note: rough development version -- will become a protected/private method. *</p> */ public void calculateSubstructureInfo() { // Set<GlycanResidue> set = this.glycanResidues; Set<GlycanResidue> glycan_residues = GlycanResidue.calculateResidueGraph( this ); //this.setGlycanResidues( glycan_residues ); if ( log.isDebugEnabled() ) { try { log.debug( "--------- glycan graph ----------\n" + this.getSugarSequence().getSugar().getGraph() ); log.debug("--------- glycan residue list ----------"); for ( GlycanResidue gr : glycan_residues ) log.debug( gr ); } catch ( Exception ex ) { // skip these, Exception has already been logged... return; } } this.glycanResidues.clear(); for ( GlycanResidue gr : glycan_residues ) this.glycanResidues.add( gr ); this.setResidueCount( glycan_residues.size() ); return; } /** * Returns the (unmodifiable) {@link Set} of {@link Residue}s * that constitute the {@link SugarSequence} of this {@link GlycanSequence}. * * @see #calculateSubstructureInfo() */ public Set<GlycanResidue> getGlycanResidues() { return unmodifiableSet( glycanResidues ); } /** * Checks the validity of this {@link GlycanSequence} and all its * associations. */ @Override public void validate() throws EurocarbException { /* check our SugarSequence */ SugarSequence ss = this.getSugarSequence(); if ( ss == null ) throw new InvalidAssociationException( this, SugarSequence.class, "getSugarSequence() returned null" ); // check the Sugar of our SugarSequence Sugar s = ss.getSugar(); if ( s == null || s.countResidues() == 0 ) { throw new InvalidAssociationException( this, Sugar.class, "Sugar object was null or contained 0 residues" ); } /* check/calculate GlycanResidues */ // sometime in the future this should also check if: // glycanResidues.size() != this.getResidueCount() if ( glycanResidues == null || glycanResidues.size() == 0 ) { this.calculateSubstructureInfo(); } // other checks go here... // ...ok, object is valid. return; } public boolean equals( Object x ) { if ( this == x ) return true; if ( (x == null) || (x.getClass() != this.getClass()) ) return false; // objects are the same class GlycanSequence gs = (GlycanSequence) x; if (gs.sequence == null) { return (this.sequence == null); } return this.sequence.toString().equals(gs.sequence.toString()); } //~~~~~~~~~~~~~~~~~~~~~ PRIVATE METHODS ~~~~~~~~~~~~~~~~~~~~~~~// /** internal use only. @see #calculateSubstructureInfo() */ void setGlycanResidues( Set<GlycanResidue> residues ) { this.glycanResidues = residues; } /** * mjh: this should be calculated from the underlying sugar, not set; * internal use only! */ void setResidueCount( int residueCount ) { assert residueCount > 0; this.residueCount = residueCount; } /** mjh: this should be calculated from the underlying sugar, not set */ void setMassMonoisotopic( BigDecimal massMonoisotopic ) { this.massMonoisotopic = massMonoisotopic; } /** mjh: this should be calculated from the underlying sugar, not set */ void setMassAverage( BigDecimal massAverage ) { this.massAverage = massAverage; } /** mjh: this value is generated on insert by the DB; internal use only! */ void setDateEntered( Date dateEntered ) { this.dateEntered = dateEntered; } /** mjh: this value is generated on insert by the DB; internal use only! */ void setDateContributed( Date dateContributed ) { this.dateContributed = dateContributed; } /** Private use only, see {@link #getReferences}. */ Set<GlycanSequenceReference> getGlycanSequenceReferences() { return this.glycanReferences; } /** Private use only, see {@link #addReference}. */ void setGlycanSequenceReferences( Set<GlycanSequenceReference> glycanReferences ) { assert glycanReferences != null; this.glycanReferences = glycanReferences; } /** Private use only, see {@link #getEvidence}. */ Set<GlycanSequenceEvidence> getGlycanSequenceEvidence() { return this.glycanEvidence; } /** Private use only, see {@link #addEvidence}. */ void setGlycanSequenceEvidence( Set<GlycanSequenceEvidence> glycanEvidence ) { this.glycanEvidence = glycanEvidence; } public BiologicalContext getBiologicalContext(int id){ for(GlycanSequenceContext context:this.glycanContexts){ if(context.getBiologicalContext().getBiologicalContextId()==id){ return context.getBiologicalContext(); } } return null; } } // end class