1*b5088357SAndrew Rist /**************************************************************
2cdf0e10cSrcweir  *
3*b5088357SAndrew Rist  * Licensed to the Apache Software Foundation (ASF) under one
4*b5088357SAndrew Rist  * or more contributor license agreements.  See the NOTICE file
5*b5088357SAndrew Rist  * distributed with this work for additional information
6*b5088357SAndrew Rist  * regarding copyright ownership.  The ASF licenses this file
7*b5088357SAndrew Rist  * to you under the Apache License, Version 2.0 (the
8*b5088357SAndrew Rist  * "License"); you may not use this file except in compliance
9*b5088357SAndrew Rist  * with the License.  You may obtain a copy of the License at
10*b5088357SAndrew Rist  *
11*b5088357SAndrew Rist  *   http://www.apache.org/licenses/LICENSE-2.0
12*b5088357SAndrew Rist  *
13*b5088357SAndrew Rist  * Unless required by applicable law or agreed to in writing,
14*b5088357SAndrew Rist  * software distributed under the License is distributed on an
15*b5088357SAndrew Rist  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16*b5088357SAndrew Rist  * KIND, either express or implied.  See the License for the
17*b5088357SAndrew Rist  * specific language governing permissions and limitations
18*b5088357SAndrew Rist  * under the License.
19*b5088357SAndrew Rist  *
20*b5088357SAndrew Rist  *************************************************************/
21*b5088357SAndrew Rist 
22*b5088357SAndrew Rist 
23cdf0e10cSrcweir 
24cdf0e10cSrcweir // MARKER(update_precomp.py): autogen include statement, do not remove
25cdf0e10cSrcweir #include "precompiled_unotools.hxx"
26cdf0e10cSrcweir 
27cdf0e10cSrcweir #include "unotools/configpathes.hxx"
28cdf0e10cSrcweir #include <rtl/ustring.hxx>
29cdf0e10cSrcweir #include <rtl/ustrbuf.hxx>
30cdf0e10cSrcweir #include <osl/diagnose.h>
31cdf0e10cSrcweir 
32cdf0e10cSrcweir //----------------------------------------------------------------------------
33cdf0e10cSrcweir namespace utl
34cdf0e10cSrcweir {
35cdf0e10cSrcweir //----------------------------------------------------------------------------
36cdf0e10cSrcweir 
37cdf0e10cSrcweir     using ::rtl::OUString;
38cdf0e10cSrcweir     using ::rtl::OUStringBuffer;
39cdf0e10cSrcweir 
40cdf0e10cSrcweir //----------------------------------------------------------------------------
41cdf0e10cSrcweir 
42cdf0e10cSrcweir static
lcl_resolveCharEntities(OUString & aLocalString)43cdf0e10cSrcweir void lcl_resolveCharEntities(OUString & aLocalString)
44cdf0e10cSrcweir {
45cdf0e10cSrcweir     sal_Int32 nEscapePos=aLocalString.indexOf('&');
46cdf0e10cSrcweir     if (nEscapePos < 0) return;
47cdf0e10cSrcweir 
48cdf0e10cSrcweir     OUStringBuffer aResult;
49cdf0e10cSrcweir     sal_Int32 nStart = 0;
50cdf0e10cSrcweir 
51cdf0e10cSrcweir     do
52cdf0e10cSrcweir     {
53cdf0e10cSrcweir         sal_Unicode ch = 0;
54cdf0e10cSrcweir         if (aLocalString.matchAsciiL(RTL_CONSTASCII_STRINGPARAM("&amp;"),nEscapePos))
55cdf0e10cSrcweir             ch = '&';
56cdf0e10cSrcweir 
57cdf0e10cSrcweir         else if (aLocalString.matchAsciiL(RTL_CONSTASCII_STRINGPARAM("&apos;"),nEscapePos))
58cdf0e10cSrcweir             ch = '\'';
59cdf0e10cSrcweir 
60cdf0e10cSrcweir         else if (aLocalString.matchAsciiL(RTL_CONSTASCII_STRINGPARAM("&quot;"),nEscapePos))
61cdf0e10cSrcweir             ch = '"';
62cdf0e10cSrcweir 
63cdf0e10cSrcweir         OSL_ENSURE(ch,"Configuration path contains '&' that is not part of a valid character escape");
64cdf0e10cSrcweir         if (ch)
65cdf0e10cSrcweir         {
66cdf0e10cSrcweir             aResult.append(aLocalString.copy(nStart,nEscapePos-nStart)).append(ch);
67cdf0e10cSrcweir 
68cdf0e10cSrcweir             sal_Int32 nEscapeEnd=aLocalString.indexOf(';',nEscapePos);
69cdf0e10cSrcweir             nStart = nEscapeEnd+1;
70cdf0e10cSrcweir             nEscapePos=aLocalString.indexOf('&',nStart);
71cdf0e10cSrcweir         }
72cdf0e10cSrcweir         else
73cdf0e10cSrcweir         {
74cdf0e10cSrcweir             nEscapePos=aLocalString.indexOf('&',nEscapePos+1);
75cdf0e10cSrcweir         }
76cdf0e10cSrcweir     }
77cdf0e10cSrcweir     while ( nEscapePos > 0);
78cdf0e10cSrcweir 
79cdf0e10cSrcweir     aResult.append(aLocalString.copy(nStart));
80cdf0e10cSrcweir 
81cdf0e10cSrcweir     aLocalString = aResult.makeStringAndClear();
82cdf0e10cSrcweir }
83cdf0e10cSrcweir 
84cdf0e10cSrcweir //----------------------------------------------------------------------------
splitLastFromConfigurationPath(OUString const & _sInPath,OUString & _rsOutPath,OUString & _rsLocalName)85cdf0e10cSrcweir sal_Bool splitLastFromConfigurationPath(OUString const& _sInPath,
86cdf0e10cSrcweir                                         OUString& _rsOutPath,
87cdf0e10cSrcweir                                         OUString& _rsLocalName)
88cdf0e10cSrcweir {
89cdf0e10cSrcweir     sal_Int32 nStart,nEnd;
90cdf0e10cSrcweir 
91cdf0e10cSrcweir     sal_Int32 nPos = _sInPath.getLength()-1;
92cdf0e10cSrcweir 
93cdf0e10cSrcweir     // strip trailing slash
94cdf0e10cSrcweir     if (nPos > 0 && _sInPath[ nPos ] == sal_Unicode('/'))
95cdf0e10cSrcweir     {
96cdf0e10cSrcweir         OSL_ENSURE(false, "Invalid config path: trailing '/' is not allowed");
97cdf0e10cSrcweir         --nPos;
98cdf0e10cSrcweir     }
99cdf0e10cSrcweir 
100cdf0e10cSrcweir     // check for predicate ['xxx'] or ["yyy"]
101cdf0e10cSrcweir     if (nPos  > 0 && _sInPath[ nPos ] == sal_Unicode(']'))
102cdf0e10cSrcweir     {
103cdf0e10cSrcweir         sal_Unicode chQuote = _sInPath[--nPos];
104cdf0e10cSrcweir 
105cdf0e10cSrcweir         if (chQuote == '\'' || chQuote == '\"')
106cdf0e10cSrcweir         {
107cdf0e10cSrcweir             nEnd = nPos;
108cdf0e10cSrcweir             nPos = _sInPath.lastIndexOf(chQuote,nEnd);
109cdf0e10cSrcweir             nStart = nPos + 1;
110cdf0e10cSrcweir             --nPos; // nPos = rInPath.lastIndexOf('[',nPos);
111cdf0e10cSrcweir         }
112cdf0e10cSrcweir         else // allow [xxx]
113cdf0e10cSrcweir         {
114cdf0e10cSrcweir             nEnd = nPos + 1;
115cdf0e10cSrcweir             nPos = _sInPath.lastIndexOf('[',nEnd);
116cdf0e10cSrcweir             nStart = nPos + 1;
117cdf0e10cSrcweir         }
118cdf0e10cSrcweir 
119cdf0e10cSrcweir         OSL_ENSURE(nPos >= 0 && _sInPath[nPos] == '[', "Invalid config path: unmatched quotes or brackets");
120cdf0e10cSrcweir         if (nPos >= 0 && _sInPath[nPos] == '[')
121cdf0e10cSrcweir         {
122cdf0e10cSrcweir             nPos =  _sInPath.lastIndexOf('/',nPos);
123cdf0e10cSrcweir         }
124cdf0e10cSrcweir         else // defined behavior for invalid pathes
125cdf0e10cSrcweir         {
126cdf0e10cSrcweir             nStart = 0, nEnd = _sInPath.getLength();
127cdf0e10cSrcweir             nPos = -1;
128cdf0e10cSrcweir         }
129cdf0e10cSrcweir 
130cdf0e10cSrcweir     }
131cdf0e10cSrcweir     else
132cdf0e10cSrcweir     {
133cdf0e10cSrcweir         nEnd = nPos+1;
134cdf0e10cSrcweir         nPos = _sInPath.lastIndexOf('/',nEnd);
135cdf0e10cSrcweir         nStart = nPos + 1;
136cdf0e10cSrcweir     }
137cdf0e10cSrcweir     OSL_ASSERT( -1 <= nPos &&
138cdf0e10cSrcweir                 nPos < nStart &&
139cdf0e10cSrcweir                 nStart < nEnd &&
140cdf0e10cSrcweir                 nEnd <= _sInPath.getLength() );
141cdf0e10cSrcweir 
142cdf0e10cSrcweir     OSL_ASSERT(nPos == -1 || _sInPath[nPos] == '/');
143cdf0e10cSrcweir     OSL_ENSURE(nPos != 0 , "Invalid config child path: immediate child of root");
144cdf0e10cSrcweir 
145cdf0e10cSrcweir     _rsLocalName = _sInPath.copy(nStart, nEnd-nStart);
146cdf0e10cSrcweir     _rsOutPath = (nPos > 0) ? _sInPath.copy(0,nPos) : OUString();
147cdf0e10cSrcweir     lcl_resolveCharEntities(_rsLocalName);
148cdf0e10cSrcweir 
149cdf0e10cSrcweir     return nPos >= 0;
150cdf0e10cSrcweir }
151cdf0e10cSrcweir 
152cdf0e10cSrcweir //----------------------------------------------------------------------------
extractFirstFromConfigurationPath(OUString const & _sInPath,OUString * _sOutPath)153cdf0e10cSrcweir OUString extractFirstFromConfigurationPath(OUString const& _sInPath, OUString* _sOutPath)
154cdf0e10cSrcweir {
155cdf0e10cSrcweir     sal_Int32 nSep      = _sInPath.indexOf('/');
156cdf0e10cSrcweir     sal_Int32 nBracket  = _sInPath.indexOf('[');
157cdf0e10cSrcweir 
158cdf0e10cSrcweir     sal_Int32 nStart    = nBracket + 1;
159cdf0e10cSrcweir     sal_Int32 nEnd      = nSep;
160cdf0e10cSrcweir 
161cdf0e10cSrcweir     if (0 <= nBracket) // found a bracket-quoted relative path
162cdf0e10cSrcweir     {
163cdf0e10cSrcweir         if (nSep < 0 || nBracket < nSep) // and the separator comes after it
164cdf0e10cSrcweir         {
165cdf0e10cSrcweir             sal_Unicode chQuote = _sInPath[nStart];
166cdf0e10cSrcweir             if (chQuote == '\'' || chQuote == '\"')
167cdf0e10cSrcweir             {
168cdf0e10cSrcweir                 ++nStart;
169cdf0e10cSrcweir                 nEnd      = _sInPath.indexOf(chQuote, nStart+1);
170cdf0e10cSrcweir                 nBracket  = nEnd+1;
171cdf0e10cSrcweir             }
172cdf0e10cSrcweir             else
173cdf0e10cSrcweir             {
174cdf0e10cSrcweir                 nEnd = _sInPath.indexOf(']',nStart);
175cdf0e10cSrcweir                 nBracket = nEnd;
176cdf0e10cSrcweir             }
177cdf0e10cSrcweir             OSL_ENSURE(nEnd > nStart && _sInPath[nBracket] == ']', "Invalid config path: improper mismatch of quote or bracket");
178cdf0e10cSrcweir             OSL_ENSURE((nBracket+1 == _sInPath.getLength() && nSep == -1) || (_sInPath[nBracket+1] == '/' && nSep == nBracket+1), "Invalid config path: brackets not followed by slash");
179cdf0e10cSrcweir         }
180cdf0e10cSrcweir         else // ... but our initial element name is in simple form
181cdf0e10cSrcweir             nStart = 0;
182cdf0e10cSrcweir     }
183cdf0e10cSrcweir 
184cdf0e10cSrcweir     OUString sResult = (nEnd >= 0) ? _sInPath.copy(nStart, nEnd-nStart) : _sInPath;
185cdf0e10cSrcweir     lcl_resolveCharEntities(sResult);
186cdf0e10cSrcweir 
187cdf0e10cSrcweir     if (_sOutPath != 0)
188cdf0e10cSrcweir     {
189cdf0e10cSrcweir         *_sOutPath = (nSep >= 0) ? _sInPath.copy(nSep + 1) : OUString();
190cdf0e10cSrcweir     }
191cdf0e10cSrcweir 
192cdf0e10cSrcweir     return sResult;
193cdf0e10cSrcweir }
194cdf0e10cSrcweir 
195cdf0e10cSrcweir //----------------------------------------------------------------------------
196cdf0e10cSrcweir 
197cdf0e10cSrcweir // find the position after the prefix in the nested path
198cdf0e10cSrcweir static inline
lcl_findPrefixEnd(OUString const & _sNestedPath,OUString const & _sPrefixPath)199cdf0e10cSrcweir sal_Int32 lcl_findPrefixEnd(OUString const& _sNestedPath, OUString const& _sPrefixPath)
200cdf0e10cSrcweir {
201cdf0e10cSrcweir     // TODO: currently handles only exact prefix matches
202cdf0e10cSrcweir     sal_Int32 nPrefixLength = _sPrefixPath.getLength();
203cdf0e10cSrcweir 
204cdf0e10cSrcweir     OSL_ENSURE(nPrefixLength == 0 || _sPrefixPath[nPrefixLength-1] != '/',
205cdf0e10cSrcweir                 "Cannot handle slash-terminated prefix pathes");
206cdf0e10cSrcweir 
207cdf0e10cSrcweir     sal_Bool bIsPrefix;
208cdf0e10cSrcweir     if (_sNestedPath.getLength() > nPrefixLength)
209cdf0e10cSrcweir     {
210cdf0e10cSrcweir         bIsPrefix = _sNestedPath[nPrefixLength] == '/' &&
211cdf0e10cSrcweir                     _sNestedPath.compareTo(_sPrefixPath,nPrefixLength) == 0;
212cdf0e10cSrcweir         ++nPrefixLength;
213cdf0e10cSrcweir     }
214cdf0e10cSrcweir     else if (_sNestedPath.getLength() == nPrefixLength)
215cdf0e10cSrcweir     {
216cdf0e10cSrcweir         bIsPrefix = _sNestedPath.equals(_sPrefixPath);
217cdf0e10cSrcweir     }
218cdf0e10cSrcweir     else
219cdf0e10cSrcweir     {
220cdf0e10cSrcweir         bIsPrefix = false;
221cdf0e10cSrcweir     }
222cdf0e10cSrcweir 
223cdf0e10cSrcweir     return bIsPrefix ? nPrefixLength : 0;
224cdf0e10cSrcweir }
225cdf0e10cSrcweir 
226cdf0e10cSrcweir //----------------------------------------------------------------------------
isPrefixOfConfigurationPath(OUString const & _sNestedPath,OUString const & _sPrefixPath)227cdf0e10cSrcweir sal_Bool isPrefixOfConfigurationPath(OUString const& _sNestedPath,
228cdf0e10cSrcweir                                      OUString const& _sPrefixPath)
229cdf0e10cSrcweir {
230cdf0e10cSrcweir     return _sPrefixPath.getLength() == 0 || lcl_findPrefixEnd(_sNestedPath,_sPrefixPath) != 0;
231cdf0e10cSrcweir }
232cdf0e10cSrcweir 
233cdf0e10cSrcweir //----------------------------------------------------------------------------
dropPrefixFromConfigurationPath(OUString const & _sNestedPath,OUString const & _sPrefixPath)234cdf0e10cSrcweir OUString dropPrefixFromConfigurationPath(OUString const& _sNestedPath,
235cdf0e10cSrcweir                                          OUString const& _sPrefixPath)
236cdf0e10cSrcweir {
237cdf0e10cSrcweir     if ( sal_Int32 nPrefixEnd = lcl_findPrefixEnd(_sNestedPath,_sPrefixPath) )
238cdf0e10cSrcweir     {
239cdf0e10cSrcweir 	    return _sNestedPath.copy(nPrefixEnd);
240cdf0e10cSrcweir     }
241cdf0e10cSrcweir     else
242cdf0e10cSrcweir     {
243cdf0e10cSrcweir         OSL_ENSURE(_sPrefixPath.getLength() == 0,  "Path does not start with expected prefix");
244cdf0e10cSrcweir 
245cdf0e10cSrcweir         return _sNestedPath;
246cdf0e10cSrcweir     }
247cdf0e10cSrcweir }
248cdf0e10cSrcweir 
249cdf0e10cSrcweir //----------------------------------------------------------------------------
250cdf0e10cSrcweir static
lcl_wrapName(const OUString & _sContent,const OUString & _sType)251cdf0e10cSrcweir OUString lcl_wrapName(const OUString& _sContent, const OUString& _sType)
252cdf0e10cSrcweir {
253cdf0e10cSrcweir     const sal_Unicode * const pBeginContent = _sContent.getStr();
254cdf0e10cSrcweir     const sal_Unicode * const pEndContent   = pBeginContent + _sContent.getLength();
255cdf0e10cSrcweir 
256cdf0e10cSrcweir     OSL_PRECOND(_sType.getLength(), "Unexpected config type name: empty");
257cdf0e10cSrcweir     OSL_PRECOND(pBeginContent <= pEndContent, "Invalid config name: empty");
258cdf0e10cSrcweir 
259cdf0e10cSrcweir     if (pBeginContent == pEndContent)
260cdf0e10cSrcweir         return _sType;
261cdf0e10cSrcweir 
262cdf0e10cSrcweir     rtl::OUStringBuffer aNormalized(_sType.getLength() + _sContent.getLength() + 4); // reserve approximate size initially
263cdf0e10cSrcweir 
264cdf0e10cSrcweir     // prefix: type, opening bracket and quote
265cdf0e10cSrcweir     aNormalized.append( _sType ).appendAscii( RTL_CONSTASCII_STRINGPARAM("['") );
266cdf0e10cSrcweir 
267cdf0e10cSrcweir     // content: copy over each char and handle escaping
268cdf0e10cSrcweir     for(const sal_Unicode* pCur = pBeginContent; pCur != pEndContent; ++pCur)
269cdf0e10cSrcweir     {
270cdf0e10cSrcweir         // append (escape if needed)
271cdf0e10cSrcweir         switch(*pCur)
272cdf0e10cSrcweir         {
273cdf0e10cSrcweir         case sal_Unicode('&') : aNormalized.appendAscii( RTL_CONSTASCII_STRINGPARAM("&amp;") ); break;
274cdf0e10cSrcweir         case sal_Unicode('\''): aNormalized.appendAscii( RTL_CONSTASCII_STRINGPARAM("&apos;") ); break;
275cdf0e10cSrcweir         case sal_Unicode('\"'): aNormalized.appendAscii( RTL_CONSTASCII_STRINGPARAM("&quot;") ); break;
276cdf0e10cSrcweir 
277cdf0e10cSrcweir         default: aNormalized.append( *pCur );
278cdf0e10cSrcweir         }
279cdf0e10cSrcweir     }
280cdf0e10cSrcweir 
281cdf0e10cSrcweir     // suffix: closing quote and bracket
282cdf0e10cSrcweir     aNormalized.appendAscii( RTL_CONSTASCII_STRINGPARAM("']") );
283cdf0e10cSrcweir 
284cdf0e10cSrcweir     return aNormalized.makeStringAndClear();
285cdf0e10cSrcweir }
286cdf0e10cSrcweir 
287cdf0e10cSrcweir //----------------------------------------------------------------------------
288cdf0e10cSrcweir 
wrapConfigurationElementName(OUString const & _sElementName)289cdf0e10cSrcweir OUString wrapConfigurationElementName(OUString const& _sElementName)
290cdf0e10cSrcweir {
291cdf0e10cSrcweir     return lcl_wrapName(_sElementName, OUString(RTL_CONSTASCII_USTRINGPARAM("*")) );
292cdf0e10cSrcweir }
293cdf0e10cSrcweir 
294cdf0e10cSrcweir //----------------------------------------------------------------------------
295cdf0e10cSrcweir 
wrapConfigurationElementName(OUString const & _sElementName,OUString const & _sTypeName)296cdf0e10cSrcweir OUString wrapConfigurationElementName(OUString const& _sElementName,
297cdf0e10cSrcweir                                       OUString const& _sTypeName)
298cdf0e10cSrcweir {
299cdf0e10cSrcweir     // todo: check that _sTypeName is valid
300cdf0e10cSrcweir     return lcl_wrapName(_sElementName, _sTypeName);
301cdf0e10cSrcweir }
302cdf0e10cSrcweir 
303cdf0e10cSrcweir //----------------------------------------------------------------------------
304cdf0e10cSrcweir } // namespace utl
305