OSDN Git Service

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