merge the formfield patch from ooo-build
[ooovba.git] / configmgr / source / treemgr / configpath.cxx
blob6a10b72b03feb4ff18321879587e7812b8aa5b3f
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: configpath.cxx,v $
10 * $Revision: 1.16 $
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
39 #include <algorithm>
40 #define INCLUDED_ALGORITHM
41 #endif
43 #ifndef CFG_PATH_STRICT
44 //#define CFG_PATH_STRICT 1
45 #endif
47 #define dprint(a,b,c)
49 namespace configmgr
51 namespace configuration
54 //-------------------------------------------------------------------------
56 //-----------------------------------------------------------------------------
57 // Name validation
58 //-----------------------------------------------------------------------------
59 namespace
61 //-------------------------------------------------------------------------
62 inline
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;
69 inline
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) )
89 return false;
91 for (sal_Unicode const* pValidate = pStr+1; pValidate != pEnd; ++pValidate)
93 if (!isValidNameStart(*pValidate) && !isValidNameCont(*pValidate))
94 return false;
97 return true;
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");
106 return sName;
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)");
115 return sName;
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
130 static
131 inline
132 bool detectAbsolutePath(sal_Unicode const* _pPath) SAL_THROW(())
134 OSL_ASSERT( _pPath != NULL );
135 return *_pPath == c_cDelimiter;
137 //-----------------------------------------------------------------------------
139 static
140 inline
141 rtl::OUString makeWildcardType() SAL_THROW(())
143 return rtl::OUString(&c_cAnytype,1);
145 //-----------------------------------------------------------------------------
147 // even handles empty strings (if NUL-terminated)
148 static
149 inline
150 bool isWildcardType(sal_Unicode const* _sType) SAL_THROW(())
152 OSL_ASSERT( _sType != NULL );
153 return _sType[0] == c_cAnytype &&
154 _sType[1] == 0;
156 //-----------------------------------------------------------------------------
158 static
159 inline
160 bool isEmptyString(sal_Unicode const* _sType) SAL_THROW(())
162 OSL_ASSERT( _sType != NULL );
163 return _sType[0] == 0;
165 //-----------------------------------------------------------------------------
166 static
167 inline
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 //-----------------------------------------------------------------------------
185 namespace Path
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(())
193 : m_aName(_sName)
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);
211 return 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);
222 return sType;
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())
253 return true;
255 if (lhs.getName() != rhs.getName())
256 return false;
258 // simple names are considered equivalent to wildcard namess
259 if (lhs.isSimpleName() || rhs.isSimpleName())
260 return true;
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) )
269 return true;
271 return false;
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);
339 return nHash;
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))
378 Rep aResult(_aPath);
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();
388 return aResult;
390 //-----------------------------------------------------------------------------
391 } // namespace Path
392 //-----------------------------------------------------------------------------
394 namespace
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[] = "&amp;";
406 static sal_Char const c_apos_name[] = "&apos;";
407 static sal_Char const c_quot_name[] = "&quot;";
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
417 static
418 sal_Int32 cfg_ustr_ascii_compare_WithLength( const sal_Unicode* pStr1,
419 sal_Int32 nStr1Len,
420 const sal_Char* pStr2 )
422 while( nStr1Len )
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;
429 ++pStr1;
430 ++pStr2;
431 --nStr1Len;
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
440 @return
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
454 chResult = 0;
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,"&lt;")) chResult = sal_Unicode('<');
461 else if (0 == cfg_ustr_ascii_compare_WithLength(pBegin,nLen,"&gt;")) chResult = sal_Unicode('>');
462 else chResult = 0;
464 return chResult;
467 //-----------------------------------------------------------------------------
469 /** find the escape sequence to use for the given char
470 @return
471 an escape sequence, or NULL, if the char should not be escaped
473 inline
474 sal_Char const* implGetEscape(sal_Unicode ch ) SAL_THROW(())
476 switch (ch)
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
489 @return
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
506 @return
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
542 --nStart;
544 if (nStart < 0)
546 OSL_ENSURE(false, "Could not find opening quote or bracket for bracketed predicate");
547 return NULL;
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.
561 inline
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.
569 inline
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
581 @return
582 the normalized, bracketed and quoted predicate
583 @throw
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)
605 if (ch == c_amp)
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
613 ch = ch2;
614 pCur = pEndEscape;
615 OSL_ASSERT(*pCur == c_end_escape);
617 else
619 OSL_ENSURE(false, "Character '&' must be escaped in this context");
620 #if 0
621 throw InvalidName(rtl::OUString(pBeginContent,pEndContent-pBeginContent),
622 "is not a valid element name string. "
623 "Character '&' must be escaped in this context");
624 #endif
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 );
639 else
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)
651 @return
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
667 // handle an escape
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;
683 ++pReadPos;
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)
699 @return
700 the normalized predicate
701 @throw
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;
728 sStopCharBuf[1] = 0;
730 pStopChars = sStopCharBuf;
733 // non-quoted strings are not really valid, but we tolerate them
734 else
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;
743 if (pBegin == pEnd)
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))
752 Path::Rep aResult;
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
766 else
767 OSL_ENSURE(false, "Warning: trying to parse relative path as absolute");
768 #endif
770 else
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.");
780 #endif
781 --pEnd;
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) );
818 pEnd = pNameStart;
819 if (pNameStart != pBegin) --pEnd;
821 dprint (stderr, "\n", "");
822 return aResult;
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());
849 return sComposite;
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);
858 if (nPos >= 0)
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);
875 else
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(())
903 : m_aRep(aName)
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();
991 #endif
992 //-----------------------------------------------------------------------------
994 rtl::OUString AbsolutePath::toString() const SAL_THROW(())
996 return m_aRep.toString(true);
998 //-----------------------------------------------------------------------------