1 /////////////////////////////////////////////////////////////////////////////
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.
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.
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 /////////////////////////////////////////////////////////////////////////////
18 * @file ProjectFile.cpp
20 * @brief Implementation file for ProjectFile class.
22 // ID line follows -- this is updated by CVS
23 // $Id: ProjectFile.cpp 7081 2010-01-01 20:33:30Z kimmov $
25 #include "ProjectFile.h"
29 #include <Poco/FileStream.h>
30 #include <Poco/XML/XMLWriter.h>
31 #include <Poco/SAX/SAXParser.h>
32 #include <Poco/SAX/ContentHandler.h>
33 #include <Poco/Exception.h>
34 #include "UnicodeString.h"
37 using Poco::FileStream;
38 using Poco::XML::SAXParser;
39 using Poco::XML::ContentHandler;
40 using Poco::XML::Locator;
41 using Poco::XML::XMLWriter;
42 using Poco::XML::XMLChar;
43 using Poco::XML::XMLString;
44 using Poco::XML::Attributes;
45 using Poco::Exception;
49 // Constants for xml element names
50 const char Root_element_name[] = "project";
51 const char Paths_element_name[] = "paths";
52 const char Left_element_name[] = "left";
53 const char Middle_element_name[] = "middle";
54 const char Right_element_name[] = "right";
55 const char Filter_element_name[] = "filter";
56 const char Subfolders_element_name[] = "subfolders";
57 const char Left_ro_element_name[] = "left-readonly";
58 const char Middle_ro_element_name[] = "middle-readonly";
59 const char Right_ro_element_name[] = "right-readonly";
64 String xmlch2tstr(const XMLChar *ch, int length)
66 return toTString(std::string(ch, length));
69 void writeElement(XMLWriter& writer, const std::string& tagname, const std::string& characters)
71 writer.startElement("", "", tagname);
72 writer.characters(characters);
73 writer.endElement("", "", tagname);
78 class ProjectFileHandler: public ContentHandler
81 ProjectFileHandler(ProjectFile *pProject) : m_pProject(pProject) {}
83 void setDocumentLocator(const Locator* loc) {}
84 void startDocument() {}
86 void startElement(const XMLString& uri, const XMLString& localName, const XMLString& qname, const Attributes& attributes)
88 m_stack.push(localName);
90 void endElement(const XMLString& uri, const XMLString& localName, const XMLString& qname)
94 void characters(const XMLChar ch[], int start, int length)
96 if (m_stack.size() != 3)
99 const std::string& nodename = m_stack.top();
100 if (nodename == Left_element_name)
102 m_pProject->m_paths.SetLeft(m_pProject->m_paths.GetLeft() + xmlch2tstr(ch + start, length));
103 m_pProject->m_bHasLeft = true;
105 else if (nodename == Middle_element_name)
107 m_pProject->m_paths.SetMiddle(m_pProject->m_paths.GetMiddle() + xmlch2tstr(ch + start, length));
108 m_pProject->m_bHasMiddle = true;
110 else if (nodename == Right_element_name)
112 m_pProject->m_paths.SetRight(m_pProject->m_paths.GetRight() + xmlch2tstr(ch + start, length));
113 m_pProject->m_bHasRight = true;
115 else if (nodename == Filter_element_name)
117 m_pProject->m_filter += xmlch2tstr(ch + start, length);
118 m_pProject->m_bHasFilter = true;
120 else if (nodename == Subfolders_element_name)
122 m_pProject->m_subfolders = atoi(std::string(ch + start, length).c_str());
123 m_pProject->m_bHasSubfolders = true;
125 else if (nodename == Left_ro_element_name)
127 m_pProject->m_bLeftReadOnly = atoi(std::string(ch + start, length).c_str()) != 0;
129 else if (nodename == Middle_ro_element_name)
131 m_pProject->m_bMiddleReadOnly = atoi(std::string(ch + start, length).c_str()) != 0;
133 else if (nodename == Right_ro_element_name)
135 m_pProject->m_bRightReadOnly = atoi(std::string(ch + start, length).c_str()) != 0;
138 void ignorableWhitespace(const XMLChar ch[], int start, int length) {}
139 void processingInstruction(const XMLString& target, const XMLString& data) {}
140 void startPrefixMapping(const XMLString& prefix, const XMLString& uri) {}
141 void endPrefixMapping(const XMLString& prefix) {}
142 void skippedEntity(const XMLString& name) {}
145 ProjectFile *m_pProject;
146 std::stack<std::string> m_stack;
149 /** @brief File extension for path files */
150 const String ProjectFile::PROJECTFILE_EXT = toTString("WinMerge");
153 * @brief Standard constructor.
155 ProjectFile::ProjectFile()
157 , m_bHasMiddle(false)
159 , m_bHasFilter(false)
160 , m_bHasSubfolders(false)
162 , m_bLeftReadOnly(false)
163 , m_bMiddleReadOnly(false)
164 , m_bRightReadOnly(false)
169 * @brief Open given path-file and read data from it to member variables.
170 * @param [in] path Path to project file.
171 * @param [out] sError Error string if error happened.
172 * @return true if reading succeeded, false if error happened.
174 bool ProjectFile::Read(const String& path)
176 ProjectFileHandler handler(this);
178 parser.setContentHandler(&handler);
179 parser.parse(toUTF8(path));
184 * @brief Save data from member variables to path-file.
185 * @param [in] path Path to project file.
186 * @param [out] sError Error string if error happened.
187 * @return true if saving succeeded, false if error happened.
189 bool ProjectFile::Save(const String& path) const
191 FileStream out(toUTF8(path));
192 XMLWriter writer(out, XMLWriter::WRITE_XML_DECLARATION | XMLWriter::PRETTY_PRINT);
193 writer.startDocument();
194 writer.startElement("", "", Root_element_name);
196 writer.startElement("", "", Paths_element_name);
198 if (!m_paths.GetLeft().empty())
199 writeElement(writer, Left_element_name, toUTF8(m_paths.GetLeft()));
200 if (!m_paths.GetMiddle().empty())
201 writeElement(writer, Middle_element_name, toUTF8(m_paths.GetMiddle()));
202 if (!m_paths.GetRight().empty())
203 writeElement(writer, Right_element_name, toUTF8(m_paths.GetRight()));
204 if (!m_filter.empty())
205 writeElement(writer, Filter_element_name, toUTF8(m_filter));
206 writeElement(writer, Subfolders_element_name, m_subfolders != 0 ? "1" : "0");
207 writeElement(writer, Left_ro_element_name, m_bLeftReadOnly ? "1" : "0");
208 if (!m_paths.GetMiddle().empty())
209 writeElement(writer, Middle_ro_element_name, m_bMiddleReadOnly ? "1" : "0");
210 writeElement(writer, Right_ro_element_name, m_bRightReadOnly ? "1" : "0");
212 writer.endElement("", "", Paths_element_name);
214 writer.endElement("", "", Root_element_name);
215 writer.endDocument();
220 * @brief Returns if left path is defined in project file.
221 * @return true if project file has left path.
223 bool ProjectFile::HasLeft() const
229 * @brief Returns if middle path is defined.
231 bool ProjectFile::HasMiddle() const
237 * @brief Returns if right path is defined in project file.
238 * @return true if project file has right path.
240 bool ProjectFile::HasRight() const
246 * @brief Returns if filter is defined in project file.
247 * @return true if project file has filter.
249 bool ProjectFile::HasFilter() const
255 * @brief Returns if subfolder is defined in projectfile.
256 * @return true if project file has subfolder definition.
258 bool ProjectFile::HasSubfolders() const
260 return m_bHasSubfolders;
264 * @brief Returns left path.
265 * @param [out] pReadOnly true if readonly was specified for path.
268 String ProjectFile::GetLeft(bool * pReadOnly /*=NULL*/) const
271 *pReadOnly = m_bLeftReadOnly;
272 return m_paths.GetLeft();
276 * @brief Returns if left path is specified read-only.
277 * @return true if left path is read-only, false otherwise.
279 bool ProjectFile::GetLeftReadOnly() const
281 return m_bLeftReadOnly;
285 * @brief Set left path, returns old left path.
286 * @param [in] sLeft Left path.
287 * @param [in] bReadOnly Will path be recorded read-only?
289 void ProjectFile::SetLeft(const String& sLeft, const bool * pReadOnly /*=NULL*/)
291 m_paths.SetLeft(sLeft, false);
293 m_bLeftReadOnly = *pReadOnly;
297 * @brief Returns middle path.
298 * @param [out] pReadOnly true if readonly was specified for path.
300 String ProjectFile::GetMiddle(bool * pReadOnly /*=NULL*/) const
303 *pReadOnly = m_bMiddleReadOnly;
304 return m_paths.GetMiddle();
308 * @brief Returns if middle path is specified read-only.
310 bool ProjectFile::GetMiddleReadOnly() const
312 return m_bMiddleReadOnly;
316 * @brief Set middle path, returns old middle path.
317 * @param [in] sMiddle Middle path.
318 * @param [in] bReadOnly Will path be recorded read-only?
320 void ProjectFile::SetMiddle(const String& sMiddle, const bool * pReadOnly /*=NULL*/)
322 m_paths.SetMiddle(sMiddle, false);
324 m_bMiddleReadOnly = *pReadOnly;
330 * @brief Returns right path.
331 * @param [out] pReadOnly true if readonly was specified for path.
332 * @return Right path.
334 String ProjectFile::GetRight(bool * pReadOnly /*=NULL*/) const
337 *pReadOnly = m_bRightReadOnly;
338 return m_paths.GetRight();
342 * @brief Returns if right path is specified read-only.
343 * @return true if right path is read-only, false otherwise.
345 bool ProjectFile::GetRightReadOnly() const
347 return m_bRightReadOnly;
351 * @brief Set right path, returns old right path.
352 * @param [in] sRight Right path.
353 * @param [in] bReadOnly Will path be recorded read-only?
355 void ProjectFile::SetRight(const String& sRight, const bool * pReadOnly /*=NULL*/)
357 m_paths.SetRight(sRight, false);
359 m_bRightReadOnly = *pReadOnly;
363 * @brief Returns filter.
364 * @return Filter string.
366 String ProjectFile::GetFilter() const
373 * @param [in] sFilter New filter string to set.
375 void ProjectFile::SetFilter(const String& sFilter)
381 * @brief Returns subfolder included -setting.
382 * @return != 0 if subfolders are included.
384 int ProjectFile::GetSubfolders() const
390 * @brief set subfolder.
391 * @param [in] iSubfolder New value for subfolder inclusion.
393 void ProjectFile::SetSubfolders(int iSubfolder)
395 m_subfolders = iSubfolder ? 1 : 0;
401 * @param [in] files Files in project
402 * @param [in] bSubFolders If true subfolders included (recursive compare)
404 void ProjectFile::SetPaths(const PathContext& files, bool bSubfolders)
407 m_subfolders = bSubfolders;
411 * @brief Returns left and right paths and recursive from project file
413 * @param [out] files Files in project
414 * @param [out] bSubFolders If true subfolders included (recursive compare)
416 void ProjectFile::GetPaths(PathContext& files, bool & bSubfolders) const
420 bSubfolders = (GetSubfolders() == 1);