Files
bullet3/Extras/COLLADA_DOM/src/dae/daeURI.cpp

1223 lines
33 KiB
C++

/*
* Copyright 2006 Sony Computer Entertainment Inc.
*
* Licensed under the SCEA Shared Source License, Version 1.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://research.scea.com/scea_shared_source_license.html
*
* 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.
*/
#include <dae/daeURI.h>
#include <ctype.h>
#include <dae/daeDocument.h>
#include <dae/daeErrorHandler.h>
#ifdef _WIN32
#include <direct.h> // for getcwd (windows)
#else
#include <unistd.h> // for getcwd (linux)
#endif
daeString safeCreate(daeString src);
void safeDelete(daeString src);
daeString findCharacterReverse(daeString string, daeChar stopChar);
daeURIResolverPtrArray daeURIResolver::_KnownResolvers;
static daeURI ApplicationURI(1);
void
daeURI::setBaseURI(daeURI& uri)
{
ApplicationURI.reset();
ApplicationURI.setURI(uri.getURI());
}
daeURI*
daeURI::getBaseURI()
{
return &ApplicationURI;
}
void
daeURI::initialize()
{
// Initialize a URI to it's empty state, same as daeURI::reset but also clears out "container"
uriString = NULL;
originalURIString = NULL;
protocol = NULL;
authority = NULL;
filepath = NULL;
file = NULL;
id = NULL;
extension = NULL;
state = uri_empty;
element = NULL;
container = NULL;
external = false;
}
daeURI::~daeURI()
{
reset();
}
daeURI::daeURI()
{
initialize();
// reset(); // No need to call reset in the constructor, initialize does the same thing.
}
daeURI::daeURI(int dummy)
{
(void)dummy;
// This constructor builds a base URI from the current working directory
// This should work for windows or linux
// !!!GAC the buffers should probably be bigger
char buffer[1024], *b1;
strcpy(buffer, "file:///");
#ifdef NO_GETCWD
// The platform has no getcwd call, so leave the value as file:///
#else
#ifdef _WIN32
// Windows getcwd always returns a path beginning with a drive letter, so we add file:/// to the beginning
getcwd(&buffer[8],1024-8);
#else
// Linux getcwd always returns a path beginning with a slash, so we add file:// to the beginning
getcwd(&buffer[7],1024-7);
#endif
#endif
// If the path contains windows backslashes, flip them to forward slashes
for(b1 = buffer;*b1 != 0;b1++)
{
if(*b1 == '\\')
{
*b1 = '/';
}
}
// The path must end in a slash or the last part of it will be taken as a filename
if(*(b1-1) != '/')
{
*(b1++) = '/';
}
*b1 = '\0';
initialize();
setURI(buffer);
validate();
}
daeURI::daeURI(daeString uriString, daeBool nofrag)
{
initialize();
// !!!GAC this is inefficient as hell, but was the best way to isolate this functionality till the
// !!!GAC daeURI class can be written to support modifying URIs better (should be possible to make a URI,
// !!!GAC change any member and have getURI return the proper const string URI)
if(nofrag)
{
// Strip off the fragment part before constructing the URI
daeString temp = safeCreate(uriString);
daeChar* fragment = (daeChar*)findCharacterReverse(temp, '#');
if(fragment)
{
*fragment = 0;
}
setURI(temp);
safeDelete(temp);
}
else
{
// Generate the URI without changing the string
setURI(uriString);
}
if(nofrag)
validate();
}
daeURI::daeURI(daeURI& baseURI, daeString uriString)
{
initialize();
setURI(uriString);
validate(&baseURI);
}
daeURI::daeURI(daeURI& copyFrom)
{
initialize();
setURI(copyFrom.getOriginalURI());
element = copyFrom.element; // !!!GAC SetURI immediately clears element so we must do this after
state = copyFrom.state;
}
void
daeURI::copyFrom(daeURI& copyFrom)
{
setURI(copyFrom.getOriginalURI());
element = copyFrom.element; // !!!GAC SetURI immediately clears element so we must do this after
state = copyFrom.state;
// !!!GAC Should there be a call to validate in here?
}
void
daeURI::reset()
{
// Free up any allocated memory
if (uriString != NULL)
safeDelete(uriString);
if (originalURIString != NULL)
safeDelete(originalURIString);
if (protocol != NULL)
safeDelete(protocol);
if (authority != NULL)
safeDelete(authority);
if (filepath != NULL)
safeDelete(filepath);
if (file != NULL)
safeDelete(file);
if (id != NULL)
safeDelete(id);
if (extension != NULL)
safeDelete(extension);
// Set everything to the empty string
uriString = NULL;
originalURIString = NULL;
protocol = NULL;
authority = NULL;
filepath = NULL;
file = NULL;
id = NULL;
extension = NULL;
state = uri_empty;
element = NULL;
// container = NULL; // !!!GAC don't want to clear this, our container doesn't change once it's set
}
daeString
findCharacterReverse(daeString string, daeChar stopChar)
{
if (string == NULL)
return NULL;
daeString cur = string + strlen(string)-1;
while((cur >= string) && (*cur != stopChar))
cur--;
if ((cur >= string) && (*cur == stopChar))
return cur;
return NULL;
}
daeString
findCharacter(daeString string, daeChar stopChar)
{
if (string == NULL)
return NULL;
daeString end = string + strlen(string);
daeString cur = string;
while((*cur != stopChar) && (cur < end))
cur++;
if (*cur == stopChar)
return cur;
return NULL;
}
daeString safeCreate(daeString src)
{
if (src == NULL)
return NULL;
daeChar* ret = (daeChar*)daeMemorySystem::malloc("uri",strlen(src)+1);
if (ret == NULL)
return NULL;
strcpy(ret,src);
return ret;
}
void safeDelete(daeString src)
{
if(src != NULL)
{
daeMemorySystem::free("uri",(void*)src);
src = NULL;
}
}
void
daeURI::setURI(daeString _URIString)
{
originalURIString = safeCreate(_URIString);
internalSetURI(_URIString);
}
daeChar *validScheme(daeString uri_string)
{
// attempt to find a valid scheme in a string
// Failure to find a scheme returns a NULL, success returns the position of the terminating :
// First character must be alpha, fail if it's not
if(!isalpha(*uri_string))
return(NULL);
// Advance to the next character
uri_string++;
// Scheme must be at least two (valid) characters long, so go through this loop at least once
do
{
// If the character is NOT alpha, digit, +, - or . then this isn't a valid scheme
// Note this also fails if we encounter a null terminator before hitting the first :
if(!(isalpha(*uri_string) || isdigit(*uri_string) || *uri_string == '.' || *uri_string == '+' || *uri_string == '-'))
return(NULL);
uri_string++;
} while(*uri_string != ':' );
return((daeChar *)uri_string);
}
void
daeURI::internalSetURI(daeString _URIString)
{
daeChar* tmp;
// Store the originalURI so you can fix it post Reset
daeString oURI = originalURIString;
originalURIString = NULL;
// Reset everything
reset();
// Fix original URI String
originalURIString = oURI;
uriString = safeCreate(_URIString);
tmp = (daeChar*)daeMemorySystem::malloc("tmp",strlen(_URIString)+1);
if ((uriString == NULL)||(tmp == NULL))
return;
strcpy(tmp,uriString);
daeChar* curSrc = tmp;
#if 1
// Check for a scheme, two or more characters followed by a :
// daeChar* colon = (daeChar*)findCharacter(curSrc,':');
daeChar* colon = validScheme(curSrc);
// if(colon && (colon-tmp >= 2 ))
if(colon)
{
// Found a scheme, remember it (protocol should be named scheme)
*colon = '\0';
protocol = safeCreate(curSrc);
// Advance the current pointer past the colon
curSrc = colon+1;
}
// Check for a net path containing an authority, this would begin with //
if(curSrc[0] == '/' && curSrc[1] == '/')
{
// Advance past the double slash to where the authority would start, then find the next slash
curSrc = curSrc + 2;
daeChar* slash = (daeChar*)findCharacter(curSrc,'/');
// Temporarily remove that slash (avoids some branches)
if ( slash != NULL ) {
*slash = '\0';
}
// Save the text between the slashes as the authority
authority = safeCreate(curSrc);
// Put the slash back and advance the current pointer to it, this puts us at the start of the path
if (slash != NULL ) {
*slash = '/';
curSrc = slash;
}
}
// Search backwards from the end of the URI for the # which denotes the fragment (called ID here)
daeChar* idSymbol = (daeChar*)findCharacterReverse(curSrc,'#');
if (idSymbol != NULL)
{
// There was a fragment, isolate it by changing the # to a null
*idSymbol = '\0';
idSymbol++;
}
id = safeCreate(idSymbol);
// Search backwards for the last / in the path, everything after is the filename
daeChar* fname = (daeChar*)findCharacterReverse(curSrc,'/');
daeChar* dir;
if (fname == NULL)
{
// No / found, so the whole thing is the file name and there is no path
fname = curSrc;
dir = NULL;
}
else
{
// Found a slash, so the filename starts after it and dir starts at curSrc
fname++;
dir = curSrc;
}
file = safeCreate(fname);
// Pull the extension (if any) off of the filepath
daeString extStr = findCharacterReverse(fname,'.');
if (extStr != NULL)
extension = safeCreate(extStr+1);
// Now pull off the directory path if it exists by putting a zero at the beginning of fname, this insures filepath will end in a slash
if(fname != NULL) *fname = 0;
filepath = safeCreate(dir);
state = uri_loaded;
daeMemorySystem::free("tmp",tmp);
#else
daeBool isAbsolute;
daeChar* colon = (daeChar*)findCharacter(curSrc,':');
// IS ABSOLUTE REFERENCE
if (colon && (strlen(colon) > 2) && (colon[1] == '/') && (colon[2] == '/'))
{
*colon = '\0';
protocol = safeCreate(curSrc);
curSrc = colon+3;
daeString hosttmp = curSrc;
daeChar* slash = (daeChar*)findCharacter(curSrc,'/');
if (slash != NULL)
{
*slash = '\0';
authority = safeCreate(hosttmp);
curSrc = slash+1;
}
isAbsolute = true;
}
else {
protocol = NULL;
isAbsolute = false;
}
// Look for the # which denotes the fragment (called ID here)
daeChar* idSymbol = (daeChar*)findCharacterReverse(curSrc,'#');
if (idSymbol != NULL)
{
// There was a fragment, isolate it by changing the # to a null
*idSymbol = '\0';
idSymbol++;
}
daeChar* dir = NULL;
daeChar* fname = (daeChar*)findCharacterReverse(curSrc,'/');
if (fname == NULL)
fname = curSrc;
else {
*fname = '\0';
fname++;
dir = curSrc;
}
filepath = safeCreate(dir);
int dirLen = (int)strlen(filepath);
// append a '/'
if ((filepath != NULL) && (dirLen > 0) &&
(filepath[dirLen-1] != '/')) {
daeMemorySystem::free("uri",(void*)filepath);
filepath = (daeString)daeMemorySystem::malloc("uri", dirLen+2);
strcpy((daeChar*)filepath,dir);
*((daeChar*)filepath+dirLen) = '/';
*((daeChar*)filepath+dirLen+1) = '\0';
}
file = safeCreate(fname);
id = safeCreate(idSymbol);
daeString extStr = findCharacterReverse(fname,'.');
if (extStr != NULL)
extension = safeCreate(extStr+1);
state = uri_loaded;
daeMemorySystem::free("tmp",tmp);
#endif
}
void
daeURI::print()
{
fprintf(stderr,"URI(%s)\n",uriString);
fprintf(stderr,"protocol = %s\n",protocol);
fprintf(stderr,"authority = %s\n",authority);
fprintf(stderr,"path = %s\n",filepath);
fprintf(stderr,"file = %s\n",file);
fprintf(stderr,"id = %s\n",id);
fprintf(stderr,"URI without base = %s\n",originalURIString);
fflush(stderr);
}
const char* protoString = "://";
const char* hostString = "/";
const char* queryString = "#";
const char* filepathString = "/";
daeString
daeURI::getURI() const
{
return uriString;
}
daeString
daeURI::getOriginalURI() const
{
return originalURIString;
}
void
daeURI::validate(daeURI* baseURI)
{
// If no base URI was supplied, get the application base and use it
if (baseURI == NULL)
{
if ( container == NULL || (baseURI = container->getDocumentURI()) == NULL ) {
baseURI = getBaseURI();
}
if (this == baseURI ) {
return;
}
}
#if 1
// This is rewritten according to the updated rfc 3986
if((protocol != NULL) && (strlen(protocol)>0)) // if defined(R.scheme) then
{
// Everything stays the same except path which we normalize
// T.scheme = R.scheme;
// T.authority = R.authority;
// T.path = remove_dot_segments(R.path);
// T.query = R.query;
normalizeURIPath((char *)filepath);
if ( (file == NULL) || (strlen(file)==0) ) {
//safeDelete(file);
//safeDelete(extension);
//file = safeCreate(baseURI->file);
//extension = safeCreate(baseURI->extension);
}
}
else
{
if((authority != NULL) && (strlen(authority)>0)) // if defined(R.authority) then
{
// Authority and query stay the same, path is normalized
// T.authority = R.authority;
// T.path = remove_dot_segments(R.path);
// T.query = R.query;
normalizeURIPath((char *)filepath);
if ( (file == NULL) || (strlen(file)==0) ) {
//safeDelete(file);
//safeDelete(extension);
//file = safeCreate(baseURI->file);
//extension = safeCreate(baseURI->extension);
}
}
else
{
if(((filepath == NULL) || (strlen(filepath)==0)) && ((file == NULL) || (strlen(file)==0))) // if (R.path == "") then
{
// T.path = Base.path;
safeDelete(filepath);
safeDelete(file);
safeDelete(extension);
filepath = safeCreate(baseURI->filepath);
file = safeCreate(baseURI->file);
extension = safeCreate(baseURI->extension);
// We don't handle querys, but if we did
//if defined(R.query) then
// T.query = R.query;
//else
// T.query = Base.query;
//endif;
}
else
{
if((filepath != NULL) && (*filepath == '/')) //if (R.path starts-with "/") then
{
// T.path = remove_dot_segments(R.path);
normalizeURIPath((char *)filepath);
}
else
{
//T.path = merge(Base.path, R.path);
daeChar* newPath;
if((strlen(baseURI->authority) != 0) && (strlen(baseURI->filepath)==0) && (strlen(baseURI->file) == 0)) //authority defined, path empty
{
newPath = (daeChar*)daeMemorySystem::malloc("uri", strlen(filepath) + 2);
*newPath = '/';
*(newPath+1) = 0;
strcat(newPath,filepath);
}
else
{
size_t l = 0;
if ( filepath != NULL ) {
l = strlen(filepath);
}
newPath = (daeChar*)daeMemorySystem::malloc("uri", strlen(baseURI->filepath) + l + 1);
*newPath = 0;
strcat(newPath,baseURI->filepath);
if ( filepath != NULL ) {
strcat(newPath,filepath);
}
else {
strcat(newPath,"");
}
}
//T.path = remove_dot_segments(T.path);
normalizeURIPath(newPath);
safeDelete(filepath);
filepath = newPath;
}
// T.query = R.query;
}
// T.authority = Base.authority;
safeDelete(authority);
authority = safeCreate(baseURI->authority);
}
// T.scheme = Base.scheme;
safeDelete(protocol);
protocol = safeCreate(baseURI->protocol);
}
// T.fragment = R.fragment;
// Now for the purpose of maintaining the class members, we reassemble all this into a string version of the URI
size_t len = 0;
if ( protocol != NULL ) {
len += strlen(protocol);
}
if ( authority != NULL ) {
len += strlen(authority);
}
if ( filepath != NULL ) {
len += strlen(filepath);
}
if ( file != NULL ) {
len += strlen(file);
}
if ( queryString != NULL ) {
len += strlen(queryString);
}
if ( id != NULL ) {
len += strlen(id);
}
daeChar* newURI = (daeChar*)
daeMemorySystem::malloc("uri", len + 4 );
*newURI = 0;
if(protocol != NULL && *protocol != 0)
{
strcat(newURI, protocol);
strcat(newURI, ":");
}
strcat(newURI, "//");
if(authority != NULL && *authority != 0)
{
strcat(newURI, authority);
}
if(filepath != NULL)
strcat(newURI, filepath);
if(file != NULL)
strcat(newURI, file);
if(id != NULL && *id != 0)
{
strcat(newURI,"#");
strcat(newURI,id);
}
// This becomes the new uriString, no need to call internalSetUri because all the class members are up to date
safeDelete(uriString);
uriString = newURI;
state = uri_pending;
if ( container != NULL ) {
daeString fp = container->getDocumentURI()->getFilepath();
daeString f = container->getDocumentURI()->getFile();
if ( strcmp( fp, filepath ) != 0 || strcmp( f, file ) != 0 ) {
//external reference
container->getDocument()->addExternalReference( *this );
external = true;
}
else if ( external ) {
//was external but now isn't
container->getDocument()->removeExternalReference( *this );
external = false;
}
}
#else
// RFC 2396 part 5.2 step 3, if the scheme (protocol here) is defined we are done, otherwise inherit the base URI's protocol
if ((protocol == NULL)||(strlen(protocol)==0))
{
// Make a copy of the base's protocol, not a reference to it
safeDelete(protocol);
protocol = safeCreate(baseURI->protocol);
// part 5.2 step 4, if the authority is defined we skip to step 7, otherwise inherit the base URI's authority
if((authority == NULL) || (strlen(authority)== 0))
{
// Make a copy of the base's authority, not a reference to it
safeDelete(authority);
authority = safeCreate(baseURI->authority);
// part 5.2 step 5 if the path part (filepath here) begins with a slash we skip to step 7, otherwise resolve the relative path against the base
if((filepath == NULL) || (*filepath != '/'))
{
// part 5.2 step 2, if scheme, authority and path are all empty, this is a reference to the current doc
// COLLADA DOM wants this to resolve to the URI of the document + the fragment
// To make this happen we have the URI inherit the file part of the base (if present) and the path
if( ((filepath == NULL) || (strlen(filepath)==0)) && // filepath empty
((file == NULL) || (strlen(file)==0)) && // file empty
((baseURI->file != NULL) && (strlen(baseURI->file) > 0))) // baseURI file NOT empty
{
// inherit the base's filename
safeDelete(file);
file = safeCreate(baseURI->file);
}
// part 5.2 step 6, resolving a relative path reference
// note that in this implementation the filepath does not contain the last segment (the filename.ext)
// Allocate enough memory to hold the merged path
daeChar* newPath = (daeChar*)daeMemorySystem::malloc(
"uri",
strlen(baseURI->filepath) +
strlen(filepath) + 1);
*newPath = 0;
// part 5.2 step 6(a) copy the baseURI filepath to the buffer
strcat(newPath,baseURI->filepath);
// part 5.2 step 6(b) copy this URI's filepath to the buffer
if(*filepath != 0)
{
strcat(newPath,filepath);
}
// part 5.2 step 6(c-g) normalize the new path
normalizeURIPath(newPath);
// part 5.2 step 6(h) replace the old filepath with the new path
safeDelete(filepath);
filepath = newPath;
}
}
// part 5.2 step 7 assemble the final complete URI
// Allocate memory to hold the assembled version of the URI
daeChar* newURI = (daeChar*)
daeMemorySystem::malloc(
"uri",
strlen(protocol) + // really scheme
1 + // space for ":"
strlen(authority) + // really authority
2 + // space for "//"
strlen(filepath) + // path without the filename
strlen(file) + // filename part of the path
strlen(queryString) + // "#"
strlen(id) + // really fragment
1); // terminating zero
*newURI = 0;
if(protocol != NULL && *protocol != 0)
{
strcat(newURI, protocol);
strcat(newURI, ":");
}
if(authority != NULL && *authority != 0)
{
strcat(newURI, authority);
}
strcat(newURI, "//");
if(filepath != NULL)
strcat(newURI, filepath);
if(file != NULL)
strcat(newURI, file);
if(id != NULL && *id != 0)
{
strcat(newURI,"#");
strcat(newURI,id);
}
// Reset the URI to the new one
// Just setting the uriString would probably be enough
internalSetURI(newURI);
daeMemorySystem::free("uri",newURI);
}
state = uri_pending;
#endif
#if 0
// If there is no protocol, assume this is a relative URI that needs to be resolved against the base
if ((protocol == NULL)||(strlen(protocol)==0))
{
// !!!GAC if the base URI contains a file and this uri does not, copy it over (why??)
if (((baseURI->file != NULL) && (strlen(baseURI->file)>0)) &&
((file == NULL)||(strlen(file)==0)))
{
if (file != NULL)
safeDelete(file);
file = safeCreate(baseURI->file);
}
// !!!GAC this is a quick and dirty attempt to get the relative URIs properly resolved and the internal
// !!!GAC paths normalized. This code should be rewritten when there's time, I wanted to get this up quick
// !!!GAC so we could test the rest of the system to make sure handing it "correct" URIs doesn't break things.
// Start by allocating memory and putting together just the path component
daeChar* newPath = (daeChar*)
daeMemorySystem::malloc(
"tmp",
strlen(baseURI->filepath) +
strlen(filepathString) +
strlen(filepath) +
strlen(filepathString) +
strlen(file)+1);
*newPath = 0;
strcat(newPath,baseURI->filepath);
strcat(newPath,filepathString); // !!!GAC this may put in an extra / but if it does the normalization will fix it
if(*filepath != 0)
{
strcat(newPath,filepath); // !!!GAC only do this if filepath is not empty
strcat(newPath,filepathString); // !!!GAC only do this if filepath is not empty
}
strcat(newPath,file);
// Normalize the path according to RFC 2396 (removes extra /, .., . and so on)
normalizeURIPath(newPath);
// !!!GAC Allocate memory for the complete URI and assemble it
daeChar* newURI = (daeChar*)
daeMemorySystem::malloc(
"tmp",
strlen(baseURI->protocol) +
strlen(protoString) +
strlen(authority) +
strlen(hostString) +
strlen(newPath) +
strlen(queryString) +
strlen(id)+1);
*newURI = 0;
strcat(newURI,baseURI->protocol); // Should be called "scheme" from RFC 2396
strcat(newURI,protoString);
strcat(newURI,authority); // Should be called "authority"
// strcat(newURI,hostString); // !!!GAC not necessary, path always begins with a /
strcat(newURI,newPath);
if(strlen(id) != 0)
{
// !!!GAC don't append the #id unless it's needed (bug 297)
strcat(newURI,queryString);
strcat(newURI,id);
}
internalSetURI(newURI);
daeMemorySystem::free("tmp",newPath);
daeMemorySystem::free("tmp",newURI);
}
state = uri_pending;
#endif
}
void
daeURI::resolveElement(daeString typeNameHint)
{
if (state == uri_empty)
return;
if (state == uri_loaded) {
if (container != NULL)
validate(container->getDocumentURI());
else
validate();
}
daeURIResolver::attemptResolveElement(*((daeURI*)this), typeNameHint );
}
void
daeURI::resolveURI()
{
// !!!GAC bug 486, there used to be code here that just returned if state was uri_empty or uri_resolve_local this has been removed.
if (element != NULL)
{
// !!!GAC bug 388 and 421, need to add a fragment (#) before the ID (was setURI(element->getID()))
if(element->getID() == NULL || element->getID()[0] == 0)
{
// We can't resolve to an element that has no ID, so if the ID is blank, fail and return
state = uri_failed_invalid_reference;
return;
}
daeChar* newID = (daeChar*)daeMemorySystem::malloc("tmp",strlen(element->getID())+2);
strcpy(newID,"#");
strcat(newID,element->getID());
// !!!GAC We have to save element and container because setURI clears them for some reason
daeElementRef elementSave = element;
setURI(newID);
// !!!GAC Hopefully, calling validate like below is the right thing to do to get the full URI resolution
element = elementSave;
validate(element->getDocumentURI());
element = elementSave;
daeMemorySystem::free("tmp",newID);
state = uri_success; // !!!GAC The element pointer and the URI should agree now, so set success
}
else
{
state = uri_failed_invalid_reference;
}
}
daeBool daeURI::getPath(daeChar *dest, daeInt size)
{
if( file == NULL )
{
//printf("****** %s : %s\n", uriString, originalURIString);
return false;
}
*dest = 0;
int lenPath = 0;
if ( filepath != NULL ) lenPath = (int)strlen(filepath);
int lenFile = (int)strlen(file);
int len = lenPath + lenFile;
if (len < size)
{
if ( filepath != NULL ) {
strcpy(dest,filepath);
}
strcat(dest,file);
return true;
}
else
return false;
}
void
daeURIResolver::attemptResolveElement(daeURI& uri, daeString typeNameHint)
{
int i;
int cnt =(int) _KnownResolvers.getCount();
for(i=0;i<cnt;i++)
if ((_KnownResolvers[i]->isProtocolSupported(uri.getProtocol()))&&
((uri.getFile() == NULL) ||
(uri.getFile()[0] == '\0') ||
(_KnownResolvers[i]->isExtensionSupported(uri.getExtension()))) &&
(_KnownResolvers[i]->resolveElement(uri, typeNameHint)))
return;
}
void
daeURIResolver::attemptResolveURI(daeURI& uri)
{
int i,cnt = (int)_KnownResolvers.getCount();
daeBool foundProtocol = false;
for(i=0;i<cnt;i++)
if (_KnownResolvers[i]->isProtocolSupported(uri.getProtocol())) {
foundProtocol = true;
if (_KnownResolvers[i]->resolveURI(uri))
return;
}
#if defined(_DEBUG) && defined(WIN32)
char msg[256];
sprintf(msg,"daeURIResolver::attemptResolveURI(%s) - failed\n", uri.getURI());
daeErrorHandler::get()->handleWarning( msg );
#endif
if (!foundProtocol) {
uri.setState(daeURI::uri_failed_unsupported_protocol);
#if defined(_DEBUG) && defined(WIN32)
char msg[128];
sprintf(msg,"**protocol '%s' is not supported**\n",uri.getProtocol());
daeErrorHandler::get()->handleWarning( msg );
#endif
}
else {
#if defined(_DEBUG) && defined(WIN32)
char msg[256];
sprintf(msg,"**file(%s/%s) or id(%s) failed to resolve\n",
uri.getFilepath(),uri.getFile(),uri.getID());
daeErrorHandler::get()->handleWarning( msg );
#endif
}
}
daeBool daeURIResolver::_loadExternalDocuments = true;
daeURIResolver::daeURIResolver()
{
_KnownResolvers.append((daeURIResolver*)this);
}
daeURIResolver::~daeURIResolver()
{
_KnownResolvers.remove((daeURIResolver*)this);
}
// This code is loosely based on the RFC 2396 normalization code from
// libXML. Specifically it does the RFC steps 6.c->6.g from section 5.2
// The path is modified in place, there is no error return.
void daeURI::normalizeURIPath(char *path)
{
char
*cur, // location we are currently processing
*out; // Everything from this back we are done with
// Return if the path pointer is null
if (path == NULL) return;
// Skip any initial / characters to get us to the start of the first segment
for(cur=path; *cur == '/'; cur++);
// Return if we hit the end of the string
if (*cur == 0) return;
// Keep everything we've seen so far.
out = cur;
// Analyze each segment in sequence for cases (c) and (d).
while (*cur != 0)
{
// (c) All occurrences of "./", where "." is a complete path segment, are removed from the buffer string.
if ((*cur == '.') && (*(cur+1) == '/'))
{
cur += 2;
// If there were multiple slashes, skip them too
while (*cur == '/') cur++;
continue;
}
// (d) If the buffer string ends with "." as a complete path segment, that "." is removed.
if ((*cur == '.') && (*(cur+1) == 0))
break;
// If we passed the above tests copy the segment to the output side
while (*cur != '/' && *cur != 0)
{
*(out++) = *(cur++);
}
if(*cur != 0)
{
// Skip any occurrances of // at the end of the segment
while ((*cur == '/') && (*(cur+1) == '/')) cur++;
// Bring the last character in the segment (/ or a null terminator) into the output
*(out++) = *(cur++);
}
}
*out = 0;
// Restart at the beginning of the first segment for the next part
for(cur=path; *cur == '/'; cur++);
if (*cur == 0) return;
// Analyze each segment in sequence for cases (e) and (f).
//
// e) All occurrences of "<segment>/../", where <segment> is a
// complete path segment not equal to "..", are removed from the
// buffer string. Removal of these path segments is performed
// iteratively, removing the leftmost matching pattern on each
// iteration, until no matching pattern remains.
//
// f) If the buffer string ends with "<segment>/..", where <segment>
// is a complete path segment not equal to "..", that
// "<segment>/.." is removed.
//
// To satisfy the "iterative" clause in (e), we need to collapse the
// string every time we find something that needs to be removed. Thus,
// we don't need to keep two pointers into the string: we only need a
// "current position" pointer.
//
bool trew = true;
while (trew)
{
char *segp, *tmp;
// At the beginning of each iteration of this loop, "cur" points to
// the first character of the segment we want to examine.
// Find the end of the current segment.
for(segp = cur;(*segp != '/') && (*segp != 0); ++segp);
// If this is the last segment, we're done (we need at least two
// segments to meet the criteria for the (e) and (f) cases).
if (*segp == 0)
break;
// If the first segment is "..", or if the next segment _isn't_ "..",
// keep this segment and try the next one.
++segp;
if (((*cur == '.') && (cur[1] == '.') && (segp == cur+3))
|| ((*segp != '.') || (segp[1] != '.')
|| ((segp[2] != '/') && (segp[2] != 0))))
{
cur = segp;
continue;
}
// If we get here, remove this segment and the next one and back up
// to the previous segment (if there is one), to implement the
// "iteratively" clause. It's pretty much impossible to back up
// while maintaining two pointers into the buffer, so just compact
// the whole buffer now.
// If this is the end of the buffer, we're done.
if (segp[2] == 0)
{
*cur = 0;
break;
}
// Strings overlap during this copy, but not in a bad way, just avoid using strcpy
tmp = cur;
segp += 3;
while ((*(tmp++) = *(segp++)) != 0);
// If there are no previous segments, then keep going from here.
segp = cur;
while ((segp > path) && (*(--segp) == '/'));
if (segp == path)
continue;
// "segp" is pointing to the end of a previous segment; find it's
// start. We need to back up to the previous segment and start
// over with that to handle things like "foo/bar/../..". If we
// don't do this, then on the first pass we'll remove the "bar/..",
// but be pointing at the second ".." so we won't realize we can also
// remove the "foo/..".
for(cur = segp;(cur > path) && (*(cur-1) != '/'); cur--);
}
*out = 0;
// g) If the resulting buffer string still begins with one or more
// complete path segments of "..", then the reference is
// considered to be in error. Implementations may handle this
// error by retaining these components in the resolved path (i.e.,
// treating them as part of the final URI), by removing them from
// the resolved path (i.e., discarding relative levels above the
// root), or by avoiding traversal of the reference.
//
// We discard them from the final path.
if (*path == '/')
{
for(cur=path; (*cur == '/') && (cur[1] == '.') && (cur[2] == '.') && ((cur[3] == '/') || (cur[3] == 0)); cur += 3);
if (cur != path)
{
for(out=path; *cur != 0; *(out++) = *(cur++));
*out = 0;
}
}
return;
}
// This function will take a resolved URI and create a version of it that is relative to
// another existing URI. The new URI is stored in the "originalURI"
int daeURI::makeRelativeTo(daeURI* relativeToURI)
{
if( getState() == uri_empty || relativeToURI->getState() == uri_empty )
return(DAE_ERR_INVALID_CALL);
if( getState() == uri_loaded )
{
if (container != NULL)
validate(container->getDocumentURI());
else
validate();
}
if( relativeToURI->getState() == uri_loaded )
{
if (relativeToURI->getContainer() != NULL)
relativeToURI->validate(relativeToURI->getContainer()->getDocumentURI());
else
relativeToURI->validate();
}
// Can only do this function if both URIs have the same scheme and authority
if((strcmp(getProtocol(), relativeToURI->getProtocol()) != 0) || (strcmp(getAuthority(), relativeToURI->getAuthority()) != 0))
return(DAE_ERR_INVALID_CALL); // !!!GAC Need to assign a real error code to this
// advance till we find a segment that doesn't match
const char *this_filepath = getFilepath();
const char *relativeTo_filepath = relativeToURI->getFilepath();
const char *this_slash = this_filepath;
const char *relativeTo_slash = relativeTo_filepath;
while(*this_filepath == *relativeTo_filepath)
{
if(*this_filepath == '/')
{
this_slash = this_filepath;
relativeTo_slash = relativeTo_filepath;
}
this_filepath++;
relativeTo_filepath++;
}
// Decide how many ../ segments are needed (Filepath should always end in a /)
int segment_count = 0;
relativeTo_slash++;
while(*relativeTo_slash != 0)
{
if(*relativeTo_slash == '/')
segment_count ++;
relativeTo_slash++;
}
this_slash++;
// Delete old URI string
safeDelete(originalURIString);
// Allocate memory for a new "originalURI" and free the old one
char *newRelativeURI;
if ( getID() == NULL )
{
newRelativeURI = (char*) daeMemorySystem::malloc("uri",strlen(this_slash)+ strlen(file)+(segment_count*3)+1);
}
else
{
newRelativeURI = (char*) daeMemorySystem::malloc("uri",strlen(this_slash)+ strlen(file)+(segment_count*3)+strlen(id)+2);
}
char *temp = newRelativeURI;
for(int i = 0; i < segment_count; i++)
{
strcpy(temp,"../");
temp += 3;
}
strcpy(temp,this_slash);
strcat(temp,file);
if(id!=NULL && strlen(getID()) != 0)
{
strcat(temp,"#");
strcat(temp,getID());
}
originalURIString = newRelativeURI;
return(DAE_OK);
}