OSDN Git Service

Fix sf.net ticket #2221: Possible Bugs in 2.16.2 BUG 01
[winmerge-jp/winmerge-jp.git] / Src / ProjectFile.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 //    License (GPLv2+):
3 //    This program is free software; you can redistribute it and/or modify
4 //    it under the terms of the GNU General Public License as published by
5 //    the Free Software Foundation; either version 2 of the License, or (at
6 //    your option) any later version.
7 //    
8 //    This program is distributed in the hope that it will be useful, but
9 //    WITHOUT ANY WARRANTY; without even the implied warranty of
10 //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 //    GNU General Public License for more details.
12 //
13 //    You should have received a copy of the GNU General Public License
14 //    along with this program; if not, write to the Free Software
15 //    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16 /////////////////////////////////////////////////////////////////////////////
17 /** 
18  * @file  ProjectFile.cpp
19  *
20  * @brief Implementation file for ProjectFile class.
21  */
22
23 #include "pch.h"
24 #include "ProjectFile.h"
25 #include <stack>
26 #include <string>
27 #include <Poco/FileStream.h>
28 #include <Poco/XML/XMLWriter.h>
29 #include <Poco/SAX/SAXParser.h>
30 #include <Poco/SAX/ContentHandler.h>
31 #include <Poco/Exception.h>
32 #include "UnicodeString.h"
33 #include "unicoder.h"
34
35 using Poco::FileStream;
36 using Poco::XML::SAXParser;
37 using Poco::XML::ContentHandler;
38 using Poco::XML::Locator;
39 using Poco::XML::XMLWriter;
40 using Poco::XML::XMLChar;
41 using Poco::XML::XMLString;
42 using Poco::XML::Attributes;
43 using Poco::Exception;
44 using ucr::toTString;
45 using ucr::toUTF8;
46
47 // Constants for xml element names
48 const char Root_element_name[] = "project";
49 const char Paths_element_name[] = "paths";
50 const char Left_element_name[] = "left";
51 const char Middle_element_name[] = "middle";
52 const char Right_element_name[] = "right";
53 const char Filter_element_name[] = "filter";
54 const char Subfolders_element_name[] = "subfolders";
55 const char Left_ro_element_name[] = "left-readonly";
56 const char Middle_ro_element_name[] = "middle-readonly";
57 const char Right_ro_element_name[] = "right-readonly";
58
59 namespace
60 {
61
62 String xmlch2tstr(const XMLChar *ch, int length)
63 {
64         return toTString(std::string(ch, length));
65 }
66
67 void writeElement(XMLWriter& writer, const std::string& tagname, const std::string& characters)
68 {
69         writer.startElement("", "", tagname);
70         writer.characters(characters);
71         writer.endElement("", "", tagname);
72 }
73
74 }
75
76 class ProjectFileHandler: public ContentHandler
77 {
78 public:
79         explicit ProjectFileHandler(ProjectFile *pProject) : m_pProject(pProject) {}
80
81         void setDocumentLocator(const Locator* loc) {}
82         void startDocument() {}
83         void endDocument() {}
84         void startElement(const XMLString& uri, const XMLString& localName, const XMLString& qname, const Attributes& attributes)
85         {
86                 m_stack.push(localName);
87         }
88         void endElement(const XMLString& uri, const XMLString& localName, const XMLString& qname)
89         {
90                 m_stack.pop();
91         }
92         void characters(const XMLChar ch[], int start, int length)
93         {
94                 if (m_stack.size() != 3)
95                         return;
96
97                 const std::string& nodename = m_stack.top();
98                 if (nodename == Left_element_name)
99                 {
100                         m_pProject->m_paths.SetLeft(m_pProject->m_paths.GetLeft() + xmlch2tstr(ch + start, length), false);
101                         m_pProject->m_bHasLeft = true;
102                 }
103                 else if (nodename == Middle_element_name)
104                 {
105                         m_pProject->m_paths.SetMiddle(m_pProject->m_paths.GetMiddle() + xmlch2tstr(ch + start, length), false);
106                         m_pProject->m_bHasMiddle = true;
107                 }
108                 else if (nodename == Right_element_name)
109                 {
110                         m_pProject->m_paths.SetRight(m_pProject->m_paths.GetRight() + xmlch2tstr(ch + start, length), false);
111                         m_pProject->m_bHasRight = true;
112                 }
113                 else if (nodename == Filter_element_name)
114                 {
115                         m_pProject->m_filter += xmlch2tstr(ch + start, length);
116                         m_pProject->m_bHasFilter = true;
117                 }
118                 else if (nodename == Subfolders_element_name)
119                 {
120                         m_pProject->m_subfolders = atoi(std::string(ch + start, length).c_str());
121                         m_pProject->m_bHasSubfolders = true;
122                 }
123                 else if (nodename == Left_ro_element_name)
124                 {
125                         m_pProject->m_bLeftReadOnly = atoi(std::string(ch + start, length).c_str()) != 0;
126                 }
127                 else if (nodename == Middle_ro_element_name)
128                 {
129                         m_pProject->m_bMiddleReadOnly = atoi(std::string(ch +  start, length).c_str()) != 0;
130                 }
131                 else if (nodename == Right_ro_element_name)
132                 {
133                         m_pProject->m_bRightReadOnly = atoi(std::string(ch + start, length).c_str()) != 0;
134                 }
135         }
136         void ignorableWhitespace(const XMLChar ch[], int start, int length)     {}
137         void processingInstruction(const XMLString& target, const XMLString& data) {}
138         void startPrefixMapping(const XMLString& prefix, const XMLString& uri) {}
139         void endPrefixMapping(const XMLString& prefix) {}
140         void skippedEntity(const XMLString& name) {}
141
142 private:
143         ProjectFile *m_pProject;
144         std::stack<std::string> m_stack;
145 };
146
147 /** @brief File extension for path files */
148 const String ProjectFile::PROJECTFILE_EXT = toTString("WinMerge");
149
150 /** 
151  * @brief Standard constructor.
152  */
153  ProjectFile::ProjectFile()
154 : m_bHasLeft(false)
155 , m_bHasMiddle(false)
156 , m_bHasRight(false)
157 , m_bHasFilter(false)
158 , m_bHasSubfolders(false)
159 , m_subfolders(-1)
160 , m_bLeftReadOnly(false)
161 , m_bMiddleReadOnly(false)
162 , m_bRightReadOnly(false)
163 {
164 }
165
166 /** 
167  * @brief Open given path-file and read data from it to member variables.
168  * @param [in] path Path to project file.
169  * @param [out] sError Error string if error happened.
170  * @return true if reading succeeded, false if error happened.
171  */
172 bool ProjectFile::Read(const String& path)
173 {
174         ProjectFileHandler handler(this);
175         SAXParser parser;
176         parser.setContentHandler(&handler);
177         parser.parse(toUTF8(path));
178         return true;
179 }
180
181 /** 
182  * @brief Save data from member variables to path-file.
183  * @param [in] path Path to project file.
184  * @param [out] sError Error string if error happened.
185  * @return true if saving succeeded, false if error happened.
186  */
187 bool ProjectFile::Save(const String& path) const
188 {
189         FileStream out(toUTF8(path), FileStream::trunc);
190         XMLWriter writer(out, XMLWriter::WRITE_XML_DECLARATION | XMLWriter::PRETTY_PRINT);
191         writer.startDocument();
192         writer.startElement("", "", Root_element_name);
193         {
194                 writer.startElement("", "", Paths_element_name);
195                 {
196                         if (!m_paths.GetLeft().empty())
197                                 writeElement(writer, Left_element_name, toUTF8(m_paths.GetLeft()));
198                         if (!m_paths.GetMiddle().empty())
199                                 writeElement(writer, Middle_element_name, toUTF8(m_paths.GetMiddle()));
200                         if (!m_paths.GetRight().empty())
201                                 writeElement(writer, Right_element_name, toUTF8(m_paths.GetRight()));
202                         if (!m_filter.empty())
203                                 writeElement(writer, Filter_element_name, toUTF8(m_filter));
204                         writeElement(writer, Subfolders_element_name, m_subfolders != 0 ? "1" : "0");
205                         writeElement(writer, Left_ro_element_name, m_bLeftReadOnly ? "1" : "0");
206                         if (!m_paths.GetMiddle().empty())
207                                 writeElement(writer, Middle_ro_element_name, m_bMiddleReadOnly ? "1" : "0");
208                         writeElement(writer, Right_ro_element_name, m_bRightReadOnly ? "1" : "0");
209                 }
210                 writer.endElement("", "", Paths_element_name);
211         }
212         writer.endElement("", "", Root_element_name);
213         writer.endDocument();
214         return true;
215 }
216
217 /** 
218  * @brief Returns if left path is defined in project file.
219  * @return true if project file has left path.
220  */
221 bool ProjectFile::HasLeft() const
222 {
223         return m_bHasLeft;
224 }
225
226 /** 
227  * @brief Returns if middle path is defined.
228  */
229 bool ProjectFile::HasMiddle() const
230 {
231         return m_bHasMiddle;
232 }
233
234 /** 
235  * @brief Returns if right path is defined in project file.
236  * @return true if project file has right path.
237  */
238 bool ProjectFile::HasRight() const
239 {
240         return m_bHasRight;
241 }
242
243 /** 
244  * @brief Returns if filter is defined in project file.
245  * @return true if project file has filter.
246  */
247 bool ProjectFile::HasFilter() const
248 {
249         return m_bHasFilter;
250 }
251
252 /** 
253  * @brief Returns if subfolder is defined in projectfile.
254  * @return true if project file has subfolder definition.
255  */
256 bool ProjectFile::HasSubfolders() const
257 {
258         return m_bHasSubfolders;
259 }
260
261 /** 
262  * @brief Returns left path.
263  * @param [out] pReadOnly true if readonly was specified for path.
264  * @return Left path.
265  */
266 String ProjectFile::GetLeft(bool * pReadOnly /*= nullptr*/) const
267 {
268         if (pReadOnly != nullptr)
269                 *pReadOnly = m_bLeftReadOnly;
270         return m_paths.GetLeft();
271 }
272
273 /** 
274  * @brief Returns if left path is specified read-only.
275  * @return true if left path is read-only, false otherwise.
276  */
277 bool ProjectFile::GetLeftReadOnly() const
278 {
279         return m_bLeftReadOnly;
280 }
281
282 /** 
283  * @brief Set left path, returns old left path.
284  * @param [in] sLeft Left path.
285  * @param [in] bReadOnly Will path be recorded read-only?
286  */
287 void ProjectFile::SetLeft(const String& sLeft, const bool * pReadOnly /*= nullptr*/)
288 {
289         m_paths.SetLeft(sLeft, false);
290         if (pReadOnly != nullptr)
291                 m_bLeftReadOnly = *pReadOnly;
292 }
293
294 /** 
295  * @brief Returns middle path.
296  * @param [out] pReadOnly true if readonly was specified for path.
297  */
298 String ProjectFile::GetMiddle(bool * pReadOnly /*= nullptr*/) const
299 {
300         if (pReadOnly != nullptr)
301                 *pReadOnly = m_bMiddleReadOnly;
302         return m_paths.GetMiddle();
303 }
304
305 /** 
306  * @brief Returns if middle path is specified read-only.
307  */
308 bool ProjectFile::GetMiddleReadOnly() const
309 {
310         return m_bMiddleReadOnly;
311 }
312
313 /** 
314  * @brief Set middle path, returns old middle path.
315  * @param [in] sMiddle Middle path.
316  * @param [in] bReadOnly Will path be recorded read-only?
317  */
318 void ProjectFile::SetMiddle(const String& sMiddle, const bool * pReadOnly /*= nullptr*/)
319 {
320         m_paths.SetMiddle(sMiddle, false);
321         if (pReadOnly != nullptr)
322                 m_bMiddleReadOnly = *pReadOnly;
323
324         return;
325 }
326
327 /** 
328  * @brief Returns right path.
329  * @param [out] pReadOnly true if readonly was specified for path.
330  * @return Right path.
331  */
332 String ProjectFile::GetRight(bool * pReadOnly /*= nullptr*/) const
333 {
334         if (pReadOnly != nullptr)
335                 *pReadOnly = m_bRightReadOnly;
336         return m_paths.GetRight();
337 }
338
339 /** 
340  * @brief Returns if right path is specified read-only.
341  * @return true if right path is read-only, false otherwise.
342  */
343 bool ProjectFile::GetRightReadOnly() const
344 {
345         return m_bRightReadOnly;
346 }
347
348 /** 
349  * @brief Set right path, returns old right path.
350  * @param [in] sRight Right path.
351  * @param [in] bReadOnly Will path be recorded read-only?
352  */
353 void ProjectFile::SetRight(const String& sRight, const bool * pReadOnly /*= nullptr*/)
354 {
355         m_paths.SetRight(sRight, false);
356         if (pReadOnly != nullptr)
357                 m_bRightReadOnly = *pReadOnly;
358 }
359
360 /** 
361  * @brief Returns filter.
362  * @return Filter string.
363  */
364 String ProjectFile::GetFilter() const
365 {
366         return m_filter;
367 }
368
369 /** 
370  * @brief Set filter.
371  * @param [in] sFilter New filter string to set.
372  */
373 void ProjectFile::SetFilter(const String& sFilter)
374 {
375         m_filter = sFilter;
376 }
377
378 /** 
379  * @brief Returns subfolder included -setting.
380  * @return != 0 if subfolders are included.
381  */
382 int ProjectFile::GetSubfolders() const
383 {
384         return m_subfolders;
385 }
386
387 /** 
388  * @brief set subfolder.
389  * @param [in] iSubfolder New value for subfolder inclusion.
390  */
391 void ProjectFile::SetSubfolders(bool bSubfolder)
392 {
393         m_subfolders = bSubfolder ? 1 : 0;
394 }
395
396 /** 
397  * @brief 
398  *
399  * @param [in] files Files in project
400  * @param [in] bSubFolders If true subfolders included (recursive compare)
401  */
402 void ProjectFile::SetPaths(const PathContext& files, bool bSubfolders)
403 {
404         m_paths = files;
405         m_subfolders = bSubfolders;
406 }
407
408 /** 
409  * @brief Returns left and right paths and recursive from project file
410  * 
411  * @param [out] files Files in project
412  * @param [out] bSubFolders If true subfolders included (recursive compare)
413  */
414 void ProjectFile::GetPaths(PathContext& files, bool & bSubfolders) const
415 {
416         files = m_paths;
417         if (HasSubfolders())
418                 bSubfolders = (GetSubfolders() == 1);
419 }