OSDN Git Service

Use Poco::XML library instead of SCEW and Expat libraries
[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 // ID line follows -- this is updated by CVS
23 // $Id: ProjectFile.cpp 7081 2010-01-01 20:33:30Z kimmov $
24
25 #include "ProjectFile.h"
26
27 #include <Poco/SAX/InputSource.h>
28 #include <Poco/XML/XMLWriter.h>
29 #include <Poco/DOM/AutoPtr.h>
30 #include <Poco/DOM/Document.h>
31 #include <Poco/DOM/DOMParser.h>
32 #include <Poco/DOM/DOMWriter.h>
33 #include <Poco/DOM/NodeIterator.h>
34 #include <Poco/DOM/NodeFilter.h>
35 #include <Poco/DOM/Text.h>
36 #include <Poco/Exception.h>
37 #include "UnicodeString.h"
38 #include "unicoder.h"
39
40 using Poco::AutoPtr;
41 using Poco::XML::InputSource;
42 using Poco::XML::DOMParser;
43 using Poco::XML::DOMWriter;
44 using Poco::XML::Document;
45 using Poco::XML::Element;
46 using Poco::XML::NodeFilter;
47 using Poco::XML::NodeIterator;
48 using Poco::XML::Node;
49 using Poco::XML::Text;
50 using Poco::XML::XMLWriter;
51 using ucr::UTF82T;
52 using ucr::T2UTF8;
53
54 // Constants for xml element names
55 const char Root_element_name[] = "project";
56 const char Paths_element_name[] = "paths";
57 const char Left_element_name[] = "left";
58 const char Middle_element_name[] = "middle";
59 const char Right_element_name[] = "right";
60 const char Filter_element_name[] = "filter";
61 const char Subfolders_element_name[] = "subfolders";
62 const char Left_ro_element_name[] = "left-readonly";
63 const char Middle_ro_element_name[] = "middle-readonly";
64 const char Right_ro_element_name[] = "right-readonly";
65
66
67 /** @brief File extension for path files */
68 const String ProjectFile::PROJECTFILE_EXT = UTF82T("WinMerge");
69
70 /** 
71  * @brief Standard constructor.
72  */
73  ProjectFile::ProjectFile()
74 : m_bHasLeft(false)
75 , m_bHasMiddle(false)
76 , m_bHasRight(false)
77 , m_bHasFilter(false)
78 , m_bHasSubfolders(false)
79 , m_subfolders(-1)
80 , m_bLeftReadOnly(false)
81 , m_bMiddleReadOnly(false)
82 , m_bRightReadOnly(false)
83 {
84 }
85
86 /** 
87  * @brief Open given path-file and read data from it to member variables.
88  * @param [in] path Path to project file.
89  * @param [out] sError Error string if error happened.
90  * @return true if reading succeeded, false if error happened.
91  */
92 bool ProjectFile::Read(const String& path)
93 {
94         DOMParser parser;
95         parser.setFeature(DOMParser::FEATURE_FILTER_WHITESPACE, true);
96         AutoPtr<Document> tree = parser.parse(T2UTF8(path));
97         if (!tree)
98                 return false;
99         Element* root = GetRootElement(tree.get());
100         if (!root)
101                 return false;
102         // Currently our content is paths, so expect
103         // having paths in valid project file!
104         if (!GetPathsData(root))
105                 return false;
106
107         return true;
108 }
109
110 /** 
111  * @brief Return project file XML's root element.
112  * @param [in] tree XML tree we got from the parser.
113  * @return Root element pointer.
114  */
115 Element* ProjectFile::GetRootElement(const Document * tree)
116 {
117         if (!tree)
118                 return NULL;
119         // Make sure we have correct root element
120         Element *root = dynamic_cast<Element *>(tree->firstChild());
121         if (root->nodeName() != Root_element_name)
122                 return NULL;
123         return root;
124 }
125
126 /** 
127  * @brief Reads the paths data from the XML data.
128  * This function reads the paths data inside given element in XML data.
129  * @param [in] parent Parent element for the paths data.
130  * @return true if pathdata was found from the file.
131  */
132 bool ProjectFile::GetPathsData(const Element * parent)
133 {
134         if (!parent)
135                 return false;
136         
137         Node* paths = parent->firstChild();
138         if (!paths || paths->nodeName() != Paths_element_name)
139                 return false;
140
141         NodeIterator it(paths, NodeFilter::SHOW_ELEMENT);
142         Node* pNode = it.nextNode();
143         while (pNode)
144         {
145                 std::string nodename = pNode->nodeName();
146                 if (nodename == Left_element_name)
147                 {
148                         m_paths.SetLeft(UTF82T(pNode->innerText()).c_str());
149                         m_bHasLeft = true;
150                 }
151                 else if (nodename == Middle_element_name)
152                 {
153                         m_paths.SetMiddle(UTF82T(pNode->innerText()).c_str());
154                         m_bHasMiddle = true;
155                 }
156                 else if (nodename == Right_element_name)
157                 {
158                         m_paths.SetRight(UTF82T(pNode->innerText()).c_str());
159                         m_bHasRight = true;
160                 }
161                 else if (nodename == Filter_element_name)
162                 {
163                         m_filter = UTF82T(pNode->innerText());
164                         m_bHasFilter = true;
165                 }
166                 else if (nodename == Subfolders_element_name)
167                 {
168                         m_subfolders = atoi(pNode->innerText().c_str());
169                         m_bHasSubfolders = true;
170                 }
171                 else if (nodename == Left_ro_element_name)
172                 {
173                         m_bLeftReadOnly = atoi(pNode->innerText().c_str()) != 0;
174                 }
175                 else if (nodename == Middle_ro_element_name)
176                 {
177                         m_bMiddleReadOnly = atoi(pNode->innerText().c_str()) != 0;
178                 }
179                 else if (nodename == Right_ro_element_name)
180                 {
181                         m_bRightReadOnly = atoi(pNode->innerText().c_str()) != 0;
182                 }
183                 pNode = it.nextNode();
184         }
185         return true;
186 }
187
188 /** 
189  * @brief Save data from member variables to path-file.
190  * @param [in] path Path to project file.
191  * @param [out] sError Error string if error happened.
192  * @return true if saving succeeded, false if error happened.
193  */
194 bool ProjectFile::Save(const String& path) const
195 {
196         AutoPtr<Document> doc = new Document();
197         AutoPtr<Element> root = doc->createElement(Root_element_name);
198         if (!root)
199                 return false;
200         doc->appendChild(root);
201         AutoPtr<Element> paths = AddPathsElement(root);
202         if (!paths)
203                 return false;
204         AddPathsContent(paths);
205
206         DOMWriter writer;
207         writer.setOptions(XMLWriter::WRITE_XML_DECLARATION | XMLWriter::PRETTY_PRINT);
208         writer.writeNode(T2UTF8(path), doc);
209
210         return true;
211 }
212
213 /**
214  * @brief Add paths element into XML tree.
215  * @param [in] parent Parent element for the paths element.
216  * @return pointer to added paths element.
217  */
218 Element* ProjectFile::AddPathsElement(Element * parent) const
219 {
220         Element *element = parent->ownerDocument()->createElement(Paths_element_name);
221         parent->appendChild(element);
222         return element;
223 }
224
225 static Element *createElement(const Document *doc, const std::string& tagname, const std::string& content)
226 {
227         Element *element = doc->createElement(tagname);
228         AutoPtr<Text> text = doc->createTextNode(content);
229         element->appendChild(text);
230         return element;
231 }
232
233 /**
234  * @brief Add paths data to the XML tree.
235  * This function adds our paths data to the XML tree.
236  * @param [in] parent Parent element for paths data.
237  * @return true if we succeeded, false otherwise.
238  */
239 bool ProjectFile::AddPathsContent(Element * parent) const
240 {
241         Document *doc = parent->ownerDocument();
242         AutoPtr<Element> element;
243
244         if (!m_paths.GetLeft().empty())
245         {
246                 element = createElement(doc, Left_element_name, T2UTF8(m_paths.GetLeft()));
247                 parent->appendChild(element);
248         }
249
250         if (!m_paths.GetMiddle().empty())
251         {
252                 element = createElement(doc, Middle_element_name, T2UTF8(m_paths.GetMiddle()));
253                 parent->appendChild(element);
254         }
255
256         if (!m_paths.GetRight().empty())
257         {
258                 element = createElement(doc, Right_element_name, T2UTF8(m_paths.GetRight()));
259                 parent->appendChild(element);
260         }
261
262         if (!m_filter.empty())
263         {
264                 element = createElement(doc, Filter_element_name, T2UTF8(m_filter));
265                 parent->appendChild(element);
266         }
267
268         element = createElement(doc, Subfolders_element_name, m_subfolders != 0 ? "1" : "0");
269         parent->appendChild(element);
270
271         element = createElement(doc, Left_ro_element_name, m_bLeftReadOnly ? "1" : "0");
272         parent->appendChild(element);
273
274         if (!m_paths.GetMiddle().empty())
275         {
276                 element = createElement(doc, Middle_ro_element_name, m_bMiddleReadOnly ? "1" : "0");
277                 parent->appendChild(element);
278         }
279
280         element = createElement(doc, Right_ro_element_name, m_bRightReadOnly ? "1" : "0");
281         parent->appendChild(element);
282
283         return true;
284 }
285
286 /** 
287  * @brief Returns if left path is defined in project file.
288  * @return true if project file has left path.
289  */
290 bool ProjectFile::HasLeft() const
291 {
292         return m_bHasLeft;
293 }
294
295 /** 
296  * @brief Returns if middle path is defined.
297  */
298 bool ProjectFile::HasMiddle() const
299 {
300         return m_bHasMiddle;
301 }
302
303 /** 
304  * @brief Returns if right path is defined in project file.
305  * @return true if project file has right path.
306  */
307 bool ProjectFile::HasRight() const
308 {
309         return m_bHasRight;
310 }
311
312 /** 
313  * @brief Returns if filter is defined in project file.
314  * @return true if project file has filter.
315  */
316 bool ProjectFile::HasFilter() const
317 {
318         return m_bHasFilter;
319 }
320
321 /** 
322  * @brief Returns if subfolder is defined in projectfile.
323  * @return true if project file has subfolder definition.
324  */
325 bool ProjectFile::HasSubfolders() const
326 {
327         return m_bHasSubfolders;
328 }
329
330 /** 
331  * @brief Returns left path.
332  * @param [out] pReadOnly true if readonly was specified for path.
333  * @return Left path.
334  */
335 String ProjectFile::GetLeft(bool * pReadOnly /*=NULL*/) const
336 {
337         if (pReadOnly)
338                 *pReadOnly = m_bLeftReadOnly;
339         return m_paths.GetLeft();
340 }
341
342 /** 
343  * @brief Returns if left path is specified read-only.
344  * @return true if left path is read-only, false otherwise.
345  */
346 bool ProjectFile::GetLeftReadOnly() const
347 {
348         return m_bLeftReadOnly;
349 }
350
351 /** 
352  * @brief Set left path, returns old left path.
353  * @param [in] sLeft Left path.
354  * @param [in] bReadOnly Will path be recorded read-only?
355  */
356 void ProjectFile::SetLeft(const String& sLeft, const bool * pReadOnly /*=NULL*/)
357 {
358         m_paths.SetLeft(sLeft.c_str(), false);
359         if (pReadOnly)
360                 m_bLeftReadOnly = *pReadOnly;
361 }
362
363 /** 
364  * @brief Returns middle path.
365  * @param [out] pReadOnly true if readonly was specified for path.
366  */
367 String ProjectFile::GetMiddle(bool * pReadOnly /*=NULL*/) const
368 {
369         if (pReadOnly)
370                 *pReadOnly = m_bMiddleReadOnly;
371         return m_paths.GetMiddle();
372 }
373
374 /** 
375  * @brief Returns if middle path is specified read-only.
376  */
377 bool ProjectFile::GetMiddleReadOnly() const
378 {
379         return m_bMiddleReadOnly;
380 }
381
382 /** 
383  * @brief Set middle path, returns old middle path.
384  * @param [in] sMiddle Middle path.
385  * @param [in] bReadOnly Will path be recorded read-only?
386  */
387 void ProjectFile::SetMiddle(const String& sMiddle, const bool * pReadOnly /*=NULL*/)
388 {
389         m_paths.SetMiddle(sMiddle.c_str(), false);
390         if (pReadOnly)
391                 m_bMiddleReadOnly = *pReadOnly;
392
393         return;
394 }
395
396 /** 
397  * @brief Returns right path.
398  * @param [out] pReadOnly true if readonly was specified for path.
399  * @return Right path.
400  */
401 String ProjectFile::GetRight(bool * pReadOnly /*=NULL*/) const
402 {
403         if (pReadOnly)
404                 *pReadOnly = m_bRightReadOnly;
405         return m_paths.GetRight();
406 }
407
408 /** 
409  * @brief Returns if right path is specified read-only.
410  * @return true if right path is read-only, false otherwise.
411  */
412 bool ProjectFile::GetRightReadOnly() const
413 {
414         return m_bRightReadOnly;
415 }
416
417 /** 
418  * @brief Set right path, returns old right path.
419  * @param [in] sRight Right path.
420  * @param [in] bReadOnly Will path be recorded read-only?
421  */
422 void ProjectFile::SetRight(const String& sRight, const bool * pReadOnly /*=NULL*/)
423 {
424         m_paths.SetRight(sRight.c_str(), false);
425         if (pReadOnly)
426                 m_bRightReadOnly = *pReadOnly;
427 }
428
429 /** 
430  * @brief Returns filter.
431  * @return Filter string.
432  */
433 String ProjectFile::GetFilter() const
434 {
435         return m_filter;
436 }
437
438 /** 
439  * @brief Set filter.
440  * @param [in] sFilter New filter string to set.
441  */
442 void ProjectFile::SetFilter(const String& sFilter)
443 {
444         m_filter = sFilter;
445 }
446
447 /** 
448  * @brief Returns subfolder included -setting.
449  * @return != 0 if subfolders are included.
450  */
451 int ProjectFile::GetSubfolders() const
452 {
453         return m_subfolders;
454 }
455
456 /** 
457  * @brief set subfolder.
458  * @param [in] iSubfolder New value for subfolder inclusion.
459  */
460 void ProjectFile::SetSubfolders(int iSubfolder)
461 {
462         m_subfolders = iSubfolder ? 1 : 0;
463 }
464
465 /** 
466  * @brief 
467  *
468  * @param [in] files Files in project
469  * @param [in] bSubFolders If true subfolders included (recursive compare)
470  */
471 void ProjectFile::SetPaths(const PathContext& files, bool bSubfolders)
472 {
473         m_paths = files;
474         m_subfolders = bSubfolders;
475 }
476
477 /** 
478  * @brief Returns left and right paths and recursive from project file
479  * 
480  * @param [out] files Files in project
481  * @param [out] bSubFolders If true subfolders included (recursive compare)
482  */
483 void ProjectFile::GetPaths(PathContext& files, bool & bSubfolders) const
484 {
485         files = m_paths;
486         if (HasSubfolders())
487                 bSubfolders = (GetSubfolders() == 1);
488 }