xref: /trunk/main/xmloff/source/core/nmspmap.cxx (revision 1ecadb572e7010ff3b3382ad9bf179dbc6efadbb)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_xmloff.hxx"
30 #include <tools/debug.hxx>
31 #include <rtl/ustring.hxx>
32 #include <rtl/ustrbuf.hxx>
33 
34 #ifndef _XMLTOKEN_HXX
35 #include <xmloff/xmltoken.hxx>
36 #endif
37 #include <xmloff/nmspmap.hxx>
38 
39 #include "xmloff/xmlnmspe.hxx"
40 
41 
42 using ::rtl::OUString;
43 using ::rtl::OUStringBuffer;
44 using namespace ::xmloff::token;
45 
46 /* The basic idea of this class is that we have two two ways to search our
47  * data...by prefix and by key. We use an STL hash_map for fast prefix
48  * searching and an STL map for fast key searching.
49  *
50  * The references to an 'Index' refer to an earlier implementation of the
51  * name space map and remain to support code which uses these interfaces.
52  *
53  * In this implementation, key and index should always be the same number.
54  *
55  * All references to Indices are now deprecated and the corresponding
56  * 'Key' methods should be used instead
57  *
58  * Martin 13/06/01
59  */
60 
61 SvXMLNamespaceMap::SvXMLNamespaceMap()
62 : sXMLNS( GetXMLToken ( XML_XMLNS ) )
63 {
64 }
65 
66 SvXMLNamespaceMap::SvXMLNamespaceMap( const SvXMLNamespaceMap& rMap )
67 : sXMLNS( GetXMLToken ( XML_XMLNS ) )
68 {
69     aNameHash = rMap.aNameHash;
70     aNameMap  = rMap.aNameMap;
71 }
72 
73 void SvXMLNamespaceMap::operator=( const SvXMLNamespaceMap& rMap )
74 {
75     aNameHash = rMap.aNameHash;
76     aNameMap = rMap.aNameMap;
77 }
78 
79 SvXMLNamespaceMap::~SvXMLNamespaceMap()
80 {
81     QNameCache::iterator aIter = aQNameCache.begin(), aEnd = aQNameCache.end();
82     while ( aIter != aEnd )
83     {
84         const OUString *pString = (*aIter).first.second;
85         aIter++;
86         delete pString;
87     }
88 }
89 
90 int SvXMLNamespaceMap::operator ==( const SvXMLNamespaceMap& rCmp ) const
91 {
92     return static_cast < int > (aNameHash == rCmp.aNameHash);
93 }
94 
95 sal_uInt16 SvXMLNamespaceMap::_Add( const OUString& rPrefix, const OUString &rName, sal_uInt16 nKey )
96 {
97     if( XML_NAMESPACE_UNKNOWN == nKey )
98     {
99         // create a new unique key with UNKNOWN flag set
100         nKey = XML_NAMESPACE_UNKNOWN_FLAG;
101         do
102         {
103             NameSpaceMap::const_iterator aIter = aNameMap.find ( nKey );
104             if( aIter == aNameMap.end() )
105                 break;
106             nKey++;
107         }
108         while ( sal_True );
109     }
110     ::vos::ORef<NameSpaceEntry> pEntry(new NameSpaceEntry);
111     pEntry->sName   = rName;
112     pEntry->nKey    = nKey;
113     pEntry->sPrefix = rPrefix;
114     aNameHash[ rPrefix ] = pEntry;
115     aNameMap [ nKey ]    = pEntry;
116     return nKey;
117 }
118 
119 sal_uInt16 SvXMLNamespaceMap::Add( const OUString& rPrefix, const OUString& rName,
120                                sal_uInt16 nKey )
121 {
122     if( XML_NAMESPACE_UNKNOWN == nKey )
123         nKey = GetKeyByName( rName );
124 
125     DBG_ASSERT( XML_NAMESPACE_NONE != nKey,
126                 "SvXMLNamespaceMap::Add: invalid namespace key" );
127 
128     if( XML_NAMESPACE_NONE == nKey )
129         return USHRT_MAX;
130 
131     if ( aNameHash.find ( rPrefix ) == aNameHash.end() )
132         nKey = _Add( rPrefix, rName, nKey );
133 
134     return nKey;
135 }
136 
137 sal_uInt16 SvXMLNamespaceMap::AddIfKnown( const OUString& rPrefix, const OUString& rName )
138 {
139     sal_uInt16 nKey = GetKeyByName( rName );
140 
141     DBG_ASSERT( XML_NAMESPACE_NONE != nKey,
142                 "SvXMLNamespaceMap::AddIfKnown: invalid namespace key" );
143 
144     if( XML_NAMESPACE_NONE == nKey )
145         return XML_NAMESPACE_UNKNOWN;
146 
147     if( XML_NAMESPACE_UNKNOWN != nKey )
148     {
149         NameSpaceHash::const_iterator aIter = aNameHash.find( rPrefix );
150         if( aIter == aNameHash.end() || (*aIter).second->sName != rName )
151             nKey = _Add( rPrefix, rName, nKey );
152     }
153 
154     return nKey;
155 }
156 
157 
158 sal_uInt16 SvXMLNamespaceMap::GetKeyByPrefix( const OUString& rPrefix ) const
159 {
160     NameSpaceHash::const_iterator aIter = aNameHash.find(rPrefix);
161     return (aIter != aNameHash.end()) ? (*aIter).second->nKey : USHRT_MAX;
162 }
163 
164 sal_uInt16 SvXMLNamespaceMap::GetKeyByName( const OUString& rName ) const
165 {
166     sal_uInt16 nKey = XML_NAMESPACE_UNKNOWN;
167     NameSpaceHash::const_iterator aIter = aNameHash.begin(), aEnd = aNameHash.end();
168     while (aIter != aEnd )
169     {
170         if ((*aIter).second->sName == rName)
171         {
172             nKey = (*aIter).second->nKey;
173             break;
174         }
175         aIter++;
176     }
177     return nKey;
178 }
179 
180 const OUString& SvXMLNamespaceMap::GetPrefixByKey( sal_uInt16 nKey ) const
181 {
182     NameSpaceMap::const_iterator aIter = aNameMap.find (nKey);
183     return (aIter != aNameMap.end()) ? (*aIter).second->sPrefix : sEmpty;
184 }
185 
186 const OUString& SvXMLNamespaceMap::GetNameByKey( sal_uInt16 nKey ) const
187 {
188     NameSpaceMap::const_iterator aIter = aNameMap.find (nKey);
189     return (aIter != aNameMap.end()) ? (*aIter).second->sName : sEmpty;
190 }
191 
192 OUString SvXMLNamespaceMap::GetAttrNameByKey( sal_uInt16 nKey ) const
193 {
194     OUStringBuffer sAttrName;
195     NameSpaceMap::const_iterator aIter = aNameMap.find ( nKey );
196     if (aIter != aNameMap.end())
197     {
198         sAttrName.append( sXMLNS  );
199         const ::rtl::OUString & prefix( (*aIter).second->sPrefix );
200         if (prefix.getLength()) // not default namespace
201         {
202             sAttrName.append( sal_Unicode(':') );
203             sAttrName.append( prefix );
204         }
205     }
206     return sAttrName.makeStringAndClear();
207 }
208 
209 OUString SvXMLNamespaceMap::GetQNameByKey( sal_uInt16 nKey,
210                             const OUString& rLocalName,
211                             sal_Bool bCache) const
212 {
213     // We always want to return at least the rLocalName...
214 
215     switch ( nKey )
216     {
217         case XML_NAMESPACE_UNKNOWN:
218             // ...if it's a completely unknown namespace, assert and return the local name
219             DBG_ASSERT( sal_False, "SvXMLNamespaceMap::GetQNameByKey: invalid namespace key" );
220         case XML_NAMESPACE_NONE:
221             // ...if there isn't one, return the local name
222             return rLocalName;
223         case XML_NAMESPACE_XMLNS:
224         {
225             // ...if it's in the xmlns namespace, make the prefix
226             // don't bother caching this, it rarely happens
227             OUStringBuffer sQName;
228             sQName.append ( sXMLNS );
229             if (rLocalName.getLength()) // not default namespace
230             {
231                 sQName.append ( sal_Unicode(':') );
232                 sQName.append ( rLocalName );
233             }
234             return sQName.makeStringAndClear();;
235         }
236         case XML_NAMESPACE_XML:
237         {
238             // this namespace is reserved, and needs not to be declared
239             OUStringBuffer sQName;
240             sQName.append ( GetXMLToken(XML_XML) );
241             sQName.append ( sal_Unicode(':') );
242             sQName.append ( rLocalName );
243             return sQName.makeStringAndClear();;
244         }
245         default:
246         {
247             QNameCache::const_iterator aQCacheIter;
248             if (bCache)
249                 aQCacheIter = aQNameCache.find ( QNamePair ( nKey, &rLocalName ) );
250             else
251                 aQCacheIter = aQNameCache.end();
252             if ( aQCacheIter != aQNameCache.end() )
253                 return (*aQCacheIter).second;
254             else
255             {
256                 NameSpaceMap::const_iterator aIter = aNameMap.find ( nKey );
257                 if ( aIter != aNameMap.end() )
258                 {
259                     OUStringBuffer sQName;
260                     // ...if it's in our map, make the prefix
261                     const OUString & prefix( (*aIter).second->sPrefix );
262                     if (prefix.getLength()) // not default namespace
263                     {
264                         sQName.append( prefix );
265                         sQName.append( sal_Unicode(':') );
266                     }
267                     sQName.append ( rLocalName );
268                     if (bCache)
269                     {
270                         OUString sString(sQName.makeStringAndClear());
271                         OUString *pString = new OUString ( rLocalName );
272                         const_cast < QNameCache * > (&aQNameCache)->operator[] ( QNamePair ( nKey, pString ) ) = sString;
273                         return sString;
274                     }
275                     else
276                         return sQName.makeStringAndClear();
277                 }
278                 else
279                 {
280                     // ... if it isn't, this is a Bad Thing, assert and return the local name
281                     DBG_ASSERT( sal_False, "SvXMLNamespaceMap::GetQNameByKey: invalid namespace key" );
282                     return rLocalName;
283                 }
284             }
285         }
286     }
287 }
288 
289 sal_uInt16 SvXMLNamespaceMap::_GetKeyByAttrName(
290                             const OUString& rAttrName,
291                             OUString *pLocalName,
292                             sal_Bool bCache) const
293 {
294     return _GetKeyByAttrName( rAttrName, 0, pLocalName, 0, bCache );
295 }
296 
297 sal_uInt16 SvXMLNamespaceMap::_GetKeyByAttrName( const OUString& rAttrName,
298                                             OUString *pPrefix,
299                                             OUString *pLocalName,
300                                             OUString *pNamespace,
301                                             sal_Bool bCache) const
302 {
303     sal_uInt16 nKey = XML_NAMESPACE_UNKNOWN;
304 
305     NameSpaceHash::const_iterator it;
306     if (bCache)
307         it = aNameCache.find ( rAttrName );
308     else
309         it = aNameCache.end();
310     if ( it != aNameCache.end() )
311     {
312         const NameSpaceEntry &rEntry = (*it).second.getBody();
313         if ( pPrefix )
314             *pPrefix = rEntry.sPrefix;
315         if ( pLocalName )
316             *pLocalName = rEntry.sName;
317         nKey = rEntry.nKey;
318         if ( pNamespace )
319         {
320             NameSpaceMap::const_iterator aMapIter = aNameMap.find (nKey);
321             *pNamespace = aMapIter != aNameMap.end() ? (*aMapIter).second->sName : sEmpty;
322         }
323     }
324     else
325     {
326     vos::ORef<NameSpaceEntry> xEntry(new NameSpaceEntry());
327 
328         sal_Int32 nColonPos = rAttrName.indexOf( sal_Unicode(':') );
329         if( -1L == nColonPos )
330         {
331             // case: no ':' found -> default namespace
332             xEntry->sPrefix = OUString();
333             xEntry->sName = rAttrName;
334         }
335         else
336         {
337             // normal case: ':' found -> get prefix/suffix
338             xEntry->sPrefix = rAttrName.copy( 0L, nColonPos );
339             xEntry->sName = rAttrName.copy( nColonPos + 1L );
340         }
341 
342         if( pPrefix )
343             *pPrefix = xEntry->sPrefix;
344         if( pLocalName )
345             *pLocalName = xEntry->sName;
346 
347         NameSpaceHash::const_iterator aIter = aNameHash.find( xEntry->sPrefix );
348         if ( aIter != aNameHash.end() )
349         {
350             // found: retrieve namespace key
351             nKey = xEntry->nKey = (*aIter).second->nKey;
352             if ( pNamespace )
353                 *pNamespace = (*aIter).second->sName;
354         }
355         else if ( xEntry->sPrefix == sXMLNS )
356             // not found, but xmlns prefix: return xmlns 'namespace'
357             nKey = xEntry->nKey = XML_NAMESPACE_XMLNS;
358         else if( nColonPos == -1L )
359             // not found, and no namespace: 'namespace' none
360             nKey = xEntry->nKey = XML_NAMESPACE_NONE;
361 
362         if (bCache)
363     {
364         typedef std::pair< const rtl::OUString, vos::ORef<NameSpaceEntry> > value_type;
365         (void) const_cast<NameSpaceHash*>(&aNameCache)->insert (value_type (rAttrName, xEntry));
366     }
367     }
368 
369     return nKey;
370 }
371 
372 sal_uInt16 SvXMLNamespaceMap::GetFirstKey() const
373 {
374     return aNameMap.empty() ? USHRT_MAX : (*aNameMap.begin()).second->nKey;
375 }
376 
377 sal_uInt16 SvXMLNamespaceMap::GetNextKey( sal_uInt16 nLastKey ) const
378 {
379     NameSpaceMap::const_iterator aIter = aNameMap.find ( nLastKey );
380     return (++aIter == aNameMap.end()) ? USHRT_MAX : (*aIter).second->nKey;
381 }
382 
383 
384 // All methods after this are deprecated...
385 
386 sal_uInt16 SvXMLNamespaceMap::GetKeyByIndex( sal_uInt16 nIdx ) const
387 {
388     return nIdx;
389 }
390 
391 sal_uInt16 SvXMLNamespaceMap::GetIndexByKey( sal_uInt16 nKey ) const
392 {
393     return nKey;
394 }
395 sal_uInt16 SvXMLNamespaceMap::GetFirstIndex() const
396 {
397     return aNameMap.empty() ? USHRT_MAX : (*aNameMap.begin()).second->nKey;
398 }
399 
400 sal_uInt16 SvXMLNamespaceMap::GetNextIndex( sal_uInt16 nOldIdx ) const
401 {
402     NameSpaceMap::const_iterator aIter = aNameMap.find ( nOldIdx );
403     return (++aIter == aNameMap.end()) ? USHRT_MAX : (*aIter).second->nKey;
404 }
405 
406 sal_Bool SvXMLNamespaceMap::AddAtIndex( sal_uInt16 /*nIdx*/, const OUString& rPrefix,
407                                     const OUString& rName, sal_uInt16 nKey )
408 {
409     sal_Bool bRet = sal_False;
410 
411     if( XML_NAMESPACE_UNKNOWN == nKey )
412         nKey = GetKeyByName( rName );
413 
414     DBG_ASSERT( XML_NAMESPACE_NONE != nKey,
415                 "SvXMLNamespaceMap::AddAtIndex: invalid namespace key" );
416     if( XML_NAMESPACE_NONE != nKey && ! ( aNameHash.count ( rPrefix ) ) )
417     {
418         _Add( rPrefix, rName, nKey );
419         bRet = sal_True;
420     }
421     return bRet;
422 }
423 
424 sal_Bool SvXMLNamespaceMap::AddAtIndex( sal_uInt16 nIdx, const sal_Char *pPrefix,
425                                     const sal_Char *pName, sal_uInt16 nKey )
426 {
427     OUString sPrefix( OUString::createFromAscii(pPrefix) );
428     OUString sName( OUString::createFromAscii(pName) );
429 
430     return AddAtIndex( nIdx, sPrefix, sName, nKey );
431 }
432 
433 OUString SvXMLNamespaceMap::GetAttrNameByIndex( sal_uInt16 nIdx ) const
434 {
435     return GetAttrNameByKey( nIdx );
436 }
437 
438 OUString SvXMLNamespaceMap::GetQNameByIndex( sal_uInt16 nIdx,
439                                            const OUString& rLocalName ) const
440 {
441     return GetQNameByKey( nIdx, rLocalName );
442 }
443 
444 const OUString& SvXMLNamespaceMap::GetPrefixByIndex( sal_uInt16 nIdx ) const
445 {
446     NameSpaceMap::const_iterator aIter = aNameMap.find (nIdx);
447     return (aIter != aNameMap.end()) ? (*aIter).second->sPrefix : sEmpty;
448 }
449 
450 const OUString& SvXMLNamespaceMap::GetNameByIndex( sal_uInt16 nIdx ) const
451 {
452     NameSpaceMap::const_iterator aIter = aNameMap.find (nIdx);
453     return (aIter != aNameMap.end()) ? (*aIter).second->sName : sEmpty;
454 }
455 
456 sal_uInt16 SvXMLNamespaceMap::GetIndexByPrefix( const OUString& rPrefix ) const
457 {
458     NameSpaceHash::const_iterator aIter = aNameHash.find(rPrefix);
459     return (aIter != aNameHash.end()) ? (*aIter).second->nKey : USHRT_MAX;
460 }
461 sal_uInt16 SvXMLNamespaceMap::GetKeyByAttrName(
462                             const OUString& rAttrName,
463                             OUString *pLocalName,
464                             sal_uInt16 /*nIdxGuess*/) const
465 {
466     return _GetKeyByAttrName( rAttrName, 0, pLocalName, 0 );
467 }
468 
469 sal_uInt16 SvXMLNamespaceMap::GetKeyByAttrName( const OUString& rAttrName,
470                                             OUString *pPrefix,
471                                             OUString *pLocalName,
472                                             OUString *pNamespace,
473                                             sal_uInt16 /*nIdxGuess*/ ) const
474 {
475     return _GetKeyByAttrName ( rAttrName, pPrefix, pLocalName, pNamespace );
476 }
477 
478 sal_Bool SvXMLNamespaceMap::NormalizeURI( ::rtl::OUString& rName )
479 {
480     // try OASIS + W3 URI normalization
481     sal_Bool bSuccess = NormalizeOasisURN( rName );
482     if( ! bSuccess )
483         bSuccess = NormalizeW3URI( rName );
484     return bSuccess;
485 }
486 
487 sal_Bool SvXMLNamespaceMap::NormalizeW3URI( ::rtl::OUString& rName )
488 {
489     // check if URI matches:
490     // http://www.w3.org/[0-9]*/[:letter:]*
491     //                   (year)/(WG name)
492     // For the following WG/standards names:
493     // - xforms
494 
495     sal_Bool bSuccess = sal_False;
496     const OUString sURIPrefix = GetXMLToken( XML_URI_W3_PREFIX );
497     if( rName.compareTo( sURIPrefix, sURIPrefix.getLength() ) == 0 )
498     {
499         const OUString sURISuffix = GetXMLToken( XML_URI_XFORMS_SUFFIX );
500         sal_Int32 nCompareFrom = rName.getLength() - sURISuffix.getLength();
501         if( rName.copy( nCompareFrom ).equals( sURISuffix ) )
502         {
503             // found W3 prefix, and xforms suffix
504             rName = GetXMLToken( XML_N_XFORMS_1_0 );
505             bSuccess = sal_True;
506         }
507     }
508     return bSuccess;
509 }
510 
511 sal_Bool SvXMLNamespaceMap::NormalizeOasisURN( ::rtl::OUString& rName )
512 {
513     // #i38644#
514     // we exported the wrong namespace for smil, so we correct this here on load
515     // for older documents
516     if( IsXMLToken( rName, ::xmloff::token::XML_N_SVG ) )
517     {
518         rName = GetXMLToken( ::xmloff::token::XML_N_SVG_COMPAT );
519         return sal_True;
520     }
521     else if( IsXMLToken( rName, ::xmloff::token::XML_N_FO ) )
522     {
523         rName = GetXMLToken( ::xmloff::token::XML_N_FO_COMPAT );
524         return sal_True;
525     }
526     else if( IsXMLToken( rName, ::xmloff::token::XML_N_SMIL ) ||
527              IsXMLToken( rName, ::xmloff::token::XML_N_SMIL_OLD )  )
528     {
529         rName = GetXMLToken( ::xmloff::token::XML_N_SMIL_COMPAT );
530         return sal_True;
531     }
532 
533     //
534     // Check if URN matches
535     // :urn:oasis:names:tc:[^:]*:xmlns:[^:]*:1.[^:]*
536     //                     |---|       |---| |-----|
537     //                     TC-Id      Sub-Id Version
538 
539     sal_Int32 nNameLen = rName.getLength();
540     // :urn:oasis:names:tc.*
541     const OUString& rOasisURN = GetXMLToken( XML_URN_OASIS_NAMES_TC );
542     if( 0 != rName.compareTo( rOasisURN, rOasisURN.getLength() ) )
543         return sal_False;
544 
545     // :urn:oasis:names:tc:.*
546     sal_Int32 nPos = rOasisURN.getLength();
547     if( nPos >= nNameLen || rName[nPos] != ':' )
548         return sal_False;
549 
550     // :urn:oasis:names:tc:[^:]:.*
551     sal_Int32 nTCIdStart = nPos+1;
552     sal_Int32 nTCIdEnd = rName.indexOf( ':', nTCIdStart );
553     if( -1 == nTCIdEnd )
554         return sal_False;
555 
556     // :urn:oasis:names:tc:[^:]:xmlns.*
557     nPos = nTCIdEnd + 1;
558     OUString sTmp( rName.copy( nPos ) );
559     const OUString& rXMLNS = GetXMLToken( XML_XMLNS );
560     if( 0!= sTmp.compareTo( rXMLNS, rXMLNS.getLength() ) )
561         return sal_False;
562 
563     // :urn:oasis:names:tc:[^:]:xmlns:.*
564     nPos += rXMLNS.getLength();
565     if( nPos >= nNameLen || rName[nPos] != ':' )
566         return sal_False;
567 
568     // :urn:oasis:names:tc:[^:]:xmlns:[^:]*:.*
569     nPos = rName.indexOf( ':', nPos+1 );
570     if( -1 == nPos )
571         return sal_False;
572 
573     // :urn:oasis:names:tc:[^:]:xmlns:[^:]*:[^:][^:][^:][^:]*
574     sal_Int32 nVersionStart = nPos+1;
575     if( nVersionStart+2 >= nNameLen ||
576         -1 != rName.indexOf( ':', nVersionStart ) )
577         return sal_False;
578 
579     // :urn:oasis:names:tc:[^:]:xmlns:[^:]*:1\.[^:][^:]*
580     if( rName[nVersionStart] != '1' || rName[nVersionStart+1] != '.' )
581         return sal_False;
582 
583     // replace [tcid] with current TCID and version with current version.
584     OUStringBuffer aNewName( nNameLen +20 );
585     aNewName.append( rName.copy( 0, nTCIdStart ) );
586     aNewName.append( GetXMLToken( XML_OPENDOCUMENT ) );
587     aNewName.append( rName.copy( nTCIdEnd, nVersionStart-nTCIdEnd ) );
588     aNewName.append( GetXMLToken( XML_1_0 ) );
589 
590     rName = aNewName.makeStringAndClear();
591 
592     return sal_True;
593 }
594