/**
   * Resolves a reference to a local or remote file or element. If the link refers to an element in
   * the current document, this method returns that element. If the link refers to a remote
   * document, this method will initiate asynchronous retrieval of the document, and return a URL of
   * the downloaded document in the file cache, if it is available locally. If the link identifies a
   * COLLADA document, the document will be returned as a parsed ColladaRoot.
   *
   * @param link the address of the document or element to resolve. This may be a full URL, a URL
   *     fragment that identifies an element in the current document ("#myElement"), or a URL and a
   *     fragment identifier ("http://server.com/model.dae#myElement").
   * @return the requested element, or null if the element is not found.
   * @throws IllegalArgumentException if the address is null.
   */
  public Object resolveReference(String link) {
    if (link == null) {
      String message = Logging.getMessage("nullValue.DocumentSourceIsNull");
      Logging.logger().severe(message);
      throw new IllegalArgumentException(message);
    }

    try {
      String[] linkParts = link.split("#");
      String linkBase = linkParts[0];
      String linkRef = linkParts.length > 1 ? linkParts[1] : null;

      // See if it's a reference to an internal element.
      if (WWUtil.isEmpty(linkBase) && !WWUtil.isEmpty(linkRef)) return this.getItemByID(linkRef);

      // Interpret the path relative to the current document.
      String path = this.getSupportFilePath(linkBase);
      if (path == null) path = linkBase;

      // See if it's an already found and parsed COLLADA file.
      Object o = WorldWind.getSessionCache().get(path);
      if (o != null && o instanceof ColladaRoot)
        return linkRef != null ? ((ColladaRoot) o).getItemByID(linkRef) : o;

      URL url = WWIO.makeURL(path);
      if (url == null) {
        // See if the reference can be resolved to a local file.
        o = this.resolveLocalReference(path, linkRef);
      }

      // If we didn't find a local file, treat it as a remote reference.
      if (o == null) o = this.resolveRemoteReference(path, linkRef);

      if (o != null) return o;

      // If the reference was not resolved as a remote reference, look for a local element
      // identified by the
      // reference string. This handles the case of malformed internal references that omit the #
      // sign at the
      // beginning of the reference.
      return this.getItemByID(link);
    } catch (Exception e) {
      String message = Logging.getMessage("generic.UnableToResolveReference", link);
      Logging.logger().warning(message);
    }

    return null;
  }
  /**
   * Creates a Collada root for an untyped source. The source must be either a {@link File} or a
   * {@link String} identifying either a file path or a {@link URL}. Null is returned if the source
   * type is not recognized.
   *
   * @param docSource either a {@link File} or a {@link String} identifying a file path or {@link
   *     URL}.
   * @return a new {@link ColladaRoot} for the specified source, or null if the source type is not
   *     supported.
   * @throws IllegalArgumentException if the source is null.
   * @throws IOException if an error occurs while reading the source.
   */
  public static ColladaRoot create(Object docSource) throws IOException {
    if (docSource == null) {
      String message = Logging.getMessage("nullValue.DocumentSourceIsNull");
      Logging.logger().severe(message);
      throw new IllegalArgumentException(message);
    }

    if (docSource instanceof File) {
      return new ColladaRoot((File) docSource);
    } else if (docSource instanceof URL) {
      return new ColladaRoot((URL) docSource);
    } else if (docSource instanceof String) {
      File file = new File((String) docSource);
      if (file.exists()) return new ColladaRoot(file);

      URL url = WWIO.makeURL(docSource);
      if (url != null) return new ColladaRoot(url);
    } else if (docSource instanceof InputStream) {
      return new ColladaRoot((InputStream) docSource);
    }

    return null;
  }