LibreOffice Module sc (master)  1
xmlcontext.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  */
9 
10 #include <orcusfiltersimpl.hxx>
11 #include <orcusinterface.hxx>
12 #include <orcusxml.hxx>
13 #include <document.hxx>
14 #include <tokenarray.hxx>
15 
16 #include <vcl/weld.hxx>
17 #include <ucbhelper/content.hxx>
18 #include <sal/log.hxx>
19 #include <osl/file.hxx>
20 
21 #include <orcus/xml_structure_tree.hpp>
22 #include <orcus/xml_namespace.hpp>
23 #include <orcus/orcus_xml.hpp>
24 #include <orcus/sax_parser_base.hpp>
25 #include <orcus/stream.hpp>
26 
27 #include <com/sun/star/io/XInputStream.hpp>
29 
30 #include <string>
31 #include <sstream>
32 
33 namespace com::sun::star::ucb { class XCommandEnvironment; }
34 
35 #define BUFFER_SIZE 4096
36 
37 using namespace com::sun::star;
38 
39 namespace {
40 
41 ScOrcusXMLTreeParam::EntryData& setUserDataToEntry(weld::TreeView& rControl,
43 {
44  rStore.push_back(std::make_unique<ScOrcusXMLTreeParam::EntryData>(eType));
45  rControl.set_id(rEntry, OUString::number(reinterpret_cast<sal_Int64>(rStore.back().get())));
46  return *rStore.back();
47 }
48 
49 void setEntityNameToUserData(
51  const orcus::xml_structure_tree::entity_name& entity, const orcus::xml_structure_tree::walker& walker)
52 {
53  rEntryData.mnNamespaceID = walker.get_xmlns_index(entity.ns);
54 }
55 
56 OUString toString(const orcus::xml_structure_tree::entity_name& entity, const orcus::xml_structure_tree::walker& walker)
57 {
58  OUStringBuffer aBuf;
59  if (entity.ns)
60  {
61  // Namespace exists. Use the short version of the xml namespace name for display.
62  std::string aShortName = walker.get_xmlns_short_name(entity.ns);
63  aBuf.appendAscii(aShortName.c_str());
64  aBuf.append(':');
65  }
66  aBuf.append(OUString(entity.name.get(), entity.name.size(), RTL_TEXTENCODING_UTF8));
67  return aBuf.makeStringAndClear();
68 }
69 
70 void populateTree(
71  weld::TreeView& rTreeCtrl, orcus::xml_structure_tree::walker& rWalker,
72  const orcus::xml_structure_tree::entity_name& rElemName, bool bRepeat,
73  const weld::TreeIter* pParent, ScOrcusXMLTreeParam& rParam)
74 {
75  OUString sEntry(toString(rElemName, rWalker));
76  std::unique_ptr<weld::TreeIter> xEntry(rTreeCtrl.make_iterator());
77  rTreeCtrl.insert(pParent, -1, &sEntry, nullptr, nullptr, nullptr, false, xEntry.get());
78  rTreeCtrl.set_image(*xEntry, rParam.maImgElementDefault, -1);
79 
80  ScOrcusXMLTreeParam::EntryData& rEntryData = setUserDataToEntry(rTreeCtrl,
81  *xEntry, rParam.m_UserDataStore,
83 
84  setEntityNameToUserData(rEntryData, rElemName, rWalker);
85 
86  if (bRepeat)
87  {
88  // Recurring elements use different icon.
89  rTreeCtrl.set_image(*xEntry, rParam.maImgElementRepeat, -1);
90  }
91 
92  orcus::xml_structure_tree::entity_names_type aNames = rWalker.get_attributes();
93 
94  // Insert attributes.
95  for (const orcus::xml_structure_tree::entity_name& rAttrName : aNames)
96  {
97  OUString sAttr(toString(rAttrName, rWalker));
98  std::unique_ptr<weld::TreeIter> xAttr(rTreeCtrl.make_iterator());
99  rTreeCtrl.insert(xEntry.get(), -1, &sAttr, nullptr, nullptr, nullptr, false, xAttr.get());
100 
101  ScOrcusXMLTreeParam::EntryData& rAttrData =
102  setUserDataToEntry(rTreeCtrl, *xAttr, rParam.m_UserDataStore, ScOrcusXMLTreeParam::Attribute);
103  setEntityNameToUserData(rAttrData, rAttrName, rWalker);
104 
105  rTreeCtrl.set_image(*xAttr, rParam.maImgAttribute, -1);
106  }
107 
108  aNames = rWalker.get_children();
109 
110  // Non-leaf if it has child elements, leaf otherwise.
111  rEntryData.mbLeafNode = aNames.empty();
112 
113  // Insert child elements recursively.
114  for (const auto& rName : aNames)
115  {
116  orcus::xml_structure_tree::element aElem = rWalker.descend(rName);
117  populateTree(rTreeCtrl, rWalker, rName, aElem.repeat, xEntry.get(), rParam);
118  rWalker.ascend();
119  }
120 }
121 
122 class TreeUpdateSwitch
123 {
124  weld::TreeView& mrTreeCtrl;
125 public:
126  explicit TreeUpdateSwitch(weld::TreeView& rTreeCtrl) : mrTreeCtrl(rTreeCtrl)
127  {
128  mrTreeCtrl.freeze();
129  }
130 
131  ~TreeUpdateSwitch()
132  {
133  mrTreeCtrl.thaw();
134  }
135 };
136 
137 void loadContentFromURL(const OUString& rURL, std::string& rStrm)
138 {
139  ucbhelper::Content aContent(
140  rURL, uno::Reference<ucb::XCommandEnvironment>(), comphelper::getProcessComponentContext());
141  uno::Reference<io::XInputStream> xStrm = aContent.openStream();
142 
143  std::ostringstream aStrmBuf;
144  uno::Sequence<sal_Int8> aBytes;
145  size_t nBytesRead = 0;
146  do
147  {
148  nBytesRead = xStrm->readBytes(aBytes, BUFFER_SIZE);
149  const sal_Int8* p = aBytes.getConstArray();
150  aStrmBuf << std::string(p, p + nBytesRead);
151  }
152  while (nBytesRead == BUFFER_SIZE);
153 
154  rStrm = aStrmBuf.str();
155 }
156 
157 }
158 
160  ScOrcusXMLContext(), mrDoc(rDoc), maPath(rPath) {}
161 
163 
165 {
166  rParam.m_UserDataStore.clear();
167 
168  std::string aStrm;
169  loadContentFromURL(maPath, aStrm);
170 
171  if (aStrm.empty())
172  return;
173 
174  orcus::xmlns_context cxt = maNsRepo.create_context();
175  orcus::xml_structure_tree aXmlTree(cxt);
176  try
177  {
178  aXmlTree.parse(&aStrm[0], aStrm.size());
179 
180  TreeUpdateSwitch aSwitch(rTreeCtrl);
181  rTreeCtrl.clear();
182 
183  orcus::xml_structure_tree::walker aWalker = aXmlTree.get_walker();
184 
185  // Root element.
186  orcus::xml_structure_tree::element aElem = aWalker.root();
187  populateTree(rTreeCtrl, aWalker, aElem.name, aElem.repeat, nullptr, rParam);
188  }
189  catch (const orcus::sax::malformed_xml_error& e)
190  {
191  SAL_WARN("sc.orcus", "Malformed XML error: " << e.what());
192  }
193  catch (const std::exception& e)
194  {
195  SAL_WARN("sc.orcus", "parsing failed with an unknown error " << e.what());
196  }
197 
198  rTreeCtrl.all_foreach([&rTreeCtrl](weld::TreeIter& rEntry){
199  rTreeCtrl.expand_row(rEntry);
200  return false;
201  });
202 }
203 
204 namespace {
205 
206 class SetNamespaceAlias
207 {
208  orcus::orcus_xml& mrFilter;
209  orcus::xmlns_repository& mrNsRepo;
210 public:
211  SetNamespaceAlias(orcus::orcus_xml& filter, orcus::xmlns_repository& repo) :
212  mrFilter(filter), mrNsRepo(repo) {}
213 
214  void operator() (size_t index)
215  {
216  orcus::xmlns_id_t nsid = mrNsRepo.get_identifier(index);
217  if (nsid == orcus::XMLNS_UNKNOWN_ID)
218  return;
219 
220  std::string alias = mrNsRepo.get_short_name(index);
221  mrFilter.set_namespace_alias(alias.c_str(), nsid);
222  }
223 };
224 
225 }
226 
228 {
229  ScOrcusFactory aFactory(mrDoc, true);
230 
231  OUString aSysPath;
232  if (osl::FileBase::getSystemPathFromFileURL(maPath, aSysPath) != osl::FileBase::E_None)
233  return;
234 
235  OString aOSysPath = OUStringToOString(aSysPath, RTL_TEXTENCODING_UTF8);
236  const char* path = aOSysPath.getStr();
237 
238  try
239  {
240  orcus::orcus_xml filter(maNsRepo, &aFactory, nullptr);
241 
242  // Define all used namespaces.
243  std::for_each(rParam.maNamespaces.begin(), rParam.maNamespaces.end(), SetNamespaceAlias(filter, maNsRepo));
244 
245  // Set cell links.
246  for (const ScOrcusImportXMLParam::CellLink& rLink : rParam.maCellLinks)
247  {
248  OUString aTabName;
249  mrDoc.GetName(rLink.maPos.Tab(), aTabName);
250  filter.set_cell_link(
251  rLink.maPath.getStr(),
252  OUStringToOString(aTabName, RTL_TEXTENCODING_UTF8).getStr(),
253  rLink.maPos.Row(), rLink.maPos.Col());
254  }
255 
256  // Set range links.
257  for (const ScOrcusImportXMLParam::RangeLink& rLink : rParam.maRangeLinks)
258  {
259  OUString aTabName;
260  mrDoc.GetName(rLink.maPos.Tab(), aTabName);
261  filter.start_range(
262  OUStringToOString(aTabName, RTL_TEXTENCODING_UTF8).getStr(),
263  rLink.maPos.Row(), rLink.maPos.Col());
264 
265  std::for_each(rLink.maFieldPaths.begin(), rLink.maFieldPaths.end(),
266  [&filter](const OString& rFieldPath)
267  {
268  filter.append_field_link(rFieldPath.getStr(), orcus::pstring());
269  }
270  );
271 
272  std::for_each(rLink.maRowGroups.begin(), rLink.maRowGroups.end(),
273  [&filter] (const OString& rRowGroup)
274  {
275  filter.set_range_row_group(rRowGroup.getStr());
276  }
277  );
278 
279  filter.commit_range();
280  }
281 
282  orcus::file_content content(path);
283  filter.read_stream(content.data(), content.size());
284 
285  aFactory.finalize();
286  }
287  catch (const std::exception&)
288  {
289  }
290 }
291 
292 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
OUString maImgElementRepeat
Definition: orcusxml.hxx:45
Any maPath
virtual ~ScOrcusXMLContextImpl() override
Definition: xmlcontext.cxx:162
SCROW Row() const
Definition: address.hxx:261
virtual void insert(const TreeIter *pParent, int pos, const OUString *pStr, const OUString *pId, const OUString *pIconName, VirtualDevice *pImageSurface, bool bChildrenOnDemand, TreeIter *pRet)=0
signed char sal_Int8
OUString maImgAttribute
Definition: orcusxml.hxx:46
aBuf
virtual void clear()=0
virtual void finalize() override
Definition: interface.cxx:405
virtual void set_id(int row, const OUString &rId)=0
virtual std::unique_ptr< TreeIter > make_iterator(const TreeIter *pOrig=nullptr) const =0
virtual void thaw()=0
UserDataStoreType m_UserDataStore
Store all custom data instances since the tree control doesn't manage the life cycle of user data...
Definition: orcusxml.hxx:52
SCTAB Tab() const
Definition: address.hxx:270
OString OUStringToOString(std::u16string_view str, ConnectionSettings const *settings)
std::vector< RangeLink > maRangeLinks
Definition: orcusxml.hxx:76
virtual void importXML(const ScOrcusImportXMLParam &rParam) override
Definition: xmlcontext.cxx:227
virtual void loadXMLStructure(weld::TreeView &rTreeCtrl, ScOrcusXMLTreeParam &rParam) override
Definition: xmlcontext.cxx:164
DocumentType eType
orcus::xmlns_repository maNsRepo
virtual void freeze()=0
#define BUFFER_SIZE
Definition: xmlcontext.cxx:35
virtual void expand_row(const TreeIter &rIter)=0
SCCOL Col() const
Definition: address.hxx:266
Parameter used during call to ScOrcusFilters::loadXMLStructure().
Definition: orcusxml.hxx:26
std::vector< size_t > maNamespaces
Definition: orcusxml.hxx:74
OUString maImgElementDefault
Definition: orcusxml.hxx:44
css::uno::Reference< css::io::XInputStream > openStream()
virtual void all_foreach(const std::function< bool(TreeIter &)> &func)=0
std::vector< CellLink > maCellLinks
Definition: orcusxml.hxx:75
void * p
Reference< XComponentContext > getProcessComponentContext()
std::vector< std::unique_ptr< EntryData > > UserDataStoreType
Definition: orcusxml.hxx:42
virtual void set_image(int row, const OUString &rImage, int col=-1)=0
#define SAL_WARN(area, stream)
const PowerPointImport & mrFilter
void SvStream & rStrm
Custom data stored with each tree item.
Definition: orcusxml.hxx:31
SC_DLLPUBLIC bool GetName(SCTAB nTab, OUString &rName) const
Definition: document.cxx:213
ScOrcusXMLContextImpl(ScDocument &rDoc, const OUString &rPath)
XML namespace repository for this context.
Definition: xmlcontext.cxx:159
OUString toString(OptionInfo const *info)