/*******************************************************************************
* Copyright (c) 2017 Rogue Wave Software Inc. and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Rogue Wave Software Inc. - initial implementation
*******************************************************************************/
package org.eclipse.php.phpunit.ui.wizards;
import java.lang.reflect.InvocationTargetException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.dltk.core.*;
import org.eclipse.dltk.internal.core.search.DLTKSearchTypeNameMatch;
import org.eclipse.dltk.ui.DLTKPluginImages;
import org.eclipse.jface.viewers.*;
import org.eclipse.jface.window.Window;
import org.eclipse.jface.wizard.IWizardContainer;
import org.eclipse.php.core.compiler.PHPFlags;
import org.eclipse.php.internal.ui.PHPUiPlugin;
import org.eclipse.php.internal.ui.util.PHPElementImageDescriptor;
import org.eclipse.php.internal.ui.util.StatusInfo;
import org.eclipse.php.internal.ui.wizards.fields.IListAdapter;
import org.eclipse.php.internal.ui.wizards.fields.ListDialogField;
import org.eclipse.php.phpunit.PHPUnitMessages;
import org.eclipse.php.phpunit.PHPUnitPlugin;
import org.eclipse.php.phpunit.model.PHPUnitSearchEngine;
import org.eclipse.php.phpunit.ui.ElementSelectionDialog;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Shell;
@SuppressWarnings("restriction")
public class TestSuiteWizardPage extends PHPUnitWizardPage {
protected static class PHPTypeListLabelProvider extends LabelProvider implements ILabelDecorator {
private Image fClassImage;
private Image fAbsatractClassImage;
public PHPTypeListLabelProvider() {
fClassImage = DLTKPluginImages.get(DLTKPluginImages.IMG_OBJS_CLASS);
fAbsatractClassImage = PHPUiPlugin.getImageDescriptorRegistry()
.get(new PHPElementImageDescriptor(DLTKPluginImages.DESC_OBJS_CLASS,
PHPElementImageDescriptor.ABSTRACT, PHPElementImageDescriptor.SMALL_SIZE));
}
@Override
public String getText(Object element) {
if (element != null) {
String elementName = ""; //$NON-NLS-1$
String fileName = ""; //$NON-NLS-1$
if (element instanceof DLTKSearchTypeNameMatch) {
DLTKSearchTypeNameMatch typeMatch = (DLTKSearchTypeNameMatch) element;
IType type = typeMatch.getType();
elementName = type.getElementName();
fileName = type.getSourceModule().getElementName();
} else if (element instanceof IType) {
IType sourceElement = (IType) element;
elementName = sourceElement.getElementName();
fileName = sourceElement.getSourceModule().getElementName();
}
StringBuilder result = new StringBuilder(elementName);
result.append(" - "); //$NON-NLS-1$
result.append(fileName); // $NON-NLS-1$
return result.toString();
}
return ""; //$NON-NLS-1$
}
@Override
public Image getImage(Object element) {
Image result = null;
if (element != null) {
IType type = null;
if (element instanceof DLTKSearchTypeNameMatch) {
DLTKSearchTypeNameMatch typeMatch = (DLTKSearchTypeNameMatch) element;
type = typeMatch.getType();
}
if (element instanceof IType) {
type = ((IType) element);
}
try {
int flags = type.getFlags();
if (PHPFlags.isClass(flags)) {
if (PHPFlags.isAbstract(flags)) {
result = fAbsatractClassImage;
} else {
result = fClassImage;
}
}
} catch (ModelException e) {
PHPUnitPlugin.log(e);
}
}
return result;
}
@Override
public Image decorateImage(Image image, Object element) {
return image;
}
@Override
public String decorateText(String text, Object element) {
return text;
}
}
private final static String PAGE_NAME = "TestCaseWizardPage"; //$NON-NLS-1$
private final static String TEST_SUFFIX = "Suite"; //$NON-NLS-1$
private ListDialogField<IType> fElementsToTestList;
private Button addInterfacesBtn;
private IType[] PHP_UNIT_SUITE_BASE_CLASS_CACHE;
private IType[] PHP_UNIT_CASE_AND_SUITE_NON_ABSTRAXT_CLASS_CACHE;
public TestSuiteWizardPage() {
super(PAGE_NAME);
setTitle(PHPUnitMessages.TestSuiteWizardPage_5);
setDescription(PHPUnitMessages.TestSuiteWizardPage_4);
fSuperClassDialogField.setText(PHPUnitSearchEngine.CLASS_SUITE);
superClassChanged();
}
public TestSuiteWizardPage(final String pageName) {
super(pageName);
}
@Override
protected IType chooseSuperClass() {
final IContainer root = getTestContainer();
if (root == null) {
return null;
}
Shell shell = getShell();
IProject project = root.getProject();
final IScriptProject scriptProject = DLTKCore.create(project);
findSuiteBaseClasses(scriptProject);
if (PHP_UNIT_SUITE_BASE_CLASS_CACHE != null && PHP_UNIT_SUITE_BASE_CLASS_CACHE.length > 0) {
ElementSelectionDialog dialog = new ElementSelectionDialog(shell, PHP_UNIT_SUITE_BASE_CLASS_CACHE); // $NON-NLS-1$
dialog.setTitle(PHPUnitMessages.PHPUnitWizardPage_10);
dialog.setMessage(PHPUnitMessages.PHPUnitWizardPage_11);
if (dialog.open() == Window.OK) {
final Object[] resultArray = dialog.getResult();
if (resultArray != null && resultArray.length > 0) {
return (IType) resultArray[0];
}
}
}
return null;
}
/**
* This method finds all types that extend basic PHPUnit suite class,
* including abstract classes.
*
* @param scriptProject
*/
private void findSuiteBaseClasses(final IScriptProject scriptProject) {
if (PHP_UNIT_SUITE_BASE_CLASS_CACHE != null) {
return;
}
final PHPUnitSearchEngine searchEngine = new PHPUnitSearchEngine(scriptProject);
final List<IType> elementsList = new ArrayList<>();
try {
IWizardContainer container = getContainer();
if (getControl() != null) {
container.run(true, true, pm -> {
pm.beginTask(PHPUnitMessages.PHPUnitSearchEngine_Searching, IProgressMonitor.UNKNOWN);
List<IType> elements = searchEngine.findPHPUnitClassesByTestSuite(scriptProject, true, false,
new SubProgressMonitor(pm, IProgressMonitor.UNKNOWN));
if (pm.isCanceled()) {
return;
}
elementsList.addAll(elements);
pm.done();
});
if (!elementsList.isEmpty()) {
PHP_UNIT_SUITE_BASE_CLASS_CACHE = elementsList.toArray(new IType[elementsList.size()]);
}
}
} catch (InvocationTargetException | InterruptedException e) {
PHPUnitPlugin.log(e);
}
}
/**
* This method returns all non types that extend from PHPUnit test case or
* test suite, that may be used to build another test suite
*
* @param scriptProject
*/
private void findAllNonAbstractSuitesAndCases(final IScriptProject scriptProject) {
if (PHP_UNIT_CASE_AND_SUITE_NON_ABSTRAXT_CLASS_CACHE != null) {
return;
}
final PHPUnitSearchEngine searchEngine = new PHPUnitSearchEngine(scriptProject);
final List<IType> elementsList = new ArrayList<>();
try {
if (getContainer() != null && getContainer().getCurrentPage() != null) {
getContainer().run(true, true, pm -> {
pm.beginTask(PHPUnitMessages.PHPUnitSearchEngine_Searching, IProgressMonitor.UNKNOWN);
List<IType> elements = searchEngine.findAllTestCasesAndSuites(scriptProject, false,
new SubProgressMonitor(pm, IProgressMonitor.UNKNOWN));
if (pm.isCanceled()) {
return;
}
elementsList.addAll(elements);
pm.done();
});
if (!elementsList.isEmpty()) {
PHP_UNIT_CASE_AND_SUITE_NON_ABSTRAXT_CLASS_CACHE = (IType[]) elementsList
.toArray(new IType[elementsList.size()]);
}
}
} catch (InvocationTargetException | InterruptedException e) {
PHPUnitPlugin.log(e);
}
}
@Override
protected void containerChanged() {
super.containerChanged();
if (getTestContainer() != null) {
fClassNameProposal = getTestContainer() != null ? getTestContainer().getName() + testSuffix() : ""; //$NON-NLS-1$
if (!fClassNameManual && fClassNameDialogField != null) {
fClassNameDialogField.setText(fClassNameProposal);
}
classNameChanged();
elementsToTestChanged();
}
}
@Override
protected void createContainerControls(final Composite parent, final int nColumns) {
super.createContainerControls(parent, nColumns);
// set default and focus
final IContainer container = getTestContainer();
if (container != null) {
setClassName(container.getName() + testSuffix());
}
}
@Override
protected void createElementToTestControls(final Composite composite, final int columns) {
String[] addButtons = new String[] { "Add", null, "Remove" }; //$NON-NLS-1$ //$NON-NLS-2$
IListAdapter<IType> listAdapter = new IListAdapter<IType>() {
@Override
public void customButtonPressed(ListDialogField<IType> field, int index) {
}
@Override
public void doubleClicked(ListDialogField<IType> field) {
}
@Override
public void selectionChanged(ListDialogField<IType> field) {
}
};
fElementsToTestList = new ListDialogField<IType>(listAdapter, addButtons, new PHPTypeListLabelProvider()) {
// override these methods to validate interfaces
@Override
public void removeElement(IType element) throws IllegalArgumentException {
super.removeElement(element);
elementsToTestChanged();
}
@Override
public void removeElements(List<IType> elements) {
super.removeElements(elements);
elementsToTestChanged();
}
@Override
public void removeAllElements() {
super.removeAllElements();
elementsToTestChanged();
}
};
fElementsToTestList.setLabelText(PHPUnitMessages.TestSuiteWizardPage_2);
fElementsToTestList.setRemoveButtonIndex(2);
fElementsToTestList.removeAllElements();
final String INTERFACE = "interface"; //$NON-NLS-1$
Control[] controls = fElementsToTestList.doFillIntoGrid(composite, 3);
GridData gd = new GridData(GridData.FILL_BOTH);
gd.grabExcessHorizontalSpace = true;
controls[1].setLayoutData(gd);
final TableViewer tableViewer = fElementsToTestList.getTableViewer();
tableViewer.setColumnProperties(new String[] { INTERFACE });
gd = (GridData) fElementsToTestList.getListControl(null).getLayoutData();
gd.grabExcessVerticalSpace = true;
gd.widthHint = getMaxFieldWidth();
addInterfacesBtn = (Button) fElementsToTestList.getButtonBox(composite).getChildren()[0];
addInterfacesBtn.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
IType[] result = choosePHPUnitElementsToTest();
if (result != null && result.length > 0) {
fElementsToTestList.addElements(Arrays.asList(result));
fElementsToTestList.refresh();
elementsToTestChanged();
}
}
});
}
// Interfaces Selection Control
protected IType[] choosePHPUnitElementsToTest() {
IProject project = getTestContainer().getProject();
IScriptProject scriptProject = DLTKCore.create(project);
findAllNonAbstractSuitesAndCases(scriptProject);
if (PHP_UNIT_CASE_AND_SUITE_NON_ABSTRAXT_CLASS_CACHE != null
&& PHP_UNIT_CASE_AND_SUITE_NON_ABSTRAXT_CLASS_CACHE.length > 0) {
ElementSelectionDialog dialog = new ElementSelectionDialog(getShell(),
PHP_UNIT_CASE_AND_SUITE_NON_ABSTRAXT_CLASS_CACHE); // $NON-NLS-1$
dialog.setTitle(PHPUnitMessages.PHPUnitWizardPage_10);
dialog.setMessage(PHPUnitMessages.PHPUnitWizardPage_11);
dialog.setMultipleSelection(true);
if (dialog.open() == Window.OK) {
final Object[] resultArray = dialog.getResult();
if (resultArray != null && resultArray.length > 0) {
IType[] typeResult = new IType[resultArray.length];
for (int i = 0; i < resultArray.length; i++) {
IType type = (IType) resultArray[i];
typeResult[i] = type;
}
return typeResult;
}
}
}
return null;
}
@Override
protected String defaultSuperClass() {
return PHPUnitSearchEngine.CLASS_SUITE;
}
protected void elementsToTestChanged() {
final StatusInfo status = new StatusInfo();
if (fElementsToTestList != null) {
List<IType> addedElementsToTest = fElementsToTestList.getElements();
final IType[] addedElementsToTestArray = (IType[]) addedElementsToTest
.toArray(new IType[addedElementsToTest.size()]);
if (PHP_UNIT_CASE_AND_SUITE_NON_ABSTRAXT_CLASS_CACHE == null) {
if (getTestContainer() != null && getContainer().getCurrentPage() != null) {
IProject project = getTestContainer().getProject();
if (project != null) {
final IScriptProject scriptProject = DLTKCore.create(project);
findAllNonAbstractSuitesAndCases(scriptProject);
}
}
}
if (PHP_UNIT_CASE_AND_SUITE_NON_ABSTRAXT_CLASS_CACHE != null) {
List<IType> allTests = Arrays.asList(PHP_UNIT_CASE_AND_SUITE_NON_ABSTRAXT_CLASS_CACHE);
for (IType test : addedElementsToTestArray) {
if (!allTests.contains(test)) {
status.setError(
MessageFormat.format(PHPUnitMessages.PHPUnitValidator_Not_Test, test.getElementName()));
}
}
fElementToTestStatus = status;
updateStatus(getStatusList());
}
}
}
public IType[] getElementsToTest() {
List<IType> elements = fElementsToTestList.getElements();
return elements.toArray(new IType[elements.size()]);
}
@Override
public Object init(final IStructuredSelection selection) {
super.init(selection);
IModelElement element;
final IType superClass = null;
final List<IContainer> containers = new ArrayList<>(1);
IContainer container = null;
Object next;
for (final Iterator i = selection.iterator(); i.hasNext();) {
next = i.next();
if ((container = getInitialContainer(new StructuredSelection(next))) != null)
containers.add(container);
else if (superClass == null && (element = getInitialPHPElement(new StructuredSelection(next))) != null) {
final IResource res = element.getResource();
if (res != null) {
setContainer(res.getParent());
}
}
}
if (containers.size() > 1) {
for (final Iterator<IContainer> i = containers.iterator(); i.hasNext();) {
final IContainer iContainer = i.next();
// if a project, just select it:
if (iContainer.getType() == IResource.PROJECT) {
container = iContainer;
break;
}
// if a folder, first find the most rooted folder
if (container == null
|| iContainer.getFullPath().segmentCount() < container.getFullPath().segmentCount())
container = iContainer;
}
// now find the common root:
for (final Iterator<IContainer> i = containers.iterator(); i.hasNext();) {
// the most rooted folder has highest priority:
IContainer iContainer = (IContainer) i.next();
if (iContainer.getProject() != container.getProject())
continue;
// get higher to the level of the most rooted folder:
for (int j = 0; j < iContainer.getFullPath().segmentCount()
- container.getFullPath().segmentCount(); ++j)
iContainer = iContainer.getParent();
// if still not equals - get 1 level higher:
if (iContainer != container)
container = container.getParent();
}
setContainer(container);
} else if (!containers.isEmpty()) {
setContainer((IContainer) containers.get(0));
}
return null;
}
@Override
protected String testSuffix() {
return TEST_SUFFIX;
}
@Override
protected void invalidatCachedElements() {
PHP_UNIT_SUITE_BASE_CLASS_CACHE = null;
PHP_UNIT_CASE_AND_SUITE_NON_ABSTRAXT_CLASS_CACHE = null;
}
}