blob: 6ce9b6389443316e0978e93d5431408177e0e4d6 [file] [log] [blame]
package org.eclipse.jst.j2ee.internal.web.operations;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jem.util.emf.workbench.ProjectUtilities;
import org.eclipse.jst.j2ee.application.internal.operations.IAnnotationsDataModel;
import org.eclipse.jst.j2ee.internal.J2EEVersionConstants;
import org.eclipse.jst.j2ee.internal.common.J2EEVersionUtil;
import org.eclipse.jst.j2ee.internal.common.operations.NewJavaClassDataModelProvider;
import org.eclipse.jst.j2ee.internal.project.J2EEProjectUtilities;
import org.eclipse.jst.j2ee.model.IModelProvider;
import org.eclipse.jst.j2ee.model.ModelProviderManager;
import org.eclipse.jst.j2ee.web.validation.UrlPattern;
import org.eclipse.osgi.util.NLS;
import org.eclipse.wst.common.componentcore.internal.operation.IArtifactEditOperationDataModelProperties;
import org.eclipse.wst.common.frameworks.datamodel.IDataModel;
import org.eclipse.wst.common.frameworks.datamodel.IDataModelOperation;
import org.eclipse.wst.common.frameworks.datamodel.IDataModelProvider;
import org.eclipse.wst.common.frameworks.internal.plugin.WTPCommonPlugin;
public class NewFilterClassDataModelProvider extends NewJavaClassDataModelProvider implements INewFilterClassDataModelProperties {
/**
* String array of the default, minimum required fully qualified Filter interfaces
*/
private final static String[] FILTER_INTERFACES = {"javax.servlet.Filter"}; //$NON-NLS-1$
private final static String NON_ANNOTATED_TEMPLATE_DEFAULT = "filterNonAnnotated.javajet"; //$NON-NLS-1$
/**
* The cache of all the interfaces the filter java class will implement.
*/
private List interfaceList;
private String classNameCache;
private String existingClassNameCache;
private static boolean useAnnotations = false;
/**
* Subclasses may extend this method to provide their own default operation for this data model
* provider. This implementation uses the AddFilterOperation to drive the filter creation. It
* will not return null.
*
* @see IDataModel#getDefaultOperation()
*
* @return IDataModelOperation AddFilterOperation
*/
public IDataModelOperation getDefaultOperation() {
return new AddFilterOperation(getDataModel());
}
/**
* 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.datamodel.IDataModelProvider#isPropertyEnabled(String)
* @see IAnnotationsDataModel#USE_ANNOTATIONS
*
* @param propertyName
* @return boolean should property be enabled?
*/
public boolean isPropertyEnabled(String propertyName) {
// Annotations should only be enabled on a valid j2ee project of version 1.3 or higher
if (USE_ANNOTATIONS.equals(propertyName)) {
if (getBooleanProperty(USE_EXISTING_CLASS) || !isAnnotationsSupported())
return false;
return true;
}
// Otherwise return super implementation
return super.isPropertyEnabled(propertyName);
}
/**
* Subclasses may extend this method to add their own data model's properties as valid base
* properties.
*
* @see org.eclipse.wst.common.frameworks.datamodel.IDataModelProvider#getPropertyNames()
*/
public Set getPropertyNames() {
// Add filter specific properties defined in this data model
Set propertyNames = super.getPropertyNames();
propertyNames.add(INIT);
propertyNames.add(DESTROY);
propertyNames.add(TO_STRING);
propertyNames.add(DO_FILTER);
propertyNames.add(INIT_PARAM);
propertyNames.add(FILTER_MAPPINGS);
propertyNames.add(USE_ANNOTATIONS);
propertyNames.add(DISPLAY_NAME);
propertyNames.add(DESCRIPTION);
propertyNames.add(USE_EXISTING_CLASS);
return propertyNames;
}
/**
* 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 filter
* with doFilter.
*
* @see NewJavaClassDataModelProvider#getDefaultProperty(String)
* @see IDataModelProvider#getDefaultProperty(String)
*
* @param propertyName
* @return Object default value of property
*/
public Object getDefaultProperty(String propertyName) {
if (propertyName.equals(DISPLAY_NAME)) {
String className = getStringProperty(CLASS_NAME);
int index = className.lastIndexOf("."); //$NON-NLS-1$
className = className.substring(index+1);
return className;
}
else if (propertyName.equals(USE_ANNOTATIONS))
return shouldDefaultAnnotations();
else if (propertyName.equals(DESTROY))
return Boolean.TRUE;
else if (propertyName.equals(DO_FILTER))
return Boolean.TRUE;
else if (propertyName.equals(INIT))
return Boolean.TRUE;
else if (propertyName.equals(FILTER_MAPPINGS))
return getDefaultFilterMapping();
else if (propertyName.equals(INTERFACES))
return getFilterInterfaces();
else if (propertyName.equals(SUPERCLASS))
return "";
else if (propertyName.equals(USE_EXISTING_CLASS))
return Boolean.FALSE;
// Otherwise check super for default value for property
return super.getDefaultProperty(propertyName);
}
/**
* Returns the default Url Mapping depending upon the display name of the Filter
*
* @return List containting the default Url Mapping
*/
private Object getDefaultFilterMapping() {
List filterMappings = null;
String text = (String) getProperty(DISPLAY_NAME);
if (text != null) {
filterMappings = new ArrayList();
filterMappings.add(new FilterMappingItem(FilterMappingItem.URL_PATTERN, "/" + text)); //$NON-NLS-1$
}
return filterMappings;
}
/**
* 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.datamodel.IDataModelProvider#propertySet(String,
* Object)
*
* @param propertyName
* @param propertyValue
* @return boolean was property set?
*/
public boolean propertySet(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;
getDataModel().notifyPropertyChange(USE_ANNOTATIONS, IDataModel.ENABLE_CHG);
}
// 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.propertySet(propertyName, propertyValue);
// If class name is changed, update the display name to be the same
if (propertyName.equals(CLASS_NAME) && !getDataModel().isPropertySet(DISPLAY_NAME)) {
getDataModel().notifyPropertyChange(DISPLAY_NAME, IDataModel.DEFAULT_CHG);
}
// After the property is set, if project changed, update the nature and the annotations
// enablement
if (propertyName.equals(COMPONENT_NAME)) {
getDataModel().notifyPropertyChange(USE_ANNOTATIONS, IDataModel.ENABLE_CHG);
}
// 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);
}
if (propertyName.equals(USE_EXISTING_CLASS)) {
getDataModel().notifyPropertyChange(USE_ANNOTATIONS, IDataModel.ENABLE_CHG);
if (((Boolean) propertyValue).booleanValue()) {
classNameCache = getStringProperty(CLASS_NAME);
setProperty(CLASS_NAME, existingClassNameCache);
} else {
existingClassNameCache = getStringProperty(CLASS_NAME);
setProperty(CLASS_NAME, classNameCache);
}
}
// Return whether property was set
return result;
}
/**
* This method checks to see if valid annotation providers exist and if valid project version levels exist.
* @return should annotations be supported for this project in this workspace
*/
protected boolean isAnnotationsSupported() {
//TODO add this check back in for defect 146696
// if (!isAnnotationProviderDefined())
// return false;
if (!getDataModel().isPropertySet(IArtifactEditOperationDataModelProperties.PROJECT_NAME))
return true;
if (getStringProperty(IArtifactEditOperationDataModelProperties.PROJECT_NAME).equals("")) //$NON-NLS-1$
return true;
IProject project = ProjectUtilities.getProject(getStringProperty(IArtifactEditOperationDataModelProperties.PROJECT_NAME));
String moduleName = getStringProperty(IArtifactEditOperationDataModelProperties.COMPONENT_NAME);
if (project == null || moduleName == null || moduleName.equals(""))return true; //$NON-NLS-1$
int j2eeVersion = J2EEVersionUtil.convertVersionStringToInt(J2EEProjectUtilities.getJ2EEProjectVersion(project));
return j2eeVersion > J2EEVersionConstants.VERSION_1_2;
}
/**
* 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,
* filter mappings, display name, and existing class fields specific to the filter java class
* creation. It does not accept a null parameter. This method will not return null.
*
* @see NewJavaClassDataModelProvider#validate(String)
*
* @param propertyName
* @return IStatus is property value valid?
*/
public IStatus validate(String propertyName) {
IStatus result = Status.OK_STATUS;
// If our default is the superclass, we know it is ok
if (propertyName.equals(SUPERCLASS) && "".equals(getStringProperty(propertyName)))
return WTPCommonPlugin.OK_STATUS;
// Validate init params
if (propertyName.equals(INIT_PARAM))
return validateInitParamList((List) getProperty(propertyName));
// Validate url pattern and servlet name mappings
if (propertyName.equals(FILTER_MAPPINGS))
return validateFilterMappingList((List) getProperty(FILTER_MAPPINGS));
// Validate the filter name in DD
if (propertyName.equals(DISPLAY_NAME))
return validateDisplayName(getStringProperty(propertyName));
if (propertyName.equals(CLASS_NAME)) {
if (getStringProperty(propertyName).length()!=0 && getBooleanProperty(USE_EXISTING_CLASS))
return WTPCommonPlugin.OK_STATUS;
result = super.validateJavaClassName(getStringProperty(propertyName));
if (result.isOK()) {
result = validateJavaClassName(getStringProperty(propertyName));
if (result.isOK()&&!getBooleanProperty(USE_EXISTING_CLASS))
result = canCreateTypeInClasspath(getStringProperty(CLASS_NAME));
}
return result;
}
// Otherwise defer to super to validate the property
return super.validate(propertyName);
}
/**
* 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 NewFilterClassDataModelProvider#validate(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.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 filter
* mappings list and ensure there are not duplicate entries. It will accept
* a null parameter. It will not return null.
*
* @see NewFilterClassDataModelProvider#validate(String)
*
* @param prop
* @return IStatus is filter mapping list valid?
*/
private IStatus validateFilterMappingList(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.ERR_DUPLICATED_URL_MAPPING;
return WTPCommonPlugin.createErrorStatus(msg);
}
String isValidValue = validateValue(prop);
if (isValidValue != null && isValidValue.length() > 0) {
NLS.bind(WebMessages.ERR_URL_PATTERN_INVALID, isValidValue);
String resourceString = WebMessages.getResourceString(WebMessages.ERR_URL_PATTERN_INVALID, new String[]{isValidValue});
return WTPCommonPlugin.createErrorStatus(resourceString);
}
} else {
String msg = WebMessages.ERR_FILTER_MAPPING_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 invalid pattern's value in a list. It will accept a null parameter.
*
* @see NewFilterClassDataModelProvider#validateFilterMappingList(List)
*
* @param input
* @return String first invalid pattern's value
*/
private String validateValue(List prop) {
if (prop == null) {
return "";
}
int size = prop.size();
for (int i = 0; i < size; i++) {
IFilterMappingItem filterMappingValue = (IFilterMappingItem) prop.get(i);
if (filterMappingValue.isUrlPatternType() &&
!UrlPattern.isValid(filterMappingValue.getName()))
return filterMappingValue.getName();
}
return "";
}
/**
* 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 parameter. It will not return
* null.
*
* @see NewFilterClassDataModelProvider#validateInitParamList(List)
* @see NewFilterClassDataModelProvider#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++) {
Object object = input.get(i);
if (object instanceof IFilterMappingItem) {
IFilterMappingItem item = (IFilterMappingItem) object;
for (int j = i + 1; j < n; j++) {
IFilterMappingItem item2 = (IFilterMappingItem) input.get(j);
if (item.getName().equals(item2.getName()) &&
item.getMappingType() == item2.getMappingType()) {
dup = true;
break;
}
}
if (dup) break;
} else {
String[] sArray1 = (String[]) object;
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 NewFilterClassDataModelProvider#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 filter 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 FILTER_INTERFACES. This method will not return null.
*
* @see INewFilterClassDataModelProperties#FILTER_INTERFACES
*
* @return List of servlet interfaces to be implemented
*/
private List getFilterInterfaces() {
if (interfaceList == null) {
interfaceList = new ArrayList();
// Add minimum required list of servlet interfaces to be implemented
for (int i = 0; i < FILTER_INTERFACES.length; i++) {
interfaceList.add(FILTER_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 filter 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 NewFilterClassDataModelProvider#validate(String)
*
* @param prop
* @return IStatus is filter display name valid?
*/
private IStatus validateDisplayName(String prop) {
// Ensure the filter display name is not null or empty
if (prop == null || prop.trim().length() == 0) {
String msg = WebMessages.ERR_DISPLAY_NAME_EMPTY;
return WTPCommonPlugin.createErrorStatus(msg);
}
if (getTargetProject() == null || getTargetComponent() == null)
return WTPCommonPlugin.OK_STATUS;
IModelProvider provider = ModelProviderManager.getModelProvider( getTargetProject() );
Object mObj = provider.getModelObject();
if( mObj instanceof org.eclipse.jst.j2ee.webapplication.WebApp ){
org.eclipse.jst.j2ee.webapplication.WebApp webApp = (org.eclipse.jst.j2ee.webapplication.WebApp) mObj;
List filters = webApp.getFilters();
boolean exists = false;
// Ensure the display does not already exist in the web application
if (filters != null && !filters.isEmpty()) {
for (int i = 0; i < filters.size(); i++) {
String name = ((org.eclipse.jst.j2ee.webapplication.Filter) filters.get(i)).getName();
if (prop.equals(name))
exists = true;
}
}
// If the filter 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);
}
} else if ( mObj instanceof org.eclipse.jst.javaee.web.WebApp){
org.eclipse.jst.javaee.web.WebApp webApp= (org.eclipse.jst.javaee.web.WebApp) mObj;
List filters = webApp.getFilters();
boolean exists = false;
// Ensure the display does not already exist in the web application
if (filters != null && !filters.isEmpty()) {
for (int i = 0; i < filters.size(); i++) {
String name = ((org.eclipse.jst.javaee.web.Filter) filters.get(i)).getFilterName();
if (prop.equals(name))
exists = true;
}
}
// If the filter 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);
}
}
// Otherwise, return OK
return WTPCommonPlugin.OK_STATUS;
}
/**
* @return boolean should the default annotations be true?
*/
private static Boolean shouldDefaultAnnotations() {
if (useAnnotations)
return Boolean.TRUE;
return Boolean.FALSE;
}
}