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.
24 #include "ProjectFile.h"
27 #include <Poco/FileStream.h>
28 #include <Poco/XML/XMLWriter.h>
29 #include <Poco/SAX/SAXParser.h>
30 #include <Poco/SAX/ContentHandler.h>
31 #include <Poco/Exception.h>
32 #include "UnicodeString.h"
35 using Poco::FileStream;
36 using Poco::XML::SAXParser;
37 using Poco::XML::ContentHandler;
38 using Poco::XML::Locator;
39 using Poco::XML::XMLWriter;
40 using Poco::XML::XMLChar;
41 using Poco::XML::XMLString;
42 using Poco::XML::Attributes;
43 using Poco::Exception;
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 Middle_element_name[] = "middle";
52 const char Right_element_name[] = "right";
53 const char Filter_element_name[] = "filter";
54 const char Subfolders_element_name[] = "subfolders";
55 const char Left_ro_element_name[] = "left-readonly";
56 const char Middle_ro_element_name[] = "middle-readonly";
57 const char Right_ro_element_name[] = "right-readonly";
62 String xmlch2tstr(const XMLChar *ch, int length)
64 return toTString(std::string(ch, length));
67 void writeElement(XMLWriter& writer, const std::string& tagname, const std::string& characters)
69 writer.startElement("", "", tagname);
70 writer.characters(characters);
71 writer.endElement("", "", tagname);
76 class ProjectFileHandler: public ContentHandler
79 explicit ProjectFileHandler(ProjectFile *pProject) : m_pProject(pProject) {}
81 void setDocumentLocator(const Locator* loc) {}
82 void startDocument() {}
84 void startElement(const XMLString& uri, const XMLString& localName, const XMLString& qname, const Attributes& attributes)
86 m_stack.push(localName);
88 void endElement(const XMLString& uri, const XMLString& localName, const XMLString& qname)
92 void characters(const XMLChar ch[], int start, int length)
94 if (m_stack.size() != 3)
97 const std::string& nodename = m_stack.top();
98 if (nodename == Left_element_name)
100 m_pProject->m_paths.SetLeft(m_pProject->m_paths.GetLeft() + xmlch2tstr(ch + start, length), false);
101 m_pProject->m_bHasLeft = true;
103 else if (nodename == Middle_element_name)
105 m_pProject->m_paths.SetMiddle(m_pProject->m_paths.GetMiddle() + xmlch2tstr(ch + start, length), false);
106 m_pProject->m_bHasMiddle = true;
108 else if (nodename == Right_element_name)
110 m_pProject->m_paths.SetRight(m_pProject->m_paths.GetRight() + xmlch2tstr(ch + start, length), false);
111 m_pProject->m_bHasRight = true;
113 else if (nodename == Filter_element_name)
115 m_pProject->m_filter += xmlch2tstr(ch + start, length);
116 m_pProject->m_bHasFilter = true;
118 else if (nodename == Subfolders_element_name)
120 m_pProject->m_subfolders = atoi(std::string(ch + start, length).c_str());
121 m_pProject->m_bHasSubfolders = true;
123 else if (nodename == Left_ro_element_name)
125 m_pProject->m_bLeftReadOnly = atoi(std::string(ch + start, length).c_str()) != 0;
127 else if (nodename == Middle_ro_element_name)
129 m_pProject->m_bMiddleReadOnly = atoi(std::string(ch + start, length).c_str()) != 0;
131 else if (nodename == Right_ro_element_name)
133 m_pProject->m_bRightReadOnly = atoi(std::string(ch + start, length).c_str()) != 0;
136 void ignorableWhitespace(const XMLChar ch[], int start, int length) {}
137 void processingInstruction(const XMLString& target, const XMLString& data) {}
138 void startPrefixMapping(const XMLString& prefix, const XMLString& uri) {}
139 void endPrefixMapping(const XMLString& prefix) {}
140 void skippedEntity(const XMLString& name) {}
143 ProjectFile *m_pProject;
144 std::stack<std::string> m_stack;
147 /** @brief File extension for path files */
148 const String ProjectFile::PROJECTFILE_EXT = toTString("WinMerge");
151 * @brief Standard constructor.
153 ProjectFile::ProjectFile()
155 , m_bHasMiddle(false)
157 , m_bHasFilter(false)
158 , m_bHasSubfolders(false)
160 , m_bLeftReadOnly(false)
161 , m_bMiddleReadOnly(false)
162 , m_bRightReadOnly(false)
167 * @brief Open given path-file and read data from it to member variables.
168 * @param [in] path Path to project file.
169 * @param [out] sError Error string if error happened.
170 * @return true if reading succeeded, false if error happened.
172 bool ProjectFile::Read(const String& path)
174 ProjectFileHandler handler(this);
176 parser.setContentHandler(&handler);
177 parser.parse(toUTF8(path));
182 * @brief Save data from member variables to path-file.
183 * @param [in] path Path to project file.
184 * @param [out] sError Error string if error happened.
185 * @return true if saving succeeded, false if error happened.
187 bool ProjectFile::Save(const String& path) const
189 FileStream out(toUTF8(path), FileStream::trunc);
190 XMLWriter writer(out, XMLWriter::WRITE_XML_DECLARATION | XMLWriter::PRETTY_PRINT);
191 writer.startDocument();
192 writer.startElement("", "", Root_element_name);
194 writer.startElement("", "", Paths_element_name);
196 if (!m_paths.GetLeft().empty())
197 writeElement(writer, Left_element_name, toUTF8(m_paths.GetLeft()));
198 if (!m_paths.GetMiddle().empty())
199 writeElement(writer, Middle_element_name, toUTF8(m_paths.GetMiddle()));
200 if (!m_paths.GetRight().empty())
201 writeElement(writer, Right_element_name, toUTF8(m_paths.GetRight()));
202 if (!m_filter.empty())
203 writeElement(writer, Filter_element_name, toUTF8(m_filter));
204 writeElement(writer, Subfolders_element_name, m_subfolders != 0 ? "1" : "0");
205 writeElement(writer, Left_ro_element_name, m_bLeftReadOnly ? "1" : "0");
206 if (!m_paths.GetMiddle().empty())
207 writeElement(writer, Middle_ro_element_name, m_bMiddleReadOnly ? "1" : "0");
208 writeElement(writer, Right_ro_element_name, m_bRightReadOnly ? "1" : "0");
210 writer.endElement("", "", Paths_element_name);
212 writer.endElement("", "", Root_element_name);
213 writer.endDocument();
218 * @brief Returns if left path is defined in project file.
219 * @return true if project file has left path.
221 bool ProjectFile::HasLeft() const
227 * @brief Returns if middle path is defined.
229 bool ProjectFile::HasMiddle() const
235 * @brief Returns if right path is defined in project file.
236 * @return true if project file has right path.
238 bool ProjectFile::HasRight() const
244 * @brief Returns if filter is defined in project file.
245 * @return true if project file has filter.
247 bool ProjectFile::HasFilter() const
253 * @brief Returns if subfolder is defined in projectfile.
254 * @return true if project file has subfolder definition.
256 bool ProjectFile::HasSubfolders() const
258 return m_bHasSubfolders;
262 * @brief Returns left path.
263 * @param [out] pReadOnly true if readonly was specified for path.
266 String ProjectFile::GetLeft(bool * pReadOnly /*= nullptr*/) const
268 if (pReadOnly != nullptr)
269 *pReadOnly = m_bLeftReadOnly;
270 return m_paths.GetLeft();
274 * @brief Returns if left path is specified read-only.
275 * @return true if left path is read-only, false otherwise.
277 bool ProjectFile::GetLeftReadOnly() const
279 return m_bLeftReadOnly;
283 * @brief Set left path, returns old left path.
284 * @param [in] sLeft Left path.
285 * @param [in] bReadOnly Will path be recorded read-only?
287 void ProjectFile::SetLeft(const String& sLeft, const bool * pReadOnly /*= nullptr*/)
289 m_paths.SetLeft(sLeft, false);
290 if (pReadOnly != nullptr)
291 m_bLeftReadOnly = *pReadOnly;
295 * @brief Returns middle path.
296 * @param [out] pReadOnly true if readonly was specified for path.
298 String ProjectFile::GetMiddle(bool * pReadOnly /*= nullptr*/) const
300 if (pReadOnly != nullptr)
301 *pReadOnly = m_bMiddleReadOnly;
302 return m_paths.GetMiddle();
306 * @brief Returns if middle path is specified read-only.
308 bool ProjectFile::GetMiddleReadOnly() const
310 return m_bMiddleReadOnly;
314 * @brief Set middle path, returns old middle path.
315 * @param [in] sMiddle Middle path.
316 * @param [in] bReadOnly Will path be recorded read-only?
318 void ProjectFile::SetMiddle(const String& sMiddle, const bool * pReadOnly /*= nullptr*/)
320 m_paths.SetMiddle(sMiddle, false);
321 if (pReadOnly != nullptr)
322 m_bMiddleReadOnly = *pReadOnly;
328 * @brief Returns right path.
329 * @param [out] pReadOnly true if readonly was specified for path.
330 * @return Right path.
332 String ProjectFile::GetRight(bool * pReadOnly /*= nullptr*/) const
334 if (pReadOnly != nullptr)
335 *pReadOnly = m_bRightReadOnly;
336 return m_paths.GetRight();
340 * @brief Returns if right path is specified read-only.
341 * @return true if right path is read-only, false otherwise.
343 bool ProjectFile::GetRightReadOnly() const
345 return m_bRightReadOnly;
349 * @brief Set right path, returns old right path.
350 * @param [in] sRight Right path.
351 * @param [in] bReadOnly Will path be recorded read-only?
353 void ProjectFile::SetRight(const String& sRight, const bool * pReadOnly /*= nullptr*/)
355 m_paths.SetRight(sRight, false);
356 if (pReadOnly != nullptr)
357 m_bRightReadOnly = *pReadOnly;
361 * @brief Returns filter.
362 * @return Filter string.
364 String ProjectFile::GetFilter() const
371 * @param [in] sFilter New filter string to set.
373 void ProjectFile::SetFilter(const String& sFilter)
379 * @brief Returns subfolder included -setting.
380 * @return != 0 if subfolders are included.
382 int ProjectFile::GetSubfolders() const
388 * @brief set subfolder.
389 * @param [in] iSubfolder New value for subfolder inclusion.
391 void ProjectFile::SetSubfolders(bool bSubfolder)
393 m_subfolders = bSubfolder ? 1 : 0;
399 * @param [in] files Files in project
400 * @param [in] bSubFolders If true subfolders included (recursive compare)
402 void ProjectFile::SetPaths(const PathContext& files, bool bSubfolders)
405 m_subfolders = bSubfolders;
409 * @brief Returns left and right paths and recursive from project file
411 * @param [out] files Files in project
412 * @param [out] bSubFolders If true subfolders included (recursive compare)
414 void ProjectFile::GetPaths(PathContext& files, bool & bSubfolders) const
418 bSubfolders = (GetSubfolders() == 1);