OSDN Git Service

680a51485a81df1e91cf6c254648e4d96d66a019
[winmerge-jp/winmerge-jp.git] / Src / SubstitutionFiltersList.cpp
1 /** 
2  * @file  SubstitutionFiltersList.cpp
3  *
4  * @brief Implementation for SubstitutionFiltersList class.
5  */
6
7 #include "pch.h"
8 #include "SubstitutionFiltersList.h"
9 #include "SubstitutionList.h"
10 #include <vector>
11 #include <cassert>
12 #include <Poco/Exception.h>
13 #include "OptionsMgr.h"
14 #include "OptionsDef.h"
15 #include "UnicodeString.h"
16
17 /** @brief Registry key for saving Substitution filters. */
18 static const tchar_t SubstitutionFiltersRegPath[] = _T("SubstitutionFilters");
19
20 /**
21  * @brief Default constructor.
22  */
23 SubstitutionFiltersList::SubstitutionFiltersList()
24 : m_pOptionsMgr(nullptr)
25 {
26 }
27
28 /**
29  * @brief Destructor, empties the list.
30  */
31 SubstitutionFiltersList::~SubstitutionFiltersList() = default;
32
33 /**
34  * @brief Add new filter to the list.
35  * @param [in] filter Filter string to add.
36  * @param [in] enabled Is filter enabled?
37  */
38 void SubstitutionFiltersList::Add(const String& filter0, const String& filter1,
39         bool useRegExp, bool caseSensitive, bool matchWholeWordOnly, bool enabled)
40 {
41         SubstitutionFilter item;
42         item.useRegExp = useRegExp;
43         item.caseSensitive = caseSensitive;
44         item.matchWholeWordOnly = matchWholeWordOnly;
45         item.pattern = filter0;
46         item.replacement = filter1;
47         item.enabled = enabled;
48         m_items.emplace_back(item);
49 }
50
51 /**
52  * @brief Return filter from given index.
53  * @param [in] ind Index of filter.
54  * @return Filter item from the index. If the index is beyond table limit,
55  *  return the last item in the list.
56  */
57 const SubstitutionFilter& SubstitutionFiltersList::GetAt(size_t ind) const
58 {
59         if (ind < m_items.size())
60                 return m_items[ind];
61         else
62                 return m_items.back();
63 }
64
65 /**
66  * @brief Clone filter list from another list.
67  * This function clones filter list from another list. Current items in the
68  * list are removed and new items added from the given list.
69  * @param [in] list List to clone.
70  */
71 void SubstitutionFiltersList::CloneFrom(const SubstitutionFiltersList *list)
72 {
73         Empty();
74         size_t count = list->GetCount();
75         m_enabled = list->m_enabled;
76         for (size_t i = 0; i < count; i++)
77         {
78                 const SubstitutionFilter &item = list->GetAt(i);
79                 Add(item.pattern, item.replacement, item.useRegExp,
80                         item.caseSensitive, item.matchWholeWordOnly, item.enabled);
81         }
82 }
83
84 /**
85  * @brief Compare filter lists.
86  * @param [in] list List to compare.
87  * @return true if lists are identical, false otherwise.
88  */
89 bool SubstitutionFiltersList::Compare(const SubstitutionFiltersList *list) const
90 {
91         if (list->GetCount() != GetCount())
92                 return false;
93         if (list->GetEnabled() != GetEnabled())
94                 return false;
95
96         for (size_t i = 0; i < GetCount(); i++)
97         {
98                 const SubstitutionFilter &item1 = list->GetAt(i);
99                 const SubstitutionFilter &item2 = GetAt(i);
100
101                 if
102                 (
103                            item1.enabled != item2.enabled
104                         || item1.useRegExp != item2.useRegExp
105                         || item1.caseSensitive != item2.caseSensitive
106                         || item1.matchWholeWordOnly != item2.matchWholeWordOnly
107                         || item1.pattern != item2.pattern
108                         || item1.replacement != item2.replacement
109                 )
110                         return false;
111         }
112         return true;
113 }
114
115 /**
116  * @brief Read filter list from the options system.
117  * @param [in] pOptionsMgr Pointer to options system.
118  */
119 void SubstitutionFiltersList::Initialize(COptionsMgr *pOptionsMgr)
120 {
121         assert(pOptionsMgr != nullptr);
122         String valuename(SubstitutionFiltersRegPath);
123
124         m_pOptionsMgr = pOptionsMgr;
125
126         m_enabled = m_pOptionsMgr->GetBool(OPT_SUBSTITUTION_FILTERS_ENABLED);
127
128         Empty();
129
130         valuename += _T("/Values");
131         m_pOptionsMgr->InitOption(valuename, 0);
132         size_t count = m_pOptionsMgr->GetInt(valuename);
133
134         for (unsigned i = 0; i < count; i++)
135         {
136                 String nameEnabled = strutils::format(_T("%s/Enabled%02u"), SubstitutionFiltersRegPath, i);
137                 m_pOptionsMgr->InitOption(nameEnabled, true);
138                 bool enabled = m_pOptionsMgr->GetBool(nameEnabled);
139
140                 String nameUseRegExp = strutils::format(_T("%s/UseRegExp%02u"), SubstitutionFiltersRegPath, i);
141                 m_pOptionsMgr->InitOption(nameUseRegExp, false);
142                 bool useRegExp = m_pOptionsMgr->GetBool(nameUseRegExp);
143
144                 String nameCaseSensitive = strutils::format(_T("%s/CaseSensitive%02u"), SubstitutionFiltersRegPath, i);
145                 m_pOptionsMgr->InitOption(nameCaseSensitive, false);
146                 bool caseSensitive = m_pOptionsMgr->GetBool(nameCaseSensitive);
147
148                 String nameMatchWholeWordOnly = strutils::format(_T("%s/MatchWholeWordOnly%02u"), SubstitutionFiltersRegPath, i);
149                 m_pOptionsMgr->InitOption(nameMatchWholeWordOnly, false);
150                 bool matchWholeWordOnly = m_pOptionsMgr->GetBool(nameMatchWholeWordOnly);
151
152                 String name0 = strutils::format(_T("%s/Pattern%02u"), SubstitutionFiltersRegPath, i);
153                 m_pOptionsMgr->InitOption(name0, _T(""));
154                 String pattern = m_pOptionsMgr->GetString(name0);
155
156                 String name1 = strutils::format(_T("%s/Replacement%02u"), SubstitutionFiltersRegPath, i);
157                 m_pOptionsMgr->InitOption(name1, _T(""));
158                 String replacement = m_pOptionsMgr->GetString(name1);
159
160                 Add(pattern, replacement, useRegExp, caseSensitive, matchWholeWordOnly, enabled);
161         }
162 }
163
164 /**
165  * @brief Save Substitution Filters to options system.
166  */
167 void SubstitutionFiltersList::SaveFilters()
168 {
169         assert(m_pOptionsMgr != nullptr);
170         String valuename(SubstitutionFiltersRegPath);
171
172         m_pOptionsMgr->SaveOption(OPT_SUBSTITUTION_FILTERS_ENABLED, m_enabled);
173
174         size_t count = m_items.size();
175         valuename += _T("/Values");
176         m_pOptionsMgr->SaveOption(valuename, static_cast<int>(count));
177
178         for (size_t i = 0; i < count; i++)
179         {
180                 const SubstitutionFilter& item = m_items[i];
181
182                 String nameEnabled = strutils::format(_T("%s/Enabled%02u"), SubstitutionFiltersRegPath, i);
183                 m_pOptionsMgr->InitOption(nameEnabled, true);
184                 m_pOptionsMgr->SaveOption(nameEnabled, item.enabled);
185
186                 String nameUseRegExp = strutils::format(_T("%s/UseRegExp%02u"), SubstitutionFiltersRegPath, i);
187                 m_pOptionsMgr->InitOption(nameUseRegExp, false);
188                 m_pOptionsMgr->SaveOption(nameUseRegExp, item.useRegExp);
189
190                 String nameCaseSensitive = strutils::format(_T("%s/CaseSensitive%02u"), SubstitutionFiltersRegPath, i);
191                 m_pOptionsMgr->InitOption(nameCaseSensitive, false);
192                 m_pOptionsMgr->SaveOption(nameCaseSensitive, item.caseSensitive);
193
194                 String nameMatchWholeWordOnly = strutils::format(_T("%s/MatchWholeWordOnly%02u"), SubstitutionFiltersRegPath, i);
195                 m_pOptionsMgr->InitOption(nameMatchWholeWordOnly, false);
196                 m_pOptionsMgr->SaveOption(nameMatchWholeWordOnly, item.matchWholeWordOnly);
197
198                 String name0 = strutils::format(_T("%s/Pattern%02u"), SubstitutionFiltersRegPath, i);
199                 m_pOptionsMgr->InitOption(name0, _T(""));
200                 m_pOptionsMgr->SaveOption(name0, item.pattern);
201
202                 String name1 = strutils::format(_T("%s/Replacement%02u"), SubstitutionFiltersRegPath, i);
203                 m_pOptionsMgr->InitOption(name1, _T(""));
204                 m_pOptionsMgr->SaveOption(name1, item.replacement);
205         }
206
207         // Remove options we don't need anymore
208         // We could have earlier 10 pcs but now we only need 5
209         String filterEnabled = strutils::format(_T("%s/Enabled%02u"), SubstitutionFiltersRegPath, count);
210         int retvalEnabled = m_pOptionsMgr->RemoveOption(filterEnabled);
211
212         String filterUseRegExp = strutils::format(_T("%s/UseRegExp%02u"), SubstitutionFiltersRegPath, count);
213         int retvalUseRegExp = m_pOptionsMgr->RemoveOption(filterUseRegExp);
214
215         String filterCaseSensitive = strutils::format(_T("%s/CaseSensitive%02u"), SubstitutionFiltersRegPath, count);
216         int retvalCaseSensitive = m_pOptionsMgr->RemoveOption(filterCaseSensitive);
217
218         String filterMatchWholeWordOnly = strutils::format(_T("%s/MatchWholeWordOnly%02u"), SubstitutionFiltersRegPath, count);
219         int retvalMatchWholeWordOnly = m_pOptionsMgr->RemoveOption(filterMatchWholeWordOnly);
220
221         String filter0 = strutils::format(_T("%s/Pattern%02u"), SubstitutionFiltersRegPath, count);
222         int retval0 = m_pOptionsMgr->RemoveOption(filter0);
223
224         String filter1 = strutils::format(_T("%s/Replacement%02u"), SubstitutionFiltersRegPath, count);
225         int retval1 = m_pOptionsMgr->RemoveOption(filter1);
226
227         while (retvalEnabled == COption::OPT_OK || retvalUseRegExp == COption::OPT_OK ||
228                 retvalCaseSensitive == COption::OPT_OK || retvalMatchWholeWordOnly == COption::OPT_OK ||
229                 retval0 == COption::OPT_OK || retval1 == COption::OPT_OK)
230         {
231                 ++count;
232                 filterEnabled = strutils::format(_T("%s/Enabled%02u"), SubstitutionFiltersRegPath, count);
233                 retvalEnabled = m_pOptionsMgr->RemoveOption(filterEnabled);
234                 filterUseRegExp = strutils::format(_T("%s/UseRegExp%02u"), SubstitutionFiltersRegPath, count);
235                 retvalUseRegExp = m_pOptionsMgr->RemoveOption(filterUseRegExp);
236                 filterCaseSensitive = strutils::format(_T("%s/CaseSensitive%02u"), SubstitutionFiltersRegPath, count);
237                 retvalCaseSensitive = m_pOptionsMgr->RemoveOption(filterCaseSensitive);
238                 filterMatchWholeWordOnly = strutils::format(_T("%s/MatchWholeWordOnly%02u"), SubstitutionFiltersRegPath, count);
239                 retvalMatchWholeWordOnly = m_pOptionsMgr->RemoveOption(filterMatchWholeWordOnly);
240                 filter0 = strutils::format(_T("%s/Pattern%02u"), SubstitutionFiltersRegPath, count);
241                 retval0 = m_pOptionsMgr->RemoveOption(filter0);
242                 filter1 = strutils::format(_T("%s/Replacement%02u"), SubstitutionFiltersRegPath, count);
243                 retval1 = m_pOptionsMgr->RemoveOption(filter1);
244         }
245 }
246
247 std::shared_ptr<SubstitutionList> SubstitutionFiltersList::MakeSubstitutionList(bool throwIfInvalid)
248 {
249         int i = 0;
250         std::shared_ptr<SubstitutionList> plist(new SubstitutionList);
251         for (auto& item : m_items)
252         {
253                 if (item.enabled && !item.pattern.empty())
254                 {
255                         try
256                         {
257                                 if (item.useRegExp)
258                                 {
259                                         plist->Add(
260                                                 ucr::toUTF8(item.pattern),
261                                                 ucr::toUTF8(item.replacement),
262                                                 (item.caseSensitive ? 0 : Poco::RegularExpression::RE_CASELESS) |
263                                                  Poco::RegularExpression::RE_MULTILINE);
264                                 }
265                                 else
266                                 {
267                                         plist->Add(
268                                                 ucr::toUTF8(item.pattern),
269                                                 ucr::toUTF8(item.replacement),
270                                                 item.caseSensitive, item.matchWholeWordOnly);
271                                 }
272                         }
273                         catch (const Poco::RegularExpressionException& e)
274                         {
275                                 if (throwIfInvalid)
276                                 {
277                                         plist.reset();
278                                         const String msg = strutils::format(_T("#%d: %s"), i + 1, e.message().c_str());
279                                         throw Poco::RegularExpressionException(ucr::toUTF8(msg).c_str(), e.code());
280                                 }
281                         }
282                 }
283                 i++;
284         }
285         return plist;
286 }