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>SequenceDBSearchResult</code> objects represent a result of a
041 * search of a <code>SymbolList</code> against the sequences within a
042 * <code>SequenceDB</code> object. The core data (query sequence,
043 * database, search parameters, hits) have accessors, while
044 * 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 Matthew Pocock
051 * @since 1.1
052 * @deprecated SimpleSeqSimilaritySearchResult has been made
053 * Annotatable and is now functionally identical.
054 * @see AbstractChangeable
055 * @see SeqSimilaritySearchResult
056 * @see Annotatable
057 */
058public class SequenceDBSearchResult extends AbstractChangeable
059    implements SeqSimilaritySearchResult, Annotatable
060{
061    protected transient ChangeForwarder annotationForwarder;
062
063    private Sequence   querySequence;
064    private SequenceDB sequenceDB;
065    private Map        searchParameters;
066    private Annotation annotation;
067    private List       hits;
068
069    // Hashcode is cached after first calculation because the data on
070    // which is is based do not change
071    private int hc;
072    private boolean hcCalc;
073
074    /**
075     * Creates a new <code>SequenceDBSearchResult</code>.
076     *
077     * @param querySequence a <code>Sequence</code>.
078     * @param sequenceDB a <code>SequenceDB</code>.
079     * @param searchParameters a <code>Map</code>.
080     * @param annotation an <code>Annotation</code>.
081     * @param hits a <code>List</code>.
082     */
083    public SequenceDBSearchResult(Sequence   querySequence,
084                                  SequenceDB sequenceDB,
085                                  Map        searchParameters,
086                                  List       hits,
087                                  Annotation annotation)
088    {
089        if (querySequence == null)
090        {
091            throw new IllegalArgumentException("querySequence was null");
092        }
093
094        if (sequenceDB == null)
095        {
096            throw new IllegalArgumentException("sequenceDB was null");
097        }
098
099        if (searchParameters != null)
100        {
101            this.searchParameters =
102                Collections.unmodifiableMap(searchParameters);
103        }
104
105        if (annotation == null)
106        {
107            throw new IllegalArgumentException("annotation was null");
108        }
109
110        if (hits == null)
111        {
112            throw new IllegalArgumentException("hits was null");
113        }
114
115        // Lock the sequenceDB by vetoing all changes
116        sequenceDB.addChangeListener(ChangeListener.ALWAYS_VETO);
117
118        // Lock the querySeq by vetoing all changes
119        querySequence.addChangeListener(ChangeListener.ALWAYS_VETO);
120
121        // Lock the annotation by vetoing all changes to properties
122        annotation.addChangeListener(ChangeListener.ALWAYS_VETO);
123
124        this.querySequence = querySequence;
125        this.sequenceDB    = sequenceDB;
126        this.annotation    = annotation;
127        this.hits          = Collections.unmodifiableList(hits);
128
129        hcCalc = false;
130    }
131
132    public Sequence getQuerySequence()
133    {
134        return querySequence;
135    }
136
137    public SequenceDB getSequenceDB()
138    {
139        return sequenceDB;
140    }
141
142    public Map getSearchParameters()
143    {
144        return searchParameters;
145    }
146
147    public List getHits()
148    {
149        return hits;
150    }
151
152    /**
153     * <code>getAnnotation</code> returns the Annotation associated
154     * with this hit.
155     *
156     * @return an <code>Annotation</code>.
157     */
158    public Annotation getAnnotation()
159    {
160        return annotation;
161    }
162
163    public boolean equals(Object other)
164    {
165        if (other == this) return true;
166        if (other == null) return false;
167
168        if (! other.getClass().equals(this.getClass())) return false;
169
170        SequenceDBSearchResult that = (SequenceDBSearchResult) 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 "SequenceDBSearchResult 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}