| // Copyright 2017 PDFium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com |
| |
| #include "xfa/fde/xml/cfde_xmlnode.h" |
| |
| #include <vector> |
| |
| #include "third_party/base/stl_util.h" |
| #include "xfa/fde/xml/cfde_xmlchardata.h" |
| #include "xfa/fde/xml/cfde_xmlelement.h" |
| #include "xfa/fde/xml/cfde_xmlinstruction.h" |
| #include "xfa/fde/xml/cfde_xmltext.h" |
| #include "xfa/fgas/crt/fgas_codepage.h" |
| |
| CFDE_XMLNode::CFDE_XMLNode() |
| : m_pParent(nullptr), |
| m_pChild(nullptr), |
| m_pPrior(nullptr), |
| m_pNext(nullptr) {} |
| |
| FDE_XMLNODETYPE CFDE_XMLNode::GetType() const { |
| return FDE_XMLNODE_Unknown; |
| } |
| |
| CFDE_XMLNode::~CFDE_XMLNode() { |
| DeleteChildren(); |
| } |
| |
| void CFDE_XMLNode::DeleteChildren() { |
| CFDE_XMLNode* pChild = m_pChild; |
| while (pChild) { |
| CFDE_XMLNode* pNext = pChild->m_pNext; |
| delete pChild; |
| pChild = pNext; |
| } |
| m_pChild = nullptr; |
| } |
| |
| int32_t CFDE_XMLNode::CountChildNodes() const { |
| int32_t iCount = 0; |
| CFDE_XMLNode* pChild = m_pChild; |
| while (pChild) { |
| iCount++; |
| pChild = pChild->m_pNext; |
| } |
| return iCount; |
| } |
| |
| CFDE_XMLNode* CFDE_XMLNode::GetChildNode(int32_t index) const { |
| CFDE_XMLNode* pChild = m_pChild; |
| while (pChild) { |
| if (index == 0) { |
| return pChild; |
| } |
| index--; |
| pChild = pChild->m_pNext; |
| } |
| return nullptr; |
| } |
| |
| int32_t CFDE_XMLNode::GetChildNodeIndex(CFDE_XMLNode* pNode) const { |
| int32_t index = 0; |
| CFDE_XMLNode* pChild = m_pChild; |
| while (pChild) { |
| if (pChild == pNode) { |
| return index; |
| } |
| index++; |
| pChild = pChild->m_pNext; |
| } |
| return -1; |
| } |
| |
| CFDE_XMLNode* CFDE_XMLNode::GetPath(const wchar_t* pPath, |
| int32_t iLength, |
| bool bQualifiedName) const { |
| ASSERT(pPath); |
| if (iLength < 0) { |
| iLength = FXSYS_wcslen(pPath); |
| } |
| if (iLength == 0) { |
| return nullptr; |
| } |
| CFX_WideString csPath; |
| const wchar_t* pStart = pPath; |
| const wchar_t* pEnd = pPath + iLength; |
| wchar_t ch; |
| while (pStart < pEnd) { |
| ch = *pStart++; |
| if (ch == L'/') { |
| break; |
| } else { |
| csPath += ch; |
| } |
| } |
| iLength -= pStart - pPath; |
| CFDE_XMLNode* pFind = nullptr; |
| if (csPath.GetLength() < 1) { |
| pFind = GetNodeItem(CFDE_XMLNode::Root); |
| } else if (csPath.Compare(L"..") == 0) { |
| pFind = m_pParent; |
| } else if (csPath.Compare(L".") == 0) { |
| pFind = (CFDE_XMLNode*)this; |
| } else { |
| CFX_WideString wsTag; |
| CFDE_XMLNode* pNode = m_pChild; |
| while (pNode) { |
| if (pNode->GetType() == FDE_XMLNODE_Element) { |
| if (bQualifiedName) { |
| ((CFDE_XMLElement*)pNode)->GetTagName(wsTag); |
| } else { |
| ((CFDE_XMLElement*)pNode)->GetLocalTagName(wsTag); |
| } |
| if (wsTag.Compare(csPath) == 0) { |
| if (iLength < 1) { |
| pFind = pNode; |
| } else { |
| pFind = pNode->GetPath(pStart, iLength, bQualifiedName); |
| } |
| if (pFind) |
| return pFind; |
| } |
| } |
| pNode = pNode->m_pNext; |
| } |
| } |
| if (!pFind || iLength < 1) |
| return pFind; |
| return pFind->GetPath(pStart, iLength, bQualifiedName); |
| } |
| |
| int32_t CFDE_XMLNode::InsertChildNode(CFDE_XMLNode* pNode, int32_t index) { |
| pNode->m_pParent = this; |
| if (!m_pChild) { |
| m_pChild = pNode; |
| pNode->m_pPrior = nullptr; |
| pNode->m_pNext = nullptr; |
| return 0; |
| } |
| if (index == 0) { |
| pNode->m_pNext = m_pChild; |
| pNode->m_pPrior = nullptr; |
| m_pChild->m_pPrior = pNode; |
| m_pChild = pNode; |
| return 0; |
| } |
| int32_t iCount = 0; |
| CFDE_XMLNode* pFind = m_pChild; |
| while (++iCount != index && pFind->m_pNext) { |
| pFind = pFind->m_pNext; |
| } |
| pNode->m_pPrior = pFind; |
| pNode->m_pNext = pFind->m_pNext; |
| if (pFind->m_pNext) |
| pFind->m_pNext->m_pPrior = pNode; |
| pFind->m_pNext = pNode; |
| return iCount; |
| } |
| |
| void CFDE_XMLNode::RemoveChildNode(CFDE_XMLNode* pNode) { |
| ASSERT(m_pChild && pNode); |
| if (m_pChild == pNode) { |
| m_pChild = pNode->m_pNext; |
| } else { |
| pNode->m_pPrior->m_pNext = pNode->m_pNext; |
| } |
| if (pNode->m_pNext) |
| pNode->m_pNext->m_pPrior = pNode->m_pPrior; |
| pNode->m_pParent = nullptr; |
| pNode->m_pNext = nullptr; |
| pNode->m_pPrior = nullptr; |
| } |
| |
| CFDE_XMLNode* CFDE_XMLNode::GetNodeItem(CFDE_XMLNode::NodeItem eItem) const { |
| switch (eItem) { |
| case CFDE_XMLNode::Root: { |
| CFDE_XMLNode* pParent = (CFDE_XMLNode*)this; |
| while (pParent->m_pParent) { |
| pParent = pParent->m_pParent; |
| } |
| return pParent; |
| } |
| case CFDE_XMLNode::Parent: |
| return m_pParent; |
| case CFDE_XMLNode::FirstSibling: { |
| CFDE_XMLNode* pItem = (CFDE_XMLNode*)this; |
| while (pItem->m_pPrior) { |
| pItem = pItem->m_pPrior; |
| } |
| return pItem == (CFDE_XMLNode*)this ? nullptr : pItem; |
| } |
| case CFDE_XMLNode::PriorSibling: |
| return m_pPrior; |
| case CFDE_XMLNode::NextSibling: |
| return m_pNext; |
| case CFDE_XMLNode::LastSibling: { |
| CFDE_XMLNode* pItem = (CFDE_XMLNode*)this; |
| while (pItem->m_pNext) |
| pItem = pItem->m_pNext; |
| return pItem == (CFDE_XMLNode*)this ? nullptr : pItem; |
| } |
| case CFDE_XMLNode::FirstNeighbor: { |
| CFDE_XMLNode* pParent = (CFDE_XMLNode*)this; |
| while (pParent->m_pParent) |
| pParent = pParent->m_pParent; |
| return pParent == (CFDE_XMLNode*)this ? nullptr : pParent; |
| } |
| case CFDE_XMLNode::PriorNeighbor: { |
| if (!m_pPrior) |
| return m_pParent; |
| |
| CFDE_XMLNode* pItem = m_pPrior; |
| while (pItem->m_pChild) { |
| pItem = pItem->m_pChild; |
| while (pItem->m_pNext) |
| pItem = pItem->m_pNext; |
| } |
| return pItem; |
| } |
| case CFDE_XMLNode::NextNeighbor: { |
| if (m_pChild) |
| return m_pChild; |
| if (m_pNext) |
| return m_pNext; |
| CFDE_XMLNode* pItem = m_pParent; |
| while (pItem) { |
| if (pItem->m_pNext) |
| return pItem->m_pNext; |
| pItem = pItem->m_pParent; |
| } |
| return nullptr; |
| } |
| case CFDE_XMLNode::LastNeighbor: { |
| CFDE_XMLNode* pItem = (CFDE_XMLNode*)this; |
| while (pItem->m_pParent) { |
| pItem = pItem->m_pParent; |
| } |
| while (true) { |
| while (pItem->m_pNext) |
| pItem = pItem->m_pNext; |
| if (!pItem->m_pChild) |
| break; |
| pItem = pItem->m_pChild; |
| } |
| return pItem == (CFDE_XMLNode*)this ? nullptr : pItem; |
| } |
| case CFDE_XMLNode::FirstChild: |
| return m_pChild; |
| case CFDE_XMLNode::LastChild: { |
| if (!m_pChild) |
| return nullptr; |
| |
| CFDE_XMLNode* pChild = m_pChild; |
| while (pChild->m_pNext) |
| pChild = pChild->m_pNext; |
| return pChild; |
| } |
| default: |
| break; |
| } |
| return nullptr; |
| } |
| |
| int32_t CFDE_XMLNode::GetNodeLevel() const { |
| int32_t iLevel = 0; |
| const CFDE_XMLNode* pItem = m_pParent; |
| while (pItem) { |
| iLevel++; |
| pItem = pItem->m_pParent; |
| } |
| return iLevel; |
| } |
| |
| bool CFDE_XMLNode::InsertNodeItem(CFDE_XMLNode::NodeItem eItem, |
| CFDE_XMLNode* pNode) { |
| switch (eItem) { |
| case CFDE_XMLNode::NextSibling: { |
| pNode->m_pParent = m_pParent; |
| pNode->m_pNext = m_pNext; |
| pNode->m_pPrior = this; |
| if (m_pNext) { |
| m_pNext->m_pPrior = pNode; |
| } |
| m_pNext = pNode; |
| return true; |
| } |
| case CFDE_XMLNode::PriorSibling: { |
| pNode->m_pParent = m_pParent; |
| pNode->m_pNext = this; |
| pNode->m_pPrior = m_pPrior; |
| if (m_pPrior) { |
| m_pPrior->m_pNext = pNode; |
| } else if (m_pParent) { |
| m_pParent->m_pChild = pNode; |
| } |
| m_pPrior = pNode; |
| return true; |
| } |
| default: |
| return false; |
| } |
| } |
| |
| CFDE_XMLNode* CFDE_XMLNode::RemoveNodeItem(CFDE_XMLNode::NodeItem eItem) { |
| CFDE_XMLNode* pNode = nullptr; |
| switch (eItem) { |
| case CFDE_XMLNode::NextSibling: |
| if (m_pNext) { |
| pNode = m_pNext; |
| m_pNext = pNode->m_pNext; |
| if (m_pNext) { |
| m_pNext->m_pPrior = this; |
| } |
| pNode->m_pParent = nullptr; |
| pNode->m_pNext = nullptr; |
| pNode->m_pPrior = nullptr; |
| } |
| break; |
| default: |
| break; |
| } |
| return pNode; |
| } |
| |
| CFDE_XMLNode* CFDE_XMLNode::Clone(bool bRecursive) { |
| return nullptr; |
| } |
| |
| void CFDE_XMLNode::SaveXMLNode(const CFX_RetainPtr<IFGAS_Stream>& pXMLStream) { |
| CFDE_XMLNode* pNode = (CFDE_XMLNode*)this; |
| switch (pNode->GetType()) { |
| case FDE_XMLNODE_Instruction: { |
| CFX_WideString ws; |
| CFDE_XMLInstruction* pInstruction = (CFDE_XMLInstruction*)pNode; |
| if (pInstruction->m_wsTarget.CompareNoCase(L"xml") == 0) { |
| ws = L"<?xml version=\"1.0\" encoding=\""; |
| uint16_t wCodePage = pXMLStream->GetCodePage(); |
| if (wCodePage == FX_CODEPAGE_UTF16LE) { |
| ws += L"UTF-16"; |
| } else if (wCodePage == FX_CODEPAGE_UTF16BE) { |
| ws += L"UTF-16be"; |
| } else { |
| ws += L"UTF-8"; |
| } |
| ws += L"\"?>"; |
| pXMLStream->WriteString(ws.c_str(), ws.GetLength()); |
| } else { |
| ws.Format(L"<?%s", pInstruction->m_wsTarget.c_str()); |
| pXMLStream->WriteString(ws.c_str(), ws.GetLength()); |
| std::vector<CFX_WideString>& attributes = pInstruction->m_Attributes; |
| int32_t i; |
| int32_t iCount = pdfium::CollectionSize<int32_t>(attributes); |
| CFX_WideString wsValue; |
| for (i = 0; i < iCount; i += 2) { |
| ws = L" "; |
| ws += attributes[i]; |
| ws += L"=\""; |
| wsValue = attributes[i + 1]; |
| wsValue.Replace(L"&", L"&"); |
| wsValue.Replace(L"<", L"<"); |
| wsValue.Replace(L">", L">"); |
| wsValue.Replace(L"\'", L"'"); |
| wsValue.Replace(L"\"", L"""); |
| ws += wsValue; |
| ws += L"\""; |
| pXMLStream->WriteString(ws.c_str(), ws.GetLength()); |
| } |
| std::vector<CFX_WideString>& targetdata = pInstruction->m_TargetData; |
| iCount = pdfium::CollectionSize<int32_t>(targetdata); |
| for (i = 0; i < iCount; i++) { |
| ws = L" \""; |
| ws += targetdata[i]; |
| ws += L"\""; |
| pXMLStream->WriteString(ws.c_str(), ws.GetLength()); |
| } |
| ws = L"?>"; |
| pXMLStream->WriteString(ws.c_str(), ws.GetLength()); |
| } |
| } break; |
| case FDE_XMLNODE_Element: { |
| CFX_WideString ws; |
| ws = L"<"; |
| ws += ((CFDE_XMLElement*)pNode)->m_wsTag; |
| pXMLStream->WriteString(ws.c_str(), ws.GetLength()); |
| std::vector<CFX_WideString>& attributes = |
| static_cast<CFDE_XMLElement*>(pNode)->m_Attributes; |
| int32_t iCount = pdfium::CollectionSize<int32_t>(attributes); |
| CFX_WideString wsValue; |
| for (int32_t i = 0; i < iCount; i += 2) { |
| ws = L" "; |
| ws += attributes[i]; |
| ws += L"=\""; |
| wsValue = attributes[i + 1]; |
| wsValue.Replace(L"&", L"&"); |
| wsValue.Replace(L"<", L"<"); |
| wsValue.Replace(L">", L">"); |
| wsValue.Replace(L"\'", L"'"); |
| wsValue.Replace(L"\"", L"""); |
| ws += wsValue; |
| ws += L"\""; |
| pXMLStream->WriteString(ws.c_str(), ws.GetLength()); |
| } |
| if (pNode->m_pChild) { |
| ws = L"\n>"; |
| pXMLStream->WriteString(ws.c_str(), ws.GetLength()); |
| CFDE_XMLNode* pChild = pNode->m_pChild; |
| while (pChild) { |
| pChild->SaveXMLNode(pXMLStream); |
| pChild = pChild->m_pNext; |
| } |
| ws = L"</"; |
| ws += ((CFDE_XMLElement*)pNode)->m_wsTag; |
| ws += L"\n>"; |
| } else { |
| ws = L"\n/>"; |
| } |
| pXMLStream->WriteString(ws.c_str(), ws.GetLength()); |
| } break; |
| case FDE_XMLNODE_Text: { |
| CFX_WideString ws = ((CFDE_XMLText*)pNode)->m_wsText; |
| ws.Replace(L"&", L"&"); |
| ws.Replace(L"<", L"<"); |
| ws.Replace(L">", L">"); |
| ws.Replace(L"\'", L"'"); |
| ws.Replace(L"\"", L"""); |
| pXMLStream->WriteString(ws.c_str(), ws.GetLength()); |
| } break; |
| case FDE_XMLNODE_CharData: { |
| CFX_WideString ws = L"<![CDATA["; |
| ws += ((CFDE_XMLCharData*)pNode)->m_wsCharData; |
| ws += L"]]>"; |
| pXMLStream->WriteString(ws.c_str(), ws.GetLength()); |
| } break; |
| case FDE_XMLNODE_Unknown: |
| break; |
| default: |
| break; |
| } |
| } |
| |
| void CFDE_XMLNode::CloneChildren(CFDE_XMLNode* pClone) { |
| if (!m_pChild) { |
| return; |
| } |
| CFDE_XMLNode* pNext = m_pChild; |
| CFDE_XMLNode* pCloneNext = pNext->Clone(true); |
| pClone->InsertChildNode(pCloneNext); |
| pNext = pNext->m_pNext; |
| while (pNext) { |
| CFDE_XMLNode* pChild = pNext->Clone(true); |
| pCloneNext->InsertNodeItem(CFDE_XMLNode::NextSibling, pChild); |
| pCloneNext = pChild; |
| pNext = pNext->m_pNext; |
| } |
| } |