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      * Returns {@code true} if the given name is a legal module name.
  67      */
  68     public static boolean isModuleName(String name) {
  69         int next;
  70         int off = 0;
  71         while ((next = name.indexOf('.', off)) != -1) {
  72             String id = name.substring(off, next);
  73             if (!isJavaIdentifier(id))
  74                 return false;
  75             off = next+1;
  76         }
  77         String last = name.substring(off);
  78         return isJavaIdentifier(last);
  79     }
  80 
  81     /**
  82      * Checks a name to ensure that it's a legal package name.
  83      *
  84      * @throws IllegalArgumentException if name is null or not a legal
  85      *         package name
  86      */
  87     public static String requirePackageName(String name) {
  88         return requireTypeName("package name", name);
  89     }
  90 
  91     /**
  92      * Returns {@code true} if the given name is a legal package name.
  93      */
  94     public static boolean isPackageName(String name) {
  95         return isTypeName(name);
  96     }
  97 
  98     /**
  99      * Checks a name to ensure that it's a legal qualified class name
 100      *
 101      * @throws IllegalArgumentException if name is null or not a legal
 102      *         qualified class name
 103      */
 104     public static String requireServiceTypeName(String name) {
 105         return requireQualifiedClassName("service type name", name);
 106     }
 107 
 108     /**
 109      * Checks a name to ensure that it's a legal qualified class name.
 110      *
 111      * @throws IllegalArgumentException if name is null or not a legal
 112      *         qualified class name
 113      */
 114     public static String requireServiceProviderName(String name) {
 115         return requireQualifiedClassName("service provider name", name);
 116     }
 117 
 118     /**
 119      * Checks a name to ensure that it's a legal qualified class name in
 120      * a named package.
 121      *
 122      * @throws IllegalArgumentException if name is null or not a legal
 123      *         qualified class name in a named package
 124      */
 125     public static String requireQualifiedClassName(String what, String name) {
 126         requireTypeName(what, name);
 127         if (name.indexOf('.') == -1)
 128             throw new IllegalArgumentException(name + ": is not a qualified name of"
 129                                                + " a Java class in a named package");
 130         return name;
 131     }
 132 
 133     /**
 134      * Returns {@code true} if the given name is a legal class name.
 135      */
 136     public static boolean isClassName(String name) {
 137         return isTypeName(name);
 138     }
 139 
 140     /**
 141      * Returns {@code true} if the given name is a legal type name.
 142      */
 143     private static boolean isTypeName(String name) {
 144         int next;
 145         int off = 0;
 146         while ((next = name.indexOf('.', off)) != -1) {
 147             String id = name.substring(off, next);
 148             if (!isJavaIdentifier(id))
 149                 return false;
 150             off = next+1;
 151         }
 152         String last = name.substring(off);
 153         return isJavaIdentifier(last);
 154     }
 155 
 156     /**
 157      * Checks if the given name is a legal type name.
 158      *
 159      * @throws IllegalArgumentException if name is null or not a legal
 160      *         type name
 161      */
 162     private static String requireTypeName(String what, String name) {
 163         if (name == null)
 164             throw new IllegalArgumentException("Null " + what);
 165         int next;
 166         int off = 0;
 167         while ((next = name.indexOf('.', off)) != -1) {
 168             String id = name.substring(off, next);
 169             if (!isJavaIdentifier(id)) {
 170                 throw new IllegalArgumentException(name + ": Invalid " + what
 171                         + ": '" + id + "' is not a Java identifier");
 172             }
 173             off = next + 1;
 174         }
 175         String last = name.substring(off);
 176         if (!isJavaIdentifier(last)) {
 177             throw new IllegalArgumentException(name + ": Invalid " + what
 178                     + ": '" + last + "' is not a Java identifier");
 179         }
 180         return name;
 181     }
 182 
 183     /**
 184      * Returns true if the given char sequence is a legal Java identifier,
 185      * otherwise false.
 186      */
 187     private static boolean isJavaIdentifier(CharSequence cs) {
 188         if (cs.length() == 0 || RESERVED.contains(cs))
 189             return false;
 190 
 191         int first = Character.codePointAt(cs, 0);
 192         if (!Character.isJavaIdentifierStart(first))
 193             return false;
 194 
 195         int i = Character.charCount(first);
 196         while (i < cs.length()) {
 197             int cp = Character.codePointAt(cs, i);
 198             if (!Character.isJavaIdentifierPart(cp))
 199                 return false;
 200             i += Character.charCount(cp);
 201         }
 202 
 203         return true;
 204     }
 205 
 206     // keywords, boolean and null literals, not allowed in identifiers
 207     private static final Set<String> RESERVED = Set.of(
 208             "abstract",
 209             "assert",
 210             "boolean",
 211             "break",
 212             "byte",
 213             "case",
 214             "catch",
 215             "char",
 216             "class",
 217             "const",
 218             "continue",
 219             "default",
 220             "do",
 221             "double",
 222             "else",
 223             "enum",
 224             "extends",
 225             "final",
 226             "finally",
 227             "float",
 228             "for",
 229             "goto",
 230             "if",
 231             "implements",
 232             "import",
 233             "instanceof",
 234             "int",
 235             "interface",
 236             "long",
 237             "native",
 238             "new",
 239             "package",
 240             "private",
 241             "protected",
 242             "public",
 243             "return",
 244             "short",
 245             "static",
 246             "strictfp",
 247             "super",
 248             "switch",
 249             "synchronized",
 250             "this",
 251             "throw",
 252             "throws",
 253             "transient",
 254             "try",
 255             "void",
 256             "volatile",
 257             "while",
 258             "true",
 259             "false",
 260             "null",
 261             "_"
 262     );
 263 }