/**
   * Open the specified file for reading. If the specified name is "System.in", then a reader from
   * standard in is returned. If the name begins with "$CLASSPATH" or "xxxxxxCLASSPATHxxxxxx", then
   * the name is passed to {@link #nameToURL(String, URI, ClassLoader)} If the file name is not
   * absolute, the it is assumed to be relative to the specified base URI.
   *
   * @see #nameToURL(String, URI, ClassLoader)
   * @param name File name.
   * @param base The base URI for relative references.
   * @param classLoader The class loader to use to locate system resources, or null to use the
   *     system class loader that was used to load this class.
   * @return If the name is null or the empty string, then null is returned, otherwise a buffered
   *     reader is returned.
   * @exception IOException If the file cannot be opened.
   */
  public static BufferedReader openForReading(String name, URI base, ClassLoader classLoader)
      throws IOException {
    if ((name == null) || name.trim().equals("")) {
      return null;
    }

    if (name.trim().equals("System.in")) {
      if (STD_IN == null) {
        STD_IN = new BufferedReader(new InputStreamReader(System.in));
      }

      return STD_IN;
    }

    // Not standard input. Try URL mechanism.
    URL url = nameToURL(name, base, classLoader);

    if (url == null) {
      throw new IOException(
          "Could not convert \"" + name + "\" with base \"" + base + "\" to a URL.");
    }

    InputStreamReader inputStreamReader = null;
    try {
      inputStreamReader = new InputStreamReader(url.openStream());
    } catch (IOException ex) {
      // Try it as a jar url.
      // WebStart ptalon MapReduce needs this.
      try {
        URL possibleJarURL = ClassUtilities.jarURLEntryResource(url.toString());
        if (possibleJarURL != null) {
          inputStreamReader = new InputStreamReader(possibleJarURL.openStream());
        }
        // If possibleJarURL is null, this throws an exception,
        // which we ignore and report the first exception (ex)
        return new BufferedReader(inputStreamReader);
      } catch (Exception ex2) {
        try {
          if (inputStreamReader != null) {
            inputStreamReader.close();
          }
        } catch (IOException ex3) {
          // Ignore
        }
        IOException ioException = new IOException("Failed to open \"" + url + "\".");
        ioException.initCause(ex);
        throw ioException;
      }
    }

    return new BufferedReader(inputStreamReader);
  }
  /**
   * Given a file or URL name, return as a URL. If the file name is relative, then it is interpreted
   * as being relative to the specified base directory. If the name begins with
   * "xxxxxxCLASSPATHxxxxxx" or "$CLASSPATH" then search for the file relative to the classpath.
   *
   * <p>Note that "xxxxxxCLASSPATHxxxxxx" is the value of the globally defined constant $CLASSPATH
   * available in the Ptolemy II expression language. II expression language.
   *
   * <p>If no file is found, then throw an exception.
   *
   * <p>This method is similar to {@link #nameToFile(String, URI)} except that in this method, the
   * file or URL must be readable. Usually, this method is use for reading a file and is used for
   * writing {@link #nameToFile(String, URI)}.
   *
   * @param name The name of a file or URL.
   * @param baseDirectory The base directory for relative file names, or null to specify none.
   * @param classLoader The class loader to use to locate system resources, or null to use the
   *     system class loader that was used to load this class.
   * @return A URL, or null if the name is null or the empty string.
   * @exception IOException If the file cannot be read, or if the file cannot be represented as a
   *     URL (e.g. System.in), or the name specification cannot be parsed.
   * @see #nameToFile(String, URI)
   */
  public static URL nameToURL(String name, URI baseDirectory, ClassLoader classLoader)
      throws IOException {
    if ((name == null) || name.trim().equals("")) {
      return null;
    }

    if (name.startsWith(_CLASSPATH_VALUE) || name.startsWith("$CLASSPATH")) {
      URL result = _searchClassPath(name, classLoader);
      if (result == null) {
        throw new IOException("Cannot find file '" + _trimClassPath(name) + "' in classpath");
      }

      return result;
    }

    File file = new File(name);

    if (file.isAbsolute()) {
      if (!file.canRead()) {
        // FIXME: This is a hack.
        // Expanding the configuration with Ptolemy II installed
        // in a directory with spaces in the name fails on
        // JAIImageReader because PtolemyII.jpg is passed in
        // to this method as C:\Program%20Files\Ptolemy\...
        file = new File(StringUtilities.substitute(name, "%20", " "));

        URL possibleJarURL = null;

        if (!file.canRead()) {
          // ModelReference and FilePortParameters sometimes
          // have paths that have !/ in them.
          possibleJarURL = ClassUtilities.jarURLEntryResource(name);

          if (possibleJarURL != null) {
            file = new File(possibleJarURL.getFile());
          }
        }

        if (!file.canRead()) {
          throw new IOException(
              "Cannot read file '"
                  + name
                  + "' or '"
                  + StringUtilities.substitute(name, "%20", " ")
                  + "'"
                  + ((possibleJarURL == null) ? "" : (" or '" + possibleJarURL.getFile() + "")));
        }
      }

      return file.toURI().toURL();
    } else {
      // Try relative to the base directory.
      if (baseDirectory != null) {
        // Try to resolve the URI.
        URI newURI;

        try {
          newURI = baseDirectory.resolve(name);
        } catch (Exception ex) {
          // FIXME: Another hack
          // This time, if we try to open some of the JAI
          // demos that have actors that have defaults FileParameters
          // like "$PTII/doc/img/PtolemyII.jpg", then resolve()
          // bombs.
          String name2 = StringUtilities.substitute(name, "%20", " ");
          try {
            newURI = baseDirectory.resolve(name2);
            name = name2;
          } catch (Exception ex2) {
            IOException io =
                new IOException(
                    "Problem with URI format in '"
                        + name
                        + "'. "
                        + "and '"
                        + name2
                        + "' "
                        + "This can happen if the file name "
                        + "is not absolute "
                        + "and is not present relative to the "
                        + "directory in which the specified model "
                        + "was read (which was '"
                        + baseDirectory
                        + "')");
            io.initCause(ex2);
            throw io;
          }
        }

        String urlString = newURI.toString();

        try {
          // Adding another '/' for remote execution.
          if ((newURI.getScheme() != null) && (newURI.getAuthority() == null)) {
            // Change from Efrat:
            // "I made these change to allow remote
            // execution of a workflow from within a web
            // service."

            // "The first modification was due to a URI
            // authentication exception when trying to
            // create a file object from a URI on the
            // remote side. The second modification was
            // due to the file protocol requirements to
            // use 3 slashes, 'file:///' on the remote
            // side, although it would be probably be a
            // good idea to also make sure first that the
            // url string actually represents the file
            // protocol."
            urlString = urlString.substring(0, 6) + "//" + urlString.substring(6);

            // } else {
            // urlString = urlString.substring(0, 6) + "/"
            // + urlString.substring(6);
          }
          // Unfortunately, between Java 1.5 and 1.6,
          // The URL constructor changed.
          // In 1.5, new URL("file:////foo").toString()
          // returns "file://foo"
          // In 1.6, new URL("file:////foo").toString()
          // return "file:////foo".
          // See http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6561321
          return new URL(urlString);
        } catch (Exception ex3) {
          try {
            // Under Webstart, opening
            // hoc/demo/ModelReference/ModelReference.xml
            // requires this because the URL is relative.
            return new URL(baseDirectory.toURL(), urlString);
          } catch (Exception ex4) {

            try {
              // Under Webstart, ptalon, EightChannelFFT
              // requires this.
              return new URL(baseDirectory.toURL(), newURI.toString());
            } catch (Exception ex5) {
              // Ignore
            }

            IOException io =
                new IOException(
                    "Problem with URI format in '"
                        + urlString
                        + "'. "
                        + "This can happen if the '"
                        + urlString
                        + "' is not absolute"
                        + " and is not present relative to the directory"
                        + " in which the specified model was read"
                        + " (which was '"
                        + baseDirectory
                        + "')");
            io.initCause(ex3);
            throw io;
          }
        }
      }

      // As a last resort, try an absolute URL.

      URL url = new URL(name);

      // If we call new URL("http", null, /foo);
      // then we get "http:/foo", which should be "http://foo"
      // This change suggested by Dan Higgins and Kevin Kruland
      // See kepler/src/util/URLToLocalFile.java
      try {
        String fixedURLAsString = url.toString().replaceFirst("(https?:)//?", "$1//");
        url = new URL(fixedURLAsString);
      } catch (Exception e) {
        // Ignore
      }
      return url;
    }
  }