/*******************************************************************************
* Copyright (c) 2000, 2009 IBM Corporation 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:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.wst.jsdt.internal.ui.wizards.buildpaths;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.DirectoryDialog;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.model.WorkbenchContentProvider;
import org.eclipse.ui.model.WorkbenchLabelProvider;
import org.eclipse.wst.jsdt.core.IIncludePathEntry;
import org.eclipse.wst.jsdt.core.IJavaScriptProject;
import org.eclipse.wst.jsdt.core.JavaScriptCore;
import org.eclipse.wst.jsdt.internal.corext.util.Messages;
import org.eclipse.wst.jsdt.internal.ui.IJavaHelpContextIds;
import org.eclipse.wst.jsdt.internal.ui.JavaScriptPlugin;
import org.eclipse.wst.jsdt.internal.ui.dialogs.StatusInfo;
import org.eclipse.wst.jsdt.internal.ui.dialogs.StatusUtil;
import org.eclipse.wst.jsdt.internal.ui.util.PixelConverter;
import org.eclipse.wst.jsdt.internal.ui.wizards.IStatusChangeListener;
import org.eclipse.wst.jsdt.internal.ui.wizards.NewWizardMessages;
import org.eclipse.wst.jsdt.internal.ui.wizards.dialogfields.DialogField;
import org.eclipse.wst.jsdt.internal.ui.wizards.dialogfields.IDialogFieldListener;
import org.eclipse.wst.jsdt.internal.ui.wizards.dialogfields.IStringButtonAdapter;
import org.eclipse.wst.jsdt.internal.ui.wizards.dialogfields.LayoutUtil;
import org.eclipse.wst.jsdt.internal.ui.wizards.dialogfields.SelectionButtonDialogField;
import org.eclipse.wst.jsdt.internal.ui.wizards.dialogfields.StringButtonDialogField;
/**
* UI to set the source attachment archive and root.
* Same implementation for both setting attachments for libraries from
* variable entries and for normal (internal or external) jar.
*/
public class SourceAttachmentBlock {
private IStatusChangeListener fContext;
private StringButtonDialogField fFileNameField;
private SelectionButtonDialogField fWorkspaceButton;
private SelectionButtonDialogField fExternalFolderButton;
private IStatus fNameStatus;
/**
* The path to which the archive variable points.
* Null if invalid path or not resolvable. Must not exist.
*/
private IPath fFileVariablePath;
private IWorkspaceRoot fWorkspaceRoot;
private Control fSWTWidget;
private Label fFullPathResolvedLabel;
private IJavaScriptProject fProject;
private IIncludePathEntry fEntry;
private IPath fContainerPath;
/**
* @param context listeners for status updates
* @param entry The entry to edit
*/
public SourceAttachmentBlock(IStatusChangeListener context, IIncludePathEntry entry) {
Assert.isNotNull(entry);
fContext= context;
fEntry= entry;
int kind= entry.getEntryKind();
Assert.isTrue(kind == IIncludePathEntry.CPE_LIBRARY || kind == IIncludePathEntry.CPE_VARIABLE);
fWorkspaceRoot= ResourcesPlugin.getWorkspace().getRoot();
fNameStatus= new StatusInfo();
SourceAttachmentAdapter adapter= new SourceAttachmentAdapter();
fFileNameField= new StringButtonDialogField(adapter);
fFileNameField.setDialogFieldListener(adapter);
fFileNameField.setLabelText(NewWizardMessages.SourceAttachmentBlock_filename_label);
fFileNameField.setButtonLabel(NewWizardMessages.SourceAttachmentBlock_filename_externalfile_button);
fWorkspaceButton= new SelectionButtonDialogField(SWT.PUSH);
fWorkspaceButton.setDialogFieldListener(adapter);
fWorkspaceButton.setLabelText(NewWizardMessages.SourceAttachmentBlock_filename_internal_button);
fExternalFolderButton= new SelectionButtonDialogField(SWT.PUSH);
fExternalFolderButton.setDialogFieldListener(adapter);
fExternalFolderButton.setLabelText(NewWizardMessages.SourceAttachmentBlock_filename_externalfolder_button);
// set the old settings
setDefaults();
}
/**
* @deprecated Use API {@link org.eclipse.wst.jsdt.ui.wizards.BuildPathDialogAccess#configureSourceAttachment(Shell, IIncludePathEntry)}
*/
public SourceAttachmentBlock(IStatusChangeListener context, IIncludePathEntry entry, IPath containerPath, IJavaScriptProject project) {
this(context, entry);
fContainerPath= containerPath;
fProject= project;
}
public void setDefaults() {
if (fEntry.getSourceAttachmentPath() != null) {
fFileNameField.setText(fEntry.getSourceAttachmentPath().toString());
} else {
fFileNameField.setText(""); //$NON-NLS-1$
}
}
private boolean isVariableEntry() {
return fEntry.getEntryKind() == IIncludePathEntry.CPE_VARIABLE;
}
/**
* Gets the source attachment path chosen by the user
*/
public IPath getSourceAttachmentPath() {
if (fFileNameField.getText().length() == 0) {
return null;
}
return getFilePath();
}
/**
* Gets the source attachment root chosen by the user
* Returns null to let JCore automatically detect the root.
*/
public IPath getSourceAttachmentRootPath() {
return null;
}
public IIncludePathEntry getNewEntry() {
CPListElement elem= CPListElement.createFromExisting(fEntry, fProject);
return elem.getClasspathEntry();
}
/**
* Creates the control
*/
public Control createControl(Composite parent) {
PixelConverter converter= new PixelConverter(parent);
fSWTWidget= parent;
Composite composite= new Composite(parent, SWT.NONE);
GridLayout layout= new GridLayout();
layout.marginHeight= 0;
layout.marginWidth= 0;
layout.numColumns= 4;
composite.setLayout(layout);
if (isVariableEntry()) {
int widthHint= converter.convertWidthInCharsToPixels(40);
int labelWidthHint= widthHint * 2;
Label message= new Label(composite, SWT.WRAP);
GridData gd= new GridData(GridData.FILL, GridData.BEGINNING, false, false, 4, 1);
message.setLayoutData(gd);
message.setText(Messages.format(NewWizardMessages.SourceAttachmentBlock_message, fEntry.getPath().lastSegment()));
//DialogField.createEmptySpace(composite, 1);
Label desc= new Label(composite, SWT.WRAP);
gd= new GridData(GridData.FILL, GridData.BEGINNING, false, false, 4, 1);
gd.widthHint= labelWidthHint;
desc.setLayoutData(gd);
desc.setText(NewWizardMessages.SourceAttachmentBlock_filename_description);
fFileNameField.doFillIntoGrid(composite, 4);
LayoutUtil.setWidthHint(fFileNameField.getTextControl(null), widthHint);
// label that shows the resolved path for variable jars
//DialogField.createEmptySpace(composite, 1);
fFullPathResolvedLabel= new Label(composite, SWT.WRAP);
fFullPathResolvedLabel.setText(getResolvedLabelString());
gd= new GridData(GridData.FILL, GridData.BEGINNING, false, false, 4, 1);
gd.widthHint= labelWidthHint;
fFullPathResolvedLabel.setLayoutData(gd);
LayoutUtil.setHorizontalGrabbing(fFileNameField.getTextControl(null));
} else {
int widthHint= converter.convertWidthInCharsToPixels(60);
GridData gd= new GridData(GridData.FILL, GridData.BEGINNING, false, false, 3, 1);
gd.widthHint= converter.convertWidthInCharsToPixels(50);
Label message= new Label(composite, SWT.LEFT + SWT.WRAP);
message.setLayoutData(gd);
message.setText(Messages.format(NewWizardMessages.SourceAttachmentBlock_message, fEntry.getPath().lastSegment()));
fWorkspaceButton.doFillIntoGrid(composite, 1);
((GridData) fWorkspaceButton.getSelectionButton(null).getLayoutData()).verticalAlignment= SWT.END;
// archive name field
fFileNameField.doFillIntoGrid(composite, 4);
LayoutUtil.setWidthHint(fFileNameField.getTextControl(null), widthHint);
LayoutUtil.setHorizontalGrabbing(fFileNameField.getTextControl(null));
// Additional 'browse workspace' button for normal jars
DialogField.createEmptySpace(composite, 3);
fExternalFolderButton.doFillIntoGrid(composite, 1);
}
fFileNameField.postSetFocusOnDialogField(parent.getDisplay());
Dialog.applyDialogFont(composite);
PlatformUI.getWorkbench().getHelpSystem().setHelp(composite, IJavaHelpContextIds.SOURCE_ATTACHMENT_BLOCK);
return composite;
}
private class SourceAttachmentAdapter implements IStringButtonAdapter, IDialogFieldListener {
// -------- IStringButtonAdapter --------
public void changeControlPressed(DialogField field) {
attachmentChangeControlPressed(field);
}
// ---------- IDialogFieldListener --------
public void dialogFieldChanged(DialogField field) {
attachmentDialogFieldChanged(field);
}
}
private void attachmentChangeControlPressed(DialogField field) {
if (field == fFileNameField) {
IPath jarFilePath= isVariableEntry() ? chooseExtension() : chooseExtJarFile();
if (jarFilePath != null) {
fFileNameField.setText(jarFilePath.toString());
}
}
}
// ---------- IDialogFieldListener --------
private void attachmentDialogFieldChanged(DialogField field) {
if (field == fFileNameField) {
fNameStatus= updateFileNameStatus();
} else if (field == fWorkspaceButton) {
IPath jarFilePath= chooseInternal();
if (jarFilePath != null) {
fFileNameField.setText(jarFilePath.toString());
}
return;
} else if (field == fExternalFolderButton) {
IPath folderPath= chooseExtFolder();
if (folderPath != null) {
fFileNameField.setText(folderPath.toString());
}
return;
}
doStatusLineUpdate();
}
private void doStatusLineUpdate() {
fFileNameField.enableButton(canBrowseFileName());
// set the resolved path for variable jars
if (fFullPathResolvedLabel != null) {
fFullPathResolvedLabel.setText(getResolvedLabelString());
}
IStatus status= StatusUtil.getMostSevere(new IStatus[] { fNameStatus });
fContext.statusChanged(status);
}
private boolean canBrowseFileName() {
if (!isVariableEntry()) {
return true;
}
// to browse with a variable JAR, the variable name must point to a directory
if (fFileVariablePath != null) {
return fFileVariablePath.toFile().isDirectory();
}
return false;
}
private String getResolvedLabelString() {
IPath resolvedPath= getResolvedPath(getFilePath());
if (resolvedPath != null) {
return resolvedPath.toOSString();
}
return ""; //$NON-NLS-1$
}
private IPath getResolvedPath(IPath path) {
if (path != null) {
String varName= path.segment(0);
if (varName != null) {
IPath varPath= JavaScriptCore.getIncludepathVariable(varName);
if (varPath != null) {
return varPath.append(path.removeFirstSegments(1));
}
}
}
return null;
}
private IStatus updateFileNameStatus() {
StatusInfo status= new StatusInfo();
fFileVariablePath= null;
String fileName= fFileNameField.getText();
if (fileName.length() == 0) {
// no source attachment
return status;
} else {
if (!Path.EMPTY.isValidPath(fileName)) {
status.setError(NewWizardMessages.SourceAttachmentBlock_filename_error_notvalid);
return status;
}
IPath filePath= Path.fromOSString(fileName);
IPath resolvedPath;
if (isVariableEntry()) {
if (filePath.getDevice() != null) {
status.setError(NewWizardMessages.SourceAttachmentBlock_filename_error_deviceinpath);
return status;
}
String varName= filePath.segment(0);
if (varName == null) {
status.setError(NewWizardMessages.SourceAttachmentBlock_filename_error_notvalid);
return status;
}
fFileVariablePath= JavaScriptCore.getIncludepathVariable(varName);
if (fFileVariablePath == null) {
status.setError(NewWizardMessages.SourceAttachmentBlock_filename_error_varnotexists);
return status;
}
resolvedPath= fFileVariablePath.append(filePath.removeFirstSegments(1));
if (resolvedPath.isEmpty()) {
status.setWarning(NewWizardMessages.SourceAttachmentBlock_filename_warning_varempty);
return status;
}
File file= resolvedPath.toFile();
if (!file.exists()) {
String message= Messages.format(NewWizardMessages.SourceAttachmentBlock_filename_error_filenotexists, resolvedPath.toOSString());
status.setWarning(message);
return status;
}
if (!resolvedPath.isAbsolute()) {
String message= Messages.format(NewWizardMessages.SourceAttachmentBlock_filename_error_notabsolute, filePath.toString());
status.setError(message);
return status;
}
String deprecationMessage= BuildPathSupport.getDeprecationMessage(varName);
if (deprecationMessage != null) {
status.setWarning(deprecationMessage);
return status;
}
} else {
// JDT/Core only supports source attachments in archives on the
// local file system. So using getLocation is save here.
File file= filePath.toFile();
IResource res= fWorkspaceRoot.findMember(filePath);
if (res != null && res.getLocation() != null) {
file= res.getLocation().toFile();
}
if (!file.exists()) {
String message= Messages.format(NewWizardMessages.SourceAttachmentBlock_filename_error_filenotexists, filePath.toString());
status.setError(message);
return status;
}
if (res == null) {
if (!filePath.isAbsolute()) {
String message= Messages.format(NewWizardMessages.SourceAttachmentBlock_filename_error_notabsolute, filePath.toString());
status.setError(message);
return status;
}
}
}
}
return status;
}
private IPath getFilePath() {
return Path.fromOSString(fFileNameField.getText()).makeAbsolute();
}
private IPath chooseExtension() {
IPath currPath= getFilePath();
if (currPath.segmentCount() == 0) {
currPath= fEntry.getPath();
}
IPath resolvedPath= getResolvedPath(currPath);
File initialSelection= resolvedPath != null ? resolvedPath.toFile() : null;
String currVariable= currPath.segment(0);
JARFileSelectionDialog dialog= new JARFileSelectionDialog(getShell(), false, true);
dialog.setTitle(NewWizardMessages.SourceAttachmentBlock_extvardialog_title);
dialog.setMessage(NewWizardMessages.SourceAttachmentBlock_extvardialog_description);
dialog.setInput(fFileVariablePath.toFile());
dialog.setInitialSelection(initialSelection);
if (dialog.open() == Window.OK) {
File result= (File) dialog.getResult()[0];
IPath returnPath= Path.fromOSString(result.getPath()).makeAbsolute();
return modifyPath(returnPath, currVariable);
}
return null;
}
/*
* Opens a dialog to choose a jar from the file system.
*/
private IPath chooseExtJarFile() {
IPath currPath= getFilePath();
if (currPath.segmentCount() == 0) {
currPath= fEntry.getPath();
}
if (ArchiveFileFilter.isArchivePath(currPath)) {
currPath= currPath.removeLastSegments(1);
}
FileDialog dialog= new FileDialog(getShell());
dialog.setText(NewWizardMessages.SourceAttachmentBlock_extjardialog_text);
dialog.setFilterExtensions(new String[] {"*.jar;*.zip;*.js"}); //$NON-NLS-1$
dialog.setFilterPath(currPath.toOSString());
String res= dialog.open();
if (res != null) {
return Path.fromOSString(res).makeAbsolute();
}
return null;
}
private IPath chooseExtFolder() {
IPath currPath= getFilePath();
if (currPath.segmentCount() == 0) {
currPath= fEntry.getPath();
}
if (ArchiveFileFilter.isArchivePath(currPath)) {
currPath= currPath.removeLastSegments(1);
}
DirectoryDialog dialog= new DirectoryDialog(getShell());
dialog.setMessage(NewWizardMessages.SourceAttachmentBlock_extfolderdialog_message);
dialog.setText(NewWizardMessages.SourceAttachmentBlock_extfolderdialog_text);
dialog.setFilterPath(currPath.toOSString());
String res= dialog.open();
if (res != null) {
return Path.fromOSString(res).makeAbsolute();
}
return null;
}
/*
* Opens a dialog to choose an internal jar.
*/
private IPath chooseInternal() {
String initSelection= fFileNameField.getText();
ViewerFilter filter= new ArchiveFileFilter((List) null, false);
ILabelProvider lp= new WorkbenchLabelProvider();
ITreeContentProvider cp= new WorkbenchContentProvider();
IResource initSel= null;
if (initSelection.length() > 0) {
initSel= fWorkspaceRoot.findMember(new Path(initSelection));
}
if (initSel == null) {
initSel= fWorkspaceRoot.findMember(fEntry.getPath());
}
FolderSelectionDialog dialog= new FolderSelectionDialog(getShell(), lp, cp);
dialog.setAllowMultiple(false);
dialog.addFilter(filter);
dialog.setTitle(NewWizardMessages.SourceAttachmentBlock_intjardialog_title);
dialog.setMessage(NewWizardMessages.SourceAttachmentBlock_intjardialog_message);
dialog.setInput(fWorkspaceRoot);
dialog.setInitialSelection(initSel);
if (dialog.open() == Window.OK) {
IResource res= (IResource) dialog.getFirstResult();
return res.getFullPath();
}
return null;
}
private Shell getShell() {
if (fSWTWidget != null) {
return fSWTWidget.getShell();
}
return JavaScriptPlugin.getActiveWorkbenchShell();
}
/**
* Takes a path and replaces the beginning with a variable name
* (if the beginning matches with the variables value)
*/
private IPath modifyPath(IPath path, String varName) {
if (varName == null || path == null) {
return null;
}
if (path.isEmpty()) {
return new Path(varName);
}
IPath varPath= JavaScriptCore.getIncludepathVariable(varName);
if (varPath != null) {
if (varPath.isPrefixOf(path)) {
path= path.removeFirstSegments(varPath.segmentCount());
} else {
path= new Path(path.lastSegment());
}
} else {
path= new Path(path.lastSegment());
}
return new Path(varName).append(path);
}
/**
* Creates a runnable that sets the source attachment by modifying the project's classpath.
*/
public static IRunnableWithProgress getRunnable(final Shell shell, final IIncludePathEntry newEntry, final IJavaScriptProject jproject, final IPath containerPath) {
return new IRunnableWithProgress() {
public void run(IProgressMonitor monitor) throws InvocationTargetException {
try {
String[] changedAttributes= {};
BuildPathSupport.modifyClasspathEntry(shell, newEntry, changedAttributes, jproject, containerPath, monitor);
} catch (CoreException e) {
throw new InvocationTargetException(e);
}
}
};
}
/**
* @deprecated Use {@link #getRunnable(Shell, IIncludePathEntry, IJavaScriptProject, IPath)}
*/
public IRunnableWithProgress getRunnable(final Shell shell) {
return getRunnable(shell, getNewEntry(), fProject, fContainerPath);
}
}