1 /*
   2  * Copyright (c) 2005, 2015, 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      * 1.9: To be determined
  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      * @since 9
 149      */
 150      RELEASE_9;
 151 
 152     // Note that when adding constants for newer releases, the
 153     // behavior of latest() and latestSupported() must be updated too.
 154 
 155     /**
 156      * Returns the latest source version that can be modeled.
 157      *
 158      * @return the latest source version that can be modeled
 159      */
 160     public static SourceVersion latest() {
 161         return RELEASE_9;
 162     }
 163 
 164     private static final SourceVersion latestSupported = getLatestSupported();
 165 
 166     private static SourceVersion getLatestSupported() {
 167         try {
 168             String specVersion = System.getProperty("java.specification.version");
 169 
 170             switch (specVersion) {
 171                 case "9":
 172                 case "1.9":
 173                     return RELEASE_9;
 174                 case "1.8":
 175                     return RELEASE_8;
 176                 case "1.7":
 177                     return RELEASE_7;
 178                 case "1.6":
 179                     return RELEASE_6;
 180             }
 181         } catch (SecurityException se) {}
 182 
 183         return RELEASE_5;
 184     }
 185 
 186     /**
 187      * Returns the latest source version fully supported by the
 188      * current execution environment.  {@code RELEASE_5} or later must
 189      * be returned.
 190      *
 191      * @return the latest source version that is fully supported
 192      */
 193     public static SourceVersion latestSupported() {
 194         return latestSupported;
 195     }
 196 
 197     /**
 198      * Returns whether or not {@code name} is a syntactically valid
 199      * identifier (simple name) or keyword in the latest source
 200      * version.  The method returns {@code true} if the name consists
 201      * of an initial character for which {@link
 202      * Character#isJavaIdentifierStart(int)} returns {@code true},
 203      * followed only by characters for which {@link
 204      * Character#isJavaIdentifierPart(int)} returns {@code true}.
 205      * This pattern matches regular identifiers, keywords, and the
 206      * literals {@code "true"}, {@code "false"}, and {@code "null"}.
 207      * The method returns {@code false} for all other strings.
 208      *
 209      * @param name the string to check
 210      * @return {@code true} if this string is a
 211      * syntactically valid identifier or keyword, {@code false}
 212      * otherwise.
 213      */
 214     public static boolean isIdentifier(CharSequence name) {
 215         String id = name.toString();
 216 
 217         if (id.length() == 0) {
 218             return false;
 219         }
 220         int cp = id.codePointAt(0);
 221         if (!Character.isJavaIdentifierStart(cp)) {
 222             return false;
 223         }
 224         for (int i = Character.charCount(cp);
 225                 i < id.length();
 226                 i += Character.charCount(cp)) {
 227             cp = id.codePointAt(i);
 228             if (!Character.isJavaIdentifierPart(cp)) {
 229                 return false;
 230             }
 231         }
 232         return true;
 233     }
 234 
 235     /**
 236      *  Returns whether or not {@code name} is a syntactically valid
 237      *  qualified name in the latest source version.  Unlike {@link
 238      *  #isIdentifier isIdentifier}, this method returns {@code false}
 239      *  for keywords and literals.
 240      *
 241      * @param name the string to check
 242      * @return {@code true} if this string is a
 243      * syntactically valid name, {@code false} otherwise.
 244      * @jls 6.2 Names and Identifiers
 245      */
 246     public static boolean isName(CharSequence name) {
 247         String id = name.toString();
 248 
 249         for(String s : id.split("\\.", -1)) {
 250             if (!isIdentifier(s) || isKeyword(s))
 251                 return false;
 252         }
 253         return true;
 254     }
 255 
 256     private final static Set<String> keywords;
 257     static {
 258         Set<String> s = new HashSet<>();
 259         String [] kws = {
 260             "abstract", "continue",     "for",          "new",          "switch",
 261             "assert",   "default",      "if",           "package",      "synchronized",
 262             "boolean",  "do",           "goto",         "private",      "this",
 263             "break",    "double",       "implements",   "protected",    "throw",
 264             "byte",     "else",         "import",       "public",       "throws",
 265             "case",     "enum",         "instanceof",   "return",       "transient",
 266             "catch",    "extends",      "int",          "short",        "try",
 267             "char",     "final",        "interface",    "static",       "void",
 268             "class",    "finally",      "long",         "strictfp",     "volatile",
 269             "const",    "float",        "native",       "super",        "while",
 270             // literals
 271             "null",     "true",         "false"
 272         };
 273         for(String kw : kws)
 274             s.add(kw);
 275         keywords = Collections.unmodifiableSet(s);
 276     }
 277 
 278     /**
 279      *  Returns whether or not {@code s} is a keyword or literal in the
 280      *  latest source version.
 281      *
 282      * @param s the string to check
 283      * @return {@code true} if {@code s} is a keyword or literal, {@code false} otherwise.
 284      */
 285     public static boolean isKeyword(CharSequence s) {
 286         return keywords.contains(s.toString());
 287     }
 288 }