blob: 33c8f7df5608329862980744f02d3b4a3e0b5ece [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.web.operations;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jem.util.emf.workbench.JavaProjectUtilities;
import org.eclipse.jst.j2ee.application.internal.operations.IAnnotationsDataModel;
import org.eclipse.jst.j2ee.internal.J2EEVersionConstants;
import org.eclipse.jst.j2ee.internal.common.operations.NewJavaClassDataModel;
import org.eclipse.jst.j2ee.internal.web.operations.WebPropertiesUtil;
import org.eclipse.jst.j2ee.web.componentcore.util.WebArtifactEdit;
import org.eclipse.jst.j2ee.webapplication.Servlet;
import org.eclipse.jst.j2ee.webapplication.WebApp;
import org.eclipse.wst.common.componentcore.ArtifactEdit;
import org.eclipse.wst.common.componentcore.resources.ComponentHandle;
import org.eclipse.wst.common.frameworks.internal.operations.WTPOperation;
import org.eclipse.wst.common.frameworks.internal.plugin.WTPCommonPlugin;
/**
* The NewServletClassData model is a subclass of WTPOperationDataModel and follows the WTP Operation and WTP Wizard frameworks.
* @see org.eclipse.wst.common.frameworks.internal.operations.WTPOperationDataModel
* @see org.eclipse.wst.common.frameworks.internal.operations.WTPOperation
*
* This data model is a subclass of the NewJavaClassDataModel, which stores base properties necessary in
* the creation of a default java class.
* @see org.eclipse.jst.j2ee.internal.common.operations.NewJavaClassDataModel
*
* The NewServletClassDataModel provides more specific properties for java class creation that are required
* in creating a servlet java class. The data model is used to store these values for the NewServletClassOperation.
* That operation will create the servlet java class based on the settings defined here in the data model.
* @see org.eclipse.jst.j2ee.internal.web.operations.NewServletClassOperation
*
* This data model implements the IAnnotationsDataModel to get the USE_ANNOTATIONS property for determining
* whether or not to generate an annotated java class.
* @see org.eclipse.jst.j2ee.application.internal.operations.IAnnotationsDataModel
*
* Clients can subclass this data model 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 NewServletClassDataModel extends NewJavaClassDataModel implements IAnnotationsDataModel {
/**
* Optional, boolean property used to specify whether to generate the init method.
* The default is false.
*/
public static final String INIT = "NewServletClassDataModel.INIT"; //$NON-NLS-1$
/**
* Optional, boolean property used to specify whether to generate the doPost method.
* The default is true.
*/
public static final String DO_POST = "NewServletClassDataModel.DO_POST"; //$NON-NLS-1$
/**
* Optional, boolean property used to specify whether to generate the destroy method.
* The default is false.
*/
public static final String DESTROY = "NewServletClassDataModel.DESTROY"; //$NON-NLS-1$
/**
* Optional, boolean property used to specify whether to generate the toString method.
* The default is false.
*/
public static final String TO_STRING = "NewServletClassDataModel.TO_STRING"; //$NON-NLS-1$
/**
* Optional, boolean property used to specify whether to generate the doPut method.
* The default is false.
*/
public static final String DO_PUT = "NewServletClassDataModel.DO_PUT"; //$NON-NLS-1$
/**
* Optional, boolean property used to specify whether to generate the doGet method.
* The default is true.
*/
public static final String DO_GET = "NewServletClassDataModel.DO_GET"; //$NON-NLS-1$
/**
* Optional, boolean property used to specify whether to generate the getServletInfo method.
* The default is false.
*/
public static final String GET_SERVLET_INFO = "NewServletClassDataModel.GET_SERVLET_INFO"; //$NON-NLS-1$
/**
* Optional, boolean property used to specify whether to generate the doDelete method.
* The default is false.
*/
public static final String DO_DELETE = "NewServletClassDataModel.DO_DELETE"; //$NON-NLS-1$
/**
* Optional, boolean property used to determine if building JSP or servlet.
* The default is true.
*/
public static final String IS_SERVLET_TYPE = "NewServletClassDataModel.IS_SERVLET_TYPE"; //$NON-NLS-1$
/**
* Optional, List property used to cache all the init params defined on the servlet.
*/
public static final String INIT_PARAM = "NewServletClassDataModel.INIT_PARAM"; //$NON-NLS-1$
/**
* Optional, List propety used to cache all the servlet mappings for this servlet on the web application.
*/
public static final String URL_MAPPINGS = "NewServletClassDataModel.URL_MAPPINGS"; //$NON-NLS-1$
/**
* Required, String property of the display name for the servlet
*/
public static final String DISPLAY_NAME = "NewServletClassDataModel.DISPLAY_NAME"; //$NON-NLS-1$
/**
* Optional, String property of the description info for the servlet
*/
public static final String DESCRIPTION = "NewServletClassDataModel.DESCRIPTION"; //$NON-NLS-1$
/**
* The fully qualified default servlet superclass: HttpServlet.
*/
public final static String SERVLET_SUPERCLASS = "javax.servlet.http.HttpServlet"; //$NON-NLS-1$
/**
* String array of the default, minimum required fully qualified Servlet interfaces
*/
public final static String[] SERVLET_INTERFACES = {"javax.servlet.Servlet"}; //$NON-NLS-1$
/**
* The javajet template file used in creating the servlet template class
*/
public static final String TEMPLATE_FILE = "servletXDoclet.javajet"; //$NON-NLS-1$
public static final String NON_ANNOTATED_TEMPLATE_FILE = "servletXDocletNonAnnotated.javajet"; //$NON-NLS-1$
/**
* The cache of all the interfaces the servlet java class will implement.
*/
private List interfaceList;
private static boolean useAnnotations = true;
/**
* Subclasses may extend this method to provide their own default operation for this
* data model. This implementation uses the AddServletOperation to drive the servlet
* creation. It will not return null.
* @see org.eclipse.wst.common.frameworks.internal.operations.WTPOperationDataModel#getDefaultOperation()
*
* @return WTPOperation AddServletOperation
*/
public WTPOperation getDefaultOperation() {
return new AddServletOperation(this);
}
/**
* Subclasses may extend this method to provide their own determination of whether or not
* certain properties should be disabled or enabled. This method does not accept null parameter.
* It will not return null. This implementation makes sure annotation support is only allowed
* on web projects of J2EE version 1.3 or higher.
* @see org.eclipse.wst.common.frameworks.internal.operations.WTPOperationDataModel#basicIsEnabled(String)
* @see IAnnotationsDataModel#USE_ANNOTATIONS
*
* @param propertyName
* @return Boolean should property be enabled?
*/
protected Boolean basicIsEnabled(String propertyName) {
// Annotations should only be enabled on a valid j2ee project of version 1.3 or higher
if (USE_ANNOTATIONS.equals(propertyName)) {
if (!isAnnotationsSupported())
return Boolean.FALSE;
return Boolean.TRUE;
}
// Otherwise return super implementation
return super.basicIsEnabled(propertyName);
}
/**
* Subclasses may extend this method to add their own specific data model properties as valid
* base properties. This implementation adds the servlet specific properties to those added
* by the NewJavaClassDataModel.
* @see NewJavaClassDataModel#initValidBaseProperties()
* @see org.eclipse.wst.common.frameworks.internal.operations.WTPOperationDataModel#initValidBaseProperties()
*/
protected void initValidBaseProperties() {
super.initValidBaseProperties();
// Add servlet specific properties defined in this data model
addValidBaseProperty(INIT);
addValidBaseProperty(DO_POST);
addValidBaseProperty(DESTROY);
addValidBaseProperty(TO_STRING);
addValidBaseProperty(DO_PUT);
addValidBaseProperty(DO_GET);
addValidBaseProperty(GET_SERVLET_INFO);
addValidBaseProperty(DO_DELETE);
addValidBaseProperty(IS_SERVLET_TYPE);
addValidBaseProperty(INIT_PARAM);
addValidBaseProperty(URL_MAPPINGS);
addValidBaseProperty(USE_ANNOTATIONS);
addValidBaseProperty(DISPLAY_NAME);
addValidBaseProperty(DESCRIPTION);
}
/**
* Subclasses may extend this method to provide their own default values for
* any of the properties in the data model hierarchy. This method does not
* accept a null parameter. It may return null. This implementation sets
* annotation use to be true, and to generate a servlet with doGet and doPost.
* @see NewJavaClassDataModel#getDefaultProperty(String)
* @see org.eclipse.wst.common.frameworks.internal.operations.WTPOperationDataModel#getDefaultProperty(String)
*
* @param propertyName
* @return Object default value of property
*/
protected Object getDefaultProperty(String propertyName) {
// Generate a doPost method by default
if (propertyName.equals(DO_POST))
return new Boolean(true);
// Generate a doGet method by default
else if (propertyName.equals(DO_GET))
return new Boolean(true);
// Use servlet by default
else if (propertyName.equals(IS_SERVLET_TYPE))
return new Boolean(true);
// Create an annotated servlet java class by default
else if (propertyName.equals(USE_ANNOTATIONS))
return shouldDefaultAnnotations();
else if (propertyName.equals(DISPLAY_NAME))
return getProperty(CLASS_NAME);
else if (propertyName.equals(URL_MAPPINGS)) {
return getDefaultUrlMapping();
}
// Otherwise check super for default value for property
return super.getDefaultProperty(propertyName);
}
/**
* Returns the default Url Mapping depending upon the display name of
* the Servlet
* @return List containting the default Url Mapping
*/
private Object getDefaultUrlMapping() {
List urlMappings = null;
String text = (String)getProperty(DISPLAY_NAME);
if (text != null) {
urlMappings = new ArrayList();
urlMappings.add(new String[]{"/" + text}); //$NON-NLS-1$
}
return urlMappings;
}
/**
* Subclasses may extend this method to add their own specific behaviour when a certain
* property in the data model heirarchy is set. This method does not accept null for
* the property name, but it will for propertyValue. It will not return null. It will return
* false if the set fails. This implementation verifies the display name is set to the
* classname, that the annotations is disabled/enabled properly, and that the target project
* name is determined from the source folder setting.
* @see org.eclipse.wst.common.frameworks.internal.operations.WTPOperationDataModel#doSetProperty(String, Object)
*
* @param propertyName
* @param propertyValue
* @return boolean was property set?
*/
protected boolean doSetProperty(String propertyName, Object propertyValue) {
// If annotations is changed, notify an enablement change
if (propertyName.equals(USE_ANNOTATIONS)) {
useAnnotations = ((Boolean) propertyValue).booleanValue();
if (useAnnotations && !isAnnotationsSupported())
return true;
notifyEnablementChange(USE_ANNOTATIONS);
}
// If the source folder is changed, ensure we have the correct project name
if (propertyName.equals(SOURCE_FOLDER)) {
// Get the project name from the source folder name
String sourceFolder = (String) propertyValue;
int index = sourceFolder.indexOf(File.separator);
String projectName = sourceFolder;
if (index == 0)
projectName = sourceFolder.substring(1);
index = projectName.indexOf(File.separator);
if (index != -1) {
projectName = projectName.substring(0, index);
setProperty(PROJECT_NAME, projectName);
}
}
// Call super to set the property on the data model
boolean result = super.doSetProperty(propertyName, propertyValue);
// If class name is changed, update the display name to be the same
if (propertyName.equals(CLASS_NAME) && !isSet(DISPLAY_NAME)) {
notifyDefaultChange(DISPLAY_NAME);
}
// After the property is set, if project changed, update the nature and the annotations enablement
if (propertyName.equals(MODULE_NAME)) {
notifyEnablementChange(USE_ANNOTATIONS);
}
// After property is set, if annotations is set to true, update its value based on the new level of the project
if (getBooleanProperty(USE_ANNOTATIONS)) {
if (!isAnnotationsSupported())
setBooleanProperty(USE_ANNOTATIONS, false);
}
// Return whether property was set
return result;
}
protected boolean isAnnotationsSupported() {
if (getTargetProject()==null || getComponent()==null) return true;
WebArtifactEdit webEdit = null;
try {
webEdit = WebArtifactEdit.getWebArtifactEditForRead(getComponent());
if (webEdit == null)
return false;
return webEdit.getJ2EEVersion() > J2EEVersionConstants.VERSION_1_2;
} catch (Exception e) {
e.printStackTrace();
return false;
} finally {
if (webEdit != null)
webEdit.dispose();
}
}
/**
* Subclasses may extend this method to provide their own validation on any of the valid
* data model properties in the hierarchy. This implementation adds validation for
* the init params, servlet mappings, display name, and existing class
* fields specific to the servlet java class creation. It does not accept a null
* parameter. This method will not return null.
* @see NewJavaClassDataModel#doValidateProperty(String)
* @see org.eclipse.wst.common.frameworks.internal.operations.WTPOperationDataModel#doValidateProperty(String)
*
* @param propertyName
* @return IStatus is property value valid?
*/
protected IStatus doValidateProperty(String propertyName) {
// If our default is the superclass, we know it is ok
if (propertyName.equals(SUPERCLASS) && getStringProperty(propertyName).equals(SERVLET_SUPERCLASS))
return WTPCommonPlugin.OK_STATUS;
IStatus result = super.doValidateProperty(propertyName);
if (!result.isOK())
return result;
// Validate init params
if (propertyName.equals(INIT_PARAM))
return validateInitParamList((List) getProperty(propertyName));
// Validate servlet mappings
if (propertyName.equals(URL_MAPPINGS))
return validateURLMappingList((List) getProperty(propertyName));
// Validate the servlet name in DD
if (propertyName.equals(DISPLAY_NAME))
return validateDisplayName(getStringProperty(propertyName));
// Otherwise defer to super to validate the property
return result;
}
/**
* Subclasses may extend this method to provide their own validation of the specified
* java classname. This implementation will ensure the class name is not set to Servlet
* and then will forward on to the NewJavaClassDataModel to validate the class name as
* valid java. This method does not accept null as a parameter. It will not return null.
* @see NewServletClassDataModel#validateExistingClass(boolean)
* @see NewJavaClassDataModel#validateJavaClassName(String)
*
* @param className
* @return IStatus is java classname valid?
*/
protected IStatus validateJavaClassName(String className) {
// First use the NewJavaClassDataModel to validate the classname as proper java syntax
IStatus status = super.validateJavaClassName(className);
if (status.isOK()) {
// Do not allow the name to be "Servlet"
if (className.equals("Servlet")) { //$NON-NLS-1$
String msg = WebMessages.getResourceString(WebMessages.ERR_SERVLET_JAVA_CLASS_NAME_INVALID);
return WTPCommonPlugin.createErrorStatus(msg);
}
return WTPCommonPlugin.OK_STATUS;
}
// Return the status
return status;
}
/**
* This method is intended for internal use only. It will be used to validate the init params list
* to ensure there are not any duplicates. This method will accept a null paramter. It will
* not return null.
* @see NewServletClassDataModel#doValidateProperty(String)
*
* @param prop
* @return IStatus is init params list valid?
*/
private IStatus validateInitParamList(List prop) {
if (prop != null && !prop.isEmpty()) {
// Ensure there are not duplicate entries in the list
boolean dup = hasDuplicatesInStringArrayList(prop);
if (dup) {
String msg = WebMessages.getResourceString(WebMessages.ERR_DUPLICATED_INIT_PARAMETER);
return WTPCommonPlugin.createErrorStatus(msg);
}
}
// Return OK
return WTPCommonPlugin.OK_STATUS;
}
/**
* This method is intended for internal use only. This will validate the servlet mappings
* list and ensure there are not duplicate entries. It will accept a null parameter.
* It will not return null.
* @see NewServletClassDataModel#doValidateProperty(String)
*
* @param prop
* @return IStatus is servlet mapping list valid?
*/
private IStatus validateURLMappingList(List prop) {
if (prop != null && !prop.isEmpty()) {
// Ensure there are not duplicates in the mapping list
boolean dup = hasDuplicatesInStringArrayList(prop);
if (dup) {
String msg = WebMessages.getResourceString(WebMessages.ERR_DUPLICATED_URL_MAPPING);
return WTPCommonPlugin.createErrorStatus(msg);
}
} else {
String msg = WebMessages.getResourceString(WebMessages.ERR_SERVLET_MAPPING_URL_PATTERN_EMPTY);
return WTPCommonPlugin.createErrorStatus(msg);
}
// Return OK
return WTPCommonPlugin.OK_STATUS;
}
/**
* This method is intended for internal use only. It provides a simple
* algorithm for detecting if there are duplicate entries in a list. It will
* accept a null paramter. It will not return null.
* @see NewServletClassDataModel#validateInitParamList(List)
* @see NewServletClassDataModel#validateURLMappingList(List)
*
* @param input
* @return boolean are there dups in the list?
*/
private boolean hasDuplicatesInStringArrayList(List input) {
// If list is null or empty return false
if (input == null)
return false;
int n = input.size();
boolean dup = false;
// nested for loops to check each element to see if other elements are the same
for (int i = 0; i < n; i++) {
String[] sArray1 = (String[]) input.get(i);
for (int j = i + 1; j < n; j++) {
String[] sArray2 = (String[]) input.get(j);
if (isTwoStringArraysEqual(sArray1, sArray2)) {
dup = true;
break;
}
}
if (dup)
break;
}
// Return boolean status for duplicates
return dup;
}
/**
* This method is intended for internal use only. This checks to see if the two string
* arrays are equal. If either of the arrays are null or empty, it returns false.
* @see NewServletClassDataModel#hasDuplicatesInStringArrayList(List)
*
* @param sArray1
* @param sArray2
* @return boolean are Arrays equal?
*/
private boolean isTwoStringArraysEqual(String[] sArray1, String[] sArray2) {
// If either array is null, return false
if (sArray1 == null || sArray2 == null)
return false;
int n1 = sArray1.length;
int n2 = sArray1.length;
// If either array is empty, return false
if (n1 == 0 || n2 == 0)
return false;
// If they don't have the same length, return false
if (n1 != n2)
return false;
// If their first elements do not match, return false
if (!sArray1[0].equals(sArray2[0]))
return false;
// Otherwise return true
return true;
}
/**
* This method will return the list of servlet interfaces to be implemented for the
* new servlet java class. It will intialize the list using lazy initialization to
* the minimum interfaces required by the data model SERVLET_INTERFACES. This method
* will not return null.
* @see NewServletClassDataModel#SERVLET_INTERFACES
*
* @return List of servlet interfaces to be implemented
*/
public final List getServletInterfaces() {
if (interfaceList == null) {
interfaceList = new ArrayList();
// Add minimum required list of servlet interfaces to be implemented
for (int i = 0; i < SERVLET_INTERFACES.length; i++) {
interfaceList.add(SERVLET_INTERFACES[i]);
}
}
// Return interface list
return interfaceList;
}
/**
* This method is intended for internal use only. This will validate whether the display name
* selected is a valid display name for the servlet in the specified web application. It will
* make sure the name is not empty and that it doesn't already exist in the web app. This
* method will accept null as a parameter. It will not return null.
* @see NewServletClassDataModel#doValidateProperty(String)
*
* @param prop
* @return IStatus is servlet display name valid?
*/
private IStatus validateDisplayName(String prop) {
// Ensure the servlet display name is not null or empty
if (prop == null || prop.trim().length() == 0) {
String msg = WebMessages.getResourceString(WebMessages.ERR_DISPLAY_NAME_EMPTY);
return WTPCommonPlugin.createErrorStatus(msg);
}
if (getTargetProject()==null || getComponent()==null)
return WTPCommonPlugin.OK_STATUS;
ArtifactEdit edit = null;
try {
edit = getArtifactEditForRead();
WebApp webApp = (WebApp) edit.getContentModelRoot();
if (webApp == null)
return WTPCommonPlugin.OK_STATUS;
List servlets = webApp.getServlets();
boolean exists = false;
// Ensure the display does not already exist in the web application
if (servlets != null && !servlets.isEmpty()) {
for (int i = 0; i < servlets.size(); i++) {
String name = ((Servlet) servlets.get(i)).getServletName();
if (prop.equals(name))
exists = true;
}
}
// If the servlet name already exists, throw an error
if (exists) {
String msg = WebMessages.getResourceString(WebMessages.ERR_SERVLET_DISPLAY_NAME_EXIST, new String[]{prop});
return WTPCommonPlugin.createErrorStatus(msg);
}
} finally {
if (edit!=null)
edit.dispose();
}
//Otherwise, return OK
return WTPCommonPlugin.OK_STATUS;
}
/**
* 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() {
// Ensure project is not null
IProject project = getTargetProject();
if (project == null)
return null;
IJavaProject javaProject = JavaProjectUtilities.getJavaProject(project);
if (javaProject != null) {
// Ensure source folder exists
IFolder sourcefolder = getJavaSourceFolder();
// Get the java package for the selected folder
if (sourcefolder != null)
return javaProject.getPackageFragmentRoot(sourcefolder);
}
return null;
}
/**
* Subclasses may extend this method to perform their own retrieval of a default java source folder.
* This implementation returns the JavaSource folder as default since we are in a
* web project. This method may return null.
* @see NewJavaClassDataModel#getDefaultJavaSourceFolder()
*
* @return IFolder default java source folder
*/
protected IFolder getDefaultJavaSourceFolder() {
// Ensure project is not null
IProject project = getTargetProject();
if (project == null)
return null;
return (IFolder) WebPropertiesUtil.getJavaSourceFolder(project);
}
/**
* @return boolean should the default annotations be true?
*/
private static Boolean shouldDefaultAnnotations() {
if (useAnnotations)
return Boolean.TRUE;
return Boolean.FALSE;
}
}