/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (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.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * $Id: XMLUri.cpp 1799602 2017-06-22 18:14:37Z scantor $
 */

// ---------------------------------------------------------------------------
//  Includes
// ---------------------------------------------------------------------------
#include <xercesc/util/Janitor.hpp>
#include <xercesc/util/XMLURL.hpp>
#include <xercesc/util/XMLUri.hpp>
#include <xercesc/util/XMLChar.hpp>
#include <xercesc/util/OutOfMemoryException.hpp>

XERCES_CPP_NAMESPACE_BEGIN

// ---------------------------------------------------------------------------
//  XMLUri: static data
// ---------------------------------------------------------------------------

//      Amended by RFC2732
//      reserved      = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" |
//                      "$" | "," | "[" | "]"
//
const XMLCh XMLUri::RESERVED_CHARACTERS[] =
{
    chSemiColon, chForwardSlash, chQuestion, chColon, chAt,
    chAmpersand, chEqual, chPlus, chDollarSign, chComma, chOpenSquare,
    chCloseSquare, chNull
};

//
//      mark          = "-" | "_" | "." | "!" | "~" | "*" | "'" |
//                      "(" | ")"
//
const XMLCh XMLUri::MARK_CHARACTERS[] =
{
    chDash, chUnderscore, chPeriod, chBang, chTilde,
    chAsterisk, chSingleQuote, chOpenParen, chCloseParen, chNull
};

// combination of MARK and RESERVED
const XMLCh XMLUri::MARK_OR_RESERVED_CHARACTERS[] =
{
    chDash, chUnderscore, chPeriod, chBang, chTilde,
    chAsterisk, chSingleQuote, chOpenParen, chCloseParen,
    chSemiColon, chForwardSlash, chQuestion, chColon, chAt,
    chAmpersand, chEqual, chPlus, chDollarSign, chComma, chOpenSquare,
    chCloseSquare, chNull
};

//
//      scheme        = alpha *( alpha | digit | "+" | "-" | "." )
//
const XMLCh XMLUri::SCHEME_CHARACTERS[] =
{
    chPlus, chDash, chPeriod, chNull
};

//
//      userinfo      = *( unreserved | escaped |
//                         ";" | ":" | "&" | "=" | "+" | "$" | "," )
//
const XMLCh XMLUri::USERINFO_CHARACTERS[] =
{
    chSemiColon, chColon, chAmpersand, chEqual, chPlus,
    chDollarSign, chPeriod, chNull
};

//
//      reg_name     = 1*( unreserved | escaped | "$" | "," |
//                         ";" | ":" | "@" | "&" | "=" | "+" )
//
const XMLCh XMLUri::REG_NAME_CHARACTERS[] =
{
    chDollarSign, chComma, chSemiColon, chColon, chAt,
    chAmpersand, chEqual, chPlus, chNull
};

//      pchar plus ';' and '/'.
//      pchar         = unreserved | escaped |
//                      ":" | "@" | "&" | "=" | "+" | "$" | ","
const XMLCh XMLUri::PATH_CHARACTERS[] =
{
    chSemiColon, chForwardSlash, chColon, chAt, chAmpersand,
    chEqual, chPlus, chDollarSign, chComma, chNull
};


// ---------------------------------------------------------------------------
//  Local methods and data
// ---------------------------------------------------------------------------
static const int BUF_LEN = 64;

//
// "Scheme"
// "Parameters"
// "UserInfo"
// "Host"
// "Port"
// "RegName"
// "Path"
// "Query"
// "Fragment"
//
static const XMLCh errMsg_SCHEME[] =
{
    chLatin_s, chLatin_c, chLatin_h, chLatin_e,
    chLatin_m, chLatin_e, chNull
};

static const XMLCh errMsg_PARAMS[] =
{
    chLatin_p, chLatin_a, chLatin_r, chLatin_a, chLatin_m,
    chLatin_e, chLatin_t, chLatin_e, chLatin_r, chLatin_s, chNull
};

static const XMLCh errMsg_USERINFO[] =
{
    chLatin_u, chLatin_s, chLatin_e, chLatin_r,
    chLatin_i, chLatin_n, chLatin_f, chLatin_o, chNull
};

static const XMLCh errMsg_HOST[] =
{
    chLatin_h, chLatin_o, chLatin_s, chLatin_t, chNull
};

static const XMLCh errMsg_PORT[] =
{
    chLatin_p, chLatin_o, chLatin_r, chLatin_t, chNull
};

static const XMLCh errMsg_REGNAME[] =
{
    chLatin_R, chLatin_e, chLatin_g,
    chLatin_N, chLatin_a, chLatin_m, chLatin_e, chNull
};

static const XMLCh errMsg_PATH[] =
{
    chLatin_p, chLatin_a, chLatin_t, chLatin_h, chNull
};

static const XMLCh errMsg_QUERY[] =
{
    chLatin_q, chLatin_u, chLatin_e, chLatin_r, chLatin_y, chNull
};

static const XMLCh errMsg_FRAGMENT[] =
{
    chLatin_f, chLatin_r, chLatin_a, chLatin_g,
    chLatin_m, chLatin_e, chLatin_n, chLatin_t, chNull
};

//
//  "//"
//  "/"
//  "./"
//  "/."
//  "/../"
//  "/.."
//
static const XMLCh DOUBLE_SLASH[] =
{
    chForwardSlash, chForwardSlash, chNull
};

static const XMLCh SINGLE_SLASH[] =
{
    chForwardSlash, chNull
};

static const XMLCh SLASH_DOT_SLASH[] =
{
    chForwardSlash, chPeriod, chForwardSlash, chNull
};

static const XMLCh SLASH_DOT[] =
{
    chForwardSlash, chPeriod, chNull
};

static const XMLCh SLASH_DOTDOT_SLASH[] =
{
    chForwardSlash, chPeriod, chPeriod, chForwardSlash, chNull
};

static const XMLCh SLASH_DOTDOT[] =
{
    chForwardSlash, chPeriod, chPeriod, chNull
};

//
//  ":/?#"
//
// REVISIT: why?
static const XMLCh SCHEME_SEPARATORS[] =
{
    chColon, chForwardSlash, chQuestion, chPound, chNull
};

// ---------------------------------------------------------------------------
//  XMLUri: Constructors and Helper methods
// ---------------------------------------------------------------------------
// ctor# 2

typedef JanitorMemFunCall<XMLUri>   CleanupType;

XMLUri::XMLUri(const XMLCh* const uriSpec,
               MemoryManager* const manager)
: fPort(-1)
, fScheme(0)
, fUserInfo(0)
, fHost(0)
, fRegAuth(0)
, fPath(0)
, fQueryString(0)
, fFragment(0)
, fURIText(0)
, fMemoryManager(manager)
{
    CleanupType cleanup(this, &XMLUri::cleanUp);

    try {
        initialize((XMLUri *)0, uriSpec);
    }
    catch(const OutOfMemoryException&)
    {
        cleanup.release();

        throw;
    }

    cleanup.release();
}

// ctor# 7 relative ctor
XMLUri::XMLUri(const XMLUri* const      baseURI
              , const XMLCh* const   uriSpec
              , MemoryManager* const manager)
: fPort(-1)
, fScheme(0)
, fUserInfo(0)
, fHost(0)
, fRegAuth(0)
, fPath(0)
, fQueryString(0)
, fFragment(0)
, fURIText(0)
, fMemoryManager(manager)
{
    CleanupType cleanup(this, &XMLUri::cleanUp);

    try {
        initialize(baseURI, uriSpec);
    }
    catch(const OutOfMemoryException&)
    {
        cleanup.release();

        throw;
    }

    cleanup.release();
}

//Copy constructor
XMLUri::XMLUri(const XMLUri& toCopy)
: XSerializable(toCopy)
, XMemory(toCopy)
, fPort(-1)
, fScheme(0)
, fUserInfo(0)
, fHost(0)
, fRegAuth(0)
, fPath(0)
, fQueryString(0)
, fFragment(0)
, fURIText(0)
, fMemoryManager(toCopy.fMemoryManager)
{
    CleanupType cleanup(this, &XMLUri::cleanUp);

    try {
        initialize(toCopy);
    }
    catch(const OutOfMemoryException&)
    {
        cleanup.release();

        throw;
    }

    cleanup.release();
}

XMLUri& XMLUri::operator=(const XMLUri& toAssign)
{
    cleanUp();

    CleanupType cleanup(this, &XMLUri::cleanUp);

    try {
        initialize(toAssign);
    }
    catch(const OutOfMemoryException&)
    {
        cleanup.release();

        throw;
    }

    cleanup.release();

    return *this;
}

XMLUri::~XMLUri()
{
    cleanUp();
}

void XMLUri::cleanUp()
{
    if (fScheme)
        XMLString::release(&fScheme, fMemoryManager);//delete[] fScheme;

    if (fUserInfo)
        XMLString::release(&fUserInfo, fMemoryManager);//delete[] fUserInfo;

    if (fHost)
        XMLString::release(&fHost, fMemoryManager);//delete[] fHost;

    if (fRegAuth)
        XMLString::release(&fRegAuth, fMemoryManager);//delete[] fRegAuth;

    if (fPath)
        XMLString::release(&fPath, fMemoryManager);//delete[] fPath;

    if (fQueryString)
        XMLString::release(&fQueryString, fMemoryManager);//delete[] fQueryString;

    if (fFragment)
        XMLString::release(&fFragment, fMemoryManager);//delete[] fFragment;

    XMLString::release(&fURIText, fMemoryManager);//delete[] fURIText;
}

void XMLUri::initialize(const XMLUri& toCopy)
{
    //
    // assuming that all fields from the toCopy are valid,
    // therefore need NOT to go through various setXXX() methods
    //
    fMemoryManager = toCopy.fMemoryManager;
    fScheme = XMLString::replicate(toCopy.fScheme, fMemoryManager);
    fUserInfo = XMLString::replicate(toCopy.fUserInfo, fMemoryManager);
    fHost = XMLString::replicate(toCopy.fHost, fMemoryManager);
    fPort = toCopy.fPort;
    fRegAuth = XMLString::replicate(toCopy.fRegAuth, fMemoryManager);
    fPath = XMLString::replicate(toCopy.fPath, fMemoryManager);
    fQueryString = XMLString::replicate(toCopy.fQueryString, fMemoryManager);
    fFragment = XMLString::replicate(toCopy.fFragment, fMemoryManager);
}

void XMLUri::initialize(const XMLUri* const baseURI
                      , const XMLCh*  const uriSpec)
{

    // get a trimmed version of uriSpec
    // uriSpec will NO LONGER be used in this function.
    //
    XMLCh* trimmedUriSpec = XMLString::replicate(uriSpec, fMemoryManager);
    XMLString::trim(trimmedUriSpec);
    ArrayJanitor<XMLCh> janName(trimmedUriSpec, fMemoryManager);
    XMLSize_t trimmedUriSpecLen = XMLString::stringLen(trimmedUriSpec);

    if ( !baseURI &&
        (!trimmedUriSpec || trimmedUriSpecLen == 0))
    {
        ThrowXMLwithMemMgr1(MalformedURLException
               , XMLExcepts::XMLNUM_URI_Component_Empty
               , errMsg_PARAMS
               , fMemoryManager);
    }

	// just make a copy of the base if spec is empty
	if (!trimmedUriSpec || trimmedUriSpecLen == 0)
    {
        initialize(*baseURI);
        return;
	}

	XMLSize_t index = 0;
	bool foundScheme = false;

	// Check for scheme, which must be before `/', '?' or '#'.
        int colonIdx = XMLString::indexOf(trimmedUriSpec, chColon);
        int slashIdx = XMLString::indexOf(trimmedUriSpec, chForwardSlash);
        int queryIdx = XMLString::indexOf(trimmedUriSpec, chQuestion);
        int fragmentIdx = XMLString::indexOf(trimmedUriSpec, chPound);

        if ((colonIdx <= 0) ||
            (colonIdx > slashIdx && slashIdx != -1) ||
            (colonIdx > queryIdx && queryIdx != -1) ||
            (colonIdx > fragmentIdx && fragmentIdx != -1))
        {
            // A standalone base is a valid URI according to spec
            if ( colonIdx == 0 || (!baseURI && fragmentIdx != 0) )
            {
                ThrowXMLwithMemMgr(MalformedURLException, XMLExcepts::XMLNUM_URI_No_Scheme, fMemoryManager);
            }
        }
        else
        {
            foundScheme = true;
            initializeScheme(trimmedUriSpec);
            index = XMLString::stringLen(fScheme)+1;
        }

    // It's an error if we stop here
    if (index == trimmedUriSpecLen || (foundScheme && (trimmedUriSpec[index] == chPound)))
    {
        ThrowXMLwithMemMgr1(MalformedURLException
                , XMLExcepts::XMLNUM_URI_Component_Empty
                , errMsg_PATH
                , fMemoryManager);
    }

	// two slashes means generic URI syntax, so we get the authority
    XMLCh* authUriSpec = (XMLCh*) fMemoryManager->allocate
    (
        (trimmedUriSpecLen+1) * sizeof(XMLCh)
    );//new XMLCh[trimmedUriSpecLen+1];
    ArrayJanitor<XMLCh> authName(authUriSpec, fMemoryManager);
    XMLString::subString(authUriSpec, trimmedUriSpec, index, trimmedUriSpecLen, fMemoryManager);

    if (((index+1) < trimmedUriSpecLen) &&
        XMLString::startsWith(authUriSpec, DOUBLE_SLASH))
    {
        index += 2;
        XMLSize_t startPos = index;

        // get authority - everything up to path, query or fragment
        XMLCh testChar;
        while (index < trimmedUriSpecLen)
        {
            testChar = trimmedUriSpec[index];
            if (testChar == chForwardSlash ||
                testChar == chQuestion     ||
                testChar == chPound         )
            {
                break;
            }

            index++;
        }

        // if we found authority, parse it out, otherwise we set the
        // host to empty string
        if (index > startPos)
        {
            XMLString::subString(authUriSpec, trimmedUriSpec, startPos, index, fMemoryManager);
            initializeAuthority(authUriSpec);
        }
        else
        {
            //fHost = 0;
            setHost(XMLUni::fgZeroLenString);
        }
    }

    // we need to check if index has exceed the lenght or not
    if (index >= trimmedUriSpecLen)
        return;

    XMLCh* pathUriSpec = (XMLCh*) fMemoryManager->allocate
    (
        (trimmedUriSpecLen+1) * sizeof(XMLCh)
    );//new XMLCh[trimmedUriSpecLen+1];
    ArrayJanitor<XMLCh> pathUriSpecName(pathUriSpec, fMemoryManager);
    XMLString::subString(pathUriSpec, trimmedUriSpec, index, trimmedUriSpecLen, fMemoryManager);

	initializePath(pathUriSpec);

	// Resolve relative URI to base URI - see RFC 2396 Section 5.2
	// In some cases, it might make more sense to throw an exception
	// (when scheme is specified is the string spec and the base URI
	// is also specified, for example), but we're just following the
	// RFC specifications
	if ( baseURI )
    {
        // check to see if this is the current doc - RFC 2396 5.2 #2
        // note that this is slightly different from the RFC spec in that
        // we don't include the check for query string being null
        // - this handles cases where the urispec is just a query
        // string or a fragment (e.g. "?y" or "#s") -
        // see <http://www.ics.uci.edu/~fielding/url/test1.html> which
        // identified this as a bug in the RFC
        if ((!fPath || !*fPath) &&
            fScheme == 0 &&
            fHost == 0 && fRegAuth == 0)
        {
            fScheme = XMLString::replicate(baseURI->getScheme(), fMemoryManager);
            fMemoryManager->deallocate(fUserInfo);//delete [] fUserInfo;
            fUserInfo = XMLString::replicate(baseURI->getUserInfo(), fMemoryManager);
            fHost = XMLString::replicate(baseURI->getHost(), fMemoryManager);
            fPort = baseURI->getPort();
            fRegAuth = XMLString::replicate(baseURI->getRegBasedAuthority(), fMemoryManager);
            fMemoryManager->deallocate(fPath);//delete [] fPath;
            fPath = XMLString::replicate(baseURI->getPath(), fMemoryManager);

            if ( !fQueryString )
            {
                fQueryString = XMLString::replicate(baseURI->getQueryString(), fMemoryManager);
            }
            return;
        }

        // check for scheme - RFC 2396 5.2 #3
        // if we found a scheme, it means absolute URI, so we're done
        if (fScheme == 0)
        {
            fScheme = XMLString::replicate(baseURI->getScheme(), fMemoryManager);
        }
        else
        {
            return;
        }

        // check for authority - RFC 2396 5.2 #4
        // if we found a host, then we've got a network path, so we're done
        if (fHost == 0 && fRegAuth == 0)
        {
            fMemoryManager->deallocate(fUserInfo);//delete [] fUserInfo;
            fUserInfo = XMLString::replicate(baseURI->getUserInfo(), fMemoryManager);
            fHost = XMLString::replicate(baseURI->getHost(), fMemoryManager);
            fPort = baseURI->getPort();
            fRegAuth = XMLString::replicate(baseURI->getRegBasedAuthority(), fMemoryManager);
        }
        else
        {
            return;
        }

        // check for absolute path - RFC 2396 5.2 #5
        if ((fPath && *fPath) &&
            XMLString::startsWith(fPath, SINGLE_SLASH))
        {
            return;
        }

        // if we get to this point, we need to resolve relative path
        // RFC 2396 5.2 #6

        XMLCh* basePath = XMLString::replicate(baseURI->getPath(), fMemoryManager);
        ArrayJanitor<XMLCh> basePathName(basePath, fMemoryManager);

        XMLSize_t bufLen = trimmedUriSpecLen+XMLString::stringLen(fPath)+XMLString::stringLen(basePath)+1;
        XMLCh* path = (XMLCh*) fMemoryManager->allocate(bufLen * sizeof(XMLCh));//new XMLCh[bufLen];
        ArrayJanitor<XMLCh> pathName(path, fMemoryManager);
        path[0] = 0;

        XMLCh* tmp1 = (XMLCh*) fMemoryManager->allocate(bufLen * sizeof(XMLCh));//new XMLCh[bufLen];
        ArrayJanitor<XMLCh> tmp1Name(tmp1, fMemoryManager);
        XMLCh* tmp2 = (XMLCh*) fMemoryManager->allocate(bufLen * sizeof(XMLCh));//new XMLCh[bufLen];
        ArrayJanitor<XMLCh> tmp2Name(tmp2, fMemoryManager);

        // 6a - get all but the last segment of the base URI path
        if (basePath)
        {
            int lastSlash = XMLString::lastIndexOf(basePath, chForwardSlash);
            if (lastSlash != -1)
            {
                XMLString::subString(path, basePath, 0, lastSlash+1, fMemoryManager);
            }
        }

        // 6b - append the relative URI path
        XMLString::catString(path, fPath);

        // 6c - remove all "./" where "." is a complete path segment
        int iIndex = -1;
        while ((iIndex = XMLString::patternMatch(path, SLASH_DOT_SLASH)) != -1)
        {
            XMLString::subString(tmp1, path, 0, iIndex, fMemoryManager);
            XMLString::subString(tmp2, path, iIndex+2, XMLString::stringLen(path), fMemoryManager);

            path[0] = 0;
            XMLString::catString(path, tmp1);
            XMLString::catString(path, tmp2);
        }

        // 6d - remove "." if path ends with "." as a complete path segment
        if (XMLString::endsWith(path, SLASH_DOT))
        {
            path[XMLString::stringLen(path) - 1] = chNull;
        }

        // 6e - remove all "<segment>/../" where "<segment>" is a complete
        // path segment not equal to ".."
        iIndex = -1;
        int segIndex = -1;
        int offset = 1;

        while ((iIndex = XMLString::patternMatch(&(path[offset]), SLASH_DOTDOT_SLASH)) != -1)
        {
			// Undo offset
			iIndex += offset;

			// Find start of <segment> within substring ending at found point.
			XMLString::subString(tmp1, path, 0, iIndex-1, fMemoryManager);
			segIndex = XMLString::lastIndexOf(tmp1, chForwardSlash);

			// Ensure <segment> exists and != ".."
            if (segIndex != -1                &&
                (path[segIndex+1] != chPeriod ||
                 path[segIndex+2] != chPeriod ||
				 segIndex + 3 != iIndex))
            {

                XMLString::subString(tmp1, path, 0, segIndex, fMemoryManager);
                XMLString::subString(tmp2, path, iIndex+3, XMLString::stringLen(path), fMemoryManager);

                path[0] = 0;
                XMLString::catString(path, tmp1);
                XMLString::catString(path, tmp2);

                offset = (segIndex == 0 ? 1 : segIndex);
            }
            else
            {
                offset += 4;
            }
        }// while

        // 6f - remove ending "<segment>/.." where "<segment>" is a
        // complete path segment
        if (XMLString::endsWith(path, SLASH_DOTDOT))
        {
			// Find start of <segment> within substring ending at found point.
            index = XMLString::stringLen(path) - 3;
			XMLString::subString(tmp1, path, 0, index-1, fMemoryManager);
			segIndex = XMLString::lastIndexOf(tmp1, chForwardSlash);

            if (segIndex != -1                &&
                (path[segIndex+1] != chPeriod ||
                 path[segIndex+2] != chPeriod ||
				 segIndex + 3 != (int)index))
            {
                path[segIndex+1] = chNull;
            }
        }

        if (getPath())
            fMemoryManager->deallocate(fPath);//delete [] fPath;

        fPath = XMLString::replicate(path, fMemoryManager);

    }
}

// ---------------------------------------------------------------------------
//  Components initialization
// ---------------------------------------------------------------------------

//
// authority     = server | reg_name
// server        = [ [ userinfo "@" ] hostport ]
// hostport      = host [ ":" port ]
//
// reg_name      = 1*( unreserved | escaped | "$" | "," |
//                    ";" | ":" | "@" | "&" | "=" | "+" )
//
// userinfo      = *( unreserved | escaped |
//                 ";" | ":" | "&" | "=" | "+" | "$" | "," )
//

void XMLUri::initializeAuthority(const XMLCh* const uriSpec)
{

    int index = 0;
    XMLSize_t start = 0;
    const XMLSize_t end = XMLString::stringLen(uriSpec);

    //
    // server = [ [ userinfo "@" ] hostport ]
    // userinfo is everything up @,
    //
    XMLCh* userinfo = (XMLCh*) fMemoryManager->allocate
    (
        (end+1) * sizeof(XMLCh)
    );//new XMLCh[end+1];
    ArrayJanitor<XMLCh> userName(userinfo, fMemoryManager);
    index = XMLString::indexOf(&(uriSpec[start]), chAt);

    if ( index != -1)
    {
        XMLString::subString(userinfo, &(uriSpec[start]), 0, index, fMemoryManager);
        index++; // skip the @
        start += index;
    }
    else
    {
        userinfo = 0;
    }

    //
    // hostport = host [ ":" port ]
    // host is everything up to ':', or up to
    // and including ']' if followed by ':'.
    //
    XMLCh* host = (XMLCh*) fMemoryManager->allocate
    (
        (end+1) * sizeof(XMLCh)
    );//new XMLCh[end+1];
    ArrayJanitor<XMLCh> hostName(host, fMemoryManager);

    // Search for port boundary.
    if (start < end && uriSpec[start] == chOpenSquare)
    {
    	index = XMLString::indexOf(&(uriSpec[start]), chCloseSquare);
    	if (index != -1)
    	{
            // skip the ']'
            index = ((start + index + 1) < end
              && uriSpec[start + index + 1] == chColon) ? index+1 : -1;
    	}
    }
    else
    {
        index = XMLString::indexOf(&(uriSpec[start]), chColon);
    }

    if ( index != -1 )
    {
        XMLString::subString(host, &(uriSpec[start]), 0, index, fMemoryManager);
        index++;  // skip the :
        start +=index;
    }
    else
    {
        XMLString::subString(host, &(uriSpec[start]), 0, end-start, fMemoryManager);
        start = end;
    }

    // port is everything after ":"

    XMLCh* portStr = (XMLCh*) fMemoryManager->allocate
    (
        (end+1) * sizeof(XMLCh)
    );//new XMLCh[end+1];
    ArrayJanitor<XMLCh> portName(portStr, fMemoryManager);
    int port = -1;

    if ((host && *host) &&   // non empty host
        (index != -1)                    &&   // ":" found
        (start < end)                     )   // ":" is not the last
    {
        XMLString::subString(portStr, &(uriSpec[start]), 0, end-start, fMemoryManager);

        if (portStr && *portStr)
        {
            port = XMLString::parseInt(portStr, fMemoryManager);
        }
    } // if > 0

    // Check if we have server based authority.
    if (isValidServerBasedAuthority(host, port, userinfo, fMemoryManager))
    {
        if (fHost)
            fMemoryManager->deallocate(fHost);//delete [] fHost;

        if (fUserInfo)
            fMemoryManager->deallocate(fUserInfo);//delete[] fUserInfo;

        fHost = XMLString::replicate(host, fMemoryManager);
        fPort = port;
        fUserInfo = XMLString::replicate(userinfo, fMemoryManager);

        return;
    }
    // This must be registry based authority or the URI is malformed.
    setRegBasedAuthority(uriSpec);
}

// scheme = alpha *( alpha | digit | "+" | "-" | "." )
void XMLUri::initializeScheme(const XMLCh* const uriSpec)
{
    const XMLCh* tmpPtr = XMLString::findAny(uriSpec, SCHEME_SEPARATORS);

    if ( !tmpPtr )
    {
        ThrowXMLwithMemMgr(MalformedURLException, XMLExcepts::XMLNUM_URI_No_Scheme, fMemoryManager);
    }
	else
    {
        XMLCh* scheme = (XMLCh*) fMemoryManager->allocate
        (
            (XMLString::stringLen(uriSpec) + 1) * sizeof(XMLCh)
        );//new XMLCh[XMLString::stringLen(uriSpec)+1];
        ArrayJanitor<XMLCh> tmpName(scheme, fMemoryManager);
        XMLString::subString(scheme, uriSpec, 0, (tmpPtr - uriSpec), fMemoryManager);
        setScheme(scheme);
	}

}

void XMLUri::initializePath(const XMLCh* const uriSpec)
{
    if ( !uriSpec )
    {
        ThrowXMLwithMemMgr1(MalformedURLException
                , XMLExcepts::XMLNUM_URI_Component_Empty
                , errMsg_PATH
                , fMemoryManager);
    }

    XMLSize_t index = 0;
    XMLSize_t start = 0;
    XMLSize_t end = XMLString::stringLen(uriSpec);
    XMLCh testChar = 0;

    // path - everything up to query string or fragment
    if (start < end)
    {
        // RFC 2732 only allows '[' and ']' to appear in the opaque part.
        if (!getScheme() || uriSpec[start] == chForwardSlash)
        {
            // Scan path.
            // abs_path = "/"  path_segments
            // rel_path = rel_segment [ abs_path ]
            while (index < end)
            {
                testChar = uriSpec[index];
                if (testChar == chQuestion || testChar == chPound)
                {
                    break;
                }

                // check for valid escape sequence
                if (testChar == chPercent)
                {
                    if (index + 2 >= end)
                    {
                        XMLCh value1[3];
                        value1[1] = chNull;
                        value1[2] = chNull;
                        XMLString::moveChars(value1, &(uriSpec[index]), (index + 1 >= end ? 1 : 2));
                        ThrowXMLwithMemMgr2(MalformedURLException
                            , XMLExcepts::XMLNUM_URI_Component_Invalid_EscapeSequence
                            , errMsg_PATH
                            , value1
                            , fMemoryManager);
                    }
                    else if (!XMLString::isHex(uriSpec[index+1]) || !XMLString::isHex(uriSpec[index+2]))
                    {
                        XMLCh value1[4];
                        XMLString::moveChars(value1, &(uriSpec[index]), 3);
                        value1[3] = chNull;
                        ThrowXMLwithMemMgr2(MalformedURLException
                                , XMLExcepts::XMLNUM_URI_Component_Invalid_EscapeSequence
                                , errMsg_PATH
                                , value1
                                , fMemoryManager);
                    }
                }
                else if (!isUnreservedCharacter(testChar) &&
                         !isPathCharacter(testChar))
                {
                    XMLCh value1[2];
                    value1[0] = testChar;
                    value1[1] = chNull;
                    ThrowXMLwithMemMgr2(MalformedURLException
                            , XMLExcepts::XMLNUM_URI_Component_Invalid_Char
                            , errMsg_PATH
                            , value1
                            , fMemoryManager);
                }

                index++;
            }//while (index < end)
        }
        else
        {
            // Scan opaque part.
            // opaque_part = uric_no_slash *uric
            while (index < end)
            {
                testChar = uriSpec[index];
                if (testChar == chQuestion || testChar == chPound)
                {
                    break;
                }

                // check for valid escape sequence
                if (testChar == chPercent)
                {
                    if (index + 2 >= end)
                    {
                        XMLCh value1[3];
                        value1[1] = chNull;
                        value1[2] = chNull;
                        XMLString::moveChars(value1, &(uriSpec[index]), (index + 1 >= end ? 1 : 2));
                        ThrowXMLwithMemMgr2(MalformedURLException
                            , XMLExcepts::XMLNUM_URI_Component_Invalid_EscapeSequence
                            , errMsg_PATH
                            , value1
                            , fMemoryManager);
                    }
                    else if (!XMLString::isHex(uriSpec[index+1]) || !XMLString::isHex(uriSpec[index+2]))
                    {
                        XMLCh value1[4];
                        XMLString::moveChars(value1, &(uriSpec[index]), 3);
                        value1[3] = chNull;
                        ThrowXMLwithMemMgr2(MalformedURLException
                                , XMLExcepts::XMLNUM_URI_Component_Invalid_EscapeSequence
                                , errMsg_PATH
                                , value1
                                , fMemoryManager);
                    }
                }
                // If the scheme specific part is opaque, it can contain '['
                // and ']'. uric_no_slash wasn't modified by RFC 2732, which
                // I've interpreted as an error in the spec, since the
                // production should be equivalent to (uric - '/'), and uric
                // contains '[' and ']'.
                else if (!isReservedOrUnreservedCharacter(testChar))
                {
                    XMLCh value1[2];
                    value1[0] = testChar;
                    value1[1] = chNull;
                    ThrowXMLwithMemMgr2(MalformedURLException
                            , XMLExcepts::XMLNUM_URI_Component_Invalid_Char
                            , errMsg_PATH
                            , value1
                            , fMemoryManager);
                }

                index++;
            }//while (index < end)
        }
    } //if (start < end)

    if (getPath())
    {
        fMemoryManager->deallocate(fPath);//delete [] fPath;
    }

    fPath = (XMLCh*) fMemoryManager->allocate((index+1) * sizeof(XMLCh));//new XMLCh[index+1];
    XMLString::subString(fPath, uriSpec, start, index, fMemoryManager);

    // query - starts with ? and up to fragment or end
    if (testChar == chQuestion)
    {
        index++;
        start = index;
        while (index < end)
        {
            testChar = uriSpec[index];
            if (testChar == chPound)
            {
                break;
            }

            if (testChar == chPercent)
            {
                if (index + 2 >= end)
                {
                    XMLCh value1[3];
                    value1[1] = chNull;
                    value1[2] = chNull;
                    XMLString::moveChars(value1, &(uriSpec[index]), (index + 1 >= end ? 1 : 2));
                    ThrowXMLwithMemMgr2(MalformedURLException
                        , XMLExcepts::XMLNUM_URI_Component_Invalid_EscapeSequence
                        , errMsg_QUERY
                        , value1
                        , fMemoryManager);
                }
                if (!XMLString::isHex(uriSpec[index+1]) || !XMLString::isHex(uriSpec[index+2]))
                {
                    XMLCh value1[4];
                    XMLString::moveChars(value1, &(uriSpec[index]), 3);
                    value1[3] = chNull;
                    ThrowXMLwithMemMgr2(MalformedURLException
                            , XMLExcepts::XMLNUM_URI_Component_Invalid_EscapeSequence
                            , errMsg_QUERY
                            , value1
                            , fMemoryManager);
                }
            }
            else if (!isReservedOrUnreservedCharacter(testChar))
            {
                XMLCh value1[2];
                value1[0] = testChar;
                value1[1] = chNull;
                ThrowXMLwithMemMgr2(MalformedURLException
                        , XMLExcepts::XMLNUM_URI_Component_Invalid_Char
                        , errMsg_QUERY
                        , value1
                        , fMemoryManager);
            }
            index++;
        }

        if (getQueryString())
        {
            fMemoryManager->deallocate(fQueryString);//delete [] fQueryString;
        }

        fQueryString = (XMLCh*) fMemoryManager->allocate
        (
            (index - start + 1) * sizeof(XMLCh)
        );//new XMLCh[index - start + 1];
        XMLString::subString(fQueryString, uriSpec, start, index, fMemoryManager);
    }

    // fragment - starts with #
    if (testChar == chPound)
    {
        index++;
        start = index;
        while (index < end)
        {
            testChar = uriSpec[index];

            if (testChar == chPercent)
            {
                if (index + 2 >= end)
                {
                    XMLCh value1[3];
                    value1[1] = chNull;
                    value1[2] = chNull;
                    XMLString::moveChars(value1, &(uriSpec[index]), (index + 1 >= end ? 1 : 2));
                    ThrowXMLwithMemMgr2(MalformedURLException
                        , XMLExcepts::XMLNUM_URI_Component_Invalid_EscapeSequence
                        , errMsg_FRAGMENT
                        , value1
                        , fMemoryManager);
                }
                if (!XMLString::isHex(uriSpec[index+1]) || !XMLString::isHex(uriSpec[index+2]))
                {
                    XMLCh value1[4];
                    XMLString::moveChars(value1, &(uriSpec[index]), 3);
                    value1[3] = chNull;
                    ThrowXMLwithMemMgr2(MalformedURLException
                            , XMLExcepts::XMLNUM_URI_Component_Invalid_EscapeSequence
                            , errMsg_FRAGMENT
                            , value1
                            , fMemoryManager);
                }
            }
            else if (!isReservedOrUnreservedCharacter(testChar))
            {
                XMLCh value1[2];
                value1[0] = testChar;
                value1[1] = chNull;
                ThrowXMLwithMemMgr2(MalformedURLException
                        , XMLExcepts::XMLNUM_URI_Component_Invalid_Char
                        , errMsg_FRAGMENT
                        , value1
                        , fMemoryManager);
            }

            index++;

        }

        if (getFragment())
            fMemoryManager->deallocate(fFragment);//delete [] fFragment;

        //make sure that there is something following the '#'
        if (index > start)
        {
            fFragment = (XMLCh*) fMemoryManager->allocate
            (
                (index - start + 1) * sizeof(XMLCh)
            );//new XMLCh[index - start + 1];
            XMLString::subString(fFragment, uriSpec, start, index, fMemoryManager);
        }
        else
        {
            // RFC 2396, 4.0. URI Reference
            // URI-reference = [absoulteURI | relativeURI] [# fragment]
            //
            // RFC 2396, 4.1. Fragment Identifier
            // fragment = *uric
            //
            // empty fragment is valid
            fFragment = 0;
        }
    }

}

// ---------------------------------------------------------------------------
//  Setter
// ---------------------------------------------------------------------------
void XMLUri::setScheme(const XMLCh* const newScheme)
{
    if ( !newScheme )
    {
        ThrowXMLwithMemMgr1(MalformedURLException
                , XMLExcepts::XMLNUM_URI_Component_Set_Null
                , errMsg_SCHEME
                , fMemoryManager);
    }

    if (!isConformantSchemeName(newScheme))
    {
        ThrowXMLwithMemMgr2(MalformedURLException
                , XMLExcepts::XMLNUM_URI_Component_Not_Conformant
                , errMsg_SCHEME
                , newScheme
                , fMemoryManager);
    }

    if (getScheme())
    {
        fMemoryManager->deallocate(fScheme);//delete [] fScheme;
    }

    fScheme = XMLString::replicate(newScheme, fMemoryManager);
    XMLString::lowerCase(fScheme);
}

//
// server = [ [ userinfo "@" ] hostport ]
// hostport = host [":" port]
//
// setUserInfo(), setHost() and setPort() are closely related
// three methods, in a word, userinfo and port has dependency
// on host.
//
// if host is not present, userinfo must be null and port = -1
//
void XMLUri::setUserInfo(const XMLCh* const newUserInfo)
{
    if ( newUserInfo &&
         !getHost()    )
    {
        ThrowXMLwithMemMgr2(MalformedURLException
                , XMLExcepts::XMLNUM_URI_NullHost
                , errMsg_USERINFO
                , newUserInfo
                , fMemoryManager);
    }

    isConformantUserInfo(newUserInfo, fMemoryManager);

    if (getUserInfo())
    {
        fMemoryManager->deallocate(fUserInfo);//delete [] fUserInfo;
    }

    //sometimes we get passed a empty string rather than a null.
    //Other procedures rely on it being null
    if(newUserInfo && *newUserInfo) {
        fUserInfo = XMLString::replicate(newUserInfo, fMemoryManager);
    }
    else
        fUserInfo = 0;

}

void XMLUri::setHost(const XMLCh* const newHost)
{
    if ( !newHost )
    {
        if (getHost())
            fMemoryManager->deallocate(fHost);//delete [] fHost;

        fHost = 0;
        setUserInfo(0);
        setPort(-1);

        return;
    }

    if ( *newHost && !isWellFormedAddress(newHost, fMemoryManager))
    {
        ThrowXMLwithMemMgr2(MalformedURLException
                , XMLExcepts::XMLNUM_URI_Component_Not_Conformant
                , errMsg_HOST
                , newHost
                , fMemoryManager);
    }

    if (getHost())
    {
        fMemoryManager->deallocate(fHost);//delete [] fHost;
    }

    fHost = XMLString::replicate(newHost, fMemoryManager);
    setRegBasedAuthority(0);
}

void XMLUri::setPort(int newPort)
{
    if (newPort >= 0 && newPort <= 65535)
    {
        if (!getHost())
        {
            XMLCh value1[BUF_LEN+1];
            XMLString::binToText(newPort, value1, BUF_LEN, 10, fMemoryManager);
            ThrowXMLwithMemMgr2(MalformedURLException
                    , XMLExcepts::XMLNUM_URI_NullHost
                    , errMsg_PORT
                    , value1
                    , fMemoryManager);
        }
    }
    else if (newPort != -1)
    {
        XMLCh value1[BUF_LEN+1];
        XMLString::binToText(newPort, value1, BUF_LEN, 10, fMemoryManager);
        ThrowXMLwithMemMgr1(MalformedURLException
                , XMLExcepts::XMLNUM_URI_PortNo_Invalid
                , value1
                , fMemoryManager);
    }

    fPort = newPort;
}

void XMLUri::setRegBasedAuthority(const XMLCh* const newRegAuth)
{
    if ( !newRegAuth )
    {
        if (getRegBasedAuthority())
            fMemoryManager->deallocate(fRegAuth);//delete [] fRegAuth;

        fRegAuth = 0;
        return;
    }
    // reg_name = 1*( unreserved | escaped | "$" | "," |
    //            ";" | ":" | "@" | "&" | "=" | "+" )
    else if ( !*newRegAuth || !isValidRegistryBasedAuthority(newRegAuth) )
    {
        ThrowXMLwithMemMgr2(MalformedURLException
                , XMLExcepts::XMLNUM_URI_Component_Not_Conformant
                , errMsg_REGNAME
                , newRegAuth
                , fMemoryManager);
    }

    if (getRegBasedAuthority())
        fMemoryManager->deallocate(fRegAuth);//delete [] fRegAuth;

    fRegAuth = XMLString::replicate(newRegAuth, fMemoryManager);
    setHost(0);
}

//
// setPath(), setQueryString() and setFragment() are closely
// related three methods as well.
//
void XMLUri::setPath(const XMLCh* const newPath)
{
    if (!newPath)
    {
        if (getPath())
            fMemoryManager->deallocate(fPath);//delete [] fPath;

        fPath = 0;
        setQueryString(0);
        setFragment(0);
    }
    else
    {
        initializePath(newPath);
    }
}

//
// fragment = *uric
//
void XMLUri::setFragment(const XMLCh* const newFragment)
{
	if ( !newFragment )
    {
        if (getFragment())
            fMemoryManager->deallocate(fFragment);//delete [] fFragment;

        fFragment = 0;
	}
	else if (!isGenericURI())
    {
        ThrowXMLwithMemMgr2(MalformedURLException
                , XMLExcepts::XMLNUM_URI_Component_for_GenURI_Only
                , errMsg_FRAGMENT
                , newFragment
                , fMemoryManager);
	}
	else if ( !getPath() )
    {
        ThrowXMLwithMemMgr2(MalformedURLException
               , XMLExcepts::XMLNUM_URI_NullPath
               , errMsg_FRAGMENT
               , newFragment
               , fMemoryManager);
	}
	else if (!isURIString(newFragment))
    {
        ThrowXMLwithMemMgr1(MalformedURLException
                , XMLExcepts::XMLNUM_URI_Component_Invalid_Char
                , errMsg_FRAGMENT
                , fMemoryManager);
	}
	else
    {
        if (getFragment())
        {
            fMemoryManager->deallocate(fFragment);//delete [] fFragment;
        }

        fFragment = XMLString::replicate(newFragment, fMemoryManager);
	}
}

//
// query = *uric
//
void XMLUri::setQueryString(const XMLCh* const newQueryString)
{
	if ( !newQueryString )
    {
        if (getQueryString())
            fMemoryManager->deallocate(fQueryString);//delete [] fQueryString;

        fQueryString = 0;
	}
	else if (!isGenericURI())
    {
        ThrowXMLwithMemMgr2(MalformedURLException
                , XMLExcepts::XMLNUM_URI_Component_for_GenURI_Only
                , errMsg_QUERY
                , newQueryString
                , fMemoryManager);
	}
	else if ( !getPath() )
    {
        ThrowXMLwithMemMgr2(MalformedURLException
                , XMLExcepts::XMLNUM_URI_NullPath
                , errMsg_QUERY
                , newQueryString
                , fMemoryManager);
	}
	else if (!isURIString(newQueryString))
    {
        ThrowXMLwithMemMgr2(MalformedURLException
               , XMLExcepts::XMLNUM_URI_Component_Invalid_Char
               , errMsg_QUERY
               , newQueryString
               , fMemoryManager);
	}
	else
    {
        if (getQueryString())
        {
            fMemoryManager->deallocate(fQueryString);//delete [] fQueryString;
        }

        fQueryString = XMLString::replicate(newQueryString, fMemoryManager);
	}
}

// ---------------------------------------------------------------------------
//  XMLUri: Public, static methods
// ---------------------------------------------------------------------------

//
//  scheme = alpha *( alpha | digit | "+" | "-" | "." )
//  alphanum = alpha | digit
//
bool XMLUri::isConformantSchemeName(const XMLCh* const scheme)
{
	if ( !scheme )
        return false;

    const XMLCh* tmpStr = scheme;
    if (!XMLString::isAlpha(*tmpStr))     // first: alpha
        return false;

    // second onwards: ( alpha | digit | "+" | "-" | "." )
    tmpStr++;
    while (*tmpStr)
    {
        if ( !XMLString::isAlphaNum(*tmpStr) &&
             (XMLString::indexOf(SCHEME_CHARACTERS, *tmpStr) == -1))
            return false;

        tmpStr++;
    }

    return true;
}

//
// userinfo = *( unreserved | escaped |
//              ";" | ":" | "&" | "=" | "+" | "$" | "," )
//
void XMLUri::isConformantUserInfo(const XMLCh* const userInfo
                                  , MemoryManager* const manager)
{
	if ( !userInfo )
        return;

    const XMLCh* tmpStr = userInfo;
    while (*tmpStr)
    {
        if ( isUnreservedCharacter(*tmpStr) ||
            (XMLString::indexOf(USERINFO_CHARACTERS, *tmpStr) != -1))
        {
            tmpStr++;
        }
        else if (*tmpStr == chPercent)               // '%'
        {
            if (XMLString::stringLen(tmpStr) >= 3
                && XMLString::isHex(*(tmpStr+1))     // 1st hex
                && XMLString::isHex(*(tmpStr+2))  )  // 2nd hex
            {
                tmpStr+=3;
            }
            else
            {
                XMLCh value1[4];
                value1[0] = chPercent;
                value1[1] = *(tmpStr+1);
                value1[2] = *(tmpStr+2);
                value1[3] = chNull;

                ThrowXMLwithMemMgr2(MalformedURLException
                        , XMLExcepts::XMLNUM_URI_Component_Invalid_EscapeSequence
                        , errMsg_USERINFO
                        , value1
                        , manager);
            }
        }
        else
        {
            ThrowXMLwithMemMgr2(MalformedURLException
                    , XMLExcepts::XMLNUM_URI_Component_Invalid_Char
                    , errMsg_USERINFO
                    , userInfo
                    , manager);
        }
    } //while

    return;
}

bool XMLUri::isValidServerBasedAuthority(const XMLCh* const host,
                                         const XMLSize_t hostLen,
                                         const int port,
                                         const XMLCh* const userinfo,
                                         const XMLSize_t userLen)
{
    // The order is important, do not change
    if (!isWellFormedAddress(host, hostLen))
        return false;

    // check port number
    if ((port > 65535) || (port < 0 && port != -1))
        return false;

    // check userinfo
    XMLSize_t index = 0;
    while (index < userLen)
    {
        if (isUnreservedCharacter(userinfo[index]) ||
            (XMLString::indexOf(USERINFO_CHARACTERS, userinfo[index]) != -1))
        {
            index++;
        }
        else if (userinfo[index] == chPercent)               // '%'
        {
            if (index + 2 < userLen
                && XMLString::isHex(userinfo[index+1])       // 1st hex
                && XMLString::isHex(userinfo[index+2])  )    // 2nd hex
                index +=3;
            else
                return false;
        }
        else
            return false;
    } //while

    return true;
}

bool XMLUri::isValidServerBasedAuthority(const XMLCh* const host
                                         , const int port
                                         , const XMLCh* const userinfo
                                         , MemoryManager* const manager)
{
    // The order is important, do not change
    if (!isWellFormedAddress(host, manager))
        return false;

    // check port number
    if ((port > 65535) || (port < 0 && port != -1))
        return false;

    // check userinfo
    if (!userinfo)
        return true;

    const XMLCh* tmpStr = userinfo;
    while (*tmpStr)
    {
        if ( isUnreservedCharacter(*tmpStr) ||
            (XMLString::indexOf(USERINFO_CHARACTERS, *tmpStr) != -1))
        {
            tmpStr++;
        }
        else if (*tmpStr == chPercent)               // '%'
        {
            if (XMLString::stringLen(tmpStr) >= 3
                && XMLString::isHex(*(tmpStr+1))     // 1st hex
                && XMLString::isHex(*(tmpStr+2))  )  // 2nd hex
            {
                tmpStr+=3;
            }
            else
                return false;
        }
        else
            return false;
    } //while

    return true;
}

bool XMLUri::isValidRegistryBasedAuthority(const XMLCh* const authority,
                                           const XMLSize_t authLen)
{
    // check authority
    XMLSize_t index = 0;
    while (index < authLen)
    {
        if (isUnreservedCharacter(authority[index]) ||
            (XMLString::indexOf(REG_NAME_CHARACTERS, authority[index]) != -1))
        {
            index++;
        }
        else if (authority[index] == chPercent)               // '%'
        {
            if (index + 2 < authLen
                && XMLString::isHex(authority[index+1])       // 1st hex
                && XMLString::isHex(authority[index+2])  )    // 2nd hex
                index +=3;
            else
                return false;
        }
        else
            return false;
    } //while

    return true;
}

bool XMLUri::isValidRegistryBasedAuthority(const XMLCh* const authority)
{
    // check authority
    if (!authority)
        return false;

    const XMLCh* tmpStr = authority;
    while (*tmpStr)
    {
        if (isUnreservedCharacter(*tmpStr) ||
            (XMLString::indexOf(REG_NAME_CHARACTERS, *tmpStr) != -1))
        {
            tmpStr++;
        }
        else if (*tmpStr == chPercent)               // '%'
        {
            if (XMLString::stringLen(tmpStr) >= 3
                && XMLString::isHex(*(tmpStr + 1))     // 1st hex
                && XMLString::isHex(*(tmpStr + 2)))  // 2nd hex
            {
                tmpStr+=3;
            }
            else
                return false;
        }
        else
            return false;
    } //while

    return true;
}

//
// uric     = reserved | unreserved | escaped
// escaped  = "%" hex hex
// hex      = digit | "A" | "B" | "C" | "D" | "E" | "F" |
//                    "a" | "b" | "c" | "d" | "e" | "f"
//
bool XMLUri::isURIString(const XMLCh* const uricString)
{
	if (!uricString || !*uricString)
        return false;

    const XMLCh* tmpStr = uricString;

    while (*tmpStr)
    {
        if (isReservedOrUnreservedCharacter(*tmpStr))
        {
            tmpStr++;
        }
        else if (*tmpStr == chPercent)               // '%'
        {
            if (XMLString::stringLen(tmpStr) >=3
                && XMLString::isHex(*(tmpStr+1))     // 1st hex
                && XMLString::isHex(*(tmpStr+2))  )  // 2nd hex
            {
                tmpStr+=3;
            }
            else
            {
                return false;
            }
        }
        else
        {
            return false;
        }
    }

    return true;
}

//
//  host          = hostname | IPv4address
//
//  hostname      = *( domainlabel "." ) toplabel [ "." ]
//  domainlabel   = alphanum | alphanum *( alphanum | "-" ) alphanum
//  toplabel      = alpha | alpha *( alphanum | "-" ) alphanum
//
//  IPv4address   = 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT
//
bool XMLUri::isWellFormedAddress(const XMLCh* const addrString
                                 , MemoryManager* const manager)
{
    // Check that we have a non-zero length string.
    if (!addrString || !*addrString)
        return false;

    // Get address length.
    XMLSize_t addrStrLen = XMLString::stringLen(addrString);

    // Check if the host is a valid IPv6reference.
    if (*addrString == chOpenSquare)
    {
        return isWellFormedIPv6Reference(addrString, addrStrLen);
    }

    //
    // Cannot start with a '.', '-', or end with a '-'.
    //
    if (*addrString == chPeriod ||
        *addrString == chDash ||
        addrString[addrStrLen-1] == chDash)
        return false;

    // rightmost domain label starting with digit indicates IP address
    // since top level domain label can only start with an alpha
    // see RFC 2396 Section 3.2.2

    int lastPeriodPos = XMLString::lastIndexOf(addrString, chPeriod);

    // if the string ends with "."
    // get the second last "."
    if (XMLSize_t(lastPeriodPos + 1) == addrStrLen)
    {
        XMLCh* tmp2 = (XMLCh*) manager->allocate
        (
            addrStrLen * sizeof(XMLCh)
        );//new XMLCh[addrStrLen];
        XMLString::subString(tmp2, addrString, 0, lastPeriodPos, manager);
        lastPeriodPos = XMLString::lastIndexOf(tmp2, chPeriod);
        manager->deallocate(tmp2);//delete [] tmp2;

        if ( XMLString::isDigit(addrString[lastPeriodPos + 1]))
			return false;
    }

    if (XMLString::isDigit(addrString[lastPeriodPos + 1]))
    {
        return isWellFormedIPv4Address(addrString, addrStrLen);
    } // end of IPv4address
    else
    {
        //
        //  hostname      = *( domainlabel "." ) toplabel [ "." ]
        //  domainlabel   = alphanum | alphanum *( alphanum | "-" ) alphanum
        //  toplabel      = alpha | alpha *( alphanum | "-" ) alphanum

        // RFC 2396 states that hostnames take the form described in
        // RFC 1034 (Section 3) and RFC 1123 (Section 2.1). According
        // to RFC 1034, hostnames are limited to 255 characters.
        if (addrStrLen > 255) {
            return false;
        }

        unsigned int labelCharCount = 0;

        // domain labels can contain alphanumerics and '-"
        // but must start and end with an alphanumeric
        for (XMLSize_t i = 0; i < addrStrLen; i++)
        {
            if (addrString[i] == chPeriod)
            {
              if (((i > 0)  &&
                   (!XMLString::isAlphaNum(addrString[i-1]))) ||
                  ((i + 1 < addrStrLen) &&
                   (!XMLString::isAlphaNum(addrString[i+1])))  )
                {
                    return false;
                }
                labelCharCount = 0;
            }
            else if (!XMLString::isAlphaNum(addrString[i]) &&
                      addrString[i] != chDash)
            {
                return false;
            }
            // RFC 1034: Labels must be 63 characters or less.
            else if (++labelCharCount > 63) {
                return false;
            }
        } //for
    }

    return true;
}

//
//  RFC 2732 amended RFC 2396 by replacing the definition
//  of IPv4address with the one defined by RFC 2373.
//
//  IPv4address   = 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT
//
bool XMLUri::isWellFormedIPv4Address(const XMLCh* const addr, const XMLSize_t length)
{
    int numDots = 0;
    int numDigits = 0;

    // IPv4address = 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT
    //
    // make sure that
    // 1) we see only digits and dot separators,
    // 2) that any dot separator is preceded and followed by a digit
    // 3) that we find 3 dots
    // 4) that each segment contains 1 to 3 digits.
    // 5) that each segment is not greater than 255.
    for (XMLSize_t i = 0; i < length; ++i)
    {
        if (addr[i] == chPeriod)
        {
            if ((i == 0) ||
                (i+1 == length) ||
                !XMLString::isDigit(addr[i+1]))
            {
               return false;
            }
            numDigits = 0;
            if (++numDots > 3)
                return false;
        }
        else if (!XMLString::isDigit(addr[i]))
        {
            return false;
        }
        // Check that that there are no more than three digits
        // in this segment.
        else if (++numDigits > 3)
        {
            return false;
        }
        // Check that this segment is not greater than 255.
        else if (numDigits == 3)
        {
            XMLCh first = addr[i-2];
            XMLCh second = addr[i-1];
            XMLCh last = addr[i];
            if (!(first < chDigit_2 ||
                 (first == chDigit_2 &&
                 (second < chDigit_5 ||
                 (second == chDigit_5 && last <= chDigit_5)))))
            {
                return false;
            }
        }
    } //for
    return (numDots == 3);
}

//
//  IPv6reference = "[" IPv6address "]"
//
bool XMLUri::isWellFormedIPv6Reference(const XMLCh* const addr, const XMLSize_t length)
{
    XMLSize_t end = length-1;

    // Check if string is a potential match for IPv6reference.
    if (!(length > 2 && addr[0] == chOpenSquare && addr[end] == chCloseSquare))
    {
        return false;
    }

    // Counter for the number of 16-bit sections read in the address.
    int counter = 0;

    // Scan hex sequence before possible '::' or IPv4 address.
    int iIndex = scanHexSequence(addr, 1, end, counter);
    if (iIndex == -1)
        return false;

    XMLSize_t index=(XMLSize_t)iIndex;
    // Address must contain 128-bits of information.
    if (index == end)
    {
       return (counter == 8);
    }

    if (index+1 < end && addr[index] == chColon)
    {
        if (addr[index+1] == chColon)
        {
            // '::' represents at least one 16-bit group of zeros.
            if (++counter > 8)
            {
                return false;
            }
            index += 2;
            // Trailing zeros will fill out the rest of the address.
            if (index == end)
            {
                return true;
            }
        }
        // If the second character wasn't ':', in order to be valid,
        // the remainder of the string must match IPv4Address,
        // and we must have read exactly 6 16-bit groups.
        else
        {
            if (counter == 6)
                return isWellFormedIPv4Address(addr+index+1, end-index-1);
            else
                return false;
        }
    }
    else
    {
       return false;
    }

    // 3. Scan hex sequence after '::'.
    int prevCount = counter;
    iIndex = scanHexSequence(addr, index, end, counter);
    if (iIndex == -1)
        return false;

    index=(XMLSize_t)iIndex;
    // If this is the end of the address then
    // we've got 128-bits of information.
    if (index == end)
    {
        return true;
    }

    // The address ends in an IPv4 address, or it is invalid.
    // scanHexSequence has already made sure that we have the right number of bits.
    XMLSize_t shiftCount = (counter > prevCount) ? index+1 : index;
    return isWellFormedIPv4Address(addr + shiftCount, end - shiftCount);
}

//
//  For use with isWellFormedIPv6Reference only.
//
int XMLUri::scanHexSequence (const XMLCh* const addr, XMLSize_t index, XMLSize_t end, int& counter)
{
    XMLCh testChar = chNull;
    int numDigits = 0;
    XMLSize_t start = index;

    // Trying to match the following productions:
    // hexseq = hex4 *( ":" hex4)
    // hex4   = 1*4HEXDIG
    for (; index < end; ++index)
    {
      	testChar = addr[index];
      	if (testChar == chColon)
      	{
      	    // IPv6 addresses are 128-bit, so there can be at most eight sections.
      	    if (numDigits > 0 && ++counter > 8)
      	    {
      	        return -1;
      	    }
      	    // This could be '::'.
      	    if (numDigits == 0 || ((index+1 < end) && addr[index+1] == chColon))
      	    {
      	        return (int)index;
      	    }
      	    numDigits = 0;
        }
        // This might be invalid or an IPv4address. If it's potentially an IPv4address,
        // backup to just after the last valid character that matches hexseq.
        else if (!XMLString::isHex(testChar))
        {
            if (testChar == chPeriod && numDigits < 4 && numDigits > 0 && counter <= 6)
            {
                int back = (int)index - numDigits - 1;
                return (back >= (int)start) ? back : (int)start;
            }
            return -1;
        }
        // There can be at most 4 hex digits per group.
        else if (++numDigits > 4)
        {
            return -1;
        }
    }
    return (numDigits > 0 && ++counter <= 8) ? (int)end : -1;
}

bool XMLUri::isGenericURI()
{
    return (getHost() != 0);
}


//
//  This method will take the broken out parts of the URI and build up the
//  full text. We don't do this unless someone asks us to, since its often
//  never required.
//
void XMLUri::buildFullText()
{
    // Calculate the worst case size of the buffer required
    XMLSize_t bufSize = XMLString::stringLen(fScheme) + 1
                           + XMLString::stringLen(fFragment) + 1
                           + XMLString::stringLen(fHost ? fHost : fRegAuth) + 2
                           + XMLString::stringLen(fPath)
                           + XMLString::stringLen(fQueryString) + 1
                           + XMLString::stringLen(fUserInfo) + 1
                           + 32;

    // Clean up the existing buffer and allocate another
    fMemoryManager->deallocate(fURIText);//delete [] fURIText;
    fURIText = (XMLCh*) fMemoryManager->allocate(bufSize * sizeof(XMLCh));//new XMLCh[bufSize];
    *fURIText = 0;

    XMLCh* outPtr = fURIText;
    if (fScheme != 0)
    {
        XMLString::catString(fURIText, getScheme());
        outPtr += XMLString::stringLen(fURIText);
        *outPtr++ = chColon;
    }

    // Authority
    if (fHost || fRegAuth)
    {
        *outPtr++ = chForwardSlash;
        *outPtr++ = chForwardSlash;

        // Server based authority.
        if (fHost)
        {
            if (fUserInfo)
            {
                XMLString::copyString(outPtr, fUserInfo);
                outPtr += XMLString::stringLen(fUserInfo);
                *outPtr++ = chAt;
            }

            XMLString::copyString(outPtr, fHost);
            outPtr += XMLString::stringLen(fHost);

            //
            //  If the port is -1, then we don't put it in. Else we need
            //  to because it was explicitly provided.
            //
            if (fPort != -1)
            {
                *outPtr++ = chColon;

                XMLCh tmpBuf[17];
                XMLString::binToText(fPort, tmpBuf, 16, 10, fMemoryManager);
                XMLString::copyString(outPtr, tmpBuf);
                outPtr += XMLString::stringLen(tmpBuf);
            }
        }
        // Registry based authority.
        else {
            XMLString::copyString(outPtr, fRegAuth);
            outPtr += XMLString::stringLen(fRegAuth);
        }
    }

    if (fPath)
    {
        XMLString::copyString(outPtr, fPath);
        outPtr += XMLString::stringLen(fPath);
    }

    if (fQueryString)
    {
        *outPtr++ = chQuestion;
        XMLString::copyString(outPtr, fQueryString);
        outPtr += XMLString::stringLen(fQueryString);
    }

    if (fFragment)
    {
        *outPtr++ = chPound;
        XMLString::copyString(outPtr, fFragment);
        outPtr += XMLString::stringLen(fFragment);
    }

    // Cap it off in case the last op was not a string copy
    *outPtr = 0;
}

// NOTE: no check for NULL value of uriStr (caller responsiblilty)
bool XMLUri::isValidURI(const XMLUri* const baseURI
                       , const XMLCh* const uriStr
                       , bool bAllowSpaces/*=false*/)
{
    // get a trimmed version of uriStr
    // uriStr will NO LONGER be used in this function.
    const XMLCh* trimmedUriSpec = uriStr;

    while (XMLChar1_0::isWhitespace(*trimmedUriSpec))
        trimmedUriSpec++;

    XMLSize_t trimmedUriSpecLen = XMLString::stringLen(trimmedUriSpec);

    while (trimmedUriSpecLen) {
        if (XMLChar1_0::isWhitespace(trimmedUriSpec[trimmedUriSpecLen-1]))
            trimmedUriSpecLen--;
        else
            break;
    }

    if (trimmedUriSpecLen == 0)
    {
        if (!baseURI)
            return false;
        else
            return true;
    }

    XMLSize_t index = 0;
    bool foundScheme = false;

    // Check for scheme, which must be before `/', '?' or '#'.
    int colonIdx = XMLString::indexOf(trimmedUriSpec, chColon);
    int slashIdx = XMLString::indexOf(trimmedUriSpec, chForwardSlash);
    int queryIdx = XMLString::indexOf(trimmedUriSpec, chQuestion);
    int fragmentIdx = XMLString::indexOf(trimmedUriSpec, chPound);

    if ((colonIdx <= 0) ||
        (colonIdx > slashIdx && slashIdx != -1) ||
        (colonIdx > queryIdx && queryIdx != -1) ||
        (colonIdx > fragmentIdx && fragmentIdx != -1))
    {
        // A standalone base is a valid URI according to spec
        if (colonIdx == 0 || (!baseURI && fragmentIdx != 0))
            return false;
    }
    else
    {
        if (!processScheme(trimmedUriSpec, index))
            return false;
        foundScheme = true;
        ++index;
    }

    // It's an error if we stop here
    if (index == trimmedUriSpecLen || (foundScheme && (trimmedUriSpec[index] == chPound)))
        return false;

	// two slashes means generic URI syntax, so we get the authority
    const XMLCh* authUriSpec = trimmedUriSpec +  index;
    if (((index+1) < trimmedUriSpecLen) &&
        XMLString::startsWith(authUriSpec, DOUBLE_SLASH))
    {
        index += 2;
        XMLSize_t startPos = index;

        // get authority - everything up to path, query or fragment
        XMLCh testChar;
        while (index < trimmedUriSpecLen)
        {
            testChar = trimmedUriSpec[index];
            if (testChar == chForwardSlash ||
                testChar == chQuestion     ||
                testChar == chPound         )
            {
                break;
            }

            index++;
        }

        // if we found authority, parse it out, otherwise we set the
        // host to empty string
        if (index > startPos)
        {
            if (!processAuthority(trimmedUriSpec + startPos, index - startPos))
                return false;
        }
    }

    // we need to check if index has exceed the lenght or not
    if (index < trimmedUriSpecLen)
    {
	    if (!processPath(trimmedUriSpec + index, trimmedUriSpecLen - index, foundScheme, bAllowSpaces))
            return false;
    }

    return true;
}

// NOTE: no check for NULL value of uriStr (caller responsiblilty)
// NOTE: this routine is the same as above, but it uses a flag to
//       indicate the existance of a baseURI rather than an XMLuri.
bool XMLUri::isValidURI(bool haveBaseURI, const XMLCh* const uriStr, bool bAllowSpaces/*=false*/)
{
    // get a trimmed version of uriStr
    // uriStr will NO LONGER be used in this function.
    const XMLCh* trimmedUriSpec = uriStr;

    while (XMLChar1_0::isWhitespace(*trimmedUriSpec))
        trimmedUriSpec++;

    XMLSize_t trimmedUriSpecLen = XMLString::stringLen(trimmedUriSpec);

    while (trimmedUriSpecLen) {
        if (XMLChar1_0::isWhitespace(trimmedUriSpec[trimmedUriSpecLen-1]))
            trimmedUriSpecLen--;
        else
            break;
    }

    if (trimmedUriSpecLen == 0)
    {
        if (!haveBaseURI)
            return false;
        return true;
    }

    XMLSize_t index = 0;
    bool foundScheme = false;

    // Check for scheme, which must be before `/', '?' or '#'.
    int colonIdx = XMLString::indexOf(trimmedUriSpec, chColon);
    int slashIdx = XMLString::indexOf(trimmedUriSpec, chForwardSlash);
    int queryIdx = XMLString::indexOf(trimmedUriSpec, chQuestion);
    int fragmentIdx = XMLString::indexOf(trimmedUriSpec, chPound);

    if ((colonIdx <= 0) ||
        (colonIdx > slashIdx && slashIdx != -1) ||
        (colonIdx > queryIdx && queryIdx != -1) ||
        (colonIdx > fragmentIdx && fragmentIdx != -1))
    {
        // A standalone base is a valid URI according to spec
        if (colonIdx == 0 || (!haveBaseURI && fragmentIdx != 0))
            return false;
    }
    else
    {
        if (!processScheme(trimmedUriSpec, index))
            return false;
        foundScheme = true;
        ++index;
    }

    // It's an error if we stop here
    if (index == trimmedUriSpecLen || (foundScheme && (trimmedUriSpec[index] == chPound)))
        return false;

	// two slashes means generic URI syntax, so we get the authority
    const XMLCh* authUriSpec = trimmedUriSpec +  index;
    if (((index+1) < trimmedUriSpecLen) &&
        XMLString::startsWith(authUriSpec, DOUBLE_SLASH))
    {
        index += 2;
        XMLSize_t startPos = index;

        // get authority - everything up to path, query or fragment
        XMLCh testChar;
        while (index < trimmedUriSpecLen)
        {
            testChar = trimmedUriSpec[index];
            if (testChar == chForwardSlash ||
                testChar == chQuestion     ||
                testChar == chPound         )
            {
                break;
            }

            index++;
        }

        // if we found authority, parse it out, otherwise we set the
        // host to empty string
        if (index > startPos)
        {
            if (!processAuthority(trimmedUriSpec + startPos, index - startPos))
                return false;
        }
    }

    // we need to check if index has exceed the length or not
    if (index < trimmedUriSpecLen)
    {
        if (!processPath(trimmedUriSpec + index, trimmedUriSpecLen - index, foundScheme, bAllowSpaces))
            return false;
    }

    return true;
}

bool XMLUri::isWellFormedAddress(const XMLCh* const addrString,
                                 const XMLSize_t addrStrLen)
{
    // Check that we have a non-zero length string.
    if (addrStrLen == 0)
        return false;

    // Check if the host is a valid IPv6reference.
    if (*addrString == chOpenSquare)
    {
        return isWellFormedIPv6Reference(addrString, addrStrLen);
    }

    //
    // Cannot start with a '.', '-', or end with a '-'.
    //
    if (*addrString == chPeriod ||
        *addrString == chDash ||
        addrString[addrStrLen-1] == chDash)
        return false;

    // rightmost domain label starting with digit indicates IP address
    // since top level domain label can only start with an alpha
    // see RFC 2396 Section 3.2.2

    int lastPeriodPos = XMLString::lastIndexOf(chPeriod, addrString, addrStrLen);

    // if the string ends with "."
    // get the second last "."
    if (XMLSize_t(lastPeriodPos + 1) == addrStrLen)
    {
        lastPeriodPos = XMLString::lastIndexOf(chPeriod, addrString, lastPeriodPos);

        if ( XMLString::isDigit(addrString[lastPeriodPos + 1]))
			return false;
    }

    if (XMLString::isDigit(addrString[lastPeriodPos + 1]))
    {
        return isWellFormedIPv4Address(addrString, addrStrLen);
    } // end of IPv4address
    else
    {
        //
        //  hostname      = *( domainlabel "." ) toplabel [ "." ]
        //  domainlabel   = alphanum | alphanum *( alphanum | "-" ) alphanum
        //  toplabel      = alpha | alpha *( alphanum | "-" ) alphanum

        // RFC 2396 states that hostnames take the form described in
        // RFC 1034 (Section 3) and RFC 1123 (Section 2.1). According
        // to RFC 1034, hostnames are limited to 255 characters.
        if (addrStrLen > 255) {
            return false;
        }

        unsigned int labelCharCount = 0;

        // domain labels can contain alphanumerics and '-"
        // but must start and end with an alphanumeric
        for (XMLSize_t i = 0; i < addrStrLen; i++)
        {
            if (addrString[i] == chPeriod)
            {
              if (((i > 0)  &&
                   (!XMLString::isAlphaNum(addrString[i-1]))) ||
                  ((i + 1 < addrStrLen) &&
                   (!XMLString::isAlphaNum(addrString[i+1])))  )
                {
                    return false;
                }
                labelCharCount = 0;
            }
            else if (!XMLString::isAlphaNum(addrString[i]) &&
                      addrString[i] != chDash)
            {
                return false;
            }
            // RFC 1034: Labels must be 63 characters or less.
            else if (++labelCharCount > 63) {
                return false;
            }
        } //for
    }

    return true;
}

bool XMLUri::processScheme(const XMLCh* const schemeStr, XMLSize_t& index)
{
    const XMLCh* tmpPtr = XMLString::findAny(schemeStr, SCHEME_SEPARATORS);

    if (tmpPtr) {
        index = tmpPtr - schemeStr;
        return isConformantSchemeName(schemeStr, index);
    }
    else {
        return false;
    }
}


bool XMLUri::isConformantSchemeName( const XMLCh* const scheme
                                   , const XMLSize_t schemeLen)
{
    if (!XMLString::isAlpha(*scheme))     // first: alpha
        return false;

    // second onwards: ( alpha | digit | "+" | "-" | "." )
    for (XMLSize_t i=1; i<schemeLen; i++)
    {
        if ( !XMLString::isAlphaNum(scheme[i]) &&
             (XMLString::indexOf(SCHEME_CHARACTERS, scheme[i]) == -1))
            return false;
    }

    return true;
}

bool XMLUri::processAuthority( const XMLCh* const authSpec
                             , const XMLSize_t authLen)
{
    int index = XMLString::indexOf(authSpec, chAt);
    XMLSize_t start = 0;

    // server = [ [ userinfo "@" ] hostport ]
    // userinfo is everything up @,
    const XMLCh* userinfo;
    int userInfoLen = 0;
    if ((index != -1) && (XMLSize_t(index) < authLen))
    {
        userinfo = authSpec;
        userInfoLen = index;
        start = index + 1;
    }
    else
    {
        userinfo = XMLUni::fgZeroLenString;
    }

    // hostport = host [ ":" port ]
    // host is everything up to ':', or up to
    // and including ']' if followed by ':'.
    //
    // Search for port boundary.
    const XMLCh* host;
    XMLSize_t hostLen = 0;
    if ((start < authLen) && (authSpec[start] == chOpenSquare))
    {
    	index = XMLString::indexOf(&(authSpec[start]), chCloseSquare);
    	if ((index != -1) && (XMLSize_t(index) < authLen))
    	{
            // skip the ']'
            index = ((start + index + 1) < authLen
              && authSpec[start + index + 1] == chColon) ? index+1 : -1;
    	}
    }
    else
    {
        index = XMLString::indexOf(&(authSpec[start]), chColon);
        if (index!=-1 && XMLSize_t(index) >= authLen)
            index = -1;
    }

    host = &(authSpec[start]);
    if (index != -1)
    {
        hostLen = index;
        start += index + 1;  // skip the :
    }
    else
    {
        hostLen = authLen - start;
        start = authLen;
    }

    // port is everything after ":"
    int port = -1;
    if ((hostLen) &&   // non empty host
        (index != -1)                    &&   // ":" found
        (start < authLen)                     )   // ":" is not the last
    {
        const XMLCh* portStr = &(authSpec[start]);
        if (*portStr)
        {
            port = 0;
            for (XMLSize_t i=0; i<(authLen - start); i++)
            {
                if (portStr[i] < chDigit_0 || portStr[i] > chDigit_9)
                {
                  // Assume this is a registry-based authority.
                  //
                  port = -1;
                  hostLen = 0;
                  host = XMLUni::fgZeroLenString;
                  userInfoLen = 0;
                  userinfo = XMLUni::fgZeroLenString;
                  break;
                }

                port = (port * 10) + (int) (portStr[i] - chDigit_0);
            }
        }
    }

    return isValidServerBasedAuthority(host, hostLen, port, userinfo, userInfoLen)
      || isValidRegistryBasedAuthority(authSpec, authLen);
}

bool XMLUri::processPath(const XMLCh* const pathStr,
                         const XMLSize_t pathStrLen,
                         const bool isSchemePresent,
                         const bool bAllowSpaces/*=false*/)
{
    if (pathStrLen != 0)
    {
        XMLSize_t index = 0;
        XMLCh testChar = chNull;
        bool isOpaque = (!isSchemePresent || *pathStr == chForwardSlash);

        // path - everything up to query string or fragment
        //
        // RFC 2732 only allows '[' and ']' to appear in the opaque part.
        while (index < pathStrLen)
        {
            testChar = pathStr[index];
            if (testChar == chQuestion || testChar == chPound)
                break;

            if (testChar == chPercent)
            {
                if (index+2 >= pathStrLen ||
                    !XMLString::isHex(pathStr[index+1]) ||
                    !XMLString::isHex(pathStr[index+2]))
                        return false;
            }
            else if (testChar==chSpace)
            {
                if(!bAllowSpaces)
                    return false;
            }
            else if (!isUnreservedCharacter(testChar) &&
                     ((isOpaque && !isPathCharacter(testChar)) ||
                      (!isOpaque && !isReservedCharacter(testChar))))
            {
                return false;
            }

            index++;
        }

        // query - starts with ? and up to fragment or end
        // fragment - starts with #
        bool isQuery = (testChar == chQuestion);
        if (isQuery || testChar == chPound)
        {
            index++;
            while (index < pathStrLen)
            {
                testChar = pathStr[index];
                if (testChar == chPound && isQuery) {
                    isQuery = false;
                    index++;
                    continue;
                }

                if (testChar == chPercent)
                {
                    if (index+2 >= pathStrLen ||
                        !XMLString::isHex(pathStr[index+1]) ||
                        !XMLString::isHex(pathStr[index+2]))
                        return false;
                }
                else if (testChar==chSpace)
                {
                    if(!bAllowSpaces)
                        return false;
                }
                else if (!isReservedOrUnreservedCharacter(testChar))
                {
                    return false;
                }
                index++;
            }
        }
    } //if (pathStrLen...)

    return true;
}

/***
 * [Bug7698]: filenames with embedded spaces in schemaLocation strings not handled properly
 *
 * This method is called when Scanner/TraverseSchema knows that the URI reference is
 * for local file.
 *
 ***/
void XMLUri::normalizeURI(const XMLCh*     const systemURI,
                                XMLBuffer&       normalizedURI)
{
    const XMLCh* pszSrc = systemURI;

    normalizedURI.reset();

    while (*pszSrc) {

        if ((*(pszSrc) == chPercent)
        &&  (*(pszSrc+1) == chDigit_2)
        &&  (*(pszSrc+2) == chDigit_0))
        {
            pszSrc += 3;
            normalizedURI.append(chSpace);
        }
        else
        {
            normalizedURI.append(*pszSrc);
            pszSrc++;
        }
    }
}

/***
 * Support for Serialization/De-serialization
 ***/

IMPL_XSERIALIZABLE_TOCREATE(XMLUri)

void XMLUri::serialize(XSerializeEngine& serEng)
{

    if (serEng.isStoring())
    {
        serEng<<fPort;
        serEng.writeString(fScheme);
        serEng.writeString(fUserInfo);
        serEng.writeString(fHost);
        serEng.writeString(fRegAuth);
        serEng.writeString(fPath);
        serEng.writeString(fQueryString);
        serEng.writeString(fFragment);
        serEng.writeString(fURIText);
    }
    else
    {
        serEng>>fPort;
        serEng.readString(fScheme);
        serEng.readString(fUserInfo);
        serEng.readString(fHost);
        serEng.readString(fRegAuth);
        serEng.readString(fPath);
        serEng.readString(fQueryString);
        serEng.readString(fFragment);
        serEng.readString(fURIText);
    }

}

XMLUri::XMLUri(MemoryManager* const manager)
: fPort(-1)
, fScheme(0)
, fUserInfo(0)
, fHost(0)
, fRegAuth(0)
, fPath(0)
, fQueryString(0)
, fFragment(0)
, fURIText(0)
, fMemoryManager(manager)
{
}

XERCES_CPP_NAMESPACE_END
