content: /*
   * Copyright 2002-2018 the original author or authors.
   *
   * Licensed 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
   *
   *      https://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.
   */
  
  package com.alibaba.nacos.common.packagescan.util;
  
  import com.alibaba.nacos.common.packagescan.resource.Resource;
  import com.alibaba.nacos.common.utils.ClassUtils;
  import com.alibaba.nacos.common.utils.StringUtils;
  
  import java.io.File;
  import java.io.FileNotFoundException;
  import java.net.MalformedURLException;
  import java.net.URI;
  import java.net.URISyntaxException;
  import java.net.URL;
  import java.net.URLConnection;
  
  /**
   * Copy from https://github.com/spring-projects/spring-framework.git, with less modifications
   * Utility methods for resolving resource locations to files in the
   * file system. Mainly for internal use within the framework.
   *
   * <p>Consider using Spring's Resource abstraction in the core package
   * for handling all kinds of file resources in a uniform manner.
   * method can resolve any location to a {@link Resource}
   * object, which in turn allows one to obtain a {@code java.io.File} in the
   * file system through its {@code getFile()} method.
   *
   * @author Juergen Hoeller
   * @since 1.1.5
   */
  public abstract class ResourceUtils {
  
      /**
       * Pseudo URL prefix for loading from the class path: "classpath:".
       */
      public static final String CLASSPATH_URL_PREFIX = "classpath:";
  
      /**
       * URL prefix for loading from the file system: "file:".
       */
      public static final String FILE_URL_PREFIX = "file:";
  
      /**
       * URL prefix for loading from a jar file: "jar:".
       */
      public static final String JAR_URL_PREFIX = "jar:";
  
      /**
       * URL prefix for loading from a war file on Tomcat: "war:".
       */
      public static final String WAR_URL_PREFIX = "war:";
  
      /**
       * URL protocol for a file in the file system: "file".
       */
      public static final String URL_PROTOCOL_FILE = "file";
  
      /**
       * URL protocol for an entry from a jar file: "jar".
       */
      public static final String URL_PROTOCOL_JAR = "jar";
  
      /**
       * URL protocol for an entry from a war file: "war".
       */
      public static final String URL_PROTOCOL_WAR = "war";
  
      /**
       * URL protocol for an entry from a zip file: "zip".
       */
      public static final String URL_PROTOCOL_ZIP = "zip";
  
      /**
       * URL protocol for an entry from a WebSphere jar file: "wsjar".
       */
      public static final String URL_PROTOCOL_WSJAR = "wsjar";
  
      /**
       * URL protocol for an entry from a JBoss jar file: "vfszip".
       */
      public static final String URL_PROTOCOL_VFSZIP = "vfszip";
  
      /**
       * URL protocol for a JBoss file system resource: "vfsfile".
       */
      public static final String URL_PROTOCOL_VFSFILE = "vfsfile";
  
      /**
       * URL protocol for a general JBoss VFS resource: "vfs".
       */
      public static final String URL_PROTOCOL_VFS = "vfs";
  
      /**
       * File extension for a regular jar file: ".jar".
       */
      public static final String JAR_FILE_EXTENSION = ".jar";
  
      /**
       * Separator between JAR URL and file path within the JAR: "!/".
       */
      public static final String JAR_URL_SEPARATOR = "!/";
  
      /**
       * Special separator between WAR URL and jar part on Tomcat.
       */
      public static final String WAR_URL_SEPARATOR = "*/";
  
      /**
       * Return whether the given resource location is a URL:
       * either a special "classpath" pseudo URL or a standard URL.
       *
       * @param resourceLocation the location String to check
       * @return whether the location qualifies as a URL
       * @see #CLASSPATH_URL_PREFIX
       * @see URL
       */
      public static boolean isUrl(String resourceLocation) {
          if (resourceLocation == null) {
              return false;
          }
          if (resourceLocation.startsWith(CLASSPATH_URL_PREFIX)) {
              return true;
          }
          try {
              new URL(resourceLocation);
              return true;
          } catch (MalformedURLException ex) {
              return false;
          }
      }
  
      /**
       * Resolve the given resource location to a {@code java.io.File},
       * i.e. to a file in the file system.
       *
       * <p>Does not check whether the file actually exists; simply returns
       * the File that the given location would correspond to.
       *
       * @param resourceLocation the resource location to resolve: either a
       *                         "classpath:" pseudo URL, a "file:" URL, or a plain file path
       * @return a corresponding File object
       * @throws FileNotFoundException if the resource cannot be resolved to
       *                               a file in the file system
       */
      public static File getFile(String resourceLocation) throws FileNotFoundException {
          AbstractAssert.notNull(resourceLocation, "Resource location must not be null");
          if (resourceLocation.startsWith(CLASSPATH_URL_PREFIX)) {
              String path = resourceLocation.substring(CLASSPATH_URL_PREFIX.length());
              String description = "class path resource [" + path + "]";
              ClassLoader cl = ClassUtils.getDefaultClassLoader();
              URL url = (cl != null ? cl.getResource(path) : ClassLoader.getSystemResource(path));
              if (url == null) {
                  throw new FileNotFoundException(description
                          + " cannot be resolved to absolute file path because it does not exist");
              }
              return getFile(url, description);
          }
          try {
              // try URL
              return getFile(new URL(resourceLocation));
          } catch (MalformedURLException ex) {
              // no URL -> treat as file path
              return new File(resourceLocation);
          }
      }
  
      /**
       * Resolve the given resource URL to a {@code java.io.File},
       * i.e. to a file in the file system.
       *
       * @param resourceUrl the resource URL to resolve
       * @return a corresponding File object
       * @throws FileNotFoundException if the URL cannot be resolved to
       *                               a file in the file system
       */
      public static File getFile(URL resourceUrl) throws FileNotFoundException {
          return getFile(resourceUrl, "URL");
      }
  
      /**
       * Resolve the given resource URL to a {@code java.io.File},
       * i.e. to a file in the file system.
       *
       * @param resourceUrl the resource URL to resolve
       * @param description a description of the original resource that
       *                    the URL was created for (for example, a class path location)
       * @return a corresponding File object
       * @throws FileNotFoundException if the URL cannot be resolved to
       *                               a file in the file system
       */
      public static File getFile(URL resourceUrl, String description) throws FileNotFoundException {
          AbstractAssert.notNull(resourceUrl, "Resource URL must not be null");
          if (!URL_PROTOCOL_FILE.equals(resourceUrl.getProtocol())) {
              throw new FileNotFoundException(
                      description + " cannot be resolved to absolute file path "
                              + "because it does not reside in the file system: " + resourceUrl);
          }
          try {
              return new File(toUri(resourceUrl).getSchemeSpecificPart());
          } catch (URISyntaxException ex) {
              // Fallback for URLs that are not valid URIs (should hardly ever happen).
              return new File(resourceUrl.getFile());
          }
      }
  
      /**
       * Resolve the given resource URI to a {@code java.io.File},
       * i.e. to a file in the file system.
       *
       * @param resourceUri the resource URI to resolve
       * @return a corresponding File object
       * @throws FileNotFoundException if the URL cannot be resolved to
       *                               a file in the file system
       * @since 2.5
       */
      public static File getFile(URI resourceUri) throws FileNotFoundException {
          return getFile(resourceUri, "URI");
      }
  
      /**
       * Resolve the given resource URI to a {@code java.io.File},
       * i.e. to a file in the file system.
       *
       * @param resourceUri the resource URI to resolve
       * @param description a description of the original resource that
       *                    the URI was created for (for example, a class path location)
       * @return a corresponding File object
       * @throws FileNotFoundException if the URL cannot be resolved to
       *                               a file in the file system
       * @since 2.5
       */
      public static File getFile(URI resourceUri, String description) throws FileNotFoundException {
          AbstractAssert.notNull(resourceUri, "Resource URI must not be null");
          if (!URL_PROTOCOL_FILE.equals(resourceUri.getScheme())) {
              throw new FileNotFoundException(
                      description + " cannot be resolved to absolute file path "
                              + "because it does not reside in the file system: " + resourceUri);
          }
          return new File(resourceUri.getSchemeSpecificPart());
      }
  
      /**
       * Determine whether the given URL points to a resource in the file system,
       * i.e. has protocol "file", "vfsfile" or "vfs".
       *
       * @param url the URL to check
       * @return whether the URL has been identified as a file system URL
       */
      public static boolean isFileUrl(URL url) {
          String protocol = url.getProtocol();
          return (URL_PROTOCOL_FILE.equals(protocol)
                  || URL_PROTOCOL_VFSFILE.equals(protocol)
                  || URL_PROTOCOL_VFS.equals(protocol));
      }
  
      /**
       * Determine whether the given URL points to a resource in a jar file.
       * i.e. has protocol "jar", "war, ""zip", "vfszip" or "wsjar".
       *
       * @param url the URL to check
       * @return whether the URL has been identified as a JAR URL
       */
      public static boolean isJarUrl(URL url) {
          String protocol = url.getProtocol();
          return (URL_PROTOCOL_JAR.equals(protocol) || URL_PROTOCOL_WAR.equals(protocol)
                  || URL_PROTOCOL_ZIP.equals(protocol) || URL_PROTOCOL_VFSZIP.equals(protocol)
                  || URL_PROTOCOL_WSJAR.equals(protocol));
      }
  
      /**
       * Determine whether the given URL points to a jar file itself,
       * that is, has protocol "file" and ends with the ".jar" extension.
       *
       * @param url the URL to check
       * @return whether the URL has been identified as a JAR file URL
       * @since 4.1
       */
      public static boolean isJarFileUrl(URL url) {
          return (URL_PROTOCOL_FILE.equals(url.getProtocol())
                  && url.getPath().toLowerCase().endsWith(JAR_FILE_EXTENSION));
      }
  
      /**
       * Extract the URL for the actual jar file from the given URL
       * (which may point to a resource in a jar file or to a jar file itself).
       *
       * @param jarUrl the original URL
       * @return the URL for the actual jar file
       * @throws MalformedURLException if no valid jar file URL could be extracted
       */
      public static URL extractJarFileUrl(URL jarUrl) throws MalformedURLException {
          String urlFile = jarUrl.getFile();
          int separatorIndex = urlFile.indexOf(JAR_URL_SEPARATOR);
          if (separatorIndex != -1) {
              String jarFile = urlFile.substring(0, separatorIndex);
              try {
                  return new URL(jarFile);
              } catch (MalformedURLException ex) {
                  // Probably no protocol in original jar URL, like "jar:C:/mypath/myjar.jar".
                  // This usually indicates that the jar file resides in the file system.
                  if (!jarFile.startsWith("/")) {
                      jarFile = "/" + jarFile;
                  }
                  return new URL(FILE_URL_PREFIX + jarFile);
              }
          } else {
              return jarUrl;
          }
      }
  
      /**
       * Extract the URL for the outermost archive from the given jar/war URL
       * (which may point to a resource in a jar file or to a jar file itself).
       *
       * <p>In the case of a jar file nested within a war file, this will return
       * a URL to the war file since that is the one resolvable in the file system.
       *
       * @param jarUrl the original URL
       * @return the URL for the actual jar file
       * @throws MalformedURLException if no valid jar file URL could be extracted
       * @see #extractJarFileUrl(URL)
       * @since 4.1.8
       */
      public static URL extractArchiveUrl(URL jarUrl) throws MalformedURLException {
          String urlFile = jarUrl.getFile();
  
          int endIndex = urlFile.indexOf(WAR_URL_SEPARATOR);
          if (endIndex != -1) {
              // Tomcat's "war:file:...mywar.war*/WEB-INF/lib/myjar.jar!/myentry.txt"
              String warFile = urlFile.substring(0, endIndex);
              if (URL_PROTOCOL_WAR.equals(jarUrl.getProtocol())) {
                  return new URL(warFile);
              }
              int startIndex = warFile.indexOf(WAR_URL_PREFIX);
              if (startIndex != -1) {
                  return new URL(warFile.substring(startIndex + WAR_URL_PREFIX.length()));
              }
          }
  
          // Regular "jar:file:...myjar.jar!/myentry.txt"
          return extractJarFileUrl(jarUrl);
      }
  
      /**
       * Create a URI instance for the given URL,
       * replacing spaces with "%20" URI encoding first.
       *
       * @param url the URL to convert into a URI instance
       * @return the URI instance
       * @throws URISyntaxException if the URL wasn't a valid URI
       * @see URL#toURI()
       */
      public static URI toUri(URL url) throws URISyntaxException {
          return toUri(url.toString());
      }
  
      /**
       * Create a URI instance for the given location String,
       * replacing spaces with "%20" URI encoding first.
       *
       * @param location the location String to convert into a URI instance
       * @return the URI instance
       * @throws URISyntaxException if the location wasn't a valid URI
       */
      public static URI toUri(String location) throws URISyntaxException {
          return new URI(StringUtils.replace(location, " ", "%20"));
      }
  
      /**
       * Set the {@link URLConnection#setUseCaches "useCaches"} flag on the
       * given connection, preferring {@code false} but leaving the
       * flag at {@code true} for JNLP based resources.
       *
       * @param con the URLConnection to set the flag on
       */
      public static void useCachesIfNecessary(URLConnection con) {
          con.setUseCaches(con.getClass().getSimpleName().startsWith("JNLP"));
      }
  
  }
  