blob: 08f9b1514fb21ab692f518b3f7900ac0c456c289 [file] [log] [blame]
/**********************************************************************
* This file is part of "Object Teams Development Tooling"-Software
*
* Copyright 2011 GK Software AG
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Please visit http://www.eclipse.org/objectteams for updates and contact.
*
* Contributors:
* Stephan Herrmann - Initial API and implementation
**********************************************************************/
package org.eclipse.objectteams.otdt.internal.refactoring.adaptor;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMember;
import org.eclipse.jdt.core.ISourceRange;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.refactoring.CompilationUnitChange;
import org.eclipse.jdt.core.refactoring.descriptors.ExtractInterfaceDescriptor;
import org.eclipse.jdt.core.refactoring.descriptors.JavaRefactoringDescriptor;
import org.eclipse.jdt.internal.core.refactoring.descriptors.RefactoringSignatureDescriptorFactory;
import org.eclipse.jdt.internal.core.manipulation.StubUtility;
import org.eclipse.jdt.internal.corext.refactoring.JDTRefactoringDescriptorComment;
import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringDescriptorUtil;
import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
import org.eclipse.jdt.internal.corext.refactoring.changes.DynamicValidationRefactoringChange;
import org.eclipse.jdt.internal.corext.util.JdtFlags;
import org.eclipse.jdt.internal.corext.util.Messages;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.internal.ui.viewsupport.BasicElementLabels;
import org.eclipse.jdt.ui.JavaElementLabels;
import org.eclipse.ltk.core.refactoring.CategorizedTextEditGroup;
import org.eclipse.ltk.core.refactoring.Change;
import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
import org.eclipse.ltk.core.refactoring.TextEditBasedChange;
import org.eclipse.ltk.core.refactoring.TextEditChangeGroup;
import org.eclipse.objectteams.otdt.core.OTModelManager;
import org.eclipse.objectteams.otdt.internal.refactoring.RefactoringMessages;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Text;
import org.eclipse.text.edits.InsertEdit;
import org.eclipse.text.edits.TextEdit;
import org.eclipse.text.edits.TextEditGroup;
import base org.eclipse.jdt.internal.corext.refactoring.structure.ExtractInterfaceProcessor;
import base org.eclipse.jdt.internal.corext.refactoring.structure.constraints.SuperTypeRefactoringProcessor;
import base org.eclipse.jdt.internal.corext.util.CodeFormatterUtil;
import base org.eclipse.jdt.internal.ui.refactoring.ExtractInterfaceWizard.ExtractInterfaceInputPage;
/**
* When extracting an interface from a role, offer to create the new interface
* as a role of the enclosing team.
*
* @author stephan
*/
@SuppressWarnings({ "restriction", "decapsulation" })
public team class ExtractInterfaceAdaptor {
/**
* Step 1: add a checkbox to the wizard: "[ ] Create as role of the enclosing team"
*/
protected class InputPage playedBy ExtractInterfaceInputPage {
Processor getProcessor() -> get ExtractInterfaceProcessor fProcessor;
Button createCheckbox(Composite parent, String title, boolean value)
-> Button createCheckbox(Composite parent, String title, boolean value);
Button fAsRole;
void createAsRoleCheckBox(Composite parent) <- after Text createTextInputField(Composite parent)
when (OTModelManager.isRole(getProcessor().getSubType()));
void createAsRoleCheckBox(Composite parent) {
String title= RefactoringMessages.ExtractInterfaceAdaptor_createAsRole_checkbox;
boolean defaultValue= true;
fAsRole = createCheckbox(parent, title, defaultValue);
getProcessor().setAsRole(defaultValue);
fAsRole.addSelectionListener(new SelectionAdapter(){
@Override
public void widgetSelected(SelectionEvent e) {
getProcessor().setAsRole(fAsRole.getSelection());
}
});
}
}
/**
* Superclass of the main role {@link Processor} in order to adapt final methods.
* This role (and its sub) is only active when marked as "fAsRole", which is set from the checkbox.
*/
protected class SuperTypeProcessor playedBy SuperTypeRefactoringProcessor when (fAsRole) {
boolean fAsRole = false;
createTypeTemplate <- replace createTypeTemplate;
callin String createTypeTemplate(ICompilationUnit unit, String imports, String fileComment, String comment, String content)
throws CoreException
{
return base.createTypeTemplate(unit, imports, fileComment, comment, content); // no adaptation, overridden for real work
}
createTypeSource <- replace createTypeSource;
callin String createTypeSource(ICompilationUnit extractedWorkingCopy, IType superType)
throws CoreException
{
return base.createTypeSource(extractedWorkingCopy, superType); // no adaptation, overridden for real work
}
}
/**
* Main adaptation: create different changes: don't create a new compilation unit,
* but insert the new interface into the enclosing team.
* This role is also a team to provide a temporary activation context for its
* nested role {@link Formatter}.
*/
protected team class Processor extends SuperTypeProcessor playedBy ExtractInterfaceProcessor {
IMember[] getMembers() -> get IMember[] fMembers;
protected IType getSubType() -> get IType fSubType;
// indentation of the new interface depends on the nesting level of the current role (consumed by nested role Formatter).
int indentationLevel;
public void setAsRole(boolean value) {
fAsRole = value;
}
createChange <- replace createChange;
/** This method is essentially copied from its base and adjusted as marked. */
@SuppressWarnings({ "inferredcallout", "basecall" })
callin Change createChange(IProgressMonitor monitor) throws CoreException {
Assert.isNotNull(monitor);
IMember[] fMembers = getMembers();
try {
monitor.beginTask("", 1); //$NON-NLS-1$
monitor.setTaskName(RefactoringCoreMessages.ExtractInterfaceProcessor_creating);
final Map<String, String> arguments= new HashMap<String, String>();
String project= null;
final IJavaProject javaProject= fSubType.getJavaProject();
if (javaProject != null)
project= javaProject.getElementName();
int flags= JavaRefactoringDescriptor.JAR_MIGRATION | JavaRefactoringDescriptor.JAR_REFACTORING | RefactoringDescriptor.STRUCTURAL_CHANGE | RefactoringDescriptor.MULTI_CHANGE;
try {
if (fSubType.isLocal() || fSubType.isAnonymous())
flags|= JavaRefactoringDescriptor.JAR_SOURCE_ATTACHMENT;
} catch (JavaModelException exception) {
JavaPlugin.log(exception);
}
//{ObjectTeams: changed part: get new type from the enclosing team instead of creating a new compilation unit:
final IType type= fSubType.getDeclaringType().getType(fSuperName);
// SH}
final String description= Messages.format(RefactoringCoreMessages.ExtractInterfaceProcessor_description_descriptor_short, BasicElementLabels.getJavaElementName(fSuperName));
final String header= Messages.format(RefactoringCoreMessages.ExtractInterfaceProcessor_descriptor_description, new String[] { JavaElementLabels.getElementLabel(type, JavaElementLabels.ALL_FULLY_QUALIFIED), JavaElementLabels.getElementLabel(fSubType, JavaElementLabels.ALL_FULLY_QUALIFIED) });
final JDTRefactoringDescriptorComment comment= new JDTRefactoringDescriptorComment(project, this, header);
comment.addSetting(Messages.format(RefactoringCoreMessages.ExtractInterfaceProcessor_refactored_element_pattern, JavaElementLabels.getElementLabel(type, JavaElementLabels.ALL_FULLY_QUALIFIED)));
final String[] settings= new String[fMembers.length];
for (int index= 0; index < settings.length; index++)
settings[index]= JavaElementLabels.getElementLabel(fMembers[index], JavaElementLabels.ALL_FULLY_QUALIFIED);
comment.addSetting(JDTRefactoringDescriptorComment.createCompositeSetting(RefactoringCoreMessages.ExtractInterfaceProcessor_extracted_members_pattern, settings));
addSuperTypeSettings(comment, true);
final ExtractInterfaceDescriptor descriptor= RefactoringSignatureDescriptorFactory.createExtractInterfaceDescriptor(project, description, comment.asString(), arguments, flags);
arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT, JavaRefactoringDescriptorUtil.elementToHandle(project, fSubType));
arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_NAME, fSuperName);
for (int index= 0; index < fMembers.length; index++)
arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_ELEMENT + (index + 1), JavaRefactoringDescriptorUtil.elementToHandle(project, fMembers[index]));
arguments.put(ATTRIBUTE_COMMENTS, Boolean.valueOf(fComments).toString());
arguments.put(ATTRIBUTE_REPLACE, Boolean.valueOf(fReplace).toString());
arguments.put(ATTRIBUTE_INSTANCEOF, Boolean.valueOf(fInstanceOf).toString());
//{ObjectTeams: different way to construct this change:
TextEditBasedChange[] allChanges = fChangeManager.getAllChanges();
if (fSuperSource != null && fSuperSource.length() > 0) {
ICompilationUnit compilationUnit = fSubType.getCompilationUnit();
// find insert position, start at name of enclosing team ...
ISourceRange nameRange = fSubType.getDeclaringType().getNameRange();
int offset = nameRange.getOffset() + nameRange.getLength();
// ... then travel to the line past the next '{'
String source = compilationUnit.getSource();
while (offset < source.length() && source.charAt(offset++) != '{');
while (offset < source.length() && source.charAt(offset++) != '\n');
// create the change:
CompilationUnitChange createIfcChange = new CompilationUnitChange("", compilationUnit); //$NON-NLS-1$
InsertEdit edit = new InsertEdit(offset, fSuperSource);
createIfcChange.setEdit(edit);
// insert it into the existing changes ...
CompilationUnitChange combinedChange = (CompilationUnitChange) fChangeManager.get(compilationUnit);
// ... using the same category:
TextEditChangeGroup[] groups = combinedChange.getTextEditChangeGroups();
TextEditGroup group = new CategorizedTextEditGroup(NLS.bind(RefactoringMessages.ExtractInterfaceAdaptor_createInterface_changeName,fSuperName), edit, groups[0].getGroupCategorySet());
// finally assemble everything:
combinedChange.addEdit(edit);
combinedChange.addChangeGroup(new TextEditChangeGroup(createIfcChange, group));
allChanges = new TextEditBasedChange[]{combinedChange};
}
// SH}
final DynamicValidationRefactoringChange change= new DynamicValidationRefactoringChange(descriptor, RefactoringCoreMessages.ExtractInterfaceRefactoring_name, allChanges);
/* orig:
final IFile file= ResourceUtil.getFile(fSubType.getCompilationUnit());
if (fSuperSource != null && fSuperSource.length() > 0)
change.add(new CreateCompilationUnitChange(fSubType.getPackageFragment().getCompilationUnit(JavaModelUtil.getRenamedCUName(fSubType.getCompilationUnit(), fSuperName)), fSuperSource, file.getCharset(false)));
:giro */
monitor.worked(1);
return change;
} finally {
monitor.done();
}
}
/** avoid generating file comment, package declaration and import statements. */
@Override
@SuppressWarnings("basecall")
callin String createTypeTemplate(ICompilationUnit unit, String imports, String fileComment, String comment, String content)
throws CoreException
{
Assert.isNotNull(unit);
Assert.isNotNull(imports);
Assert.isNotNull(content);
final String delimiter= StubUtility.getLineDelimiterUsed(unit.getJavaProject());
if (!content.startsWith(JdtFlags.VISIBILITY_STRING_PUBLIC))
content = JdtFlags.VISIBILITY_STRING_PROTECTED+' '+content;
String source = StubUtility.getCompilationUnitContent(unit, ""/*packageDeclaration*/, null/*fileComment*/, comment, content, delimiter); //$NON-NLS-1$
return source+'\n';
}
/** This callin activates the enclosing Processor team in order to let the Formatter role feed our special indentation into formatting. */
@Override
callin String createTypeSource(ICompilationUnit extractedWorkingCopy, IType superType)
throws CoreException
{
IType currentType = superType;
this.indentationLevel = 0;
while ((currentType = currentType.getDeclaringType()) != null)
this.indentationLevel++;
within(this)
return base.createTypeSource(extractedWorkingCopy, superType);
}
/** Sole purpose of this role: manipulate the indentation level if the enclosing Processor team is active. */
protected class Formatter playedBy CodeFormatterUtil
{
TextEdit format2(int kind, String source, int indentationLevel, String lineSeparator, Map<String, String> options)
<- replace TextEdit format2(int kind, String source, int indentationLevel, String lineSeparator, Map<String, String> options);
static callin TextEdit format2(int kind, String source, int indentationLevel, String lineSeparator, Map<String, String> options) {
return base.format2(kind, source, Processor.this.indentationLevel, lineSeparator, options);
}
}
}
}