45b0f418425d327e8cd334485f43ddfddae1be64
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / core / JavaConventions.java
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
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package net.sourceforge.phpdt.core;
12
13 //import java.util.StringTokenizer;
14
15 //import net.sourceforge.phpdt.core.compiler.CharOperation;
16 import net.sourceforge.phpdt.core.compiler.ITerminalSymbols;
17 import net.sourceforge.phpdt.core.compiler.InvalidInputException;
18 import net.sourceforge.phpdt.internal.compiler.parser.Scanner;
19 //import net.sourceforge.phpdt.internal.core.ClasspathEntry;
20 import net.sourceforge.phpdt.internal.core.JavaModelStatus;
21 import net.sourceforge.phpdt.internal.core.util.Util;
22
23 import org.eclipse.core.resources.IResource;
24 //import org.eclipse.core.resources.IWorkspace;
25 import org.eclipse.core.resources.ResourcesPlugin;
26 //import org.eclipse.core.runtime.IPath;
27 import org.eclipse.core.runtime.IStatus;
28 import org.eclipse.core.runtime.Status;
29
30 /**
31  * Provides methods for checking Java-specific conventions such as name syntax.
32  * <p>
33  * This class provides static methods and constants only; it is not intended to
34  * be instantiated or subclassed by clients.
35  * </p>
36  */
37 public final class JavaConventions {
38
39         //private final static char DOT = '.';
40
41         private final static Scanner SCANNER = new Scanner();
42
43         private JavaConventions() {
44                 // Not instantiable
45         }
46
47         /**
48          * Returns whether the given package fragment root paths are considered to
49          * overlap.
50          * <p>
51          * Two root paths overlap if one is a prefix of the other, or they point to
52          * the same location. However, a JAR is allowed to be nested in a root.
53          * 
54          * @param rootPath1
55          *            the first root path
56          * @param rootPath2
57          *            the second root path
58          * @return true if the given package fragment root paths are considered to
59          *         overlap, false otherwise
60          * @deprecated Overlapping roots are allowed in 2.1
61          */
62 //      public static boolean isOverlappingRoots(IPath rootPath1, IPath rootPath2) {
63 //              if (rootPath1 == null || rootPath2 == null) {
64 //                      return false;
65 //              }
66 //              // String extension1 = rootPath1.getFileExtension();
67 //              // String extension2 = rootPath2.getFileExtension();
68 //              // if (extension1 != null &&
69 //              // (extension1.equalsIgnoreCase(SuffixConstants.EXTENSION_JAR) ||
70 //              // extension1.equalsIgnoreCase(SuffixConstants.EXTENSION_ZIP))) {
71 //              // return false;
72 //              // }
73 //              // if (extension2 != null &&
74 //              // (extension2.equalsIgnoreCase(SuffixConstants.EXTENSION_JAR) ||
75 //              // extension2.equalsIgnoreCase(SuffixConstants.EXTENSION_ZIP))) {
76 //              // return false;
77 //              // }
78 //              return rootPath1.isPrefixOf(rootPath2)
79 //                              || rootPath2.isPrefixOf(rootPath1);
80 //      }
81
82         /*
83          * Returns the current identifier extracted by the scanner (without unicode
84          * escapes) from the given id. Returns <code>null</code> if the id was not
85          * valid
86          */
87         private static synchronized char[] scannedIdentifier(String id) {
88                 if (id == null) {
89                         return null;
90                 }
91                 String trimmed = id.trim();
92                 if (!trimmed.equals(id)) {
93                         return null;
94                 }
95                 try {
96                         SCANNER.setSource(id.toCharArray());
97                         int token = SCANNER.getNextToken();
98                         char[] currentIdentifier;
99                         try {
100                                 currentIdentifier = SCANNER.getCurrentIdentifierSource();
101                         } catch (ArrayIndexOutOfBoundsException e) {
102                                 return null;
103                         }
104                         int nextToken = SCANNER.getNextToken();
105                         if (token == ITerminalSymbols.TokenNameIdentifier
106                                         && nextToken == ITerminalSymbols.TokenNameEOF
107                                         && SCANNER.startPosition == SCANNER.source.length) { // to
108                                                                                                                                                         // handle
109                                                                                                                                                         // case
110                                                                                                                                                         // where
111                                                                                                                                                         // we
112                                                                                                                                                         // had
113                                                                                                                                                         // an
114                                                                                                                                                         // ArrayIndexOutOfBoundsException
115                                 // while reading the last token
116                                 return currentIdentifier;
117                         } else {
118                                 return null;
119                         }
120                 } catch (InvalidInputException e) {
121                         return null;
122                 }
123         }
124
125         /**
126          * Validate the given compilation unit name. A compilation unit name must
127          * obey the following rules:
128          * <ul>
129          * <li> it must not be null
130          * <li> it must include the <code>".java"</code> suffix
131          * <li> its prefix must be a valid identifier
132          * <li> it must not contain any characters or substrings that are not valid
133          * on the file system on which workspace root is located.
134          * </ul>
135          * </p>
136          * 
137          * @param name
138          *            the name of a compilation unit
139          * @return a status object with code <code>IStatus.OK</code> if the given
140          *         name is valid as a compilation unit name, otherwise a status
141          *         object indicating what is wrong with the name
142          */
143         public static IStatus validateCompilationUnitName(String name) {
144                 if (name == null) {
145                         return new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Util
146                                         .bind("convention.unit.nullName"), null); //$NON-NLS-1$
147                 }
148                 if (!net.sourceforge.phpdt.internal.compiler.util.Util
149                                 .isJavaFileName(name)) {
150                         return new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Util
151                                         .bind("convention.unit.notJavaName"), null); //$NON-NLS-1$
152                 }
153                 String identifier;
154                 int index;
155                 index = name.lastIndexOf('.');
156                 if (index == -1) {
157                         return new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Util
158                                         .bind("convention.unit.notJavaName"), null); //$NON-NLS-1$
159                 }
160                 identifier = name.substring(0, index);
161                 // JSR-175 metadata strongly recommends "package-info.java" as the
162                 // file in which to store package annotations and
163                 // the package-level spec (replaces package.html)
164                 if (!identifier.equals("package-info")) { //$NON-NLS-1$
165                         IStatus status = validateIdentifier(identifier);
166                         if (!status.isOK()) {
167                                 return status;
168                         }
169                 }
170                 IStatus status = ResourcesPlugin.getWorkspace().validateName(name,
171                                 IResource.FILE);
172                 if (!status.isOK()) {
173                         return status;
174                 }
175                 return JavaModelStatus.VERIFIED_OK;
176         }
177
178         /**
179          * Validate the given .class file name. A .class file name must obey the
180          * following rules:
181          * <ul>
182          * <li> it must not be null
183          * <li> it must include the <code>".class"</code> suffix
184          * <li> its prefix must be a valid identifier
185          * <li> it must not contain any characters or substrings that are not valid
186          * on the file system on which workspace root is located.
187          * </ul>
188          * </p>
189          * 
190          * @param name
191          *            the name of a .class file
192          * @return a status object with code <code>IStatus.OK</code> if the given
193          *         name is valid as a .class file name, otherwise a status object
194          *         indicating what is wrong with the name
195          * @since 2.0
196          */
197         // public static IStatus validateClassFileName(String name) {
198         // if (name == null) {
199         // return new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1,
200         // Util.bind("convention.classFile.nullName"), null); //$NON-NLS-1$
201         // }
202         // if
203         // (!net.sourceforge.phpdt.internal.compiler.util.Util.isClassFileName(name))
204         // {
205         // return new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1,
206         // Util.bind("convention.classFile.notClassFileName"), null); //$NON-NLS-1$
207         // }
208         // String identifier;
209         // int index;
210         // index = name.lastIndexOf('.');
211         // if (index == -1) {
212         // return new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1,
213         // Util.bind("convention.classFile.notClassFileName"), null); //$NON-NLS-1$
214         // }
215         // identifier = name.substring(0, index);
216         // IStatus status = validateIdentifier(identifier);
217         // if (!status.isOK()) {
218         // return status;
219         // }
220         // status = ResourcesPlugin.getWorkspace().validateName(name,
221         // IResource.FILE);
222         // if (!status.isOK()) {
223         // return status;
224         // }
225         // return JavaModelStatus.VERIFIED_OK;
226         // }
227         /**
228          * Validate the given field name.
229          * <p>
230          * Syntax of a field name corresponds to VariableDeclaratorId (JLS2 8.3).
231          * For example, <code>"x"</code>.
232          * 
233          * @param name
234          *            the name of a field
235          * @return a status object with code <code>IStatus.OK</code> if the given
236          *         name is valid as a field name, otherwise a status object
237          *         indicating what is wrong with the name
238          */
239         public static IStatus validateFieldName(String name) {
240                 return validateIdentifier(name);
241         }
242
243         /**
244          * Validate the given Java identifier. The identifier must not have the same
245          * spelling as a Java keyword, boolean literal (<code>"true"</code>,
246          * <code>"false"</code>), or null literal (<code>"null"</code>). See
247          * section 3.8 of the <em>Java Language Specification, Second Edition</em>
248          * (JLS2). A valid identifier can act as a simple type name, method name or
249          * field name.
250          * 
251          * @param id
252          *            the Java identifier
253          * @return a status object with code <code>IStatus.OK</code> if the given
254          *         identifier is a valid Java identifier, otherwise a status object
255          *         indicating what is wrong with the identifier
256          */
257         public static IStatus validateIdentifier(String id) {
258                 if (scannedIdentifier(id) != null) {
259                         return JavaModelStatus.VERIFIED_OK;
260                 } else {
261                         return new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Util.bind(
262                                         "convention.illegalIdentifier", id), null); //$NON-NLS-1$
263                 }
264         }
265
266         /**
267          * Validate the given import declaration name.
268          * <p>
269          * The name of an import corresponds to a fully qualified type name or an
270          * on-demand package name as defined by ImportDeclaration (JLS2 7.5). For
271          * example, <code>"java.util.*"</code> or
272          * <code>"java.util.Hashtable"</code>.
273          * 
274          * @param name
275          *            the import declaration
276          * @return a status object with code <code>IStatus.OK</code> if the given
277          *         name is valid as an import declaration, otherwise a status object
278          *         indicating what is wrong with the name
279          */
280 //      public static IStatus validateImportDeclaration(String name) {
281 //              if (name == null || name.length() == 0) {
282 //                      return new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Util
283 //                                      .bind("convention.import.nullImport"), null); //$NON-NLS-1$
284 //              }
285 //              if (name.charAt(name.length() - 1) == '*') {
286 //                      if (name.charAt(name.length() - 2) == '.') {
287 //                              return validatePackageName(name.substring(0, name.length() - 2));
288 //                      } else {
289 //                              return new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Util
290 //                                              .bind("convention.import.unqualifiedImport"), null); //$NON-NLS-1$
291 //                      }
292 //              }
293 //              return validatePackageName(name);
294 //      }
295
296         /**
297          * Validate the given Java type name, either simple or qualified. For
298          * example, <code>"java.lang.Object"</code>, or <code>"Object"</code>.
299          * <p>
300          * 
301          * @param name
302          *            the name of a type
303          * @return a status object with code <code>IStatus.OK</code> if the given
304          *         name is valid as a Java type name, a status with code
305          *         <code>IStatus.WARNING</code> indicating why the given name is
306          *         discouraged, otherwise a status object indicating what is wrong
307          *         with the name
308          */
309 //      public static IStatus validateJavaTypeName(String name) {
310 //              if (name == null) {
311 //                      return new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Util
312 //                                      .bind("convention.type.nullName"), null); //$NON-NLS-1$
313 //              }
314 //              String trimmed = name.trim();
315 //              if (!name.equals(trimmed)) {
316 //                      return new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Util
317 //                                      .bind("convention.type.nameWithBlanks"), null); //$NON-NLS-1$
318 //              }
319 //              int index = name.lastIndexOf('.');
320 //              char[] scannedID;
321 //              if (index == -1) {
322 //                      // simple name
323 //                      scannedID = scannedIdentifier(name);
324 //              } else {
325 //                      // qualified name
326 //                      String pkg = name.substring(0, index).trim();
327 //                      IStatus status = validatePackageName(pkg);
328 //                      if (!status.isOK()) {
329 //                              return status;
330 //                      }
331 //                      String type = name.substring(index + 1).trim();
332 //                      scannedID = scannedIdentifier(type);
333 //              }
334 //
335 //              if (scannedID != null) {
336 //                      IStatus status = ResourcesPlugin.getWorkspace().validateName(
337 //                                      new String(scannedID), IResource.FILE);
338 //                      if (!status.isOK()) {
339 //                              return status;
340 //                      }
341 //                      if (CharOperation.contains('$', scannedID)) {
342 //                              return new Status(IStatus.WARNING, JavaCore.PLUGIN_ID, -1, Util
343 //                                              .bind("convention.type.dollarName"), null); //$NON-NLS-1$
344 //                      }
345 //                      if ((scannedID.length > 0 && Character.isLowerCase(scannedID[0]))) {
346 //                              return new Status(IStatus.WARNING, JavaCore.PLUGIN_ID, -1, Util
347 //                                              .bind("convention.type.lowercaseName"), null); //$NON-NLS-1$
348 //                      }
349 //                      return JavaModelStatus.VERIFIED_OK;
350 //              } else {
351 //                      return new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Util.bind(
352 //                                      "convention.type.invalidName", name), null); //$NON-NLS-1$
353 //              }
354 //      }
355
356         /**
357          * Validate the given method name. The special names "&lt;init&gt;" and
358          * "&lt;clinit&gt;" are not valid.
359          * <p>
360          * The syntax for a method name is defined by Identifier of MethodDeclarator
361          * (JLS2 8.4). For example "println".
362          * 
363          * @param name
364          *            the name of a method
365          * @return a status object with code <code>IStatus.OK</code> if the given
366          *         name is valid as a method name, otherwise a status object
367          *         indicating what is wrong with the name
368          */
369 //      public static IStatus validateMethodName(String name) {
370 //
371 //              return validateIdentifier(name);
372 //      }
373
374         /**
375          * Validate the given package name.
376          * <p>
377          * The syntax of a package name corresponds to PackageName as defined by
378          * PackageDeclaration (JLS2 7.4). For example, <code>"java.lang"</code>.
379          * <p>
380          * Note that the given name must be a non-empty package name (that is,
381          * attempting to validate the default package will return an error status.)
382          * Also it must not contain any characters or substrings that are not valid
383          * on the file system on which workspace root is located.
384          * 
385          * @param name
386          *            the name of a package
387          * @return a status object with code <code>IStatus.OK</code> if the given
388          *         name is valid as a package name, otherwise a status object
389          *         indicating what is wrong with the name
390          */
391 //      public static IStatus validatePackageName(String name) {
392 //
393 //              if (name == null) {
394 //                      return new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Util
395 //                                      .bind("convention.package.nullName"), null); //$NON-NLS-1$
396 //              }
397 //              int length;
398 //              if ((length = name.length()) == 0) {
399 //                      return new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Util
400 //                                      .bind("convention.package.emptyName"), null); //$NON-NLS-1$
401 //              }
402 //              if (name.charAt(0) == DOT || name.charAt(length - 1) == DOT) {
403 //                      return new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Util
404 //                                      .bind("convention.package.dotName"), null); //$NON-NLS-1$
405 //              }
406 //              if (CharOperation.isWhitespace(name.charAt(0))
407 //                              || CharOperation.isWhitespace(name.charAt(name.length() - 1))) {
408 //                      return new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Util
409 //                                      .bind("convention.package.nameWithBlanks"), null); //$NON-NLS-1$
410 //              }
411 //              int dot = 0;
412 //              while (dot != -1 && dot < length - 1) {
413 //                      if ((dot = name.indexOf(DOT, dot + 1)) != -1 && dot < length - 1
414 //                                      && name.charAt(dot + 1) == DOT) {
415 //                              return new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Util
416 //                                              .bind("convention.package.consecutiveDotsName"), null); //$NON-NLS-1$
417 //                      }
418 //              }
419 //              IWorkspace workspace = ResourcesPlugin.getWorkspace();
420 //              StringTokenizer st = new StringTokenizer(name, new String(
421 //                              new char[] { DOT }));
422 //              boolean firstToken = true;
423 //              IStatus warningStatus = null;
424 //              while (st.hasMoreTokens()) {
425 //                      String typeName = st.nextToken();
426 //                      typeName = typeName.trim(); // grammar allows spaces
427 //                      char[] scannedID = scannedIdentifier(typeName);
428 //                      if (scannedID == null) {
429 //                              return new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Util
430 //                                              .bind("convention.illegalIdentifier", typeName), null); //$NON-NLS-1$
431 //                      }
432 //                      IStatus status = workspace.validateName(new String(scannedID),
433 //                                      IResource.FOLDER);
434 //                      if (!status.isOK()) {
435 //                              return status;
436 //                      }
437 //                      if (firstToken && scannedID.length > 0
438 //                                      && Character.isUpperCase(scannedID[0])) {
439 //                              if (warningStatus == null) {
440 //                                      warningStatus = new Status(IStatus.WARNING,
441 //                                                      JavaCore.PLUGIN_ID, -1,
442 //                                                      Util.bind("convention.package.uppercaseName"), null); //$NON-NLS-1$
443 //                              }
444 //                      }
445 //                      firstToken = false;
446 //              }
447 //              if (warningStatus != null) {
448 //                      return warningStatus;
449 //              }
450 //              return JavaModelStatus.VERIFIED_OK;
451 //      }
452
453         /**
454          * Validate a given classpath and output location for a project, using the
455          * following rules:
456          * <ul>
457          * <li> Classpath entries cannot collide with each other; that is, all entry
458          * paths must be unique.
459          * <li> The project output location path cannot be null, must be absolute
460          * and located inside the project.
461          * <li> Specific output locations (specified on source entries) can be null,
462          * if not they must be located inside the project,
463          * <li> A project entry cannot refer to itself directly (that is, a project
464          * cannot prerequisite itself).
465          * <li> Classpath entries or output locations cannot coincidate or be nested
466          * in each other, except for the following scenarii listed below:
467          * <ul>
468          * <li> A source folder can coincidate with its own output location, in
469          * which case this output can then contain library archives. However, a
470          * specific output location cannot coincidate with any library or a distinct
471          * source folder than the one referring to it. </li>
472          * <li> A source/library folder can be nested in any source folder as long
473          * as the nested folder is excluded from the enclosing one. </li>
474          * <li> An output location can be nested in a source folder, if the source
475          * folder coincidates with the project itself, or if the output location is
476          * excluded from the source folder.
477          * </ul>
478          * </ul>
479          * 
480          * Note that the classpath entries are not validated automatically. Only
481          * bound variables or containers are considered in the checking process
482          * (this allows to perform a consistency check on a classpath which has
483          * references to yet non existing projects, folders, ...).
484          * <p>
485          * This validation is intended to anticipate classpath issues prior to
486          * assigning it to a project. In particular, it will automatically be
487          * performed during the classpath setting operation (if validation fails,
488          * the classpath setting will not complete).
489          * <p>
490          * 
491          * @param javaProject
492          *            the given java project
493          * @param rawClasspath
494          *            the given classpath
495          * @param projectOutputLocation
496          *            the given output location
497          * @return a status object with code <code>IStatus.OK</code> if the given
498          *         classpath and output location are compatible, otherwise a status
499          *         object indicating what is wrong with the classpath or output
500          *         location
501          * @since 2.0
502          */
503 //      public static IJavaModelStatus validateClasspath(IJavaProject javaProject,
504 //                      IClasspathEntry[] rawClasspath, IPath projectOutputLocation) {
505 //
506 //              return ClasspathEntry.validateClasspath(javaProject, rawClasspath,
507 //                              projectOutputLocation);
508 //      }
509
510         /**
511          * Returns a Java model status describing the problem related to this
512          * classpath entry if any, a status object with code <code>IStatus.OK</code>
513          * if the entry is fine (that is, if the given classpath entry denotes a
514          * valid element to be referenced onto a classpath).
515          * 
516          * @param project
517          *            the given java project
518          * @param entry
519          *            the given classpath entry
520          * @param checkSourceAttachment
521          *            a flag to determine if source attachement should be checked
522          * @return a java model status describing the problem related to this
523          *         classpath entry if any, a status object with code
524          *         <code>IStatus.OK</code> if the entry is fine
525          * @since 2.0
526          */
527 //      public static IJavaModelStatus validateClasspathEntry(IJavaProject project,
528 //                      IClasspathEntry entry, boolean checkSourceAttachment) {
529 //              return ClasspathEntry.validateClasspathEntry(project, entry,
530 //                              checkSourceAttachment, true/* recurse in container */);
531 //      }
532 }