OSDN Git Service

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