Update ooo320-m1
[ooovba.git] / xmloff / source / core / nmspmap.cxx
blobe33a2ae108093ffa099692d7be45b5fef45c7810
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: nmspmap.cxx,v $
10 * $Revision: 1.24 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_xmloff.hxx"
33 #include <tools/debug.hxx>
34 #include <rtl/ustring.hxx>
35 #include <rtl/ustrbuf.hxx>
37 #ifndef _XMLTOKEN_HXX
38 #include <xmloff/xmltoken.hxx>
39 #endif
40 #include <xmloff/nmspmap.hxx>
42 #include "xmlnmspe.hxx"
45 using ::rtl::OUString;
46 using ::rtl::OUStringBuffer;
47 using namespace ::xmloff::token;
49 /* The basic idea of this class is that we have two two ways to search our
50 * data...by prefix and by key. We use an STL hash_map for fast prefix
51 * searching and an STL map for fast key searching.
53 * The references to an 'Index' refer to an earlier implementation of the
54 * name space map and remain to support code which uses these interfaces.
56 * In this implementation, key and index should always be the same number.
58 * All references to Indices are now deprecated and the corresponding
59 * 'Key' methods should be used instead
61 * Martin 13/06/01
64 SvXMLNamespaceMap::SvXMLNamespaceMap()
65 : sXMLNS( GetXMLToken ( XML_XMLNS ) )
69 SvXMLNamespaceMap::SvXMLNamespaceMap( const SvXMLNamespaceMap& rMap )
70 : sXMLNS( GetXMLToken ( XML_XMLNS ) )
72 aNameHash = rMap.aNameHash;
73 aNameMap = rMap.aNameMap;
76 void SvXMLNamespaceMap::operator=( const SvXMLNamespaceMap& rMap )
78 aNameHash = rMap.aNameHash;
79 aNameMap = rMap.aNameMap;
82 SvXMLNamespaceMap::~SvXMLNamespaceMap()
84 QNameCache::iterator aIter = aQNameCache.begin(), aEnd = aQNameCache.end();
85 while ( aIter != aEnd )
87 const OUString *pString = (*aIter).first.second;
88 aIter++;
89 delete pString;
93 int SvXMLNamespaceMap::operator ==( const SvXMLNamespaceMap& rCmp ) const
95 return static_cast < int > (aNameHash == rCmp.aNameHash);
98 sal_uInt16 SvXMLNamespaceMap::_Add( const OUString& rPrefix, const OUString &rName, sal_uInt16 nKey )
100 if( XML_NAMESPACE_UNKNOWN == nKey )
102 // create a new unique key with UNKNOWN flag set
103 nKey = XML_NAMESPACE_UNKNOWN_FLAG;
106 NameSpaceMap::const_iterator aIter = aNameMap.find ( nKey );
107 if( aIter == aNameMap.end() )
108 break;
109 nKey++;
111 while ( sal_True );
113 ::vos::ORef<NameSpaceEntry> pEntry(new NameSpaceEntry);
114 pEntry->sName = rName;
115 pEntry->nKey = nKey;
116 pEntry->sPrefix = rPrefix;
117 aNameHash[ rPrefix ] = pEntry;
118 aNameMap [ nKey ] = pEntry;
119 return nKey;
122 sal_uInt16 SvXMLNamespaceMap::Add( const OUString& rPrefix, const OUString& rName,
123 sal_uInt16 nKey )
125 if( XML_NAMESPACE_UNKNOWN == nKey )
126 nKey = GetKeyByName( rName );
128 DBG_ASSERT( XML_NAMESPACE_NONE != nKey,
129 "SvXMLNamespaceMap::Add: invalid namespace key" );
131 if( XML_NAMESPACE_NONE == nKey )
132 return USHRT_MAX;
134 if ( aNameHash.find ( rPrefix ) == aNameHash.end() )
135 nKey = _Add( rPrefix, rName, nKey );
137 return nKey;
140 sal_uInt16 SvXMLNamespaceMap::AddIfKnown( const OUString& rPrefix, const OUString& rName )
142 sal_uInt16 nKey = GetKeyByName( rName );
144 DBG_ASSERT( XML_NAMESPACE_NONE != nKey,
145 "SvXMLNamespaceMap::AddIfKnown: invalid namespace key" );
147 if( XML_NAMESPACE_NONE == nKey )
148 return XML_NAMESPACE_UNKNOWN;
150 if( XML_NAMESPACE_UNKNOWN != nKey )
152 NameSpaceHash::const_iterator aIter = aNameHash.find( rPrefix );
153 if( aIter == aNameHash.end() || (*aIter).second->sName != rName )
154 nKey = _Add( rPrefix, rName, nKey );
157 return nKey;
161 sal_uInt16 SvXMLNamespaceMap::GetKeyByPrefix( const OUString& rPrefix ) const
163 NameSpaceHash::const_iterator aIter = aNameHash.find(rPrefix);
164 return (aIter != aNameHash.end()) ? (*aIter).second->nKey : USHRT_MAX;
167 sal_uInt16 SvXMLNamespaceMap::GetKeyByName( const OUString& rName ) const
169 sal_uInt16 nKey = XML_NAMESPACE_UNKNOWN;
170 NameSpaceHash::const_iterator aIter = aNameHash.begin(), aEnd = aNameHash.end();
171 while (aIter != aEnd )
173 if ((*aIter).second->sName == rName)
175 nKey = (*aIter).second->nKey;
176 break;
178 aIter++;
180 return nKey;
183 const OUString& SvXMLNamespaceMap::GetPrefixByKey( sal_uInt16 nKey ) const
185 NameSpaceMap::const_iterator aIter = aNameMap.find (nKey);
186 return (aIter != aNameMap.end()) ? (*aIter).second->sPrefix : sEmpty;
189 const OUString& SvXMLNamespaceMap::GetNameByKey( sal_uInt16 nKey ) const
191 NameSpaceMap::const_iterator aIter = aNameMap.find (nKey);
192 return (aIter != aNameMap.end()) ? (*aIter).second->sName : sEmpty;
195 OUString SvXMLNamespaceMap::GetAttrNameByKey( sal_uInt16 nKey ) const
197 OUStringBuffer sAttrName;
198 NameSpaceMap::const_iterator aIter = aNameMap.find ( nKey );
199 if (aIter != aNameMap.end())
201 sAttrName.append( sXMLNS );
202 const ::rtl::OUString & prefix( (*aIter).second->sPrefix );
203 if (prefix.getLength()) // not default namespace
205 sAttrName.append( sal_Unicode(':') );
206 sAttrName.append( prefix );
209 return sAttrName.makeStringAndClear();
212 OUString SvXMLNamespaceMap::GetQNameByKey( sal_uInt16 nKey,
213 const OUString& rLocalName,
214 sal_Bool bCache) const
216 // We always want to return at least the rLocalName...
218 switch ( nKey )
220 case XML_NAMESPACE_UNKNOWN:
221 // ...if it's a completely unknown namespace, assert and return the local name
222 DBG_ASSERT( sal_False, "SvXMLNamespaceMap::GetQNameByKey: invalid namespace key" );
223 case XML_NAMESPACE_NONE:
224 // ...if there isn't one, return the local name
225 return rLocalName;
226 case XML_NAMESPACE_XMLNS:
228 // ...if it's in the xmlns namespace, make the prefix
229 // don't bother caching this, it rarely happens
230 OUStringBuffer sQName;
231 sQName.append ( sXMLNS );
232 if (rLocalName.getLength()) // not default namespace
234 sQName.append ( sal_Unicode(':') );
235 sQName.append ( rLocalName );
237 return sQName.makeStringAndClear();;
239 case XML_NAMESPACE_XML:
241 // this namespace is reserved, and needs not to be declared
242 OUStringBuffer sQName;
243 sQName.append ( GetXMLToken(XML_XML) );
244 sQName.append ( sal_Unicode(':') );
245 sQName.append ( rLocalName );
246 return sQName.makeStringAndClear();;
248 default:
250 QNameCache::const_iterator aQCacheIter;
251 if (bCache)
252 aQCacheIter = aQNameCache.find ( QNamePair ( nKey, &rLocalName ) );
253 else
254 aQCacheIter = aQNameCache.end();
255 if ( aQCacheIter != aQNameCache.end() )
256 return (*aQCacheIter).second;
257 else
259 NameSpaceMap::const_iterator aIter = aNameMap.find ( nKey );
260 if ( aIter != aNameMap.end() )
262 OUStringBuffer sQName;
263 // ...if it's in our map, make the prefix
264 const OUString & prefix( (*aIter).second->sPrefix );
265 if (prefix.getLength()) // not default namespace
267 sQName.append( prefix );
268 sQName.append( sal_Unicode(':') );
270 sQName.append ( rLocalName );
271 if (bCache)
273 OUString sString(sQName.makeStringAndClear());
274 OUString *pString = new OUString ( rLocalName );
275 const_cast < QNameCache * > (&aQNameCache)->operator[] ( QNamePair ( nKey, pString ) ) = sString;
276 return sString;
278 else
279 return sQName.makeStringAndClear();
281 else
283 // ... if it isn't, this is a Bad Thing, assert and return the local name
284 DBG_ASSERT( sal_False, "SvXMLNamespaceMap::GetQNameByKey: invalid namespace key" );
285 return rLocalName;
292 sal_uInt16 SvXMLNamespaceMap::_GetKeyByAttrName(
293 const OUString& rAttrName,
294 OUString *pLocalName,
295 sal_Bool bCache) const
297 return _GetKeyByAttrName( rAttrName, 0, pLocalName, 0, bCache );
300 sal_uInt16 SvXMLNamespaceMap::_GetKeyByAttrName( const OUString& rAttrName,
301 OUString *pPrefix,
302 OUString *pLocalName,
303 OUString *pNamespace,
304 sal_Bool bCache) const
306 sal_uInt16 nKey = XML_NAMESPACE_UNKNOWN;
308 NameSpaceHash::const_iterator it;
309 if (bCache)
310 it = aNameCache.find ( rAttrName );
311 else
312 it = aNameCache.end();
313 if ( it != aNameCache.end() )
315 const NameSpaceEntry &rEntry = (*it).second.getBody();
316 if ( pPrefix )
317 *pPrefix = rEntry.sPrefix;
318 if ( pLocalName )
319 *pLocalName = rEntry.sName;
320 nKey = rEntry.nKey;
321 if ( pNamespace )
323 NameSpaceMap::const_iterator aMapIter = aNameMap.find (nKey);
324 *pNamespace = aMapIter != aNameMap.end() ? (*aMapIter).second->sName : sEmpty;
327 else
329 vos::ORef<NameSpaceEntry> xEntry(new NameSpaceEntry());
331 sal_Int32 nColonPos = rAttrName.indexOf( sal_Unicode(':') );
332 if( -1L == nColonPos )
334 // case: no ':' found -> default namespace
335 xEntry->sPrefix = OUString();
336 xEntry->sName = rAttrName;
338 else
340 // normal case: ':' found -> get prefix/suffix
341 xEntry->sPrefix = rAttrName.copy( 0L, nColonPos );
342 xEntry->sName = rAttrName.copy( nColonPos + 1L );
345 if( pPrefix )
346 *pPrefix = xEntry->sPrefix;
347 if( pLocalName )
348 *pLocalName = xEntry->sName;
350 NameSpaceHash::const_iterator aIter = aNameHash.find( xEntry->sPrefix );
351 if ( aIter != aNameHash.end() )
353 // found: retrieve namespace key
354 nKey = xEntry->nKey = (*aIter).second->nKey;
355 if ( pNamespace )
356 *pNamespace = (*aIter).second->sName;
358 else if ( xEntry->sPrefix == sXMLNS )
359 // not found, but xmlns prefix: return xmlns 'namespace'
360 nKey = xEntry->nKey = XML_NAMESPACE_XMLNS;
361 else if( nColonPos == -1L )
362 // not found, and no namespace: 'namespace' none
363 nKey = xEntry->nKey = XML_NAMESPACE_NONE;
365 if (bCache)
367 typedef std::pair< const rtl::OUString, vos::ORef<NameSpaceEntry> > value_type;
368 (void) const_cast<NameSpaceHash*>(&aNameCache)->insert (value_type (rAttrName, xEntry));
372 return nKey;
375 sal_uInt16 SvXMLNamespaceMap::GetFirstKey() const
377 return aNameMap.empty() ? USHRT_MAX : (*aNameMap.begin()).second->nKey;
380 sal_uInt16 SvXMLNamespaceMap::GetNextKey( sal_uInt16 nLastKey ) const
382 NameSpaceMap::const_iterator aIter = aNameMap.find ( nLastKey );
383 return (++aIter == aNameMap.end()) ? USHRT_MAX : (*aIter).second->nKey;
387 // All methods after this are deprecated...
389 sal_uInt16 SvXMLNamespaceMap::GetKeyByIndex( sal_uInt16 nIdx ) const
391 return nIdx;
394 sal_uInt16 SvXMLNamespaceMap::GetIndexByKey( sal_uInt16 nKey ) const
396 return nKey;
398 sal_uInt16 SvXMLNamespaceMap::GetFirstIndex() const
400 return aNameMap.empty() ? USHRT_MAX : (*aNameMap.begin()).second->nKey;
403 sal_uInt16 SvXMLNamespaceMap::GetNextIndex( sal_uInt16 nOldIdx ) const
405 NameSpaceMap::const_iterator aIter = aNameMap.find ( nOldIdx );
406 return (++aIter == aNameMap.end()) ? USHRT_MAX : (*aIter).second->nKey;
409 sal_Bool SvXMLNamespaceMap::AddAtIndex( sal_uInt16 /*nIdx*/, const OUString& rPrefix,
410 const OUString& rName, sal_uInt16 nKey )
412 sal_Bool bRet = sal_False;
414 if( XML_NAMESPACE_UNKNOWN == nKey )
415 nKey = GetKeyByName( rName );
417 DBG_ASSERT( XML_NAMESPACE_NONE != nKey,
418 "SvXMLNamespaceMap::AddAtIndex: invalid namespace key" );
419 if( XML_NAMESPACE_NONE != nKey && ! ( aNameHash.count ( rPrefix ) ) )
421 _Add( rPrefix, rName, nKey );
422 bRet = sal_True;
424 return bRet;
427 sal_Bool SvXMLNamespaceMap::AddAtIndex( sal_uInt16 nIdx, const sal_Char *pPrefix,
428 const sal_Char *pName, sal_uInt16 nKey )
430 OUString sPrefix( OUString::createFromAscii(pPrefix) );
431 OUString sName( OUString::createFromAscii(pName) );
433 return AddAtIndex( nIdx, sPrefix, sName, nKey );
436 OUString SvXMLNamespaceMap::GetAttrNameByIndex( sal_uInt16 nIdx ) const
438 return GetAttrNameByKey( nIdx );
441 OUString SvXMLNamespaceMap::GetQNameByIndex( sal_uInt16 nIdx,
442 const OUString& rLocalName ) const
444 return GetQNameByKey( nIdx, rLocalName );
447 const OUString& SvXMLNamespaceMap::GetPrefixByIndex( sal_uInt16 nIdx ) const
449 NameSpaceMap::const_iterator aIter = aNameMap.find (nIdx);
450 return (aIter != aNameMap.end()) ? (*aIter).second->sPrefix : sEmpty;
453 const OUString& SvXMLNamespaceMap::GetNameByIndex( sal_uInt16 nIdx ) const
455 NameSpaceMap::const_iterator aIter = aNameMap.find (nIdx);
456 return (aIter != aNameMap.end()) ? (*aIter).second->sName : sEmpty;
459 sal_uInt16 SvXMLNamespaceMap::GetIndexByPrefix( const OUString& rPrefix ) const
461 NameSpaceHash::const_iterator aIter = aNameHash.find(rPrefix);
462 return (aIter != aNameHash.end()) ? (*aIter).second->nKey : USHRT_MAX;
464 sal_uInt16 SvXMLNamespaceMap::GetKeyByAttrName(
465 const OUString& rAttrName,
466 OUString *pLocalName,
467 sal_uInt16 /*nIdxGuess*/) const
469 return _GetKeyByAttrName( rAttrName, 0, pLocalName, 0 );
472 sal_uInt16 SvXMLNamespaceMap::GetKeyByAttrName( const OUString& rAttrName,
473 OUString *pPrefix,
474 OUString *pLocalName,
475 OUString *pNamespace,
476 USHORT /*nIdxGuess*/ ) const
478 return _GetKeyByAttrName ( rAttrName, pPrefix, pLocalName, pNamespace );
481 sal_Bool SvXMLNamespaceMap::NormalizeURI( ::rtl::OUString& rName )
483 // try OASIS + W3 URI normalization
484 sal_Bool bSuccess = NormalizeOasisURN( rName );
485 if( ! bSuccess )
486 bSuccess = NormalizeW3URI( rName );
487 return bSuccess;
490 sal_Bool SvXMLNamespaceMap::NormalizeW3URI( ::rtl::OUString& rName )
492 // check if URI matches:
493 // http://www.w3.org/[0-9]*/[:letter:]*
494 // (year)/(WG name)
495 // For the following WG/standards names:
496 // - xforms
498 sal_Bool bSuccess = sal_False;
499 const OUString sURIPrefix = GetXMLToken( XML_URI_W3_PREFIX );
500 if( rName.compareTo( sURIPrefix, sURIPrefix.getLength() ) == 0 )
502 const OUString sURISuffix = GetXMLToken( XML_URI_XFORMS_SUFFIX );
503 sal_Int32 nCompareFrom = rName.getLength() - sURISuffix.getLength();
504 if( rName.copy( nCompareFrom ).equals( sURISuffix ) )
506 // found W3 prefix, and xforms suffix
507 rName = GetXMLToken( XML_N_XFORMS_1_0 );
508 bSuccess = sal_True;
511 return bSuccess;
514 sal_Bool SvXMLNamespaceMap::NormalizeOasisURN( ::rtl::OUString& rName )
516 // #i38644#
517 // we exported the wrong namespace for smil, so we correct this here on load
518 // for older documents
519 if( IsXMLToken( rName, ::xmloff::token::XML_N_SVG ) )
521 rName = GetXMLToken( ::xmloff::token::XML_N_SVG_COMPAT );
522 return sal_True;
524 else if( IsXMLToken( rName, ::xmloff::token::XML_N_FO ) )
526 rName = GetXMLToken( ::xmloff::token::XML_N_FO_COMPAT );
527 return sal_True;
529 else if( IsXMLToken( rName, ::xmloff::token::XML_N_SMIL ) ||
530 IsXMLToken( rName, ::xmloff::token::XML_N_SMIL_OLD ) )
532 rName = GetXMLToken( ::xmloff::token::XML_N_SMIL_COMPAT );
533 return sal_True;
537 // Check if URN matches
538 // :urn:oasis:names:tc:[^:]*:xmlns:[^:]*:1.[^:]*
539 // |---| |---| |-----|
540 // TC-Id Sub-Id Version
542 sal_Int32 nNameLen = rName.getLength();
543 // :urn:oasis:names:tc.*
544 const OUString& rOasisURN = GetXMLToken( XML_URN_OASIS_NAMES_TC );
545 if( 0 != rName.compareTo( rOasisURN, rOasisURN.getLength() ) )
546 return sal_False;
548 // :urn:oasis:names:tc:.*
549 sal_Int32 nPos = rOasisURN.getLength();
550 if( nPos >= nNameLen || rName[nPos] != ':' )
551 return sal_False;
553 // :urn:oasis:names:tc:[^:]:.*
554 sal_Int32 nTCIdStart = nPos+1;
555 sal_Int32 nTCIdEnd = rName.indexOf( ':', nTCIdStart );
556 if( -1 == nTCIdEnd )
557 return sal_False;
559 // :urn:oasis:names:tc:[^:]:xmlns.*
560 nPos = nTCIdEnd + 1;
561 OUString sTmp( rName.copy( nPos ) );
562 const OUString& rXMLNS = GetXMLToken( XML_XMLNS );
563 if( 0!= sTmp.compareTo( rXMLNS, rXMLNS.getLength() ) )
564 return sal_False;
566 // :urn:oasis:names:tc:[^:]:xmlns:.*
567 nPos += rXMLNS.getLength();
568 if( nPos >= nNameLen || rName[nPos] != ':' )
569 return sal_False;
571 // :urn:oasis:names:tc:[^:]:xmlns:[^:]*:.*
572 nPos = rName.indexOf( ':', nPos+1 );
573 if( -1 == nPos )
574 return sal_False;
576 // :urn:oasis:names:tc:[^:]:xmlns:[^:]*:[^:][^:][^:][^:]*
577 sal_Int32 nVersionStart = nPos+1;
578 if( nVersionStart+2 >= nNameLen ||
579 -1 != rName.indexOf( ':', nVersionStart ) )
580 return sal_False;
582 // :urn:oasis:names:tc:[^:]:xmlns:[^:]*:1\.[^:][^:]*
583 if( rName[nVersionStart] != '1' || rName[nVersionStart+1] != '.' )
584 return sal_False;
586 // replace [tcid] with current TCID and version with current version.
587 OUStringBuffer aNewName( nNameLen +20 );
588 aNewName.append( rName.copy( 0, nTCIdStart ) );
589 aNewName.append( GetXMLToken( XML_OPENDOCUMENT ) );
590 aNewName.append( rName.copy( nTCIdEnd, nVersionStart-nTCIdEnd ) );
591 aNewName.append( GetXMLToken( XML_1_0 ) );
593 rName = aNewName.makeStringAndClear();
595 return sal_True;