package org.codehaus.mojo.javacc; /* * 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. */ import java.io.File; import java.net.MalformedURLException; import java.net.URL; import java.nio.ByteBuffer; import java.nio.charset.Charset; /** * Assists in handling of URLs. * * @author Benjamin Bentmann * @version $Id$ */ class UrlUtils { /** * The UTF-8 character set, used to decode octets in URLs. */ private static final Charset UTF8 = Charset.forName( "UTF-8" ); /** * The protocol prefix for "jar:" URLs. */ private static final String JAR = "jar:"; /** * The protocol prefix for "file:" URLs. */ private static final String FILE = "file:"; /** * The protocol prefix for "jar:file:" URLs. */ private static final String JAR_FILE = JAR + FILE; /** * Gets the absolute filesystem path to the class path root for the specified resource. The root is either a JAR * file or a directory with loose class files. If the URL does not use a supported protocol, an exception will be * thrown. * * @param url The URL to the resource, may be <code>null</code>. * @param resource The name of the resource, must not be <code>null</code>. * @return The absolute filesystem path to the class path root of the resource or <code>null</code> if the input * URL was <code>null</code>. */ public static File getResourceRoot( URL url, String resource ) { String path = null; if ( url != null ) { String spec = url.toExternalForm(); if ( ( JAR_FILE ).regionMatches( true, 0, spec, 0, JAR_FILE.length() ) ) { URL jar; try { jar = new URL( spec.substring( JAR.length(), spec.lastIndexOf( "!/" ) ) ); } catch ( MalformedURLException e ) { throw new IllegalArgumentException( "Invalid JAR URL: " + url + ", " + e.getMessage() ); } path = decodeUrl( jar.getPath() ); } else if ( FILE.regionMatches( true, 0, spec, 0, FILE.length() ) ) { path = decodeUrl( url.getPath() ); path = path.substring( 0, path.length() - resource.length() ); } else { throw new IllegalArgumentException( "Invalid class path URL: " + url ); } } return ( path != null ) ? new File( path ) : null; } /** * Decodes the specified URL as per RFC 3986, i.e. transforms percent-encoded octets to characters by decoding with * the UTF-8 character set. This function is primarily intended for usage with {@link java.net.URL} which * unfortunately does not enforce proper URLs. As such, this method will leniently accept invalid characters or * malformed percent-encoded octets and simply pass them literally through to the result string. Except for rare * edge cases, this will make unencoded URLs pass through unaltered. * * @param url The URL to decode, may be <code>null</code>. * @return The decoded URL or <code>null</code> if the input was <code>null</code>. */ public static String decodeUrl( String url ) { String decoded = url; if ( url != null && url.indexOf( '%' ) >= 0 ) { int n = url.length(); StringBuffer buffer = new StringBuffer(); ByteBuffer bytes = ByteBuffer.allocate( n ); for ( int i = 0; i < n; ) { if ( url.charAt( i ) == '%' ) { try { do { byte octet = (byte) Integer.parseInt( url.substring( i + 1, i + 3 ), 16 ); bytes.put( octet ); i += 3; } while ( i < n && url.charAt( i ) == '%' ); continue; } catch ( RuntimeException e ) { // malformed percent-encoded octet, fall through and append characters literally } finally { if ( bytes.position() > 0 ) { bytes.flip(); buffer.append( UTF8.decode( bytes ).toString() ); bytes.clear(); } } } buffer.append( url.charAt( i++ ) ); } decoded = buffer.toString(); } return decoded; } }