1) Moved net.sourceforge.phpeclipse.ui\src\net\sourceforge\phpdt back to net.sourcefo...
[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.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;
23
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;
30
31 /**
32  * Provides methods for checking Java-specific conventions such as name syntax.
33  * <p>
34  * This class provides static methods and constants only; it is not intended to
35  * be instantiated or subclassed by clients.
36  * </p>
37  */
38 public final class JavaConventions {
39
40         //private final static char DOT = '.';
41
42         private final static Scanner SCANNER = new Scanner();
43
44         private JavaConventions() {
45                 // Not instantiable
46         }
47
48         /**
49          * Returns whether the given package fragment root paths are considered to
50          * overlap.
51          * <p>
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.
54          * 
55          * @param rootPath1
56          *            the first root path
57          * @param rootPath2
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
62          */
63 //      public static boolean isOverlappingRoots(IPath rootPath1, IPath rootPath2) {
64 //              if (rootPath1 == null || rootPath2 == null) {
65 //                      return false;
66 //              }
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))) {
72 //              // return false;
73 //              // }
74 //              // if (extension2 != null &&
75 //              // (extension2.equalsIgnoreCase(SuffixConstants.EXTENSION_JAR) ||
76 //              // extension2.equalsIgnoreCase(SuffixConstants.EXTENSION_ZIP))) {
77 //              // return false;
78 //              // }
79 //              return rootPath1.isPrefixOf(rootPath2)
80 //                              || rootPath2.isPrefixOf(rootPath1);
81 //      }
82
83         /*
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
86          * valid
87          */
88         private static synchronized char[] scannedIdentifier(String id) {
89                 if (id == null) {
90                         return null;
91                 }
92                 String trimmed = id.trim();
93                 if (!trimmed.equals(id)) {
94                         return null;
95                 }
96                 try {
97                         SCANNER.setSource(id.toCharArray());
98                         TokenName token = SCANNER.getNextToken();
99                         char[] currentIdentifier;
100                         try {
101                                 currentIdentifier = SCANNER.getCurrentIdentifierSource();
102                         } catch (ArrayIndexOutOfBoundsException e) {
103                                 return null;
104                         }
105                         TokenName nextToken = SCANNER.getNextToken();
106                         if (token == ITerminalSymbols.TokenName.IDENTIFIER
107                                         && nextToken == ITerminalSymbols.TokenName.EOF
108                                         && SCANNER.startPosition == SCANNER.source.length) { // to
109                                                                                                                                                         // handle
110                                                                                                                                                         // case
111                                                                                                                                                         // where
112                                                                                                                                                         // we
113                                                                                                                                                         // had
114                                                                                                                                                         // an
115                                                                                                                                                         // ArrayIndexOutOfBoundsException
116                                 // while reading the last token
117                                 return currentIdentifier;
118                         } else {
119                                 return null;
120                         }
121                 } catch (InvalidInputException e) {
122                         return null;
123                 }
124         }
125
126         /**
127          * Validate the given compilation unit name. A compilation unit name must
128          * obey the following rules:
129          * <ul>
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.
135          * </ul>
136          * </p>
137          * 
138          * @param name
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
143          */
144         public static IStatus validateCompilationUnitName(String name) {
145                 if (name == null) {
146                         return new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Util
147                                         .bind("convention.unit.nullName"), null); //$NON-NLS-1$
148                 }
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$
153                 }
154                 String identifier;
155                 int index;
156                 index = name.lastIndexOf('.');
157                 if (index == -1) {
158                         return new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Util
159                                         .bind("convention.unit.notJavaName"), null); //$NON-NLS-1$
160                 }
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()) {
168                                 return status;
169                         }
170                 }
171                 IStatus status = ResourcesPlugin.getWorkspace().validateName(name,
172                                 IResource.FILE);
173                 if (!status.isOK()) {
174                         return status;
175                 }
176                 return JavaModelStatus.VERIFIED_OK;
177         }
178
179         /**
180          * Validate the given .class file name. A .class file name must obey the
181          * following rules:
182          * <ul>
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.
188          * </ul>
189          * </p>
190          * 
191          * @param name
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
196          * @since 2.0
197          */
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$
202         // }
203         // if
204         // (!net.sourceforge.phpdt.internal.compiler.util.Util.isClassFileName(name))
205         // {
206         // return new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1,
207         // Util.bind("convention.classFile.notClassFileName"), null); //$NON-NLS-1$
208         // }
209         // String identifier;
210         // int index;
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$
215         // }
216         // identifier = name.substring(0, index);
217         // IStatus status = validateIdentifier(identifier);
218         // if (!status.isOK()) {
219         // return status;
220         // }
221         // status = ResourcesPlugin.getWorkspace().validateName(name,
222         // IResource.FILE);
223         // if (!status.isOK()) {
224         // return status;
225         // }
226         // return JavaModelStatus.VERIFIED_OK;
227         // }
228         /**
229          * Validate the given field name.
230          * <p>
231          * Syntax of a field name corresponds to VariableDeclaratorId (JLS2 8.3).
232          * For example, <code>"x"</code>.
233          * 
234          * @param name
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
239          */
240         public static IStatus validateFieldName(String name) {
241                 return validateIdentifier(name);
242         }
243
244         /**
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
250          * field name.
251          * 
252          * @param id
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
257          */
258         public static IStatus validateIdentifier(String id) {
259                 if (scannedIdentifier(id) != null) {
260                         return JavaModelStatus.VERIFIED_OK;
261                 } else {
262                         return new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Util.bind(
263                                         "convention.illegalIdentifier", id), null); //$NON-NLS-1$
264                 }
265         }
266
267         /**
268          * Validate the given import declaration name.
269          * <p>
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>.
274          * 
275          * @param name
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
280          */
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$
285 //              }
286 //              if (name.charAt(name.length() - 1) == '*') {
287 //                      if (name.charAt(name.length() - 2) == '.') {
288 //                              return validatePackageName(name.substring(0, name.length() - 2));
289 //                      } else {
290 //                              return new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Util
291 //                                              .bind("convention.import.unqualifiedImport"), null); //$NON-NLS-1$
292 //                      }
293 //              }
294 //              return validatePackageName(name);
295 //      }
296
297         /**
298          * Validate the given Java type name, either simple or qualified. For
299          * example, <code>"java.lang.Object"</code>, or <code>"Object"</code>.
300          * <p>
301          * 
302          * @param name
303          *            the name of a type
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
308          *         with the name
309          */
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$
314 //              }
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$
319 //              }
320 //              int index = name.lastIndexOf('.');
321 //              char[] scannedID;
322 //              if (index == -1) {
323 //                      // simple name
324 //                      scannedID = scannedIdentifier(name);
325 //              } else {
326 //                      // qualified name
327 //                      String pkg = name.substring(0, index).trim();
328 //                      IStatus status = validatePackageName(pkg);
329 //                      if (!status.isOK()) {
330 //                              return status;
331 //                      }
332 //                      String type = name.substring(index + 1).trim();
333 //                      scannedID = scannedIdentifier(type);
334 //              }
335 //
336 //              if (scannedID != null) {
337 //                      IStatus status = ResourcesPlugin.getWorkspace().validateName(
338 //                                      new String(scannedID), IResource.FILE);
339 //                      if (!status.isOK()) {
340 //                              return status;
341 //                      }
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$
345 //                      }
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$
349 //                      }
350 //                      return JavaModelStatus.VERIFIED_OK;
351 //              } else {
352 //                      return new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Util.bind(
353 //                                      "convention.type.invalidName", name), null); //$NON-NLS-1$
354 //              }
355 //      }
356
357         /**
358          * Validate the given method name. The special names "&lt;init&gt;" and
359          * "&lt;clinit&gt;" are not valid.
360          * <p>
361          * The syntax for a method name is defined by Identifier of MethodDeclarator
362          * (JLS2 8.4). For example "println".
363          * 
364          * @param name
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
369          */
370 //      public static IStatus validateMethodName(String name) {
371 //
372 //              return validateIdentifier(name);
373 //      }
374
375         /**
376          * Validate the given package name.
377          * <p>
378          * The syntax of a package name corresponds to PackageName as defined by
379          * PackageDeclaration (JLS2 7.4). For example, <code>"java.lang"</code>.
380          * <p>
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.
385          * 
386          * @param name
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
391          */
392 //      public static IStatus validatePackageName(String name) {
393 //
394 //              if (name == null) {
395 //                      return new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Util
396 //                                      .bind("convention.package.nullName"), null); //$NON-NLS-1$
397 //              }
398 //              int length;
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$
402 //              }
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$
406 //              }
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$
411 //              }
412 //              int dot = 0;
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$
418 //                      }
419 //              }
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$
432 //                      }
433 //                      IStatus status = workspace.validateName(new String(scannedID),
434 //                                      IResource.FOLDER);
435 //                      if (!status.isOK()) {
436 //                              return status;
437 //                      }
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$
444 //                              }
445 //                      }
446 //                      firstToken = false;
447 //              }
448 //              if (warningStatus != null) {
449 //                      return warningStatus;
450 //              }
451 //              return JavaModelStatus.VERIFIED_OK;
452 //      }
453
454         /**
455          * Validate a given classpath and output location for a project, using the
456          * following rules:
457          * <ul>
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:
468          * <ul>
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.
478          * </ul>
479          * </ul>
480          * 
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, ...).
485          * <p>
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).
490          * <p>
491          * 
492          * @param javaProject
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
501          *         location
502          * @since 2.0
503          */
504 //      public static IJavaModelStatus validateClasspath(IJavaProject javaProject,
505 //                      IClasspathEntry[] rawClasspath, IPath projectOutputLocation) {
506 //
507 //              return ClasspathEntry.validateClasspath(javaProject, rawClasspath,
508 //                              projectOutputLocation);
509 //      }
510
511         /**
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).
516          * 
517          * @param project
518          *            the given java project
519          * @param entry
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
526          * @since 2.0
527          */
528 //      public static IJavaModelStatus validateClasspathEntry(IJavaProject project,
529 //                      IClasspathEntry entry, boolean checkSourceAttachment) {
530 //              return ClasspathEntry.validateClasspathEntry(project, entry,
531 //                              checkSourceAttachment, true/* recurse in container */);
532 //      }
533 }