View Javadoc
1   package org.codehaus.plexus.languages.java.version;
2   
3   import java.io.IOException;
4   import java.io.InputStream;
5   import java.io.UncheckedIOException;
6   import java.nio.file.Files;
7   import java.nio.file.Path;
8   
9   /*
10   * Licensed to the Apache Software Foundation (ASF) under one
11   * or more contributor license agreements.  See the NOTICE file
12   * distributed with this work for additional information
13   * regarding copyright ownership.  The ASF licenses this file
14   * to you under the Apache License, Version 2.0 (the
15   * "License"); you may not use this file except in compliance
16   * with the License.  You may obtain a copy of the License at
17   *
18   *   http://www.apache.org/licenses/LICENSE-2.0
19   *
20   * Unless required by applicable law or agreed to in writing,
21   * software distributed under the License is distributed on an
22   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
23   * KIND, either express or implied.  See the License for the
24   * specific language governing permissions and limitations
25   * under the License.
26   */
27  
28  /**
29   * Reads the bytecode of a Java class to detect the major, minor and Java
30   * version that was compiled.
31   *
32   * @author Jorge Solórzano
33   */
34  public final class JavaClassfileVersion {
35  
36      private final int major;
37      private final int minor;
38  
39      JavaClassfileVersion(int major, int minor) {
40          if (major < 45) {
41              throw new IllegalArgumentException("Java class major version must be 45 or above.");
42          }
43          this.major = major;
44          this.minor = minor;
45      }
46  
47      /**
48       * Reads the bytecode of a Java class file and returns the
49       * {@link JavaClassfileVersion}.
50       *
51       * @param bytes {@code byte[]} of the Java class file
52       * @return the {@link JavaClassfileVersion} of the byte array
53       */
54      public static JavaClassfileVersion of(byte[] bytes) {
55          return JavaClassfileVersionParser.of(bytes);
56      }
57  
58      /**
59       * Reads the bytecode of a Java class file and returns the
60       * {@link JavaClassfileVersion}.
61       *
62       * @param path {@link Path} of the Java class file
63       * @return the {@link JavaClassfileVersion} of the path java class
64       */
65      public static JavaClassfileVersion of(Path path) {
66          try (InputStream is = Files.newInputStream(path)) {
67              byte[] bytes = new byte[8];
68              int total = 0;
69              while (total < 8) {
70                  int l = is.read(bytes, total, 8 - total);
71                  if (l > 0) {
72                      total += l;
73                  }
74                  if (l == -1) {
75                      break;
76                  }
77              }
78              return of(bytes);
79          } catch (IOException ex) {
80              throw new UncheckedIOException(ex);
81          }
82      }
83  
84      /**
85       * JavaVersion of the class file version detected.
86       *
87       * @return JavaVersion based on the major version of the class file.
88       */
89      public JavaVersion javaVersion() {
90          int javaVer = major - 44;
91          String javaVersion = javaVer < 9 ? "1." + javaVer : Integer.toString(javaVer);
92  
93          return JavaVersion.parse(javaVersion);
94      }
95  
96      /**
97       * Returns the major version of the parsed classfile.
98       *
99       * @return the major classfile version
100      */
101     public int majorVersion() {
102         return major;
103     }
104 
105     /**
106      * Returns the minor version of the parsed classfile.
107      *
108      * @return the minor classfile version
109      */
110     public int minorVersion() {
111         return minor;
112     }
113 
114     /**
115      * Returns if the classfile use preview features.
116      *
117      * @return {@code true} if the classfile use preview features.
118      */
119     public boolean isPreview() {
120         return minor == 65535;
121     }
122 
123     /**
124      * Returns a String representation of the Java class file version, e.g.
125      * {@code 65.0 (Java 21)}.
126      *
127      * @return String representation of the Java class file version
128      */
129     @Override
130     public String toString() {
131         return major + "." + minor + " (Java " + javaVersion() + ")";
132     }
133 
134     @Override
135     public int hashCode() {
136         final int prime = 31;
137         int result = 1;
138         result = prime * result + major;
139         result = prime * result + minor;
140         return result;
141     }
142 
143     @Override
144     public boolean equals(Object obj) {
145         if (this == obj) return true;
146         if (!(obj instanceof JavaClassfileVersion)) return false;
147         JavaClassfileVersion other = (JavaClassfileVersion) obj;
148         if (major != other.major) return false;
149         if (minor != other.minor) return false;
150         return true;
151     }
152 }