OSDN Git Service

- Use Poco::RegularExpression instead of PCRE library. (Poco::RegularExpression inter...
[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 "stdafx.h"
26 #include <scew/scew.h>
27 #include "unicoder.h"
28 #include "UnicodeString.h"
29 #include "ProjectFile.h"
30
31 // Constants for xml element names
32 const char Root_element_name[] = "project";
33 const char Paths_element_name[] = "paths";
34 const char Left_element_name[] = "left";
35 const char Middle_element_name[] = "middle";
36 const char Right_element_name[] = "right";
37 const char Filter_element_name[] = "filter";
38 const char Subfolders_element_name[] = "subfolders";
39 const char Left_ro_element_name[] = "left-readonly";
40 const char Middle_ro_element_name[] = "middle-readonly";
41 const char Right_ro_element_name[] = "right-readonly";
42
43
44 static String UTF82T(const char *str)
45 {
46         TCHAR *tstr = ucr::convertUTF8toT(str ? str : "");
47         String newstr(tstr);
48         free(tstr);
49         return newstr;
50 }
51
52 static std::string T2UTF8(const TCHAR *str)
53 {
54         char *utf8 = (char *)ucr::convertTtoUTF8(str ? str : _T(""));
55         std::string newstr(utf8);
56         free(utf8);
57         return newstr;
58 }
59
60 /** 
61  * @brief Standard constructor.
62  */
63  ProjectFile::ProjectFile()
64 : m_bHasLeft(FALSE)
65 , m_bHasMiddle(FALSE)
66 , m_bHasRight(FALSE)
67 , m_bHasFilter(FALSE)
68 , m_bHasSubfolders(FALSE)
69 , m_subfolders(-1)
70 , m_bLeftReadOnly(FALSE)
71 , m_bMiddleReadOnly(FALSE)
72 , m_bRightReadOnly(FALSE)
73 {
74 }
75
76 /** 
77  * @brief Open given path-file and read data from it to member variables.
78  * @param [in] path Path to project file.
79  * @param [out] sError Error string if error happened.
80  * @return TRUE if reading succeeded, FALSE if error happened.
81  */
82 BOOL ProjectFile::Read(LPCTSTR path, String *sError)
83 {
84         BOOL loaded = FALSE;
85     scew_tree* tree = NULL;
86     scew_parser* parser = NULL;
87
88     parser = scew_parser_create();
89     scew_parser_ignore_whitespaces(parser, 1);
90
91         scew_reader *reader = NULL;
92         FILE * fp = _tfopen(path, _T("r"));
93         if (fp)
94         {
95                 reader = scew_reader_fp_create(fp);
96                 if (reader)
97                 {
98                         tree = scew_parser_load (parser, reader);
99
100                         if (tree)
101                         {
102                                 scew_element * root = GetRootElement(tree);
103                                 if (root)
104                                 {
105                                         // Currently our content is paths, so expect
106                                         // having paths in valid project file!
107                                         if (GetPathsData(root))
108                                                 loaded = TRUE;
109                                 };
110                         }
111                 }
112
113                 scew_tree_free(tree);
114                 scew_reader_free(reader);
115
116                 /* Frees the SCEW parser */
117                 scew_parser_free(parser);
118                 fclose(fp);
119         }
120         return loaded;
121 }
122
123 /** 
124  * @brief Return project file XML's root element.
125  * @param [in] tree XML tree we got from the parser.
126  * @return Root element pointer.
127  */
128 scew_element* ProjectFile::GetRootElement(scew_tree * tree)
129 {
130         scew_element * root = NULL;
131
132         if (tree != NULL)
133         {
134                 root = scew_tree_root(tree);
135         }
136
137         if (root != NULL)
138         {
139                 // Make sure we have correct root element
140                 if (strcmp(Root_element_name, scew_element_name(root)) != 0)
141                 {
142                         root = NULL;
143                 }
144         }
145         return root;
146 }
147
148 /** 
149  * @brief Reads the paths data from the XML data.
150  * This function reads the paths data inside given element in XML data.
151  * @param [in] parent Parent element for the paths data.
152  * @return TRUE if pathdata was found from the file.
153  */
154 BOOL ProjectFile::GetPathsData(scew_element * parent)
155 {
156         BOOL bFoundPaths = FALSE;
157         scew_element *paths = NULL;
158
159         if (parent != NULL)
160         {
161                 paths = scew_element_by_name(parent, Paths_element_name);
162         }
163
164         if (paths != NULL)
165         {
166                 bFoundPaths = TRUE;
167                 scew_element *left = NULL;
168                 scew_element *middle = NULL;
169                 scew_element *right = NULL;
170                 scew_element *filter = NULL;
171                 scew_element *subfolders = NULL;
172                 scew_element *left_ro = NULL;
173                 scew_element *middle_ro = NULL;
174                 scew_element *right_ro = NULL;
175
176                 left = scew_element_by_name(paths, Left_element_name);
177                 middle = scew_element_by_name(paths, Middle_element_name);
178                 right = scew_element_by_name(paths, Right_element_name);
179                 filter = scew_element_by_name(paths, Filter_element_name);
180                 subfolders = scew_element_by_name(paths, Subfolders_element_name);
181                 left_ro = scew_element_by_name(paths, Left_ro_element_name);
182                 middle_ro = scew_element_by_name(paths, Middle_ro_element_name);
183                 right_ro = scew_element_by_name(paths, Right_ro_element_name);
184
185                 if (left)
186                 {
187                         LPCSTR path = NULL;
188                         path = scew_element_contents(left);
189                         m_paths.SetLeft(UTF82T(path).c_str());
190                         m_bHasLeft = TRUE;
191                 }
192                 if (middle)
193                 {
194                         LPCSTR path = NULL;
195                         path = scew_element_contents(middle);
196                         m_paths.SetMiddle(UTF82T(path).c_str());
197                         m_bHasMiddle = TRUE;
198                 }
199                 if (right)
200                 {
201                         LPCSTR path = NULL;
202                         path = scew_element_contents(right);
203                         m_paths.SetRight(UTF82T(path).c_str());
204                         m_bHasRight = TRUE;
205                 }
206                 if (filter)
207                 {
208                         LPCSTR filtername = NULL;
209                         filtername = scew_element_contents(filter);
210                         m_filter = UTF82T(filtername);
211                         m_bHasFilter = TRUE;
212                 }
213                 if (subfolders)
214                 {
215                         LPCSTR folders = NULL;
216                         folders = scew_element_contents(subfolders);
217                         m_subfolders = atoi(folders);
218                         m_bHasSubfolders = TRUE;
219                 }
220                 if (left_ro)
221                 {
222                         LPCSTR readonly = NULL;
223                         readonly = scew_element_contents(left_ro);
224                         m_bLeftReadOnly = (atoi(readonly) != 0);
225                 }
226                 if (middle_ro)
227                 {
228                         LPCSTR readonly = NULL;
229                         readonly = scew_element_contents(middle_ro);
230                         m_bMiddleReadOnly = (atoi(readonly) != 0);
231                 }
232                 if (right_ro)
233                 {
234                         LPCSTR readonly = NULL;
235                         readonly = scew_element_contents(right_ro);
236                         m_bRightReadOnly = (atoi(readonly) != 0);
237                 }
238         }
239         return bFoundPaths;
240 }
241
242 /** 
243  * @brief Save data from member variables to path-file.
244  * @param [in] path Path to project file.
245  * @param [out] sError Error string if error happened.
246  * @return TRUE if saving succeeded, FALSE if error happened.
247  */
248 BOOL ProjectFile::Save(LPCTSTR path, String *sError)
249 {
250         BOOL success = TRUE;
251         scew_tree* tree = NULL;
252         scew_element* root = NULL;
253         scew_element* paths = NULL;
254
255         tree = scew_tree_create();
256         root = scew_tree_set_root(tree, Root_element_name);
257         if (root != NULL)
258         {
259                 paths = AddPathsElement(root);
260         }
261         else
262                 success = FALSE;
263
264         if (paths != NULL)
265         {
266                 AddPathsContent(paths);
267         }
268         else
269                 success = FALSE;
270         
271         scew_tree_set_xml_encoding(tree, "UTF-8");
272
273         scew_writer *writer = NULL;
274         scew_printer *printer = NULL;
275         FILE * fp = _tfopen(path, _T("w"));
276         if (fp)
277         {
278                 writer = scew_writer_fp_create(fp);
279                 if (writer)
280                 {
281                         printer = scew_printer_create(writer);
282                         
283                         if (!scew_printer_print_tree(printer, tree) ||
284                                 !scew_printf(_XT("\n")))
285                         {
286                                 success = FALSE;
287                                 *sError = LoadResString(IDS_FILEWRITE_ERROR);
288                         }
289                 }
290                 else
291                 {
292                         success = FALSE;
293                         *sError = LoadResString(IDS_FILEWRITE_ERROR);
294                 }
295                 fclose(fp);
296         }
297         else
298         {
299                 success = FALSE;
300         }
301         
302         /* Frees the SCEW tree */
303         scew_tree_free(tree);
304         scew_writer_free(writer);
305         scew_printer_free(printer);
306
307         if (success == FALSE)
308         {
309                 *sError = LoadResString(IDS_FILEWRITE_ERROR);
310         }
311         return success;
312 }
313
314 /**
315  * @brief Add paths element into XML tree.
316  * @param [in] parent Parent element for the paths element.
317  * @return pointer to added paths element.
318  */
319 scew_element* ProjectFile::AddPathsElement(scew_element * parent)
320 {
321         scew_element* element = NULL;
322         element = scew_element_add(parent, Paths_element_name);
323         return element;
324 }
325
326 /**
327  * @brief Covert characters that are unsafe for use in XML
328  * @param [in] str The string to be converted
329  * @return The converted string
330  */
331 static String EscapeXML(const String &str)
332 {
333         String escapedStr = str;
334         string_replace(escapedStr, _T("&"), _T("&amp;"));
335         string_replace(escapedStr, _T("<"), _T("&lt;"));
336         string_replace(escapedStr, _T(">"), _T("&gt;"));
337         return escapedStr;
338 }
339
340 /**
341  * @brief Add paths data to the XML tree.
342  * This function adds our paths data to the XML tree.
343  * @param [in] parent Parent element for paths data.
344  * @return TRUE if we succeeded, FALSE otherwise.
345  */
346 BOOL ProjectFile::AddPathsContent(scew_element * parent)
347 {
348         scew_element* element = NULL;
349
350         if (!m_paths.GetLeft().empty())
351         {
352                 element = scew_element_add(parent, Left_element_name);
353                 scew_element_set_contents(element, T2UTF8(EscapeXML(m_paths.GetLeft()).c_str()).c_str());
354         }
355
356         if (!m_paths.GetMiddle().empty())
357         {
358                 element = scew_element_add(parent, Middle_element_name);
359                 scew_element_set_contents(element, T2UTF8(EscapeXML(m_paths.GetMiddle()).c_str()).c_str());
360         }
361
362         if (!m_paths.GetRight().empty())
363         {
364                 element = scew_element_add(parent, Right_element_name);
365                 scew_element_set_contents(element, T2UTF8(EscapeXML(m_paths.GetRight()).c_str()).c_str());
366         }
367
368         if (!m_filter.empty())
369         {
370                 element = scew_element_add(parent, Filter_element_name);
371                 String filter = m_filter;
372                 scew_element_set_contents(element, T2UTF8(EscapeXML(filter).c_str()).c_str());
373         }
374
375         element = scew_element_add(parent, Subfolders_element_name);
376         if (m_subfolders != 0)
377                 scew_element_set_contents(element, "1");
378         else
379                 scew_element_set_contents(element, "0");
380
381         element = scew_element_add(parent, Left_ro_element_name);
382         if (m_bLeftReadOnly)
383                 scew_element_set_contents(element, "1");
384         else
385                 scew_element_set_contents(element, "0");
386
387         if (!m_paths.GetMiddle().empty())
388         {
389                 element = scew_element_add(parent, Middle_ro_element_name);
390                 if (m_bMiddleReadOnly)
391                         scew_element_set_contents(element, "1");
392                 else
393                         scew_element_set_contents(element, "0");
394         }
395         element = scew_element_add(parent, Right_ro_element_name);
396         if (m_bRightReadOnly)
397                 scew_element_set_contents(element, "1");
398         else
399                 scew_element_set_contents(element, "0");
400
401         return TRUE;
402 }
403
404 /** 
405  * @brief Returns if left path is defined in project file.
406  * @return TRUE if project file has left path.
407  */
408 BOOL ProjectFile::HasLeft() const
409 {
410         return m_bHasLeft;
411 }
412
413 /** 
414  * @brief Returns if middle path is defined.
415  */
416 BOOL ProjectFile::HasMiddle() const
417 {
418         return m_bHasMiddle;
419 }
420
421 /** 
422  * @brief Returns if right path is defined in project file.
423  * @return TRUE if project file has right path.
424  */
425 BOOL ProjectFile::HasRight() const
426 {
427         return m_bHasRight;
428 }
429
430 /** 
431  * @brief Returns if filter is defined in project file.
432  * @return TRUE if project file has filter.
433  */
434 BOOL ProjectFile::HasFilter() const
435 {
436         return m_bHasFilter;
437 }
438
439 /** 
440  * @brief Returns if subfolder is defined in projectfile.
441  * @return TRUE if project file has subfolder definition.
442  */
443 BOOL ProjectFile::HasSubfolders() const
444 {
445         return m_bHasSubfolders;
446 }
447
448 /** 
449  * @brief Returns left path.
450  * @param [out] pReadOnly TRUE if readonly was specified for path.
451  * @return Left path.
452  */
453 String ProjectFile::GetLeft(BOOL * pReadOnly /*=NULL*/) const
454 {
455         if (pReadOnly)
456                 *pReadOnly = m_bLeftReadOnly;
457         return m_paths.GetLeft();
458 }
459
460 /** 
461  * @brief Returns if left path is specified read-only.
462  * @return TRUE if left path is read-only, FALSE otherwise.
463  */
464 BOOL ProjectFile::GetLeftReadOnly() const
465 {
466         return m_bLeftReadOnly;
467 }
468
469 /** 
470  * @brief Set left path, returns old left path.
471  * @param [in] sLeft Left path.
472  * @param [in] bReadOnly Will path be recorded read-only?
473  */
474 void ProjectFile::SetLeft(const String& sLeft, const BOOL * pReadOnly /*=NULL*/)
475 {
476         m_paths.SetLeft(sLeft.c_str(), false);
477         if (pReadOnly)
478                 m_bLeftReadOnly = *pReadOnly;
479 }
480
481 /** 
482  * @brief Returns middle path.
483  * @param [out] pReadOnly TRUE if readonly was specified for path.
484  */
485 String ProjectFile::GetMiddle(BOOL * pReadOnly /*=NULL*/) const
486 {
487         if (pReadOnly)
488                 *pReadOnly = m_bMiddleReadOnly;
489         return m_paths.GetMiddle();
490 }
491
492 /** 
493  * @brief Returns if middle path is specified read-only.
494  */
495 BOOL ProjectFile::GetMiddleReadOnly() const
496 {
497         return m_bMiddleReadOnly;
498 }
499
500 /** 
501  * @brief Set middle path, returns old middle path.
502  * @param [in] sMiddle Middle path.
503  * @param [in] bReadOnly Will path be recorded read-only?
504  */
505 void ProjectFile::SetMiddle(const String& sMiddle, const BOOL * pReadOnly /*=NULL*/)
506 {
507         m_paths.SetMiddle(sMiddle.c_str(), false);
508         if (pReadOnly)
509                 m_bMiddleReadOnly = *pReadOnly;
510
511         return;
512 }
513
514 /** 
515  * @brief Returns right path.
516  * @param [out] pReadOnly TRUE if readonly was specified for path.
517  * @return Right path.
518  */
519 String ProjectFile::GetRight(BOOL * pReadOnly /*=NULL*/) const
520 {
521         if (pReadOnly)
522                 *pReadOnly = m_bRightReadOnly;
523         return m_paths.GetRight();
524 }
525
526 /** 
527  * @brief Returns if right path is specified read-only.
528  * @return TRUE if right path is read-only, FALSE otherwise.
529  */
530 BOOL ProjectFile::GetRightReadOnly() const
531 {
532         return m_bRightReadOnly;
533 }
534
535 /** 
536  * @brief Set right path, returns old right path.
537  * @param [in] sRight Right path.
538  * @param [in] bReadOnly Will path be recorded read-only?
539  */
540 void ProjectFile::SetRight(const String& sRight, const BOOL * pReadOnly /*=NULL*/)
541 {
542         m_paths.SetRight(sRight.c_str(), false);
543         if (pReadOnly)
544                 m_bRightReadOnly = *pReadOnly;
545 }
546
547 /** 
548  * @brief Returns filter.
549  * @return Filter string.
550  */
551 String ProjectFile::GetFilter() const
552 {
553         return m_filter;
554 }
555
556 /** 
557  * @brief Set filter.
558  * @param [in] sFilter New filter string to set.
559  */
560 void ProjectFile::SetFilter(const String& sFilter)
561 {
562         m_filter = sFilter;
563 }
564
565 /** 
566  * @brief Returns subfolder included -setting.
567  * @return != 0 if subfolders are included.
568  */
569 int ProjectFile::GetSubfolders() const
570 {
571         return m_subfolders;
572 }
573
574 /** 
575  * @brief set subfolder.
576  * @param [in] iSubfolder New value for subfolder inclusion.
577  */
578 void ProjectFile::SetSubfolders(int iSubfolder)
579 {
580         m_subfolders = iSubfolder ? 1 : 0;
581 }
582
583 /** 
584  * @brief 
585  *
586  * @param [in] files Files in project
587  * @param [in] bSubFolders If TRUE subfolders included (recursive compare)
588  */
589 void ProjectFile::SetPaths(const PathContext& files, BOOL bSubfolders)
590 {
591         m_paths = files;
592         m_subfolders = bSubfolders;
593 }
594
595 /** 
596  * @brief Returns left and right paths and recursive from project file
597  * 
598  * @param [out] files Files in project
599  * @param [out] bSubFolders If TRUE subfolders included (recursive compare)
600  */
601 void ProjectFile::GetPaths(PathContext& files, BOOL & bSubfolders) const
602 {
603         files = m_paths;
604         if (HasSubfolders())
605                 bSubfolders = (GetSubfolders() == 1);
606 }