LibreOffice Module xmloff (master)  1
namespacemap.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 #include <sal/config.h>
21 
22 #include <rtl/ustring.hxx>
23 #include <rtl/ustrbuf.hxx>
24 #include <sal/log.hxx>
25 
26 #include <xmloff/xmltoken.hxx>
27 #include <xmloff/namespacemap.hxx>
28 
29 #include <xmloff/xmlnamespace.hxx>
30 
31 
32 using namespace ::xmloff::token;
33 
34 /* The basic idea of this class is that we have two ways to search our
35  * data, by prefix and by key. We use an unordered_map for fast prefix
36  * searching and an STL map for fast key searching.
37  *
38  * The references to an 'Index' refer to an earlier implementation of the
39  * name space map and remain to support code which uses these interfaces.
40  *
41  * In this implementation, key and index should always be the same number.
42  *
43  * All references to Indices are now deprecated and the corresponding
44  * 'Key' methods should be used instead
45  *
46  * Martin 13/06/01
47  */
48 
49 const OUString sEmpty;
50 
52 : sXMLNS( GetXMLToken ( XML_XMLNS ) )
53 {
54 }
55 
57 : sXMLNS( GetXMLToken ( XML_XMLNS ) )
58 {
59  aNameHash = rMap.aNameHash;
60  aNameMap = rMap.aNameMap;
61 }
62 
64 {
65  aNameHash = rMap.aNameHash;
66  aNameMap = rMap.aNameMap;
67  return *this;
68 }
69 
71 {
72 }
73 
75 {
76  return aNameHash == rCmp.aNameHash;
77 }
78 
79 sal_uInt16 SvXMLNamespaceMap::Add_( const OUString& rPrefix, const OUString &rName, sal_uInt16 nKey )
80 {
81  if( XML_NAMESPACE_UNKNOWN == nKey )
82  {
83  // create a new unique key with UNKNOWN flag set
85  do
86  {
87  NameSpaceMap::const_iterator aIter = aNameMap.find ( nKey );
88  if( aIter == aNameMap.end() )
89  break;
90  nKey++;
91  }
92  while ( true );
93  }
95  pEntry->sName = rName;
96  pEntry->nKey = nKey;
97  pEntry->sPrefix = rPrefix;
98  aNameHash[ rPrefix ] = pEntry;
99  aNameMap [ nKey ] = pEntry;
100  return nKey;
101 }
102 
103 sal_uInt16 SvXMLNamespaceMap::Add( const OUString& rPrefix, const OUString& rName,
104  sal_uInt16 nKey )
105 {
106  if( XML_NAMESPACE_UNKNOWN == nKey )
107  nKey = GetKeyByName( rName );
108 
109 #ifdef NDEBUG
110  if( XML_NAMESPACE_NONE == nKey )
111  return USHRT_MAX;
112 #else
113  assert(XML_NAMESPACE_NONE != nKey);
114 #endif
115 
116  if ( aNameHash.find ( rPrefix ) == aNameHash.end() )
117  nKey = Add_( rPrefix, rName, nKey );
118 
119  return nKey;
120 }
121 
122 sal_uInt16 SvXMLNamespaceMap::AddIfKnown( const OUString& rPrefix, const OUString& rName )
123 {
124  sal_uInt16 nKey = GetKeyByName( rName );
125 
126 #ifdef NDEBUG
127  if( XML_NAMESPACE_NONE == nKey )
128  return XML_NAMESPACE_UNKNOWN;
129 #else
130  assert(nKey != XML_NAMESPACE_NONE);
131 #endif
132 
133  if( XML_NAMESPACE_UNKNOWN != nKey )
134  {
135  NameSpaceHash::const_iterator aIter = aNameHash.find( rPrefix );
136  if( aIter == aNameHash.end() || (*aIter).second->sName != rName )
137  nKey = Add_( rPrefix, rName, nKey );
138  }
139 
140  return nKey;
141 }
142 
143 
144 sal_uInt16 SvXMLNamespaceMap::GetKeyByPrefix( const OUString& rPrefix ) const
145 {
146  NameSpaceHash::const_iterator aIter = aNameHash.find(rPrefix);
147  return (aIter != aNameHash.end()) ? (*aIter).second->nKey : USHRT_MAX;
148 }
149 
150 sal_uInt16 SvXMLNamespaceMap::GetKeyByName( const OUString& rName ) const
151 {
152  sal_uInt16 nKey = XML_NAMESPACE_UNKNOWN;
153  auto aIter = std::find_if(aNameHash.cbegin(), aNameHash.cend(),
154  [&rName](const NameSpaceHash::value_type& rEntry) { return rEntry.second->sName == rName; });
155 
156  if (aIter != aNameHash.cend())
157  nKey = (*aIter).second->nKey;
158 
159  return nKey;
160 }
161 
162 const OUString& SvXMLNamespaceMap::GetPrefixByKey( sal_uInt16 nKey ) const
163 {
164  NameSpaceMap::const_iterator aIter = aNameMap.find (nKey);
165  return (aIter != aNameMap.end()) ? (*aIter).second->sPrefix : sEmpty;
166 }
167 
168 const OUString& SvXMLNamespaceMap::GetNameByKey( sal_uInt16 nKey ) const
169 {
170  NameSpaceMap::const_iterator aIter = aNameMap.find (nKey);
171  return (aIter != aNameMap.end()) ? (*aIter).second->sName : sEmpty;
172 }
173 
174 OUString SvXMLNamespaceMap::GetAttrNameByKey( sal_uInt16 nKey ) const
175 {
176  NameSpaceMap::const_iterator aIter = aNameMap.find ( nKey );
177  if (aIter == aNameMap.end())
178  return OUString();
179 
180  const OUString & prefix( (*aIter).second->sPrefix );
181  if (prefix.isEmpty()) // default namespace
182  return sXMLNS;
183 
184  return sXMLNS + ":" + prefix;
185 }
186 
187 OUString SvXMLNamespaceMap::GetQNameByKey( sal_uInt16 nKey,
188  const OUString& rLocalName,
189  bool bCache) const
190 {
191  // We always want to return at least the rLocalName...
192 
193  switch ( nKey )
194  {
196  // ...if it's a completely unknown namespace, assert and return the local name
197  SAL_WARN("xmloff.core", "unknown namespace, probable missing xmlns: declaration");
198  [[fallthrough]];
199  case XML_NAMESPACE_NONE:
200  // ...if there isn't one, return the local name
201  return rLocalName;
202  case XML_NAMESPACE_XMLNS:
203  {
204  // ...if it's in the xmlns namespace, make the prefix
205  // don't bother caching this, it rarely happens
206  OUStringBuffer sQName;
207  sQName.append ( sXMLNS );
208  if (!rLocalName.isEmpty()) // not default namespace
209  {
210  sQName.append ( ':' );
211  sQName.append ( rLocalName );
212  }
213  return sQName.makeStringAndClear();
214  }
215  case XML_NAMESPACE_XML:
216  {
217  // this namespace is reserved, and needs not to be declared
218  return GetXMLToken(XML_XML) + ":" + rLocalName;
219  }
220  default:
221  {
222  QNameCache::const_iterator aQCacheIter;
223  if (bCache)
224  aQCacheIter = aQNameCache.find ( QNamePair ( nKey, rLocalName ) );
225  else
226  aQCacheIter = aQNameCache.end();
227  if ( aQCacheIter != aQNameCache.end() )
228  return (*aQCacheIter).second;
229  else
230  {
231  NameSpaceMap::const_iterator aIter = aNameMap.find ( nKey );
232  if ( aIter != aNameMap.end() )
233  {
234  // ...if it's in our map, make the prefix
235  const OUString & prefix( (*aIter).second->sPrefix );
236  OUStringBuffer sQName(prefix.getLength() + 1 + rLocalName.getLength());
237  if (!prefix.isEmpty()) // not default namespace
238  {
239  sQName.append( prefix );
240  sQName.append( ':' );
241  }
242  sQName.append ( rLocalName );
243  if (bCache)
244  {
245  OUString sString(sQName.makeStringAndClear());
246  aQNameCache.emplace(QNamePair(nKey, rLocalName), sString);
247  return sString;
248  }
249  else
250  return sQName.makeStringAndClear();
251  }
252  else
253  {
254  // ... if it isn't, this is a Bad Thing, assert and return the local name
255  assert(false);
256  return rLocalName;
257  }
258  }
259  }
260  }
261 }
262 
264  const OUString& rAttrValue,
265  OUString *pLocalName) const
266 {
267  return GetKeyByQName(rAttrValue, nullptr, pLocalName, nullptr, QNameMode::AttrValue);
268 }
269 
275 sal_uInt16 SvXMLNamespaceMap::GetKeyByQName(const OUString& rQName,
276  OUString *pPrefix,
277  OUString *pLocalName,
278  OUString *pNamespace,
279  QNameMode const eMode) const
280 {
281  sal_uInt16 nKey;
282 
283  NameSpaceHash::const_iterator it;
284  if (eMode == QNameMode::AttrNameCached)
285  it = aNameCache.find ( rQName );
286  else
287  it = aNameCache.end();
288  if ( it != aNameCache.end() )
289  {
290  const NameSpaceEntry &rEntry = *((*it).second);
291  if ( pPrefix )
292  *pPrefix = rEntry.sPrefix;
293  if ( pLocalName )
294  *pLocalName = rEntry.sName;
295  nKey = rEntry.nKey;
296  if ( pNamespace )
297  {
298  NameSpaceMap::const_iterator aMapIter = aNameMap.find (nKey);
299  *pNamespace = aMapIter != aNameMap.end() ? (*aMapIter).second->sName : OUString();
300  }
301  }
302  else
303  {
304  OUString sEntryPrefix, sEntryName;
305 
306  sal_Int32 nColonPos = rQName.indexOf( ':' );
307  if( -1 == nColonPos )
308  {
309  // case: no ':' found -> default namespace
310  sEntryName = rQName;
311  }
312  else
313  {
314  // normal case: ':' found -> get prefix/suffix
315  sEntryPrefix = rQName.copy( 0L, nColonPos );
316  sEntryName = rQName.copy( nColonPos + 1 );
317  }
318 
319  if (eMode == QNameMode::AttrNameCached && sEntryName.indexOf(':') != -1)
320  {
321  SAL_INFO("xmloff", "invalid attribute name with multiple ':'");
322  return XML_NAMESPACE_UNKNOWN;
323  }
324 
325  if( pPrefix )
326  *pPrefix = sEntryPrefix;
327  if( pLocalName )
328  *pLocalName = sEntryName;
329 
330  NameSpaceHash::const_iterator aIter = aNameHash.find( sEntryPrefix );
331  if ( aIter != aNameHash.end() )
332  {
333  // found: retrieve namespace key
334  nKey = (*aIter).second->nKey;
335  if ( pNamespace )
336  *pNamespace = (*aIter).second->sName;
337  }
338  else if ( sEntryPrefix == sXMLNS )
339  // not found, but xmlns prefix: return xmlns 'namespace'
340  nKey = XML_NAMESPACE_XMLNS;
341  else if( nColonPos == -1 )
342  // not found, and no namespace: 'namespace' none
343  nKey = XML_NAMESPACE_NONE;
344  else
345  nKey = XML_NAMESPACE_UNKNOWN;
346 
347  if (eMode == QNameMode::AttrNameCached)
348  {
350  xEntry->sPrefix = std::move(sEntryPrefix);
351  xEntry->sName = std::move(sEntryName);
352  xEntry->nKey = std::move(nKey);
353  aNameCache.emplace(rQName, std::move(xEntry));
354  }
355  }
356 
357  return nKey;
358 }
359 
361 {
362  return aNameMap.empty() ? USHRT_MAX : (*aNameMap.begin()).second->nKey;
363 }
364 
365 sal_uInt16 SvXMLNamespaceMap::GetNextKey( sal_uInt16 nLastKey ) const
366 {
367  NameSpaceMap::const_iterator aIter = aNameMap.find ( nLastKey );
368  return (++aIter == aNameMap.end()) ? USHRT_MAX : (*aIter).second->nKey;
369 }
370 
371 
372 // All methods after this are deprecated...
373 
374 sal_uInt16 SvXMLNamespaceMap::GetIndexByKey( sal_uInt16 nKey )
375 {
376  return nKey;
377 }
379 {
380  return aNameMap.empty() ? USHRT_MAX : (*aNameMap.begin()).second->nKey;
381 }
382 
383 sal_uInt16 SvXMLNamespaceMap::GetNextIndex( sal_uInt16 nOldIdx ) const
384 {
385  NameSpaceMap::const_iterator aIter = aNameMap.find ( nOldIdx );
386  return (++aIter == aNameMap.end()) ? USHRT_MAX : (*aIter).second->nKey;
387 }
388 
389 void SvXMLNamespaceMap::AddAtIndex( const OUString& rPrefix,
390  const OUString& rName, sal_uInt16 nKey )
391 {
392  if( XML_NAMESPACE_UNKNOWN == nKey )
393  nKey = GetKeyByName( rName );
394 
395  assert(XML_NAMESPACE_NONE != nKey);
396  if( XML_NAMESPACE_NONE != nKey && ! ( aNameHash.count ( rPrefix ) ) )
397  {
398  Add_( rPrefix, rName, nKey );
399  }
400 }
401 
402 OUString SvXMLNamespaceMap::GetAttrNameByIndex( sal_uInt16 nIdx ) const
403 {
404  return GetAttrNameByKey( nIdx );
405 }
406 
407 OUString SvXMLNamespaceMap::GetQNameByIndex( sal_uInt16 nIdx,
408  const OUString& rLocalName ) const
409 {
410  return GetQNameByKey( nIdx, rLocalName );
411 }
412 
413 const OUString& SvXMLNamespaceMap::GetPrefixByIndex( sal_uInt16 nIdx ) const
414 {
415  NameSpaceMap::const_iterator aIter = aNameMap.find (nIdx);
416  return (aIter != aNameMap.end()) ? (*aIter).second->sPrefix : sEmpty;
417 }
418 
419 const OUString& SvXMLNamespaceMap::GetNameByIndex( sal_uInt16 nIdx ) const
420 {
421  NameSpaceMap::const_iterator aIter = aNameMap.find (nIdx);
422  return (aIter != aNameMap.end()) ? (*aIter).second->sName : sEmpty;
423 }
424 
425 sal_uInt16 SvXMLNamespaceMap::GetIndexByPrefix( const OUString& rPrefix ) const
426 {
427  NameSpaceHash::const_iterator aIter = aNameHash.find(rPrefix);
428  return (aIter != aNameHash.end()) ? (*aIter).second->nKey : USHRT_MAX;
429 }
431  const OUString& rAttrName,
432  OUString *pLocalName) const
433 {
434  return GetKeyByQName(rAttrName, nullptr, pLocalName, nullptr, QNameMode::AttrNameCached);
435 }
436 
437 sal_uInt16 SvXMLNamespaceMap::GetKeyByAttrName( const OUString& rAttrName,
438  OUString *pPrefix,
439  OUString *pLocalName,
440  OUString *pNamespace ) const
441 {
442  return GetKeyByQName(rAttrName, pPrefix, pLocalName, pNamespace, QNameMode::AttrNameCached);
443 }
444 
445 bool SvXMLNamespaceMap::NormalizeURI( OUString& rName )
446 {
447  // try OASIS + W3 URI normalization
448  bool bSuccess = NormalizeOasisURN( rName );
449  if( ! bSuccess )
450  bSuccess = NormalizeW3URI( rName );
451  return bSuccess;
452 }
453 
454 bool SvXMLNamespaceMap::NormalizeW3URI( OUString& rName )
455 {
456  // check if URI matches:
457  // http://www.w3.org/[0-9]*/[:letter:]*
458  // (year)/(WG name)
459  // For the following WG/standards names:
460  // - xforms
461 
462  bool bSuccess = false;
463  const OUString& sURIPrefix = GetXMLToken( XML_URI_W3_PREFIX );
464  if( rName.startsWith( sURIPrefix ) )
465  {
466  const OUString& sURISuffix = GetXMLToken( XML_URI_XFORMS_SUFFIX );
467  sal_Int32 nCompareFrom = rName.getLength() - sURISuffix.getLength();
468  if( rName.copy( nCompareFrom ) == sURISuffix )
469  {
470  // found W3 prefix, and xforms suffix
471  rName = GetXMLToken( XML_N_XFORMS_1_0 );
472  bSuccess = true;
473  }
474  }
475  return bSuccess;
476 }
477 
479 {
480  // #i38644#
481  // we exported the wrong namespace for smil, so we correct this here on load
482  // for older documents
483  if( IsXMLToken( rName, ::xmloff::token::XML_N_SVG ) )
484  {
486  return true;
487  }
488  else if( IsXMLToken( rName, ::xmloff::token::XML_N_FO ) )
489  {
491  return true;
492  }
493  else if( IsXMLToken( rName, ::xmloff::token::XML_N_SMIL ) ||
495  {
497  return true;
498  }
499 
500 
501  // Check if URN matches
502  // :urn:oasis:names:tc:[^:]*:xmlns:[^:]*:1.[^:]*
503  // |---| |---| |-----|
504  // TC-Id Sub-Id Version
505 
506  sal_Int32 nNameLen = rName.getLength();
507  // :urn:oasis:names:tc.*
508  const OUString& rOasisURN = GetXMLToken( XML_URN_OASIS_NAMES_TC );
509  if( !rName.startsWith( rOasisURN ) )
510  return false;
511 
512  // :urn:oasis:names:tc:.*
513  sal_Int32 nPos = rOasisURN.getLength();
514  if( nPos >= nNameLen || rName[nPos] != ':' )
515  return false;
516 
517  // :urn:oasis:names:tc:[^:]:.*
518  sal_Int32 nTCIdStart = nPos+1;
519  sal_Int32 nTCIdEnd = rName.indexOf( ':', nTCIdStart );
520  if( -1 == nTCIdEnd )
521  return false;
522 
523  // :urn:oasis:names:tc:[^:]:xmlns.*
524  nPos = nTCIdEnd + 1;
525  OUString sTmp( rName.copy( nPos ) );
526  const OUString& rXMLNS = GetXMLToken( XML_XMLNS );
527  if( !sTmp.startsWith( rXMLNS ) )
528  return false;
529 
530  // :urn:oasis:names:tc:[^:]:xmlns:.*
531  nPos += rXMLNS.getLength();
532  if( nPos >= nNameLen || rName[nPos] != ':' )
533  return false;
534 
535  // :urn:oasis:names:tc:[^:]:xmlns:[^:]*:.*
536  nPos = rName.indexOf( ':', nPos+1 );
537  if( -1 == nPos )
538  return false;
539 
540  // :urn:oasis:names:tc:[^:]:xmlns:[^:]*:[^:][^:][^:][^:]*
541  sal_Int32 nVersionStart = nPos+1;
542  if( nVersionStart+2 >= nNameLen ||
543  -1 != rName.indexOf( ':', nVersionStart ) )
544  return false;
545 
546  // :urn:oasis:names:tc:[^:]:xmlns:[^:]*:1\.[^:][^:]*
547  if( rName[nVersionStart] != '1' || rName[nVersionStart+1] != '.' )
548  return false;
549 
550  // replace [tcid] with current TCID and version with current version.
551 
552  rName = rName.copy( 0, nTCIdStart ) +
554  rName.copy( nTCIdEnd, nVersionStart-nTCIdEnd ) +
555  GetXMLToken( XML_1_0 );
556 
557  return true;
558 }
559 
560 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
bool IsXMLToken(const OUString &rString, enum XMLTokenEnum eToken)
compare eToken to the string
Definition: xmltoken.cxx:3434
sal_uInt16 GetKeyByPrefix(const OUString &rPrefix) const
SAL_DLLPRIVATE sal_uInt16 Add_(const OUString &rPrefix, const OUString &rName, sal_uInt16 nKey)
static sal_uInt16 GetIndexByKey(sal_uInt16 nKey)
NameSpaceHash aNameCache
sal_uInt16 GetFirstKey() const
OUString GetAttrNameByKey(sal_uInt16 nKey) const
sal_uInt16 GetKeyByAttrName(const OUString &rAttrName, OUString *pPrefix, OUString *pLocalName, OUString *pNamespace) const
const sal_uInt16 XML_NAMESPACE_UNKNOWN
const sal_uInt16 XML_NAMESPACE_XMLNS
bool operator==(const SvXMLNamespaceMap &rCmp) const
const BorderLinePrimitive2D *pCandidateB assert(pCandidateA)
const OUString sEmpty
OUString sPrefix
sal_uInt16 GetKeyByAttrValueQName(const OUString &rAttrName, OUString *pLocalName) const
constexpr sal_uInt16 XML_NAMESPACE_XML
SvXMLNamespaceMap & operator=(const SvXMLNamespaceMap &rCmp)
sal_uInt16 GetIndexByPrefix(const OUString &rPrefix) const
static bool NormalizeURI(OUString &rName)
static bool NormalizeW3URI(OUString &rName)
sal_uInt16 nKey
const sal_uInt16 XML_NAMESPACE_UNKNOWN_FLAG
OUString GetAttrNameByIndex(sal_uInt16 nIdx) const
QNameCache aQNameCache
sal_uInt16 GetNextKey(sal_uInt16 nOldKey) const
const sal_uInt16 XML_NAMESPACE_NONE
OUString GetQNameByKey(sal_uInt16 nKey, const OUString &rLocalName, bool bCache=true) const
sal_uInt16 AddIfKnown(const OUString &rPrefix, const OUString &rName)
const OUString & GetNameByKey(sal_uInt16 nKey) const
NameSpaceHash aNameHash
static bool NormalizeOasisURN(OUString &rName)
const OUString & GetXMLToken(enum XMLTokenEnum eToken)
return the OUString representation for eToken
Definition: xmltoken.cxx:3378
const o3tl::enumarray< SvxAdjust, unsigned short > aSvxToUnoAdjust USHRT_MAX
Handling of tokens in XML:
#define SAL_INFO(area, stream)
sal_uInt16 Add(const OUString &rPrefix, const OUString &rName, sal_uInt16 nKey=XML_NAMESPACE_UNKNOWN)
sal_uInt16 GetKeyByName(const OUString &rName) const
#define SAL_WARN(area, stream)
void AddAtIndex(const OUString &rPrefix, const OUString &rName, sal_uInt16 nKey)
const OUString & GetPrefixByIndex(sal_uInt16 nIdx) const
const OUString & GetNameByIndex(sal_uInt16 nIdx) const
::std::pair< sal_uInt16, OUString > QNamePair
OUString GetQNameByIndex(sal_uInt16 nIdx, const OUString &rLocalName) const
const OUString & GetPrefixByKey(sal_uInt16 nKey) const
sal_uInt16 GetFirstIndex() const
sal_uInt16 nPos
sal_uInt16 GetKeyByQName(const OUString &rQName, OUString *pPrefix, OUString *pLocalName, OUString *pNamespace, QNameMode eMode) const
NameSpaceMap aNameMap
sal_uInt16 GetNextIndex(sal_uInt16 nOldIdx) const