aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/rules/java/JavaUtil.java
blob: 7f49a9e277a0c5733ffc1ba9edebfccb518242f9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
// Copyright 2014 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.devtools.build.lib.rules.java;

import com.google.common.collect.ImmutableSet;
import com.google.devtools.build.lib.vfs.FileSystemUtils;
import com.google.devtools.build.lib.vfs.PathFragment;

/**
 * Utility methods for use by Java-related parts of the build system.
 */
public final class JavaUtil {

  private JavaUtil() {}

  //---------- Java related methods

  /*
   * TODO(bazel-team): (2009)
   *
   * This way of figuring out Java source roots is basically
   * broken. I think we need to support these two use cases:
   * (1) A user puts their shell into a directory named java.
   * (2) Someplace in the tree, there's a package named java.
   *
   * (1) is more important than (2); and (2) cannot always be guaranteed
   * due to sloppy implementations in the past; most notably the old
   * tools/boilerplate_rules.mk code for compiling Java.
   *
   * Basically, to implement correct semantics, we will need to configure
   * Java source roots based on the package path, plus some heuristics to
   * support legacy code, maybe.
   *
   * Roughly:
   * Given a path, find the source root that applies to it by
   * - walk over the elements in the package path
   * - add "java", "javatests" to them
   * - find the first element that is a maximal prefix to the Java file
   * - for experimental, some legacy support that basically has some
   * arbitrary padding before the Java sourceroot.
   */

  /**
   * Given the filename of a Java source file, returns the name of the toplevel Java class defined
   * within it.
   */
  public static String getJavaClassName(PathFragment path) {
    return FileSystemUtils.removeExtension(path.getBaseName());
  }

  /**
   * Finds the index of the segment in a Java path fragment that precedes the source root.
   * Starts from the first "java" or "javatests" or "src" segment.
   * If the found item was "src", check if this is followed by "main" or "test" and then "java"
   * or "resources" (maven layout).
   * If the found item was "src", or "java"/"javatests" at the first segment, check for a nested
   * root directory (src, java or javatests). A nested root must be followed by (com|net|org),
   * or matching maven structure for nested "src", to be accepted, to avoid false positives.
   *
   * @param path a Java source dir or file path
   * @return the index of the java segment or -1 iff no java segment was found.
   */
  private static int javaSegmentIndex(PathFragment path) {
    if (path.isAbsolute()) {
      throw new IllegalArgumentException("path must not be absolute: '" + path + "'");
    }
    int rootIndex = path.getFirstSegment(ImmutableSet.of("java", "javatests", "src"));
    if (rootIndex < 0) {
      return rootIndex;
    }
    final boolean isSrc = "src".equals(path.getSegment(rootIndex));
    int checkMavenIndex = isSrc ? rootIndex : -1;
    if (rootIndex == 0 || isSrc) {
      // Check for a nested root directory.
      for (int i = rootIndex + 1, max = path.segmentCount() - 2; i <= max; i++) {
        String segment = path.getSegment(i);
        if ("src".equals(segment)
            || (isSrc && ("javatests".equals(segment) || "java".equals(segment)))) {
          String next = path.getSegment(i + 1);
          if ("com".equals(next) || "org".equals(next) || "net".equals(next)) {
            // Check for common first element of java package, to avoid false positives.
            rootIndex = i;
          } else if ("src".equals(segment)) {
            // Also accept maven style src/(main|test)/(java|resources).
            checkMavenIndex = i;
          }
          break;
        }
      }
    }
    // Check for (main|test)/(java|resources) after /src/.
    if (checkMavenIndex >= 0 && checkMavenIndex + 2 < path.segmentCount()) {
      String next = path.getSegment(checkMavenIndex + 1);
      if ("main".equals(next) || "test".equals(next)) {
        next = path.getSegment(checkMavenIndex + 2);
        if ("java".equals(next) || "resources".equals(next)) {
          rootIndex = checkMavenIndex + 2;
        }
      }
    }
    return rootIndex;
  }

  /**
   * Given the PathFragment of a Java source file, returns the Java package to which it belongs.
   */
  public static String getJavaPackageName(PathFragment path) {
    int index = javaSegmentIndex(path) + 1;
    path = path.subFragment(index, path.segmentCount() - 1);
    return path.getPathString().replace('/', '.');
  }

  /**
   * Given the PathFragment of a file without extension, returns the
   * Java fully qualified class name based on the Java root relative path of the
   * specified path or 'null' if no java root can be determined.
   * <p>
   * For example, "java/foo/bar/wiz" and "javatests/foo/bar/wiz" both
   * result in "foo.bar.wiz".
   *
   * TODO(bazel-team): (2011) We need to have a more robust way to determine the Java root
   * of a relative path rather than simply trying to find the "java" or "javatests"
   * or "src" directory.
   */
  public static String getJavaFullClassname(PathFragment path) {
    PathFragment javaPath = getJavaPath(path);
    if (javaPath != null) {
      return javaPath.getPathString().replace('/', '.');
    }
    return null;
  }

  /**
   * Given the PathFragment of a Java source file, returns the Java root relative path or 'null' if
   * no java root can be determined.
   *
   * <p>
   * For example, "{workspace}/java/foo/bar/wiz" and "{workspace}/javatests/foo/bar/wiz"
   * both result in "foo/bar/wiz".
   *
   * TODO(bazel-team): (2011) We need to have a more robust way to determine the Java root
   * of a relative path rather than simply trying to find the "java" or
   * "javatests" directory.
   */
  public static PathFragment getJavaPath(PathFragment path) {
    int index = javaSegmentIndex(path);
    if (index >= 0) {
      return path.subFragment(index + 1);
    }
    return null;
  }

  /**
   * Given the PathFragment of a Java source file, returns the
   * Java root of the specified path or 'null' if no java root can be
   * determined.
   * <p>
   * Example 1: "{workspace}/java/foo/bar/wiz" and "{workspace}/javatests/foo/bar/wiz"
   * result in "{workspace}/java" and "{workspace}/javatests" Example 2:
   * "java/foo/bar/wiz" and "javatests/foo/bar/wiz" result in "java" and
   * "javatests"
   *
   * TODO(bazel-team): (2011) We need to have a more robust way to determine the Java root
   * of a relative path rather than simply trying to find the "java" or
   * "javatests" directory.
   */
  public static PathFragment getJavaRoot(PathFragment path) {
    int index = javaSegmentIndex(path);
    if (index >= 0) {
      return path.subFragment(0, index + 1);
    }
    return null;
  }

}