/*******************************************************************************
* 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.internal.ui.wizards.types;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.dltk.core.*;
import org.eclipse.dltk.core.index2.search.ISearchEngine.MatchRule;
import org.eclipse.dltk.core.search.IDLTKSearchConstants;
import org.eclipse.dltk.core.search.IDLTKSearchScope;
import org.eclipse.dltk.core.search.SearchEngine;
import org.eclipse.dltk.ui.dialogs.FilteredTypesSelectionDialog;
import org.eclipse.dltk.ui.dialogs.ITypeInfoFilterExtension;
import org.eclipse.dltk.ui.dialogs.TypeSelectionExtension;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.window.Window;
import org.eclipse.osgi.util.NLS;
import org.eclipse.php.core.PHPToolkitUtil;
import org.eclipse.php.core.PHPVersion;
import org.eclipse.php.core.compiler.PHPFlags;
import org.eclipse.php.internal.core.PHPLanguageToolkit;
import org.eclipse.php.internal.core.model.PHPModelAccess;
import org.eclipse.php.internal.ui.preferences.includepath.IncludePathUtils;
import org.eclipse.php.internal.ui.util.SWTUtil;
import org.eclipse.php.internal.ui.util.StatusInfo;
import org.eclipse.php.internal.ui.util.StatusUtil;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.*;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.FilteredItemsSelectionDialog;
public class NewPHPClassPage extends NewPHPTypePage {
protected Text superClassName;
private Label superPath;
private IType superClassData;
private Button browseSuperBtn;
private static final String[] CLASS_CHECKBOXES_PHP5 = new String[] { REQUIRE_ONCE, CONSTRUCTOR, PHP_DOC_BLOCKS,
null, DESTRUCTOR, TODOS, null, INHERITED_ABSTRACT_METHODS };
public static final String[] CLASS_MODIFIERS = new String[] { "none", //$NON-NLS-1$
"final", "abstract" }; //$NON-NLS-1$ //$NON-NLS-2$
public static final int VALIDATE_SUPER_CLASS = 5;
protected StatusInfo superClassStatus;
public NewPHPClassPage() {
super(Messages.NewPHPClassPage_3);
fTypeKind = CLASS_TYPE;
setMessage(Messages.NewPHPClassPage_4);
setDescription(Messages.NewPHPClassPage_4);
setTitle(Messages.NewPHPClassPage_6);
superClassStatus = new StatusInfo();
interfacesStatus = new StatusInfo();
}
@Override
public void createControl(Composite parent) {
initializeDialogUnits(parent);
super.createControl(parent);
final Composite composite = new Composite(parent, SWT.NONE);
GridLayout layout = new GridLayout(1, false);
composite.setLayout(layout);
// the location section will be generic all new elements types (classes,
// interfaces etc.)
// that's why it is in PHPTypePage
createLocationSection(composite);
// create this element's section
createElementSection(composite);
setControl(composite);
initValues();
}
@Override
protected void initValues() {
super.initValues();
if (superClassData != null) {
superClassName.setText(superClassData.getElementName());
}
}
@Override
protected void validatePageValues(int validationCode) {
super.validatePageValues(validationCode);
IProject currentProject = getCurrentProject();
if (currentProject != null) {
IScriptProject model = DLTKCore.create(currentProject);
if (superClassName != null && (superClassName.getText().length()) > 0 && (model != null)) {
validateSuperClass(getSuperclassName());
}
}
}
private void createElementSection(final Composite composite) {
// the element section is specific to an interface OR a class
final Composite elementSection = new Composite(composite, SWT.NONE);
GridLayout layout = new GridLayout(3, false);
elementSection.setLayout(layout);
GridData gd = new GridData(SWT.FILL, SWT.BEGINNING, true, false);
elementSection.setLayoutData(gd);
addElementNameText(elementSection, Messages.NewPHPClassPage_7);
if (phpVersion != PHPVersion.PHP5) {
addNamespaceText(elementSection);
}
addElementModifiers(elementSection, CLASS_MODIFIERS);
addElementSuperClass(elementSection);
createSuperInterfacesControls(elementSection);
if (phpVersion != null && phpVersion.isGreaterThan(PHPVersion.PHP5_3)) {
createTraitsControls(elementSection);
}
addCheckboxesCreation(elementSection, CLASS_CHECKBOXES_PHP5);
}
// add the UI section that handles the Superclass selection
private void addElementSuperClass(Composite elementSection) {
GridData gd = new GridData();
Label superclassLabel = new Label(elementSection, SWT.NULL);
superclassLabel.setText(Messages.NewPHPClassPage_8);
superclassLabel.setLayoutData(gd);
superClassName = new Text(elementSection, SWT.BORDER | SWT.SINGLE);
gd = new GridData(GridData.FILL_HORIZONTAL);
superClassName.setLayoutData(gd);
superClassName.addModifyListener(e -> {
final IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
String projectName = getProjectName(getSourceText());
if (projectName != null && projectName.length() > 0) {
IProject currentProject = workspaceRoot.getProject(projectName);
IScriptProject model = DLTKCore.create(currentProject);
String superClassName = ((Text) e.getSource()).getText().trim();
if (model != null) {
validateSuperClass(superClassName);
}
}
});
browseSuperBtn = new Button(elementSection, SWT.PUSH);
browseSuperBtn.setText(Messages.NewPHPClassPage_9);
gd = new GridData();
gd.widthHint = SWTUtil.getButtonWidthHint(browseSuperBtn);
browseSuperBtn.setLayoutData(gd);
browseSuperBtn.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
IType result = chooseSuperClass();
superClassData = result;
if (result != null) {
superClassName.setText(result.getElementName());
}
changeButtonEnableStatus();
}
});
// // use this dummy to take care of layout
new Label(elementSection, SWT.NULL);
superPath = new Label(elementSection, SWT.NULL);
gd = new GridData(GridData.FILL_HORIZONTAL);
superPath.setLayoutData(gd);
new Label(elementSection, SWT.NULL);
}
/**
* @see NewPHPTypePage.java
*/
@Override
protected void sourceFolderChanged() {
super.sourceFolderChanged();
if (browseSuperBtn == null) {
return;
}
if (sourceFolderStatus.getCode() != IStatus.OK) {
browseSuperBtn.setEnabled(false);
} else {
browseSuperBtn.setEnabled(true);
}
String sourcePath = getSourceText();
Path projPath = new Path(sourcePath);
if (projPath.segmentCount() != 1) {
return;
}
String projectPath = projPath.segment(1);
if (projectPath == null || projectPath.length() == 0) {
return;
}
IScriptProject model = getProject();
// check that superclass exists in model
String superclassName = getSuperclassName();
if (superclassName.length() > 0) {
validateSuperClass(superclassName);
}
// check that interfaces exist in model
if (getInterfaces().size() > 0) {
validateInterfaces(model);
}
if (getTraits().size() > 0) {
validateTraits(model);
}
}
private void validateSuperClass(final String superclassName) {
superPath.setText(""); //$NON-NLS-1$
superClassStatus = new StatusInfo();
if (superclassName.length() == 0) {
superClassData = null;
changeButtonEnableStatus();
updateStatus(findMostSevereStatus());
return;
}
superClassData = null;
IDLTKSearchScope scope = SearchEngine.createSearchScope(getProject());
IType[] types = PHPModelAccess.getDefault().findTypes(superclassName, MatchRule.EXACT, 0, 0, scope,
new NullProgressMonitor());
String error = null;
for (IType type : types) {
int modifiers = 0;
try {
modifiers = type.getFlags();
} catch (ModelException e) {
}
if (PHPFlags.isInterface(modifiers)) {
error = NLS.bind("Not a class: ''{0}''", superclassName); //$NON-NLS-1$
} else if (PHPFlags.isFinal(modifiers)) {
error = NLS.bind("Class ''{0}'' is final", superclassName); //$NON-NLS-1$
} else {
superClassData = type;
IPath relativeLocation = IncludePathUtils.getRelativeLocationFromIncludePath(type.getScriptProject(),
type);
superPath.setText(relativeLocation.toString());
}
}
if (superClassData == null) {
superPath.setText(""); //$NON-NLS-1$
if (error != null) {
superClassStatus.setError(error);
} else {
superClassStatus.setError(NLS.bind("Cannot find: ''{0}''", //$NON-NLS-1$
superclassName));
}
}
updateStatus(findMostSevereStatus());
changeButtonEnableStatus();
}
/**
* Returns the Name object of the superclass for the PHP element
*
* @return
*/
public String getSuperclassName() {
return superClassName.getText().trim();
}
/**
* Creates a UI component withing this Wizard page for selecting the
* Superclass
*
* @return the selected super class
*/
protected IType chooseSuperClass() {
final IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
IProject project = workspaceRoot.getProject(getProjectName(sourceText.getText()));
FilteredTypesSelectionDialog dialog = new FilteredTypesSelectionDialog(getShell(), false,
PlatformUI.getWorkbench().getProgressService(),
SearchEngine.createSearchScope(DLTKCore.create(project)), IDLTKSearchConstants.TYPE,
new TypeSelectionExtension() {
@Override
public ITypeInfoFilterExtension getFilterExtension() {
return typeInfoRequestor -> {
int modifiers = typeInfoRequestor.getModifiers();
if (!PHPFlags.isFinal(modifiers) && !PHPFlags.isInterface(modifiers)
&& !PHPFlags.isNamespace(modifiers) && !PHPFlags.isTrait(modifiers)) {
return true;
}
return false;
};
}
}, PHPLanguageToolkit.getDefault());
dialog.setListLabelProvider(new PHPFullPathLabelProvider());
dialog.setListSelectionLabelDecorator(new PHPFullPathLabelProvider());
dialog.setDetailsLabelProvider(new StatusLineLabelProvider());
dialog.setTitle(Messages.NewPHPClassPage_0);
dialog.setMessage(Messages.NewPHPClassPage_15);
dialog.setInitialPattern("", //$NON-NLS-1$
FilteredItemsSelectionDialog.FULL_SELECTION);
if (dialog.open() == Window.OK) {
Object[] resultArray = dialog.getResult();
if ((resultArray != null) && (resultArray.length > 0)) {
return (IType) resultArray[0];
}
}
return null;
}
/**
* Returns true/false - whether the requested modifer's check box is
* selected
*
* @param checkBoxIndex
* - the index of the checkbox
* @return
*/
public boolean isCreateModifierChecked(int checkBoxIndex) {
boolean result = false;
if (modifiers != null) {
Control[] btns = modifiers.getChildren();
if (btns.length > checkBoxIndex) {
Button btn = (Button) btns[checkBoxIndex];
result = btn.getSelection();
}
}
return result;
}
public IType getSuperClassData() {
return superClassData;
}
/**
* Finds the most severe error (if there is one)
*/
@Override
protected IStatus findMostSevereStatus() {
return StatusUtil.getMostSevere(new IStatus[] { elementNameStatus, sourceFolderStatus, newFileStatus,
existingFileStatus, superClassStatus, interfacesStatus, namespaceStatus });
}
@Override
protected IModelElement getInitialPHPElement(IStructuredSelection selection) {
IModelElement codeData = super.getInitialPHPElement(selection);
// fixed bug 14462 - in case of class insert the class as super class
if (codeData != null && PHPToolkitUtil.isPHPElement(codeData)) {
int type = codeData.getElementType();
if (type == IModelElement.TYPE && !PHPFlags.isInterface(type) && !PHPFlags.isNamespace(type)) {
superClassData = (IType) codeData;
}
}
return codeData;
}
@Override
protected void changeButtonEnableStatus() {
super.changeButtonEnableStatus();
Button button = getButton(NewPHPClassPage.INHERITED_ABSTRACT_METHODS);
if (button != null) {
button.setEnabled(!fSuperInterfacesDialogField.getElements().isEmpty() || superClassData != null);
}
}
@Override
protected boolean requireOnceShouldEnabled() {
return super.requireOnceShouldEnabled() || superClassData != null;
}
}