OSDN Git Service

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