package de.thetaphi.forbiddenapis;

/*
 * (C) Copyright Uwe Schindler (Generics Policeman) and others.
 *
 * 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
 *
 * 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.net.URL;
import java.util.Locale;
import java.util.regex.Pattern;

/** Some static utilities for analyzing with ASM, also constants. */
public final class AsmUtils {

  private AsmUtils() {}
  
  private static final String REGEX_META_CHARS = ".^$+{}[]|()\\";
  
  /** Package prefixes of documented Java API (extracted from Javadocs of Java 8). */
  private static final Pattern PORTABLE_RUNTIME_PACKAGE_PATTERN = makePkgPrefixPattern("java", "javax", "org.ietf.jgss", "org.omg", "org.w3c.dom", "org.xml.sax");
  
  /** Pattern that matches all module names, which are shipped by default in Java.
   * (see: {@code http://openjdk.java.net/projects/jigsaw/spec/sotms/}):
   * The remaining platform modules will share the 'java.' name prefix and are likely to include,
   * e.g., java.sql for database connectivity, java.xml for XML processing, and java.logging for
   * logging. Modules that are not defined in the Java SE 9 Platform Specification but instead
   * specific to the JDK will, by convention, share the 'jdk.' name prefix.
   */
  private static final Pattern RUNTIME_MODULES_PATTERN = makePkgPrefixPattern("java", "jdk");
  
  private static Pattern makePkgPrefixPattern(String... prefixes) {
    final StringBuilder sb = new StringBuilder();
    boolean first = true;
    for (final String p : prefixes) {
      sb.append(first ? '(' : '|').append(Pattern.quote(p));
      first = false;
    }
    sb.append(")").append(Pattern.quote(".")).append(".*");
    return Pattern.compile(sb.toString());
  }
  
  private static boolean isRegexMeta(char c) {
    return REGEX_META_CHARS.indexOf(c) != -1;
  }

  /** Returns true, if the given binary class name (dotted) is part of the documented and portable Java APIs. */
  public static boolean isPortableRuntimeClass(String className) {
    return PORTABLE_RUNTIME_PACKAGE_PATTERN.matcher(className).matches();
  }
  
  /** Returns true, if the given Java 9 module name is part of the runtime (no custom 3rd party module).
   * @param module the module name or {@code null}, if in unnamed module
   */
  public static boolean isRuntimeModule(String module) {
    return module != null && RUNTIME_MODULES_PATTERN.matcher(module).matches();
  }
  
  /** Converts a binary class name (dotted) to the JVM internal one (slashed). Only accepts valid class names, no arrays. */
  public static String binaryToInternal(String clazz) {
    if (clazz.indexOf('/') >= 0 || clazz.indexOf('[') >= 0) {
      throw new IllegalArgumentException(String.format(Locale.ENGLISH, "'%s' is not a valid binary class name.", clazz));
    }
    return clazz.replace('.', '/');
  }
  
  /** Converts a binary class name to a &quot;{@code .class}&quot; file resource name. */
  public static String getClassResourceName(String clazz) {
    return binaryToInternal(clazz).concat(".class");
  }
  
  /** Returns true is a string is a glob pattern */
  public static boolean isGlob(String s) {
    return s.indexOf('*') >= 0 || s.indexOf('?') >= 0;
  }
  
  /** Returns a regex pattern that matches on any of the globs on class names (e.g., "sun.misc.**") */
  public static Pattern glob2Pattern(String... globs) {
    final StringBuilder regex = new StringBuilder();
    boolean needOr = false;
    for (String glob : globs) {
      if (needOr) {
        regex.append('|');
      }
      int i = 0, len = glob.length();
      while (i < len) {
        char c = glob.charAt(i++);
        switch (c) {
          case '*':
            if (i < len && glob.charAt(i) == '*') {
              // crosses package boundaries
              regex.append(".*");
              i++;
            } else {
              // do not cross package boundaries
              regex.append("[^.]*");
            }
            break;
            
          case '?':
            // do not cross package boundaries
            regex.append("[^.]");
            break;
          
          default:
            if (isRegexMeta(c)) {
              regex.append('\\');
            }
            regex.append(c);
        }
      }
      needOr = true;
    }
    return Pattern.compile(regex.toString(), 0);
  }
  
  /** Returns the module name from a {@code jrt:/} URL; returns null if no module given or wrong URL type. */
  public static String getModuleName(URL jrtUrl) {
    if (!"jrt".equalsIgnoreCase(jrtUrl.getProtocol())) {
      return null;
    }
    String mod = jrtUrl.getPath();
    if (mod != null && mod.length() >= 1) {
      mod = mod.substring(1);
      int p = mod.indexOf('/');
      if (p >= 0) {
        mod = mod.substring(0, p);
      }
      return mod.isEmpty() ? null : mod;
    }
    return null;
  }

}

@@ -655,16 +655,52 @@
 e.%0A */%0A%0A
+import java.net.URISyntaxException;%0A
 import j
@@ -5059,24 +5059,99 @@
 null;%0A    %7D%0A
+    try %7B%0A      // use URI class to also decode path and remove escapes:%0A  
     String m
@@ -5158,24 +5158,32 @@
 od = jrtUrl.
+toURI().
 getPath();%0A 
@@ -5181,24 +5181,26 @@
 Path();%0A    
+  
 if (mod != n
@@ -5229,24 +5229,26 @@
  1) %7B%0A      
+  
 mod = mod.su
@@ -5259,16 +5259,18 @@
 ing(1);%0A
+  
       in
@@ -5295,24 +5295,26 @@
 '/');%0A      
+  
 if (p %3E= 0) 
@@ -5311,24 +5311,26 @@
  (p %3E= 0) %7B%0A
+  
         mod 
@@ -5354,26 +5354,30 @@
 , p);%0A      
+ 
+ 
 %7D%0A
+  
       return
@@ -5405,26 +5405,30 @@
  : mod;%0A    
-%7D%0A
+  %7D%0A  
     return n
@@ -5432,15 +5432,79 @@
 n null;%0A
+    %7D catch (URISyntaxException use) %7B%0A      return null;%0A    %7D%0A
   %7D%0A%0A%7D%0A

Use URI to get decoded patch before extracting module name