Home » apache-ant-1.7.1-src » org.apache.tools » ant » launch » [javadoc | source]
    1   /*
    2    *  Licensed to the Apache Software Foundation (ASF) under one or more
    3    *  contributor license agreements.  See the NOTICE file distributed with
    4    *  this work for additional information regarding copyright ownership.
    5    *  The ASF licenses this file to You under the Apache License, Version 2.0
    6    *  (the "License"); you may not use this file except in compliance with
    7    *  the License.  You may obtain a copy of the License at
    8    *
    9    *      http://www.apache.org/licenses/LICENSE-2.0
   10    *
   11    *  Unless required by applicable law or agreed to in writing, software
   12    *  distributed under the License is distributed on an "AS IS" BASIS,
   13    *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   14    *  See the License for the specific language governing permissions and
   15    *  limitations under the License.
   16    *
   17    */
   18   package org.apache.tools.ant.launch;
   19   
   20   import java.net.MalformedURLException;
   21   
   22   import java.net.URL;
   23   import java.io.File;
   24   import java.io.FilenameFilter;
   25   import java.io.ByteArrayOutputStream;
   26   import java.io.UnsupportedEncodingException;
   27   import java.text.CharacterIterator;
   28   import java.text.StringCharacterIterator;
   29   import java.util.Locale;
   30   
   31   // CheckStyle:LineLengthCheck OFF - urls are long!
   32   /**
   33    * The Locator is a utility class which is used to find certain items
   34    * in the environment.
   35    *
   36    * It is used at boot time in the launcher, and cannot make use of any of Ant's other classes.
   37    *
   38    * This is a surprisingly brittle piece of code, and has had lots of bugs filed against it.
   39    * {@link <a href="http://issues.apache.org/bugzilla/show_bug.cgi?id=42275">running ant off a network share can cause Ant to fail</a>}
   40    * {@link <a href="http://issues.apache.org/bugzilla/show_bug.cgi?id=8031">use File.toURI().toURL().toExternalForm()</a>}
   41    * {@link <a href="http://issues.apache.org/bugzilla/show_bug.cgi?id=42222">Locator implementation not encoding URI strings properly: spaces in paths</a>}
   42    * It also breaks Eclipse 3.3 Betas
   43    * {@link <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=183283">Exception if installation path has spaces</a>}
   44    *
   45    * Be very careful when making changes to this class, as a break will upset a lot of people.
   46    * @since Ant 1.6
   47    */
   48   // CheckStyle:LineLengthCheck ON - urls are long!
   49   public final class Locator {
   50   
   51       private static final int NIBBLE = 4;
   52       private static final int NIBBLE_MASK   = 0xF;
   53   
   54       private static final int ASCII_SIZE = 128;
   55   
   56       private static final int BYTE_SIZE = 256;
   57   
   58       private static final int WORD = 16;
   59   
   60       private static final int SPACE = 0x20;
   61       private static final int DEL = 0x7F;
   62   
   63       /**
   64        * encoding used to represent URIs
   65        */
   66       public static final String URI_ENCODING = "UTF-8";
   67       // stolen from org.apache.xerces.impl.XMLEntityManager#getUserDir()
   68       // of the Xerces-J team
   69       // which ASCII characters need to be escaped
   70       private static boolean[] gNeedEscaping = new boolean[ASCII_SIZE];
   71       // the first hex character if a character needs to be escaped
   72       private static char[] gAfterEscaping1 = new char[ASCII_SIZE];
   73       // the second hex character if a character needs to be escaped
   74       private static char[] gAfterEscaping2 = new char[ASCII_SIZE];
   75       private static char[] gHexChs = {'0', '1', '2', '3', '4', '5', '6', '7',
   76                                        '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
   77       /** Error string used when an invalid uri is seen */
   78       public static final String ERROR_NOT_FILE_URI
   79           = "Can only handle valid file: URIs, not ";
   80   
   81       // initialize the above 3 arrays
   82       static {
   83           for (int i = 0; i < SPACE; i++) {
   84               gNeedEscaping[i] = true;
   85               gAfterEscaping1[i] = gHexChs[i >> NIBBLE];
   86               gAfterEscaping2[i] = gHexChs[i & NIBBLE_MASK];
   87           }
   88           gNeedEscaping[DEL] = true;
   89           gAfterEscaping1[DEL] = '7';
   90           gAfterEscaping2[DEL] = 'F';
   91           char[] escChs = {' ', '<', '>', '#', '%', '"', '{', '}',
   92                            '|', '\\', '^', '~', '[', ']', '`'};
   93           int len = escChs.length;
   94           char ch;
   95           for (int i = 0; i < len; i++) {
   96               ch = escChs[i];
   97               gNeedEscaping[ch] = true;
   98               gAfterEscaping1[ch] = gHexChs[ch >> NIBBLE];
   99               gAfterEscaping2[ch] = gHexChs[ch & NIBBLE_MASK];
  100           }
  101       }
  102       /**
  103        * Not instantiable
  104        */
  105       private Locator() {
  106       }
  107   
  108       /**
  109        * Find the directory or jar file the class has been loaded from.
  110        *
  111        * @param c the class whose location is required.
  112        * @return the file or jar with the class or null if we cannot
  113        *         determine the location.
  114        *
  115        * @since Ant 1.6
  116        */
  117       public static File getClassSource(Class c) {
  118           String classResource = c.getName().replace('.', '/') + ".class";
  119           return getResourceSource(c.getClassLoader(), classResource);
  120       }
  121   
  122       /**
  123        * Find the directory or jar a given resource has been loaded from.
  124        *
  125        * @param c the classloader to be consulted for the source.
  126        * @param resource the resource whose location is required.
  127        *
  128        * @return the file with the resource source or null if
  129        *         we cannot determine the location.
  130        *
  131        * @since Ant 1.6
  132        */
  133       public static File getResourceSource(ClassLoader c, String resource) {
  134           if (c == null) {
  135               c = Locator.class.getClassLoader();
  136           }
  137           URL url = null;
  138           if (c == null) {
  139               url = ClassLoader.getSystemResource(resource);
  140           } else {
  141               url = c.getResource(resource);
  142           }
  143           if (url != null) {
  144               String u = url.toString();
  145               try {
  146                   if (u.startsWith("jar:file:")) {
  147                       int pling = u.indexOf("!");
  148                       String jarName = u.substring("jar:".length(), pling);
  149                       return new File(fromURI(jarName));
  150                   } else if (u.startsWith("file:")) {
  151                       int tail = u.indexOf(resource);
  152                       String dirName = u.substring(0, tail);
  153                       return new File(fromURI(dirName));
  154                   }
  155               } catch (IllegalArgumentException e) {
  156                   //unable to determine the URI for reasons unknown.
  157                   return null;
  158               }
  159           }
  160           return null;
  161       }
  162   
  163       /**
  164        * Constructs a file path from a <code>file:</code> URI.
  165        *
  166        * <p>Will be an absolute path if the given URI is absolute.</p>
  167        *
  168        * <p>Prior to Java 1.4,
  169        * swallows '%' that are not followed by two characters.</p>
  170        *
  171        * See <a href="http://www.w3.org/TR/xml11/#dt-sysid">dt-sysid</a>
  172        * which makes some mention of how
  173        * characters not supported by URI Reference syntax should be escaped.
  174        *
  175        * @param uri the URI designating a file in the local filesystem.
  176        * @return the local file system path for the file.
  177        * @throws IllegalArgumentException if the URI is malformed or not a legal file: URL
  178        * @since Ant 1.6
  179        */
  180       public static String fromURI(String uri) {
  181           // #buzilla8031: first try Java 1.4.
  182           String result = null;
  183           //result = fromUriJava14(uri);
  184           if (result == null) {
  185               result = fromURIJava13(uri);
  186           }
  187           return result;
  188       }
  189   
  190   
  191       /**
  192        * Java1.4+ code to extract the path from the URI.
  193        * @param uri
  194        * @return null if a conversion was not possible
  195        */
  196       private static String fromUriJava14(String uri) {
  197           Class uriClazz = null;
  198           try {
  199               uriClazz = Class.forName("java.net.URI");
  200           } catch (ClassNotFoundException cnfe) {
  201               // Fine, Java 1.3 or earlier, do it by hand.
  202               return null;
  203           }
  204           // Also check for properly formed URIs. Ant formerly recommended using
  205           // nonsense URIs such as "file:./foo.xml" in XML includes. You shouldn't
  206           // do that (just "foo.xml" is correct) but for compatibility we special-case
  207           // things when the path is not absolute, and fall back to the old parsing behavior.
  208           if (uriClazz != null && uri.startsWith("file:/")) {
  209               try {
  210                   java.lang.reflect.Method createMethod
  211                           = uriClazz.getMethod("create", new Class[]{String.class});
  212                   Object uriObj = createMethod.invoke(null, new Object[]{encodeURI(uri)});
  213                   java.lang.reflect.Constructor fileConst
  214                           = File.class.getConstructor(new Class[]{uriClazz});
  215                   File f = (File) fileConst.newInstance(new Object[]{uriObj});
  216                   //bug #42227 forgot to decode before returning
  217                   return decodeUri(f.getAbsolutePath());
  218               } catch (java.lang.reflect.InvocationTargetException e) {
  219                   Throwable e2 = e.getTargetException();
  220                   if (e2 instanceof IllegalArgumentException) {
  221                       // Bad URI, pass this on.
  222                       // no, this is downgraded to a warning after various
  223                       // JRE bugs surfaced. Hand off
  224                       // to our built in code on a failure
  225                       //throw new IllegalArgumentException(
  226                       //   "Bad URI " + uri + ":" + e2.getMessage(), e2);
  227                       e2.printStackTrace();
  228   
  229                   } else {
  230                       // Unexpected target exception? Should not happen.
  231                       e2.printStackTrace();
  232                   }
  233               } catch (Exception e) {
  234                   // Reflection problems? Should not happen, debug.
  235                   e.printStackTrace();
  236               }
  237           }
  238           return null;
  239       }
  240   
  241       /**
  242        * package-private for testing in same classloader
  243        * @param uri uri to expand
  244        * @return the decoded URI
  245        * @since Ant1.7.1
  246        */
  247       static String fromURIJava13(String uri) {
  248           // Fallback method for Java 1.3 or earlier.
  249   
  250           URL url = null;
  251           try {
  252               url = new URL(uri);
  253           } catch (MalformedURLException emYouEarlEx) {
  254               // Ignore malformed exception
  255           }
  256           if (url == null || !("file".equals(url.getProtocol()))) {
  257               throw new IllegalArgumentException(ERROR_NOT_FILE_URI + uri);
  258           }
  259           StringBuffer buf = new StringBuffer(url.getHost());
  260           if (buf.length() > 0) {
  261               buf.insert(0, File.separatorChar).insert(0, File.separatorChar);
  262           }
  263           String file = url.getFile();
  264           int queryPos = file.indexOf('?');
  265           buf.append((queryPos < 0) ? file : file.substring(0, queryPos));
  266   
  267           uri = buf.toString().replace('/', File.separatorChar);
  268   
  269           if (File.pathSeparatorChar == ';' && uri.startsWith("\\") && uri.length() > 2
  270               && Character.isLetter(uri.charAt(1)) && uri.lastIndexOf(':') > -1) {
  271               uri = uri.substring(1);
  272           }
  273           String path = null;
  274           try {
  275               path = decodeUri(uri);
  276               String cwd = System.getProperty("user.dir");
  277               int posi = cwd.indexOf(":");
  278               if ((posi > 0) && path.startsWith(File.separator)) {
  279                  path = cwd.substring(0, posi + 1) + path;
  280               }
  281           } catch (UnsupportedEncodingException exc) {
  282               // not sure whether this is clean, but this method is
  283               // declared not to throw exceptions.
  284               throw new IllegalStateException(
  285                   "Could not convert URI " + uri + " to path: "
  286                   + exc.getMessage());
  287           }
  288           return path;
  289       }
  290   
  291       /**
  292        * Decodes an Uri with % characters.
  293        * The URI is escaped
  294        * @param uri String with the uri possibly containing % characters.
  295        * @return The decoded Uri
  296        * @throws UnsupportedEncodingException if UTF-8 is not available
  297        * @since Ant 1.7
  298        */
  299       public static String decodeUri(String uri) throws UnsupportedEncodingException {
  300           if (uri.indexOf('%') == -1) {
  301               return uri;
  302           }
  303           ByteArrayOutputStream sb = new ByteArrayOutputStream(uri.length());
  304           CharacterIterator iter = new StringCharacterIterator(uri);
  305           for (char c = iter.first(); c != CharacterIterator.DONE;
  306                c = iter.next()) {
  307               if (c == '%') {
  308                   char c1 = iter.next();
  309                   if (c1 != CharacterIterator.DONE) {
  310                       int i1 = Character.digit(c1, WORD);
  311                       char c2 = iter.next();
  312                       if (c2 != CharacterIterator.DONE) {
  313                           int i2 = Character.digit(c2, WORD);
  314                           sb.write((char) ((i1 << NIBBLE) + i2));
  315                       }
  316                   }
  317               } else {
  318                   sb.write(c);
  319               }
  320           }
  321           return sb.toString(URI_ENCODING);
  322       }
  323   
  324       /**
  325        * Encodes an Uri with % characters.
  326        * The URI is escaped
  327        * @param path String to encode.
  328        * @return The encoded string, according to URI norms
  329        * @throws UnsupportedEncodingException if UTF-8 is not available
  330        * @since Ant 1.7
  331        */
  332       public static String encodeURI(String path) throws UnsupportedEncodingException {
  333           int i = 0;
  334           int len = path.length();
  335           int ch = 0;
  336           StringBuffer sb = null;
  337           for (; i < len; i++) {
  338               ch = path.charAt(i);
  339               // if it's not an ASCII character, break here, and use UTF-8 encoding
  340               if (ch >= ASCII_SIZE) {
  341                   break;
  342               }
  343               if (gNeedEscaping[ch]) {
  344                   if (sb == null) {
  345                       sb = new StringBuffer(path.substring(0, i));
  346                   }
  347                   sb.append('%');
  348                   sb.append(gAfterEscaping1[ch]);
  349                   sb.append(gAfterEscaping2[ch]);
  350                   // record the fact that it's escaped
  351               } else if (sb != null) {
  352                   sb.append((char) ch);
  353               }
  354           }
  355   
  356           // we saw some non-ascii character
  357           if (i < len) {
  358               if (sb == null) {
  359                   sb = new StringBuffer(path.substring(0, i));
  360               }
  361               // get UTF-8 bytes for the remaining sub-string
  362               byte[] bytes = null;
  363               byte b;
  364               bytes = path.substring(i).getBytes(URI_ENCODING);
  365               len = bytes.length;
  366   
  367               // for each byte
  368               for (i = 0; i < len; i++) {
  369                   b = bytes[i];
  370                   // for non-ascii character: make it positive, then escape
  371                   if (b < 0) {
  372                       ch = b + BYTE_SIZE;
  373                       sb.append('%');
  374                       sb.append(gHexChs[ch >> NIBBLE]);
  375                       sb.append(gHexChs[ch & NIBBLE_MASK]);
  376                   } else if (gNeedEscaping[b]) {
  377                       sb.append('%');
  378                       sb.append(gAfterEscaping1[b]);
  379                       sb.append(gAfterEscaping2[b]);
  380                   } else {
  381                       sb.append((char) b);
  382                   }
  383               }
  384           }
  385           return sb == null ? path : sb.toString();
  386       }
  387   
  388       /**
  389        * Convert a File to a URL.
  390        * File.toURL() does not encode characters like #.
  391        * File.toURI() has been introduced in java 1.4, so
  392        * ANT cannot use it (except by reflection)
  393        * FileUtils.toURI() cannot be used by Locator.java
  394        * Implemented this way.
  395        * File.toURL() adds file: and changes '\' to '/' for dos OSes
  396        * encodeURI converts characters like ' ' and '#' to %DD
  397        * @param file the file to convert
  398        * @return URL the converted File
  399        * @throws MalformedURLException on error
  400        */
  401       public static URL fileToURL(File file)
  402           throws MalformedURLException {
  403           try {
  404               return new URL(encodeURI(file.toURL().toString()));
  405           } catch (UnsupportedEncodingException ex) {
  406               throw new MalformedURLException(ex.toString());
  407           }
  408       }
  409   
  410       /**
  411        * Get the File necessary to load the Sun compiler tools. If the classes
  412        * are available to this class, then no additional URL is required and
  413        * null is returned. This may be because the classes are explicitly in the
  414        * class path or provided by the JVM directly.
  415        *
  416        * @return the tools jar as a File if required, null otherwise.
  417        */
  418       public static File getToolsJar() {
  419           // firstly check if the tools jar is already in the classpath
  420           boolean toolsJarAvailable = false;
  421           try {
  422               // just check whether this throws an exception
  423               Class.forName("com.sun.tools.javac.Main");
  424               toolsJarAvailable = true;
  425           } catch (Exception e) {
  426               try {
  427                   Class.forName("sun.tools.javac.Main");
  428                   toolsJarAvailable = true;
  429               } catch (Exception e2) {
  430                   // ignore
  431               }
  432           }
  433           if (toolsJarAvailable) {
  434               return null;
  435           }
  436           // couldn't find compiler - try to find tools.jar
  437           // based on java.home setting
  438           String libToolsJar
  439               = File.separator + "lib" + File.separator + "tools.jar";
  440           String javaHome = System.getProperty("java.home");
  441           File toolsJar = new File(javaHome + libToolsJar);
  442           if (toolsJar.exists()) {
  443               // Found in java.home as given
  444               return toolsJar;
  445           }
  446           if (javaHome.toLowerCase(Locale.US).endsWith(File.separator + "jre")) {
  447               javaHome = javaHome.substring(
  448                   0, javaHome.length() - "/jre".length());
  449               toolsJar = new File(javaHome + libToolsJar);
  450           }
  451           if (!toolsJar.exists()) {
  452               System.out.println("Unable to locate tools.jar. "
  453                    + "Expected to find it in " + toolsJar.getPath());
  454               return null;
  455           }
  456           return toolsJar;
  457       }
  458   
  459       /**
  460        * Get an array of URLs representing all of the jar files in the
  461        * given location. If the location is a file, it is returned as the only
  462        * element of the array. If the location is a directory, it is scanned for
  463        * jar files.
  464        *
  465        * @param location the location to scan for Jars.
  466        *
  467        * @return an array of URLs for all jars in the given location.
  468        *
  469        * @exception MalformedURLException if the URLs for the jars cannot be
  470        *            formed.
  471        */
  472       public static URL[] getLocationURLs(File location)
  473            throws MalformedURLException {
  474           return getLocationURLs(location, new String[]{".jar"});
  475       }
  476   
  477       /**
  478        * Get an array of URLs representing all of the files of a given set of
  479        * extensions in the given location. If the location is a file, it is
  480        * returned as the only element of the array. If the location is a
  481        * directory, it is scanned for matching files.
  482        *
  483        * @param location the location to scan for files.
  484        * @param extensions an array of extension that are to match in the
  485        *        directory search.
  486        *
  487        * @return an array of URLs of matching files.
  488        * @exception MalformedURLException if the URLs for the files cannot be
  489        *            formed.
  490        */
  491       public static URL[] getLocationURLs(File location,
  492                                           final String[] extensions)
  493            throws MalformedURLException {
  494           URL[] urls = new URL[0];
  495   
  496           if (!location.exists()) {
  497               return urls;
  498           }
  499           if (!location.isDirectory()) {
  500               urls = new URL[1];
  501               String path = location.getPath();
  502               String littlePath = path.toLowerCase(Locale.US);
  503               for (int i = 0; i < extensions.length; ++i) {
  504                   if (littlePath.endsWith(extensions[i])) {
  505                       urls[0] = fileToURL(location);
  506                       break;
  507                   }
  508               }
  509               return urls;
  510           }
  511           File[] matches = location.listFiles(
  512               new FilenameFilter() {
  513                   public boolean accept(File dir, String name) {
  514                       String littleName = name.toLowerCase(Locale.US);
  515                       for (int i = 0; i < extensions.length; ++i) {
  516                           if (littleName.endsWith(extensions[i])) {
  517                               return true;
  518                           }
  519                       }
  520                       return false;
  521                   }
  522               });
  523           urls = new URL[matches.length];
  524           for (int i = 0; i < matches.length; ++i) {
  525               urls[i] = fileToURL(matches[i]);
  526           }
  527           return urls;
  528       }
  529   }

Save This Page
Home » apache-ant-1.7.1-src » org.apache.tools » ant » launch » [javadoc | source]