// (c) 2003 Allen I Holub. All rights reserved. // package com.holub.net; import java.net.*; import java.io.*; import com.holub.tools.DateUtil; /** This utility provides methods for building relative URLs. * * <!-- ====================== distribution terms ===================== --> * <p><blockquote * style="border-style: solid; border-width:thin; padding: 1em 1em 1em 1em;"> * <center> * Copyright © 2003, Allen I. Holub. All rights reserved. * </center> * <br> * <br> * This code is distributed under the terms of the * <a href="http://www.gnu.org/licenses/gpl.html" * >GNU Public License</a> (GPL) * with the following ammendment to section 2.c: * <p> * As a requirement for distributing this code, your splash screen, * about box, or equivalent must include an my name, copyright, * <em>and URL</em>. An acceptable message would be: * <center> * This program contains Allen Holub's <em>XXX</em> utility.<br> * (c) 2003 Allen I. Holub. All Rights Reserved.<br> * http://www.holub.com<br> * </center> * If your progam does not run interactively, then the foregoing * notice must appear in your documentation. * </blockquote> * <!-- =============================================================== --> * @author Allen I. Holub */ public class UrlUtil { /** Create a URL that adjusts the <i>context</i> URL to * reflect the relative path specified by the <i>spec</i>. * Here are some examples: * <table border=1 cellspacing=0 cellpadding=3> * <tr> * <td><code>context</code></td> * <td><code>spec</code></td> * <td>Result</td> * </tr> * <tr> * <td><code>http://www.holub.com/x/y/REMOVE.EXT</code></td> * <td><code>../../b/ADD.EXT</code></td> * <td><code>http://www.holub.com/b/ADD.EXT</code></td> * </tr> * <tr> * <td><code>http://www.holub.com/</code></td> * <td><code>/b/ADD.EXT</code></td> * <td><code>http://www.holub.com/b/ADD.EXT</code></td> * </tr> * <tr> * <td><code>http://www.holub.com/a/</code></td> * <td><code>./b/ADD.EXT</code></td> * <td><code>http://www.holub.com/a/b/ADD.EXT</code></td> * </tr> * <tr> * <td><code>http://host/</code></td> * <td><code>/b</code></td> * <td><code>http://host/b</code></td> * </tr> * <tr> * <td><code>http://host/x</code></td> * <td><code>/b</code></td> * <td><code>http://host/b</code></td> * </tr> * <tr> * <td><code>http://host/</code></td> * <td><code>b</code></td> * <td><code>http://host/b</code></td> * </tr> * <tr> * <td><code>http://host/x/</code></td> * <td><code>../b</code></td> * <td><code>//host/b</code></td> * </tr> * </table> * <p> * @throws MalformedURLException in the case of a bad URL, including * impossible relative paths. For example, the following call * throws an exception: * <pre> * URL context = new URL("http://www.holub.com/a/" ); * URL relative = UrlUtil.relative( context, "../../b" ); * </pre> * * @param context the URL that serves as the base point * of the relative address. If the path component of * this URL ends in a slash (which is the case if you * put a slash into the original URL constructor), then * the URL is assumed to represent a file, otherwise * it's assumed to represent a directory. * * @param spec the relative part of the file spec. If it * starts with a /, then it's relative to the * host root directory and this construnctor * will work just like {@link java.net.URL#URL(URL,String)}. */ public static URL relative( URL context, String spec ) throws MalformedURLException { String path = context.getPath(); String originalPath = path; String originalSpec = spec; int pathEnd = path.length()-1; int specStart = 0; if( !path.startsWith("/") ) path = "/" + path ; /*D.ebug( "Working on " + context.getProtocol() + "://" + context.getHost() + path + " + " + spec ); */ if( spec.startsWith("/") ) // it's off the root directory return new URL( context, spec ); if( !path.endsWith("/" ) ) // it's a file pathEnd = path.lastIndexOf('/', pathEnd ); if( spec.startsWith("./") ) // harmless, doesn't mean anything specStart += 2; while( spec.startsWith("..", specStart) ) { specStart += 2; pathEnd = path.lastIndexOf('/', pathEnd-1 ); if( pathEnd == -1 ) throw new MalformedURLException( "Illegal relative path. Cannot get to " + originalSpec + " from " + originalPath ); if( spec.startsWith("/", specStart) ) ++specStart; } return new URL( context.getProtocol() + "://" + context.getHost() + path.substring(0,pathEnd+1) + spec.substring(specStart) ); } public static String decodeUrlEncoding( String data ) { StringReader in = new StringReader( data ); StringBuffer result = new StringBuffer(); int c; for( int cursor = 0; cursor < data.length(); ++cursor ) { switch( c = data.charAt(cursor) ) { default : result.append((char)c); break; case '&' : result.append('\n'); break; case '+' : result.append(' '); break; case '%' : int high = data.charAt(++cursor); int low = data.charAt(++cursor); high = (high >= 'a') ? (high - 'a') + 0xa : (high >= 'A') ? (high - 'A') + 0xa : (high - '0') ; low = (low >= 'a') ? (low - 'a') + 0xa : (low >= 'A') ? (low - 'A') + 0xa : (low - '0') ; c = (high << 4) + low; if( c == '\n' ) result.append(" "); else result.append((char)c); break; } } return result.toString(); } public static class Test { public static void main(String[] args) throws Exception { /*Tester t = new Tester( args.length > 0, com.holub.io.Std.out() ); URL uri = UrlUtil.relative( new URL("http://www.holub.com/x/y/REMOVE.EXT"), "../../add/ADD.EXT" ); t.check( "UrlUtil.1", "http://www.holub.com/add/ADD.EXT", uri.toExternalForm() ); uri = UrlUtil.relative(new URL("http://www.holub.com/"), "/add/ADD.EXT"); t.check( "UrlUtil.2", "http://www.holub.com/add/ADD.EXT", uri.toExternalForm()); uri = UrlUtil.relative(new URL("http://www.holub.com/fred/"), "./add/ADD.EXT"); t.check( "UrlUtil.3", "http://www.holub.com/fred/add/ADD.EXT", uri.toExternalForm() ); uri = UrlUtil.relative(new URL("http://h/"), "/b"); t.check( "UrlUtil.4", "http://h/b", uri.toExternalForm() ); uri = UrlUtil.relative(new URL("http://h/x"), "/b"); t.check( "UrlUtil.5", "http://h/b", uri.toExternalForm() ); uri = UrlUtil.relative(new URL("http://h/"), "b"); t.check( "UrlUtil.6", "http://h/b", uri.toExternalForm() ); uri = UrlUtil.relative(new URL("http://h/x/"), "../b"); t.check( "UrlUtil.7", "http://h/b", uri.toExternalForm() ); try { uri = UrlUtil.relative(new URL("http://a/"), "../b"); t.failure("UrlUtil.8.1", "Failed to catch exception"); } catch( MalformedURLException e) { t.success("UrlUtil.8.1", "Caught Exception: "+e.getMessage() ); } try { uri = UrlUtil.relative(new URL("http://a/b"), "../../c"); t.failure("UrlUtil.8.2", "Failed to catch exception"); } catch( MalformedURLException e) { t.success("UrlUtil.8.2", "Caught Exception: "+e.getMessage() ); } try { uri = UrlUtil.relative(new URL("http://a/b/c"), "../../c"); t.failure("UrlUtil.8.3", "Failed to catch exception"); } catch( MalformedURLException e) { t.success("UrlUtil.8.3", "Caught Exception: "+e.getMessage() ); }*/ } } }