Version 5.2.6.1, tag libreoffice-5.2.6.1
[LibreOffice.git] / xmloff / source / core / nmspmap.cxx
blobe86b6590858348902286168fb900da30474026d2
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sal/config.h>
22 #include <rtl/ustring.hxx>
23 #include <rtl/ustrbuf.hxx>
25 #include <xmloff/xmltoken.hxx>
26 #include <xmloff/nmspmap.hxx>
28 #include <xmloff/xmlnmspe.hxx>
31 using namespace ::xmloff::token;
33 /* The basic idea of this class is that we have two two ways to search our
34 * data...by prefix and by key. We use an unordered_map for fast prefix
35 * searching and an STL map for fast key searching.
37 * The references to an 'Index' refer to an earlier implementation of the
38 * name space map and remain to support code which uses these interfaces.
40 * In this implementation, key and index should always be the same number.
42 * All references to Indices are now deprecated and the corresponding
43 * 'Key' methods should be used instead
45 * Martin 13/06/01
48 SvXMLNamespaceMap::SvXMLNamespaceMap()
49 : sXMLNS( GetXMLToken ( XML_XMLNS ) )
53 SvXMLNamespaceMap::SvXMLNamespaceMap( const SvXMLNamespaceMap& rMap )
54 : sXMLNS( GetXMLToken ( XML_XMLNS ) )
56 aNameHash = rMap.aNameHash;
57 aNameMap = rMap.aNameMap;
60 SvXMLNamespaceMap& SvXMLNamespaceMap::operator=( const SvXMLNamespaceMap& rMap )
62 aNameHash = rMap.aNameHash;
63 aNameMap = rMap.aNameMap;
64 return *this;
67 SvXMLNamespaceMap::~SvXMLNamespaceMap()
71 bool SvXMLNamespaceMap::operator ==( const SvXMLNamespaceMap& rCmp ) const
73 return aNameHash == rCmp.aNameHash;
76 sal_uInt16 SvXMLNamespaceMap::Add_( const OUString& rPrefix, const OUString &rName, sal_uInt16 nKey )
78 if( XML_NAMESPACE_UNKNOWN == nKey )
80 // create a new unique key with UNKNOWN flag set
81 nKey = XML_NAMESPACE_UNKNOWN_FLAG;
84 NameSpaceMap::const_iterator aIter = aNameMap.find ( nKey );
85 if( aIter == aNameMap.end() )
86 break;
87 nKey++;
89 while ( true );
91 ::rtl::Reference<NameSpaceEntry> pEntry(new NameSpaceEntry);
92 pEntry->sName = rName;
93 pEntry->nKey = nKey;
94 pEntry->sPrefix = rPrefix;
95 aNameHash[ rPrefix ] = pEntry;
96 aNameMap [ nKey ] = pEntry;
97 return nKey;
100 sal_uInt16 SvXMLNamespaceMap::Add( const OUString& rPrefix, const OUString& rName,
101 sal_uInt16 nKey )
103 if( XML_NAMESPACE_UNKNOWN == nKey )
104 nKey = GetKeyByName( rName );
106 #ifdef NDEBUG
107 if( XML_NAMESPACE_NONE == nKey )
108 return USHRT_MAX;
109 #else
110 assert(XML_NAMESPACE_NONE != nKey);
111 #endif
113 if ( aNameHash.find ( rPrefix ) == aNameHash.end() )
114 nKey = Add_( rPrefix, rName, nKey );
116 return nKey;
119 sal_uInt16 SvXMLNamespaceMap::AddIfKnown( const OUString& rPrefix, const OUString& rName )
121 sal_uInt16 nKey = GetKeyByName( rName );
123 #ifdef NDEBUG
124 if( XML_NAMESPACE_NONE == nKey )
125 return XML_NAMESPACE_UNKNOWN;
126 #else
127 assert(nKey != XML_NAMESPACE_NONE);
128 #endif
130 if( XML_NAMESPACE_UNKNOWN != nKey )
132 NameSpaceHash::const_iterator aIter = aNameHash.find( rPrefix );
133 if( aIter == aNameHash.end() || (*aIter).second->sName != rName )
134 nKey = Add_( rPrefix, rName, nKey );
137 return nKey;
141 sal_uInt16 SvXMLNamespaceMap::GetKeyByPrefix( const OUString& rPrefix ) const
143 NameSpaceHash::const_iterator aIter = aNameHash.find(rPrefix);
144 return (aIter != aNameHash.end()) ? (*aIter).second->nKey : USHRT_MAX;
147 sal_uInt16 SvXMLNamespaceMap::GetKeyByName( const OUString& rName ) const
149 sal_uInt16 nKey = XML_NAMESPACE_UNKNOWN;
150 NameSpaceHash::const_iterator aIter = aNameHash.begin(), aEnd = aNameHash.end();
151 while (aIter != aEnd )
153 if ((*aIter).second->sName == rName)
155 nKey = (*aIter).second->nKey;
156 break;
158 ++aIter;
160 return nKey;
163 const OUString& SvXMLNamespaceMap::GetPrefixByKey( sal_uInt16 nKey ) const
165 NameSpaceMap::const_iterator aIter = aNameMap.find (nKey);
166 return (aIter != aNameMap.end()) ? (*aIter).second->sPrefix : sEmpty;
169 const OUString& SvXMLNamespaceMap::GetNameByKey( sal_uInt16 nKey ) const
171 NameSpaceMap::const_iterator aIter = aNameMap.find (nKey);
172 return (aIter != aNameMap.end()) ? (*aIter).second->sName : sEmpty;
175 OUString SvXMLNamespaceMap::GetAttrNameByKey( sal_uInt16 nKey ) const
177 OUStringBuffer sAttrName;
178 NameSpaceMap::const_iterator aIter = aNameMap.find ( nKey );
179 if (aIter != aNameMap.end())
181 sAttrName.append( sXMLNS );
182 const OUString & prefix( (*aIter).second->sPrefix );
183 if (!prefix.isEmpty()) // not default namespace
185 sAttrName.append( ':' );
186 sAttrName.append( prefix );
189 return sAttrName.makeStringAndClear();
192 OUString SvXMLNamespaceMap::GetQNameByKey( sal_uInt16 nKey,
193 const OUString& rLocalName,
194 bool bCache) const
196 // We always want to return at least the rLocalName...
198 switch ( nKey )
200 case XML_NAMESPACE_UNKNOWN:
201 // ...if it's a completely unknown namespace, assert and return the local name
202 SAL_WARN("xmloff.core", "unknown namespace, probable missing xmlns: declaration");
203 SAL_FALLTHROUGH;
204 case XML_NAMESPACE_NONE:
205 // ...if there isn't one, return the local name
206 return rLocalName;
207 case XML_NAMESPACE_XMLNS:
209 // ...if it's in the xmlns namespace, make the prefix
210 // don't bother caching this, it rarely happens
211 OUStringBuffer sQName;
212 sQName.append ( sXMLNS );
213 if (!rLocalName.isEmpty()) // not default namespace
215 sQName.append ( ':' );
216 sQName.append ( rLocalName );
218 return sQName.makeStringAndClear();
220 case XML_NAMESPACE_XML:
222 // this namespace is reserved, and needs not to be declared
223 OUStringBuffer sQName;
224 sQName.append ( GetXMLToken(XML_XML) );
225 sQName.append ( ':' );
226 sQName.append ( rLocalName );
227 return sQName.makeStringAndClear();
229 default:
231 QNameCache::const_iterator aQCacheIter;
232 if (bCache)
233 aQCacheIter = aQNameCache.find ( QNamePair ( nKey, rLocalName ) );
234 else
235 aQCacheIter = aQNameCache.end();
236 if ( aQCacheIter != aQNameCache.end() )
237 return (*aQCacheIter).second;
238 else
240 NameSpaceMap::const_iterator aIter = aNameMap.find ( nKey );
241 if ( aIter != aNameMap.end() )
243 OUStringBuffer sQName;
244 // ...if it's in our map, make the prefix
245 const OUString & prefix( (*aIter).second->sPrefix );
246 if (!prefix.isEmpty()) // not default namespace
248 sQName.append( prefix );
249 sQName.append( ':' );
251 sQName.append ( rLocalName );
252 if (bCache)
254 OUString sString(sQName.makeStringAndClear());
255 aQNameCache.insert(
256 QNameCache::value_type(
257 QNamePair(nKey, rLocalName), sString));
258 return sString;
260 else
261 return sQName.makeStringAndClear();
263 else
265 // ... if it isn't, this is a Bad Thing, assert and return the local name
266 assert(false);
267 return rLocalName;
274 sal_uInt16 SvXMLNamespaceMap::GetKeyByAttrName_(
275 const OUString& rAttrName,
276 OUString *pLocalName) const
278 return GetKeyByAttrName_( rAttrName, nullptr, pLocalName, nullptr, false/*bCache*/ );
281 sal_uInt16 SvXMLNamespaceMap::GetKeyByAttrName_( const OUString& rAttrName,
282 OUString *pPrefix,
283 OUString *pLocalName,
284 OUString *pNamespace,
285 bool bCache) const
287 sal_uInt16 nKey = XML_NAMESPACE_UNKNOWN;
289 NameSpaceHash::const_iterator it;
290 if (bCache)
291 it = aNameCache.find ( rAttrName );
292 else
293 it = aNameCache.end();
294 if ( it != aNameCache.end() )
296 const NameSpaceEntry &rEntry = *((*it).second);
297 if ( pPrefix )
298 *pPrefix = rEntry.sPrefix;
299 if ( pLocalName )
300 *pLocalName = rEntry.sName;
301 nKey = rEntry.nKey;
302 if ( pNamespace )
304 NameSpaceMap::const_iterator aMapIter = aNameMap.find (nKey);
305 *pNamespace = aMapIter != aNameMap.end() ? (*aMapIter).second->sName : OUString();
308 else
310 rtl::Reference<NameSpaceEntry> xEntry(new NameSpaceEntry());
312 sal_Int32 nColonPos = rAttrName.indexOf( ':' );
313 if( -1L == nColonPos )
315 // case: no ':' found -> default namespace
316 (xEntry->sPrefix).clear();
317 xEntry->sName = rAttrName;
319 else
321 // normal case: ':' found -> get prefix/suffix
322 xEntry->sPrefix = rAttrName.copy( 0L, nColonPos );
323 xEntry->sName = rAttrName.copy( nColonPos + 1L );
326 if( pPrefix )
327 *pPrefix = xEntry->sPrefix;
328 if( pLocalName )
329 *pLocalName = xEntry->sName;
331 NameSpaceHash::const_iterator aIter = aNameHash.find( xEntry->sPrefix );
332 if ( aIter != aNameHash.end() )
334 // found: retrieve namespace key
335 nKey = xEntry->nKey = (*aIter).second->nKey;
336 if ( pNamespace )
337 *pNamespace = (*aIter).second->sName;
339 else if ( xEntry->sPrefix == sXMLNS )
340 // not found, but xmlns prefix: return xmlns 'namespace'
341 nKey = xEntry->nKey = XML_NAMESPACE_XMLNS;
342 else if( nColonPos == -1L )
343 // not found, and no namespace: 'namespace' none
344 nKey = xEntry->nKey = XML_NAMESPACE_NONE;
346 if (bCache)
348 aNameCache.insert(NameSpaceHash::value_type(rAttrName, xEntry));
352 return nKey;
355 sal_uInt16 SvXMLNamespaceMap::GetFirstKey() const
357 return aNameMap.empty() ? USHRT_MAX : (*aNameMap.begin()).second->nKey;
360 sal_uInt16 SvXMLNamespaceMap::GetNextKey( sal_uInt16 nLastKey ) const
362 NameSpaceMap::const_iterator aIter = aNameMap.find ( nLastKey );
363 return (++aIter == aNameMap.end()) ? USHRT_MAX : (*aIter).second->nKey;
367 // All methods after this are deprecated...
369 sal_uInt16 SvXMLNamespaceMap::GetIndexByKey( sal_uInt16 nKey )
371 return nKey;
373 sal_uInt16 SvXMLNamespaceMap::GetFirstIndex() const
375 return aNameMap.empty() ? USHRT_MAX : (*aNameMap.begin()).second->nKey;
378 sal_uInt16 SvXMLNamespaceMap::GetNextIndex( sal_uInt16 nOldIdx ) const
380 NameSpaceMap::const_iterator aIter = aNameMap.find ( nOldIdx );
381 return (++aIter == aNameMap.end()) ? USHRT_MAX : (*aIter).second->nKey;
384 void SvXMLNamespaceMap::AddAtIndex( sal_uInt16 /*nIdx*/, const OUString& rPrefix,
385 const OUString& rName, sal_uInt16 nKey )
387 if( XML_NAMESPACE_UNKNOWN == nKey )
388 nKey = GetKeyByName( rName );
390 assert(XML_NAMESPACE_NONE != nKey);
391 if( XML_NAMESPACE_NONE != nKey && ! ( aNameHash.count ( rPrefix ) ) )
393 Add_( rPrefix, rName, nKey );
397 OUString SvXMLNamespaceMap::GetAttrNameByIndex( sal_uInt16 nIdx ) const
399 return GetAttrNameByKey( nIdx );
402 OUString SvXMLNamespaceMap::GetQNameByIndex( sal_uInt16 nIdx,
403 const OUString& rLocalName ) const
405 return GetQNameByKey( nIdx, rLocalName );
408 const OUString& SvXMLNamespaceMap::GetPrefixByIndex( sal_uInt16 nIdx ) const
410 NameSpaceMap::const_iterator aIter = aNameMap.find (nIdx);
411 return (aIter != aNameMap.end()) ? (*aIter).second->sPrefix : sEmpty;
414 const OUString& SvXMLNamespaceMap::GetNameByIndex( sal_uInt16 nIdx ) const
416 NameSpaceMap::const_iterator aIter = aNameMap.find (nIdx);
417 return (aIter != aNameMap.end()) ? (*aIter).second->sName : sEmpty;
420 sal_uInt16 SvXMLNamespaceMap::GetIndexByPrefix( const OUString& rPrefix ) const
422 NameSpaceHash::const_iterator aIter = aNameHash.find(rPrefix);
423 return (aIter != aNameHash.end()) ? (*aIter).second->nKey : USHRT_MAX;
425 sal_uInt16 SvXMLNamespaceMap::GetKeyByAttrName(
426 const OUString& rAttrName,
427 OUString *pLocalName,
428 sal_uInt16 /*nIdxGuess*/) const
430 return GetKeyByAttrName_( rAttrName, nullptr, pLocalName );
433 sal_uInt16 SvXMLNamespaceMap::GetKeyByAttrName( const OUString& rAttrName,
434 OUString *pPrefix,
435 OUString *pLocalName,
436 OUString *pNamespace,
437 sal_uInt16 /*nIdxGuess*/ ) const
439 return GetKeyByAttrName_ ( rAttrName, pPrefix, pLocalName, pNamespace );
442 bool SvXMLNamespaceMap::NormalizeURI( OUString& rName )
444 // try OASIS + W3 URI normalization
445 bool bSuccess = NormalizeOasisURN( rName );
446 if( ! bSuccess )
447 bSuccess = NormalizeW3URI( rName );
448 return bSuccess;
451 bool SvXMLNamespaceMap::NormalizeW3URI( OUString& rName )
453 // check if URI matches:
454 // http://www.w3.org/[0-9]*/[:letter:]*
455 // (year)/(WG name)
456 // For the following WG/standards names:
457 // - xforms
459 bool bSuccess = false;
460 const OUString& sURIPrefix = GetXMLToken( XML_URI_W3_PREFIX );
461 if( rName.startsWith( sURIPrefix ) )
463 const OUString& sURISuffix = GetXMLToken( XML_URI_XFORMS_SUFFIX );
464 sal_Int32 nCompareFrom = rName.getLength() - sURISuffix.getLength();
465 if( rName.copy( nCompareFrom ).equals( sURISuffix ) )
467 // found W3 prefix, and xforms suffix
468 rName = GetXMLToken( XML_N_XFORMS_1_0 );
469 bSuccess = true;
472 return bSuccess;
475 bool SvXMLNamespaceMap::NormalizeOasisURN( OUString& rName )
477 // #i38644#
478 // we exported the wrong namespace for smil, so we correct this here on load
479 // for older documents
480 if( IsXMLToken( rName, ::xmloff::token::XML_N_SVG ) )
482 rName = GetXMLToken( ::xmloff::token::XML_N_SVG_COMPAT );
483 return true;
485 else if( IsXMLToken( rName, ::xmloff::token::XML_N_FO ) )
487 rName = GetXMLToken( ::xmloff::token::XML_N_FO_COMPAT );
488 return true;
490 else if( IsXMLToken( rName, ::xmloff::token::XML_N_SMIL ) ||
491 IsXMLToken( rName, ::xmloff::token::XML_N_SMIL_OLD ) )
493 rName = GetXMLToken( ::xmloff::token::XML_N_SMIL_COMPAT );
494 return true;
498 // Check if URN matches
499 // :urn:oasis:names:tc:[^:]*:xmlns:[^:]*:1.[^:]*
500 // |---| |---| |-----|
501 // TC-Id Sub-Id Version
503 sal_Int32 nNameLen = rName.getLength();
504 // :urn:oasis:names:tc.*
505 const OUString& rOasisURN = GetXMLToken( XML_URN_OASIS_NAMES_TC );
506 if( !rName.startsWith( rOasisURN ) )
507 return false;
509 // :urn:oasis:names:tc:.*
510 sal_Int32 nPos = rOasisURN.getLength();
511 if( nPos >= nNameLen || rName[nPos] != ':' )
512 return false;
514 // :urn:oasis:names:tc:[^:]:.*
515 sal_Int32 nTCIdStart = nPos+1;
516 sal_Int32 nTCIdEnd = rName.indexOf( ':', nTCIdStart );
517 if( -1 == nTCIdEnd )
518 return false;
520 // :urn:oasis:names:tc:[^:]:xmlns.*
521 nPos = nTCIdEnd + 1;
522 OUString sTmp( rName.copy( nPos ) );
523 const OUString& rXMLNS = GetXMLToken( XML_XMLNS );
524 if( !sTmp.startsWith( rXMLNS ) )
525 return false;
527 // :urn:oasis:names:tc:[^:]:xmlns:.*
528 nPos += rXMLNS.getLength();
529 if( nPos >= nNameLen || rName[nPos] != ':' )
530 return false;
532 // :urn:oasis:names:tc:[^:]:xmlns:[^:]*:.*
533 nPos = rName.indexOf( ':', nPos+1 );
534 if( -1 == nPos )
535 return false;
537 // :urn:oasis:names:tc:[^:]:xmlns:[^:]*:[^:][^:][^:][^:]*
538 sal_Int32 nVersionStart = nPos+1;
539 if( nVersionStart+2 >= nNameLen ||
540 -1 != rName.indexOf( ':', nVersionStart ) )
541 return false;
543 // :urn:oasis:names:tc:[^:]:xmlns:[^:]*:1\.[^:][^:]*
544 if( rName[nVersionStart] != '1' || rName[nVersionStart+1] != '.' )
545 return false;
547 // replace [tcid] with current TCID and version with current version.
549 rName = rName.copy( 0, nTCIdStart ) +
550 GetXMLToken( XML_OPENDOCUMENT ) +
551 rName.copy( nTCIdEnd, nVersionStart-nTCIdEnd ) +
552 GetXMLToken( XML_1_0 );
554 return true;
557 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */