1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: configpath.cxx,v $
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_configmgr.hxx"
34 #include "configpath.hxx"
35 #include "configexcept.hxx"
36 #include <rtl/ustrbuf.hxx>
38 #ifndef INCLUDED_ALGORITHM
40 #define INCLUDED_ALGORITHM
43 #ifndef CFG_PATH_STRICT
44 //#define CFG_PATH_STRICT 1
51 namespace configuration
54 //-------------------------------------------------------------------------
56 //-----------------------------------------------------------------------------
58 //-----------------------------------------------------------------------------
61 //-------------------------------------------------------------------------
63 bool isValidNameStart(sal_Unicode ch
) SAL_THROW(())
65 return (sal_Unicode('A') <= ch
&& ch
<= sal_Unicode('Z')) ||
66 (sal_Unicode('a') <= ch
&& ch
<= sal_Unicode('z')) ||
67 sal_Unicode('_') == ch
;
70 bool isValidNameCont(sal_Unicode ch
) SAL_THROW(())
72 return ( (sal_Unicode('0') <= ch
&& ch
<= sal_Unicode('9'))
73 || (sal_Unicode('.') == ch
) // eg for module names
74 || (sal_Unicode('-') == ch
) // eg for locale names
75 || (sal_Unicode(':') == ch
) // support special namespaced names
79 //-------------------------------------------------------------------------
81 //-----------------------------------------------------------------------------
83 bool isSimpleName(rtl::OUString
const& sName
) SAL_THROW(())
85 sal_Unicode
const* const pStr
= sName
.getStr();
86 sal_Unicode
const* const pEnd
= pStr
+ sName
.getLength();
88 if ( (pStr
== pEnd
) || !isValidNameStart(*pStr
) )
91 for (sal_Unicode
const* pValidate
= pStr
+1; pValidate
!= pEnd
; ++pValidate
)
93 if (!isValidNameStart(*pValidate
) && !isValidNameCont(*pValidate
))
99 //-----------------------------------------------------------------------------
101 rtl::OUString
validateNodeName(rtl::OUString
const& sName
)
103 if (!isSimpleName(sName
))
104 throw InvalidName(sName
, "is not a valid name for a configuration node");
108 //-----------------------------------------------------------------------------
110 rtl::OUString
validateElementName(rtl::OUString
const& sName
)
112 if (sName
.getLength() == 0)
113 throw InvalidName(sName
, "is not a valid name for a configuration item (empty names are not permitted)");
117 //-----------------------------------------------------------------------------
119 namespace // path helpers I
121 //-----------------------------------------------------------------------------
122 const sal_Unicode c_cDelimiter
= '/';
124 const sal_Unicode c_lBracket
= '[', c_rBracket
= ']';
126 const sal_Unicode c_cAnytype
= '*';
127 //-----------------------------------------------------------------------------
129 // Textually an Absolute path starts with a slash
132 bool detectAbsolutePath(sal_Unicode
const* _pPath
) SAL_THROW(())
134 OSL_ASSERT( _pPath
!= NULL
);
135 return *_pPath
== c_cDelimiter
;
137 //-----------------------------------------------------------------------------
141 rtl::OUString
makeWildcardType() SAL_THROW(())
143 return rtl::OUString(&c_cAnytype
,1);
145 //-----------------------------------------------------------------------------
147 // even handles empty strings (if NUL-terminated)
150 bool isWildcardType(sal_Unicode
const* _sType
) SAL_THROW(())
152 OSL_ASSERT( _sType
!= NULL
);
153 return _sType
[0] == c_cAnytype
&&
156 //-----------------------------------------------------------------------------
160 bool isEmptyString(sal_Unicode
const* _sType
) SAL_THROW(())
162 OSL_ASSERT( _sType
!= NULL
);
163 return _sType
[0] == 0;
165 //-----------------------------------------------------------------------------
168 sal_Unicode
lastChar(rtl::OUString
const& _sString
) SAL_THROW(())
170 sal_Int32
const nLen
= _sString
.getLength();
172 OSL_PRECOND( nLen
> 0, "Non-empty string expected");
174 return _sString
[nLen
-1];
176 //-----------------------------------------------------------------------------
178 rtl::OUString
implMakeCompositeName(rtl::OUString
const& _sBaseName
, rtl::OUString
const& _sPredicate
) SAL_THROW((InvalidName
));
179 void implSplitCompositeName(rtl::OUString
const& _aCompositeName
, rtl::OUString
& _rBaseName
, rtl::OUString
& _rPredicate
) SAL_THROW(());
180 //-----------------------------------------------------------------------------
182 //-----------------------------------------------------------------------------
184 //-----------------------------------------------------------------------------
187 //-----------------------------------------------------------------------------
188 // class configuration::Path::Component
189 //-----------------------------------------------------------------------------
191 inline // though public, this method is not available outside this translation unit
192 Component::Component(rtl::OUString
const& _sName
) SAL_THROW(())
196 //-----------------------------------------------------------------------------
198 bool Component::isSimpleName() const SAL_THROW(())
200 return m_aName
.getLength() != 0 && lastChar(m_aName
) != c_rBracket
;
202 //-----------------------------------------------------------------------------
204 rtl::OUString
Component::getName() const SAL_THROW(())
206 if (isSimpleName()) return m_aName
;
208 rtl::OUString sName
, sType
;
209 implSplitCompositeName(m_aName
,sType
,sName
);
213 //-----------------------------------------------------------------------------
215 rtl::OUString
Component::getTypeName() const SAL_THROW(())
217 if (isSimpleName()) return rtl::OUString();
219 rtl::OUString sName
, sType
;
220 implSplitCompositeName(m_aName
,sType
,sName
);
224 //-----------------------------------------------------------------------------
226 Component
makeEmptyComponent() SAL_THROW(())
228 return Component( rtl::OUString() );
230 //-----------------------------------------------------------------------------
232 Component
wrapSimpleName(rtl::OUString
const& _sName
)
234 OSL_ENSURE( isSimpleName(_sName
), "Simple name expected creating path component");
235 if (!isSimpleName(_sName
))
236 throw InvalidName(_sName
, "is not a simple name. Cannot convert to path component");
238 return Component( _sName
);
240 //-----------------------------------------------------------------------------
242 Component
makeCompositeName(rtl::OUString
const& _sElementName
, rtl::OUString
const& _sTypeName
)
244 return Component( implMakeCompositeName(_sTypeName
,_sElementName
) );
246 //-----------------------------------------------------------------------------
249 bool matches(Component
const& lhs
,Component
const& rhs
) SAL_THROW(())
251 // this extra preflight check might be left out (is it good for performance ?)
252 if (lhs
.getInternalName() == rhs
.getInternalName())
255 if (lhs
.getName() != rhs
.getName())
258 // simple names are considered equivalent to wildcard namess
259 if (lhs
.isSimpleName() || rhs
.isSimpleName())
262 rtl::OUString aTypeLHS
= lhs
.getTypeName();
263 rtl::OUString aTypeRHS
= rhs
.getTypeName();
265 // this would need an extra test without our preflight check
266 OSL_ASSERT(aTypeLHS
!= aTypeRHS
); // would have been dicovered by first check
268 if ( isWildcardType(aTypeLHS
) || isWildcardType(aTypeRHS
) )
273 //-----------------------------------------------------------------------------
275 //-----------------------------------------------------------------------------
276 // weak comparison of components
277 //-----------------------------------------------------------------------------
278 bool before(Component
const& lhs
, Component
const& rhs
) SAL_THROW(())
279 { return lhs
.getName() < rhs
.getName(); }
281 //-----------------------------------------------------------------------------
282 bool equiv(Component
const& lhs
, Component
const& rhs
) SAL_THROW(())
283 { return lhs
.getName() == rhs
.getName(); }
285 //-----------------------------------------------------------------------------
286 size_t hashCode(Component
const& comp
) SAL_THROW(())
287 { return comp
.getName().hashCode(); }
289 //-----------------------------------------------------------------------------
291 //-----------------------------------------------------------------------------
292 // class configuration::Path::Rep
293 //-----------------------------------------------------------------------------
294 void Rep::check_not_empty() const
296 if (m_aComponents
.empty())
298 OSL_ENSURE(!m_aComponents
.empty(),"Trying to access components of an empty path");
299 throw Exception("Trying to access components of an empty path");
302 //-----------------------------------------------------------------------------
304 void Rep::prepend(Rep
const& _aOther
) SAL_THROW(())
306 // to prepend the other path append its components
307 m_aComponents
.insert( m_aComponents
.end(),
308 _aOther
.m_aComponents
.begin(),
309 _aOther
.m_aComponents
.end());
312 //-----------------------------------------------------------------------------
314 rtl::OUString
Rep::toString(bool _bAbsolute
) const SAL_THROW(())
316 std::vector
<Component
>::const_reverse_iterator cur
= begin();
317 std::vector
<Component
>::const_reverse_iterator
const stop
= end();
319 rtl::OUStringBuffer sRet
;
321 if (!_bAbsolute
&& cur
!= stop
)
322 sRet
= cur
++->toPathString();
324 for ( ;cur
!= stop
; ++cur
)
325 sRet
.append( c_cDelimiter
).append( cur
->toPathString() );
327 return sRet
.makeStringAndClear();
329 //-----------------------------------------------------------------------------
331 size_t Rep::hashCode() const SAL_THROW(())
333 const unsigned long mangle_factor
= 11; // 1011 (2)
334 unsigned long nHash
= 0;
335 for (std::vector
<Component
>::const_reverse_iterator it
= begin(), stop
= end(); it
!= stop
; ++it
)
337 nHash
= mangle_factor
*nHash
+ Path::hashCode(*it
);
341 //-----------------------------------------------------------------------------
343 bool before(Rep
const& lhs
, Rep
const& rhs
) SAL_THROW(())
345 return std::lexicographical_compare(lhs
.begin(),lhs
.end(),rhs
.begin(),rhs
.end(), Before());
347 //-----------------------------------------------------------------------------
349 bool equiv(Rep
const& lhs
, Rep
const& rhs
) SAL_THROW(())
351 return (lhs
.countComponents() == rhs
.countComponents()) &&
352 std::equal(lhs
.begin(),lhs
.end(),rhs
.begin(),Equiv());
354 //-----------------------------------------------------------------------------
356 bool matches(Rep
const& lhs
, Rep
const& rhs
) SAL_THROW(())
358 return (lhs
.countComponents() == rhs
.countComponents()) &&
359 std::equal(lhs
.begin(),lhs
.end(),rhs
.begin(),Matches());
361 //-----------------------------------------------------------------------------
363 bool isAbsolutePath(rtl::OUString
const& _sPath
) SAL_THROW(())
365 return detectAbsolutePath(_sPath
);
367 //-----------------------------------------------------------------------------
369 bool hasMatchingPrefix(Rep
const& _aPath
, Rep
const& _aPrefix
) SAL_THROW(())
371 return (_aPath
.countComponents() >= _aPrefix
.countComponents()) &&
372 std::equal( _aPrefix
.begin(), _aPrefix
.end(), _aPath
.begin(), Matches());
374 //-----------------------------------------------------------------------------
376 Rep
stripMatchingPrefix(Rep
const& _aPath
,Rep
const& _aPrefix
) // SAL_THROW((InvalidName))
380 for (std::vector
<Component
>::const_reverse_iterator it
= _aPrefix
.begin(); it
!= _aPrefix
.end(); ++it
)
382 if (aResult
.isEmpty() || !matches(*it
,aResult
.getFirstName()))
383 throw InvalidName(aResult
.getFirstName().toPathString(), "does not match the expected location.");
385 aResult
.dropFirstName();
390 //-----------------------------------------------------------------------------
392 //-----------------------------------------------------------------------------
396 //-----------------------------------------------------------------------------
397 const sal_Unicode c_quot
= '\"';
398 const sal_Unicode c_apos
= '\'';
399 const sal_Unicode c_amp
= '&';
401 const sal_Unicode c_end_escape
= ';';
403 const sal_Unicode c_normal_quot
= c_apos
;
404 //-------------------------------------------
405 static sal_Char
const c_amp_name
[] = "&";
406 static sal_Char
const c_apos_name
[] = "'";
407 static sal_Char
const c_quot_name
[] = """;
409 const sal_Int32 c_nMinEscapeLen
= sizeof c_amp_name
- 1;
410 const sal_Int32 c_nMaxEscapeLen
= sizeof c_quot_name
- 1;
411 //-------------------------------------------------------------------------
412 /// distinguishes which kind of path is held in a path object
413 enum PathType
{ eRELATIVE
= 1, eABSOLUTE
= 2 };
415 //-----------------------------------------------------------------------------
416 // missing or mis leading in SAL/rtl: pStr1[nLength] must NOT be evaluated
418 sal_Int32
cfg_ustr_ascii_compare_WithLength( const sal_Unicode
* pStr1
,
420 const sal_Char
* pStr2
)
424 sal_Int32 nRet
= static_cast<sal_Int32
>(*pStr1
)-
425 static_cast<sal_Int32
>(static_cast<unsigned char>(*pStr2
));
427 if (nRet
!= 0 || *pStr2
== 0) return nRet
;
434 return -static_cast<sal_Int32
>(static_cast<unsigned char>(*pStr2
));
436 //-----------------------------------------------------------------------------
439 /** find the char being escaped by the escape sequence in the given string range
441 the char being escaped or zero, if the range is no known escape
443 sal_Unicode
implParseEscape(sal_Unicode
const * pBegin
, sal_Unicode
const * pEnd
) SAL_THROW(())
445 OSL_PRECOND( pBegin
< pEnd
, "Nonempty string range expected" );
446 OSL_PRECOND( pBegin
[0] == c_amp
, "String range is not a possible escape: missing start marker" );
447 OSL_PRECOND( pEnd
[-1] == c_end_escape
, "String range is not a possible escape: missing end marker" );
449 sal_Int32
const nLen
= pEnd
- pBegin
;
451 sal_Unicode chResult
;
453 if ( c_nMinEscapeLen
> nLen
|| nLen
> c_nMaxEscapeLen
) // quick check, if there is no possible match
455 // the standard escapes
456 else if (0 == cfg_ustr_ascii_compare_WithLength(pBegin
,nLen
,c_amp_name
)) chResult
= c_amp
;
457 else if (0 == cfg_ustr_ascii_compare_WithLength(pBegin
,nLen
,c_apos_name
)) chResult
= c_apos
;
458 else if (0 == cfg_ustr_ascii_compare_WithLength(pBegin
,nLen
,c_quot_name
)) chResult
= c_quot
;
459 // extra escapes for XML compatibility
460 else if (0 == cfg_ustr_ascii_compare_WithLength(pBegin
,nLen
,"<")) chResult
= sal_Unicode('<');
461 else if (0 == cfg_ustr_ascii_compare_WithLength(pBegin
,nLen
,">")) chResult
= sal_Unicode('>');
467 //-----------------------------------------------------------------------------
469 /** find the escape sequence to use for the given char
471 an escape sequence, or NULL, if the char should not be escaped
474 sal_Char
const* implGetEscape(sal_Unicode ch
) SAL_THROW(())
478 case c_amp
: return c_amp_name
;
479 case c_apos
: return c_apos_name
;
480 case c_quot
: return c_quot_name
;
482 default: return NULL
;
486 //-----------------------------------------------------------------------------
488 /** find the start of the path component ending before pEnd in the string starting at pBegin
490 a pointer to the last character before pEnd that is not a name delimiter
492 sal_Unicode
const * implFindNameStart(sal_Unicode
const * pBegin
, sal_Unicode
const * pEnd
) SAL_THROW(())
494 OSL_PRECOND(pBegin
<= pEnd
, "Invalid string range");
496 sal_Int32
const nLen
= pEnd
-pBegin
;
497 sal_Int32
const nPos
= rtl_ustr_lastIndexOfChar_WithLength(pBegin
, nLen
, c_cDelimiter
) + 1;
499 OSL_ASSERT(0 <= nPos
&& nPos
<= nLen
);
501 return pBegin
+ nPos
;
503 //-----------------------------------------------------------------------------
505 /** find the start of the bracketed & quoted predicate ending before pEnd in the string starting at pBegin
507 <ul><li>a pointer to the opening bracket matching the closing bracket at pEnd[-1], if found</li>
508 <li><var>pEnd</var>, if no bracketed string was found</li>
509 <li>NULL, if there was a closing bracket, but the beginning could not be discovered</li></ul>
511 sal_Unicode
const * implFindPredicateStart(sal_Unicode
const * pBegin
, sal_Unicode
const * pEnd
) SAL_THROW(())
513 OSL_PRECOND(pBegin
< pEnd
, "Nonempty string range required");
515 if (pEnd
== pBegin
|| pEnd
[-1] != c_rBracket
) return pEnd
;
517 if (--pEnd
== pBegin
)
519 OSL_ENSURE(false, "Invalid path component: single ']'");
520 return NULL
; // string was only "]"
523 sal_Unicode chQuote
= *--pEnd
;
525 if (chQuote
!= c_quot
&& chQuote
!= c_apos
)
527 // should we support empty brackets ?
528 if (chQuote
== c_lBracket
)
530 OSL_ENSURE(false, "Empty predicate brackets found");
531 return NULL
; // for now we don't
535 // should we support brackets with non-quoted strings ?
536 chQuote
= c_lBracket
; // for now we do
539 sal_Int32 nStart
= rtl_ustr_lastIndexOfChar_WithLength(pBegin
, pEnd
-pBegin
, chQuote
);
541 if (chQuote
!= c_lBracket
) // needed to support non-quoted strings
546 OSL_ENSURE(false, "Could not find opening quote or bracket for bracketed predicate");
550 if (pBegin
[nStart
] != c_lBracket
)
552 OSL_ENSURE(false, "Illegal quote character in string");
553 return NULL
; // for now we don't
556 return pBegin
+ nStart
;
558 //-----------------------------------------------------------------------------
560 /// find the position of the given char in the range given.
562 sal_Int32
indexOfCharInRange(sal_Unicode
const * pBegin
, sal_Unicode
const * pEnd
, sal_Unicode ch
) SAL_THROW(())
564 return rtl_ustr_indexOfChar_WithLength(pBegin
, pEnd
-pBegin
, ch
);
566 //-----------------------------------------------------------------------------
568 /// find the position of the given char in the range given.
570 bool containsChar(sal_Unicode
const * pString
, sal_Unicode ch
) SAL_THROW(())
572 return rtl_ustr_indexOfChar(pString
, ch
) >= 0;
574 //-----------------------------------------------------------------------------
576 /** validate and normalize a bracketed & quoted predicate from content the string range [pBegin,pEnd)
577 @param pRequiredEscapes
578 contains a list of characters that must be preescaped or are otherwise invalid
579 if NULL is passed, the source range is presumed to contain no escaped data
580 otherwise the ampersand (&) and all characters in the list are required to be escaped
582 the normalized, bracketed and quoted predicate
584 InvalidName, if the predicate data is not valid
586 rtl::OUString
implMakeNormalizedPredicate(sal_Unicode
const * pBeginContent
, sal_Unicode
const * pEndContent
, sal_Unicode
const* pRequiredEscapes
) SAL_THROW((InvalidName
))
588 OSL_PRECOND(pBeginContent
<= pEndContent
, "Invalid string range");
589 if (pBeginContent
== pEndContent
)
590 return rtl::OUString();
592 rtl::OUStringBuffer
aNormalized(pEndContent
-pBeginContent
+ 4); // reserve approximate size initially
594 // prefix: opening bracket and quote
595 aNormalized
.append(c_lBracket
).append(c_normal_quot
);
597 // content: copy over each char and handle escaping
598 for(sal_Unicode
const * pCur
= pBeginContent
; pCur
!= pEndContent
; ++pCur
)
600 sal_Unicode ch
= *pCur
;
602 // maybe parse contained escaping
603 if (pRequiredEscapes
)
607 // find an escape end marker (after pCur). Result is pCur, if the end marker is not there
608 sal_Unicode
const * pEndEscape
= pCur
+ 1 + indexOfCharInRange(pCur
+1,pEndContent
,c_end_escape
);
609 sal_Unicode ch2
= pCur
!= pEndEscape
? implParseEscape(pCur
,pEndEscape
+1) : 0;
611 if (ch2
!= 0) // found and read a valid escape sequence
615 OSL_ASSERT(*pCur
== c_end_escape
);
619 OSL_ENSURE(false, "Character '&' must be escaped in this context");
621 throw InvalidName(rtl::OUString(pBeginContent
,pEndContent
-pBeginContent
),
622 "is not a valid element name string. "
623 "Character '&' must be escaped in this context");
627 else if ( containsChar(pRequiredEscapes
, ch
) )
629 throw InvalidName(rtl::OUString(pBeginContent
,pEndContent
-pBeginContent
),
630 "is not a valid element name string. "
631 "Some characters must be escaped in this context");
635 // now append (escape if normal)
636 if (sal_Char
const * pEscape
= implGetEscape(ch
))
637 aNormalized
.appendAscii( pEscape
);
640 aNormalized
.append( ch
);
643 // suffix: closing quote and bracket
644 aNormalized
.append(c_normal_quot
).append(c_rBracket
);
646 return aNormalized
.makeStringAndClear();
648 //-----------------------------------------------------------------------------
650 /** extract and unescape the normalized predicate content in the string range [pBegin,pEnd)
652 the denormalized predicate content
654 rtl::OUString
implReadPredicate(sal_Unicode
const * pBegin
, sal_Unicode
const * pEnd
) SAL_THROW(())
656 OSL_PRECOND(pBegin
<= pEnd
, "Invalid string range");
658 rtl::OUStringBuffer
aContent(pEnd
-pBegin
); // reserve approximate size initially
660 sal_Unicode
const * pReadPos
= pBegin
;
662 // content: copy data, handling escapes
663 for(sal_Unicode
const * pCur
= pReadPos
; pCur
!= pEnd
; ++pCur
)
665 if (*pCur
!= c_amp
) continue; // no escape here
668 // find an escape end marker (after pCur). Result is pCur, if the end marker is not there
669 sal_Unicode
const * pEndEscape
= pCur
+ 1 + indexOfCharInRange(pCur
+1,pEnd
,c_end_escape
);
671 OSL_ENSURE(pEndEscape
!= pCur
, "Found dangling ampersand in normalized data");
673 sal_Unicode ch
= implParseEscape(pCur
,pEndEscape
+1);
675 OSL_ENSURE(ch
!= 0, "Found unreckognized escape in normalized data");
677 if (ch
!= 0) // found and read a valid escape sequence
679 // do copy of preceding data
680 aContent
.append(pReadPos
, pCur
-pReadPos
).append(ch
);
681 pCur
= pReadPos
= pEndEscape
;
685 OSL_ASSERT(*pCur
== c_end_escape
);
687 // otherwise just treat the ampersand as a mormal character
690 // do copy of remaining data
691 if (pReadPos
!= pEnd
)
692 aContent
.append(pReadPos
, pEnd
-pReadPos
);
694 return aContent
.makeStringAndClear();
696 //-----------------------------------------------------------------------------
698 /** validate and normalize the bracketed & quoted predicate in the string range [pBegin,pEnd)
700 the normalized predicate
702 InvalidName, if the predicate is not valid
704 rtl::OUString
implNormalizePredicate(sal_Unicode
const * pBegin
, sal_Unicode
const * pEnd
) SAL_THROW((InvalidName
))
706 sal_Unicode sStopCharBuf
[2];
707 sal_Unicode
const * pStopChars
;
709 OSL_PRECOND(pBegin
< pEnd
, "Nonempty string range expected");
710 OSL_PRECOND(pEnd
-pBegin
>= 2, "Bracketed string range expected");
711 OSL_PRECOND(pBegin
[0] == c_lBracket
,"Bracketed string range expected");
712 OSL_PRECOND(pEnd
[-1] == c_rBracket
, "Bracketed string range expected");
714 ++pBegin
; --pEnd
; // skip brackets
716 sal_Unicode
const chUsedQuot
= *pBegin
;
717 if (chUsedQuot
== c_apos
|| chUsedQuot
== c_quot
)
719 OSL_PRECOND(pBegin
< pEnd
&& pEnd
-pBegin
>= 2, "Bracketed quoted string range expected");
720 OSL_PRECOND(pEnd
[-1] == chUsedQuot
, "Non-matching quotes in bracketed quoted string");
722 if (pEnd
-pBegin
<= 1 || pEnd
[-1] != chUsedQuot
)
723 throw InvalidName( rtl::OUString(pBegin
, pEnd
-pBegin
), "is not a valid element predicate: quotes do not match");
725 ++pBegin
; --pEnd
; // skip quotes
727 sStopCharBuf
[0] = chUsedQuot
;
730 pStopChars
= sStopCharBuf
;
733 // non-quoted strings are not really valid, but we tolerate them
736 OSL_ENSURE(false, "Warning: Invalid path - non-quoted data in bracketed predicate");
738 static sal_Unicode
const sGeneralStoppers
[] = { c_quot
, c_apos
, c_rBracket
, c_lBracket
, 0 };
740 pStopChars
= sGeneralStoppers
;
744 throw InvalidName(rtl::OUString(pBegin
-1,2),"Empty element name in predicate");
746 return implMakeNormalizedPredicate(pBegin
, pEnd
, pStopChars
);
748 //-----------------------------------------------------------------------------
749 /// parse a path into a sequence of components
750 Path::Rep
implParsePath(rtl::OUString
const& _aPathString
, PathType eType
) SAL_THROW((InvalidName
))
754 dprint (stderr
, "implParsePath '%s' ",
755 rtl::OUStringToOString(_aPathString
, RTL_TEXTENCODING_UTF8
).getStr());
757 sal_Unicode
const * pBegin
= _aPathString
.getStr();
758 sal_Unicode
const * pEnd
= pBegin
+ _aPathString
.getLength();
760 if (eType
== eABSOLUTE
)
762 if ( detectAbsolutePath(_aPathString
) )
763 ++pBegin
; // skip the leading slash
765 #ifdef CFG_PATH_STRICT
767 OSL_ENSURE(false, "Warning: trying to parse relative path as absolute");
771 OSL_ENSURE(!detectAbsolutePath(_aPathString
), "ERROR: trying to parse absolute path as relative one");
773 if (detectAbsolutePath(pBegin
))
774 throw InvalidName(_aPathString
, "is not a valid path. Illegal empty first component");
776 else if (pBegin
!= pEnd
&& pEnd
[-1] == '/')
778 #ifdef CFG_PATH_STRICT
779 OSL_ENSURE(false, "Illegal configuration path. Terminating '/' found.");
784 while (pEnd
!= pBegin
)
786 // check for predicate
787 sal_Unicode
const * pQuoteStart
= implFindPredicateStart(pBegin
, pEnd
);
788 if (pQuoteStart
== NULL
)
789 throw InvalidName(_aPathString
, "is not a valid path. Invalid name or predicate syntax");
791 sal_Unicode
const * pNameStart
= implFindNameStart(pBegin
, pQuoteStart
);
793 rtl::OUString
aElementName(pNameStart
, pQuoteStart
-pNameStart
);
795 if (!isSimpleName(aElementName
))
797 // this is OK only for few cases WITH predicate
798 if (pQuoteStart
== pEnd
)
799 throw InvalidName(_aPathString
, "is not a valid path. Invalid name");
801 if (isEmptyString(aElementName
))
802 aElementName
= makeWildcardType();
804 else if ( !isWildcardType(aElementName
))
805 throw InvalidName(_aPathString
, "is not a valid path. Invalid type tag for predicate");
807 if (pQuoteStart
!= pEnd
)
809 dprint (stderr
, "add 'normalize predicate'", "");
810 rtl::OUString aPred
= implNormalizePredicate(pQuoteStart
,pEnd
);
811 aElementName
+= aPred
;
812 dprint (stderr
, " [result pred '%s']",
813 rtl::OUStringToOString(aPred
, RTL_TEXTENCODING_UTF8
).getStr());
816 aResult
.prepend( Path::Component(aElementName
) );
819 if (pNameStart
!= pBegin
) --pEnd
;
821 dprint (stderr
, "\n", "");
824 //-----------------------------------------------------------------------------
826 /// build a composite path component from a base name (type) and a (somewhat optional) predicate
827 rtl::OUString
implMakeCompositeName(rtl::OUString
const& _sBaseName
, rtl::OUString
const& _sPredicate
) SAL_THROW((InvalidName
))
829 rtl::OUString
sComposite(_sBaseName
);
831 if (isEmptyString(_sBaseName
))
832 sComposite
= makeWildcardType();
834 else if (!isWildcardType(_sBaseName
) && !isSimpleName(_sBaseName
))
835 throw InvalidName(_sBaseName
, "The base-name (type) part of a composite node name must be a simple word");
837 dprint (stderr
, "implMakeNormalizePred '%s' ",
838 rtl::OUStringToOString(_sPredicate
, RTL_TEXTENCODING_UTF8
).getStr());
840 sal_Unicode
const * pPredStart
= _sPredicate
.getStr();
841 sal_Unicode
const * pPredEnd
= pPredStart
+ _sPredicate
.getLength();
843 if (pPredStart
!= pPredEnd
)
844 sComposite
+= implMakeNormalizedPredicate(pPredStart
, pPredEnd
, NULL
);
846 dprint (stderr
, " [result pred '%s']\n",
847 rtl::OUStringToOString(sComposite
, RTL_TEXTENCODING_UTF8
).getStr());
851 //-----------------------------------------------------------------------------
853 /// split a composite path component into a base name (type) and a predicate (if present)
854 void implSplitCompositeName(rtl::OUString
const& _aCompositeName
, rtl::OUString
& _rBaseName
, rtl::OUString
& _rPredicate
) SAL_THROW(())
856 sal_Int32 nPos
= _aCompositeName
.indexOf(c_lBracket
);
860 OSL_ENSURE( nPos
> 0, "Invalid name: Only predicate, no base type");
862 _rBaseName
= _aCompositeName
.copy(0,nPos
);
864 sal_Unicode
const * pBeginPred
= _aCompositeName
.getStr() + nPos
;
865 sal_Unicode
const * pEndPred
= _aCompositeName
.getStr() + _aCompositeName
.getLength();
867 OSL_ASSERT(pBeginPred
[0] == c_lBracket
);
868 OSL_ENSURE(pBeginPred
[1] == c_normal_quot
, "Missing or unexpected quote mark");
869 OSL_ENSURE(pEndPred
[-1] == c_rBracket
, "Invalid name: Predicate brackets not closed");
870 OSL_ENSURE(pEndPred
[-2] == c_normal_quot
, "Missing or unexpected quote mark");
872 // skip brackets and quotes - then read data
873 _rPredicate
= implReadPredicate(pBeginPred
+2, pEndPred
-2);
877 OSL_ENSURE( _aCompositeName
.indexOf(c_rBracket
) < 0, "Invalid name: Predicate brackets not opened");
878 _rBaseName
= _aCompositeName
;
879 _rPredicate
= rtl::OUString();
882 //-----------------------------------------------------------------------------
884 //-----------------------------------------------------------------------------
886 //-----------------------------------------------------------------------------
887 // class RelativePath
888 //-----------------------------------------------------------------------------
890 // Currently unused method to check/ensure validity
891 void RelativePath::init() SAL_THROW(())
894 //-----------------------------------------------------------------------------
896 RelativePath
RelativePath::parse(rtl::OUString
const& aString
)
898 return RelativePath( implParsePath(aString
, eRELATIVE
) );
900 //-----------------------------------------------------------------------------
902 RelativePath::RelativePath(Path::Component
const& aName
) SAL_THROW(())
905 if (aName
.isEmpty()) m_aRep
.clearComponents();
907 //-----------------------------------------------------------------------------
909 RelativePath
RelativePath::compose(RelativePath
const& aPath
) const SAL_THROW(())
911 Path::Rep aResult
= aPath
.rep();
912 aResult
.prepend( this->m_aRep
);
913 return RelativePath( aResult
);
915 //-----------------------------------------------------------------------------
916 rtl::OUString
RelativePath::toString() const SAL_THROW(())
918 return m_aRep
.toString(false);
921 //-----------------------------------------------------------------------------
922 // class AbsolutePath
923 //-----------------------------------------------------------------------------
925 // Currently unused method to check/ensure validity
926 void AbsolutePath::init() SAL_THROW(())
929 //-----------------------------------------------------------------------------
931 AbsolutePath
AbsolutePath::parse(rtl::OUString
const& aString
)
933 return AbsolutePath( implParsePath(aString
, eABSOLUTE
) );
935 //-----------------------------------------------------------------------------
937 AbsolutePath
AbsolutePath::root() SAL_THROW(())
939 return AbsolutePath( Path::Rep() );
941 //-----------------------------------------------------------------------------
943 AbsolutePath
AbsolutePath::detachedRoot() SAL_THROW(())
945 Path::Rep
aRep( Path::makeEmptyComponent() ); // use 1 empty component here, to start detached names
946 return AbsolutePath( aRep
);
948 //-----------------------------------------------------------------------------
950 static inline Path::Component
implMakeSafeModuleName(rtl::OUString
const& _sModuleName
) SAL_THROW(())
952 OSL_ENSURE( isSimpleName(_sModuleName
), "A module name must be a simple name");
954 // if (isSimpleName(_sModuleName)) sModuleName = escape_name( _sModuleName );
956 return Path::Component(_sModuleName
);
958 //-----------------------------------------------------------------------------
960 AbsolutePath
AbsolutePath::makeModulePath(rtl::OUString
const& _sModuleName
) SAL_THROW(())
962 return AbsolutePath( Path::Rep( implMakeSafeModuleName(_sModuleName
) ) );
964 //-----------------------------------------------------------------------------
966 AbsolutePath
AbsolutePath::compose(RelativePath
const& aPath
) const SAL_THROW(())
968 Path::Rep aResult
= aPath
.rep();
969 aResult
.prepend( this->m_aRep
);
970 return AbsolutePath( aResult
);
972 //-----------------------------------------------------------------------------
974 AbsolutePath
AbsolutePath::getParentPath() const
976 // or: m_aRep.check_not_empty();
977 OSL_ENSURE(!isRoot(), "ERROR: Requesting the parent of a root path");
978 if (isRoot()) return *this;
980 OSL_ENSURE(!isDetached(), "ERROR: Requesting the parent of a detached path");
982 return AbsolutePath( Path::Rep(begin(),end()-1) );
984 #if OSL_DEBUG_LEVEL > 0
985 //-----------------------------------------------------------------------------
987 bool AbsolutePath::isDetached() const SAL_THROW(())
989 return !m_aRep
.isEmpty() && begin()->isEmpty();
992 //-----------------------------------------------------------------------------
994 rtl::OUString
AbsolutePath::toString() const SAL_THROW(())
996 return m_aRep
.toString(true);
998 //-----------------------------------------------------------------------------