LibreOffice Module xmloff (master)  1
txtlists.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  * This file incorporates work covered by the following license notice:
10  *
11  * Licensed to the Apache Software Foundation (ASF) under one or more
12  * contributor license agreements. See the NOTICE file distributed
13  * with this work for additional information regarding copyright
14  * ownership. The ASF licenses this file to you under the Apache
15  * License, Version 2.0 (the "License"); you may not use this file
16  * except in compliance with the License. You may obtain a copy of
17  * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 
21 #include <txtlists.hxx>
22 #include <comphelper/random.hxx>
23 
24 #include <o3tl/safeint.hxx>
25 #include <tools/datetime.hxx>
26 #include <sal/log.hxx>
27 
28 #include <xmloff/txtimp.hxx>
29 #include <xmloff/xmlimp.hxx>
30 #include <xmloff/xmlnumi.hxx>
31 
32 #include <com/sun/star/style/XStyle.hpp>
33 #include <com/sun/star/beans/XPropertySet.hpp>
34 #include <com/sun/star/container/XNameContainer.hpp>
37 #include "txtparai.hxx"
38 
39 
40 using namespace ::com::sun::star;
41 
42 
44  : msLastProcessedListId(),
45  msListStyleOfLastProcessedList()
46  // Inconsistent behavior regarding lists (#i92811#)
47 {
48 }
49 
51  XMLTextListBlockContext *i_pListBlock)
52 {
53  mListStack.emplace(i_pListBlock,
54  static_cast<XMLTextListItemContext*>(nullptr),
55  static_cast<XMLNumberedParaContext*>(nullptr));
56 }
57 
59  XMLNumberedParaContext *i_pNumberedParagraph)
60 {
61  mListStack.emplace(
62  static_cast<XMLTextListBlockContext*>(nullptr),
63  static_cast<XMLTextListItemContext*>(nullptr), i_pNumberedParagraph);
64 }
65 
67 {
68  assert(mListStack.size());
69  if ( !mListStack.empty())
70  mListStack.pop();
71 }
72 
74  XMLTextListBlockContext*& o_pListBlockContext,
75  XMLTextListItemContext*& o_pListItemContext,
76  XMLNumberedParaContext*& o_pNumberedParagraphContext )
77 {
78  if ( !mListStack.empty() ) {
79  o_pListBlockContext =
80  static_cast<XMLTextListBlockContext*>(std::get<0>(mListStack.top()).get());
81  o_pListItemContext =
82  static_cast<XMLTextListItemContext *>(std::get<1>(mListStack.top()).get());
83  o_pNumberedParagraphContext =
84  static_cast<XMLNumberedParaContext *>(std::get<2>(mListStack.top()).get());
85  }
86 }
87 
89 {
90  // may be cleared by ListBlockContext for upper list...
91  if (i_pListItem) {
92  assert(mListStack.size());
93  assert(std::get<0>(mListStack.top()).is() &&
94  "internal error: SetListItem: mListStack has no ListBlock");
95  assert(!std::get<1>(mListStack.top()).is() &&
96  "error: SetListItem: list item already exists");
97  }
98  if ( !mListStack.empty() ) {
99  std::get<1>(mListStack.top()) = i_pListItem;
100  }
101 }
102 
103 // Handling for parameter <sListStyleDefaultListId> (#i92811#)
104 void XMLTextListsHelper::KeepListAsProcessed( const OUString& sListId,
105  const OUString& sListStyleName,
106  const OUString& sContinueListId,
107  const OUString& sListStyleDefaultListId )
108 {
109  if ( IsListProcessed( sListId ) )
110  {
111  assert(false &&
112  "<XMLTextListsHelper::KeepListAsProcessed(..)> - list id already added" );
113  return;
114  }
115 
116  if ( !mpProcessedLists )
117  {
118  mpProcessedLists = std::make_unique<tMapForLists>();
119  }
120 
121  ::std::pair< OUString, OUString >
122  aListData( sListStyleName, sContinueListId );
123  (*mpProcessedLists)[ sListId ] = aListData;
124 
125  msLastProcessedListId = sListId;
126  msListStyleOfLastProcessedList = sListStyleName;
127 
128  // Inconsistent behavior regarding lists (#i92811#)
129  if ( sListStyleDefaultListId.isEmpty())
130  return;
131 
133  {
134  mpMapListIdToListStyleDefaultListId = std::make_unique<tMapForLists>();
135  }
136 
137  if ( mpMapListIdToListStyleDefaultListId->find( sListStyleName ) ==
139  {
140  ::std::pair< OUString, OUString >
141  aListIdMapData( sListId, sListStyleDefaultListId );
142  (*mpMapListIdToListStyleDefaultListId)[ sListStyleName ] =
143  aListIdMapData;
144  }
145 }
146 
147 bool XMLTextListsHelper::IsListProcessed( const OUString& sListId ) const
148 {
149  if ( !mpProcessedLists )
150  {
151  return false;
152  }
153 
154  return mpProcessedLists->find( sListId ) != mpProcessedLists->end();
155 }
156 
158  const OUString& sListId ) const
159 {
160  if ( mpProcessedLists )
161  {
162  tMapForLists::const_iterator aIter = mpProcessedLists->find( sListId );
163  if ( aIter != mpProcessedLists->end() )
164  {
165  return (*aIter).second.first;
166  }
167  }
168 
169  return OUString();
170 }
171 
173  const OUString& sListId ) const
174 {
175  if ( mpProcessedLists )
176  {
177  tMapForLists::const_iterator aIter = mpProcessedLists->find( sListId );
178  if ( aIter != mpProcessedLists->end() )
179  {
180  return (*aIter).second.second;
181  }
182  }
183 
184  return OUString();
185 }
186 
187 
189 {
190  static bool bHack = (getenv("LIBO_ONEWAY_STABLE_ODF_EXPORT") != nullptr);
191  OUString sTmpStr( "list" );
192 
193  if (bHack)
194  {
195  static sal_Int64 nIdCounter = SAL_CONST_INT64(5000000000);
196  sTmpStr += OUString::number(nIdCounter++);
197  }
198  else
199  {
200  // Value of xml:id in element <text:list> has to be a valid ID type (#i92478#)
201  DateTime aDateTime( DateTime::SYSTEM );
202  sal_Int64 n = aDateTime.GetTime();
203  n += aDateTime.GetDateUnsigned();
204  n += comphelper::rng::uniform_int_distribution(0, std::numeric_limits<int>::max());
205  // Value of xml:id in element <text:list> has to be a valid ID type (#i92478#)
206  sTmpStr += OUString::number( n );
207  }
208 
209  OUString sNewListId( sTmpStr );
210  if ( mpProcessedLists )
211  {
212  long nHitCount = 0;
213  while ( mpProcessedLists->find( sNewListId ) != mpProcessedLists->end() )
214  {
215  ++nHitCount;
216  sNewListId = sTmpStr + OUString::number( nHitCount );
217  }
218  }
219 
220  return sNewListId;
221 }
222 
223 // Provide list id for a certain list block for import (#i92811#)
225 {
226  OUString sListBlockListId( rListBlock.GetContinueListId() );
227  if ( sListBlockListId.isEmpty() )
228  {
229  sListBlockListId = rListBlock.GetListId();
230  }
231 
233  {
234  if ( !sListBlockListId.isEmpty() )
235  {
236  const OUString sListStyleName =
237  GetListStyleOfProcessedList( sListBlockListId );
238 
239  tMapForLists::const_iterator aIter =
240  mpMapListIdToListStyleDefaultListId->find( sListStyleName );
241  if ( aIter != mpMapListIdToListStyleDefaultListId->end() )
242  {
243  if ( (*aIter).second.first == sListBlockListId )
244  {
245  sListBlockListId = (*aIter).second.second;
246  }
247  }
248  }
249  }
250 
251  return sListBlockListId;
252 }
253 
254 void XMLTextListsHelper::StoreLastContinuingList( const OUString& sListId,
255  const OUString& sContinuingListId )
256 {
257  if ( !mpContinuingLists )
258  {
259  mpContinuingLists = std::make_unique<tMapForContinuingLists>();
260  }
261 
262  (*mpContinuingLists)[ sListId ] = sContinuingListId;
263 }
264 
266  const OUString& sListId ) const
267 {
268  if ( mpContinuingLists )
269  {
270  tMapForContinuingLists::const_iterator aIter =
271  mpContinuingLists->find( sListId );
272  if ( aIter != mpContinuingLists->end() )
273  {
274  return (*aIter).second;
275  }
276  }
277 
278  return sListId;
279 }
280 
281 void XMLTextListsHelper::PushListOnStack( const OUString& sListId,
282  const OUString& sListStyleName )
283 {
284  if ( !mpListStack )
285  {
286  mpListStack = std::make_unique<tStackForLists>();
287  }
288  ::std::pair< OUString, OUString >
289  aListData( sListId, sListStyleName );
290  mpListStack->push_back( aListData );
291 }
293 {
294  if ( mpListStack &&
295  !mpListStack->empty() )
296  {
297  mpListStack->pop_back();
298  }
299 }
300 
301 bool XMLTextListsHelper::EqualsToTopListStyleOnStack( const OUString& sListId ) const
302 {
303  return mpListStack && sListId == mpListStack->back().second;
304 }
305 
306 OUString
308  const sal_uInt16 i_Level,
309  const OUString& i_StyleName)
310 {
311  if (i_StyleName.isEmpty()) {
312  SAL_INFO("xmloff.text", "invalid numbered-paragraph: no style-name");
313  }
314  if (!i_StyleName.isEmpty()
315  && (i_Level < mLastNumberedParagraphs.size())
316  && (mLastNumberedParagraphs[i_Level].first == i_StyleName) )
317  {
318  assert(!mLastNumberedParagraphs[i_Level].second.isEmpty() &&
319  "internal error: numbered-paragraph style-name but no list-id?");
320  return mLastNumberedParagraphs[i_Level].second;
321  } else {
322  return GenerateNewListId();
323  }
324 }
325 
326 static void
327 ClampLevel(uno::Reference<container::XIndexReplace> const& i_xNumRules,
328  sal_Int16 & io_rLevel)
329 {
330  assert(i_xNumRules.is());
331  if (i_xNumRules.is()) {
332  const sal_Int32 nLevelCount( i_xNumRules->getCount() );
333  if ( io_rLevel >= nLevelCount ) {
334  io_rLevel = sal::static_int_cast< sal_Int16 >(nLevelCount-1);
335  }
336  }
337 }
338 
339 uno::Reference<container::XIndexReplace>
341  SvXMLImport & i_rImport,
342  const OUString& i_ListId,
343  sal_Int16 & io_rLevel, const OUString& i_StyleName)
344 {
345  assert(!i_ListId.isEmpty());
346  assert(io_rLevel >= 0);
347  NumParaList_t & rNPList( mNPLists[i_ListId] );
348  const OUString none; // default
349  if ( rNPList.empty() ) {
350  // create default list style for top level
351  sal_Int16 lev(0);
352  rNPList.emplace_back(none,
353  MakeNumRule(i_rImport, nullptr, none, none, lev) );
354  }
355  // create num rule first because this might clamp the level...
356  uno::Reference<container::XIndexReplace> xNumRules;
357  if ((0 == io_rLevel) || rNPList.empty() || !i_StyleName.isEmpty()) {
358  // no parent to inherit from, or explicit style given => new numrules!
359  // index of parent: level - 1, but maybe that does not exist
360  const size_t parent( std::min(static_cast<size_t>(io_rLevel),
361  rNPList.size()) - 1 );
362  xNumRules = MakeNumRule(i_rImport,
363  io_rLevel > 0 ? rNPList[parent].second : nullptr,
364  io_rLevel > 0 ? rNPList[parent].first : none,
365  i_StyleName, io_rLevel);
366  } else {
367  // no style given, but has a parent => reuse parent numrules!
368  ClampLevel(rNPList.back().second, io_rLevel);
369  }
370  if (static_cast<sal_uInt16>(io_rLevel) + 1U > rNPList.size()) {
371  // new level: need to enlarge
372  for (size_t i = rNPList.size();
373  i < o3tl::make_unsigned(io_rLevel); ++i)
374  {
375  NumParaList_t::value_type const rule(rNPList.back());
376  rNPList.push_back(rule);
377  }
378  NumParaList_t::value_type const rule(rNPList.back());
379  rNPList.push_back(xNumRules.is()
380  ? ::std::make_pair(i_StyleName, xNumRules)
381  : rule);
382  } else {
383  // old level: no need to enlarge; possibly shrink
384  if (xNumRules.is()) {
385  rNPList[io_rLevel] = std::make_pair(i_StyleName, xNumRules);
386  }
387  if (static_cast<sal_uInt16>(io_rLevel) + 1U < rNPList.size()) {
388  rNPList.erase(rNPList.begin() + io_rLevel + 1, rNPList.end());
389  }
390  }
391  // remember the list id
392  if (mLastNumberedParagraphs.size() <= o3tl::make_unsigned(io_rLevel)) {
393  mLastNumberedParagraphs.resize(io_rLevel+1);
394  }
395  mLastNumberedParagraphs[io_rLevel] = std::make_pair(i_StyleName, i_ListId);
396  return rNPList.back().second;
397 }
398 
400 uno::Reference<container::XIndexReplace>
402  SvXMLImport & i_rImport,
403  const uno::Reference<container::XIndexReplace>& i_rNumRule,
404  const OUString& i_ParentStyleName,
405  const OUString& i_StyleName,
406  sal_Int16 & io_rLevel,
407  bool* o_pRestartNumbering,
408  bool* io_pSetDefaults)
409 {
410  uno::Reference<container::XIndexReplace> xNumRules(i_rNumRule);
411  if ( !i_StyleName.isEmpty() && i_StyleName != i_ParentStyleName )
412  {
413  const OUString sDisplayStyleName(
415  i_StyleName) );
416  const uno::Reference < container::XNameContainer >& rNumStyles(
417  i_rImport.GetTextImport()->GetNumberingStyles() );
418  if( rNumStyles.is() && rNumStyles->hasByName( sDisplayStyleName ) )
419  {
420  uno::Reference < style::XStyle > xStyle;
421  uno::Any any = rNumStyles->getByName( sDisplayStyleName );
422  any >>= xStyle;
423 
424  uno::Reference< beans::XPropertySet > xPropSet( xStyle,
425  uno::UNO_QUERY );
426  any = xPropSet->getPropertyValue("NumberingRules");
427  any >>= xNumRules;
428  }
429  else
430  {
431  const SvxXMLListStyleContext *pListStyle(
432  i_rImport.GetTextImport()->FindAutoListStyle( i_StyleName ) );
433  if( pListStyle )
434  {
435  xNumRules = pListStyle->GetNumRules();
436  if( !xNumRules.is() )
437  {
438  pListStyle->CreateAndInsertAuto();
439  xNumRules = pListStyle->GetNumRules();
440  }
441  }
442  }
443  }
444 
445  bool bSetDefaults(io_pSetDefaults && *io_pSetDefaults);
446  if ( !xNumRules.is() )
447  {
448  // If no style name has been specified for this style and for any
449  // parent or if no num rule with the specified name exists,
450  // create a new one.
451 
452  xNumRules =
454  SAL_INFO_IF(xNumRules.is(), "xmloff.core", "cannot create numrules");
455  if ( !xNumRules.is() )
456  return xNumRules;
457 
458  // Because it is a new num rule, numbering must not be restarted.
459  if (o_pRestartNumbering) *o_pRestartNumbering = false;
460  bSetDefaults = true;
461  if (io_pSetDefaults) *io_pSetDefaults = bSetDefaults;
462  }
463 
464  ClampLevel(xNumRules, io_rLevel);
465 
466  if ( bSetDefaults )
467  {
468  // Because there is no list style sheet for this style, a default
469  // format must be set for any level of this num rule.
470  SvxXMLListStyleContext::SetDefaultStyle( xNumRules, io_rLevel,
471  false );
472  }
473 
474  return xNumRules;
475 }
476 
477 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
std::unique_ptr< tMapForLists > mpMapListIdToListStyleDefaultListId
Definition: txtlists.hxx:147
std::stack< ListStackFrame_t > mListStack
Definition: txtlists.hxx:132
const OUString & GetContinueListId() const
LastNumberedParagraphs_t mLastNumberedParagraphs
Definition: txtlists.hxx:166
#define SAL_INFO_IF(condition, area, stream)
void StoreLastContinuingList(const OUString &sListId, const OUString &sContinuingListId)
Definition: txtlists.cxx:254
sal_Int64 n
OUString GetListStyleOfProcessedList(const OUString &sListId) const
Definition: txtlists.cxx:157
std::unique_ptr< tStackForLists > mpListStack
Definition: txtlists.hxx:159
OUString msListStyleOfLastProcessedList
Definition: txtlists.hxx:141
rtl::Reference< XMLTextImportHelper > const & GetTextImport()
Definition: xmlimp.hxx:600
const OUString & GetListId() const
OUString GenerateNewListId() const
Definition: txtlists.cxx:188
const BorderLinePrimitive2D *pCandidateB assert(pCandidateA)
OUString GetContinueListIdOfProcessedList(const OUString &sListId) const
Definition: txtlists.cxx:172
void ListContextTop(XMLTextListBlockContext *&o_pListBlockContext, XMLTextListItemContext *&o_pListItemContext, XMLNumberedParaContext *&o_pNumberedParagraphContext)
peek at the top of the list context stack
Definition: txtlists.cxx:73
static css::uno::Reference< css::container::XIndexReplace > MakeNumRule(SvXMLImport &i_rImport, const css::uno::Reference< css::container::XIndexReplace > &i_xNumRule, const OUString &i_ParentStyleName, const OUString &i_StyleName, sal_Int16 &io_rLevel, bool *o_pRestartNumbering=nullptr, bool *io_pSetDefaults=nullptr)
Creates a NumRule from given style-name.
Definition: txtlists.cxx:401
void KeepListAsProcessed(const OUString &sListId, const OUString &sListStyleName, const OUString &sContinueListId, const OUString &sListStyleDefaultListId=OUString())
Definition: txtlists.cxx:104
static css::uno::Reference< css::container::XIndexReplace > CreateNumRule(const css::uno::Reference< css::frame::XModel > &rModel)
Definition: xmlnumi.cxx:1096
none
void PopListFromStack()
Definition: txtlists.cxx:292
void PushListOnStack(const OUString &sListId, const OUString &sListStyleName)
Definition: txtlists.cxx:281
int i
OUString GetLastContinuingListId(const OUString &sListId) const
Definition: txtlists.cxx:265
const Any & any
constexpr std::enable_if_t< std::is_signed_v< T >, std::make_unsigned_t< T > > make_unsigned(T value)
bool EqualsToTopListStyleOnStack(const OUString &sListId) const
Definition: txtlists.cxx:301
css::uno::Reference< css::container::XIndexReplace > EnsureNumberedParagraph(SvXMLImport &i_rImport, const OUString &i_ListId, sal_Int16 &io_rLevel, const OUString &i_StyleName)
for importing numbered-paragraph note that the ID namespace for numbered-paragraph and regular list i...
Definition: txtlists.cxx:340
std::unique_ptr< tMapForContinuingLists > mpContinuingLists
Definition: txtlists.hxx:153
void PushListContext(XMLTextListBlockContext *i_pListBlock)
list stack for importing:
Definition: txtlists.cxx:50
::std::map< OUString, NumParaList_t > mNPLists
Definition: txtlists.hxx:173
OUString msLastProcessedListId
Definition: txtlists.hxx:140
OUString GetStyleDisplayName(XmlStyleFamily nFamily, const OUString &rName) const
Definition: xmlimp.cxx:1494
void SetListItem(XMLTextListItemContext *pListItem)
set list item on top of the list context stack
Definition: txtlists.cxx:88
#define SAL_INFO(area, stream)
void PopListContext()
pop the list context stack
Definition: txtlists.cxx:66
int uniform_int_distribution(int a, int b)
const css::uno::Reference< css::container::XIndexReplace > & GetNumRules() const
Definition: xmlnumi.hxx:64
sal_Int64 GetTime() const
const css::uno::Reference< css::frame::XModel > & GetModel() const
Definition: xmlimp.hxx:408
OUString GetNumberedParagraphListId(const sal_uInt16 i_Level, const OUString &i_StyleName)
get ID of the last numbered-paragraph iff it has given style-name
Definition: txtlists.cxx:307
static void ClampLevel(uno::Reference< container::XIndexReplace > const &i_xNumRules, sal_Int16 &io_rLevel)
Definition: txtlists.cxx:327
::std::vector< ::std::pair< OUString, css::uno::Reference< css::container::XIndexReplace > > > NumParaList_t
numbered-paragraphs
Definition: txtlists.hxx:172
static void SetDefaultStyle(const css::uno::Reference< css::container::XIndexReplace > &rNumRule, sal_Int16 nLevel, bool bOrdered)
Definition: xmlnumi.cxx:1116
std::unique_ptr< tMapForLists > mpProcessedLists
Definition: txtlists.hxx:139
sal_uInt32 GetDateUnsigned() const
OUString GetListIdForListBlock(XMLTextListBlockContext const &rListBlock)
Definition: txtlists.cxx:224
bool IsListProcessed(const OUString &sListId) const
Definition: txtlists.cxx:147
const char first[]