1 ///###////////////////////////////////////////////////////////////////////////
2 //
3 // Burton Computer Corporation
4 // http://www.burton-computer.com
5 // http://www.cooldevtools.com
6 // $Id: DatabaseConfig.cc 272 2007-01-06 19:37:27Z brian $
7 //
8 // Copyright (C) 2007 Burton Computer Corporation
9 // ALL RIGHTS RESERVED
10 //
11 // This program is open source software; you can redistribute it
12 // and/or modify it under the terms of the Q Public License (QPL)
13 // version 1.0. Use of this software in whole or in part, including
14 // linking it (modified or unmodified) into other programs is
15 // subject to the terms of the QPL.
16 //
17 // This program is distributed in the hope that it will be useful,
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 // Q Public License for more details.
21 //
22 // You should have received a copy of the Q Public License
23 // along with this program; see the file LICENSE.txt. If not, visit
24 // the Burton Computer Corporation or CoolDevTools web site
25 // QPL pages at:
26 //
27 // http://www.burton-computer.com/qpl.html
28 // http://www.cooldevtools.com/qpl.html
29 //
30
31 #include <stdexcept>
32 #include <cstdlib>
33 #include "File.h"
34 #include "WordData.h"
35 #include "FrequencyDBImpl.h"
36 #include "FrequencyDBImpl_split.h"
37 #include "FrequencyDBImpl_hash.h"
38 #include "FrequencyDBImpl_cache.h"
39 #include "FrequencyDBImpl_dual.h"
40 #include "FrequencyDBImpl_dbm.h"
41 #include "FrequencyDBImpl_bdb.h"
42 #include "FrequencyDBImpl_pbl.h"
43 #include "FrequencyDBImpl_null.h"
44 #include "DatabaseConfig.h"
45
46 struct DBType {
47 const string prefix;
48 const char *suffix;
49 FrequencyDBImplFactory factory;
50 };
51
52 static DBType DBTYPES[] = {
53 #ifdef USE_MMAP
54 { "split", 0, FrequencyDBImpl_split::factory },
55 { "hash", FrequencyDBImpl_hash::SEARCH_SUFFIX, FrequencyDBImpl_hash::factory },
56 #endif
57 #ifdef USE_PBL
58 { "pbl", FrequencyDBImpl_pbl::SEARCH_SUFFIX, FrequencyDBImpl_pbl::factory },
59 #endif
60 #ifdef USE_DB
61 { "bdb", 0, FrequencyDBImpl_bdb::factory },
62 #endif
63 #ifdef USE_DBM
64 { "gdbm", 0, FrequencyDBImpl_dbm::factory },
65 #endif
66 #ifdef USE_PBL
67 { "", FrequencyDBImpl_pbl::SEARCH_SUFFIX, FrequencyDBImpl_pbl::factory },
68 #endif
69 #ifdef USE_DB
70 { "", 0, FrequencyDBImpl_bdb::factory },
71 #endif
72 #ifdef USE_DBM
73 { "", 0, FrequencyDBImpl_dbm::factory },
74 #endif
75 #ifdef USE_MMAP
76 { "", FrequencyDBImpl_hash::SEARCH_SUFFIX, FrequencyDBImpl_hash::factory },
77 #endif
78 { "", 0 }
79 };
80
DatabaseConfig()81 DatabaseConfig::DatabaseConfig()
82 : m_targetSizeMB(32),
83 m_maxCacheTerms(15000)
84 {
85 File basedir(File::getHomeDir(), ".spamprobe");
86 File sp_dir(basedir, "sp_words");
87 m_privateFilename = sp_dir.getPath();
88 }
89
~DatabaseConfig()90 DatabaseConfig::~DatabaseConfig()
91 {
92 }
93
parseCommandLineArg(string & arg,string & database_type,int & target_size_mb)94 void DatabaseConfig::parseCommandLineArg(string &arg,
95 string &database_type,
96 int &target_size_mb)
97 {
98 database_type = "";
99 target_size_mb = m_targetSizeMB;
100
101 string::size_type first_colon = arg.find(":");
102 if (first_colon == string::npos) {
103 return;
104 }
105
106 database_type = arg.substr(0, first_colon);
107
108 string::size_type last_colon = arg.find(":", first_colon + 1);
109 if (last_colon == string::npos) {
110 arg.erase(0, first_colon + 1);
111 } else {
112 target_size_mb = atoi(arg.substr(first_colon + 1, last_colon - first_colon - 1).c_str());
113 arg.erase(0, last_colon + 1);
114 }
115 }
116
createDatabaseImpl(bool read_only) const117 OWNED FrequencyDBImpl *DatabaseConfig::createDatabaseImpl(bool read_only) const
118 {
119 string private_filename(m_privateFilename);
120 Ptr<FrequencyDBImpl> private_db(createDatabaseImplUsingBestFit(m_databaseType, private_filename));
121
122 Ptr<FrequencyDBImpl> db;
123 if (m_sharedFilename.length() == 0) {
124 db.set(private_db.release());
125 } else {
126 string shared_filename(m_sharedFilename);
127 Ptr<FrequencyDBImpl> shared_db(createDatabaseImplUsingBestFit("", shared_filename));
128 db.set(new FrequencyDBImpl_dual(shared_db.release(), private_db.release(), shared_filename));
129 }
130 if (m_maxCacheTerms > 0) {
131 db.set(new FrequencyDBImpl_cache(db.release(), m_maxCacheTerms));
132 }
133 return db->open(private_filename, read_only, FrequencyDBImpl::PRIVATE_DB_MODE) ? db.release() : 0;
134 }
135
createDatabaseImplUsingBestFit(const string & type,string & filename) const136 OWNED FrequencyDBImpl *DatabaseConfig::createDatabaseImplUsingBestFit(const string &type,
137 string &filename) const
138 {
139 Ptr<FrequencyDBImpl> db;
140
141 if (db.isNull() && type.length() > 0) {
142 db.set(createDatabaseImplForType(type, filename));
143 }
144
145 if (db.isNull()) {
146 db.set(createDatabaseImplUsingExistingFile(filename));
147 }
148
149 if (db.isNull()) {
150 db.set(createDatabaseImplUsingDefaultType(filename));
151 }
152
153 if (db.isNull()) {
154 throw runtime_error(string("no database type known for filename: ") + filename);
155 }
156
157 return db.release();
158 }
159
createDatabaseImplForType(const string & type,string & filename) const160 OWNED FrequencyDBImpl *DatabaseConfig::createDatabaseImplForType(const string &type,
161 string &filename) const
162 {
163 File search_file(filename);
164
165 for (DBType *dbt = DBTYPES; dbt->factory; ++dbt) {
166 if (dbt->prefix == type) {
167 if (dbt->suffix) {
168 search_file.setSuffix(dbt->suffix);
169 }
170 if (is_debug) {
171 cerr << "USING REQUESTED DATABASE TYPE '" << dbt->prefix << "' PATH " << search_file.getPath() << endl;
172 }
173 filename = search_file.getPath();
174 return (dbt->factory)(this);
175 }
176 }
177
178 return 0;
179 }
180
createDatabaseImplUsingDefaultType(string & filename) const181 OWNED FrequencyDBImpl *DatabaseConfig::createDatabaseImplUsingDefaultType(string &filename) const
182 {
183 return createDatabaseImplForType("", filename);
184 }
185
createDatabaseImplUsingExistingFile(string & filename) const186 OWNED FrequencyDBImpl *DatabaseConfig::createDatabaseImplUsingExistingFile(string &filename) const
187 {
188 File search_file(filename);
189
190 for (DBType *dbt = DBTYPES; dbt->factory; ++dbt) {
191 if (dbt->suffix) {
192 search_file.setSuffix(dbt->suffix);
193 if (search_file.isFile()) {
194 if (is_debug) {
195 cerr << "USING DISCOVERED DATABASE TYPE '" << dbt->prefix << "' PATH " << search_file.getPath() << endl;
196 }
197 filename = search_file.getPath();
198 return (dbt->factory)(this);
199 }
200 }
201 }
202
203 return 0;
204 }
205