blob: 379bcce118f30f01145cb32e260ba7e0a02a256a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016 BSI Business Systems Integration AG.
* 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:
* BSI Business Systems Integration AG - initial API and implementation
******************************************************************************/
package org.eclipse.scout.sdk.s2e.ui.wizard;
import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceProxy;
import org.eclipse.core.resources.IResourceProxyVisitor;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.compiler.util.SuffixConstants;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.scout.sdk.core.util.CoreUtils;
import org.eclipse.scout.sdk.core.util.SdkLog;
import org.eclipse.scout.sdk.s2e.internal.S2ESdkActivator;
import org.eclipse.scout.sdk.s2e.ui.fields.proposal.IProposalListener;
import org.eclipse.scout.sdk.s2e.ui.fields.proposal.ProposalTextField;
import org.eclipse.scout.sdk.s2e.ui.fields.proposal.content.PackageContentProvider;
import org.eclipse.scout.sdk.s2e.ui.fields.proposal.content.StrictHierarchyTypeContentProvider;
import org.eclipse.scout.sdk.s2e.ui.fields.text.StyledTextField;
import org.eclipse.scout.sdk.s2e.ui.fields.text.TextField;
import org.eclipse.scout.sdk.s2e.ui.util.PackageContainer;
import org.eclipse.scout.sdk.s2e.util.S2eUtils;
import org.eclipse.scout.sdk.s2e.util.ScoutTier;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Group;
/**
* <h3>{@link CompilationUnitNewWizardPage}</h3>
*
* @author Matthias Villiger
* @since 5.2.0
*/
public class CompilationUnitNewWizardPage extends AbstractWizardPage {
private static final Pattern WELLFORMD_JAVAFIELD = Pattern.compile("\\b[A-Z][a-zA-Z0-9_]{0,200}\\b");
private static final Pattern JAVAFIELD = Pattern.compile("\\b[A-Za-z_][a-zA-Z0-9_]{0,200}\\b");
public static final String PROP_SOURCE_FOLDER = "sourceFolder";
public static final String PROP_TARGET_PACKAGE = "targetPackage";
public static final String PROP_JAVA_PROJECT = "javaProject";
public static final String PROP_ICU_NAME = "icuName";
public static final String PROP_SUPER_TYPE = "superType";
public static final String PROP_SUPER_TYPE_BASE = "superTypeBase";
public static final String PREF_SUPER_TYPE = "superType";
private final String m_superTypeDefaultBase;
private final String m_defaultSuperType;
private final ScoutTier m_sourceFolderTier;
private String m_readOnlySuffix;
private String m_icuGroupName;
// ui fields
private ProposalTextField m_sourceFolderField;
private ProposalTextField m_packageField;
private StyledTextField m_nameField;
private ProposalTextField m_superTypeField;
private Group m_icuGroupField;
public CompilationUnitNewWizardPage(String pageName, PackageContainer packageContainer, String readOnlySuffix, String superTypeBaseClass, String defaultSuperType, ScoutTier sourceFolderTier) {
super(Validate.notNull(pageName));
m_readOnlySuffix = Validate.notNull(readOnlySuffix);
m_superTypeDefaultBase = superTypeBaseClass;
m_defaultSuperType = Validate.notNull(defaultSuperType);
m_sourceFolderTier = Validate.notNull(sourceFolderTier);
setSuperTypeBaseClassInternal(superTypeBaseClass);
if (packageContainer.getPackage() != null) {
setTargetPackageInternal(packageContainer.getPackage().getElementName());
}
setSourceFolderInternal(packageContainer.getSrcFolder());
}
@Override
protected void createContent(Composite parent) {
GridLayoutFactory
.swtDefaults()
.applyTo(parent);
createIcuGroup(parent);
}
protected int getLabelWidth() {
return 100;
}
protected void createIcuGroup(Composite p) {
String groupName = getIcuGroupName();
if (StringUtils.isBlank(groupName)) {
groupName = "New Class Details";
}
m_icuGroupField = getFieldToolkit().createGroupBox(p, groupName);
int labelWidth = getLabelWidth();
boolean enabled = S2eUtils.exists(getSourceFolder());
// source folder
m_sourceFolderField = getFieldToolkit().createSourceFolderField(m_icuGroupField, "Source Folder", m_sourceFolderTier, labelWidth);
m_sourceFolderField.acceptProposal(getSourceFolder());
m_sourceFolderField.addProposalListener(new IProposalListener() {
@Override
public void proposalAccepted(Object proposal) {
setSourceFolderInternal((IPackageFragmentRoot) proposal);
pingStateChanging();
}
});
// package
m_packageField = getFieldToolkit().createPackageField(m_icuGroupField, "Package", getJavaProject(), labelWidth);
m_packageField.setText(getTargetPackage());
m_packageField.addModifyListener(new ModifyListener() {
@Override
public void modifyText(ModifyEvent e) {
setTargetPackageInternal(m_packageField.getText());
pingStateChanging();
}
});
m_packageField.setEnabled(enabled);
// name
m_nameField = getFieldToolkit().createStyledTextField(m_icuGroupField, "Name", TextField.TYPE_LABEL, labelWidth);
m_nameField.setText(getIcuName());
m_nameField.setReadOnlySuffix(getReadOnlySuffix());
m_nameField.addModifyListener(new ModifyListener() {
@Override
public void modifyText(ModifyEvent e) {
setIcuNameInternal(m_nameField.getText());
pingStateChanging();
}
});
// super type
IType superType = calcSuperTypeDefault();
if (S2eUtils.exists(superType)) {
setSuperTypeInternal(superType);
}
m_superTypeField = getFieldToolkit().createAbstractTypeProposalField(m_icuGroupField, "Super Class", getJavaProject(), getSuperTypeBaseClass(), labelWidth);
m_superTypeField.acceptProposal(getSuperType());
m_superTypeField.addProposalListener(new IProposalListener() {
@Override
public void proposalAccepted(Object proposal) {
setSuperTypeInternal((IType) proposal);
pingStateChanging();
}
});
m_superTypeField.setEnabled(enabled);
// layout
GridLayoutFactory
.swtDefaults()
.applyTo(m_icuGroupField);
applyFieldLayoutData(m_icuGroupField);
applyFieldLayoutData(m_sourceFolderField);
applyFieldLayoutData(m_packageField);
applyFieldLayoutData(m_nameField);
applyFieldLayoutData(m_superTypeField);
}
protected void applyFieldLayoutData(Control c) {
GridDataFactory
.defaultsFor(c)
.align(SWT.FILL, SWT.CENTER)
.grab(true, false)
.applyTo(c);
}
protected void handleSuperTypeChanged() {
// callback for subclasses invoked when the super type is changed
}
protected void handleSourceFolderChanged() {
// callback for subclasses invoked when the source folder is changed
}
protected void handleTargetPackageChanged() {
// callback for subclasses invoked when the target package is changed
}
protected void handleIcuNameChanged() {
// callback for subclasses invoked when the class name is changed
}
protected IType calcSuperTypeDefault() {
IType defaultSuperType = null;
String prefSuperTypeFqn = getDialogSettings().get(PREF_SUPER_TYPE);
if (StringUtils.isNotBlank(prefSuperTypeFqn)) {
defaultSuperType = resolveType(prefSuperTypeFqn);
}
if (!S2eUtils.exists(defaultSuperType)) {
defaultSuperType = resolveType(getSuperTypeDefault()); // fallback to default
}
return defaultSuperType;
}
protected IType resolveType(String fqn) {
return resolveType(getJavaProject(), fqn);
}
protected static IType resolveType(IJavaProject javaProject, String fqn) {
if (!S2eUtils.exists(javaProject)) {
return null;
}
try {
return javaProject.findType(fqn);
}
catch (JavaModelException e) {
SdkLog.info("Could not find default super type {} in project {}.", fqn, javaProject.getElementName(), e);
}
return null;
}
@Override
public boolean performFinish() {
getDialogSettings().put(PREF_SUPER_TYPE, getSuperType().getFullyQualifiedName());
return true;
}
@Override
public void setVisible(boolean visible) {
super.setVisible(visible);
if (!visible) {
return;
}
// initial focus
if (m_sourceFolderField.getSelectedProposal() == null) {
m_sourceFolderField.setFocus();
}
else if (StringUtils.isBlank(m_packageField.getText())) {
m_packageField.setFocus();
}
else {
m_nameField.setFocus();
}
}
protected void handleJavaProjectChanged() {
if (!isControlCreated()) {
return;
}
boolean isEnabled = S2eUtils.exists(getJavaProject());
m_packageField.setEnabled(isEnabled);
PackageContentProvider packageContentProvider = (PackageContentProvider) m_packageField.getContentProvider();
packageContentProvider.setJavaProject(getJavaProject());
m_packageField.setText(null);
m_superTypeField.setEnabled(isEnabled);
StrictHierarchyTypeContentProvider superTypeContentProvider = (StrictHierarchyTypeContentProvider) m_superTypeField.getContentProvider();
superTypeContentProvider.setJavaProject(getJavaProject());
m_superTypeField.acceptProposal(getSuperType(), true, true);
if (m_superTypeField.getSelectedProposal() == null) {
m_superTypeField.acceptProposal(calcSuperTypeDefault());
}
}
@Override
protected void validatePage(MultiStatus multiStatus) {
multiStatus.add(getStatusSourceFolder());
multiStatus.add(getStatusPackage());
multiStatus.add(getStatusName());
multiStatus.add(getStatusSuperType());
}
protected IStatus getStatusSourceFolder() {
if (!S2eUtils.exists(getSourceFolder())) {
return new Status(IStatus.ERROR, S2ESdkActivator.PLUGIN_ID, "Please choose a source folder.");
}
return Status.OK_STATUS;
}
protected IStatus getStatusPackage() {
return validatePackageName(getTargetPackage());
}
protected IStatus getStatusName() {
IStatus javaFieldNameStatus = validateJavaName(getIcuName(), getReadOnlySuffix());
if (javaFieldNameStatus.getSeverity() > IStatus.WARNING) {
return javaFieldNameStatus;
}
IStatus existingStatus = validateTypeNotExisting(getSourceFolder(), getTargetPackage(), getIcuName());
if (!existingStatus.isOK()) {
return existingStatus;
}
return javaFieldNameStatus;
}
protected IStatus getStatusSuperType() {
if (!S2eUtils.exists(getSuperType())) {
return new Status(IStatus.ERROR, S2ESdkActivator.PLUGIN_ID, "Please choose a super class.");
}
return Status.OK_STATUS;
}
protected ProposalTextField getSourceFolderField() {
return m_sourceFolderField;
}
protected ProposalTextField getPackageField() {
return m_packageField;
}
protected StyledTextField getNameField() {
return m_nameField;
}
protected ProposalTextField getSuperTypeField() {
return m_superTypeField;
}
/**
* Gets the status of the given java name. Checks if a name that differs from the suffix is entered and that the name
* is a valid java name.
*
* @param name
* The name to check
* @param suffix
* The suffix to compare against.
* @return A status that describes the state of the given name
*/
public static IStatus validateJavaName(String name, String suffix) {
if (StringUtils.isBlank(name) || name.equals(suffix)) {
return new Status(IStatus.ERROR, S2ESdkActivator.PLUGIN_ID, "Please specify a class name.");
}
if (WELLFORMD_JAVAFIELD.matcher(name).matches()) {
return Status.OK_STATUS;
}
else if (JAVAFIELD.matcher(name).matches()) {
return new Status(IStatus.WARNING, S2ESdkActivator.PLUGIN_ID, "Name should start with upper case.");
}
else {
// "package-info.java" will not be valid which is what we want
return new Status(IStatus.ERROR, S2ESdkActivator.PLUGIN_ID, "Name not valid.");
}
}
private static String getContainingJavaKeyWord(String s) {
for (String keyWord : CoreUtils.getJavaKeyWords()) {
if (s.startsWith(keyWord + '.') || s.endsWith('.' + keyWord) || s.contains('.' + keyWord + '.')) {
return keyWord;
}
}
return null;
}
/**
* Gets the validation status for a potential new class in the given source folder, below the given package with given
* name.<br>
* The method does no check for java classes, but checks if there already exists a resource with the target name (case
* insensitive).
*
* @param srcFolder
* The source folder
* @param pck
* The package under which the type would be created.
* @param typeName
* the name of the potential type.
* @return the {@link Status#OK_STATUS} if no file exists at the target location with the given name. An error status
* otherwise.
*/
public static IStatus validateTypeNotExisting(IPackageFragmentRoot srcFolder, String pck, String typeName) {
if (StringUtils.isBlank(typeName)) {
return Status.OK_STATUS;
}
if (!S2eUtils.exists(srcFolder)) {
return Status.OK_STATUS;
}
if (pck == null) {
pck = "";
}
IPackageFragment packageFragment = srcFolder.getPackageFragment(pck);
if (!S2eUtils.exists(packageFragment)) {
return Status.OK_STATUS;
}
IFolder folder = (IFolder) packageFragment.getResource();
if (folder == null || !folder.exists()) {
return Status.OK_STATUS;
}
final boolean[] elementFound = new boolean[1];
final String typeNameComplete = typeName + SuffixConstants.SUFFIX_STRING_java;
if (folder.exists()) {
try {
folder.accept(new IResourceProxyVisitor() {
boolean m_selfVisited = false;
@Override
public boolean visit(IResourceProxy proxy) throws CoreException {
if (proxy.getType() == IResource.FOLDER) {
if (!m_selfVisited) {
m_selfVisited = true;
return true;
}
return false;
}
else if (proxy.getType() == IResource.FILE && typeNameComplete.equalsIgnoreCase(proxy.getName())) {
elementFound[0] = true;
}
return false;
}
}, IResource.DEPTH_ONE, IResource.NONE);
}
catch (CoreException e) {
SdkLog.warning("Unable to check if the type '{}' already exists.", typeName, e);
}
}
if (elementFound[0]) {
return new Status(IStatus.ERROR, S2ESdkActivator.PLUGIN_ID, "Name '" + typeName + "' is already used. Choose another name.");
}
return Status.OK_STATUS;
}
/**
* Gets an {@link IStatus} describing the given package name
*
* @param pckName
* The package name to validate
* @return An {@link IStatus} describing the given package name.
*/
public static IStatus validatePackageName(String pckName) {
if (StringUtils.isBlank(pckName)) {
return new Status(IStatus.ERROR, S2ESdkActivator.PLUGIN_ID, "The default package is not allowed.");
}
String invalidPackageName = "The package name is not valid.";
// no double points
if (pckName.contains("..")) {
return new Status(IStatus.ERROR, S2ESdkActivator.PLUGIN_ID, invalidPackageName);
}
// invalid characters
Pattern regexPackageName = Pattern.compile("^[0-9a-zA-Z\\.\\_]*$");
if (!regexPackageName.matcher(pckName).matches()) {
return new Status(IStatus.ERROR, S2ESdkActivator.PLUGIN_ID, invalidPackageName);
}
// no start and end with number or special characters
Pattern regexPackageNameStart = Pattern.compile("[a-zA-Z]{1}.*$");
Pattern regesPackageNameEnd = Pattern.compile("^.*[a-zA-Z]{1}$");
if (!regexPackageNameStart.matcher(pckName).matches() || !regesPackageNameEnd.matcher(pckName).matches()) {
return new Status(IStatus.ERROR, S2ESdkActivator.PLUGIN_ID, invalidPackageName);
}
// reserved java keywords
String jkw = getContainingJavaKeyWord(pckName);
if (jkw != null) {
return new Status(IStatus.ERROR, S2ESdkActivator.PLUGIN_ID, "The package may not contain a reserved Java keyword: '" + jkw + "'");
}
// warn containing upper case characters
Pattern regexContainsUpperCase = Pattern.compile(".*[A-Z].*");
if (regexContainsUpperCase.matcher(pckName).matches()) {
return new Status(IStatus.WARNING, S2ESdkActivator.PLUGIN_ID, "The package should contain only lower case characters.");
}
return Status.OK_STATUS;
}
public IJavaProject getJavaProject() {
return getProperty(PROP_JAVA_PROJECT, IJavaProject.class);
}
protected void setJavaProjectInternal(IJavaProject javaProject) {
if (setProperty(PROP_JAVA_PROJECT, javaProject)) {
handleJavaProjectChanged();
}
}
public IPackageFragmentRoot getSourceFolder() {
return getProperty(PROP_SOURCE_FOLDER, IPackageFragmentRoot.class);
}
public void setSourceFolder(IPackageFragmentRoot sourceFolder) {
try {
setStateChanging(true);
setSourceFolderInternal(sourceFolder);
if (isControlCreated() && m_sourceFolderField != null) {
m_sourceFolderField.acceptProposal(sourceFolder);
}
}
finally {
setStateChanging(false);
}
}
protected void setSourceFolderInternal(IPackageFragmentRoot sourceFolder) {
if (setProperty(PROP_SOURCE_FOLDER, sourceFolder)) {
handleSourceFolderChanged();
}
if (sourceFolder != null) {
setJavaProjectInternal(sourceFolder.getJavaProject());
}
else {
setJavaProjectInternal(null);
}
}
public String getTargetPackage() {
return getProperty(PROP_TARGET_PACKAGE, String.class);
}
public void setTargetPackage(String targetPackage) {
try {
setStateChanging(true);
setTargetPackageInternal(targetPackage);
if (isControlCreated() && m_packageField != null) {
m_packageField.setText(targetPackage);
}
}
finally {
setStateChanging(false);
}
}
protected void setTargetPackageInternal(String targetPackage) {
if (setProperty(PROP_TARGET_PACKAGE, targetPackage)) {
handleTargetPackageChanged();
}
}
public String getIcuName() {
return getProperty(PROP_ICU_NAME, String.class);
}
public void setIcuName(String name) {
try {
setStateChanging(true);
setIcuNameInternal(name);
if (isControlCreated() && m_nameField != null) {
m_nameField.setText(name);
}
}
finally {
setStateChanging(false);
}
}
protected void setIcuNameInternal(String name) {
if (setProperty(PROP_ICU_NAME, name)) {
handleIcuNameChanged();
}
}
public IType getSuperType() {
return getProperty(PROP_SUPER_TYPE, IType.class);
}
public void setSuperType(IType superType) {
try {
setStateChanging(true);
setSuperTypeInternal(superType);
if (isControlCreated() && m_superTypeField != null) {
m_superTypeField.acceptProposal(superType);
}
}
finally {
setStateChanging(false);
}
}
protected void setSuperTypeInternal(IType superType) {
if (setProperty(PROP_SUPER_TYPE, superType)) {
handleSuperTypeChanged();
}
}
public String getSuperTypeBaseClass() {
return getProperty(PROP_SUPER_TYPE_BASE, String.class);
}
public void setSuperTypeBaseClass(String className) {
try {
setStateChanging(true);
setSuperTypeBaseClassInternal(className);
if (isControlCreated() && m_superTypeField != null) {
StrictHierarchyTypeContentProvider superTypeContentProvider = (StrictHierarchyTypeContentProvider) m_superTypeField.getContentProvider();
superTypeContentProvider.setBaseClassFqn(className);
m_superTypeField.acceptProposal(calcSuperTypeDefault());
}
}
finally {
setStateChanging(false);
}
}
protected void setSuperTypeBaseClassInternal(String className) {
setProperty(PROP_SUPER_TYPE_BASE, className);
}
public String getSuperTypeDefault() {
return m_defaultSuperType;
}
public String getReadOnlySuffix() {
return m_readOnlySuffix;
}
public void setReadOnlySuffix(String newSuffix) {
m_readOnlySuffix = newSuffix;
if (m_nameField != null) {
m_nameField.setReadOnlySuffix(newSuffix);
}
}
public String getIcuGroupName() {
return m_icuGroupName;
}
public void setIcuGroupName(String icuGroupName) {
m_icuGroupName = icuGroupName;
}
public String getSuperTypeDefaultBase() {
return m_superTypeDefaultBase;
}
protected Group getIcuGroupComposite() {
return m_icuGroupField;
}
}