blob: 4bf3a769894adcede4bdedbd82dc93256c91b7f4 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2003, 2004 IBM Corporation and others.
* 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:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jst.j2ee.internal.common.operations;
import java.util.List;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaConventions;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jem.util.logger.proxy.Logger;
import org.eclipse.jem.workbench.utility.JemProjectUtilities;
import org.eclipse.jst.j2ee.internal.common.J2EECommonMessages;
import org.eclipse.wst.common.componentcore.internal.operation.ArtifactEditOperationDataModel;
import org.eclipse.wst.common.frameworks.internal.operations.WTPOperation;
import org.eclipse.wst.common.frameworks.internal.plugin.WTPCommonPlugin;
/**
* This data model is a subclass of WTPOperationDataModel and follows the WTP Operation and WTP Wizard frameworks.
* @see org.eclipse.wst.common.frameworks.internal.operations.WTPOperationDataModel
*
* This data model extends the EditModelOperationDataModel to get project name and edit model ID so
* that during the operation, the edit model can be used to save changes.
* @see org.eclipse.wst.common.internal.emfworkbench.operation.EditModelOperationDataModel
*
* The NewJavaClassDataModel is used to store all the base properties which would be needed to generate
* a new instance of a java class. Validations for these properties such as class name, package name,
* superclass, and modifiers are also provided.
*
* Clients must subclass this data model to use it and to cache and provide their own specific attributes. They should also provide their
* own validation methods and default values for the properties they add.
*
* The use of this class is EXPERIMENTAL and is subject to substantial changes.
*/
public class NewJavaClassDataModel extends ArtifactEditOperationDataModel {
/**
* Required, String property used to set the unqualified java class name for the new java class.
*/
public static final String CLASS_NAME = "NewJavaClassDataModel.CLASS_NAME"; //$NON-NLS-1$
/**
* Optional, String property used to set the source folder location for the new java class. The first
* source folder found in the project will be used if one is not specified.
*/
public static final String SOURCE_FOLDER = "NewJavaClassDataModel.SOURCE_FOLDER"; //$NON-NLS-1$
/**
* Optional, String property used to to set the java package for the new java class. The default
* package is used if one is not specified.
*/
public static final String JAVA_PACKAGE = "NewJavaClassDataModel.JAVA_PACKAGE"; //$NON-NLS-1$
/**
* Optional, String property used to set the qualified java class name of the superclass of the
* new java class.
*/
public static final String SUPERCLASS = "NewJavaClassDataModel.SUPERCLASS"; //$NON-NLS-1$
/**
* Optional, boolean property used to set the visibility of the new java class. This is true
* by default.
*/
public static final String MODIFIER_PUBLIC = "NewJavaClassDataModel.MODIFIER_PUBLIC"; //$NON-NLS-1$
/**
* Optional, boolean property used to set whether the new java class is abstract. This is false
* by default.
*/
public static final String MODIFIER_ABSTRACT = "NewJavaClassDataModel.MODIFIER_ABSTRACT"; //$NON-NLS-1$
/**
* Optional, boolean property used to set whether the new java class is declared final. This is false
* by default.
*/
public static final String MODIFIER_FINAL = "NewJavaClassDataModel.MODIFIER_FINAL"; //$NON-NLS-1$
/**
* Optional, List property of all the qualified names of interfaces the new java class should implement.
*/
public static final String INTERFACES = "NewJavaClassDataModel.INTERFACES"; //$NON-NLS-1$
/**
* Optional, boolean property used to set whether the new java class should generate a main method. This
* is false by default.
*/
public static final String MAIN_METHOD = "NewJavaClassDataModel.MAIN_METHOD"; //$NON-NLS-1$
/**
* Optional, boolean property used to set whether or not the constructor from the superclass should be
* generated in the new java class. The default value is true.
*/
public static final String CONSTRUCTOR = "NewJavaClassDataModel.CONSTRUCTOR"; //$NON-NLS-1$
/**
* Optional, boolean property used to set whether the new java class should add method stubs for unimplemented
* methods defined in the interfaces of the interface list. This is true by default.
*/
public static final String ABSTRACT_METHODS = "NewJavaClassDataModel.ABSTRACT_METHODS"; //$NON-NLS-1$
/**
* Subclasses may extend this method to perform their own validation. This method should
* not return null. It does not accept null as a parameter.
* @see NewJavaClassDataModel#doValidateProperty(String)
*
* @param folderFullPath
* @return IStatus
*/
protected IStatus validateJavaSourceFolder(String folderFullPath) {
IProject project = getTargetProject();
// Ensure project is not closed
if (project == null) {
String msg = J2EECommonMessages.getResourceString(J2EECommonMessages.ERR_JAVA_CLASS_FOLDER_NOT_EXIST);
return WTPCommonPlugin.createErrorStatus(msg);
}
// Ensure project is accessible.
if (!project.isAccessible()) {
String msg = J2EECommonMessages.getResourceString(J2EECommonMessages.ERR_JAVA_CLASS_FOLDER_NOT_EXIST);
return WTPCommonPlugin.createErrorStatus(msg);
}
// Ensure the project is a java project.
try {
if (!project.hasNature(JavaCore.NATURE_ID)) {
String msg = J2EECommonMessages.getResourceString(J2EECommonMessages.ERR_JAVA_CLASS_NOT_JAVA_PROJECT);
return WTPCommonPlugin.createErrorStatus(msg);
}
} catch (CoreException e) {
Logger.getLogger().log(e);
}
//TODO use module source folders
//IFolder sourcefolder = getJavaSourceFolder();
// Ensure the selected folder is a valid java source folder for the project
// if (sourcefolder == null || (sourcefolder != null && !sourcefolder.getFullPath().equals(new Path(folderFullPath)))) {
// String msg = J2EECommonMessages.getResourceString(J2EECommonMessages.ERR_JAVA_CLASS_FOLDER_NOT_SOURCE, new String[]{folderFullPath});
// return WTPCommonPlugin.createErrorStatus(msg);
// }
// Valid source is selected
return WTPCommonPlugin.OK_STATUS;
}
/**
* Subclasses may extend this method to perform their own retrieval of a default java source folder.
* This implementation returns the output folder if it is also a source folder, otherwise it returns
* the first source folder in the project. This method may return null.
*
* @return IFolder instance of default java source folder
*/
protected IFolder getDefaultJavaSourceFolder() {
IProject project = getTargetProject();
if (project == null)
return null;
IContainer output = JemProjectUtilities.getJavaProjectOutputContainer(project);
List sources = JemProjectUtilities.getSourceContainers(project);
//TODO: We need to be able to support the project as the source, but this would be a breaking change
if (sources == null || sources.isEmpty() || ((IContainer) sources.get(0)).getType() != IResource.FOLDER)
return null;
// If the source folder and output folder are the same, return that folder
if (output != null && sources.contains(output))
return (IFolder) output;
// Otherwise, return the first source folder
return (IFolder) sources.get(0);
}
/**
* Subclasses may extend this method to create their own specialized default WTP operation.
* This implementation creates an instance of the NewJavaClassOperation to create a new
* java class. This method will not return null.
* @see WTPOperationDataModel#getDefaultOperation()
* @see NewJavaClassOperation
*
* @return WTPOperation
*/
public WTPOperation getDefaultOperation() {
return new NewJavaClassOperation(this);
}
/**
* Subclasses may extend this method to perform their own retrieval mechanism.
* This implementation simply returns the JDT package fragment root for the selected source
* folder. This method may return null.
* @see IJavaProject#getPackageFragmentRoot(org.eclipse.core.resources.IResource)
*
* @return IPackageFragmentRoot
*/
public IPackageFragmentRoot getJavaPackageFragmentRoot() {
IProject project = getTargetProject();
IJavaProject aJavaProject = JemProjectUtilities.getJavaProject(project);
// Return the source folder for the java project of the selected project
if (aJavaProject != null) {
IFolder sourcefolder = getJavaSourceFolder();
if (sourcefolder != null)
return aJavaProject.getPackageFragmentRoot(sourcefolder);
}
return null;
}
/**
* Subclasses may extend this method to add their own data model's properties as valid base properties.
* @see org.eclipse.wst.common.frameworks.internal.operations.WTPOperationDataModel#initValidBaseProperties()
*/
protected void initValidBaseProperties() {
super.initValidBaseProperties();
addValidBaseProperty(SOURCE_FOLDER);
addValidBaseProperty(JAVA_PACKAGE);
addValidBaseProperty(CLASS_NAME);
addValidBaseProperty(SUPERCLASS);
addValidBaseProperty(MODIFIER_PUBLIC);
addValidBaseProperty(MODIFIER_ABSTRACT);
addValidBaseProperty(MODIFIER_FINAL);
addValidBaseProperty(INTERFACES);
addValidBaseProperty(MAIN_METHOD);
addValidBaseProperty(CONSTRUCTOR);
addValidBaseProperty(ABSTRACT_METHODS);
}
/**
* Subclasses may extend this method to add the default values for their own specific data
* model properties. This declares the default values for the new java class.
* This method does not accept null. It may return null.
* @see WTPOperationDataModel#getDefaultProperty(String)
*
* @param propertyName
* @return default object value of the property
*/
protected Object getDefaultProperty(String propertyName) {
// Get the default source folder for the project
if (propertyName.equals(SOURCE_FOLDER)) {
IFolder sourceFolder = getDefaultJavaSourceFolder();
if (sourceFolder != null && sourceFolder.exists())
return sourceFolder.getFullPath().toOSString();
}
// Use Object as the default superclass if one is not specified
if (propertyName.equals(SUPERCLASS)) {
return new String("java.lang.Object"); //$NON-NLS-1$
}
// Use public as default visibility
if (propertyName.equals(MODIFIER_PUBLIC)) {
return new Boolean(true);
}
// Generate constructors from the superclass by default
if (propertyName.equals(CONSTRUCTOR)) {
return new Boolean(true);
}
// Generate unimplemented methods from declared interfaces by default
if (propertyName.equals(ABSTRACT_METHODS)) {
return new Boolean(true);
}
return super.getDefaultProperty(propertyName);
}
/**
* Subclasses may override this method to provide their own validation of any of the data model's
* properties. This implementation ensures that a java class can be properly generated from
* the values as specified.
* This method will not return null. This method will not accept null as a parameter.
* @see WTPOperationDataModel#doValidateProperty(java.lang.String)
*
* @param propertyName
* @return IStatus of the validity of the specifiec property
*/
protected IStatus doValidateProperty(String propertyName) {
IStatus result = super.doValidateProperty(propertyName);
if (!result.isOK())
return result;
if (propertyName.equals(SOURCE_FOLDER))
return validateFolder(getStringProperty(propertyName));
if (propertyName.equals(JAVA_PACKAGE))
return validateJavaPackage(getStringProperty(propertyName));
if (propertyName.equals(CLASS_NAME)) {
result = validateJavaClassName(getStringProperty(propertyName));
if (result.isOK())
result = canCreateTypeInClasspath(getStringProperty(CLASS_NAME));
}
if (propertyName.equals(SUPERCLASS))
return validateSuperclass(getStringProperty(propertyName));
if (propertyName.equals(MODIFIER_ABSTRACT) || propertyName.equals(MODIFIER_FINAL))
return validateModifier(propertyName,getBooleanProperty(propertyName));
return result;
}
/**
* This method will ensure the source folder is not empty and forward the validation
* of the source folder. Subclasses may override the forwarded method.
* This method does accept null. It will not return null.
* @see NewJavaClassDataModel#validateJavaSourceFolder(String)
* @see NewJavaClassDataModel#doValidateProperty(String)
*
* @param folderFullPath
* @return IStatus
*/
private IStatus validateFolder(String folderFullPath) {
// Ensure that the source folder path is not empty
if (folderFullPath == null || folderFullPath.length() == 0) {
String msg = J2EECommonMessages.getResourceString(J2EECommonMessages.ERR_JAVA_CLASS_FOLDER_NAME_EMPTY);
return WTPCommonPlugin.createErrorStatus(msg);
}
// Validate the java source folder
return validateJavaSourceFolder(folderFullPath);
}
/**
* This method will validate whether the specified package name is a valid java package name. It will
* be called during the doValidateProperty(String).
* This method will accept null. It will not return null.
* @see NewJavaClassDataModel#doValidateProperty(String)
*
* @param packName -- the package name
* @return IStatus of if the package name is valid
*/
private IStatus validateJavaPackage(String packName) {
if (packName != null && packName.trim().length() > 0) {
// Use standard java conventions to validate the package name
IStatus javaStatus = JavaConventions.validatePackageName(packName);
if (javaStatus.getSeverity() == IStatus.ERROR) {
String msg = J2EECommonMessages.getResourceString(J2EECommonMessages.ERR_JAVA_PACAKGE_NAME_INVALID) + javaStatus.getMessage();
return WTPCommonPlugin.createErrorStatus(msg);
} else if (javaStatus.getSeverity() == IStatus.WARNING) {
String msg = J2EECommonMessages.getResourceString(J2EECommonMessages.ERR_JAVA_PACKAGE_NAME_WARNING) + javaStatus.getMessage();
return WTPCommonPlugin.createErrorStatus(msg);
}
}
// java package name is valid
return WTPCommonPlugin.OK_STATUS;
}
/**
* Subclasses may override this method to provide their own validation of the class name.
* This implementation will verify if the specified class name is a valid UNQUALIFIED java class name.
* This method will not accept null. It will not return null.
* @see NewJavaClassDataModel#doValidateProperty(String)
*
* @param className
* @return IStatus of if java class name is valid
*/
protected IStatus validateJavaClassName(String className) {
// Do not allow qualified name
if (className.lastIndexOf('.') != -1) {
String msg = J2EECommonMessages.getResourceString(J2EECommonMessages.ERR_JAVA_CLASS_NAME_QUALIFIED);
return WTPCommonPlugin.createErrorStatus(msg);
}
// Check Java class name by standard java conventions
IStatus javaStatus = JavaConventions.validateJavaTypeName(className);
if (javaStatus.getSeverity() == IStatus.ERROR) {
String msg = J2EECommonMessages.getResourceString(J2EECommonMessages.ERR_JAVA_CLASS_NAME_INVALID) + javaStatus.getMessage();
return WTPCommonPlugin.createErrorStatus(msg);
} else if (javaStatus.getSeverity() == IStatus.WARNING) {
String msg = J2EECommonMessages.getResourceString(J2EECommonMessages.ERR_JAVA_CLASS_NAME_WARNING) + javaStatus.getMessage();
return WTPCommonPlugin.createWarningStatus(msg);
}
return WTPCommonPlugin.OK_STATUS;
}
/**
* This method will verify the specified superclass can be subclassed. It ensures the
* superclass is a valid java class, that it exists, and that it is not final.
* This method will accept null. It will not return null.
* @see NewJavaClassDataModel#doValidateProperty(String)
*
* @param superclassName
* @return IStatus of if the superclass can be subclassed
*/
private IStatus validateSuperclass(String superclassName) {
// Ensure the superclass name is not empty
if (superclassName == null || superclassName.trim().length() == 0) {
String msg = J2EECommonMessages.getResourceString(J2EECommonMessages.ERR_JAVA_CLASS_NAME_EMPTY);
return WTPCommonPlugin.createErrorStatus(msg);
}
// In default case of Object, return OK right away
if (superclassName.equals("java.lang.Object")) //$NON-NLS-1$
return WTPCommonPlugin.OK_STATUS;
// Ensure the unqualified java class name of the superclass is valid
String className = superclassName;
int index = superclassName.lastIndexOf("."); //$NON-NLS-1$
if (index != -1) {
className = superclassName.substring(index + 1);
}
IStatus status = validateJavaClassName(className);
// If unqualified super class name is valid, ensure validity of superclass itself
if (status.isOK()) {
// If the superclass does not exist, throw an error
if (canCreateTypeInClasspath(className).isOK()) {
String msg = J2EECommonMessages.getResourceString(J2EECommonMessages.ERR_JAVA_CLASS_SUPERCLASS_NOT_EXIST);
return WTPCommonPlugin.createErrorStatus(msg);
}
// Ensure the superclass is not final
// int flags = -1;
// try {
// flags = superClassType.getFlags();
// } catch (Exception e) {
// Logger.getLogger().log(e);
// }
// if (Modifier.isFinal(flags)) {
// String msg = J2EECommonMessages.getResourceString(J2EECommonMessages.ERR_JAVA_CLASS_SUPERCLASS_FINAL);
// return WTPCommonPlugin.createErrorStatus(msg);
// }
}
// Return status of specified superclass
return status;
}
/**
* This method will ensure that if the abstract modifier is set, that final is not set,
* and vice-versa, as this is not supported either way.
* This method will not accept null. It will not return null.
* @see NewJavaClassDataModel#doValidateProperty(String)
*
* @param prop
* @return IStatus of whether abstract value is valid
*/
private IStatus validateModifier(String propertyName, boolean prop) {
// Throw an error if both Abstract and Final are checked
if (prop) {
// Ensure final is not also checked
if (propertyName.equals(MODIFIER_ABSTRACT) && getBooleanProperty(MODIFIER_FINAL)) {
String msg = J2EECommonMessages.getResourceString(J2EECommonMessages.ERR_BOTH_FINAL_AND_ABSTRACT);
return WTPCommonPlugin.createErrorStatus(msg);
}
// Ensure abstract is not also checked
if (propertyName.equals(MODIFIER_FINAL) && getBooleanProperty(MODIFIER_ABSTRACT)) {
String msg = J2EECommonMessages.getResourceString(J2EECommonMessages.ERR_BOTH_FINAL_AND_ABSTRACT);
return WTPCommonPlugin.createErrorStatus(msg);
}
}
// Abstract and final settings are valid
return WTPCommonPlugin.OK_STATUS;
}
/**
* This method will return the qualified java class name as specified by the class name
* and package name properties in the data model.
* This method should not return null.
* @see NewJavaClassDataModel#CLASS_NAME
* @see NewJavaClassDataModel#JAVA_PACKAGE
*
* @return String qualified java classname
*/
public final String getQualifiedClassName() {
// Use the java package name and unqualified class name to create a qualified java class name
String packageName = getStringProperty(JAVA_PACKAGE);
String className = getStringProperty(CLASS_NAME);
//Ensure the class is not in the default package before adding package name to qualified name
if (packageName != null && packageName.trim().length() > 0)
return packageName + "." + className; //$NON-NLS-1$
return className;
}
/**
* This method is intended for internal use only. This will check the java model for the specified
* javaproject in the data model for the existence of the passed in qualified classname.
* This method does not accept null. It may return null.
* @see NewJavaClassDataModel#getTargetProject()
* @see JavaModelUtil#findType(IJavaProject, String)
*
* @param fullClassName
* @return IType for the specified classname
*/
private IStatus canCreateTypeInClasspath(String className) {
// Retrieve the java project for the cached project
IJavaProject javaProject = JemProjectUtilities.getJavaProject(getTargetProject());
try {
IPath path = new Path(getStringProperty(SOURCE_FOLDER)+"//"+getStringProperty(JAVA_PACKAGE)); //$NON-NLS-1$
IPackageFragment pack= javaProject.findPackageFragment(path);
if (pack != null) {
ICompilationUnit cu= pack.getCompilationUnit(className + ".java"); //$NON-NLS-1$
IResource resource= cu.getResource();
if (resource.exists()) {
String msg = J2EECommonMessages.getResourceString(J2EECommonMessages.ERR_JAVA_CLASS_NAME_EXIST);
return WTPCommonPlugin.createErrorStatus(msg);
}
}
return WTPCommonPlugin.OK_STATUS;
} catch (Exception e) {
Logger.getLogger().log(e);
}
return WTPCommonPlugin.OK_STATUS;
}
/**
* This will return the IFolder instance for the specified folder name in the data model.
* This method may return null.
* @see NewJavaClassDataModel#SOURCE_FOLDER
* @see NewJavaClassDataModel#getAllSourceFolders()
*
* @return IFolder java source folder
*/
protected final IFolder getJavaSourceFolder() {
List sources = getAllSourceFolders();
//Ensure there is valid source folder(s)
if (sources == null || sources.isEmpty() || ((IContainer) sources.get(0)).getType() != IResource.FOLDER)
return null;
String folderFullPath = getStringProperty(SOURCE_FOLDER);
// Get the source folder whose path matches the source folder name value in the data model
for (int i = 0; i < sources.size(); i++) {
IFolder folder = (IFolder) sources.get(i);
if (folder.getFullPath().equals(new Path(folderFullPath))) {
return folder;
}
}
return null;
}
/**
* This method is intended for internal use only. This will retrieve the list of
* all source folders in the target project as specified in the data model.
* This method may return null.
* @see NewJavaClassDataModel#getTargetProject()
* @see ProjectUtilities#getSourceContainers(org.eclipse.core.resources.IProject)
*
* @return List of all source folders in the target project
*/
private List getAllSourceFolders() {
// Retrieve target project
IProject project = getTargetProject();
// If project is null, return null
if (project == null)
return null;
// Return all source containers in the specified project
List sources = JemProjectUtilities.getSourceContainers(project);
return sources;
}
protected boolean doSetProperty(String propertyName, Object propertyValue) {
return super.doSetProperty(propertyName, propertyValue);
}
}