1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
21 #include "unotools/configpaths.hxx"
22 #include <rtl/ustring.hxx>
23 #include <rtl/ustrbuf.hxx>
24 #include <osl/diagnose.h>
26 //----------------------------------------------------------------------------
29 //----------------------------------------------------------------------------
32 //----------------------------------------------------------------------------
35 void lcl_resolveCharEntities(OUString
& aLocalString
)
37 sal_Int32 nEscapePos
=aLocalString
.indexOf('&');
38 if (nEscapePos
< 0) return;
40 OUStringBuffer aResult
;
46 if (aLocalString
.match("&",nEscapePos
))
49 else if (aLocalString
.match("'",nEscapePos
))
52 else if (aLocalString
.match(""",nEscapePos
))
55 OSL_ENSURE(ch
,"Configuration path contains '&' that is not part of a valid character escape");
58 aResult
.append(aLocalString
.copy(nStart
,nEscapePos
-nStart
)).append(ch
);
60 sal_Int32 nEscapeEnd
=aLocalString
.indexOf(';',nEscapePos
);
61 nStart
= nEscapeEnd
+1;
62 nEscapePos
=aLocalString
.indexOf('&',nStart
);
66 nEscapePos
=aLocalString
.indexOf('&',nEscapePos
+1);
69 while ( nEscapePos
> 0);
71 aResult
.append(aLocalString
.copy(nStart
));
73 aLocalString
= aResult
.makeStringAndClear();
76 //----------------------------------------------------------------------------
77 sal_Bool
splitLastFromConfigurationPath(OUString
const& _sInPath
,
79 OUString
& _rsLocalName
)
81 sal_Int32 nStart
,nEnd
;
83 sal_Int32 nPos
= _sInPath
.getLength()-1;
85 // strip trailing slash
86 if (nPos
> 0 && _sInPath
[ nPos
] == sal_Unicode('/'))
88 OSL_FAIL("Invalid config path: trailing '/' is not allowed");
92 // check for predicate ['xxx'] or ["yyy"]
93 if (nPos
> 0 && _sInPath
[ nPos
] == sal_Unicode(']'))
95 sal_Unicode chQuote
= _sInPath
[--nPos
];
97 if (chQuote
== '\'' || chQuote
== '\"')
100 nPos
= _sInPath
.lastIndexOf(chQuote
,nEnd
);
102 --nPos
; // nPos = rInPath.lastIndexOf('[',nPos);
107 nPos
= _sInPath
.lastIndexOf('[',nEnd
);
111 OSL_ENSURE(nPos
>= 0 && _sInPath
[nPos
] == '[', "Invalid config path: unmatched quotes or brackets");
112 if (nPos
>= 0 && _sInPath
[nPos
] == '[')
114 nPos
= _sInPath
.lastIndexOf('/',nPos
);
116 else // defined behavior for invalid paths
118 nStart
= 0, nEnd
= _sInPath
.getLength();
126 nPos
= _sInPath
.lastIndexOf('/',nEnd
);
129 OSL_ASSERT( -1 <= nPos
&&
132 nEnd
<= _sInPath
.getLength() );
134 OSL_ASSERT(nPos
== -1 || _sInPath
[nPos
] == '/');
135 OSL_ENSURE(nPos
!= 0 , "Invalid config child path: immediate child of root");
137 _rsLocalName
= _sInPath
.copy(nStart
, nEnd
-nStart
);
138 _rsOutPath
= (nPos
> 0) ? _sInPath
.copy(0,nPos
) : OUString();
139 lcl_resolveCharEntities(_rsLocalName
);
144 //----------------------------------------------------------------------------
145 OUString
extractFirstFromConfigurationPath(OUString
const& _sInPath
, OUString
* _sOutPath
)
147 sal_Int32 nSep
= _sInPath
.indexOf('/');
148 sal_Int32 nBracket
= _sInPath
.indexOf('[');
150 sal_Int32 nStart
= nBracket
+ 1;
151 sal_Int32 nEnd
= nSep
;
153 if (0 <= nBracket
) // found a bracket-quoted relative path
155 if (nSep
< 0 || nBracket
< nSep
) // and the separator comes after it
157 sal_Unicode chQuote
= _sInPath
[nStart
];
158 if (chQuote
== '\'' || chQuote
== '\"')
161 nEnd
= _sInPath
.indexOf(chQuote
, nStart
+1);
166 nEnd
= _sInPath
.indexOf(']',nStart
);
169 OSL_ENSURE(nEnd
> nStart
&& _sInPath
[nBracket
] == ']', "Invalid config path: improper mismatch of quote or bracket");
170 OSL_ENSURE((nBracket
+1 == _sInPath
.getLength() && nSep
== -1) || (_sInPath
[nBracket
+1] == '/' && nSep
== nBracket
+1), "Invalid config path: brackets not followed by slash");
172 else // ... but our initial element name is in simple form
176 OUString sResult
= (nEnd
>= 0) ? _sInPath
.copy(nStart
, nEnd
-nStart
) : _sInPath
;
177 lcl_resolveCharEntities(sResult
);
181 *_sOutPath
= (nSep
>= 0) ? _sInPath
.copy(nSep
+ 1) : OUString();
187 //----------------------------------------------------------------------------
189 // find the position after the prefix in the nested path
191 sal_Int32
lcl_findPrefixEnd(OUString
const& _sNestedPath
, OUString
const& _sPrefixPath
)
193 // TODO: currently handles only exact prefix matches
194 sal_Int32 nPrefixLength
= _sPrefixPath
.getLength();
196 OSL_ENSURE(nPrefixLength
== 0 || _sPrefixPath
[nPrefixLength
-1] != '/',
197 "Cannot handle slash-terminated prefix paths");
200 if (_sNestedPath
.getLength() > nPrefixLength
)
202 bIsPrefix
= _sNestedPath
[nPrefixLength
] == '/' &&
203 _sNestedPath
.compareTo(_sPrefixPath
,nPrefixLength
) == 0;
206 else if (_sNestedPath
.getLength() == nPrefixLength
)
208 bIsPrefix
= _sNestedPath
.equals(_sPrefixPath
);
215 return bIsPrefix
? nPrefixLength
: 0;
218 //----------------------------------------------------------------------------
219 sal_Bool
isPrefixOfConfigurationPath(OUString
const& _sNestedPath
,
220 OUString
const& _sPrefixPath
)
222 return _sPrefixPath
.isEmpty() || lcl_findPrefixEnd(_sNestedPath
,_sPrefixPath
) != 0;
225 //----------------------------------------------------------------------------
226 OUString
dropPrefixFromConfigurationPath(OUString
const& _sNestedPath
,
227 OUString
const& _sPrefixPath
)
229 if ( sal_Int32 nPrefixEnd
= lcl_findPrefixEnd(_sNestedPath
,_sPrefixPath
) )
231 return _sNestedPath
.copy(nPrefixEnd
);
235 OSL_ENSURE(_sPrefixPath
.isEmpty(), "Path does not start with expected prefix");
241 //----------------------------------------------------------------------------
243 OUString
lcl_wrapName(const OUString
& _sContent
, const OUString
& _sType
)
245 const sal_Unicode
* const pBeginContent
= _sContent
.getStr();
246 const sal_Unicode
* const pEndContent
= pBeginContent
+ _sContent
.getLength();
248 OSL_PRECOND(!_sType
.isEmpty(), "Unexpected config type name: empty");
249 OSL_PRECOND(pBeginContent
<= pEndContent
, "Invalid config name: empty");
251 if (pBeginContent
== pEndContent
)
254 OUStringBuffer
aNormalized(_sType
.getLength() + _sContent
.getLength() + 4); // reserve approximate size initially
256 // prefix: type, opening bracket and quote
257 aNormalized
.append( _sType
).append( "['" );
259 // content: copy over each char and handle escaping
260 for(const sal_Unicode
* pCur
= pBeginContent
; pCur
!= pEndContent
; ++pCur
)
262 // append (escape if needed)
265 case sal_Unicode('&') : aNormalized
.append( "&" ); break;
266 case sal_Unicode('\''): aNormalized
.append( "'" ); break;
267 case sal_Unicode('\"'): aNormalized
.append( """ ); break;
269 default: aNormalized
.append( *pCur
);
273 // suffix: closing quote and bracket
274 aNormalized
.append( "']" );
276 return aNormalized
.makeStringAndClear();
279 //----------------------------------------------------------------------------
281 OUString
wrapConfigurationElementName(OUString
const& _sElementName
)
283 return lcl_wrapName(_sElementName
, "*" );
286 //----------------------------------------------------------------------------
288 OUString
wrapConfigurationElementName(OUString
const& _sElementName
,
289 OUString
const& _sTypeName
)
291 // todo: check that _sTypeName is valid
292 return lcl_wrapName(_sElementName
, _sTypeName
);
295 //----------------------------------------------------------------------------
298 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */