1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.apache.maven.plugin.compiler;
20
21 import javax.lang.model.SourceVersion;
22
23 import java.nio.file.Path;
24 import java.util.Locale;
25
26 /**
27 * The way that source files are organized in a file system directory hierarchy.
28 * <a href="https://docs.oracle.com/en/java/javase/25/docs/specs/man/javac.html#directory-hierarchies">Directory
29 * hierarchies</a> are <i>package hierarchy</i>, <i>module hierarchy</i> and <i>module source hierarchy</i>, but
30 * for the purpose of the Maven Compiler Plugin we do not distinguish between the two latter.
31 *
32 * @see <a href="https://docs.oracle.com/en/java/javase/25/docs/specs/man/javac.html#directory-hierarchies">Directory hierarchies</a>
33 */
34 public enum DirectoryHierarchy {
35 /**
36 * Project using package hierarchy. This is the hierarchy used by all Java projects before Java 9.
37 * Note that it does not necessarily implies a class-path project. A modular project can still use
38 * the package hierarchy if the project contains only one module.
39 */
40 PACKAGE("versions"),
41
42 /**
43 * Project using package hierarchy, but in which a {@code module-info} file has been detected.
44 * This is used for compilation of tests. For the main code, we pretend that the hierarchy is
45 * {@link #MODULE_SOURCE} and move the directory output after compilation. Therefore, this
46 * enumeration value can be understood as "pseudo module source hierarchy".
47 *
48 * @see ModuleDirectoryRemover
49 *
50 * @deprecated Used only for compatibility with Maven 3.
51 */
52 @Deprecated
53 PACKAGE_WITH_MODULE("versions"),
54
55 /**
56 * A multi-module project using module source hierarchy. It could also be a module hierarchy,
57 * as the Maven Compiler Plugin does not need to distinguish <i>module hierarchy</i> and
58 * <i>module source hierarchy</i>.
59 */
60 MODULE_SOURCE("versions-modular");
61
62 /**
63 * The {@value} directory.
64 */
65 static final String META_INF = "META-INF";
66
67 /**
68 * Name of the {@code META-INF/} sub-directory where multi-release outputs are stored.
69 */
70 private final String versionDirectory;
71
72 /**
73 * Creates a new enumeration value.
74 *
75 * @param versionDirectory name of the {@code META-INF/} sub-directory where multi-release outputs are stored
76 */
77 DirectoryHierarchy(String versionDirectory) {
78 this.versionDirectory = versionDirectory;
79 }
80
81 /**
82 * Returns the directory where to write the compiled class files for all Java releases.
83 * The standard path for {@link #PACKAGE} hierarchy is {@code META-INF/versions}.
84 * The caller shall add the version number to the returned path.
85 *
86 * @param outputDirectory usually the value of {@link SourceDirectory#outputDirectory}
87 * @return the directory for all versions
88 */
89 public Path outputDirectoryForReleases(Path outputDirectory) {
90 // TODO: use Path.resolve(String, String...) with Java 22.
91 return outputDirectory.resolve(META_INF).resolve(versionDirectory);
92 }
93
94 /**
95 * Returns the directory where to write the compiled class files for a specific Java release.
96 * The standard path is {@code META-INF/versions/${release}} where {@code ${release}} is the
97 * numerical value of the {@code release} argument. However for {@link #MODULE_SOURCE} case,
98 * the returned path is rather {@code META-INF/versions-modular/${release}}.
99 * The latter is non-standard because there is no standard multi-module <abbr>JAR</abbr> formats as of 2025.
100 * The use of {@code "versions-modular"} is for allowing other plugins such as Maven <abbr>JAR</abbr> plugin
101 * to avoid confusion with the standard case.
102 *
103 * @param outputDirectory usually the value of {@link SourceDirectory#outputDirectory}
104 * @param release the release, or {@code null} for the default release
105 * @return the directory for the classes of the specified version
106 */
107 public Path outputDirectoryForReleases(Path outputDirectory, SourceVersion release) {
108 if (release == null) {
109 release = SourceVersion.latestSupported();
110 }
111 String version = release.name(); // TODO: replace by runtimeVersion() in Java 18.
112 version = version.substring(version.lastIndexOf('_') + 1);
113 return outputDirectoryForReleases(outputDirectory).resolve(version);
114 }
115
116 /**
117 * Returns a string representation for use in error message.
118 *
119 * @return human-readable string representation
120 */
121 @Override
122 public String toString() {
123 return name().replace('_', ' ').toLowerCase(Locale.US);
124 }
125 }