/*
* $Id$
*
* SARL is an general-purpose agent programming language.
* More details on http://www.sarl.io
*
* Copyright (C) 2014-2017 the original authors or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.sarl.eclipse.wizards.elements;
import java.io.ByteArrayInputStream;
import java.lang.reflect.InvocationTargetException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import javax.inject.Inject;
import javax.inject.Named;
import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.inject.Injector;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.debug.internal.ui.SWTFactory;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.ITypeHierarchy;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.core.CompilationUnit;
import org.eclipse.jdt.internal.core.DefaultWorkingCopyOwner;
import org.eclipse.jdt.internal.core.PackageFragment;
import org.eclipse.jdt.internal.ui.util.CoreUtility;
import org.eclipse.jdt.internal.ui.wizards.NewWizardMessages;
import org.eclipse.jdt.internal.ui.wizards.dialogfields.DialogField;
import org.eclipse.jdt.internal.ui.wizards.dialogfields.LayoutUtil;
import org.eclipse.jdt.internal.ui.wizards.dialogfields.SelectionButtonDialogFieldGroup;
import org.eclipse.jdt.ui.wizards.NewTypeWizardPage;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.operation.IRunnableContext;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.actions.WorkspaceModifyOperation;
import org.eclipse.xtend.core.xtend.XtendTypeDeclaration;
import org.eclipse.xtext.Constants;
import org.eclipse.xtext.common.types.JvmDeclaredType;
import org.eclipse.xtext.common.types.JvmOperation;
import org.eclipse.xtext.common.types.JvmParameterizedTypeReference;
import org.eclipse.xtext.common.types.access.IJvmTypeProvider;
import org.eclipse.xtext.formatting.IWhitespaceInformationProvider;
import org.eclipse.xtext.ui.resource.IResourceSetProvider;
import org.eclipse.xtext.ui.resource.IStorage2UriMapper;
import org.eclipse.xtext.xbase.XFeatureCall;
import org.eclipse.xtext.xbase.compiler.ISourceAppender;
import org.eclipse.xtext.xbase.compiler.ImportManager;
import org.eclipse.xtext.xbase.compiler.output.FakeTreeAppendable;
import org.eclipse.xtext.xbase.lib.Functions.Function1;
import org.eclipse.xtext.xbase.lib.Procedures.Procedure1;
import io.sarl.eclipse.SARLEclipsePlugin;
import io.sarl.eclipse.util.Jdt2Ecore;
import io.sarl.eclipse.util.Jdt2Ecore.ActionBuilder;
import io.sarl.eclipse.util.Jdt2Ecore.ConstructorBuilder;
import io.sarl.eclipse.util.Jdt2Ecore.TypeFinder;
import io.sarl.lang.actionprototype.ActionParameterTypes;
import io.sarl.lang.actionprototype.ActionPrototype;
import io.sarl.lang.codebuilder.CodeBuilderFactory;
import io.sarl.lang.codebuilder.builders.IBlockExpressionBuilder;
import io.sarl.lang.codebuilder.builders.IExpressionBuilder;
import io.sarl.lang.codebuilder.builders.ISarlActionBuilder;
import io.sarl.lang.codebuilder.builders.ISarlBehaviorUnitBuilder;
import io.sarl.lang.formatting2.FormatterFacade;
/**
* Abstract implementation of a wizard page for creating new SARL elements.
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
@SuppressWarnings("checkstyle:classfanoutcomplexity")
public abstract class AbstractNewSarlElementWizardPage extends NewTypeWizardPage {
/** filename extension for the Java code.
*/
protected static final String JAVA_FILE_EXTENSION = "java"; //$NON-NLS-1$
/** Number of columns in the composite components.
*/
protected static final int COLUMNS = 4;
/** Name of the SARL Initialize event.
*/
protected static final String INITIALIZE_EVENT_NAME = "io.sarl.core.Initialize"; //$NON-NLS-1$
/** Name of the SARL Destroy event.
*/
protected static final String DESTROY_EVENT_NAME = "io.sarl.core.Destroy"; //$NON-NLS-1$
/** Name of the SARL ContextJoined event.
*/
protected static final String CONTEXTJOINED_EVENT_NAME = "io.sarl.core.ContextJoined"; //$NON-NLS-1$
/** Name of the SARL ContextLeft event.
*/
protected static final String CONTEXTLEFT_EVENT_NAME = "io.sarl.core.ContextLeft"; //$NON-NLS-1$
/** Name of the SARL MemberJoined event.
*/
protected static final String MEMBERJOINED_EVENT_NAME = "io.sarl.core.MemberJoined"; //$NON-NLS-1$
/** Name of the SARL MemberLeft event.
*/
protected static final String MEMBERLEFT_EVENT_NAME = "io.sarl.core.MemberLeft"; //$NON-NLS-1$
/** Name of the SARL AgentSpawned event.
*/
protected static final String AGENTSPAWNED_EVENT_NAME = "io.sarl.core.AgentSpawned"; //$NON-NLS-1$
/** Name of the SARL AgentKilled event.
*/
protected static final String AGENTKILLED_EVENT_NAME = "io.sarl.core.AgentKilled"; //$NON-NLS-1$
/** Name of the SARL Logging capacity.
*/
protected static final String LOGGING_CAPACITY_NAME = "io.sarl.core.Logging"; //$NON-NLS-1$
/** Name of the SARL skill install function.
*/
protected static final String INSTALL_SKILL_NAME = "install"; //$NON-NLS-1$
/** Name of the SARL skill uninstall function.
*/
protected static final String UNINSTALL_SKILL_NAME = "uninstall"; //$NON-NLS-1$
private static final int STEPS = 8;
private static final String SETTINGS_CREATECONSTR = "create_constructor"; //$NON-NLS-1$
private static final String SETTINGS_CREATEUNIMPLEMENTED = "create_unimplemented"; //$NON-NLS-1$
private static final String SETTINGS_GENERATEEVENTHANDLERS = "generate_event_handlers"; //$NON-NLS-1$
private static final String SETTINGS_GENERATELIFECYCLEFUNCTIONS = "generate_lifecycle_functions"; //$NON-NLS-1$
/** A builder of code.
*/
@Inject
protected CodeBuilderFactory codeBuilderFactory;
/** Provider of the resource set associated to a project.
*/
@Inject
protected IResourceSetProvider resourceSetFactory;
@Inject
private Jdt2Ecore jdt2sarl;
@Inject
private FieldInitializerUtil fieldInitializer;
@Inject
private IStorage2UriMapper storage2UriMapper;
@Inject
private IWhitespaceInformationProvider whitespaceInformationProvider;
@Inject
private Injector injector;
private String sarlFileExtension;
private IResource resource;
private SelectionButtonDialogFieldGroup methodStubsButtons;
private boolean isConstructorCreationEnabled;
private boolean isInheritedCreationEnabled;
private boolean isDefaultEventGenerated;
private boolean isDefaultLifecycleFunctionsGenerated;
@Inject
private FormatterFacade formatterFacade;
@Inject
private IJvmTypeProvider.Factory jdtTypeProviderFactory;
private boolean hasSuperTypeField;
private boolean hasSuperInterfaceField;
/**
* @param typeKind - Signals the kind of the type to be created. Valid kinds are
* {@link NewTypeWizardPage#CLASS_TYPE}, {@link NewTypeWizardPage#INTERFACE_TYPE},
* {@link NewTypeWizardPage#ENUM_TYPE} and {@link NewTypeWizardPage#ANNOTATION_TYPE}.
* @param title - the title of the page.
*/
public AbstractNewSarlElementWizardPage(int typeKind, String title) {
super(typeKind, title);
}
/** Replies if the super-type field is activiated.
*
* @return <code>true</code> if the super-type control were added.
*/
public boolean isSuperTypeActivated() {
return this.hasSuperTypeField;
}
/** Replies if the super-interface field is activiated.
*
* @return <code>true</code> if the super-interface control were added.
*/
public boolean isSuperInterfaceActivated() {
return this.hasSuperInterfaceField;
}
@Override
protected void createSuperClassControls(Composite composite, int nColumns) {
this.hasSuperTypeField = true;
super.createSuperClassControls(composite, nColumns);
}
@Override
protected void createSuperInterfacesControls(Composite composite, int nColumns) {
this.hasSuperInterfaceField = true;
super.createSuperInterfacesControls(composite, nColumns);
}
/** Change the file extension used by this page.
*
* @param fileExtension - the file extension
*/
@Inject
public void setFileExtension(@Named(Constants.FILE_EXTENSIONS) String fileExtension) {
this.sarlFileExtension = fileExtension;
}
/** Update the status of the wizard.
*/
protected abstract void doStatusUpdate();
/** Replies the resource that contains the created SARL element.
*
* @return the resource of the created SARL element.
*/
public IResource getResource() {
return this.resource;
}
/** Change the resource where the created SARL element is located.
*
* @param resource - the resource of the created SARL element.
*/
protected void setResource(IResource resource) {
this.resource = resource;
}
@Override
public void setVisible(boolean visible) {
super.setVisible(visible);
if (visible) {
doStatusUpdate();
setFocus();
}
}
@Override
protected void handleFieldChanged(String fieldName) {
super.handleFieldChanged(fieldName);
doStatusUpdate();
}
@Override
protected IStatus typeNameChanged() {
assert this.sarlFileExtension != null;
final IPackageFragment packageFragment = getPackageFragment();
final String typeName = getTypeName();
if (packageFragment != null && !Strings.isNullOrEmpty(typeName)) {
if (isSarlFile(packageFragment, typeName)) {
String packageName = ""; //$NON-NLS-1$
if (!packageFragment.isDefaultPackage()) {
packageName = packageFragment.getElementName() + "."; //$NON-NLS-1$
}
return SARLEclipsePlugin.getDefault().createStatus(
IStatus.ERROR,
MessageFormat.format(
getExistingElementErrorMessage(),
packageName + getTypeName()));
}
}
return super.typeNameChanged();
}
/** Replies if the given filename is a SARL script or a generated Java file.
*
* @param packageFragment - the package in which the file should be search for.
* @param filename - the filename to test.
* @return <code>true</code> if a file (SALR or Java) with the given name exists.
*/
protected boolean isSarlFile(IPackageFragment packageFragment, String filename) {
if (isFileExists(packageFragment, filename, this.sarlFileExtension)) {
return true;
}
final IJavaProject project = getPackageFragmentRoot().getJavaProject();
if (project != null) {
try {
final String packageName = packageFragment.getElementName();
for (final IPackageFragmentRoot root : project.getPackageFragmentRoots()) {
final IPackageFragment fragment = root.getPackageFragment(packageName);
if (isFileExists(fragment, filename, JAVA_FILE_EXTENSION)) {
return true;
}
}
} catch (JavaModelException exception) {
// silent error
}
}
return false;
}
/** Replies if the given filename is a SARL script in the given package.
*
* @param packageFragment - the package in which the file should be search for.
* @param filename - the filename to test.
* @param extension - the filename extension to search for.
* @return <code>true</code> if a file (SARL or Java) with the given name exists.
*/
protected static boolean isFileExists(IPackageFragment packageFragment, String filename, String extension) {
if (packageFragment != null) {
final IResource resource = packageFragment.getResource();
if (resource instanceof IFolder) {
final IFolder folder = (IFolder) resource;
if (folder.getFile(filename + "." + extension).exists()) { //$NON-NLS-1$
return true;
}
}
}
return false;
}
/** Invoked for obtaining the error message related to an existing type.
*
* <p>The following parameters will be replaced in the error message according to
* the {@link MessageFormat} utility class: <ul>
* <li><code>{0}</code>: the name of the type.</li>
* </ul>
*
* @return the error message.
*/
protected abstract String getExistingElementErrorMessage();
/** Invoked for obtaining the error message related to an invalid subtype for the new element.
*
* <p>The following parameters will be replaced in the error message according to
* the {@link MessageFormat} utility class: <ul>
* <li><code>{0}</code>: the name of the selected type.</li>
* </ul>
*
* @return the error message.
*/
@SuppressWarnings("static-method")
protected String getInvalidSubtypeErrorMessage() {
throw new UnsupportedOperationException();
}
/** Invoked for obtaining the error message related to an invalid sub-interface for the new element.
*
* <p>The following parameters will be replaced in the error message according to
* the {@link MessageFormat} utility class: <ul>
* <li><code>{0}</code>: the name of the selected type.</li>
* </ul>
*
* @return the error message.
*/
@SuppressWarnings("static-method")
protected String getInvalidInterfaceTypeErrorMessage() {
throw new UnsupportedOperationException();
}
/** Invoked for obtaining the error message related to a missed super-interface.
*
* <p>The following parameters will be replaced in the error message according to
* the {@link MessageFormat} utility class: <ul>
* <li><code>{0}</code>: the name of the missed type.</li>
* </ul>
*
* @return the error message.
*/
@SuppressWarnings("static-method")
protected String getMissedSuperInterfaceErrorMessage() {
throw new UnsupportedOperationException();
}
/** Invoked by the wizard for initializing the page with the given selection.
*
* @param selection - the current selection.
*/
protected void init(IStructuredSelection selection) {
final IJavaElement elem = this.fieldInitializer.getSelectedResource(selection);
initContainerPage(elem);
initTypePage(elem);
//
try {
getRootSuperType();
reinitSuperClass();
} catch (Throwable exception) {
//
}
//
try {
getRootSuperInterface();
reinitSuperInterfaces();
} catch (Throwable exception) {
//
}
//
doStatusUpdate();
}
@Override
public boolean isAddComments() {
// Create the comments
return true;
}
/** Replies if the given type is a subtype of the expected super-type.
* The expected super-type is replied by {@link #getRootSuperType()}.
*
* @param className - the name of the class to be tested.
* @return <code>true</code> if the given name is the one of a subtype
* of the expected root type.
* @throws JavaModelException if there is a problem for retreiving the Java information.
*/
protected boolean isValidExtendedType(String className) throws JavaModelException {
// accept the empty field (stands for the default super type)
if (!Strings.isNullOrEmpty(className)) {
final IType rootType = getRootSuperType();
if (rootType == null) {
final IStatus status = SARLEclipsePlugin.getDefault().createStatus(IStatus.ERROR,
Messages.AbstractNewSarlElementWizardPage_3);
throw new JavaModelException(new CoreException(status));
}
final IType type = getJavaProject().findType(className);
if (type == null) {
final IStatus status = SARLEclipsePlugin.getDefault().createStatus(IStatus.ERROR,
MessageFormat.format(Messages.AbstractNewSarlElementWizardPage_4, className));
throw new JavaModelException(new CoreException(status));
}
final ITypeHierarchy hierarchy = type.newSupertypeHierarchy(new NullProgressMonitor());
if (hierarchy == null || !hierarchy.contains(rootType)) {
return false;
}
}
return true;
}
/** Replies if the given type implements the expected super-interface.
* The expected super-interface is replied by {@link #getRootSuperInterface()}.
*
* @param className - the name of the class to be tested.
* @return <code>true</code> if the given name implements a type
* of the expected root type.
* @throws JavaModelException if there is a problem for retreiving the Java information.
*/
protected boolean isValidImplementedType(String className) throws JavaModelException {
if (!Strings.isNullOrEmpty(className)) {
final IType rootType = getRootSuperInterface();
assert rootType != null;
final IType type = getJavaProject().findType(className);
assert type != null;
final ITypeHierarchy hierarchy = type.newSupertypeHierarchy(new NullProgressMonitor());
assert hierarchy != null;
if (!hierarchy.contains(rootType)) {
return false;
}
}
return true;
}
private void reinitSuperClass() {
final String className = getSuperClass();
try {
if (!isValidExtendedType(className)) {
final IType rootType = getRootSuperType();
assert rootType != null;
setSuperClass(rootType.getFullyQualifiedName(), true);
}
} catch (JavaModelException ex) {
this.fSuperClassStatus = SARLEclipsePlugin.getDefault().createStatus(IStatus.ERROR, ex);
}
}
private void reinitSuperInterfaces() {
final List<IStatus> status = new ArrayList<>();
final Set<String> validInterfaces = new HashSet<>();
for (final String interfaceName : getSuperInterfaces()) {
try {
if (!isValidImplementedType(interfaceName)) {
final IType rootType = getRootSuperInterface();
assert rootType != null;
validInterfaces.add(rootType.getFullyQualifiedName());
} else {
validInterfaces.add(interfaceName);
}
} catch (JavaModelException ex) {
status.add(SARLEclipsePlugin.getDefault().createStatus(IStatus.ERROR, ex));
}
}
setSuperInterfaces(new ArrayList<>(validInterfaces), true);
if (status.isEmpty() && isSuperInterfaceNeeded()) {
try {
status.add(SARLEclipsePlugin.getDefault().createStatus(
IStatus.ERROR, MessageFormat.format(
getMissedSuperInterfaceErrorMessage(),
getRootSuperInterface().getFullyQualifiedName())));
} catch (Throwable exception) {
//
}
}
if (status.isEmpty()) {
this.fSuperInterfacesStatus = SARLEclipsePlugin.getDefault().createOkStatus();
} else {
final IStatus[] tab = new IStatus[status.size()];
status.toArray(tab);
this.fSuperInterfacesStatus = SARLEclipsePlugin.getDefault().createMultiStatus(tab);
}
}
@Override
protected IStatus superClassChanged() {
IStatus status = super.superClassChanged();
assert status != null;
if (status.isOK() && isSuperTypeActivated()) {
final String className = getSuperClass();
try {
if (!isValidExtendedType(className)) {
status = SARLEclipsePlugin.getDefault().createStatus(
IStatus.ERROR, MessageFormat.format(
getInvalidSubtypeErrorMessage(),
className));
}
} catch (JavaModelException ex) {
status = ex.getJavaModelStatus();
}
}
return status;
}
@Override
protected IStatus superInterfacesChanged() {
IStatus status = super.superInterfacesChanged();
assert status != null;
if (status.isOK() && isSuperInterfaceActivated()) {
final List<IStatus> statusInfo = new ArrayList<>();
boolean hasInterface = false;
for (final String superInterface : getSuperInterfaces()) {
try {
if (!isValidImplementedType(superInterface)) {
statusInfo.add(SARLEclipsePlugin.getDefault().createStatus(
IStatus.ERROR, MessageFormat.format(
getInvalidInterfaceTypeErrorMessage(),
superInterface)));
} else {
hasInterface = true;
}
} catch (JavaModelException ex) {
statusInfo.add(SARLEclipsePlugin.getDefault().createStatus(IStatus.ERROR, ex));
}
}
if (!hasInterface && isSuperInterfaceNeeded()) {
try {
statusInfo.add(SARLEclipsePlugin.getDefault().createStatus(
IStatus.ERROR, MessageFormat.format(
getMissedSuperInterfaceErrorMessage(),
getRootSuperInterface().getFullyQualifiedName())));
} catch (Throwable exception) {
//
}
}
if (!statusInfo.isEmpty()) {
final IStatus[] tab = new IStatus[statusInfo.size()];
statusInfo.toArray(tab);
status = SARLEclipsePlugin.getDefault().createMultiStatus(tab);
}
}
return status;
}
/** Replies if the created type must have one super-interface.
*
* @return <code>true</code> if the type needs a super-interface;
* <code>false</code> if not.
*/
@SuppressWarnings("static-method")
protected boolean isSuperInterfaceNeeded() {
return false;
}
/** Replies the allowed root super type for the created type.
*
* @return the allowed root super type.
* @throws JavaModelException - when the Java model cannot enable to retreive the root type.
*/
@SuppressWarnings("static-method")
protected IType getRootSuperType() throws JavaModelException {
throw new UnsupportedOperationException();
}
/** Replies the allowed root super interface for the created type.
*
* @return the allowed root super interface.
* @throws JavaModelException - when the Java model cannot enable to retreive the root type.
*/
@SuppressWarnings("static-method")
protected IType getRootSuperInterface() throws JavaModelException {
throw new UnsupportedOperationException();
}
/** Create the components that are common to the creation of all
* the SARL elements.
*
* <p>You should invoke this from the {@link #createControl(Composite)} of
* the child class.
*
* @param parent - the component in which the common controls are added.
* @return the created composite.
*/
protected Composite createCommonControls(Composite parent) {
initializeDialogUnits(parent);
final Composite composite = SWTFactory.createComposite(
parent,
parent.getFont(),
COLUMNS, 1,
GridData.FILL_HORIZONTAL);
createContainerControls(composite, COLUMNS);
createPackageControls(composite, COLUMNS);
createSeparator(composite, COLUMNS);
createTypeNameControls(composite, COLUMNS);
return composite;
}
@Override
public final void createControl(Composite parent) {
final Composite composite = createCommonControls(parent);
createPageControls(composite);
setControl(composite);
readSettings();
doStatusUpdate();
}
/** Invoked to create the controls in the page.
*
* @param parent - the container of the controls.
*/
protected abstract void createPageControls(Composite parent);
/** Create the type from the data gathered in the wizard.
*
* @return the size of the created file.
*/
protected final int asyncCreateType() {
final int[] size = {0};
final IRunnableWithProgress op = new WorkspaceModifyOperation() {
@Override
protected void execute(IProgressMonitor monitor)
throws CoreException, InvocationTargetException, InterruptedException {
size[0] = createSARLType(monitor);
}
};
try {
getContainer().run(true, false, op);
} catch (InterruptedException e) {
// cancelled by user
return 0;
} catch (InvocationTargetException e) {
final Throwable realException = e.getTargetException();
SARLEclipsePlugin.getDefault().log(realException);
MessageDialog.openError(getShell(), getTitle(), realException.getMessage());
}
return size[0];
}
@Override
public final void createType(IProgressMonitor monitor) throws CoreException, InterruptedException {
// See createSARLType
throw new UnsupportedOperationException();
}
private ICompilationUnit getCompilationUnitStub() {
final String compilationUnitName = getCompilationUnitName(getTypeName());
return new CompilationUnit((PackageFragment) getPackageFragment(), compilationUnitName, DefaultWorkingCopyOwner.PRIMARY);
}
/** Create the inherited members.
*
* @param defaultSuperTypeQualifiedName the qualified name of the default super type.
* @param context the context.
* @param generateActionBlocks indicates if the action blocks must be generated.
* @param constructorBuilder the code for adding a constructor.
* @param actionBuilder the code for adding an operation.
* @param superTypeQualifiedName the qualified name of the super type.
* @param superInterfaceQualifiedNames the qualified names of the super interfaces.
* @throws JavaModelException if the java model is invalid.
*/
protected void createInheritedMembers(
String defaultSuperTypeQualifiedName,
XtendTypeDeclaration context, boolean generateActionBlocks,
ConstructorBuilder constructorBuilder, ActionBuilder actionBuilder,
String superTypeQualifiedName, String... superInterfaceQualifiedNames)
throws JavaModelException {
createInheritedMembers(defaultSuperTypeQualifiedName, context,
generateActionBlocks, constructorBuilder, actionBuilder,
superTypeQualifiedName, Arrays.asList(superInterfaceQualifiedNames));
}
/** Create the inherited members.
*
* @param defaultSuperTypeQualifiedName the qualified name of the default super type.
* @param context the context.
* @param generateActionBlocks indicates if the action blocks must be generated.
* @param constructorBuilder the code for adding a constructor.
* @param actionBuilder the code for adding an operation.
* @param superTypeQualifiedName the qualified name of the super type.
* @param superInterfaceQualifiedNames the qualified names of the super interfaces.
* @throws JavaModelException if the java model is invalid.
*/
protected void createInheritedMembers(
String defaultSuperTypeQualifiedName,
XtendTypeDeclaration context, boolean generateActionBlocks,
ConstructorBuilder constructorBuilder, ActionBuilder actionBuilder,
String superTypeQualifiedName, List<String> superInterfaceQualifiedNames)
throws JavaModelException {
final TypeFinder typeFinder = getTypeFinder();
final Map<ActionParameterTypes, IMethod> baseConstructors = Maps.newTreeMap((Comparator<ActionParameterTypes>) null);
this.jdt2sarl.populateInheritanceContext(
typeFinder,
null, null, null, null,
baseConstructors,
defaultSuperTypeQualifiedName,
Collections.<String>emptyList());
final Map<ActionParameterTypes, IMethod> constructors;
if (isCreateConstructors()) {
constructors = Maps.newTreeMap((Comparator<ActionParameterTypes>) null);
} else {
constructors = null;
}
final Map<ActionPrototype, IMethod> operationsToImplement;
if (isCreateInherited()) {
operationsToImplement = Maps.newHashMap();
} else {
operationsToImplement = null;
}
this.jdt2sarl.populateInheritanceContext(
typeFinder,
null, null, null,
operationsToImplement,
constructors,
superTypeQualifiedName,
superInterfaceQualifiedNames);
if (context != null) {
if (constructors != null && constructorBuilder != null) {
for (final Entry<ActionParameterTypes, IMethod> constructor : constructors.entrySet()) {
if (!baseConstructors.containsKey(constructor.getKey())) {
this.jdt2sarl.createStandardConstructorsWith(constructorBuilder,
Collections.singletonList(constructor.getValue()),
context);
break;
}
}
}
if (operationsToImplement != null && actionBuilder != null) {
this.jdt2sarl.createActionsWith(actionBuilder, operationsToImplement.values(),
generateActionBlocks ? context : null);
}
}
}
/** Replies the type finder in the context of the current project.
*
* @return the type finder.
* @since 0.5
*/
protected final TypeFinder getTypeFinder() {
return this.jdt2sarl.toTypeFinder(getJavaProject());
}
/** Create the SARL type.
*
* @param monitor - the progression monitor.
* @return the size of the generated code.
* @throws CoreException when the creation failed.
* @throws InterruptedException when the operation was canceled.
*/
public int createSARLType(IProgressMonitor monitor) throws CoreException, InterruptedException {
try {
final SubMonitor mainmon = SubMonitor.convert(monitor, getTitle(), STEPS);
// Create the package if not existing
IPackageFragment packageFragment = getPackageFragment();
if (!packageFragment.exists()) {
packageFragment = getPackageFragmentRoot().createPackageFragment(
getPackageFragment().getElementName(),
true,
mainmon.newChild(1));
} else {
mainmon.worked(1);
}
// Create the file
final IFolder packageResource = (IFolder) packageFragment.getResource();
if (!packageResource.exists()) {
CoreUtility.createFolder(packageResource, true, true, mainmon.newChild(1));
} else {
mainmon.worked(1);
}
IFile sarlFile = packageResource.getFile(
getTypeName() + "." //$NON-NLS-1$
+ this.sarlFileExtension);
int index = 1;
while (sarlFile.exists()) {
sarlFile = packageResource.getFile(
getTypeName() + index + "." //$NON-NLS-1$
+ this.sarlFileExtension);
++index;
}
final URI sarlUri = this.storage2UriMapper.getUri(sarlFile);
final ResourceSet resourceSet = this.resourceSetFactory.get(packageFragment.getJavaProject().getProject());
final ICompilationUnit compilationUnit = getCompilationUnitStub();
final String lineSeparator = this.whitespaceInformationProvider
.getLineSeparatorInformation(sarlUri).getLineSeparator();
mainmon.worked(1);
// Create the type content
final SubMonitor mon1 = mainmon.newChild(1);
mon1.setTaskName(MessageFormat.format(Messages.AbstractNewSarlElementWizardPage_5, getTypeName()));
final String typeComment = getTypeComment(compilationUnit, lineSeparator);
final IJvmTypeProvider typeProvider = this.jdtTypeProviderFactory.findOrCreateTypeProvider(resourceSet);
final ImportManager imports = new ImportManager(true);
this.injector.injectMembers(imports);
final FakeTreeAppendable appender = new FakeTreeAppendable(imports);
this.injector.injectMembers(appender);
generateTypeContent(appender, typeProvider, typeComment, mon1);
mon1.done();
// Build the full file content
final SubMonitor mon2 = mainmon.newChild(1);
mon2.setTaskName(MessageFormat.format(Messages.AbstractNewSarlElementWizardPage_6, getTypeName()));
final String fileComment = getFileComment(compilationUnit, lineSeparator);
final StringBuilder realContent = new StringBuilder();
if (!Strings.isNullOrEmpty(fileComment)) {
realContent.append(fileComment);
realContent.append(lineSeparator);
realContent.append(lineSeparator);
}
realContent.append(appender.getContent());
realContent.append(lineSeparator);
mon2.done();
final SubMonitor mon3 = mainmon.newChild(1);
mon3.setTaskName(MessageFormat.format(Messages.AbstractNewSarlElementWizardPage_7, getTypeName()));
final String content = this.formatterFacade.format(realContent.toString());
mon3.done();
// Write the resource
final SubMonitor mon4 = mainmon.newChild(1);
mon4.setTaskName(MessageFormat.format(Messages.AbstractNewSarlElementWizardPage_8, getTypeName()));
try (ByteArrayInputStream stream = new ByteArrayInputStream(content.getBytes())) {
sarlFile.create(stream, true, mon4);
}
setResource(sarlFile);
saveSettings();
mon4.done();
return content.length();
} catch (OperationCanceledException e) {
throw new InterruptedException();
} catch (CoreException e) {
throw e;
} catch (Exception e) {
throw new CoreException(SARLEclipsePlugin.getDefault().createStatus(IStatus.ERROR, e));
}
}
/** Read the settings of the dialog box.
*/
protected void readSettings() {
boolean createConstructors = false;
boolean createUnimplemented = true;
boolean createEventHandlers = true;
boolean createLifecycleFunctions = true;
final IDialogSettings dialogSettings = getDialogSettings();
if (dialogSettings != null) {
final IDialogSettings section = dialogSettings.getSection(getName());
if (section != null) {
createConstructors = section.getBoolean(SETTINGS_CREATECONSTR);
createUnimplemented = section.getBoolean(SETTINGS_CREATEUNIMPLEMENTED);
createEventHandlers = section.getBoolean(SETTINGS_GENERATEEVENTHANDLERS);
createLifecycleFunctions = section.getBoolean(SETTINGS_GENERATELIFECYCLEFUNCTIONS);
}
}
setMethodStubSelection(createConstructors, createUnimplemented, createEventHandlers,
createLifecycleFunctions, true);
}
/** Save the settings of the dialog box.
*/
protected void saveSettings() {
final IDialogSettings dialogSettings = getDialogSettings();
if (dialogSettings != null) {
IDialogSettings section = dialogSettings.getSection(getName());
if (section == null) {
section = dialogSettings.addNewSection(getName());
}
section.put(SETTINGS_CREATECONSTR, isCreateConstructors());
section.put(SETTINGS_CREATEUNIMPLEMENTED, isCreateInherited());
section.put(SETTINGS_GENERATEEVENTHANDLERS, isCreateStandardEventHandlers());
section.put(SETTINGS_GENERATELIFECYCLEFUNCTIONS, isCreateStandardLifecycleFunctions());
}
}
/** Invoked for retreiving the definition of the new type.
*
* @param appender the receiver of the code.
* @param typeProvider provides types.
* @param comment the comment of the element to generate.
* @param monitor the progression monitor.
* @throws Exception if an error occurs when creating the content.
*/
protected abstract void generateTypeContent(ISourceAppender appender, IJvmTypeProvider typeProvider,
String comment, IProgressMonitor monitor) throws Exception;
/** Create the controls related to the behavior units to generate.
*
* @param composite - the container of the controls.
* @param columns - the number of columns.
* @param enableConstructors - indicates if the constructor creation is enable.
* @param enableInherited - indicates if the inherited operation creation is enable.
* @param defaultEvents - indicates if the default events will be generated.
* @param lifecycleFunctions - indicates if the default lifecycle functions will be generated.
*/
protected void createMethodStubControls(Composite composite, int columns,
boolean enableConstructors, boolean enableInherited, boolean defaultEvents,
boolean lifecycleFunctions) {
this.isConstructorCreationEnabled = enableConstructors;
this.isInheritedCreationEnabled = enableInherited;
this.isDefaultEventGenerated = defaultEvents;
this.isDefaultLifecycleFunctionsGenerated = lifecycleFunctions;
final List<String> nameList = new ArrayList<>(4);
if (enableConstructors) {
nameList.add(Messages.AbstractNewSarlElementWizardPage_0);
}
if (enableInherited) {
nameList.add(Messages.AbstractNewSarlElementWizardPage_1);
}
if (defaultEvents) {
nameList.add(Messages.AbstractNewSarlElementWizardPage_17);
}
if (lifecycleFunctions) {
nameList.add(Messages.AbstractNewSarlElementWizardPage_18);
}
if (nameList.isEmpty()) {
return;
}
final String[] buttonNames = new String[nameList.size()];
nameList.toArray(buttonNames);
this.methodStubsButtons = new SelectionButtonDialogFieldGroup(SWT.CHECK, buttonNames, 1);
this.methodStubsButtons.setLabelText(Messages.AbstractNewSarlElementWizardPage_2);
final Control labelControl = this.methodStubsButtons.getLabelControl(composite);
LayoutUtil.setHorizontalSpan(labelControl, columns);
DialogField.createEmptySpace(composite);
final Control buttonGroup = this.methodStubsButtons.getSelectionButtonsGroup(composite);
LayoutUtil.setHorizontalSpan(buttonGroup, columns - 1);
}
/**
* Returns the current selection state of the 'Create Constructors' checkbox.
*
* @return the selection state of the 'Create Constructors' checkbox
*/
protected boolean isCreateConstructors() {
return this.isConstructorCreationEnabled && this.methodStubsButtons.isSelected(0);
}
/**
* Returns the current selection state of the 'Create inherited abstract methods'
* checkbox.
*
* @return the selection state of the 'Create inherited abstract methods' checkbox
*/
protected boolean isCreateInherited() {
int idx = 0;
if (this.isConstructorCreationEnabled) {
++idx;
}
return this.isInheritedCreationEnabled && this.methodStubsButtons.isSelected(idx);
}
/**
* Returns the current selection state of the 'Create standard event handlers'
* checkbox.
*
* @return the selection state of the 'Create standard event handlers' checkbox
*/
protected boolean isCreateStandardEventHandlers() {
int idx = 0;
if (this.isConstructorCreationEnabled) {
++idx;
}
if (this.isInheritedCreationEnabled) {
++idx;
}
return this.isDefaultEventGenerated && this.methodStubsButtons.isSelected(idx);
}
/**
* Returns the current selection state of the 'Create standard lifecycle functions'
* checkbox.
*
* @return the selection state of the 'Create standard lifecycle functions' checkbox
*/
protected boolean isCreateStandardLifecycleFunctions() {
int idx = 0;
if (this.isConstructorCreationEnabled) {
++idx;
}
if (this.isInheritedCreationEnabled) {
++idx;
}
if (this.isDefaultEventGenerated) {
++idx;
}
return this.isDefaultLifecycleFunctionsGenerated && this.methodStubsButtons.isSelected(idx);
}
/**
* Sets the selection state of the method stub checkboxes.
*
* @param createConstructors initial selection state of the 'Create Constructors' checkbox.
* @param createInherited initial selection state of the 'Create inherited abstract methods' checkbox.
* @param createEventHandlers initial selection state of the 'Create standard event handlers' checkbox.
* @param createLifecycleFunctions initial selection state of the 'Create standard lifecycle functions' checkbox.
* @param canBeModified if <code>true</code> the method stub checkboxes can be changed by
* the user. If <code>false</code> the buttons are "read-only"
*/
protected void setMethodStubSelection(boolean createConstructors, boolean createInherited,
boolean createEventHandlers, boolean createLifecycleFunctions, boolean canBeModified) {
if (this.methodStubsButtons != null) {
int idx = 0;
if (this.isConstructorCreationEnabled) {
this.methodStubsButtons.setSelection(idx, createConstructors);
++idx;
}
if (this.isInheritedCreationEnabled) {
this.methodStubsButtons.setSelection(idx, createInherited);
++idx;
}
if (this.isDefaultEventGenerated) {
this.methodStubsButtons.setSelection(idx, createEventHandlers);
++idx;
}
if (this.isDefaultLifecycleFunctionsGenerated) {
this.methodStubsButtons.setSelection(idx, createLifecycleFunctions);
++idx;
}
this.methodStubsButtons.setEnabled(canBeModified);
}
}
/** Create an instanceof the super-class selection dialog.
*
* @param parent the parent.
* @param context the execution context.
* @param project the Java project.
* @param extension the extension to give to the dialog box.
* @param multi indicates if the selection could be done on multiple elements.
* @return the dialog, or <code>null</code> for using the default dialog box.
*/
@SuppressWarnings("static-method")
protected AbstractSuperTypeSelectionDialog<?> createSuperClassSelectionDialog(
Shell parent, IRunnableContext context, IJavaProject project, SarlSpecificTypeSelectionExtension extension,
boolean multi) {
return null;
}
@Override
protected IType chooseSuperClass() {
final IJavaProject project = getJavaProject();
if (project == null) {
return null;
}
final IJvmTypeProvider typeProvider = this.jdtTypeProviderFactory.findOrCreateTypeProvider(
this.resourceSetFactory.get(project.getProject()));
final SarlSpecificTypeSelectionExtension extension = new SarlSpecificTypeSelectionExtension(typeProvider);
this.injector.injectMembers(extension);
final AbstractSuperTypeSelectionDialog<?> dialog = createSuperClassSelectionDialog(getShell(),
getWizard().getContainer(), project, extension, false);
if (dialog != null) {
this.injector.injectMembers(dialog);
dialog.setTitle(NewWizardMessages.NewTypeWizardPage_SuperClassDialog_title);
dialog.setMessage(NewWizardMessages.NewTypeWizardPage_SuperClassDialog_message);
dialog.setInitialPattern(getSuperClass());
if (dialog.open() == Window.OK) {
return (IType) dialog.getFirstResult();
}
} else {
super.chooseSuperClass();
}
return null;
}
/** Create an instanceof the super-interface selection dialog.
*
* @param parent the parent.
* @param context the execution context.
* @param project the Java project.
* @param extension the extension to give to the dialog box.
* @param multi indicates if the selection could be done on multiple elements.
* @return the dialog, or <code>null</code> for using the default dialog box.
*/
@SuppressWarnings("static-method")
protected AbstractSuperTypeSelectionDialog<?> createSuperInterfaceSelectionDialog(
Shell parent, IRunnableContext context, IJavaProject project, SarlSpecificTypeSelectionExtension extension,
boolean multi) {
return null;
}
private static void createInfoCall(IExpressionBuilder builder, String message) {
final JvmParameterizedTypeReference capacity = builder.newTypeRef(null, LOGGING_CAPACITY_NAME);
final String objectType = Object.class.getName();
final String objectArrayType = objectType + "[]"; //$NON-NLS-1$
final JvmOperation infoMethod = Iterables.find(
((JvmDeclaredType) capacity.getType()).getDeclaredOperations(), (it) -> {
if (Objects.equals(it.getSimpleName(), "info") //$NON-NLS-1$
&& it.getParameters().size() == 2) {
final String type1 = it.getParameters().get(0).getParameterType().getIdentifier();
final String type2 = it.getParameters().get(1).getParameterType().getIdentifier();
return Objects.equals(objectType, type1) && Objects.equals(objectArrayType, type2);
}
return false;
},
null);
if (infoMethod != null) {
builder.setExpression("info(\"" + message + "\")"); //$NON-NLS-1$ //$NON-NLS-2$
((XFeatureCall) builder.getXExpression()).setFeature(infoMethod);
}
}
/** Create the default standard SARL event templates.
*
* @param elementTypeName the name of the element type.
* @param behaviorUnitAdder the adder of behavior unit.
* @param usesAdder the adder of uses statement.
* @return {@code true} if the units are added; {@code false} otherwise.
* @since 0.5
*/
protected boolean createStandardSARLEventTemplates(String elementTypeName,
Function1<String, ISarlBehaviorUnitBuilder> behaviorUnitAdder,
Procedure1<String> usesAdder) {
if (!isCreateStandardEventHandlers()) {
return false;
}
Object type;
try {
type = getTypeFinder().findType(INITIALIZE_EVENT_NAME);
} catch (JavaModelException e) {
type = null;
}
if (type != null) {
// SARL Libraries are on the classpath
usesAdder.apply(LOGGING_CAPACITY_NAME);
ISarlBehaviorUnitBuilder unit = behaviorUnitAdder.apply(INITIALIZE_EVENT_NAME);
IBlockExpressionBuilder block = unit.getExpression();
block.setInnerDocumentation(MessageFormat.format(
Messages.AbstractNewSarlElementWizardPage_9,
elementTypeName));
IExpressionBuilder expr = block.addExpression();
createInfoCall(expr, "The " + elementTypeName + " was started."); //$NON-NLS-1$ //$NON-NLS-2$
unit = behaviorUnitAdder.apply(DESTROY_EVENT_NAME);
block = unit.getExpression();
block.setInnerDocumentation(MessageFormat.format(
Messages.AbstractNewSarlElementWizardPage_10,
elementTypeName));
expr = block.addExpression();
createInfoCall(expr, "The " + elementTypeName + " was stopped."); //$NON-NLS-1$ //$NON-NLS-2$
unit = behaviorUnitAdder.apply(AGENTSPAWNED_EVENT_NAME);
block = unit.getExpression();
block.setInnerDocumentation(MessageFormat.format(
Messages.AbstractNewSarlElementWizardPage_11,
elementTypeName));
unit = behaviorUnitAdder.apply(AGENTKILLED_EVENT_NAME);
block = unit.getExpression();
block.setInnerDocumentation(MessageFormat.format(
Messages.AbstractNewSarlElementWizardPage_12,
elementTypeName));
unit = behaviorUnitAdder.apply(CONTEXTJOINED_EVENT_NAME);
block = unit.getExpression();
block.setInnerDocumentation(MessageFormat.format(
Messages.AbstractNewSarlElementWizardPage_13,
elementTypeName));
unit = behaviorUnitAdder.apply(CONTEXTLEFT_EVENT_NAME);
block = unit.getExpression();
block.setInnerDocumentation(MessageFormat.format(
Messages.AbstractNewSarlElementWizardPage_14,
elementTypeName));
unit = behaviorUnitAdder.apply(MEMBERJOINED_EVENT_NAME);
block = unit.getExpression();
block.setInnerDocumentation(MessageFormat.format(
Messages.AbstractNewSarlElementWizardPage_15,
elementTypeName));
unit = behaviorUnitAdder.apply(MEMBERLEFT_EVENT_NAME);
block = unit.getExpression();
block.setInnerDocumentation(MessageFormat.format(
Messages.AbstractNewSarlElementWizardPage_16,
elementTypeName));
return true;
}
return false;
}
/** Create the default standard lifecycle function templates.
*
* @param elementTypeName the name of the element type.
* @param actionAdder the adder of actions.
* @param usesAdder the adder of uses statement.
* @return {@code true} if the units are added; {@code false} otherwise.
* @since 0.5
*/
protected boolean createStandardSARLLifecycleFunctionTemplates(String elementTypeName,
Function1<String, ISarlActionBuilder> actionAdder,
Procedure1<String> usesAdder) {
if (!isCreateStandardLifecycleFunctions()) {
return false;
}
usesAdder.apply(LOGGING_CAPACITY_NAME);
ISarlActionBuilder action = actionAdder.apply(INSTALL_SKILL_NAME);
IBlockExpressionBuilder block = action.getExpression();
block.setInnerDocumentation(MessageFormat.format(
Messages.AbstractNewSarlElementWizardPage_19,
elementTypeName));
IExpressionBuilder expr = block.addExpression();
createInfoCall(expr, "Installing the " + elementTypeName); //$NON-NLS-1$
action = actionAdder.apply(UNINSTALL_SKILL_NAME);
block = action.getExpression();
block.setInnerDocumentation(MessageFormat.format(
Messages.AbstractNewSarlElementWizardPage_20,
elementTypeName));
expr = block.addExpression();
createInfoCall(expr, "Uninstalling the " + elementTypeName); //$NON-NLS-1$
return true;
}
@Override
protected void chooseSuperInterfaces() {
final IJavaProject project = getJavaProject();
if (project == null) {
return;
}
final IJvmTypeProvider typeProvider = this.jdtTypeProviderFactory.findOrCreateTypeProvider(
this.resourceSetFactory.get(project.getProject()));
final SarlSpecificTypeSelectionExtension extension = new SarlSpecificTypeSelectionExtension(typeProvider);
this.injector.injectMembers(extension);
final AbstractSuperTypeSelectionDialog<?> dialog = createSuperInterfaceSelectionDialog(getShell(),
getWizard().getContainer(), project, extension, true);
if (dialog != null) {
this.injector.injectMembers(dialog);
dialog.setTitle(NewWizardMessages.NewTypeWizardPage_InterfacesDialog_interface_title);
dialog.setMessage(NewWizardMessages.NewTypeWizardPage_InterfacesDialog_message);
try {
dialog.setInitialPattern(getRootSuperInterface().getFullyQualifiedName());
} catch (JavaModelException exception) {
SARLEclipsePlugin.getDefault().log(exception);
}
if (dialog.open() == Window.OK) {
final Object[] tab = dialog.getResult();
if (tab != null) {
final List<String> list = new ArrayList<>(tab.length);
for (final Object obj : tab) {
if (obj instanceof IType) {
final IType type = (IType) obj;
list.add(type.getFullyQualifiedName());
}
}
setSuperInterfaces(list, true);
}
}
} else {
super.chooseSuperInterfaces();
}
}
}