1 /* Copyright (C) 2004 J.F.Dockes
2  *   This program is free software; you can redistribute it and/or modify
3  *   it under the terms of the GNU General Public License as published by
4  *   the Free Software Foundation; either version 2 of the License, or
5  *   (at your option) any later version.
6  *
7  *   This program is distributed in the hope that it will be useful,
8  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
9  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  *   GNU General Public License for more details.
11  *
12  *   You should have received a copy of the GNU General Public License
13  *   along with this program; if not, write to the
14  *   Free Software Foundation, Inc.,
15  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16  */
17 #ifndef _DOCSEQ_H_INCLUDED_
18 #define _DOCSEQ_H_INCLUDED_
19 
20 #include "autoconfig.h"
21 
22 #include <string>
23 #include <list>
24 #include <vector>
25 #include <mutex>
26 #include <memory>
27 
28 #include "rcldoc.h"
29 #include "hldata.h"
30 
31 // Need this for the "Snippet" class def.
32 #include "rclquery.h"
33 
34 // A result list entry.
35 struct ResListEntry {
36     Rcl::Doc doc;
37     std::string subHeader;
38 };
39 
40 /** Sort specification. */
41 class DocSeqSortSpec {
42 public:
DocSeqSortSpec()43     DocSeqSortSpec() : desc(false) {}
isNotNull()44     bool isNotNull() const {return !field.empty();}
reset()45     void reset() {field.erase();}
46     std::string field;
47     bool   desc;
48 };
49 
50 /** Filtering spec. This is only used to filter by doc category for now, hence
51     the rather specialized interface */
52 class DocSeqFiltSpec {
53 public:
DocSeqFiltSpec()54     DocSeqFiltSpec() {}
55     enum Crit {DSFS_MIMETYPE, DSFS_QLANG, DSFS_PASSALL};
orCrit(Crit crit,const std::string & value)56     void orCrit(Crit crit, const std::string& value) {
57         crits.push_back(crit);
58         values.push_back(value);
59     }
60     std::vector<Crit> crits;
61     std::vector<std::string> values;
reset()62     void reset() {crits.clear(); values.clear();}
isNotNull()63     bool isNotNull() const {return crits.size() != 0;}
64 };
65 
66 /** Interface for a list of documents coming from some source.
67 
68     The result list display data may come from different sources (ie:
69     history or Db query), and be post-processed (DocSeqSorted).
70     Additional functionality like filtering/sorting can either be
71     obtained by stacking DocSequence objects (ie: sorting history), or
72     by native capability (ex: docseqdb can sort and filter). The
73     implementation might be nicer by using more sophisticated c++ with
74     multiple inheritance of sort and filter virtual interfaces, but
75     the current one will have to do for now.
76 */
77 class DocSequence {
78 public:
DocSequence(const std::string & t)79     DocSequence(const std::string &t) : m_title(t) {}
~DocSequence()80     virtual ~DocSequence() {}
81 
82     /** Get document at given rank.
83      *
84      * @param num document rank in sequence
85      * @param doc return data
86      * @param sh subheader to display before this result (ie: date change
87      *           inside history)
88      * @return true if ok, false for error or end of data
89      */
90     virtual bool getDoc(int num, Rcl::Doc &doc, std::string *sh = 0) = 0;
91 
92     /** Get next page of documents. This accumulates entries into the result
93      *  list parameter (doesn't reset it). */
94     virtual int getSeqSlice(int offs, int cnt,
95                             std::vector<ResListEntry>& result);
96 
97     /** Get abstract for document. This is special because it may take time.
98      *  The default is to return the input doc's abstract fields, but some
99      *  sequences can compute a better value (ie: docseqdb) */
getAbstract(Rcl::Doc & doc,std::vector<std::string> & abs)100     virtual bool getAbstract(Rcl::Doc& doc, std::vector<std::string>& abs) {
101         abs.push_back(doc.meta[Rcl::Doc::keyabs]);
102         return true;
103     }
getAbstract(Rcl::Doc & doc,std::vector<Rcl::Snippet> & abs,int,bool)104     virtual bool getAbstract(Rcl::Doc& doc, std::vector<Rcl::Snippet>& abs,
105                              int, bool) {
106         abs.push_back(Rcl::Snippet(0, doc.meta[Rcl::Doc::keyabs]));
107         return true;
108     }
getFirstMatchPage(Rcl::Doc &,std::string &)109     virtual int getFirstMatchPage(Rcl::Doc&, std::string&) {
110         return -1;
111     }
112     /** Get duplicates. */
docDups(const Rcl::Doc &,std::vector<Rcl::Doc> &)113     virtual bool docDups(const Rcl::Doc&, std::vector<Rcl::Doc>&) {
114         return false;
115     }
116 
117     /** For an embedded document: get the immediately enclosing doc
118      * (e.g., for an attachment, the message it is attached to. Only
119      * makes sense is ipath is not empty. */
120     virtual bool getEnclosing(Rcl::Doc&, Rcl::Doc&);
121 
122     /** Get estimated total count in results */
123     virtual int getResCnt() = 0;
124 
125     /** Get title for result list */
title()126     virtual std::string title() {
127         return m_title;
128     }
129 
130     /** Can do snippets ? */
snippetsCapable()131     virtual bool snippetsCapable() {
132         return false;
133     }
134     /** Get description for underlying query */
135     virtual std::string getDescription() = 0;
136 
137     /** Get search terms (for highlighting abstracts). Some sequences
138      * may have no associated search terms. Implement this for them. */
getTerms(HighlightData & hld)139     virtual void getTerms(HighlightData& hld) {
140         hld.clear();
141     }
expand(Rcl::Doc &)142     virtual std::list<std::string> expand(Rcl::Doc &) {
143         return std::list<std::string>();
144     }
getReason()145     virtual std::string getReason() {
146         return m_reason;
147     }
148     /** Optional functionality. */
canFilter()149     virtual bool canFilter() {return false;}
canSort()150     virtual bool canSort() {return false;}
setFiltSpec(const DocSeqFiltSpec &)151     virtual bool setFiltSpec(const DocSeqFiltSpec &) {return false;}
setSortSpec(const DocSeqSortSpec &)152     virtual bool setSortSpec(const DocSeqSortSpec &) {return false;}
getSourceSeq()153     virtual std::shared_ptr<DocSequence> getSourceSeq() {
154         return std::shared_ptr<DocSequence>();}
155 
set_translations(const std::string & sort,const std::string & filt)156     static void set_translations(const std::string& sort,
157                                  const std::string& filt) {
158         o_sort_trans = sort;
159         o_filt_trans = filt;
160     }
161 
162 
163 protected:
164     friend class DocSeqModifier;
165     virtual std::shared_ptr<Rcl::Db> getDb() = 0;
166     static std::mutex o_dblock;
167     static std::string o_sort_trans;
168     static std::string o_filt_trans;
169     std::string          m_reason;
170 
171 private:
172     std::string          m_title;
173 };
174 
175 /** A modifier has a child sequence which does the real work and does
176  * something with the results. Some operations are just delegated
177  */
178 class DocSeqModifier : public DocSequence {
179 public:
DocSeqModifier(std::shared_ptr<DocSequence> iseq)180     DocSeqModifier(std::shared_ptr<DocSequence> iseq)
181         : DocSequence(""), m_seq(iseq)
182         {}
~DocSeqModifier()183     virtual ~DocSeqModifier() {}
184 
getAbstract(Rcl::Doc & doc,std::vector<std::string> & abs)185     virtual bool getAbstract(Rcl::Doc& doc, std::vector<std::string>& abs)
186         override{
187         if (!m_seq)
188             return false;
189         return m_seq->getAbstract(doc, abs);
190     }
getAbstract(Rcl::Doc & doc,std::vector<Rcl::Snippet> & abs,int maxlen,bool bypage)191     virtual bool getAbstract(Rcl::Doc& doc, std::vector<Rcl::Snippet>& abs,
192                              int maxlen, bool bypage) override {
193         if (!m_seq)
194             return false;
195         return m_seq->getAbstract(doc, abs, maxlen, bypage);
196     }
197     /** Get duplicates. */
docDups(const Rcl::Doc & doc,std::vector<Rcl::Doc> & dups)198     virtual bool docDups(const Rcl::Doc& doc, std::vector<Rcl::Doc>& dups)
199         override {
200         if (!m_seq)
201             return false;
202         return m_seq->docDups(doc, dups);
203     }
204 
snippetsCapable()205     virtual bool snippetsCapable() override {
206         if (!m_seq)
207             return false;
208         return m_seq->snippetsCapable();
209     }
getDescription()210     virtual std::string getDescription() override {
211         if (!m_seq)
212             return "";
213         return m_seq->getDescription();
214     }
getTerms(HighlightData & hld)215     virtual void getTerms(HighlightData& hld) override {
216         if (!m_seq)
217             return;
218         m_seq->getTerms(hld);
219     }
getEnclosing(Rcl::Doc & doc,Rcl::Doc & pdoc)220     virtual bool getEnclosing(Rcl::Doc& doc, Rcl::Doc& pdoc) override {
221         if (!m_seq)
222             return false;
223         return m_seq->getEnclosing(doc, pdoc);
224     }
getReason()225     virtual std::string getReason() override {
226         if (!m_seq)
227             return string();
228         return m_seq->getReason();
229     }
title()230     virtual std::string title() override {
231         return m_seq->title();
232     }
getSourceSeq()233     virtual std::shared_ptr<DocSequence> getSourceSeq() override {
234         return m_seq;
235     }
236 
237 protected:
getDb()238     virtual std::shared_ptr<Rcl::Db> getDb() override {
239         if (!m_seq)
240             return 0;
241         return m_seq->getDb();
242     }
243 
244     std::shared_ptr<DocSequence>    m_seq;
245 };
246 
247 class RclConfig;
248 
249 // A DocSource can juggle docseqs of different kinds to implement
250 // sorting and filtering in ways depending on the base seqs capabilities
251 class DocSource : public DocSeqModifier {
252 public:
DocSource(RclConfig * config,std::shared_ptr<DocSequence> iseq)253     DocSource(RclConfig *config, std::shared_ptr<DocSequence> iseq)
254         : DocSeqModifier(iseq), m_config(config)
255         {}
canFilter()256     virtual bool canFilter() override {return true;}
canSort()257     virtual bool canSort() override {return true;}
258     virtual bool setFiltSpec(const DocSeqFiltSpec &) override;
259     virtual bool setSortSpec(const DocSeqSortSpec &) override;
260     virtual bool getDoc(int num, Rcl::Doc &doc, std::string *sh = 0) override {
261         if (!m_seq)
262             return false;
263         return m_seq->getDoc(num, doc, sh);
264     }
getResCnt()265     virtual int getResCnt() override {
266         if (!m_seq)
267             return 0;
268         return m_seq->getResCnt();
269     }
270     virtual std::string title() override;
271 private:
272     bool buildStack();
273     void stripStack();
274     RclConfig *m_config;
275     DocSeqFiltSpec  m_fspec;
276     DocSeqSortSpec  m_sspec;
277 };
278 
279 #endif /* _DOCSEQ_H_ */
280