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