Clover coverage report - PLT Utilities Test Coverage (plt-20120304-r5436)
Coverage timestamp: Sat Mar 3 2012 22:01:56 CST
file stats: LOC: 450   Methods: 35
NCLOC: 294   Classes: 4
 
 Source file Conditionals Statements Methods TOTAL
JavaVersion.java 46.9% 51% 68.6% 51.6%
coverage coverage
 1    /*BEGIN_COPYRIGHT_BLOCK*
 2   
 3    PLT Utilities BSD License
 4   
 5    Copyright (c) 2007-2010 JavaPLT group at Rice University
 6    All rights reserved.
 7   
 8    Developed by: Java Programming Languages Team
 9    Rice University
 10    http://www.cs.rice.edu/~javaplt/
 11   
 12    Redistribution and use in source and binary forms, with or without modification, are permitted
 13    provided that the following conditions are met:
 14   
 15    - Redistributions of source code must retain the above copyright notice, this list of conditions
 16    and the following disclaimer.
 17    - Redistributions in binary form must reproduce the above copyright notice, this list of
 18    conditions and the following disclaimer in the documentation and/or other materials provided
 19    with the distribution.
 20    - Neither the name of the JavaPLT group, Rice University, nor the names of the library's
 21    contributors may be used to endorse or promote products derived from this software without
 22    specific prior written permission.
 23   
 24    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
 25    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 26    FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND
 27    CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 28    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 29    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 30    IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 31    OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 32   
 33    *END_COPYRIGHT_BLOCK*/
 34   
 35    package edu.rice.cs.plt.reflect;
 36   
 37    import java.io.Serializable;
 38    import java.io.File;
 39   
 40    /** A representation of a major Java version, with methods for parsing version number strings. */
 41    public enum JavaVersion {
 42  0 UNRECOGNIZED { public String versionString() { return "?"; } },
 43  1 JAVA_1_1 { public String versionString() { return "1.1"; } },
 44  1 JAVA_1_2 { public String versionString() { return "1.2"; } },
 45  1 JAVA_1_3 { public String versionString() { return "1.3"; } },
 46  15 JAVA_1_4 { public String versionString() { return "1.4"; } },
 47  22 JAVA_5 { public String versionString() { return "5"; } },
 48  26 JAVA_6 { public String versionString() { return "6"; } },
 49  18 JAVA_7 { public String versionString() { return "7"; } },
 50  1 FUTURE { public String versionString() { return ">7"; } };
 51   
 52    /**
 53    * The currently-available Java version, based on the {@code "java.class.version"} property. Ideally, a {@code true}
 54    * result for {@code JavaVersion.CURRENT.supports(v)} implies that all APIs associated with that version are
 55    * available at runtime. However, we do not attempt to (and cannot, in general) guarantee that the boot class path
 56    * or Java installation have not been modified to only support certain API classes.
 57    */
 58    public static final JavaVersion CURRENT = parseClassVersion(System.getProperty("java.class.version", ""));
 59   
 60    /** The currently-available Java version, based on the {@code "java.version"} property. */
 61    public static final JavaVersion.FullVersion CURRENT_FULL =
 62    parseFullVersion(System.getProperty("java.version", ""),
 63    System.getProperty("java.runtime.name", ""),
 64    System.getProperty("java.vm.vendor", ""),
 65    null);
 66   
 67    /**
 68    * {@code true} iff this version is at least as recent as {@code v}, and thus can be expected to
 69    * support {@code v}'s APIs (as long as the required features are backwards-compatible)
 70    */
 71  0 public boolean supports(JavaVersion v) { return compareTo(v) >= 0; }
 72   
 73    /** Produce the version number as a string */
 74    public abstract String versionString();
 75   
 76    /** Prepend {@code "Java "} to the version number string */
 77  8 public String toString() { return "Java " + versionString(); }
 78   
 79    /** Returns a FullVersion that corresponds to this JavaVersion, e.g. JAVA_6 will return a FullVersion 1.6.0_0. */
 80  8 public FullVersion fullVersion() {
 81  8 return new FullVersion(this, 0, 0, ReleaseType.STABLE, null, VendorType.UNKNOWN, "", null);
 82    }
 83   
 84    /**
 85    * Produce the {@code JavaVersion} corresponding to the given class version string. For example,
 86    * {@code "49.0"} maps to {@code JAVA_5}. If the text cannot be parsed, {@code UNRECOGNIZED} will be
 87    * returned.
 88    */
 89  5 public static JavaVersion parseClassVersion(String text) {
 90  5 int dot = text.indexOf('.');
 91  0 if (dot == -1) { return UNRECOGNIZED; }
 92  5 try {
 93  5 int major = Integer.parseInt(text.substring(0, dot));
 94  5 int minor = Integer.parseInt(text.substring(dot+1));
 95   
 96  5 return parseClassVersion(major, minor);
 97    }
 98  0 catch (NumberFormatException e) { return UNRECOGNIZED; }
 99    }
 100   
 101    /**
 102    * Produce the {@code JavaVersion} corresponding to the given class version pair. For example,
 103    * {@code 49,0} maps to {@code JAVA_5}. If the pair cannot be matched, {@code UNRECOGNIZED} will be
 104    * returned.
 105    */
 106  5 public static JavaVersion parseClassVersion(int major, int minor) {
 107  5 switch (major) {
 108  0 case 45:
 109  0 if (minor >= 3) { return JAVA_1_1; }
 110  0 else { return UNRECOGNIZED; }
 111  0 case 46: return JAVA_1_2;
 112  0 case 47: return JAVA_1_3;
 113  1 case 48: return JAVA_1_4;
 114  1 case 49: return JAVA_5;
 115  2 case 50: return JAVA_6;
 116  1 case 51: return JAVA_7;
 117    }
 118  0 return (major > 51) ? FUTURE : UNRECOGNIZED;
 119    }
 120   
 121    /**
 122    * Produce the {@code JavaVersion} corresponding to the given class file.
 123    */
 124  0 public static JavaVersion parseClassVersion(java.io.File classFile) {
 125  0 java.io.FileInputStream fis = null;
 126  0 try {
 127  0 fis = new java.io.FileInputStream(classFile);
 128  0 return parseClassVersion(fis);
 129    }
 130  0 catch(java.io.IOException ioe) { return UNRECOGNIZED; }
 131    finally {
 132  0 if (fis!=null) {
 133  0 try { fis.close(); }
 134    catch(java.io.IOException ioe) { /* ignore */ }
 135    }
 136    }
 137    }
 138   
 139    /**
 140    * Produce the {@code JavaVersion} corresponding to the given class file.
 141    */
 142  0 public static JavaVersion parseClassVersion(java.io.InputStream is) {
 143  0 java.io.DataInputStream dis = null;
 144  0 try {
 145  0 dis = new java.io.DataInputStream(is);
 146  0 int magic = dis.readInt();
 147  0 if (magic != 0xCAFEBABE) { return UNRECOGNIZED; }
 148  0 int minor = dis.readUnsignedShort();
 149  0 int major = dis.readUnsignedShort();
 150  0 return parseClassVersion(major, minor);
 151    }
 152  0 catch(java.io.IOException ioe) { return UNRECOGNIZED; }
 153    finally {
 154  0 if (dis!=null) {
 155  0 try { dis.close(); }
 156    catch(java.io.IOException ioe) { /* ignore */ }
 157    }
 158    }
 159    }
 160   
 161    /**
 162    * Produce the {@code JavaVersion.FullVersion} corresponding to the given version string. Accepts
 163    * input of the form "1.6.0", "1.4.2_10", or "1.5.0_05-ea". The underscore may be replaced by a dot.
 164    * If the text cannot be parsed, a trivial version with major version UNRECOGNIZED is returned.
 165    * The location of the JDK, which may be null, will be stored in the version.
 166    *
 167    * @see <a href="http://java.sun.com/j2se/versioning_naming.html#">The Sun version specification</a>
 168    */
 169  78 public static FullVersion parseFullVersion(String java_version,
 170    String java_runtime_name,
 171    String java_vm_vendor,
 172    File location) {
 173  78 VendorType vendor = VendorType.UNKNOWN;
 174  78 String vendorString = null;
 175   
 176  78 if (vendor == VendorType.UNKNOWN) {
 177  78 if (java_runtime_name.toLowerCase().contains("openjdk")) {
 178  11 vendor = VendorType.OPENJDK;
 179  11 vendorString = "OpenJDK";
 180    }
 181  67 else if (java_vm_vendor.toLowerCase().contains("apple")) {
 182  9 vendor = VendorType.APPLE;
 183  9 vendorString = "Apple";
 184    }
 185  58 else if (java_vm_vendor.toLowerCase().contains("sun") ||
 186    java_vm_vendor.toLowerCase().contains("oracle")) {
 187  25 vendor = VendorType.ORACLE;
 188  25 vendorString = "Sun";
 189    }
 190    }
 191   
 192  78 String number;
 193  78 String typeString;
 194    // if version doesn't start with "1." and has only one dot, prefix with "1."
 195    // example: 6.0 --> 1.6.0
 196  78 if ((!java_version.startsWith("1.")) && (java_version.replaceAll("[^\\.]","").length()==1)) {
 197  0 java_version = "1."+java_version;
 198    }
 199  78 int dash = java_version.indexOf('-');
 200  71 if (dash == -1) { number = java_version; typeString = null; }
 201  7 else { number = java_version.substring(0, dash); typeString = java_version.substring(dash+1); }
 202   
 203  78 int dot1 = number.indexOf('.');
 204  78 if (dot1 == -1) {
 205  0 try {
 206  0 int feature = Integer.parseInt(number);
 207   
 208  0 ReleaseType type;
 209  0 if (typeString == null) { type = ReleaseType.STABLE; }
 210  0 else if (typeString.startsWith("ea")) { type = ReleaseType.EARLY_ACCESS; }
 211  0 else if (typeString.startsWith("beta")) { type = ReleaseType.BETA; }
 212  0 else if (typeString.startsWith("rc")) { type = ReleaseType.RELEASE_CANDIDATE; }
 213  0 else { type = ReleaseType.UNRECOGNIZED; }
 214   
 215  0 JavaVersion version = UNRECOGNIZED;
 216  0 switch (feature) {
 217  0 case 1: version = JAVA_1_1; break;
 218  0 case 2: version = JAVA_1_2; break;
 219  0 case 3: version = JAVA_1_3; break;
 220  0 case 4: version = JAVA_1_4; break;
 221  0 case 5: version = JAVA_5; break;
 222  0 case 6: version = JAVA_6; break;
 223  0 case 7: version = JAVA_7; break;
 224  0 default: if (feature > 7) { version = FUTURE; } break;
 225    }
 226  0 return new FullVersion(version, 0, 0, type, typeString, vendor, vendorString, location);
 227    }
 228    catch(NumberFormatException nfe) {
 229  0 return new FullVersion(UNRECOGNIZED, 0, 0, ReleaseType.STABLE, null, vendor, vendorString, location);
 230    }
 231    }
 232   
 233  78 int dot2 = number.indexOf('.', dot1+1);
 234  0 if (dot2 == -1) { return new FullVersion(UNRECOGNIZED, 0, 0,
 235    ReleaseType.STABLE, null,
 236    vendor, vendorString, location); }
 237  78 int underscore = number.indexOf('_', dot2+1);
 238  37 if (underscore == -1) { underscore = number.indexOf('.', dot2+1); }
 239  30 if (underscore == -1) { underscore = number.length(); }
 240  78 try {
 241  78 int major = Integer.parseInt(number.substring(0, dot1));
 242  78 int feature = Integer.parseInt(number.substring(dot1+1, dot2));
 243  78 int maintenance = Integer.parseInt(number.substring(dot2+1, underscore));
 244  78 int update = (underscore >= number.length()) ? 0 : Integer.parseInt(number.substring(underscore+1));
 245   
 246  78 ReleaseType type;
 247  71 if (typeString == null) { type = ReleaseType.STABLE; }
 248  0 else if (typeString.startsWith("ea")) { type = ReleaseType.EARLY_ACCESS; }
 249  7 else if (typeString.startsWith("beta")) { type = ReleaseType.BETA; }
 250  0 else if (typeString.startsWith("rc")) { type = ReleaseType.RELEASE_CANDIDATE; }
 251  0 else { type = ReleaseType.UNRECOGNIZED; }
 252   
 253  78 JavaVersion version = UNRECOGNIZED;
 254  78 if (major == 1) {
 255  78 switch (feature) {
 256  0 case 1: version = JAVA_1_1; break;
 257  0 case 2: version = JAVA_1_2; break;
 258  0 case 3: version = JAVA_1_3; break;
 259  14 case 4: version = JAVA_1_4; break;
 260  21 case 5: version = JAVA_5; break;
 261  26 case 6: version = JAVA_6; break;
 262  17 case 7: version = JAVA_7; break;
 263  0 default: if (feature > 7) { version = FUTURE; } break;
 264    }
 265    }
 266   
 267  78 return new FullVersion(version, maintenance, update, type, typeString, vendor, vendorString, location);
 268    }
 269    catch (NumberFormatException e) {
 270  0 return new FullVersion(UNRECOGNIZED, 0, 0, ReleaseType.STABLE, null, vendor, vendorString, location);
 271    }
 272    }
 273   
 274    /**
 275    * Produce the {@code JavaVersion.FullVersion} corresponding to the given version string. Accepts
 276    * input of the form "1.6.0", "1.4.2_10", or "1.5.0_05-ea". The underscore may be replaced by a dot.
 277    * If the text cannot be parsed, a trivial version with major version UNRECOGNIZED is returned.
 278    * The location of the JDK is null and will be stored in the version.
 279    *
 280    * @see <a href="http://java.sun.com/j2se/versioning_naming.html#">The Sun version specification</a>
 281    */
 282  65 public static FullVersion parseFullVersion(String java_version,
 283    String java_runtime_name,
 284    String java_vm_vendor) {
 285  65 return parseFullVersion(java_version, java_runtime_name, java_vm_vendor, null);
 286    }
 287   
 288    /**
 289    * Produce the {@code JavaVersion.FullVersion} corresponding to the given version string. Accepts
 290    * input of the form "1.6.0", "1.4.2_10", or "1.5.0_05-ea". The underscore may be replaced by a dot.
 291    * If the text cannot be parsed, a trivial version with major version UNRECOGNIZED is returned.
 292    *
 293    * @see <a href="http://java.sun.com/j2se/versioning_naming.html#">The Sun version specification</a>
 294    */
 295  9 public static FullVersion parseFullVersion(String text) {
 296  9 return parseFullVersion(text, "", "", null); // vendor = "unrecognized"
 297    }
 298   
 299    /**
 300    * A full Java version, implemented for the sake of comparison between version numbers.
 301    *
 302    * @see <a href="http://java.sun.com/j2se/versioning_naming.html">The Sun version specification</a>
 303    */
 304    public static class FullVersion implements Comparable<FullVersion>, Serializable {
 305    private JavaVersion _majorVersion;
 306    private int _maintenance;
 307    private int _update;
 308    private ReleaseType _type;
 309    private String _typeString;
 310    private VendorType _vendor;
 311    private String _vendorString;
 312    private File _location; // may be null
 313   
 314    /** Assumes {@code typeString} is {@code null} iff {@code type} is {@code STABLE} */
 315  86 private FullVersion(JavaVersion majorVersion, int maintenance, int update, ReleaseType type,
 316    String typeString, VendorType vendor, String vendorString, File location) {
 317  86 _majorVersion = majorVersion;
 318  86 _maintenance = maintenance;
 319  86 _update = update;
 320  86 _type = type;
 321  86 _typeString = typeString;
 322  86 _vendor = vendor;
 323  86 _vendorString = vendorString;
 324  86 _location = location;
 325    }
 326   
 327    /** Get the major version associated with this full version */
 328  42 public JavaVersion majorVersion() { return _majorVersion; }
 329   
 330    /** Get a full version with the maintenance, update and release type zeroed out. */
 331  0 public FullVersion onlyMajorVersionAndVendor() {
 332  0 return new FullVersion(_majorVersion, 0, 0, ReleaseType.STABLE, null, _vendor, _vendorString, _location);
 333    }
 334   
 335    /** Get the maintenance associated with this full version */
 336  0 public int maintenance() { return _maintenance; }
 337   
 338    /** Get the update associated with this full version */
 339  0 public int update() { return _update; }
 340   
 341    /** Get the update associated with this full version */
 342  0 public ReleaseType release() { return _type; }
 343   
 344    /** Get the vendor associated with this full version */
 345  13 public VendorType vendor() { return _vendor; }
 346   
 347  0 public File location() { return _location; }
 348   
 349    /** Convenience method calling {@code majorVersion().supports(v)} */
 350  0 public boolean supports(JavaVersion v) { return _majorVersion.supports(v); }
 351   
 352    /**
 353    * Compare two versions. Major, maintenance, and update numbers are ordered sequentially. When comparing
 354    * two versions that are otherwise equivalent, early access releases precede betas, followed by
 355    * release candidates and stable releases. Within the release types, Unrecognized < OpenJDK < Apple < Oracle.
 356    * Versions with unknown vendor are only equal if their locations are also equal.
 357    */
 358  165 public int compareTo(FullVersion v) {
 359  165 int result = _majorVersion.compareTo(v._majorVersion);
 360  165 if (result == 0) {
 361  66 result = _maintenance - v._maintenance;
 362  66 if (result == 0) {
 363  45 result = _update - v._update;
 364  45 if (result == 0) {
 365  30 result = _type.compareTo(v._type);
 366  30 if (result == 0) {
 367  23 result = _vendor.compareTo(v._vendor);
 368  23 if (result == 0 && !_type.equals(ReleaseType.STABLE)) {
 369  0 result = _typeString.compareTo(v._typeString);
 370  0 if (result == 0 && _vendor.equals(VendorType.UNKNOWN)) {
 371  0 if (_location == null) {
 372  0 if (v._location == null) return 0;
 373  0 else return -1;
 374    }
 375    else {
 376  0 if (v._location == null) return 1;
 377  0 else return _location.compareTo(v._location);
 378    }
 379    }
 380    }
 381  23 else if (result == 0 && _vendor.equals(VendorType.UNKNOWN)) {
 382  5 if (_location == null) {
 383  0 if (v._location == null) return 0;
 384  0 else return -1;
 385    }
 386    else {
 387  2 if (v._location == null) return 1;
 388  3 else return _location.compareTo(v._location);
 389    }
 390    }
 391    }
 392    }
 393    }
 394    }
 395  160 return result;
 396    }
 397   
 398  77 public boolean equals(Object o) {
 399  77 if (this == o) { return true; }
 400  0 else if (!(o instanceof FullVersion)) { return false; }
 401    else {
 402  0 FullVersion v = (FullVersion) o;
 403  0 return _majorVersion.equals(v._majorVersion) &&
 404    _maintenance == v._maintenance &&
 405    _update == v._update &&
 406    _type.equals(v._type) &&
 407    _vendor.equals(v._vendor) &&
 408    (_type.equals(ReleaseType.STABLE) || _typeString.equals(v._typeString)) &&
 409    (!_vendor.equals(VendorType.UNKNOWN) ||
 410    ((_location==null) && (v._location==null)) || (_location.equals(v._location)));
 411    }
 412    }
 413   
 414  0 public int hashCode() {
 415  0 int stringHash = _typeString == null ? 0 : _typeString.hashCode();
 416  0 int fileHash = _location == null ? 0 : _location.hashCode();
 417  0 return _majorVersion.hashCode() ^ (_maintenance << 1) ^ (_update << 2) ^ (_type.hashCode() << 3)
 418    ^ (_vendor.hashCode() << 4) ^ (stringHash << 5) ^ fileHash;
 419    }
 420   
 421  85 private String stringSuffix() {
 422  85 StringBuilder result = new StringBuilder();
 423  85 result.append("." + _maintenance);
 424  47 if (_update != 0) { result.append("_" + _update); }
 425  7 if (!_type.equals(ReleaseType.STABLE)) { result.append('-').append(_typeString); }
 426  85 return result.toString();
 427    }
 428   
 429  77 private String stringSuffixWithVendor() {
 430  77 StringBuilder result = new StringBuilder(stringSuffix());
 431  77 if ((!_vendor.equals(VendorType.ORACLE)) &&
 432    (!_vendor.equals(VendorType.APPLE)) &&
 433    (!_vendor.equals(VendorType.UNKNOWN))) {
 434  11 result.append('-').append(_vendorString);
 435    }
 436  77 return result.toString();
 437    }
 438   
 439    /** Produce a string representing version number */
 440  77 public String versionString() { return _majorVersion.versionString() + stringSuffixWithVendor(); }
 441   
 442  8 public String toString() { return _majorVersion + stringSuffix(); }
 443   
 444    }
 445   
 446    private static enum ReleaseType { UNRECOGNIZED, EARLY_ACCESS, BETA, RELEASE_CANDIDATE, STABLE; }
 447   
 448    /** The vendor of this version. */
 449    public static enum VendorType { UNKNOWN, OPENJDK, APPLE, ORACLE; }
 450    }