Src: DirView.cpp Merge.rc resource.h
Src/res new file: aborted.ico
Src/Languages/*: Merge*.rc
+ PATCH: [ 1400653 ] Write project files using objects
+ Submitted by Perry
+ Src: Merge.dsp ProjectFile.cpp ProjectFile.h
+ Src new files: XmlDoc.cpp XmlDoc.h
2006-04-18 Kimmo
PATCH: [ 1471835 ] Fix setting filename in folder-compare report dialog
SOURCE=.\WMGotoDlg.cpp
# End Source File
+# Begin Source File
+
+SOURCE=.\XmlDoc.cpp
+# End Source File
# End Group
# Begin Group "Header Files"
SOURCE=.\WMGotoDlg.h
# End Source File
+# Begin Source File
+
+SOURCE=.\XmlDoc.h
+# End Source File
# End Group
# Begin Group "Resource Files"
#include "stdafx.h"
#include "ProjectFile.h"
-#include "markdown.h"
+#include "XmlDoc.h"
ProjectFile::ProjectFile()
{
/**
* @brief Get message from exception into sError, or else throw it.
*
- * If caller provided the address of an error string (sError),
- * this populates the error string (if possible) and returns FALSE
- *
- * If caller did not provide the address of an error string (sError==NULL)
- * this rethrows the error
+ * If this successfully extracts the error description into the string, it simply returns FALSE
+ * If it fails to extract the error description, it rethrows the exception
*/
static BOOL NTAPI False(CException *e, CString *sError)
{
if (sError == NULL)
throw e;
- TCHAR szError[4096] = _T("");
- e->GetErrorMessage(szError, sizeof(szError)/sizeof(szError[0]));
+ TCHAR szError[1024];
+ e->GetErrorMessage(szError, 1024);
*sError = szError;
e->Delete();
return FALSE;
/**
* @brief Open given path-file and read data from it to member variables.
- *
- * Errors are returned in sError, unless it is NULL, in which case they are thrown
*/
BOOL ProjectFile::Read(LPCTSTR path, CString *sError)
{
- try
- {
- CMarkdown::EntityMap entities;
- entities.Load();
- CMarkdown::File xmlfile = path;
- if (xmlfile.pImage == NULL)
- {
- CFileException::ThrowOsError(GetLastError(), path);
- }
- // If encoding is other than UTF-8, assume CP_ACP
- CMarkdown::String encoding = CMarkdown(xmlfile).Move("?xml").GetAttribute("encoding");
- UINT codepage = lstrcmpiA(encoding.A, "UTF-8") == 0 ? CP_UTF8 : CP_ACP;
-
- CMarkdown project = CMarkdown(xmlfile).Move("project").Pop();
- CMarkdown paths = CMarkdown(project).Move("paths").Pop();
- m_leftFile = CMarkdown::String(CMarkdown(paths).Move("left").GetInnerText()->Unicode(codepage)->Resolve(entities)).W;
- m_rightFile = CMarkdown::String(CMarkdown(paths).Move("right").GetInnerText()->Unicode(codepage)->Resolve(entities)).W;
- m_filter = CMarkdown::String(CMarkdown(paths).Move("filter").GetInnerText()->Unicode(codepage)->Resolve(entities)).W;
- sscanf(CMarkdown::String(CMarkdown(paths).Move("subfolders").GetInnerText()).A, "%d", &m_subfolders);
- }
- catch (CException *e)
- {
- return False(e, sError);
- }
- return TRUE;
+ return Serialize(false, path, sError);
}
/**
*/
BOOL ProjectFile::Save(LPCTSTR path, CString *sError)
{
+ return Serialize(true, path, sError);
+}
+
+
+/**
+ * @brief Read or write project file
+ */
+BOOL ProjectFile::Serialize(bool writing, LPCTSTR path, CString *sError)
+{
try
{
- static const char szFormat[]
- (
- "<?xml version='1.0' encoding='UTF-8'?>\n"
- "<project>\n"
- "\t<paths>\n"
- "\t\t<left>%s</left>\n"
- "\t\t<right>%s</right>\n"
- "\t\t<filter>%s</filter>\n"
- "\t\t<subfolders>%d</subfolders>\n"
- "\t</paths>\n"
- "</project>\n"
- );
- fprintf
- (
- CStdioFile(path, CFile::modeCreate|CFile::modeWrite|CFile::typeText).m_pStream,
- szFormat,
- CMarkdown::String(CMarkdown::HSTR(GetLeft().AllocSysString())->Entities()->Octets(CP_UTF8)).A,
- CMarkdown::String(CMarkdown::HSTR(GetRight().AllocSysString())->Entities()->Octets(CP_UTF8)).A,
- CMarkdown::String(CMarkdown::HSTR(GetFilter().AllocSysString())->Entities()->Octets(CP_UTF8)).A,
- GetSubfolders() ? 1 : 0
- );
+ XmlDoc::XML_LOADSAVE loadSave = (writing ? XmlDoc::XML_SAVE : XmlDoc::XML_LOAD);
+
+ XmlDoc doc(path, loadSave, _T("UTF-8"));
+ doc.Begin();
+ {
+ XmlElement project(doc, _T("project"));
+ {
+ XmlElement paths(doc, _T("paths"));
+ {
+ XmlElement(doc, _T("left"), m_leftFile);
+ } {
+ XmlElement(doc, _T("right"), m_rightFile);
+ } {
+ XmlElement(doc, _T("filter"), m_filter);
+ } {
+ XmlElement(doc, _T("subfolders"), m_subfolders);
+ }
+ }
+ }
+ doc.End();
+
}
catch (CException *e)
{
void GetPaths(CString & sLeft, CString & sRight, BOOL & bSubFolders) const;
protected:
+ BOOL Serialize(bool writing, LPCTSTR path, CString *sError);
BOOL GetVal(TCHAR *pPaths, TCHAR *pVal, CString * sval,
TCHAR *ptag1, TCHAR *ptag2, TCHAR *pbuf);
--- /dev/null
+/**
+ * @file XmlDoc.cpp
+ *
+ * @brief Implementation of the XmlDoc and XmlElement classes.
+ */
+// RCS ID line follows -- this is updated by CVS
+// $Id$
+
+#include "stdafx.h"
+#include "XmlDoc.h"
+#include "markdown.h"
+
+// Private types
+
+/**
+ * @brief Stuff you need to load an XML file
+ */
+struct XmlLoadDocData
+{
+ CMarkdown::File xmlfile;
+ CMarkdown::EntityMap entities;
+ UINT codepage;
+ XmlLoadDocData(LPCTSTR path) : xmlfile(path), codepage(-1) { }
+};
+
+/**
+ * @brief element-specific stuff used in loading an XML file
+ */
+struct XmlLoadElementData
+{
+ CMarkdown markdown;
+
+ // Have to be able to construct either from markdown of parent
+ // or from markdown file (if element with no parent, ie, root)
+ XmlLoadElementData(CMarkdown &md) : markdown(md) { }
+ XmlLoadElementData(CMarkdown::File & file) : markdown(file) { }
+};
+
+// Private functions
+
+/**
+ * @brief Convert a string into correct encoding & markdown string object
+ */
+static LPCSTR
+MakeMarkdownString(const CString & str, int codepage)
+{
+ return CMarkdown::String(CMarkdown::HSTR(str.AllocSysString())->Entities()->Octets(codepage)).A;
+}
+
+// Body of module
+
+XmlDoc::XmlDoc(LPCTSTR path, XML_LOADSAVE loadSave, LPCTSTR encoding)
+: m_path(path)
+, m_loadSave(loadSave)
+, m_live(0)
+// CString m_xml;
+// XmlElementList m_openElements;
+, m_load(NULL)
+, m_codepage(65001)
+{
+ if (m_loadSave == XML_SAVE)
+ {
+ m_xml.Format(_T("<?xml version='1.0' encoding='%s'?>"), encoding);
+ }
+ // Loading is all done in the Begin method (2 stage construction)
+
+}
+
+/**
+ * @brief 2nd stage construction of XmlDoc object (so can throw exception)
+ */
+void
+XmlDoc::Begin()
+{
+ // Saving did its (simple) initialization in the constructor
+ if (m_loadSave == XML_LOAD)
+ {
+ ASSERT(!m_load);
+ m_load = new XmlLoadDocData(m_path);
+ m_load->entities.Load();
+ if (m_load->xmlfile.pImage == NULL)
+ {
+ CFileException::ThrowOsError(GetLastError(), m_path);
+ }
+ // If encoding is other than UTF-8, assume CP_ACP
+ CMarkdown::String encoding = CMarkdown(m_load->xmlfile).Move("?xml").GetAttribute("encoding");
+ m_codepage = lstrcmpiA(encoding.A, "UTF-8") == 0 ? CP_UTF8 : CP_ACP;
+ }
+}
+
+/**
+ * @brief Finish xml document (needed to write out all XML, if saving)
+ */
+void
+XmlDoc::End()
+{
+ if (m_loadSave == XML_SAVE)
+ {
+ CString xml = GetXml();
+ CStdioFile file(m_path, CFile::modeCreate|CFile::modeWrite);
+ file.SeekToEnd();
+ file.WriteString(xml);
+ file.Close();
+ }
+}
+
+/**
+ * @brief Add some content text to XML document
+ */
+void
+XmlDoc::Append(LPCTSTR str)
+{
+ m_xml += MakeMarkdownString(str, m_codepage); // UTF-8 encoding
+}
+
+/**
+ * @brief Get all accumulated XML
+ */
+CString
+XmlDoc::GetXml() const
+{
+ // Return accumulated XML, but ensure it has trailing \n
+ CString xml = m_xml;
+ if (xml[xml.GetLength()-1] != '\n')
+ xml += '\n';
+ return xml;
+}
+
+void
+XmlDoc::StartTag(XmlElement * xel)
+{
+ LPCSTR tag = MakeMarkdownString(xel->m_tag, GetCodepage()); // UTF-8 encoding
+ xel->m_normalizedTag = tag;
+
+ if (m_loadSave == XML_SAVE)
+ {
+ // Add tabs & open tag to XML string
+ m_xml += (CString)_T("\n") + GetLeader() + _T("<") + tag + _T(">");
+ // Mark any currently open tags as multiline (for pretty printing)
+ // (as they are complex b/c they contain this tag)
+ for (POSITION pos=m_openElements.GetHeadPosition(); pos; )
+ {
+ XmlElement * xmem = m_openElements.GetNext(pos);
+ xmem->SetMultiline();
+ }
+ }
+ else // XML_LOAD)
+ {
+ ASSERT(!xel->m_load);
+ if (m_openElements.IsEmpty())
+ {
+ // Create root CMarkdown from CMarkdown::File
+ CMarkdown::File & parentFile = (m_load->xmlfile);
+ CMarkdown childmd = CMarkdown(parentFile).Move(tag).Pop();
+ xel->m_load = new XmlLoadElementData(childmd);
+ }
+ else
+ {
+ // Create child CMarkdown from parent CMarkdown
+ XmlElement * parel = m_openElements.GetTail();
+ CMarkdown & parentMd = (parel->m_load->markdown);
+ CMarkdown childMd = CMarkdown(parentMd).Move(tag);
+ xel->m_load = new XmlLoadElementData(childMd);
+ }
+ }
+ // Add this tag to list of open tags
+ m_openElements.AddTail(xel);
+}
+
+/**
+ * @brief Non-content elements call this, for CMarkdown::Pop (whatever it does)
+ */
+void
+XmlDoc::PopMe(XmlElement * xel)
+{
+ if (m_loadSave == XML_LOAD)
+ {
+ CMarkdown & myMd = xel->m_load->markdown;
+ myMd.Pop();
+ }
+}
+
+/**
+ * @brief When saving, write out end tag (at XmlElement destructor time)
+ */
+void
+XmlDoc::EndTag(XmlElement * xel)
+{
+ CString tag = xel->m_normalizedTag;
+ XmlElement * tail = m_openElements.RemoveTail();
+ ASSERT(tail == xel);
+ if (xel->isMultiline())
+ {
+ m_xml += _T("\n");
+ m_xml += GetLeader();
+ }
+ m_xml += (CString)_T("</") + tag + _T(">");
+}
+
+/**
+ * @brief (When saving) Get correct number of tabs to print before element
+ */
+CString
+XmlDoc::GetLeader() const
+{
+ CString leader;
+ for (int i=0; i<m_openElements.GetCount(); ++i)
+ leader += _T("\t");
+ return leader;
+}
+
+/**
+ * @brief Read or write some textual content at current location
+ */
+void
+XmlDoc::Content(XmlElement * xel, CString & str)
+{
+ if (m_loadSave == XML_SAVE)
+ {
+ Append(str);
+ }
+ else
+ {
+ CMarkdown::EntityMap & entities = m_load->entities;
+ CMarkdown & myMd = xel->m_load->markdown;
+ str = CMarkdown::String(myMd.GetInnerText()->Unicode(m_codepage)->Resolve(entities)).W;
+ }
+}
+
+/**
+ * @brief Read or write an int value at current location
+ */
+void
+XmlDoc::Content(XmlElement * xel, int & val)
+{
+ if (m_loadSave == XML_SAVE)
+ {
+ AppendN(val);
+ }
+ else
+ {
+ CMarkdown & myMd = xel->m_load->markdown;
+ sscanf(CMarkdown::String(myMd.GetInnerText()).A, "%d", &val);
+ }
+}
+
+/**
+ * @brief Create simple container element
+ */
+XmlElement::XmlElement(XmlDoc & doc, LPCTSTR tag)
+: m_doc(doc)
+, m_tag(tag)
+, m_multiline(false)
+, m_load(NULL)
+{
+ m_doc.StartTag(this);
+ m_doc.PopMe(this);
+}
+
+/**
+ * @brief Create simple string leaf node element
+ */
+XmlElement::XmlElement(XmlDoc & doc, LPCTSTR tag, CString & val)
+: m_doc(doc)
+, m_tag(tag)
+, m_multiline(false)
+, m_load(NULL)
+{
+ m_doc.StartTag(this);
+ m_doc.Content(this, val);
+}
+
+/**
+ * @brief Create integer string leaf node element
+ */
+XmlElement::XmlElement(XmlDoc & doc, LPCTSTR tag, int & val)
+: m_doc(doc)
+, m_tag(tag)
+, m_multiline(false)
+, m_load(NULL)
+{
+ m_doc.StartTag(this);
+ m_doc.Content(this, val);
+}
+/**
+ * brief Cleanup or memory
+ */
+XmlElement::~XmlElement()
+{
+ m_doc.EndTag(this);
+ delete m_load;
+}
+
+
+
--- /dev/null
+/**
+ * @file XmlDoc.h
+ *
+ * @brief Declaration file for XmlDoc and XmlElement classes.
+ *
+ */
+// RCS ID line follows -- this is updated by CVS
+// $Id$
+
+#ifndef XmlDoc_h_included
+#define XmlDoc_h_included
+
+class XmlElement;
+typedef CTypedPtrList<CPtrList, XmlElement*> XmlElementList;
+
+struct XmlLoadDocData;
+struct XmlLoadElementData;
+
+/**
+ * @brief Simple XML document class to ease doing XML output
+ *
+ * Sample use:
+ *
+ * XmlDoc doc(_T("C:\\somewhere\\goodstuff.xml"), XmlDoc::XML_SAVE, _T("UTF-8"));
+ * doc.Begin();
+ * {
+ * XmlElement(doc, _T("automobile"));
+ * {
+ * XmlElement(doc, _T("seats"));
+ * {
+ * XmlElement(doc, _T("front")).Append(_T("bucket"));
+ * XmlElement(doc, _T("rear")).Append(_T("bench"));
+ * }
+ * }
+ * }
+ * doc.End();
+ *
+ * Sample output:
+ *
+ * <?xml version='1.0' encoding='UTF-8'>
+ * <automobile>
+ * <seats>
+ * <front>bucket</bucket>
+ * <rear>bench</rear>
+ * </seats>
+ * </automobile>
+ */
+class XmlDoc
+{
+public:
+ typedef enum { XML_LOAD, XML_SAVE } XML_LOADSAVE;
+
+ XmlDoc(LPCTSTR path, XML_LOADSAVE loadSave, LPCTSTR encoding);
+ void Begin();
+ void End();
+
+// Implementation methods
+private:
+ CString GetXml() const;
+ int GetCodepage() const { return m_codepage; }
+
+ void Append(LPCTSTR str);
+ void AppendN(int n) { TCHAR buff[33]; m_xml += _itot(n, buff, 10); }
+
+ void Content(XmlElement * xel, CString & str);
+ void Content(XmlElement * xel, int & val);
+
+ void StartTag(XmlElement * xel);
+ void PopMe(XmlElement * xel);
+ void EndTag(XmlElement * xel);
+ CString GetLeader() const;
+
+// Implementation data
+private:
+ CString m_path;
+ XML_LOADSAVE m_loadSave;
+ int m_live;
+ CString m_xml;
+ XmlElementList m_openElements;
+ XmlLoadDocData * m_load; // hide loading implementation data inside this pointer
+ int m_codepage;
+
+friend class XmlElement;
+};
+
+/**
+ * @brief Simple XML element class to ease doing XML output
+ */
+class XmlElement
+{
+public:
+ // container
+ XmlElement(XmlDoc & doc, LPCTSTR tag);
+
+ // value
+ XmlElement(XmlDoc & doc, LPCTSTR tag, CString & val);
+ XmlElement(XmlDoc & doc, LPCTSTR tag, int & val);
+
+ ~XmlElement();
+
+// Implementation methods
+private:
+ bool isMultiline() const { return m_multiline; }
+ void SetMultiline(bool isMultiline=true) { m_multiline = isMultiline; }
+
+// Implementation data
+private:
+ XmlDoc & m_doc;
+ CString m_tag;
+ CString m_normalizedTag;
+ bool m_multiline;
+ XmlLoadElementData * m_load;
+friend XmlDoc;
+};
+
+
+#endif // XmlDoc_h_included
+