1 /*
   2  * Copyright (c) 2009, 2017, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package jdk.internal.module;
  27 
  28 import java.util.Set;
  29 
  30 /**
  31  * Utility class for checking module, package, and class names.
  32  */
  33 
  34 public final class Checks {
  35 
  36     private Checks() { }
  37 
  38     /**
  39      * Checks a name to ensure that it's a legal module name.
  40      *
  41      * @throws IllegalArgumentException if name is null or not a legal
  42      *         module name
  43      */
  44     public static String requireModuleName(String name) {
  45         if (name == null)
  46             throw new IllegalArgumentException("Null module name");
  47         int next;
  48         int off = 0;
  49         while ((next = name.indexOf('.', off)) != -1) {
  50             String id = name.substring(off, next);
  51             if (!isJavaIdentifier(id)) {
  52                 throw new IllegalArgumentException(name + ": Invalid module name"
  53                         + ": '" + id + "' is not a Java identifier");
  54             }
  55             off = next+1;
  56         }
  57         String last = name.substring(off);
  58         if (!isJavaIdentifier(last)) {
  59             throw new IllegalArgumentException(name + ": Invalid module name"
  60                     + ": '" + last + "' is not a Java identifier");
  61         }
  62         return name;
  63     }
  64 
  65     /**
  66      * Checks a name to ensure that it's a legal package name.
  67      *
  68      * @throws IllegalArgumentException if name is null or not a legal
  69      *         package name
  70      */
  71     public static String requirePackageName(String name) {
  72         return requireTypeName("package name", name);
  73     }
  74 
  75     /**
  76      * Returns {@code true} if the given name is a legal package name.
  77      */
  78     public static boolean isPackageName(String name) {
  79         return isTypeName(name);
  80     }
  81 
  82     /**
  83      * Checks a name to ensure that it's a legal qualified class name
  84      *
  85      * @throws IllegalArgumentException if name is null or not a legal
  86      *         qualified class name
  87      */
  88     public static String requireServiceTypeName(String name) {
  89         return requireQualifiedClassName("service type name", name);
  90     }
  91 
  92     /**
  93      * Checks a name to ensure that it's a legal qualified class name.
  94      *
  95      * @throws IllegalArgumentException if name is null or not a legal
  96      *         qualified class name
  97      */
  98     public static String requireServiceProviderName(String name) {
  99         return requireQualifiedClassName("service provider name", name);
 100     }
 101 
 102     /**
 103      * Checks a name to ensure that it's a legal qualified class name in
 104      * a named package.
 105      *
 106      * @throws IllegalArgumentException if name is null or not a legal
 107      *         qualified class name in a named package
 108      */
 109     public static String requireQualifiedClassName(String what, String name) {
 110         requireTypeName(what, name);
 111         if (name.indexOf('.') == -1)
 112             throw new IllegalArgumentException(name + ": is not a qualified name of"
 113                                                + " a Java class in a named package");
 114         return name;
 115     }
 116 
 117     /**
 118      * Returns {@code true} if the given name is a legal class name.
 119      */
 120     public static boolean isClassName(String name) {
 121         return isTypeName(name);
 122     }
 123 
 124     /**
 125      * Returns {@code true} if the given name is a legal type name.
 126      */
 127     private static boolean isTypeName(String name) {
 128         int next;
 129         int off = 0;
 130         while ((next = name.indexOf('.', off)) != -1) {
 131             String id = name.substring(off, next);
 132             if (!isJavaIdentifier(id))
 133                 return false;
 134             off = next+1;
 135         }
 136         String last = name.substring(off);
 137         return isJavaIdentifier(last);
 138     }
 139 
 140     /**
 141      * Checks if the given name is a legal type name.
 142      *
 143      * @throws IllegalArgumentException if name is null or not a legal
 144      *         type name
 145      */
 146     private static String requireTypeName(String what, String name) {
 147         if (name == null)
 148             throw new IllegalArgumentException("Null " + what);
 149         int next;
 150         int off = 0;
 151         while ((next = name.indexOf('.', off)) != -1) {
 152             String id = name.substring(off, next);
 153             if (!isJavaIdentifier(id)) {
 154                 throw new IllegalArgumentException(name + ": Invalid " + what
 155                         + ": '" + id + "' is not a Java identifier");
 156             }
 157             off = next + 1;
 158         }
 159         String last = name.substring(off);
 160         if (!isJavaIdentifier(last)) {
 161             throw new IllegalArgumentException(name + ": Invalid " + what
 162                     + ": '" + last + "' is not a Java identifier");
 163         }
 164         return name;
 165     }
 166 
 167     /**
 168      * Returns true if the given string is a legal Java identifier,
 169      * otherwise false.
 170      */
 171     private static boolean isJavaIdentifier(String str) {
 172         if (str.isEmpty() || RESERVED.contains(str))
 173             return false;
 174 
 175         int first = Character.codePointAt(str, 0);
 176         if (!Character.isJavaIdentifierStart(first))
 177             return false;
 178 
 179         int i = Character.charCount(first);
 180         while (i < str.length()) {
 181             int cp = Character.codePointAt(str, i);
 182             if (!Character.isJavaIdentifierPart(cp))
 183                 return false;
 184             i += Character.charCount(cp);
 185         }
 186 
 187         return true;
 188     }
 189 
 190     // keywords, boolean and null literals, not allowed in identifiers
 191     private static final Set<String> RESERVED = Set.of(
 192             "abstract",
 193             "assert",
 194             "boolean",
 195             "break",
 196             "byte",
 197             "case",
 198             "catch",
 199             "char",
 200             "class",
 201             "const",
 202             "continue",
 203             "default",
 204             "do",
 205             "double",
 206             "else",
 207             "enum",
 208             "extends",
 209             "final",
 210             "finally",
 211             "float",
 212             "for",
 213             "goto",
 214             "if",
 215             "implements",
 216             "import",
 217             "instanceof",
 218             "int",
 219             "interface",
 220             "long",
 221             "native",
 222             "new",
 223             "package",
 224             "private",
 225             "protected",
 226             "public",
 227             "return",
 228             "short",
 229             "static",
 230             "strictfp",
 231             "super",
 232             "switch",
 233             "synchronized",
 234             "this",
 235             "throw",
 236             "throws",
 237             "transient",
 238             "try",
 239             "void",
 240             "volatile",
 241             "while",
 242             "true",
 243             "false",
 244             "null",
 245             "_"
 246     );
 247 }