001/*
002 *                    BioJava development code
003 *
004 * This code may be freely distributed and modified under the
005 * terms of the GNU Lesser General Public Licence.  This should
006 * be distributed with the code.  If you do not have a copy,
007 * see:
008 *
009 *      http://www.gnu.org/copyleft/lesser.html
010 *
011 * Copyright for this code is held jointly by the individual
012 * authors.  These should be listed in @author doc comments.
013 *
014 * For more information on the BioJava project and its aims,
015 * or to join the biojava-l mailing list, visit the home page
016 * at:
017 *
018 *      http://www.biojava.org/
019 *
020 */
021
022package org.biojava.bio.search;
023
024import java.util.Collections;
025import java.util.List;
026import java.util.Map;
027
028import org.biojava.bio.Annotatable;
029import org.biojava.bio.Annotation;
030import org.biojava.bio.seq.Sequence;
031import org.biojava.bio.seq.db.SequenceDB;
032import org.biojava.utils.AbstractChangeable;
033import org.biojava.utils.ChangeForwarder;
034import org.biojava.utils.ChangeListener;
035import org.biojava.utils.ChangeSupport;
036import org.biojava.utils.ChangeType;
037import org.biojava.utils.ObjectUtil;
038
039/**
040 * <code>SimpleSeqSimilaritySearchResult</code> objects represent a
041 * result of a search of a <code>SymbolList</code> against the
042 * sequences within a <code>SequenceDB</code> object. The core data
043 * (query sequence, database, search parameters, hits) have accessors,
044 * while supplementary data are stored in the <code>Annotation</code>
045 * object. Supplementary data are typically the more loosely formatted
046 * details which vary from one search program to another (and between
047 * versions of those programs).
048 *
049 * @author Keith James
050 * @author Gerald Loeffler
051 * @since 1.1
052 *
053 * @see AbstractChangeable
054 * @see SeqSimilaritySearchResult
055 * @see Annotatable
056 */
057public class SimpleSeqSimilaritySearchResult extends AbstractChangeable
058    implements SeqSimilaritySearchResult
059{
060    protected transient ChangeForwarder annotationForwarder;
061
062    private Sequence   querySequence;
063    private SequenceDB sequenceDB;
064    private Map        searchParameters;
065    private Annotation annotation;
066    private List       hits;
067
068    // Hashcode is cached after first calculation because the data on
069    // which is is based do not change
070    private int hc;
071    private boolean hcCalc;
072
073    /**
074     * Creates a new <code>SimpleSeqSimilaritySearchResult</code>.
075     *
076     * @param querySequence a <code>Sequence</code>.
077     * @param sequenceDB a <code>SequenceDB</code>.
078     * @param searchParameters a <code>Map</code>.
079     * @param annotation an <code>Annotation</code>.
080     * @param hits a <code>List</code>.
081     */
082    public SimpleSeqSimilaritySearchResult(Sequence   querySequence,
083                                           SequenceDB sequenceDB,
084                                           Map        searchParameters,
085                                           List       hits,
086                                           Annotation annotation)
087    {
088        if (querySequence == null)
089        {
090            throw new IllegalArgumentException("querySequence was null");
091        }
092
093        if (sequenceDB == null)
094        {
095            throw new IllegalArgumentException("sequenceDB was null");
096        }
097
098        if (searchParameters != null)
099        {
100            this.searchParameters =
101                Collections.unmodifiableMap(searchParameters);
102        }
103
104        if (annotation == null)
105        {
106            throw new IllegalArgumentException("annotation was null");
107        }
108
109        if (hits == null)
110        {
111            throw new IllegalArgumentException("hits was null");
112        }
113
114        // Lock the sequenceDB by vetoing all changes
115        sequenceDB.addChangeListener(ChangeListener.ALWAYS_VETO);
116
117        // Lock the querySeq by vetoing all changes
118        querySequence.addChangeListener(ChangeListener.ALWAYS_VETO);
119
120        // Lock the annotation by vetoing all changes to properties
121        annotation.addChangeListener(ChangeListener.ALWAYS_VETO);
122
123        this.querySequence = querySequence;
124        this.sequenceDB    = sequenceDB;
125        this.annotation    = annotation;
126        this.hits          = Collections.unmodifiableList(hits);
127
128        hcCalc = false;
129    }
130
131    public Sequence getQuerySequence()
132    {
133        return querySequence;
134    }
135
136    public SequenceDB getSequenceDB()
137    {
138        return sequenceDB;
139    }
140
141    public Map getSearchParameters()
142    {
143        return searchParameters;
144    }
145
146    public List getHits()
147    {
148        return hits;
149    }
150
151    /**
152     * <code>getAnnotation</code> returns the Annotation associated
153     * with this hit.
154     *
155     * @return an <code>Annotation</code>.
156     */
157    public Annotation getAnnotation()
158    {
159        return annotation;
160    }
161
162    public boolean equals(Object other)
163    {
164        if (other == this) return true;
165        if (other == null) return false;
166
167        if (! other.getClass().equals(this.getClass())) return false;
168
169        SimpleSeqSimilaritySearchResult that =
170            (SimpleSeqSimilaritySearchResult) other;
171
172        if (! ObjectUtil.equals(this.querySequence, that.querySequence))
173            return false;
174        if (! ObjectUtil.equals(this.sequenceDB, that.sequenceDB))
175            return false;
176        if (! ObjectUtil.equals(this.searchParameters, that.searchParameters))
177            return false;
178        if (! ObjectUtil.equals(this.annotation, that.annotation))
179            return false;
180        if (! ObjectUtil.equals(this.hits, that.hits))
181            return false;
182
183        return true;
184    }
185
186    public int hashCode()
187    {
188        if (! hcCalc)
189        {
190            hc = ObjectUtil.hashCode(hc, querySequence);
191            hc = ObjectUtil.hashCode(hc, sequenceDB);
192            hc = ObjectUtil.hashCode(hc, searchParameters);
193            hc = ObjectUtil.hashCode(hc, hits);
194            hc = ObjectUtil.hashCode(hc, annotation);
195            hcCalc = true;
196        }
197
198        return hc;
199    }
200
201    public String toString()
202    {
203        return "SimpleSeqSimilaritySearchResult of " + getQuerySequence()
204            + " against " + getSequenceDB().getName();
205    }
206
207    protected ChangeSupport getChangeSupport(ChangeType ct)
208    {
209        ChangeSupport cs = super.getChangeSupport(ct);
210
211        if (annotationForwarder == null &&
212            (ct.isMatchingType(Annotatable.ANNOTATION) || Annotatable.ANNOTATION.isMatchingType(ct)))
213        {
214            annotationForwarder =
215                new ChangeForwarder.Retyper(this, cs, Annotation.PROPERTY);
216            getAnnotation().addChangeListener(annotationForwarder,
217                                              Annotatable.ANNOTATION);
218        }
219
220        return cs;
221    }
222}