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.biojavax;
023import java.util.ArrayList;
024import java.util.List;
025import java.util.zip.Checksum;
026import org.biojava.utils.AbstractChangeable;
027import org.biojava.utils.ChangeEvent;
028import org.biojava.utils.ChangeSupport;
029import org.biojava.utils.ChangeVetoException;
030import org.biojavax.utils.CRC64Checksum;
031
032/**
033 * A basic DocRef implementation.
034 * @author Richard Holland
035 * @author Mark Schreiber
036 * @author George Waldon
037 * @since 1.5
038 */
039
040public class SimpleDocRef extends AbstractChangeable implements DocRef {
041    
042    private CrossRef crossref;
043    private List<DocRefAuthor> authors;
044    private String title;
045    private String location;
046    private String remark;
047    
048    /**
049     * Creates a new document reference from the given immutable authors and
050     * location and title. Will throw exceptions if either authors or
051     * location are null, but a null title is allowable.
052     * @param authors The authors of the referenced document, as a set of DocRefAuthor instances.
053     * @param location The location of the document, e.g. the journal name and page range.
054     */
055    public SimpleDocRef(List<DocRefAuthor> authors, String location) {
056        this(authors, location, null);
057    }
058    
059    /**
060     * Creates a new document reference from the given immutable authors and
061     * location and title. Will throw exceptions if either authors or
062     * location are null, but a null title is allowable.
063     * @param authors The authors of the referenced document, as a string to be parsed
064     * with {@link DocRefAuthor.Tools#parseAuthorString(String)}.
065     * @param location The location of the document, eg. the journal name and page range.
066     */
067    public SimpleDocRef(String authors, String location) {
068        this(DocRefAuthor.Tools.parseAuthorString(authors), location, null);
069    }
070    
071    /**
072     * Creates a new document reference from the given immutable authors and
073     * location and title. Will throw exceptions if either authors or
074     * location are null, but a null title is allowable.
075     * @param authors The authors of the referenced document, as a string to be parsed
076     * with {@link DocRefAuthor.Tools#parseAuthorString(String)}.
077     * @param location The location of the document, e.g. the journal name and page range.
078     * @param title The title of the document.
079     */
080    public SimpleDocRef(String authors, String location, String title) {
081        this(DocRefAuthor.Tools.parseAuthorString(authors), location, title);
082    }
083    
084    /**
085     * Creates a new document reference from the given immutable authors and
086     * location and title. Will throw exceptions if either authors or
087     * location are null, but a null title is allowable.
088     * @param authors The authors of the referenced document, as a set of DocRefAuthor instances.
089     * @param location The location of the document, e.g. the journal name and page range.
090     * @param title The title of the document.
091     */
092    public SimpleDocRef(List<DocRefAuthor> authors, String location, String title) {
093        if (authors==null) throw new IllegalArgumentException("Authors cannot be null");
094        if (location==null) throw new IllegalArgumentException("Location cannot be null");
095        this.crossref = null;
096        this.authors = new ArrayList<DocRefAuthor>();
097        this.authors.addAll(authors);
098        this.title = title;
099        this.location = location;
100        this.remark = null;
101    }
102
103    /**
104     * Construct a doc ref with populated cross ref.
105     * @param authors
106     * @param location
107     * @param title
108     * @param crossRefKey
109     * @param crossRefValue
110     * @param crossRefVersion
111     */
112    public SimpleDocRef(String authors, String location, String title, String crossRefKey, String crossRefValue, Integer crossRefVersion) {
113        this(DocRefAuthor.Tools.parseAuthorString(authors), location, title, crossRefKey, crossRefValue, crossRefVersion);
114    }
115    
116    /**
117     * Construct a doc ref with populated cross ref.
118     * @param authors
119     * @param location
120     * @param title
121     * @param crossRefKey
122     * @param crossRefValue
123     * @param crossRefVersion
124     */
125    public SimpleDocRef(List authors, String location, String title, String crossRefKey, String crossRefValue, Integer crossRefVersion) {
126        this(authors, location, title);
127        this.setCrossref((CrossRef) RichObjectFactory.getObject(SimpleCrossRef.class, new Object[]{crossRefKey, crossRefValue, crossRefVersion}));
128    }
129    
130    // Hibernate requirement - not for public use.
131    protected SimpleDocRef() {}
132    
133    /**
134     * {@inheritDoc}
135     */
136    public void setRemark(String remark) throws ChangeVetoException {
137        if(this.remark!=null && this.remark.equals(remark)) return;
138        else if(this.remark==null && remark==null) return;
139                
140        if(!this.hasListeners(DocRef.REMARK)) {
141            this.remark = remark;
142        } else {
143            ChangeEvent ce = new ChangeEvent(
144                    this,
145                    DocRef.REMARK,
146                    remark,
147                    this.remark
148                    );
149            ChangeSupport cs = this.getChangeSupport(DocRef.REMARK);
150            synchronized(cs) {
151                cs.firePreChangeEvent(ce);
152                this.remark = remark;
153                cs.firePostChangeEvent(ce);
154            }
155        }
156    }
157    
158    // Hibernate requirement - not for public use.
159    void setCRC(String CRC) {} // ignore as field is a calculated value
160        
161    /**
162     * {@inheritDoc}
163     */
164    public void setCrossref(CrossRef crossref) throws ChangeVetoException {
165        if(this.crossref!=null && this.crossref.equals(crossref)) return;
166        else if(this.crossref==null && crossref==null) return;
167        
168        if(!this.hasListeners(DocRef.CROSSREF)) {
169            this.crossref = crossref;
170        } else {
171            ChangeEvent ce = new ChangeEvent(
172                    this,
173                    DocRef.CROSSREF,
174                    crossref,
175                    this.crossref
176                    );
177            ChangeSupport cs = this.getChangeSupport(DocRef.CROSSREF);
178            synchronized(cs) {
179                cs.firePreChangeEvent(ce);
180                this.crossref = crossref;
181                cs.firePostChangeEvent(ce);
182            }
183        }
184    }
185    
186    // Hibernate requirement - not for public use.
187    void setAuthors(String authors) { this.authors = DocRefAuthor.Tools.parseAuthorString(authors); }
188    
189    // Hibernate requirement - not for public use.
190    void setLocation(String location) { this.location = location; }
191
192    // Hibernate requirement - not for public use.
193    void setTitle(String title) { this.title = title; }
194    
195    /**
196     * {@inheritDoc}
197     */
198    public String getAuthors() { return DocRefAuthor.Tools.generateAuthorString(this.authors, true); }
199    
200    /**
201     * {@inheritDoc}
202     */
203    public List<DocRefAuthor> getAuthorList() { return new ArrayList(this.authors); }
204    
205    /**
206     * {@inheritDoc}
207     * The string to be checksummed is constructed by concatenating the authors,
208     * title, and location in that order, with no space between. If any values are
209     * null they are substituted with the text "&lt;undef>".
210     * @see CRC64Checksum
211     */
212    public String getCRC() {
213        StringBuffer sb = new StringBuffer();
214        sb.append(this.getAuthors());
215        sb.append((this.title==null || this.title.equals(""))?"<undef>":this.title);
216        sb.append((this.location==null || this.location.equals(""))?"<undef>":this.location);
217        Checksum cs = new CRC64Checksum();
218        cs.update(sb.toString().getBytes(), 0, sb.length());
219        return cs.toString();
220    }
221    
222    /**
223     * {@inheritDoc}
224     */
225    public String getRemark() { return this.remark; }
226    
227    /**
228     * {@inheritDoc}
229     */
230    public CrossRef getCrossref() { return this.crossref; }
231    
232    /**
233     * {@inheritDoc}
234     */
235    public String getLocation() { return this.location; }
236    
237    /**
238     * {@inheritDoc}
239     */
240    public String getTitle() { return this.title; }
241    
242    /**
243     * {@inheritDoc}
244     * Document references are compared first by author, then by location, then
245     * by title. If Author and location are equal and this title is null, 
246     * and theirs isn't, then this will return -1.
247     * For symmetry if our title is not null and theirs is then we return 1. If
248     * both are null then we return 0.
249     */
250    public int compareTo(Object o) {
251        if(o == this) return 0;
252        // Hibernate comparison - we haven't been populated yet
253        if (this.authors==null) return -1;
254        // Normal comparison
255        DocRef them = (DocRef)o;
256        if (!this.getAuthors().equals(them.getAuthors())) return this.getAuthors().compareTo(them.getAuthors());
257        else if (!this.getLocation().equals(them.getLocation())) return this.getLocation().compareTo(them.getLocation());
258        else if (this.getTitle()==null) {
259                if (them.getTitle()==null) return 0;
260            else return -1;
261        }
262        else if (this.getTitle() != null && them.getTitle() == null) return 1; //other cases are handled above.
263        else return this.getTitle().compareTo(them.getTitle());
264    }
265    
266    /**
267     * {@inheritDoc}
268     * Document references are equal if they have the same author and location and title.
269     */
270    public boolean equals(Object obj) {
271        if(this == obj) return true;
272        if (obj==null || !(obj instanceof DocRef)) return false;
273        // Hibernate comparison - we haven't been populated yet
274        if (this.authors==null) return false;
275        // Normal comparison
276        DocRef them = (DocRef)obj;
277        return (this.getAuthors().equals(them.getAuthors()) &&
278                this.getLocation().equals(them.getLocation()) &&
279                (
280                                (this.getTitle()==them.getTitle()) ||
281                                (this.getTitle()!=null && this.getTitle().equals(them.getTitle()))
282                )
283                );
284    }
285    
286    /**
287     * {@inheritDoc}
288     */
289    public int hashCode() {
290        int code = 17;
291        // Hibernate comparison - we haven't been populated yet
292        if (this.authors==null) return code;
293        // Normal comparison
294        code = 37*code + this.getAuthors().hashCode();
295        code = 37*code + this.location.hashCode();
296        if (this.title!=null) code = 37*code + this.title.hashCode();
297        return code;
298    }
299    
300    /**
301     * {@inheritDoc}
302     * Form: "authors; location"
303     */
304    public String toString() {
305        return this.getAuthors()+"; "+this.getLocation();
306    }
307    
308    // Hibernate requirement - not for public use.
309    private Integer id;
310    
311    /**
312     * Gets the Hibernate ID. Should be used with caution.
313     * @return the Hibernate ID, if using Hibernate.
314     */
315    public Integer getId() { return this.id; }
316    
317    /**
318     * Sets the Hibernate ID. Should be used with caution.
319     * @param id the Hibernate ID, if using Hibernate.
320     */
321    public void setId(Integer id) { this.id = id;}
322    
323}
324