001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software GmbH & Co. KG, please see the
018 * company website: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.search;
029
030import org.opencms.monitor.CmsMemoryMonitor;
031import org.opencms.monitor.I_CmsMemoryMonitorable;
032import org.opencms.search.fields.CmsSearchField;
033import org.opencms.util.CmsStringUtil;
034
035import java.text.ParseException;
036import java.util.Date;
037import java.util.HashMap;
038import java.util.Iterator;
039import java.util.Map;
040
041import org.apache.lucene.document.DateTools;
042import org.apache.lucene.document.Document;
043import org.apache.lucene.index.IndexableField;
044
045/**
046 * Contains the data of a single item in a search result.<p>
047 *
048 * @since 6.0.0
049 */
050public class CmsSearchResult implements I_CmsMemoryMonitorable, Comparable<CmsSearchResult> {
051
052    /** The creation date of this search result. */
053    protected Date m_dateCreated;
054
055    /** The last modification date of this search result. */
056    protected Date m_dateLastModified;
057
058    /** The document type of the search result. */
059    protected String m_documentType;
060
061    /** The excerpt of this search result. */
062    protected String m_excerpt;
063
064    /** Holds the values of the search result fields. */
065    protected Map<String, String> m_fields;
066
067    /** The resource path of this search result. */
068    protected String m_path;
069
070    /** The score of this search result. */
071    protected int m_score;
072
073    /** Contains the pre-calculated memory size. */
074    private int m_memorySize;
075
076    /**
077     * Creates a new search result.<p>
078     *
079     * @param score the score of this search result
080     * @param doc the Lucene document to extract fields from such as description, title, key words etc. pp.
081     * @param excerpt the excerpt of the search result's content
082     */
083    public CmsSearchResult(int score, Document doc, String excerpt) {
084
085        m_score = score;
086        m_excerpt = excerpt;
087        m_fields = new HashMap<String, String>();
088
089        Iterator<IndexableField> i = doc.getFields().iterator();
090        while (i.hasNext()) {
091            IndexableField field = i.next();
092            // content can be displayed only if it has been stored in the field
093            String name = field.name();
094            String value = field.stringValue();
095            if (CmsStringUtil.isNotEmpty(value)
096                && !CmsSearchField.FIELD_PATH.equals(name)
097                && !CmsSearchField.FIELD_DATE_CREATED.equals(name)
098                && !CmsSearchField.FIELD_DATE_LASTMODIFIED.equals(name)) {
099                // these "hard coded" fields are treated differently
100                m_fields.put(name, value);
101            }
102        }
103
104        IndexableField f = doc.getField(CmsSearchField.FIELD_PATH);
105        if (f != null) {
106            m_path = f.stringValue();
107        } else {
108            m_path = null;
109        }
110
111        f = doc.getField(CmsSearchField.FIELD_DATE_CREATED);
112        if (f != null) {
113            try {
114                m_dateCreated = DateTools.stringToDate(f.stringValue());
115            } catch (ParseException exc) {
116                m_dateCreated = null;
117            }
118        } else {
119            m_dateCreated = null;
120        }
121
122        f = doc.getField(CmsSearchField.FIELD_DATE_LASTMODIFIED);
123        if (f != null) {
124            try {
125                m_dateLastModified = DateTools.stringToDate(f.stringValue());
126            } catch (ParseException exc) {
127                m_dateLastModified = null;
128            }
129        } else {
130            m_dateLastModified = null;
131        }
132
133        f = doc.getField(CmsSearchField.FIELD_TYPE);
134        if (f != null) {
135            m_documentType = f.stringValue();
136        } else {
137            m_documentType = null;
138        }
139    }
140
141    /**
142     * Empty constructor to be used for overriding classes.
143     */
144    protected CmsSearchResult() {
145
146        // noop
147    }
148
149    /**
150     * @see java.lang.Comparable#compareTo(java.lang.Object)
151     */
152    public int compareTo(CmsSearchResult obj) {
153
154        if (obj == this) {
155            return 0;
156        }
157        return obj.m_score - m_score;
158    }
159
160    /**
161     * @see java.lang.Object#equals(java.lang.Object)
162     */
163    @Override
164    public boolean equals(Object obj) {
165
166        if (obj == this) {
167            return true;
168        }
169        if (obj instanceof CmsSearchResult) {
170            CmsSearchResult other = (CmsSearchResult)obj;
171            return m_documentType.equals(other.m_documentType) && m_path.equals(other.m_path);
172        }
173        return false;
174    }
175
176    /**
177     * Returns the date created.<p>
178     *
179     * @return the date created
180     */
181    public Date getDateCreated() {
182
183        if (m_dateCreated != null) {
184            return (Date)m_dateCreated.clone();
185        }
186        return null;
187    }
188
189    /**
190     * Returns the date last modified.<p>
191     *
192     * @return the date last modified
193     */
194    public Date getDateLastModified() {
195
196        if (m_dateLastModified != null) {
197            return (Date)m_dateLastModified.clone();
198        }
199        return null;
200    }
201
202    /**
203     * Returns the description.<p>
204     *
205     * @return the description
206     *
207     * @deprecated use {@link #getField(String)} instead with the name of the field,
208     *      for example use {@link org.opencms.search.fields.CmsLuceneField#FIELD_DESCRIPTION} to get the description (if available)
209     */
210    @Deprecated
211    public String getDescription() {
212
213        return getField(CmsSearchField.FIELD_DESCRIPTION);
214    }
215
216    /**
217     * Returns the document type of the search result document.<p>
218     *
219     * Usually this will be a VFS resource type String that can be used in the
220     * resource type manager with {@link org.opencms.loader.CmsResourceManager#getResourceType(String)}.
221     * However, what is stored in the document type field depends only on the indexer used, and therefore it
222     * may also be some String not referring  a VFS resource type but some external type or application.
223     * It may also be <code>null</code> in case it has not been set by a non-standard indexer.<p>
224     *
225     * @return the document type of the search result document
226     *
227     * @see org.opencms.loader.CmsResourceManager#getResourceType(String)
228     */
229    public String getDocumentType() {
230
231        return m_documentType;
232    }
233
234    /**
235     * Returns the excerpt.<p>
236     *
237     * @return the excerpt
238     */
239    public String getExcerpt() {
240
241        return m_excerpt;
242    }
243
244    /**
245     * Returns the text stored in the search index field with the given name.<p>
246     *
247     * @param fieldName the name of the field to get the stored text for
248     *
249     * @return the text stored in the search index field with the given name
250     */
251    public String getField(String fieldName) {
252
253        return m_fields.get(fieldName);
254    }
255
256    /**
257     * Returns the key words.<p>
258     *
259     * @return the key words
260     *
261     * @deprecated use {@link #getField(String)} instead with the name of the field,
262     *      for example use {@link org.opencms.search.fields.CmsLuceneField#FIELD_KEYWORDS} to get the keywords (if available)
263     */
264    @Deprecated
265    public String getKeywords() {
266
267        return getField(CmsSearchField.FIELD_KEYWORDS);
268    }
269
270    /**
271     * @see org.opencms.monitor.I_CmsMemoryMonitorable#getMemorySize()
272     */
273    public int getMemorySize() {
274
275        if (m_memorySize == 0) {
276            int result = 8;
277            if (m_dateCreated != null) {
278                result += CmsMemoryMonitor.getMemorySize(m_dateCreated);
279            }
280            if (m_dateLastModified != null) {
281                result += CmsMemoryMonitor.getMemorySize(m_dateLastModified);
282            }
283            if (m_path != null) {
284                result += CmsMemoryMonitor.getMemorySize(m_path);
285            }
286            if (m_fields != null) {
287                Iterator<Map.Entry<String, String>> entries = m_fields.entrySet().iterator();
288                while (entries.hasNext()) {
289                    Map.Entry<String, String> entry = entries.next();
290                    result += CmsMemoryMonitor.getMemorySize(entry.getKey());
291                    result += CmsMemoryMonitor.getMemorySize(entry.getValue());
292                }
293            }
294            if (m_excerpt != null) {
295                result += CmsMemoryMonitor.getMemorySize(m_excerpt);
296            }
297            m_memorySize = result;
298        }
299        return m_memorySize;
300    }
301
302    /**
303     * Returns the path.<p>
304     *
305     * @return the path
306     */
307    public String getPath() {
308
309        return m_path;
310    }
311
312    /**
313     * Returns the score.<p>
314     *
315     * @return the score
316     */
317    public int getScore() {
318
319        return m_score;
320    }
321
322    /**
323     * Returns the title.<p>
324     *
325     * @return the title
326     *
327     * @deprecated use {@link #getField(String)} instead with the name of the field,
328     *      for example use {@link org.opencms.search.fields.CmsLuceneField#FIELD_TITLE} to get the title (if available)
329     */
330    @Deprecated
331    public String getTitle() {
332
333        return getField(CmsSearchField.FIELD_TITLE);
334    }
335
336    /**
337     * @see java.lang.Object#hashCode()
338     */
339    @Override
340    public int hashCode() {
341
342        return (m_documentType.hashCode() * 1109) + m_path.hashCode();
343    }
344}