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 /**
  29  * Utility class for checking module, package, and class names.
  30  */
  31 
  32 public final class Checks {
  33 
  34     private Checks() { }
  35 
  36     /**
  37      * Checks a name to ensure that it's a legal module name.
  38      *
  39      * @throws IllegalArgumentException if name is null or not a legal
  40      *         module name
  41      */
  42     public static String requireModuleName(String name) {
  43         if (name == null)
  44             throw new IllegalArgumentException("Null module name");
  45         int next;
  46         int off = 0;
  47         while ((next = name.indexOf('.', off)) != -1) {
  48             if (isJavaIdentifier(name, off, (next - off)) == -1) {
  49                 String id = name.substring(off, next);
  50                 throw new IllegalArgumentException(name + ": Invalid module name"
  51                         + ": '" + id + "' is not a Java identifier");
  52             }
  53             off = next+1;
  54         }
  55         int last = isJavaIdentifier(name, off, name.length() - off);
  56         if (last == -1) {
  57             String id = name.substring(off);
  58             throw new IllegalArgumentException(name + ": Invalid module name"
  59                     + ": '" + id + "' is not a Java identifier");
  60         }
  61         return name;
  62     }
  63 
  64     /**
  65      * Returns {@code true} if the given name is a legal module name.
  66      */
  67     public static boolean isModuleName(String name) {
  68         int next;
  69         int off = 0;
  70         while ((next = name.indexOf('.', off)) != -1) {
  71             if (isJavaIdentifier(name, off, (next - off)) == -1)
  72                 return false;
  73             off = next+1;
  74         }
  75         int last = isJavaIdentifier(name, off, name.length() - off);
  76         if (last == -1)
  77             return false;
  78         return true;
  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             if (isJavaIdentifier(name, off, (next - off)) == -1)
 148                 return false;
 149             off = next+1;
 150         }
 151         int count = name.length() - off;
 152         return (isJavaIdentifier(name, off, count) != -1);
 153     }
 154 
 155     /**
 156      * Checks if the given name is a legal type name.
 157      *
 158      * @throws IllegalArgumentException if name is null or not a legal
 159      *         type name
 160      */
 161     private static String requireTypeName(String what, String name) {
 162         if (name == null)
 163             throw new IllegalArgumentException("Null " + what);
 164         int next;
 165         int off = 0;
 166         while ((next = name.indexOf('.', off)) != -1) {
 167             if (isJavaIdentifier(name, off, (next - off)) == -1) {
 168                 String id = name.substring(off, next);
 169                 throw new IllegalArgumentException(name + ": Invalid " + what
 170                         + ": '" + id + "' is not a Java identifier");
 171             }
 172             off = next + 1;
 173         }
 174         if (isJavaIdentifier(name, off, name.length() - off) == -1) {
 175             String id = name.substring(off, name.length());
 176             throw new IllegalArgumentException(name + ": Invalid " + what
 177                     + ": '" + id + "' is not a Java identifier");
 178         }
 179         return name;
 180     }
 181 
 182     /**
 183      * Returns {@code true} if the last character of the given name is legal
 184      * as the last character of a module name.
 185      *
 186      * @throws IllegalArgumentException if name is empty
 187      */
 188     public static boolean hasLegalModuleNameLastCharacter(String name) {
 189         if (name.isEmpty())
 190             throw new IllegalArgumentException("name is empty");
 191         int len = name.length();
 192         if (isASCIIString(name)) {
 193             char c = name.charAt(len-1);
 194             return Character.isJavaIdentifierStart(c);
 195         } else {
 196             int i = 0;
 197             int cp = -1;
 198             while (i < len) {
 199                 cp = name.codePointAt(i);
 200                 i += Character.charCount(cp);
 201             }
 202             return Character.isJavaIdentifierStart(cp);
 203         }
 204     }
 205 
 206     /**
 207      * Returns true if the given string only contains ASCII characters.
 208      */
 209     private static boolean isASCIIString(String s) {
 210         int i = 0;
 211         while (i < s.length()) {
 212             int c = s.charAt(i);
 213             if (c > 0x7F)
 214                 return false;
 215             i++;
 216         }
 217         return true;
 218     }
 219 
 220     /**
 221      * Checks if a char sequence is a legal Java identifier, returning the code
 222      * point of the last character if legal or {@code -1} if not legal.
 223      */
 224     private static int isJavaIdentifier(CharSequence cs, int offset, int count) {
 225         if (count == 0)
 226             return -1;
 227         int first = Character.codePointAt(cs, offset);
 228         if (!Character.isJavaIdentifierStart(first))
 229             return -1;
 230 
 231         int cp = first;
 232         int i = Character.charCount(first);
 233         while (i < count) {
 234             cp = Character.codePointAt(cs, offset+i);
 235             if (!Character.isJavaIdentifierPart(cp))
 236                 return -1;
 237             i += Character.charCount(cp);
 238         }
 239 
 240         return cp;
 241     }
 242 }