Conversion from an absolute URL to relative

  • Hi,

    Are there any method in Cocoa to convert an absolute URL to a relative URL
    with a given base URL?

    Satoshi,
    -----------------------------------------------------
    Satoshi Matsumoto <satoshi...>
    816-5 Odake, Odawara, Kanagawa, Japan 256-0802
    _______________________________________________
    cocoa-dev mailing list | <cocoa-dev...>
    Help/Unsubscribe/Archives: http://www.lists.apple.com/mailman/listinfo/cocoa-dev
    Do not post admin requests to the list. They will be ignored.
  • Hello...

    It would probably be easiest to work with the urls as NSStrings,
    using the NSURL methods that accept/return strings.

    Then, you could do something like this:

    NSString *baseURLString = @"http://www.base.com/url/";
    NSString *absoluteURLString =
    @"http://www.base.com/url/plus/other/stuff.html";

    NSString *relativeURLString;

    NSRange prefix = [absoluteURLString rangeOfString:baseURLString];

    if (prefix.location == 0)
    {
      relativeURLString = [absoluteURLString
    substringFromIndex:prefix.length];
    }
    else
    {
      /* base url was not found, or not at beginning of
    absolute url */
      relativeURLString = nil;
    }

    For the example URL strings above, the code should result in
    relativeURLString containing the string @"plus/other/stuff.html",
    which you could put back into an NSURL if you need to using it and
    baseURLString.

    Hope that helps,

    Louis

    > Hi,
    >
    > Are there any method in Cocoa to convert an absolute URL to a relative URL
    > with a given base URL?
    >
    > Satoshi,
    > -----------------------------------------------------
    > Satoshi Matsumoto <satoshi...>
    > 816-5 Odake, Odawara, Kanagawa, Japan 256-0802
    _______________________________________________
    cocoa-dev mailing list | <cocoa-dev...>
    Help/Unsubscribe/Archives: http://www.lists.apple.com/mailman/listinfo/cocoa-dev
    Do not post admin requests to the list. They will be ignored.
  • Dear Louis,

    Thank you for your kind information. It's  very helpful.

    But in following case, where absolute URLs are outside of base URL directory

    baseURLString = @"http://www.base.com/url/"
    absoluteURLString = @"http://www.base.com/img/image.jepg"

    The result relativeURL should be @"../img/image.jepg"

    I am going to write a method that can handle above cases...

    Satoshi

    on 04.3.10 4:24 PM, Louis C. Sacha at <subsLCS...> wrote:

    > Hello...
    >
    > It would probably be easiest to work with the urls as NSStrings,
    > using the NSURL methods that accept/return strings.
    >
    > Then, you could do something like this:
    >
    > NSString *baseURLString = @"http://www.base.com/url/";
    > NSString *absoluteURLString =
    > @"http://www.base.com/url/plus/other/stuff.html";
    >
    >
    > NSString *relativeURLString;
    >
    > NSRange prefix = [absoluteURLString rangeOfString:baseURLString];
    >
    > if (prefix.location == 0)
    > {
    > relativeURLString = [absoluteURLString
    > substringFromIndex:prefix.length];
    > }
    > else
    > {

    > /* base url was not found, or not at beginning of
    > absolute url */
    > relativeURLString = nil;
    > }
    >
    >
    > For the example URL strings above, the code should result in
    > relativeURLString containing the string @"plus/other/stuff.html",
    > which you could put back into an NSURL if you need to using it and
    > baseURLString.
    >
    >
    > Hope that helps,
    >
    > Louis
    >
    >> Hi,
    >>
    >> Are there any method in Cocoa to convert an absolute URL to a relative URL
    >> with a given base URL?
    >>
    >> Satoshi,
    >> -----------------------------------------------------
    >> Satoshi Matsumoto <satoshi...>
    >> 816-5 Odake, Odawara, Kanagawa, Japan 256-0802
    > _______________________________________________
    > cocoa-dev mailing list | <cocoa-dev...>
    > Help/Unsubscribe/Archives:
    > http://www.lists.apple.com/mailman/listinfo/cocoa-dev
    > Do not post admin requests to the list. They will be ignored.
    _______________________________________________
    cocoa-dev mailing list | <cocoa-dev...>
    Help/Unsubscribe/Archives: http://www.lists.apple.com/mailman/listinfo/cocoa-dev
    Do not post admin requests to the list. They will be ignored.
  • Hello...

    In order to work correctly for cases like

    > baseURLString = @" http://www.base.com/url/ "
    > absoluteURLString = @" http://www.base.com/img/image.jepg "
    >
    > The result relativeURL should be @"../img/image.jepg"

    and others (where the last item in the base url is not a directory,
    etc...), the code would have to be a bit more complex.

    /* typed in mail, not tested, etc... */

    - (NSString *)relativeURLStringForAbsoluteURLString:(NSString
    *)absoluteURLString withBaseURLString:(NSString *)baseURLString
    {
    if ((baseURLString)&&(![baseURLString isEqualToString:@""])
    {
      /* make sure that baseURLString ends in a directory */
      if (![[baseURLString
    substringFromIndex:([baseURLString length] - 1)]
    isEqualToString:@"/"])
      {
      /* note: doing this doesn't change the actual
    string that was passed in, it changes the string that the method's
    baseURLString pointer points to, and has no effect outside of this
    method's scope */
      baseURLString = [NSString
    stringWithFormat:@"%@/",[baseURLString
    stringByDeletingLastPathComponent]];
      }

      NSString *commonString = [absoluteURLString
    commonPrefixWithString:baseURLString options:NSLiteralSearch];

      if ((commonString)&&(![commonString isEqualToString:@""]))
      {
      /* make sure that the common url path ends in
    a directory */
      if (![[commonString
    substringFromIndex:([commonString length] - 1)] isEqualToString:@"/"])
      {
        commonString = [NSString
    stringWithFormat:@"%@/",[commonString
    stringByDeletingLastPathComponent]];
      }

      NSString *remainderString;
      if ([commonString
    isEqualToString:absoluteURLString]) {remainderString = @"";}
      else
      {
        NSRange commonRange =
    [absoluteURLString rangeOfString:commonString];
        remainderString = [absoluteURLString
    substringFromIndex:commonRange.length];
      }

      /* add ../ to back up to parent directory as
    many times as required */
      while (![commonString isEqualToString:baseURLString])
      {
        baseURLString = [NSString
    stringWithFormat:@"%@/",[baseURLString
    stringByDeletingLastPathComponent]];
        remainderString = [NSString
    stringWithFormat:@"../%@",remainderString]];
      }

      return remainderString;
      }

    }

    /* no baseURLString, empty base url, or base url does not match */
    return absoluteURLString;
    }

    Please remember that this was typed up in mail and I can't test it at
    the moment, so there may be errors either in typing or in logic. It
    should at least give you a general idea of what you could do by
    working with the strings to get the relative url, even if it doesn't
    work perfectly (or at all).

    Hope that helps,

    Louis
    _______________________________________________
    cocoa-dev mailing list | <cocoa-dev...>
    Help/Unsubscribe/Archives: http://www.lists.apple.com/mailman/listinfo/cocoa-dev
    Do not post admin requests to the list. They will be ignored.
  • Hello...

    I knew I should have waited until I could test that code before posting it :)

    It turns out that using the NSString method
    stringByDeletingLastPathComponent on a url won't actually work, since
    it causes the second slash after the colon in the url to be lost. For
    example http://www.somesite.com/index.html turns into
    http:/www.somesite.com and so the common prefix of the clipped
    baseURLString and the absoluteURLString would end up being http:/
    since the second slash was still present in the absoluteURLString but
    not the baseURLString. That doesn't work too well when it's used to
    figure out the relative url...

    Also, if the urls for the absolute and base urls are from different
    sites, the relative url returned should be the absolute url.

    So here's an updated version, now that I've had a chance to test it
    and make sure it basically works (although there might still be other
    bugs floating around).

    - (NSString *)relativeURLStringForAbsoluteURLString:(NSString
    *)absoluteURLString withBaseURLString:(NSString *)baseURLString
    {

    if ((baseURLString)&&(![baseURLString isEqualToString:@""]))
    {
      /* make sure that baseURLString ends in a directory */
      if (![[baseURLString
    substringFromIndex:([baseURLString length] - 1)]
    isEqualToString:@"/"])
      {
                            unsigned endOfLastDirectoryIndex =
    [baseURLString rangeOfString:@"/"
    options:(NSLiteralSearch|NSBackwardsSearch)
    range:NSMakeRange(0,[baseURLString length]-1)].location;
      if (endOfLastDirectoryIndex != NSNotFound)
    {baseURLString = [baseURLString
    substringToIndex:(endOfLastDirectoryIndex+1)];}
                            else {baseURLString = @"";}
      }

      NSString *commonString = [absoluteURLString
    commonPrefixWithString:baseURLString options:NSLiteralSearch];

      if ((commonString)&&(![commonString isEqualToString:@""]))
      {
      /* make sure that the common url path ends in
    a directory */
      if (![[commonString
    substringFromIndex:([commonString length] - 1)] isEqualToString:@"/"])
      {
        unsigned endOfLastDirectoryIndex =
    [commonString rangeOfString:@"/"
    options:(NSLiteralSearch|NSBackwardsSearch)
    range:NSMakeRange(0,[commonString length]-1)].location;
                                    if (endOfLastDirectoryIndex !=
    NSNotFound) {commonString = [commonString
    substringToIndex:(endOfLastDirectoryIndex+1)];}
                                    else {commonString = @"";}
      }

      if (([commonString
    length]>2)&&(![[commonString substringFromIndex:([commonString
    length] - 2)] isEqualToString:@"//"]))
      {
        NSString *remainderString;
        if ([commonString
    isEqualToString:absoluteURLString]) {remainderString = @"";}
        else
        {
        NSRange commonRange =
    [absoluteURLString rangeOfString:commonString];
        remainderString =
    [absoluteURLString substringFromIndex:commonRange.length];
        }

        /* add ../ to back up to parent
    directory as many times as required */
        while (![commonString
    isEqualToString:baseURLString])
        {
        unsigned truncateAfterIndex =
    [baseURLString rangeOfString:@"/"
    options:(NSLiteralSearch|NSBackwardsSearch)
    range:NSMakeRange(0,[baseURLString length]-1)].location;
                                    baseURLString = [baseURLString
    substringToIndex:(truncateAfterIndex+1)];
        remainderString = [NSString
    stringWithFormat:@"../%@",remainderString];
        }

        return remainderString;
      }
      }
    }

    /* no baseURLString, empty base url, or base url does not match */
    return absoluteURLString;
    }

    With apologies to digest readers who aren't interested, but still get
    to scroll past my code twice :)

    Louis
    _______________________________________________
    cocoa-dev mailing list | <cocoa-dev...>
    Help/Unsubscribe/Archives: http://www.lists.apple.com/mailman/listinfo/cocoa-dev
    Do not post admin requests to the list. They will be ignored.
  • Hi, Louis

    on 04.3.10 8:12 PM, Louis C. Sacha at <subsLCS...> wrote:
    > So here's an updated version, now that I've had a chance to test it
    > and make sure it basically works (although there might still be other
    > bugs floating around).

    Thank you, Louis!

    I also wrote a NSString extension as follows. :-)
    Although I am going to test it now.

    - (NSString *)relativePathForBasePath:(NSString *)basePath
    {
    NSArray *myPathComponents = [self pathComponents];
    NSArray *basePathComponents = [basePath pathComponents];
    int commonIndex, index;
    NSString *resutPath;

        commonIndex = -1;
        for( index=0; index < [basePathComponents count]; index++ )
        {
        if( ![[basePathComponents objectAtIndex:index]
            isEqualToString:[myPathComponents objectAtIndex:index]] ) break;
        commonIndex++;
        }

        if( commonIndex == -1 )
        {
        // another volume
        resutPath = [NSString stringWithString:self];
        }
        else if( commonIndex == [basePathComponents count] -1 )
        {
        // in base directory
        resutPath=[NSString stringWithString:@""];
        for( index=commonIndex+1; index < [myPathComponents count]; index++ )
            {
            resutPath = [resutPath
    stringByAppendingPathComponent:[myPathComponents objectAtIndex:index]];
            }
        }
        else
        {
        // outside of base directory
        resutPath=[NSString stringWithString:@""];
        for( index=commonIndex;  index< [basePathComponents count]-1; index++ )
            {
            resutPath=[resutPath stringByAppendingString:@"../"];
            }

        for( index=commonIndex+1; index < [myPathComponents count]; index++ )
            {
            resutPath = [resutPath
    stringByAppendingPathComponent:[myPathComponents objectAtIndex:index]];
            }
        }

        return resutPath;

    }

    -----------------------------------------------------
    Satoshi Matsumoto <satoshi...>
    816-5 Odake, Odawara, Kanagawa, Japan 256-0802
    _______________________________________________
    cocoa-dev mailing list | <cocoa-dev...>
    Help/Unsubscribe/Archives: http://www.lists.apple.com/mailman/listinfo/cocoa-dev
    Do not post admin requests to the list. They will be ignored.
previous month march 2004 next month
MTWTFSS
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31        
Go to today