1117 lines
40 KiB
C++
1117 lines
40 KiB
C++
/*************************************************************************
|
|
*
|
|
* $RCSfile: configpath.cxx,v $
|
|
*
|
|
* $Revision: 1.9 $
|
|
*
|
|
* last change: $Author: jb $ $Date: 2001-07-06 13:40:14 $
|
|
*
|
|
* The Contents of this file are made available subject to the terms of
|
|
* either of the following licenses
|
|
*
|
|
* - GNU Lesser General Public License Version 2.1
|
|
* - Sun Industry Standards Source License Version 1.1
|
|
*
|
|
* Sun Microsystems Inc., October, 2000
|
|
*
|
|
* GNU Lesser General Public License Version 2.1
|
|
* =============================================
|
|
* Copyright 2000 by Sun Microsystems, Inc.
|
|
* 901 San Antonio Road, Palo Alto, CA 94303, USA
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License version 2.1, as published by the Free Software Foundation.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
|
* MA 02111-1307 USA
|
|
*
|
|
*
|
|
* Sun Industry Standards Source License Version 1.1
|
|
* =================================================
|
|
* The contents of this file are subject to the Sun Industry Standards
|
|
* Source License Version 1.1 (the "License"); You may not use this file
|
|
* except in compliance with the License. You may obtain a copy of the
|
|
* License at http://www.openoffice.org/license.html.
|
|
*
|
|
* Software provided under this License is provided on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
|
|
* WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
|
|
* MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
|
|
* See the License for the specific provisions governing your rights and
|
|
* obligations concerning the Software.
|
|
*
|
|
* The Initial Developer of the Original Code is: Sun Microsystems, Inc.
|
|
*
|
|
* Copyright: 2000 by Sun Microsystems, Inc.
|
|
*
|
|
* All Rights Reserved.
|
|
*
|
|
* Contributor(s): _______________________________________
|
|
*
|
|
*
|
|
************************************************************************/
|
|
|
|
#include "configpath.hxx"
|
|
|
|
#ifndef CONFIGMGR_CONFIGEXCEPT_HXX_
|
|
#include "configexcept.hxx"
|
|
#endif
|
|
#ifndef _RTL_USTRBUF_HXX_
|
|
#include <rtl/ustrbuf.hxx>
|
|
#endif
|
|
|
|
#ifndef INCLUDED_ALGORITHM
|
|
#include <algorithm>
|
|
#define INCLUDED_ALGORITHM
|
|
#endif
|
|
|
|
#ifndef CFG_PATH_STRICT
|
|
//#define CFG_PATH_STRICT 1
|
|
#endif
|
|
|
|
namespace configmgr
|
|
{
|
|
namespace configuration
|
|
{
|
|
|
|
//-------------------------------------------------------------------------
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name validation
|
|
//-----------------------------------------------------------------------------
|
|
namespace
|
|
{
|
|
//-------------------------------------------------------------------------
|
|
inline
|
|
bool isValidNameStart(sal_Unicode ch) SAL_THROW(())
|
|
{
|
|
return (sal_Unicode('A') <= ch && ch <= sal_Unicode('Z')) ||
|
|
(sal_Unicode('a') <= ch && ch <= sal_Unicode('z')) ||
|
|
sal_Unicode('_') == ch;
|
|
}
|
|
inline
|
|
bool isValidNameCont(sal_Unicode ch) SAL_THROW(())
|
|
{
|
|
return ( (sal_Unicode('0') <= ch && ch <= sal_Unicode('9'))
|
|
|| (sal_Unicode('.') == ch) // eg for module names
|
|
|| (sal_Unicode('-') == ch) // eg for locale names
|
|
|| (sal_Unicode(':') == ch) // support special namespaced names
|
|
);
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
bool isSimpleName(OUString const& sName) SAL_THROW(())
|
|
{
|
|
sal_Unicode const* const pStr = sName.getStr();
|
|
sal_Unicode const* const pEnd = pStr + sName.getLength();
|
|
if (pStr == pEnd)
|
|
return false;
|
|
|
|
for (sal_Unicode const* pValidate = pStr; pValidate != pEnd; ++pValidate)
|
|
{
|
|
if (!isValidNameStart(*pValidate))
|
|
{
|
|
if (pValidate == pStr) // first char - then this means failure
|
|
return false;
|
|
|
|
else if (!isValidNameCont(*pValidate))
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
// class configuration::Name
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// tag struct to ensure construction is routed through our helpers
|
|
struct Path::PackageOnly {};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Performance: Could optimize memory usage by using a string pool
|
|
inline
|
|
Name::Name(OUString const& aString, Path::PackageOnly) SAL_THROW(())
|
|
: m_sRep(aString)
|
|
{
|
|
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
Name makeName(OUString const& sName, Name::NoValidate) SAL_THROW(())
|
|
{
|
|
return Name( sName, Path::PackageOnly() );
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
Name makeNodeName(OUString const& sName, Name::NoValidate) SAL_THROW(())
|
|
{
|
|
OSL_ENSURE( isSimpleName(sName), "Creating a Name that is invalid as member node name");
|
|
return Name( sName, Path::PackageOnly() );
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
Name makeElementName(OUString const& sName, Name::NoValidate) SAL_THROW(())
|
|
{
|
|
return Name( sName, Path::PackageOnly() );
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
Name validateNodeName(OUString const& sName)
|
|
{
|
|
if (!isSimpleName(sName))
|
|
throw InvalidName(sName, "is not a valid name for a configuration node");
|
|
|
|
return Name( sName, Path::PackageOnly() );
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
Name validateElementName(OUString const& sName)
|
|
{
|
|
return Name( sName, Path::PackageOnly() );
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
namespace // path helpers I
|
|
{
|
|
//-----------------------------------------------------------------------------
|
|
const sal_Unicode c_cDelimiter = '/';
|
|
|
|
const sal_Unicode c_lBracket = '[', c_rBracket = ']';
|
|
|
|
const sal_Unicode c_cAnytype = '*';
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Textually an Absolute path starts with a slash
|
|
static
|
|
inline
|
|
bool detectAbsolutePath(sal_Unicode const* _pPath) SAL_THROW(())
|
|
{
|
|
OSL_ASSERT( _pPath != NULL );
|
|
return *_pPath == c_cDelimiter;
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
static
|
|
inline
|
|
OUString makeWildcardType() SAL_THROW(())
|
|
{
|
|
return OUString(&c_cAnytype,1);
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// even handles empty strings (if NUL-terminated)
|
|
static
|
|
inline
|
|
bool isWildcardType(sal_Unicode const* _sType) SAL_THROW(())
|
|
{
|
|
return _sType[0] == c_cAnytype &&
|
|
_sType[1] == 0;
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
static
|
|
inline
|
|
bool isEmptyString(sal_Unicode const* _sType) SAL_THROW(())
|
|
{
|
|
return _sType[0] == 0;
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
static
|
|
inline
|
|
sal_Unicode lastChar(OUString const& _sString) SAL_THROW(())
|
|
{
|
|
sal_Int32 const nLen = _sString.getLength();
|
|
|
|
OSL_PRECOND( nLen > 0, "Non-empty string expected");
|
|
|
|
return _sString[nLen-1];
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
Name implMakeCompositeName(OUString const& _sBaseName, OUString const& _sPredicate) SAL_THROW((InvalidName));
|
|
void implSplitCompositeName(Name const& _aCompositeName, OUString& _rBaseName, OUString& _rPredicate) SAL_THROW(());
|
|
//-----------------------------------------------------------------------------
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//-----------------------------------------------------------------------------
|
|
namespace Path
|
|
{
|
|
//-----------------------------------------------------------------------------
|
|
// class configuration::Path::Component
|
|
//-----------------------------------------------------------------------------
|
|
|
|
inline // though public, this method is not available outside this translation unit
|
|
Component::Component(OUString const& _sName, Path::PackageOnly _tag) SAL_THROW(())
|
|
: m_aName(_sName, _tag)
|
|
{
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
inline // though public, this method is not available outside this translation unit
|
|
Component::Component(Name const& _aName, Path::PackageOnly) SAL_THROW(())
|
|
: m_aName(_aName)
|
|
{
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
bool Component::isSimpleName() const SAL_THROW(())
|
|
{
|
|
return !m_aName.isEmpty() && lastChar(m_aName.toString()) != c_rBracket;
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
Name Component::getName() const SAL_THROW(())
|
|
{
|
|
if (isSimpleName()) return m_aName;
|
|
|
|
OUString sName, sType;
|
|
implSplitCompositeName(m_aName,sType,sName);
|
|
|
|
return Name(sName,PackageOnly());
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
Name Component::getTypeName() const SAL_THROW(())
|
|
{
|
|
if (isSimpleName()) return Name();
|
|
|
|
OUString sName, sType;
|
|
implSplitCompositeName(m_aName,sType,sName);
|
|
|
|
return Name(sType,PackageOnly());
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
bool Component::splitCompositeName(Name& _rName, Name& _rType) const SAL_THROW(())
|
|
{
|
|
if (isSimpleName())
|
|
{
|
|
_rName = m_aName;
|
|
_rType = Name();
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
OUString sName, sType;
|
|
implSplitCompositeName(m_aName,sType,sName);
|
|
_rName = Name(sName, PackageOnly());
|
|
_rType = Name(sType, PackageOnly());
|
|
return true;
|
|
}
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
bool Component::splitCompositeName(OUString& _rName, OUString& _rType) const SAL_THROW(())
|
|
{
|
|
if (isSimpleName())
|
|
{
|
|
_rName = m_aName.toString();
|
|
_rType = OUString();
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
implSplitCompositeName(m_aName,_rType,_rName);
|
|
return true;
|
|
}
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
Component makeEmptyComponent() SAL_THROW(())
|
|
{
|
|
return Component( OUString(), PackageOnly() );
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
Component wrapSimpleName(OUString const& _sName)
|
|
{
|
|
OSL_ENSURE( isSimpleName(_sName), "Simple Name expected creating path component");
|
|
if (!isSimpleName(_sName))
|
|
throw InvalidName(_sName, "is not a simple name. Cannot convert to path component");
|
|
|
|
return Component( _sName, PackageOnly() );
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
Component wrapSimpleName(Name const& _aName)
|
|
{
|
|
return wrapSimpleName( _aName.toString() );
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
Component makeCompositeName(Name const& _aElementName, Name const& _aTypeName)
|
|
{
|
|
OUString const sElementName = _aElementName.toString();
|
|
OUString const sTypeName = _aTypeName.toString();
|
|
|
|
return Component( implMakeCompositeName(sTypeName,sElementName), PackageOnly() );
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
Component makeCompositeName(OUString const& _sElementName, OUString const& _sTypeName)
|
|
{
|
|
return Component( implMakeCompositeName(_sTypeName,_sElementName), PackageOnly() );
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
bool matches(Component const& lhs,Component const& rhs) SAL_THROW(())
|
|
{
|
|
// this extra preflight check might be left out (is it good for performance ?)
|
|
if (lhs.getInternalName() == rhs.getInternalName())
|
|
return true;
|
|
|
|
if (lhs.getName() != rhs.getName())
|
|
return false;
|
|
|
|
// simple names are considered equivalent to wildcard namess
|
|
if (lhs.isSimpleName() || rhs.isSimpleName())
|
|
return true;
|
|
|
|
Name aTypeLHS = lhs.getTypeName();
|
|
Name aTypeRHS = rhs.getTypeName();
|
|
|
|
// this would need an extra test without our preflight check
|
|
OSL_ASSERT(aTypeLHS != aTypeRHS); // would have been dicovered by first check
|
|
|
|
if ( isWildcardType(aTypeLHS.toString()) || isWildcardType(aTypeRHS.toString()) )
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// weak comparison of components
|
|
//-----------------------------------------------------------------------------
|
|
bool before(Component const& lhs, Component const& rhs) SAL_THROW(())
|
|
{ return lhs.getName() < rhs.getName(); }
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool equiv(Component const& lhs, Component const& rhs) SAL_THROW(())
|
|
{ return lhs.getName() == rhs.getName(); }
|
|
|
|
//-----------------------------------------------------------------------------
|
|
size_t hashCode(Component const& comp) SAL_THROW(())
|
|
{ return comp.getName().hashCode(); }
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// class configuration::Path::Rep
|
|
//-----------------------------------------------------------------------------
|
|
void Rep::check_not_empty() const
|
|
{
|
|
if (m_aComponents.empty())
|
|
{
|
|
OSL_ENSURE(!m_aComponents.empty(),"Trying to access components of an empty path");
|
|
throw Exception("Trying to access components of an empty path");
|
|
}
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void Rep::prepend(Rep const& _aOther) SAL_THROW(())
|
|
{
|
|
// to prepend the other path append its components
|
|
m_aComponents.insert( m_aComponents.end(),
|
|
_aOther.m_aComponents.begin(),
|
|
_aOther.m_aComponents.end());
|
|
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
OUString Rep::toString(bool _bAbsolute) const SAL_THROW(())
|
|
{
|
|
Iterator cur = begin();
|
|
Iterator const stop = end();
|
|
|
|
rtl::OUStringBuffer sRet;
|
|
|
|
if (!_bAbsolute && cur != stop)
|
|
sRet = cur++->toPathString();
|
|
|
|
for ( ;cur != stop; ++cur)
|
|
sRet.append( c_cDelimiter ).append( cur->toPathString() );
|
|
|
|
return sRet.makeStringAndClear();
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
size_t Rep::hashCode() const SAL_THROW(())
|
|
{
|
|
const unsigned long mangle_factor = 11; // 1011 (2)
|
|
unsigned long nHash = 0;
|
|
for (Iterator it = begin(), stop = end(); it != stop; ++it)
|
|
{
|
|
nHash = mangle_factor*nHash + Path::hashCode(*it);
|
|
}
|
|
return nHash;
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
bool before(Rep const& lhs, Rep const& rhs) SAL_THROW(())
|
|
{
|
|
return std::lexicographical_compare(lhs.begin(),lhs.end(),rhs.begin(),rhs.end(), Before());
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
bool equiv(Rep const& lhs, Rep const& rhs) SAL_THROW(())
|
|
{
|
|
return (lhs.countComponents() == rhs.countComponents()) &&
|
|
std::equal(lhs.begin(),lhs.end(),rhs.begin(),Equiv());
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
bool matches(Rep const& lhs, Rep const& rhs) SAL_THROW(())
|
|
{
|
|
return (lhs.countComponents() == rhs.countComponents()) &&
|
|
std::equal(lhs.begin(),lhs.end(),rhs.begin(),Matches());
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
bool isAbsolutePath(OUString const& _sPath) SAL_THROW(())
|
|
{
|
|
return detectAbsolutePath(_sPath);
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
bool hasMatchingPrefix(Rep const& _aPath, Rep const& _aPrefix) SAL_THROW(())
|
|
{
|
|
return (_aPath.countComponents() >= _aPrefix.countComponents()) &&
|
|
std::equal( _aPrefix.begin(), _aPrefix.end(), _aPath.begin(), Matches());
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
Rep stripMatchingPrefix(Rep const& _aPath,Rep const& _aPrefix) // SAL_THROW((InvalidName))
|
|
{
|
|
Rep aResult(_aPath);
|
|
|
|
for (Iterator it = _aPrefix.begin(); it != _aPrefix.end(); ++it)
|
|
{
|
|
if (aResult.isEmpty() || !matches(*it,aResult.getFirstName()))
|
|
throw InvalidName(aResult.getFirstName().toPathString(), "does not match the expected location.");
|
|
|
|
aResult.dropFirstName();
|
|
}
|
|
|
|
return aResult;
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
} // namespace Path
|
|
//-----------------------------------------------------------------------------
|
|
|
|
namespace
|
|
{
|
|
//-----------------------------------------------------------------------------
|
|
const sal_Unicode c_quot = '\"';
|
|
const sal_Unicode c_apos = '\'';
|
|
const sal_Unicode c_amp = '&';
|
|
|
|
const sal_Unicode c_end_escape = ';';
|
|
|
|
const sal_Unicode c_normal_quot = c_apos;
|
|
//-------------------------------------------
|
|
static sal_Char const c_amp_name[] = "&";
|
|
static sal_Char const c_apos_name[] = "'";
|
|
static sal_Char const c_quot_name[] = """;
|
|
|
|
const sal_Int32 c_nMinEscapeLen = sizeof c_amp_name - 1;
|
|
const sal_Int32 c_nMaxEscapeLen = sizeof c_quot_name - 1;
|
|
//-------------------------------------------------------------------------
|
|
/// distinguishes which kind of path is held in a path object
|
|
enum PathType { eRELATIVE = 1, eABSOLUTE = 2 };
|
|
|
|
// path parsing iterator type
|
|
typedef sal_Unicode const * StrPos;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// missing or mis leading in SAL/rtl: pStr1[nLength] must NOT be evaluated
|
|
static
|
|
sal_Int32 cfg_ustr_ascii_compare_WithLength( const sal_Unicode* pStr1,
|
|
sal_Int32 nStr1Len,
|
|
const sal_Char* pStr2 )
|
|
{
|
|
while( nStr1Len )
|
|
{
|
|
sal_Int32 nRet = static_cast<sal_Int32>(*pStr1)-
|
|
static_cast<sal_Int32>(static_cast<unsigned char>(*pStr2));
|
|
|
|
if (nRet != 0 || *pStr2 == 0) return nRet;
|
|
|
|
++pStr1;
|
|
++pStr2;
|
|
--nStr1Len;
|
|
}
|
|
|
|
return -static_cast<sal_Int32>(static_cast<unsigned char>(*pStr2));
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
/** find the char being escaped by the escape sequence in the given string range
|
|
@return
|
|
the char being escaped or zero, if the range is no known escape
|
|
*/
|
|
sal_Unicode implParseEscape(StrPos pBegin, StrPos pEnd) SAL_THROW(())
|
|
{
|
|
OSL_PRECOND( pBegin < pEnd, "Nonempty string range expected" );
|
|
OSL_PRECOND( pBegin[0] == c_amp, "String range is not a possible escape: missing start marker" );
|
|
OSL_PRECOND( pEnd[-1] == c_end_escape, "String range is not a possible escape: missing end marker" );
|
|
|
|
sal_Int32 const nLen = pEnd - pBegin;
|
|
|
|
sal_Unicode chResult;
|
|
|
|
if ( c_nMinEscapeLen > nLen || nLen > c_nMaxEscapeLen) // quick check, if there is no possible match
|
|
chResult = 0;
|
|
// the standard escapes
|
|
else if (0 == cfg_ustr_ascii_compare_WithLength(pBegin,nLen,c_amp_name)) chResult = c_amp;
|
|
else if (0 == cfg_ustr_ascii_compare_WithLength(pBegin,nLen,c_apos_name)) chResult = c_apos;
|
|
else if (0 == cfg_ustr_ascii_compare_WithLength(pBegin,nLen,c_quot_name)) chResult = c_quot;
|
|
// extra escapes for XML compatibility
|
|
else if (0 == cfg_ustr_ascii_compare_WithLength(pBegin,nLen,"<")) chResult = sal_Unicode('<');
|
|
else if (0 == cfg_ustr_ascii_compare_WithLength(pBegin,nLen,">")) chResult = sal_Unicode('>');
|
|
else chResult = 0;
|
|
|
|
return chResult;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
/** find the escape sequence to use for the given char
|
|
@return
|
|
an escape sequence, or NULL, if the char should not be escaped
|
|
*/
|
|
inline
|
|
sal_Char const* implGetEscape(sal_Unicode ch ) SAL_THROW(())
|
|
{
|
|
switch (ch)
|
|
{
|
|
case c_amp: return c_amp_name;
|
|
case c_apos: return c_apos_name;
|
|
case c_quot: return c_quot_name;
|
|
|
|
default: return NULL;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
/** find the start of the path component ending before pEnd in the string starting at pBegin
|
|
@return
|
|
a pointer to the last character before pEnd that is not a name delimiter
|
|
*/
|
|
StrPos implFindNameStart(StrPos pBegin, StrPos pEnd) SAL_THROW(())
|
|
{
|
|
OSL_PRECOND(pBegin <= pEnd, "Invalid string range");
|
|
|
|
sal_Int32 const nLen = pEnd-pBegin;
|
|
sal_Int32 const nPos = rtl_ustr_lastIndexOfChar_WithLength(pBegin, nLen, c_cDelimiter) + 1;
|
|
|
|
OSL_ASSERT(0 <= nPos && nPos <= nLen);
|
|
|
|
return pBegin + nPos;
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
/** find the start of the bracketed & quoted predicate ending before pEnd in the string starting at pBegin
|
|
@return
|
|
a pointer to the opening bracket matching the closing bracket at pEnd[-1], if found
|
|
<var>pEnd</var>, if no bracketed string was found
|
|
NULL, if there was a closing bracket, but the beginning could not be discovered
|
|
*/
|
|
StrPos implFindPredicateStart(StrPos pBegin, StrPos pEnd) SAL_THROW(())
|
|
{
|
|
OSL_PRECOND(pBegin < pEnd, "Nonempty string range required");
|
|
|
|
if (pEnd == pBegin || pEnd[-1] != c_rBracket) return pEnd;
|
|
|
|
if (--pEnd == pBegin)
|
|
{
|
|
OSL_ENSURE(false, "Invalid path component: single ']'");
|
|
return NULL; // string was only "]"
|
|
}
|
|
|
|
sal_Unicode chQuote = *--pEnd;
|
|
|
|
if (chQuote != c_quot && chQuote != c_apos)
|
|
{
|
|
// should we support empty brackets ?
|
|
if (chQuote == c_lBracket)
|
|
{
|
|
OSL_ENSURE(false, "Empty predicate brackets found");
|
|
return NULL; // for now we don't
|
|
|
|
}
|
|
|
|
// should we support brackets with non-quoted strings ?
|
|
chQuote = c_lBracket; // for now we do
|
|
}
|
|
|
|
sal_Int32 nStart = rtl_ustr_lastIndexOfChar_WithLength(pBegin, pEnd-pBegin, chQuote);
|
|
|
|
if (chQuote != c_lBracket) // needed to support non-quoted strings
|
|
--nStart;
|
|
|
|
if (nStart < 0)
|
|
{
|
|
OSL_ENSURE(false, "Could not find opening quote or bracket for bracketed predicate");
|
|
return NULL;
|
|
}
|
|
|
|
if (pBegin[nStart] != c_lBracket)
|
|
{
|
|
OSL_ENSURE(false, "Illegal quote character in string");
|
|
return NULL; // for now we don't
|
|
}
|
|
|
|
return pBegin + nStart;
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
/// find the position of the given char in the range given.
|
|
inline
|
|
sal_Int32 indexOfCharInRange(StrPos pBegin, StrPos pEnd, sal_Unicode ch) SAL_THROW(())
|
|
{
|
|
return rtl_ustr_indexOfChar_WithLength(pBegin, pEnd-pBegin, ch);
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
/// find the position of the given char in the range given.
|
|
inline
|
|
bool containsChar(sal_Unicode const * pString, sal_Unicode ch) SAL_THROW(())
|
|
{
|
|
return rtl_ustr_indexOfChar(pString, ch) >= 0;
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
/** validate and normalize a bracketed & quoted predicate from content the string range [pBegin,pEnd)
|
|
@param pRequiredEscapes
|
|
contains a list of characters that must be preescaped or are otherwise invalid
|
|
if NULL is passed, the source range is presumed to contain no escaped data
|
|
otherwise the ampersand (&) and all characters in the list are required to be escaped
|
|
@return
|
|
the normalized, bracketed and quoted predicate
|
|
@throw
|
|
InvalidName, if the predicate data is not valid
|
|
*/
|
|
OUString implMakeNormalizedPredicate(StrPos pBeginContent, StrPos pEndContent, sal_Unicode const* pRequiredEscapes) SAL_THROW((InvalidName))
|
|
{
|
|
OSL_PRECOND(pBeginContent <= pEndContent, "Invalid string range");
|
|
if (pBeginContent == pEndContent)
|
|
return OUString();
|
|
|
|
rtl::OUStringBuffer aNormalized(pEndContent-pBeginContent + 4); // reserve approximate size initially
|
|
|
|
// prefix: opening bracket and quote
|
|
aNormalized.append(c_lBracket).append(c_normal_quot);
|
|
|
|
// content: copy over each char and handle escaping
|
|
for(StrPos pCur = pBeginContent; pCur != pEndContent; ++pCur)
|
|
{
|
|
sal_Unicode ch = *pCur;
|
|
|
|
// maybe parse contained escaping
|
|
if (pRequiredEscapes)
|
|
{
|
|
if (ch == c_amp)
|
|
{
|
|
// find an escape end marker (after pCur). Result is pCur, if the end marker is not there
|
|
StrPos pEndEscape = pCur + 1 + indexOfCharInRange(pCur+1,pEndContent,c_end_escape);
|
|
sal_Unicode ch2 = pCur != pEndEscape ? implParseEscape(pCur,pEndEscape+1) : 0;
|
|
|
|
if (ch2 != 0) // found and read a valid escape sequence
|
|
{
|
|
ch = ch2;
|
|
pCur = pEndEscape;
|
|
OSL_ASSERT(*pCur == c_end_escape);
|
|
}
|
|
else
|
|
{
|
|
OSL_ENSURE(false, "Character '&' must be escaped in this context");
|
|
#if 0
|
|
throw InvalidName(OUString(pBeginContent,pEndContent-pBeginContent),
|
|
"is not a valid element name string. "
|
|
"Character '&' must be escaped in this context");
|
|
#endif
|
|
}
|
|
}
|
|
else if ( containsChar(pRequiredEscapes, ch) )
|
|
{
|
|
throw InvalidName(OUString(pBeginContent,pEndContent-pBeginContent),
|
|
"is not a valid element name string. "
|
|
"Some characters must be escaped in this context");
|
|
}
|
|
}
|
|
|
|
// now append (escape if normal)
|
|
if (sal_Char const * pEscape = implGetEscape(ch))
|
|
aNormalized.appendAscii( pEscape );
|
|
|
|
else
|
|
aNormalized.append( ch );
|
|
}
|
|
|
|
// suffix: closing quote and bracket
|
|
aNormalized.append(c_normal_quot).append(c_rBracket);
|
|
|
|
return aNormalized.makeStringAndClear();
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
/** extract and unescape the normalized predicate content in the string range [pBegin,pEnd)
|
|
@return
|
|
the denormalized predicate content
|
|
*/
|
|
OUString implReadPredicate(StrPos pBegin, StrPos pEnd) SAL_THROW(())
|
|
{
|
|
OSL_PRECOND(pBegin <= pEnd, "Invalid string range");
|
|
|
|
rtl::OUStringBuffer aContent(pEnd-pBegin); // reserve approximate size initially
|
|
|
|
StrPos pReadPos = pBegin;
|
|
|
|
// content: copy data, handling escapes
|
|
for(StrPos pCur = pReadPos; pCur != pEnd; ++pCur)
|
|
{
|
|
if (*pCur != c_amp) continue; // no escape here
|
|
|
|
// handle an escape
|
|
// find an escape end marker (after pCur). Result is pCur, if the end marker is not there
|
|
StrPos pEndEscape = pCur + 1 + indexOfCharInRange(pCur+1,pEnd,c_end_escape);
|
|
|
|
OSL_ENSURE(pEndEscape != pCur, "Found dangling ampersand in normalized data");
|
|
|
|
sal_Unicode ch = implParseEscape(pCur,pEndEscape+1);
|
|
|
|
OSL_ENSURE(ch != 0, "Found unreckognized escape in normalized data");
|
|
|
|
if (ch != 0) // found and read a valid escape sequence
|
|
{
|
|
// do copy of preceding data
|
|
aContent.append(pReadPos, pCur-pReadPos).append(ch);
|
|
pCur = pReadPos = pEndEscape;
|
|
|
|
++pReadPos;
|
|
|
|
OSL_ASSERT(*pCur == c_end_escape);
|
|
}
|
|
// otherwise just treat the ampersand as a mormal character
|
|
}
|
|
|
|
// do copy of remaining data
|
|
if (pReadPos != pEnd)
|
|
aContent.append(pReadPos, pEnd-pReadPos);
|
|
|
|
return aContent.makeStringAndClear();
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
/** validate and normalize the bracketed & quoted predicate in the string range [pBegin,pEnd)
|
|
@return
|
|
the normalized predicate
|
|
@throw
|
|
InvalidName, if the predicate is not valid
|
|
*/
|
|
OUString implNormalizePredicate(StrPos pBegin, StrPos pEnd) SAL_THROW((InvalidName))
|
|
{
|
|
sal_Unicode sStopCharBuf[2];
|
|
sal_Unicode const * pStopChars;
|
|
|
|
OSL_PRECOND(pBegin < pEnd, "Nonempty string range expected");
|
|
OSL_PRECOND(pEnd-pBegin >= 2, "Bracketed string range expected");
|
|
OSL_PRECOND(pBegin[0] == c_lBracket,"Bracketed string range expected");
|
|
OSL_PRECOND(pEnd[-1] == c_rBracket, "Bracketed string range expected");
|
|
|
|
++pBegin; --pEnd; // skip brackets
|
|
|
|
sal_Unicode const chUsedQuot = *pBegin;
|
|
if (chUsedQuot == c_apos || chUsedQuot == c_quot)
|
|
{
|
|
OSL_PRECOND(pBegin < pEnd && pEnd-pBegin >= 2, "Bracketed quoted string range expected");
|
|
OSL_PRECOND(pEnd[-1] == chUsedQuot, "Non-matching quotes in bracketed quoted string");
|
|
|
|
if (pEnd-pBegin <= 1 || pEnd[-1] != chUsedQuot)
|
|
throw InvalidName( OUString(pBegin, pEnd-pBegin), "is not a valid element predicate: quotes do not match");
|
|
|
|
++pBegin; --pEnd; // skip quotes
|
|
|
|
sStopCharBuf[0] = chUsedQuot;
|
|
sStopCharBuf[1] = 0;
|
|
|
|
pStopChars = sStopCharBuf;
|
|
}
|
|
|
|
// non-quoted strings are not really valid, but we tolerate them
|
|
else
|
|
{
|
|
OSL_ENSURE(false, "Warning: Invalid path - non-quoted data in bracketed predicate");
|
|
|
|
static sal_Unicode const sGeneralStoppers[] = { c_quot, c_apos, c_rBracket, c_lBracket, 0 };
|
|
|
|
pStopChars = sGeneralStoppers;
|
|
}
|
|
|
|
if (pBegin == pEnd)
|
|
throw InvalidName(OUString(pBegin-1,2),"Empty element name in predicate");
|
|
|
|
return implMakeNormalizedPredicate(pBegin, pEnd, pStopChars);
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
/// parse a path into a sequence of components
|
|
Path::Rep implParsePath(OUString const& _aPathString, PathType eType) SAL_THROW((InvalidName))
|
|
{
|
|
Path::Rep aResult;
|
|
|
|
StrPos pBegin = _aPathString.getStr();
|
|
StrPos pEnd = pBegin + _aPathString.getLength();
|
|
|
|
if (eType == eABSOLUTE)
|
|
{
|
|
if ( detectAbsolutePath(_aPathString) )
|
|
++pBegin; // skip the leading slash
|
|
|
|
#ifdef CFG_PATH_STRICT
|
|
else
|
|
OSL_ENSURE(false, "Warning: trying to parse relative path as absolute");
|
|
#endif
|
|
}
|
|
else
|
|
OSL_ENSURE(!detectAbsolutePath(_aPathString), "ERROR: trying to parse absolute path as relative one");
|
|
|
|
if (detectAbsolutePath(pBegin))
|
|
throw InvalidName(_aPathString, "is not a valid path. Illegal empty first component");
|
|
|
|
else if (pBegin != pEnd && pEnd[-1] == '/')
|
|
{
|
|
#ifdef CFG_PATH_STRICT
|
|
OSL_ENSURE(false, "Illegal configuration path. Terminating '/' found.");
|
|
#endif
|
|
--pEnd;
|
|
}
|
|
|
|
while (pEnd != pBegin)
|
|
{
|
|
// check for predicate
|
|
StrPos pQuoteStart = implFindPredicateStart(pBegin, pEnd);
|
|
if (pQuoteStart == NULL)
|
|
throw InvalidName(_aPathString, "is not a valid path. Invalid name or predicate syntax");
|
|
|
|
StrPos pNameStart = implFindNameStart(pBegin, pQuoteStart);
|
|
|
|
OUString aElementName(pNameStart, pQuoteStart-pNameStart);
|
|
|
|
if (!isSimpleName(aElementName))
|
|
{
|
|
// this is OK only for few cases WITH predicate
|
|
if (pQuoteStart == pEnd)
|
|
throw InvalidName(_aPathString, "is not a valid path. Invalid name");
|
|
|
|
if (isEmptyString(aElementName))
|
|
aElementName = makeWildcardType();
|
|
|
|
else if ( !isWildcardType(aElementName))
|
|
throw InvalidName(_aPathString, "is not a valid path. Invalid type tag for predicate");
|
|
}
|
|
if (pQuoteStart != pEnd)
|
|
aElementName += implNormalizePredicate(pQuoteStart,pEnd);
|
|
|
|
aResult.prepend( Path::Component(aElementName, Path::PackageOnly()) );
|
|
|
|
pEnd = pNameStart;
|
|
if (pNameStart != pBegin) --pEnd;
|
|
}
|
|
|
|
return aResult;
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
/// build a composite path component from a base name (type) and a (somewhat optional) predicate
|
|
Name implMakeCompositeName(OUString const& _sBaseName, OUString const& _sPredicate) SAL_THROW((InvalidName))
|
|
{
|
|
OUString sComposite(_sBaseName);
|
|
|
|
if (isEmptyString(_sBaseName))
|
|
sComposite = makeWildcardType();
|
|
|
|
else if (!isWildcardType(_sBaseName) && !isSimpleName(_sBaseName))
|
|
throw InvalidName(_sBaseName, "The base-name (type) part of a composite node name must be a simple word");
|
|
|
|
StrPos pPredStart = _sPredicate.getStr();
|
|
StrPos pPredEnd = pPredStart + _sPredicate.getLength();
|
|
|
|
if (pPredStart != pPredEnd)
|
|
sComposite += implMakeNormalizedPredicate(pPredStart, pPredEnd, NULL);
|
|
|
|
return Name( sComposite, Path::PackageOnly() );
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
/// split a composite path component into a base name (type) and a predicate (if present)
|
|
void implSplitCompositeName(Name const& _aCompositeName, OUString& _rBaseName, OUString& _rPredicate) SAL_THROW(())
|
|
{
|
|
OUString sComposite = _aCompositeName.toString();
|
|
sal_Int32 nPos = sComposite.indexOf(c_lBracket);
|
|
|
|
if (nPos >= 0)
|
|
{
|
|
OSL_ENSURE( nPos > 0, "Invalid name: Only predicate, no base type");
|
|
|
|
_rBaseName = sComposite.copy(0,nPos);
|
|
|
|
StrPos pBeginPred = sComposite.getStr() + nPos;
|
|
StrPos pEndPred = sComposite.getStr() + sComposite.getLength();
|
|
|
|
OSL_ASSERT(pBeginPred[0] == c_lBracket);
|
|
OSL_ENSURE(pBeginPred[1] == c_normal_quot, "Missing or unexpected quote mark");
|
|
OSL_ENSURE(pEndPred[-1] == c_rBracket, "Invalid name: Predicate brackets not closed");
|
|
OSL_ENSURE(pEndPred[-2] == c_normal_quot, "Missing or unexpected quote mark");
|
|
|
|
// skip brackets and quotes - then read data
|
|
_rPredicate = implReadPredicate(pBeginPred+2, pEndPred-2);
|
|
}
|
|
else
|
|
{
|
|
OSL_ENSURE( sComposite.indexOf(c_rBracket) < 0, "Invalid name: Predicate brackets not opened");
|
|
_rBaseName = sComposite;
|
|
_rPredicate = OUString();
|
|
}
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// class RelativePath
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Currently unused method to check/ensure validity
|
|
void RelativePath::init() SAL_THROW(())
|
|
{
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
RelativePath RelativePath::parse(OUString const& aString)
|
|
{
|
|
return RelativePath( implParsePath(aString, eRELATIVE) );
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
RelativePath::RelativePath(Path::Component const& aName) SAL_THROW(())
|
|
: m_aRep(aName)
|
|
{
|
|
if (aName.isEmpty()) m_aRep.clearComponents();
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
RelativePath RelativePath::compose(RelativePath const& aPath) const SAL_THROW(())
|
|
{
|
|
Path::Rep aResult = aPath.rep();
|
|
aResult.prepend( this->m_aRep );
|
|
return RelativePath( aResult );
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
OUString RelativePath::toString() const SAL_THROW(())
|
|
{
|
|
return m_aRep.toString(false);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// class AbsolutePath
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Currently unused method to check/ensure validity
|
|
void AbsolutePath::init() SAL_THROW(())
|
|
{
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
AbsolutePath AbsolutePath::parse(OUString const& aString)
|
|
{
|
|
return AbsolutePath( implParsePath(aString, eABSOLUTE) );
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
AbsolutePath AbsolutePath::root() SAL_THROW(())
|
|
{
|
|
return AbsolutePath( Path::Rep() );
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
AbsolutePath AbsolutePath::detachedRoot() SAL_THROW(())
|
|
{
|
|
Path::Rep aRep( Path::makeEmptyComponent() ); // use 1 empty component here, to start detached names
|
|
return AbsolutePath( aRep );
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
static inline Path::Component implMakeSafeModuleName(OUString const& _sModuleName) SAL_THROW(())
|
|
{
|
|
OSL_ENSURE( isSimpleName(_sModuleName), "A module name must be a simple name");
|
|
|
|
// if (isSimpleName(_sModuleName)) sModuleName = escape_name( _sModuleName );
|
|
|
|
return Path::Component(_sModuleName, Path::PackageOnly());
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
AbsolutePath AbsolutePath::makeModulePath(Name const& _aModuleName, NoValidate) SAL_THROW(())
|
|
{
|
|
return AbsolutePath( Path::Rep( implMakeSafeModuleName(_aModuleName.toString()) ) );
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
AbsolutePath AbsolutePath::makeModulePath(OUString const& _sModuleName, NoValidate) SAL_THROW(())
|
|
{
|
|
return AbsolutePath( Path::Rep( implMakeSafeModuleName(_sModuleName) ) );
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
AbsolutePath AbsolutePath::compose(RelativePath const& aPath) const SAL_THROW(())
|
|
{
|
|
Path::Rep aResult = aPath.rep();
|
|
aResult.prepend( this->m_aRep );
|
|
return AbsolutePath( aResult );
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
AbsolutePath AbsolutePath::getParentPath() const
|
|
{
|
|
// or: m_aRep.check_not_empty();
|
|
OSL_ENSURE(!isRoot(), "ERROR: Requesting the parent of a root path");
|
|
if (isRoot()) return *this;
|
|
|
|
OSL_ENSURE(!isDetached(), "ERROR: Requesting the parent of a detached path");
|
|
|
|
return AbsolutePath( Path::Rep(begin(),end()-1) );
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
bool AbsolutePath::isDetached() const SAL_THROW(())
|
|
{
|
|
return !m_aRep.isEmpty() && begin()->isEmpty();
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
OUString AbsolutePath::toString() const SAL_THROW(())
|
|
{
|
|
return m_aRep.toString(true);
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
}
|
|
}
|
|
|