/*******************************************************************************
* Copyright (c) 2012-2014 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.jdt.dom;
import org.eclipse.jdt.internal.core.util.Util;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.compiler.InvalidInputException;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
import org.eclipse.jdt.internal.compiler.parser.Scanner;
import org.eclipse.jdt.internal.compiler.parser.TerminalTokens;
import org.eclipse.jdt.internal.core.JavaModelStatus;
import org.eclipse.jdt.internal.core.util.Messages;
/**
* @author Evgen Vidolob
*/
public class JavaConventions {
private static final String PACKAGE_INFO = new String(TypeConstants.PACKAGE_INFO_NAME);
private static final Scanner SCANNER =
new Scanner(false /*comment*/, true /*whitespace*/, false /*nls*/, ClassFileConstants.JDK1_3 /*sourceLevel*/, null/*taskTag*/,
null/*taskPriorities*/, true /*taskCaseSensitive*/);
/**
* Validate the given compilation unit name for the given source and compliance levels.
* <p>
* A compilation unit name must obey the following rules:
* <ul>
* <li> it must not be null
* <li> it must be suffixed by a dot ('.') followed by one of the
* {@link org.eclipse.jdt.core.JavaCore#getJavaLikeExtensions() Java-like extensions}
* <li> its prefix must be a valid identifier
* <li> it must not contain any characters or substrings that are not valid
* on the file system on which workspace root is located.
* </ul>
* </p>
* @param name the name of a compilation unit
* @param sourceLevel the source level
* @param complianceLevel the compliance level
* @return a status object with code <code>IStatus.OK</code> if
* the given name is valid as a compilation unit name, otherwise a status
* object indicating what is wrong with the name
* @since 3.3
*/
public static IStatus validateCompilationUnitName(String name, String sourceLevel, String complianceLevel) {
if (name == null) {
return new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Messages.convention_unit_nullName, null);
}
if (!Util.isJavaLikeFileName(name)) {
return new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Messages.convention_unit_notJavaName, null);
}
String identifier;
int index;
index = name.lastIndexOf('.');
if (index == -1) {
return new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Messages.convention_unit_notJavaName, null);
}
identifier = name.substring(0, index);
// JSR-175 metadata strongly recommends "package-info.java" as the
// file in which to store package annotations and
// the package-level spec (replaces package.html)
if (!identifier.equals(PACKAGE_INFO)) {
IStatus status = validateIdentifier(identifier, sourceLevel, complianceLevel);
if (!status.isOK()) {
return status;
}
}
// IStatus status = ResourcesPlugin.getWorkspace().validateName(name, IResource.FILE);
// if (!status.isOK()) {
// return status;
// }
return JavaModelStatus.VERIFIED_OK;
}
/**
* Validate the given .class file name for the given source and compliance levels.
* <p>
* A .class file name must obey the following rules:
* <ul>
* <li> it must not be null
* <li> it must include the <code>".class"</code> suffix
* <li> its prefix must be a valid identifier
* <li> it must not contain any characters or substrings that are not valid
* on the file system on which workspace root is located.
* </ul>
* </p>
* @param name the name of a .class file
* @param sourceLevel the source level
* @param complianceLevel the compliance level
* @return a status object with code <code>IStatus.OK</code> if
* the given name is valid as a .class file name, otherwise a status
* object indicating what is wrong with the name
* @since 3.3
*/
public static IStatus validateClassFileName(String name, String sourceLevel, String complianceLevel) {
if (name == null) {
return new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Messages.convention_classFile_nullName, null); }
if (!org.eclipse.jdt.internal.compiler.util.Util.isClassFileName(name)) {
return new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Messages.convention_classFile_notClassFileName, null);
}
String identifier;
int index;
index = name.lastIndexOf('.');
if (index == -1) {
return new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Messages.convention_classFile_notClassFileName, null);
}
identifier = name.substring(0, index);
// JSR-175 metadata strongly recommends "package-info.java" as the
// file in which to store package annotations and
// the package-level spec (replaces package.html)
if (!identifier.equals(PACKAGE_INFO)) {
IStatus status = validateIdentifier(identifier, sourceLevel, complianceLevel);
if (!status.isOK()) {
return status;
}
}
// IStatus status = ResourcesPlugin.getWorkspace().validateName(name, IResource.FILE);
// if (!status.isOK()) {
// return status;
// }
return JavaModelStatus.VERIFIED_OK;
}
/**
* Validate the given Java identifier for the given source and compliance levels
* The identifier must not have the same spelling as a Java keyword,
* boolean literal (<code>"true"</code>, <code>"false"</code>), or null literal (<code>"null"</code>).
* See section 3.8 of the <em>Java Language Specification, Second Edition</em> (JLS2).
* A valid identifier can act as a simple type name, method name or field name.
*
* @param id the Java identifier
* @param sourceLevel the source level
* @param complianceLevel the compliance level
* @return a status object with code <code>IStatus.OK</code> if
* the given identifier is a valid Java identifier, otherwise a status
* object indicating what is wrong with the identifier
* @since 3.3
*/
public static IStatus validateIdentifier(String id, String sourceLevel, String complianceLevel) {
if (scannedIdentifier(id, sourceLevel, complianceLevel) != null) {
return JavaModelStatus.VERIFIED_OK;
} else {
return new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Messages.bind(Messages.convention_illegalIdentifier, id), null);
}
}
/*
* Returns the current identifier extracted by the scanner (without unicode
* escapes) from the given id and for the given source and compliance levels.
* Returns <code>null</code> if the id was not valid
*/
private static synchronized char[] scannedIdentifier(String id, String sourceLevel, String complianceLevel) {
if (id == null) {
return null;
}
// Set scanner for given source and compliance levels
SCANNER.sourceLevel = sourceLevel == null ? ClassFileConstants.JDK1_3 : CompilerOptions.versionToJdkLevel(sourceLevel);
SCANNER.complianceLevel = complianceLevel == null ? ClassFileConstants.JDK1_3 : CompilerOptions.versionToJdkLevel(complianceLevel);
try {
SCANNER.setSource(id.toCharArray());
int token = SCANNER.scanIdentifier();
if (token != TerminalTokens.TokenNameIdentifier) return null;
if (SCANNER.currentPosition == SCANNER.eofPosition) { // to handle case where we had an ArrayIndexOutOfBoundsException
try {
return SCANNER.getCurrentIdentifierSource();
} catch (ArrayIndexOutOfBoundsException e) {
return null;
}
} else {
return null;
}
}
catch (InvalidInputException e) {
return null;
}
}
}