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