1 /*******************************************************************************
2 * Copyright (c) 2000, 2004 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Common Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/cpl-v10.html
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package net.sourceforge.phpdt.core;
13 //import java.util.StringTokenizer;
15 //import net.sourceforge.phpdt.core.compiler.CharOperation;
16 import net.sourceforge.phpdt.core.compiler.ITerminalSymbols;
17 import net.sourceforge.phpdt.core.compiler.ITerminalSymbols.TokenName;
18 import net.sourceforge.phpdt.core.compiler.InvalidInputException;
19 import net.sourceforge.phpdt.internal.compiler.parser.Scanner;
20 //import net.sourceforge.phpdt.internal.core.ClasspathEntry;
21 import net.sourceforge.phpdt.internal.core.JavaModelStatus;
22 import net.sourceforge.phpdt.internal.core.util.Util;
24 import org.eclipse.core.resources.IResource;
25 //import org.eclipse.core.resources.IWorkspace;
26 import org.eclipse.core.resources.ResourcesPlugin;
27 //import org.eclipse.core.runtime.IPath;
28 import org.eclipse.core.runtime.IStatus;
29 import org.eclipse.core.runtime.Status;
32 * Provides methods for checking Java-specific conventions such as name syntax.
34 * This class provides static methods and constants only; it is not intended to
35 * be instantiated or subclassed by clients.
38 public final class JavaConventions {
40 //private final static char DOT = '.';
42 private final static Scanner SCANNER = new Scanner();
44 private JavaConventions() {
49 * Returns whether the given package fragment root paths are considered to
52 * Two root paths overlap if one is a prefix of the other, or they point to
53 * the same location. However, a JAR is allowed to be nested in a root.
58 * the second root path
59 * @return true if the given package fragment root paths are considered to
60 * overlap, false otherwise
61 * @deprecated Overlapping roots are allowed in 2.1
63 // public static boolean isOverlappingRoots(IPath rootPath1, IPath rootPath2) {
64 // if (rootPath1 == null || rootPath2 == null) {
67 // // String extension1 = rootPath1.getFileExtension();
68 // // String extension2 = rootPath2.getFileExtension();
69 // // if (extension1 != null &&
70 // // (extension1.equalsIgnoreCase(SuffixConstants.EXTENSION_JAR) ||
71 // // extension1.equalsIgnoreCase(SuffixConstants.EXTENSION_ZIP))) {
74 // // if (extension2 != null &&
75 // // (extension2.equalsIgnoreCase(SuffixConstants.EXTENSION_JAR) ||
76 // // extension2.equalsIgnoreCase(SuffixConstants.EXTENSION_ZIP))) {
79 // return rootPath1.isPrefixOf(rootPath2)
80 // || rootPath2.isPrefixOf(rootPath1);
84 * Returns the current identifier extracted by the scanner (without unicode
85 * escapes) from the given id. Returns <code>null</code> if the id was not
88 private static synchronized char[] scannedIdentifier(String id) {
92 String trimmed = id.trim();
93 if (!trimmed.equals(id)) {
97 SCANNER.setSource(id.toCharArray());
98 TokenName token = SCANNER.getNextToken();
99 char[] currentIdentifier;
101 currentIdentifier = SCANNER.getCurrentIdentifierSource();
102 } catch (ArrayIndexOutOfBoundsException e) {
105 TokenName nextToken = SCANNER.getNextToken();
106 if (token == ITerminalSymbols.TokenName.IDENTIFIER
107 && nextToken == ITerminalSymbols.TokenName.EOF
108 && SCANNER.startPosition == SCANNER.source.length) { // to
115 // ArrayIndexOutOfBoundsException
116 // while reading the last token
117 return currentIdentifier;
121 } catch (InvalidInputException e) {
127 * Validate the given compilation unit name. A compilation unit name must
128 * obey the following rules:
130 * <li> it must not be null
131 * <li> it must include the <code>".java"</code> suffix
132 * <li> its prefix must be a valid identifier
133 * <li> it must not contain any characters or substrings that are not valid
134 * on the file system on which workspace root is located.
139 * the name of a compilation unit
140 * @return a status object with code <code>IStatus.OK</code> if the given
141 * name is valid as a compilation unit name, otherwise a status
142 * object indicating what is wrong with the name
144 public static IStatus validateCompilationUnitName(String name) {
146 return new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Util
147 .bind("convention.unit.nullName"), null); //$NON-NLS-1$
149 if (!net.sourceforge.phpdt.internal.compiler.util.Util
150 .isJavaFileName(name)) {
151 return new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Util
152 .bind("convention.unit.notJavaName"), null); //$NON-NLS-1$
156 index = name.lastIndexOf('.');
158 return new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Util
159 .bind("convention.unit.notJavaName"), null); //$NON-NLS-1$
161 identifier = name.substring(0, index);
162 // JSR-175 metadata strongly recommends "package-info.java" as the
163 // file in which to store package annotations and
164 // the package-level spec (replaces package.html)
165 if (!identifier.equals("package-info")) { //$NON-NLS-1$
166 IStatus status = validateIdentifier(identifier);
167 if (!status.isOK()) {
171 IStatus status = ResourcesPlugin.getWorkspace().validateName(name,
173 if (!status.isOK()) {
176 return JavaModelStatus.VERIFIED_OK;
180 * Validate the given .class file name. A .class file name must obey the
183 * <li> it must not be null
184 * <li> it must include the <code>".class"</code> suffix
185 * <li> its prefix must be a valid identifier
186 * <li> it must not contain any characters or substrings that are not valid
187 * on the file system on which workspace root is located.
192 * the name of a .class file
193 * @return a status object with code <code>IStatus.OK</code> if the given
194 * name is valid as a .class file name, otherwise a status object
195 * indicating what is wrong with the name
198 // public static IStatus validateClassFileName(String name) {
199 // if (name == null) {
200 // return new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1,
201 // Util.bind("convention.classFile.nullName"), null); //$NON-NLS-1$
204 // (!net.sourceforge.phpdt.internal.compiler.util.Util.isClassFileName(name))
206 // return new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1,
207 // Util.bind("convention.classFile.notClassFileName"), null); //$NON-NLS-1$
209 // String identifier;
211 // index = name.lastIndexOf('.');
212 // if (index == -1) {
213 // return new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1,
214 // Util.bind("convention.classFile.notClassFileName"), null); //$NON-NLS-1$
216 // identifier = name.substring(0, index);
217 // IStatus status = validateIdentifier(identifier);
218 // if (!status.isOK()) {
221 // status = ResourcesPlugin.getWorkspace().validateName(name,
223 // if (!status.isOK()) {
226 // return JavaModelStatus.VERIFIED_OK;
229 * Validate the given field name.
231 * Syntax of a field name corresponds to VariableDeclaratorId (JLS2 8.3).
232 * For example, <code>"x"</code>.
235 * the name of a field
236 * @return a status object with code <code>IStatus.OK</code> if the given
237 * name is valid as a field name, otherwise a status object
238 * indicating what is wrong with the name
240 public static IStatus validateFieldName(String name) {
241 return validateIdentifier(name);
245 * Validate the given Java identifier. The identifier must not have the same
246 * spelling as a Java keyword, boolean literal (<code>"true"</code>,
247 * <code>"false"</code>), or null literal (<code>"null"</code>). See
248 * section 3.8 of the <em>Java Language Specification, Second Edition</em>
249 * (JLS2). A valid identifier can act as a simple type name, method name or
253 * the Java identifier
254 * @return a status object with code <code>IStatus.OK</code> if the given
255 * identifier is a valid Java identifier, otherwise a status object
256 * indicating what is wrong with the identifier
258 public static IStatus validateIdentifier(String id) {
259 if (scannedIdentifier(id) != null) {
260 return JavaModelStatus.VERIFIED_OK;
262 return new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Util.bind(
263 "convention.illegalIdentifier", id), null); //$NON-NLS-1$
268 * Validate the given import declaration name.
270 * The name of an import corresponds to a fully qualified type name or an
271 * on-demand package name as defined by ImportDeclaration (JLS2 7.5). For
272 * example, <code>"java.util.*"</code> or
273 * <code>"java.util.Hashtable"</code>.
276 * the import declaration
277 * @return a status object with code <code>IStatus.OK</code> if the given
278 * name is valid as an import declaration, otherwise a status object
279 * indicating what is wrong with the name
281 // public static IStatus validateImportDeclaration(String name) {
282 // if (name == null || name.length() == 0) {
283 // return new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Util
284 // .bind("convention.import.nullImport"), null); //$NON-NLS-1$
286 // if (name.charAt(name.length() - 1) == '*') {
287 // if (name.charAt(name.length() - 2) == '.') {
288 // return validatePackageName(name.substring(0, name.length() - 2));
290 // return new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Util
291 // .bind("convention.import.unqualifiedImport"), null); //$NON-NLS-1$
294 // return validatePackageName(name);
298 * Validate the given Java type name, either simple or qualified. For
299 * example, <code>"java.lang.Object"</code>, or <code>"Object"</code>.
304 * @return a status object with code <code>IStatus.OK</code> if the given
305 * name is valid as a Java type name, a status with code
306 * <code>IStatus.WARNING</code> indicating why the given name is
307 * discouraged, otherwise a status object indicating what is wrong
310 // public static IStatus validateJavaTypeName(String name) {
311 // if (name == null) {
312 // return new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Util
313 // .bind("convention.type.nullName"), null); //$NON-NLS-1$
315 // String trimmed = name.trim();
316 // if (!name.equals(trimmed)) {
317 // return new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Util
318 // .bind("convention.type.nameWithBlanks"), null); //$NON-NLS-1$
320 // int index = name.lastIndexOf('.');
322 // if (index == -1) {
324 // scannedID = scannedIdentifier(name);
327 // String pkg = name.substring(0, index).trim();
328 // IStatus status = validatePackageName(pkg);
329 // if (!status.isOK()) {
332 // String type = name.substring(index + 1).trim();
333 // scannedID = scannedIdentifier(type);
336 // if (scannedID != null) {
337 // IStatus status = ResourcesPlugin.getWorkspace().validateName(
338 // new String(scannedID), IResource.FILE);
339 // if (!status.isOK()) {
342 // if (CharOperation.contains('$', scannedID)) {
343 // return new Status(IStatus.WARNING, JavaCore.PLUGIN_ID, -1, Util
344 // .bind("convention.type.dollarName"), null); //$NON-NLS-1$
346 // if ((scannedID.length > 0 && Character.isLowerCase(scannedID[0]))) {
347 // return new Status(IStatus.WARNING, JavaCore.PLUGIN_ID, -1, Util
348 // .bind("convention.type.lowercaseName"), null); //$NON-NLS-1$
350 // return JavaModelStatus.VERIFIED_OK;
352 // return new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Util.bind(
353 // "convention.type.invalidName", name), null); //$NON-NLS-1$
358 * Validate the given method name. The special names "<init>" and
359 * "<clinit>" are not valid.
361 * The syntax for a method name is defined by Identifier of MethodDeclarator
362 * (JLS2 8.4). For example "println".
365 * the name of a method
366 * @return a status object with code <code>IStatus.OK</code> if the given
367 * name is valid as a method name, otherwise a status object
368 * indicating what is wrong with the name
370 // public static IStatus validateMethodName(String name) {
372 // return validateIdentifier(name);
376 * Validate the given package name.
378 * The syntax of a package name corresponds to PackageName as defined by
379 * PackageDeclaration (JLS2 7.4). For example, <code>"java.lang"</code>.
381 * Note that the given name must be a non-empty package name (that is,
382 * attempting to validate the default package will return an error status.)
383 * Also it must not contain any characters or substrings that are not valid
384 * on the file system on which workspace root is located.
387 * the name of a package
388 * @return a status object with code <code>IStatus.OK</code> if the given
389 * name is valid as a package name, otherwise a status object
390 * indicating what is wrong with the name
392 // public static IStatus validatePackageName(String name) {
394 // if (name == null) {
395 // return new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Util
396 // .bind("convention.package.nullName"), null); //$NON-NLS-1$
399 // if ((length = name.length()) == 0) {
400 // return new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Util
401 // .bind("convention.package.emptyName"), null); //$NON-NLS-1$
403 // if (name.charAt(0) == DOT || name.charAt(length - 1) == DOT) {
404 // return new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Util
405 // .bind("convention.package.dotName"), null); //$NON-NLS-1$
407 // if (CharOperation.isWhitespace(name.charAt(0))
408 // || CharOperation.isWhitespace(name.charAt(name.length() - 1))) {
409 // return new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Util
410 // .bind("convention.package.nameWithBlanks"), null); //$NON-NLS-1$
413 // while (dot != -1 && dot < length - 1) {
414 // if ((dot = name.indexOf(DOT, dot + 1)) != -1 && dot < length - 1
415 // && name.charAt(dot + 1) == DOT) {
416 // return new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Util
417 // .bind("convention.package.consecutiveDotsName"), null); //$NON-NLS-1$
420 // IWorkspace workspace = ResourcesPlugin.getWorkspace();
421 // StringTokenizer st = new StringTokenizer(name, new String(
422 // new char[] { DOT }));
423 // boolean firstToken = true;
424 // IStatus warningStatus = null;
425 // while (st.hasMoreTokens()) {
426 // String typeName = st.nextToken();
427 // typeName = typeName.trim(); // grammar allows spaces
428 // char[] scannedID = scannedIdentifier(typeName);
429 // if (scannedID == null) {
430 // return new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Util
431 // .bind("convention.illegalIdentifier", typeName), null); //$NON-NLS-1$
433 // IStatus status = workspace.validateName(new String(scannedID),
434 // IResource.FOLDER);
435 // if (!status.isOK()) {
438 // if (firstToken && scannedID.length > 0
439 // && Character.isUpperCase(scannedID[0])) {
440 // if (warningStatus == null) {
441 // warningStatus = new Status(IStatus.WARNING,
442 // JavaCore.PLUGIN_ID, -1,
443 // Util.bind("convention.package.uppercaseName"), null); //$NON-NLS-1$
446 // firstToken = false;
448 // if (warningStatus != null) {
449 // return warningStatus;
451 // return JavaModelStatus.VERIFIED_OK;
455 * Validate a given classpath and output location for a project, using the
458 * <li> Classpath entries cannot collide with each other; that is, all entry
459 * paths must be unique.
460 * <li> The project output location path cannot be null, must be absolute
461 * and located inside the project.
462 * <li> Specific output locations (specified on source entries) can be null,
463 * if not they must be located inside the project,
464 * <li> A project entry cannot refer to itself directly (that is, a project
465 * cannot prerequisite itself).
466 * <li> Classpath entries or output locations cannot coincidate or be nested
467 * in each other, except for the following scenarii listed below:
469 * <li> A source folder can coincidate with its own output location, in
470 * which case this output can then contain library archives. However, a
471 * specific output location cannot coincidate with any library or a distinct
472 * source folder than the one referring to it. </li>
473 * <li> A source/library folder can be nested in any source folder as long
474 * as the nested folder is excluded from the enclosing one. </li>
475 * <li> An output location can be nested in a source folder, if the source
476 * folder coincidates with the project itself, or if the output location is
477 * excluded from the source folder.
481 * Note that the classpath entries are not validated automatically. Only
482 * bound variables or containers are considered in the checking process
483 * (this allows to perform a consistency check on a classpath which has
484 * references to yet non existing projects, folders, ...).
486 * This validation is intended to anticipate classpath issues prior to
487 * assigning it to a project. In particular, it will automatically be
488 * performed during the classpath setting operation (if validation fails,
489 * the classpath setting will not complete).
493 * the given java project
494 * @param rawClasspath
495 * the given classpath
496 * @param projectOutputLocation
497 * the given output location
498 * @return a status object with code <code>IStatus.OK</code> if the given
499 * classpath and output location are compatible, otherwise a status
500 * object indicating what is wrong with the classpath or output
504 // public static IJavaModelStatus validateClasspath(IJavaProject javaProject,
505 // IClasspathEntry[] rawClasspath, IPath projectOutputLocation) {
507 // return ClasspathEntry.validateClasspath(javaProject, rawClasspath,
508 // projectOutputLocation);
512 * Returns a Java model status describing the problem related to this
513 * classpath entry if any, a status object with code <code>IStatus.OK</code>
514 * if the entry is fine (that is, if the given classpath entry denotes a
515 * valid element to be referenced onto a classpath).
518 * the given java project
520 * the given classpath entry
521 * @param checkSourceAttachment
522 * a flag to determine if source attachement should be checked
523 * @return a java model status describing the problem related to this
524 * classpath entry if any, a status object with code
525 * <code>IStatus.OK</code> if the entry is fine
528 // public static IJavaModelStatus validateClasspathEntry(IJavaProject project,
529 // IClasspathEntry entry, boolean checkSourceAttachment) {
530 // return ClasspathEntry.validateClasspathEntry(project, entry,
531 // checkSourceAttachment, true/* recurse in container */);