OSDN Git Service

PATCH: [ 3116407 ] Drop _UNICODE preprocessor definition
[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$
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 #  define UTF82T(lpu) UTF82W(lpu)
45 #  define T2UTF8(lpw) W2UTF8(lpw)
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 Right_element_name[] = "right";
52 const char Filter_element_name[] = "filter";
53 const char Subfolders_element_name[] = "subfolders";
54 const char Left_ro_element_name[] = "left-readonly";
55 const char Right_ro_element_name[] = "right-readonly";
56
57 /** 
58  * @brief Standard constructor.
59  */
60  ProjectFile::ProjectFile()
61 : m_bHasLeft(FALSE)
62 , m_bHasRight(FALSE)
63 , m_bHasFilter(FALSE)
64 , m_bHasSubfolders(FALSE)
65 , m_subfolders(-1)
66 , m_bLeftReadOnly(FALSE)
67 , m_bRightReadOnly(FALSE)
68 {
69 }
70
71 /** 
72  * @brief Open given path-file and read data from it to member variables.
73  * @param [in] path Path to project file.
74  * @param [out] sError Error string if error happened.
75  * @return TRUE if reading succeeded, FALSE if error happened.
76  */
77 BOOL ProjectFile::Read(LPCTSTR path, String *sError)
78 {
79         BOOL loaded = FALSE;
80     scew_tree* tree = NULL;
81     scew_parser* parser = NULL;
82
83     parser = scew_parser_create();
84     scew_parser_ignore_whitespaces(parser, 1);
85
86         scew_reader *reader = NULL;
87         FILE * fp = _tfopen(path, _T("r"));
88         if (fp)
89         {
90                 reader = scew_reader_fp_create(fp);
91                 if (reader)
92                 {
93                         tree = scew_parser_load (parser, reader);
94
95                         if (tree)
96                         {
97                                 scew_element * root = GetRootElement(tree);
98                                 if (root)
99                                 {
100                                         // Currently our content is paths, so expect
101                                         // having paths in valid project file!
102                                         if (GetPathsData(root))
103                                                 loaded = TRUE;
104                                 };
105                         }
106                 }
107
108                 scew_tree_free(tree);
109                 scew_reader_free(reader);
110
111                 /* Frees the SCEW parser */
112                 scew_parser_free(parser);
113                 fclose(fp);
114         }
115         return loaded;
116 }
117
118 /** 
119  * @brief Return project file XML's root element.
120  * @param [in] tree XML tree we got from the parser.
121  * @return Root element pointer.
122  */
123 scew_element* ProjectFile::GetRootElement(scew_tree * tree)
124 {
125         scew_element * root = NULL;
126
127         if (tree != NULL)
128         {
129                 root = scew_tree_root(tree);
130         }
131
132         if (root != NULL)
133         {
134                 // Make sure we have correct root element
135                 if (strcmp(Root_element_name, scew_element_name(root)) != 0)
136                 {
137                         root = NULL;
138                 }
139         }
140         return root;
141 }
142
143 /** 
144  * @brief Reads the paths data from the XML data.
145  * This function reads the paths data inside given element in XML data.
146  * @param [in] parent Parent element for the paths data.
147  * @return TRUE if pathdata was found from the file.
148  */
149 BOOL ProjectFile::GetPathsData(scew_element * parent)
150 {
151         USES_CONVERSION;
152         BOOL bFoundPaths = FALSE;
153         scew_element *paths = NULL;
154
155         if (parent != NULL)
156         {
157                 paths = scew_element_by_name(parent, Paths_element_name);
158         }
159
160         if (paths != NULL)
161         {
162                 bFoundPaths = TRUE;
163                 scew_element *left = NULL;
164                 scew_element *right = NULL;
165                 scew_element *filter = NULL;
166                 scew_element *subfolders = NULL;
167                 scew_element *left_ro = NULL;
168                 scew_element *right_ro = NULL;
169
170                 left = scew_element_by_name(paths, Left_element_name);
171                 right = scew_element_by_name(paths, Right_element_name);
172                 filter = scew_element_by_name(paths, Filter_element_name);
173                 subfolders = scew_element_by_name(paths, Subfolders_element_name);
174                 left_ro = scew_element_by_name(paths, Left_ro_element_name);
175                 right_ro = scew_element_by_name(paths, Right_ro_element_name);
176
177                 if (left)
178                 {
179                         LPCSTR path = NULL;
180                         path = scew_element_contents(left);
181                         m_leftFile = UTF82T(path);
182                         m_bHasLeft = TRUE;
183                 }
184                 if (right)
185                 {
186                         LPCSTR path = NULL;
187                         path = scew_element_contents(right);
188                         m_rightFile = UTF82T(path);
189                         m_bHasRight = TRUE;
190                 }
191                 if (filter)
192                 {
193                         LPCSTR filtername = NULL;
194                         filtername = scew_element_contents(filter);
195                         m_filter = UTF82T(filtername);
196                         m_bHasFilter = TRUE;
197                 }
198                 if (subfolders)
199                 {
200                         LPCSTR folders = NULL;
201                         folders = scew_element_contents(subfolders);
202                         m_subfolders = atoi(folders);
203                         m_bHasSubfolders = TRUE;
204                 }
205                 if (left_ro)
206                 {
207                         LPCSTR readonly = NULL;
208                         readonly = scew_element_contents(left_ro);
209                         m_bLeftReadOnly = (atoi(readonly) != 0);
210                 }
211                 if (right_ro)
212                 {
213                         LPCSTR readonly = NULL;
214                         readonly = scew_element_contents(right_ro);
215                         m_bRightReadOnly = (atoi(readonly) != 0);
216                 }
217         }
218         return bFoundPaths;
219 }
220
221 /** 
222  * @brief Save data from member variables to path-file.
223  * @param [in] path Path to project file.
224  * @param [out] sError Error string if error happened.
225  * @return TRUE if saving succeeded, FALSE if error happened.
226  */
227 BOOL ProjectFile::Save(LPCTSTR path, String *sError)
228 {
229         BOOL success = TRUE;
230         scew_tree* tree = NULL;
231         scew_element* root = NULL;
232         scew_element* paths = NULL;
233
234         tree = scew_tree_create();
235         root = scew_tree_set_root(tree, Root_element_name);
236         if (root != NULL)
237         {
238                 paths = AddPathsElement(root);
239         }
240         else
241                 success = FALSE;
242
243         if (paths != NULL)
244         {
245                 AddPathsContent(paths);
246         }
247         else
248                 success = FALSE;
249         
250         scew_tree_set_xml_encoding(tree, "UTF-8");
251
252         scew_writer *writer = NULL;
253         scew_printer *printer = NULL;
254         FILE * fp = _tfopen(path, _T("w"));
255         if (fp)
256         {
257                 writer = scew_writer_fp_create(fp);
258                 if (writer)
259                 {
260                         printer = scew_printer_create(writer);
261                         
262                         if (!scew_printer_print_tree(printer, tree) ||
263                                 !scew_printf(_XT("\n")))
264                         {
265                                 success = FALSE;
266                                 *sError = theApp.LoadString(IDS_FILEWRITE_ERROR);
267                         }
268                 }
269                 else
270                 {
271                         success = FALSE;
272                         *sError = theApp.LoadString(IDS_FILEWRITE_ERROR);
273                 }
274                 fclose(fp);
275         }
276         else
277         {
278                 success = FALSE;
279         }
280         
281         /* Frees the SCEW tree */
282         scew_tree_free(tree);
283         scew_writer_free(writer);
284         scew_printer_free(printer);
285
286         if (success == FALSE)
287         {
288                 *sError = theApp.LoadString(IDS_FILEWRITE_ERROR);
289         }
290         return success;
291 }
292
293 /**
294  * @brief Add paths element into XML tree.
295  * @param [in] parent Parent element for the paths element.
296  * @return pointer to added paths element.
297  */
298 scew_element* ProjectFile::AddPathsElement(scew_element * parent)
299 {
300         scew_element* element = NULL;
301         element = scew_element_add(parent, Paths_element_name);
302         return element;
303 }
304
305 /**
306  * @brief Covert characters that are unsafe for use in XML
307  * @param [in] str The string to be converted
308  * @return The converted string
309  */
310 static String EscapeXML(const String &str)
311 {
312         String escapedStr = str;
313         string_replace(escapedStr, _T("&"), _T("&amp;"));
314         string_replace(escapedStr, _T("<"), _T("&lt;"));
315         string_replace(escapedStr, _T(">"), _T("&gt;"));
316         return escapedStr;
317 }
318
319 /**
320  * @brief Add paths data to the XML tree.
321  * This function adds our paths data to the XML tree.
322  * @param [in] parent Parent element for paths data.
323  * @return TRUE if we succeeded, FALSE otherwise.
324  */
325 BOOL ProjectFile::AddPathsContent(scew_element * parent)
326 {
327         USES_CONVERSION;
328         scew_element* element = NULL;
329
330         if (!m_leftFile.empty())
331         {
332                 element = scew_element_add(parent, Left_element_name);
333                 String path = m_leftFile;
334                 scew_element_set_contents(element, T2UTF8(EscapeXML(path).c_str()));
335         }
336
337         if (!m_rightFile.empty())
338         {
339                 element = scew_element_add(parent, Right_element_name);
340                 String path = m_rightFile;
341                 scew_element_set_contents(element, T2UTF8(EscapeXML(path).c_str()));
342         }
343
344         if (!m_filter.empty())
345         {
346                 element = scew_element_add(parent, Filter_element_name);
347                 String filter = m_filter;
348                 scew_element_set_contents(element, T2UTF8(EscapeXML(filter).c_str()));
349         }
350
351         element = scew_element_add(parent, Subfolders_element_name);
352         if (m_subfolders != 0)
353                 scew_element_set_contents(element, "1");
354         else
355                 scew_element_set_contents(element, "0");
356
357         element = scew_element_add(parent, Left_ro_element_name);
358         if (m_bLeftReadOnly)
359                 scew_element_set_contents(element, "1");
360         else
361                 scew_element_set_contents(element, "0");
362
363         element = scew_element_add(parent, Right_ro_element_name);
364         if (m_bRightReadOnly)
365                 scew_element_set_contents(element, "1");
366         else
367                 scew_element_set_contents(element, "0");
368
369         return TRUE;
370 }
371
372 /** 
373  * @brief Returns if left path is defined in project file.
374  * @return TRUE if project file has left path.
375  */
376 BOOL ProjectFile::HasLeft() const
377 {
378         return m_bHasLeft;
379 }
380
381 /** 
382  * @brief Returns if right path is defined in project file.
383  * @return TRUE if project file has right path.
384  */
385 BOOL ProjectFile::HasRight() const
386 {
387         return m_bHasRight;
388 }
389
390 /** 
391  * @brief Returns if filter is defined in project file.
392  * @return TRUE if project file has filter.
393  */
394 BOOL ProjectFile::HasFilter() const
395 {
396         return m_bHasFilter;
397 }
398
399 /** 
400  * @brief Returns if subfolder is defined in projectfile.
401  * @return TRUE if project file has subfolder definition.
402  */
403 BOOL ProjectFile::HasSubfolders() const
404 {
405         return m_bHasSubfolders;
406 }
407
408 /** 
409  * @brief Returns left path.
410  * @param [out] pReadOnly TRUE if readonly was specified for path.
411  * @return Left path.
412  */
413 String ProjectFile::GetLeft(BOOL * pReadOnly /*=NULL*/) const
414 {
415         if (pReadOnly)
416                 *pReadOnly = m_bLeftReadOnly;
417         return m_leftFile;
418 }
419
420 /** 
421  * @brief Returns if left path is specified read-only.
422  * @return TRUE if left path is read-only, FALSE otherwise.
423  */
424 BOOL ProjectFile::GetLeftReadOnly() const
425 {
426         return m_bLeftReadOnly;
427 }
428
429 /** 
430  * @brief Set left path, returns old left path.
431  * @param [in] sLeft Left path.
432  * @param [in] bReadOnly Will path be recorded read-only?
433  */
434 void ProjectFile::SetLeft(const String& sLeft, const BOOL * pReadOnly /*=NULL*/)
435 {
436         m_leftFile = sLeft;
437         if (pReadOnly)
438                 m_bLeftReadOnly = *pReadOnly;
439 }
440
441 /** 
442  * @brief Returns right path.
443  * @param [out] pReadOnly TRUE if readonly was specified for path.
444  * @return Right path.
445  */
446 String ProjectFile::GetRight(BOOL * pReadOnly /*=NULL*/) const
447 {
448         if (pReadOnly)
449                 *pReadOnly = m_bRightReadOnly;
450         return m_rightFile;
451 }
452
453 /** 
454  * @brief Returns if right path is specified read-only.
455  * @return TRUE if right path is read-only, FALSE otherwise.
456  */
457 BOOL ProjectFile::GetRightReadOnly() const
458 {
459         return m_bRightReadOnly;
460 }
461
462 /** 
463  * @brief Set right path, returns old right path.
464  * @param [in] sRight Right path.
465  * @param [in] bReadOnly Will path be recorded read-only?
466  */
467 void ProjectFile::SetRight(const String& sRight, const BOOL * pReadOnly /*=NULL*/)
468 {
469         m_rightFile = sRight;
470         if (pReadOnly)
471                 m_bRightReadOnly = *pReadOnly;
472 }
473
474 /** 
475  * @brief Returns filter.
476  * @return Filter string.
477  */
478 String ProjectFile::GetFilter() const
479 {
480         return m_filter;
481 }
482
483 /** 
484  * @brief Set filter.
485  * @param [in] sFilter New filter string to set.
486  */
487 void ProjectFile::SetFilter(const String& sFilter)
488 {
489         m_filter = sFilter;
490 }
491
492 /** 
493  * @brief Returns subfolder included -setting.
494  * @return != 0 if subfolders are included.
495  */
496 int ProjectFile::GetSubfolders() const
497 {
498         return m_subfolders;
499 }
500
501 /** 
502  * @brief set subfolder.
503  * @param [in] iSubfolder New value for subfolder inclusion.
504  */
505 void ProjectFile::SetSubfolders(int iSubfolder)
506 {
507         m_subfolders = iSubfolder ? 1 : 0;
508 }