1 /*
   2  * Copyright (c) 2005, 2016, 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 javax.lang.model;
  27 
  28 import java.util.Collections;
  29 import java.util.Set;
  30 import java.util.HashSet;
  31 
  32 /**
  33  * Source versions of the Java™ programming language.
  34  *
  35  * See the appropriate edition of
  36  * <cite>The Java&trade; Language Specification</cite>
  37  * for information about a particular source version.
  38  *
  39  * <p>Note that additional source version constants will be added to
  40  * model future releases of the language.
  41  *
  42  * @author Joseph D. Darcy
  43  * @author Scott Seligman
  44  * @author Peter von der Ah&eacute;
  45  * @since 1.6
  46  */
  47 public enum SourceVersion {
  48     /*
  49      * Summary of language evolution
  50      * 1.1: nested classes
  51      * 1.2: strictfp
  52      * 1.3: no changes
  53      * 1.4: assert
  54      * 1.5: annotations, generics, autoboxing, var-args...
  55      * 1.6: no changes
  56      * 1.7: diamond syntax, try-with-resources, etc.
  57      * 1.8: lambda expressions and default methods
  58      *   9: modules, small cleanups to 1.7 and 1.8 changes
  59      */
  60 
  61     /**
  62      * The original version.
  63      *
  64      * The language described in
  65      * <cite>The Java&trade; Language Specification, First Edition</cite>.
  66      */
  67     RELEASE_0,
  68 
  69     /**
  70      * The version recognized by the Java Platform 1.1.
  71      *
  72      * The language is {@code RELEASE_0} augmented with nested classes as described in the 1.1 update to
  73      * <cite>The Java&trade; Language Specification, First Edition</cite>.
  74      */
  75     RELEASE_1,
  76 
  77     /**
  78      * The version recognized by the Java 2 Platform, Standard Edition,
  79      * v 1.2.
  80      *
  81      * The language described in
  82      * <cite>The Java&trade; Language Specification,
  83      * Second Edition</cite>, which includes the {@code
  84      * strictfp} modifier.
  85      */
  86     RELEASE_2,
  87 
  88     /**
  89      * The version recognized by the Java 2 Platform, Standard Edition,
  90      * v 1.3.
  91      *
  92      * No major changes from {@code RELEASE_2}.
  93      */
  94     RELEASE_3,
  95 
  96     /**
  97      * The version recognized by the Java 2 Platform, Standard Edition,
  98      * v 1.4.
  99      *
 100      * Added a simple assertion facility.
 101      */
 102     RELEASE_4,
 103 
 104     /**
 105      * The version recognized by the Java 2 Platform, Standard
 106      * Edition 5.0.
 107      *
 108      * The language described in
 109      * <cite>The Java&trade; Language Specification,
 110      * Third Edition</cite>.  First release to support
 111      * generics, annotations, autoboxing, var-args, enhanced {@code
 112      * for} loop, and hexadecimal floating-point literals.
 113      */
 114     RELEASE_5,
 115 
 116     /**
 117      * The version recognized by the Java Platform, Standard Edition
 118      * 6.
 119      *
 120      * No major changes from {@code RELEASE_5}.
 121      */
 122     RELEASE_6,
 123 
 124     /**
 125      * The version recognized by the Java Platform, Standard Edition
 126      * 7.
 127      *
 128      * Additions in this release include, diamond syntax for
 129      * constructors, {@code try}-with-resources, strings in switch,
 130      * binary literals, and multi-catch.
 131      * @since 1.7
 132      */
 133     RELEASE_7,
 134 
 135     /**
 136      * The version recognized by the Java Platform, Standard Edition
 137      * 8.
 138      *
 139      * Additions in this release include lambda expressions and default methods.
 140      * @since 1.8
 141      */
 142     RELEASE_8,
 143 
 144     /**
 145      * The version recognized by the Java Platform, Standard Edition
 146      * 9.
 147      *
 148      * Additions in this release include modules and removal of a
 149      * single underscore from the set of legal identifier names.
 150      *
 151      * @since 9
 152      */
 153      RELEASE_9;
 154 
 155     // Note that when adding constants for newer releases, the
 156     // behavior of latest() and latestSupported() must be updated too.
 157 
 158     /**
 159      * Returns the latest source version that can be modeled.
 160      *
 161      * @return the latest source version that can be modeled
 162      */
 163     public static SourceVersion latest() {
 164         return RELEASE_9;
 165     }
 166 
 167     private static final SourceVersion latestSupported = getLatestSupported();
 168 
 169     private static SourceVersion getLatestSupported() {
 170         try {
 171             String specVersion = System.getProperty("java.specification.version");
 172 
 173             switch (specVersion) {
 174                 case "9":
 175                 case "1.9":
 176                     return RELEASE_9;
 177                 case "1.8":
 178                     return RELEASE_8;
 179                 case "1.7":
 180                     return RELEASE_7;
 181                 case "1.6":
 182                     return RELEASE_6;
 183             }
 184         } catch (SecurityException se) {}
 185 
 186         return RELEASE_5;
 187     }
 188 
 189     /**
 190      * Returns the latest source version fully supported by the
 191      * current execution environment.  {@code RELEASE_5} or later must
 192      * be returned.
 193      *
 194      * @return the latest source version that is fully supported
 195      */
 196     public static SourceVersion latestSupported() {
 197         return latestSupported;
 198     }
 199 
 200     /**
 201      * Returns whether or not {@code name} is a syntactically valid
 202      * identifier (simple name) or keyword in the latest source
 203      * version.  The method returns {@code true} if the name consists
 204      * of an initial character for which {@link
 205      * Character#isJavaIdentifierStart(int)} returns {@code true},
 206      * followed only by characters for which {@link
 207      * Character#isJavaIdentifierPart(int)} returns {@code true}.
 208      * This pattern matches regular identifiers, keywords, and the
 209      * literals {@code "true"}, {@code "false"}, and {@code "null"}.
 210      * The method returns {@code false} for all other strings.
 211      *
 212      * @param name the string to check
 213      * @return {@code true} if this string is a
 214      * syntactically valid identifier or keyword, {@code false}
 215      * otherwise.
 216      */
 217     public static boolean isIdentifier(CharSequence name) {
 218         String id = name.toString();
 219 
 220         if (id.length() == 0) {
 221             return false;
 222         }
 223         int cp = id.codePointAt(0);
 224         if (!Character.isJavaIdentifierStart(cp)) {
 225             return false;
 226         }
 227         for (int i = Character.charCount(cp);
 228                 i < id.length();
 229                 i += Character.charCount(cp)) {
 230             cp = id.codePointAt(i);
 231             if (!Character.isJavaIdentifierPart(cp)) {
 232                 return false;
 233             }
 234         }
 235         return true;
 236     }
 237 
 238     /**
 239      * Returns whether or not {@code name} is a syntactically valid
 240      * qualified name in the latest source version.  Unlike {@link
 241      * #isIdentifier isIdentifier}, this method returns {@code false}
 242      * for keywords and literals.
 243      *
 244      * @param name the string to check
 245      * @return {@code true} if this string is a
 246      * syntactically valid name, {@code false} otherwise.
 247      * @jls 6.2 Names and Identifiers
 248      */
 249     public static boolean isName(CharSequence name) {
 250         return isName(name, latest());
 251     }
 252 
 253     /**
 254      * Returns whether or not {@code name} is a syntactically valid
 255      * qualified name in the given source version.  Unlike {@link
 256      * #isIdentifier isIdentifier}, this method returns {@code false}
 257      * for keywords and literals.
 258      *
 259      * @param name the string to check
 260      * @param version the version to use
 261      * @return {@code true} if this string is a
 262      * syntactically valid name, {@code false} otherwise.
 263      * @jls 6.2 Names and Identifiers
 264      * @since 9
 265      */
 266     public static boolean isName(CharSequence name, SourceVersion version) {
 267         String id = name.toString();
 268 
 269         for(String s : id.split("\\.", -1)) {
 270             if (!isIdentifier(s) || isKeyword(s, version))
 271                 return false;
 272         }
 273         return true;
 274     }
 275 
 276     private final static Set<String> keywords;
 277     static {
 278         Set<String> s = new HashSet<>();
 279         String [] kws = {
 280             "abstract", "continue",     "for",          "new",          "switch",
 281             "assert",   "default",      "if",           "package",      "synchronized",
 282             "boolean",  "do",           "goto",         "private",      "this",
 283             "break",    "double",       "implements",   "protected",    "throw",
 284             "byte",     "else",         "import",       "public",       "throws",
 285             "case",     "enum",         "instanceof",   "return",       "transient",
 286             "catch",    "extends",      "int",          "short",        "try",
 287             "char",     "final",        "interface",    "static",       "void",
 288             "class",    "finally",      "long",         "strictfp",     "volatile",
 289             "const",    "float",        "native",       "super",        "while",
 290             // literals
 291             "null",     "true",         "false",
 292             "_" // keyword as of 9
 293         };
 294         for(String kw : kws)
 295             s.add(kw);
 296         keywords = Collections.unmodifiableSet(s);
 297     }
 298 
 299     /**
 300      * Returns whether or not {@code s} is a keyword or literal in the
 301      * latest source version.
 302      *
 303      * @param s the string to check
 304      * @return {@code true} if {@code s} is a keyword or literal, {@code false} otherwise.
 305      */
 306     public static boolean isKeyword(CharSequence s) {
 307         return keywords.contains(s.toString());
 308     }
 309 
 310     /**
 311      * Returns whether or not {@code s} is a keyword or literal in the
 312      * given source version.
 313      *
 314      * @param s the string to check
 315      * @param version the version to use
 316      * @return {@code true} if {@code s} is a keyword or literal, {@code false} otherwise.
 317      * @since 9
 318      */
 319     public static boolean isKeyword(CharSequence s, SourceVersion version) {
 320         String id = s.toString();
 321         if ("_".equals(id)) {
 322             return version.compareTo(RELEASE_9) >= 0;
 323         } else {
 324             return keywords.contains(id);
 325         }
 326     }
 327 }