]> git.lyx.org Git - features.git/blob - src/LayoutModuleList.cpp
f02c784a118d49e85e52679fdf36b16752ac6c8e
[features.git] / src / LayoutModuleList.cpp
1 // -*- C++ -*-
2 /**
3  * \file ModuleList.cpp
4  * This file is part of LyX, the document processor.
5  * Licence details can be found in the file COPYING.
6  *
7  * \author Richard Heck
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 #include <config.h>
13
14 #include "LayoutModuleList.h"
15
16 #include "LayoutFile.h"
17 #include "ModuleList.h"
18
19 #include "support/debug.h"
20
21 #include <string>
22 #include <vector>
23
24 using namespace std;
25
26 namespace lyx {
27
28
29 // the previous document class may have loaded some modules that the
30 // new one excludes, and the new class may provide, etc, some that
31 // conflict with ones that were already loaded. So we need to go 
32 // through the list and fix everything. I suppose there are various
33 // ways this could be done, but the following seems to work at the 
34 // moment. (Thanks to Philippe Charpentier for helping work out all 
35 // the bugs---rgh.)
36 bool LayoutModuleList::adaptToBaseClass(LayoutFile const * const lay,
37                 std::list<string> removedModules)
38 {
39         // first, we remove any modules the new document class itself provides,
40         // those it excludes, and those that conflict with ones it excludes.
41         // this has to be done first because, otherwise, a module we're about
42         // to remove could prevent a default module from being added.
43         bool retval = removeBadModules(lay);
44         // next, we add any default modules the new class provides.
45         addDefaultModules(lay, removedModules);
46         // finally, we perform a general consistency check on the set of
47         // loaded modules. it's a hassle that we have to do this now, since
48         // we just went through them a bit ago, but things might have changed
49         // with the loading of the default modules.
50         retval |= checkModuleConsistency(lay);
51         return retval;
52 }
53
54
55 bool LayoutModuleList::moduleCanBeAdded(string const & modName, 
56                 LayoutFile const * const lay) const
57 {
58         // Is the module already present?
59         const_iterator it = begin();
60         const_iterator const en = end();
61         for (; it != en; it++)
62                 if (*it == modName) 
63                         return false;
64
65         LyXModule const * const lm = moduleList[modName];
66         if (!lm)
67                 return true;
68
69         // Is this module explicitly excluded by the document class?
70         const_iterator const exclmodstart = lay->excludedModules().begin();
71         const_iterator const exclmodend = lay->excludedModules().end();
72         if (find(exclmodstart, exclmodend, modName) != exclmodend)
73                 return false;
74
75         // Is this module already provided by the document class?
76         const_iterator const provmodstart = lay->providedModules().begin();
77         const_iterator const provmodend = lay->providedModules().end();
78         if (find(provmodstart, provmodend, modName) != provmodend)
79                 return false;
80
81         // Check for conflicts with used modules
82         // first the provided modules...
83         const_iterator provmodit = provmodstart;
84         for (; provmodit != provmodend; ++provmodit) {
85                 if (!LyXModule::areCompatible(modName, *provmodit))
86                         return false;
87         }
88         // and then the selected modules
89         const_iterator mit = begin();
90         const_iterator const men = end();
91         for (; mit != men; ++mit)
92                 if (!LyXModule::areCompatible(modName, *mit))
93                         return false;
94
95         // Check whether some required module is available
96         vector<string> const reqs = lm->getRequiredModules();
97         if (reqs.empty())
98                 return true;
99
100         mit = begin(); // reset
101         vector<string>::const_iterator rit = reqs.begin();
102         vector<string>::const_iterator ren = reqs.end();
103         bool foundone = false;
104         for (; rit != ren; ++rit) {
105                 if (find(mit, men, *rit) != men || 
106                     find(provmodstart, provmodend, *rit) != provmodend) {
107                         foundone = true;
108                         break;
109                 }
110         }
111
112         return foundone;
113 }
114
115
116 void LayoutModuleList::addDefaultModules(LayoutFile const * const lay,
117                 std::list<string> removedModules)
118 {
119         LayoutModuleList mods = lay->defaultModules();
120         const_iterator mit = mods.begin();
121         const_iterator const men = mods.end();
122
123         // We want to insert the default modules at the beginning of
124         // the list, but also to insert them in the correct order.
125         // The obvious thing to do would be to collect them and then
126         // insert them, but that doesn't work because a later default
127         // module may require an earlier one, and then the test below
128         //     moduleCanBeAdded(modname)
129         // will fail. So we have to do it a more complicated way.
130         iterator insertpos = begin();
131         int numinserts = 0;
132
133         for (; mit != men; mit++) {
134                 string const & modName = *mit;
135                 // make sure the user hasn't removed it
136                 if (find(removedModules.begin(), removedModules.end(), modName) !=
137                     removedModules.end()) {
138                         LYXERR(Debug::TCLASS, "Default module `" << modName << 
139                                         "' not added because removed by user.");
140                         continue;
141                 }
142
143                 if (!moduleCanBeAdded(modName, lay)) {
144                         // FIXME This could be because it's already present, so we should
145                         // probably return something indicating that.
146                         LYXERR(Debug::TCLASS, "Default module `" << modName << 
147                                         "' could not be added.");
148                         continue;
149                 }
150                 LYXERR(Debug::TCLASS, "Default module `" << modName << "' added.");
151                 insert(insertpos, modName);
152                 // now we reset insertpos
153                 ++numinserts;
154                 insertpos = begin();
155                 advance(insertpos, numinserts);
156         }
157 }
158
159
160 bool LayoutModuleList::removeBadModules(LayoutFile const * const lay)
161 {
162         // we'll write a new list of modules, since we can't just remove them,
163         // as that would invalidate our iterators
164         LayoutModuleList oldModules = *this;
165         clear();
166
167         LayoutModuleList const & provmods = lay->providedModules();
168         LayoutModuleList const & exclmods = lay->excludedModules();
169         bool consistent = true; // set to false if we have to do anything
170
171         const_iterator oit = oldModules.begin();
172         const_iterator const oen = oldModules.end();
173         for (; oit != oen; ++oit) {
174                 string const & modname = *oit;
175                 // skip modules that the class provides
176                 if (find(provmods.begin(), provmods.end(), modname) != provmods.end()) {
177                         LYXERR0("Module `" << modname << "' dropped because provided by document class.");
178                         consistent = false;
179                         continue;
180                 }
181                 // are we excluded by the document class?
182                 if (find(exclmods.begin(), exclmods.end(), modname) != exclmods.end()) {
183                         LYXERR0("Module `" << modname << "' dropped because excluded by document class.");
184                         consistent = false;
185                         continue;
186                 }
187                 // determine whether some provided module excludes us or we exclude it
188                 const_iterator pit = provmods.begin();
189                 const_iterator const pen = provmods.end();
190                 bool excluded = false;
191                 for (; !excluded && pit != pen; ++pit) {
192                         if (!LyXModule::areCompatible(modname, *pit)) {
193                                 LYXERR0("Module " << modname << 
194                                                 " dropped becuase it conflicts with provided module `" << *pit << "'.");
195                                 consistent = false;
196                                 excluded = true;
197                         }
198                 }
199                 if (excluded)
200                         continue;
201                 push_back(modname);
202         }
203         return consistent;
204 }
205
206
207 // Perform a consistency check on the set of modules. We need to make
208 // sure that none of the modules exclude each other and that requires
209 // are satisfied.
210 bool LayoutModuleList::checkModuleConsistency(LayoutFile const * const lay) {
211         bool consistent = true;
212         LayoutModuleList oldModules = *this;
213         clear();
214         const_iterator oit = oldModules.begin();
215         const_iterator const oen = oldModules.end();
216         LayoutModuleList const & provmods = lay->providedModules();
217         for (; oit != oen; ++oit) {
218                 string const & modname = *oit;
219                 bool excluded = false;
220                 // Determine whether some prior module excludes us, or we exclude it
221                 const_iterator lit = begin();
222                 const_iterator const len = end();
223                 for (; !excluded && lit != len; ++lit) {
224                         if (!LyXModule::areCompatible(modname, *lit)) {
225                                 consistent = false;
226                                 LYXERR0("Module " << modname << 
227                                                 " dropped because it is excluded by prior module " << *lit);
228                                 excluded = true;
229                         }
230                 }
231
232                 if (excluded)
233                         continue;
234
235                 // determine whether some provided module or some prior module
236                 // satisfies our requirements
237                 LyXModule const * const oldmod = moduleList[modname];
238                 if (!oldmod) {
239                         LYXERR0("Default module " << modname << 
240                                         " added although it is unavailable and can't check requirements.");
241                         continue;
242                 }
243                         
244                 vector<string> const & reqs = oldmod->getRequiredModules();
245                 if (!reqs.empty()) {
246                         // we now set excluded to true, meaning that we haven't
247                         // yet found a required module.
248                         excluded = true;
249                         vector<string>::const_iterator rit  = reqs.begin();
250                         vector<string>::const_iterator const ren = reqs.end();
251                         for (; rit != ren; ++rit) {
252                                 string const reqmod = *rit;
253                                 if (find(provmods.begin(), provmods.end(), reqmod) != 
254                                                 provmods.end()) {
255                                         excluded = false;
256                                         break;
257                                 }
258                                 if (find(begin(), end(), reqmod) != end()) {
259                                         excluded = false;
260                                         break;
261                                 }
262                         }
263                 }
264                 if (excluded) {
265                         consistent = false;
266                         LYXERR0("Module " << modname << " dropped because requirements not met.");
267                 } else {
268                         LYXERR(Debug::TCLASS, "Module " << modname << " passed consistency check.");
269                         push_back(modname);
270                 }
271         }
272         return consistent;
273 }
274
275 } // namespace lyx