OSDN Git Service

PATCH: [ 1400653 ] Write project files using objects
authorKimmo Varis <kimmov@gmail.com>
Thu, 20 Apr 2006 16:34:25 +0000 (16:34 +0000)
committerKimmo Varis <kimmov@gmail.com>
Thu, 20 Apr 2006 16:34:25 +0000 (16:34 +0000)
 - submitted by Perry

Src/Changes.txt
Src/Merge.dsp
Src/ProjectFile.cpp
Src/ProjectFile.h
Src/XmlDoc.cpp [new file with mode: 0644]
Src/XmlDoc.h [new file with mode: 0644]

index 0b096f2..477b49d 100644 (file)
@@ -8,6 +8,10 @@ Add new items to top.
   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
index 3b5e4de..7051fec 100644 (file)
@@ -915,6 +915,10 @@ SOURCE=.\Common\WindowStyle.cpp
 
 SOURCE=.\WMGotoDlg.cpp
 # End Source File
+# Begin Source File
+
+SOURCE=.\XmlDoc.cpp
+# End Source File
 # End Group
 # Begin Group "Header Files"
 
@@ -1479,6 +1483,10 @@ SOURCE=.\Common\WindowStyle.h
 
 SOURCE=.\WMGotoDlg.h
 # End Source File
+# Begin Source File
+
+SOURCE=.\XmlDoc.h
+# End Source File
 # End Group
 # Begin Group "Resource Files"
 
index b0f23e5..cbb9e37 100755 (executable)
@@ -24,7 +24,7 @@
 
 #include "stdafx.h"
 #include "ProjectFile.h"
-#include "markdown.h"
+#include "XmlDoc.h"
 
 ProjectFile::ProjectFile()
 {
@@ -34,18 +34,15 @@ 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;
@@ -53,36 +50,10 @@ static BOOL NTAPI False(CException *e, CString *sError)
 
 /** 
  * @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);
 }
 
 /** 
@@ -91,29 +62,38 @@ BOOL ProjectFile::Read(LPCTSTR path, CString *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)
        {
index deddbdc..e7e21c3 100755 (executable)
@@ -55,6 +55,7 @@ public:
        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);
 
diff --git a/Src/XmlDoc.cpp b/Src/XmlDoc.cpp
new file mode 100644 (file)
index 0000000..5ec8a99
--- /dev/null
@@ -0,0 +1,295 @@
+/**
+ * @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;
+}
+
+
+
diff --git a/Src/XmlDoc.h b/Src/XmlDoc.h
new file mode 100644 (file)
index 0000000..1514313
--- /dev/null
@@ -0,0 +1,118 @@
+/**
+ * @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
+