/*******************************************************************************
* Copyright (c) 2009, 2010 Progress Software Corporation.
* 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
******************************************************************************/
package org.fusesource.tools.core.ui.url.urlchooser;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.IPath;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.window.Window;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.ui.INewWizard;
import org.eclipse.ui.IWorkbenchWizard;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.ISelectionValidator;
import org.eclipse.ui.wizards.IWizardCategory;
import org.eclipse.ui.wizards.IWizardDescriptor;
import org.fusesource.tools.core.ui.url.urlchooser.filesystemchooser.FileSystemProvider;
import org.fusesource.tools.core.ui.url.urlchooser.filesystemchooser.LocalDirectorySystemProvider;
import org.fusesource.tools.core.ui.url.urlchooser.filesystemchooser.LocalFileSystemProvider;
import org.fusesource.tools.core.ui.url.urlchooser.workspacechooser.ResourceChooserProvider;
import org.fusesource.tools.core.ui.url.urlchooser.workspacechooser.WorkspaceChooserProvider;
import org.fusesource.tools.core.util.ResourceUtil;
/**
*/
public class URLChooser extends AbstractChooser {
private static List newWizardCategories = new ArrayList();
private final Map fileSystemProviders = Collections.synchronizedMap(new HashMap());
/**
* Style Constant to show History
*/
public static final int HIDE_HISTORY = 16;
private URLChooserFilter filter;
private final List listeners = Collections.synchronizedList(new ArrayList(3));
private MenuItem recent;
private Menu recentMenu;
private final List recentFiles = new LinkedList();
private static final int RECENT_FILES_COUNT = 10;
protected static final String DEFAULT_HISTORY_CONTEXT = "default.context";
protected String historyContext = null;
private final DefaultURLChooserPrefHandler urlChooserPrefHandler = DefaultURLChooserPrefHandler.getInstance();
static {
newWizardCategories = new ArrayList();
newWizardCategories.add("org.fusesource.tools.newWizard");
}
protected URLChooser() {
}
public URLChooser(Composite parent) {
this(parent, Collections.EMPTY_LIST, Collections.EMPTY_LIST, STYLE_NONE);
}
public URLChooser(Composite parent, int style) {
this(parent, style, DEFAULT_HISTORY_CONTEXT);
}
public URLChooser(Composite parent, int style, String context) {
this(parent, Collections.EMPTY_LIST, Collections.EMPTY_LIST, style, context);
}
public URLChooser(Composite parent, List supportedProviderIds, int style) {
this(parent, supportedProviderIds, Collections.EMPTY_LIST, style);
}
public URLChooser(Composite parent, List supportedProviderIds, int style, String context) {
this(parent, supportedProviderIds, Collections.EMPTY_LIST, style, context);
}
public URLChooser(Composite parent, List supportedProviderIds, String context) {
this(parent, supportedProviderIds, Collections.EMPTY_LIST, STYLE_NONE, context);
}
/**
* Provide a list of provider ids that is supported by this instance of the FileChooser. This
* method may not be called again.
*
* @param supportedProviderIds
* MAY BE NULL
*/
public URLChooser(Composite parent, List supportedProviderIds, List tempFSProviders, int style) {
this(parent, supportedProviderIds, tempFSProviders, null, style, DEFAULT_HISTORY_CONTEXT);
}
public URLChooser(Composite parent, List supportedProviderIds, List tempFSProviders, int style, String context) {
this(parent, supportedProviderIds, tempFSProviders, null, style, context);
}
/**
* Provide a list of provider ids that is supported by this instance of the FileChooser. This
* method may not be called again.
*
* @param supportedProviderIds
* MAY BE NULL
* @param tempFSProviders
* a list or temporary (just for this instance) FS Providers
* @param listener
* a quick way to specify a listener
*/
public URLChooser(Composite parent, List supportedProviderIds, List tempFSProviders,
URLChooserChangeListener listener, int style) {
this(parent, tempFSProviders, supportedProviderIds, listener, style, DEFAULT_HISTORY_CONTEXT);
}
public URLChooser(Composite parent, List supportedProviderIds, List tempFSProviders,
URLChooserChangeListener listener, int style, String context) {
if (context == null) {
historyContext = DEFAULT_HISTORY_CONTEXT;
} else {
historyContext = context;
}
loadFileSystemProviders();
init(parent, tempFSProviders, supportedProviderIds, listener, style);
}
protected void init(Composite parent, List tempFSProviders, List supportedProviderIds,
URLChooserChangeListener listener, int style) {
super.init(parent, tempFSProviders, supportedProviderIds, null, style);
if (listener != null) {
addURLChangeListener(listener);
}
}
@Override
protected void openSelectedValue() {
openInEditor();
}
protected void openInEditor() {
URL url = getSelectedValueAsURL();
if (url != null) {
for (Iterator iterator = actionProviders.iterator(); iterator.hasNext();) {
FileSystemProvider provider = (FileSystemProvider) iterator.next();
if (provider.validate(url)) {
provider.open(url);
}
}
}
}
public URL[] acceptDrop(DropTargetEvent data) {
Object[] objects = acceptObjectDrop(data);
return toUrlArray(objects);
}
private URL[] toUrlArray(Object[] objects) {
if (objects == null) {
return null;
}
List retVal = new ArrayList();
for (Object object : objects) {
if (object instanceof URL) {
retVal.add(object);
}
}
return (URL[]) retVal.toArray(new URL[retVal.size()]);
}
@Override
protected Collection getProvidersList(List providersList) {
if (providersList.size() == 0) {
return fileSystemProviders.values();
}
List retList = new ArrayList(providersList.size());
for (Iterator iterator = providersList.iterator(); iterator.hasNext();) {
String id = iterator.next().toString();
Object o = fileSystemProviders.get(id);
if (o != null) {
retList.add(o);
}
}
return retList;
}
public void setPathAsRelative() {
showRelativeFilePath = true;
}
public URL[] getSelectedValuesAsURL() {
if (selectedValues == null || selectedValues.length == 0) {
return null;
}
return stringArrayToURLArray(selectedValues);
}
public String getSelectedValue() {
if (selectedValues == null || selectedValues.length == 0) {
return null;
}
return selectedValues[0].toString();
}
public String[] getSelectedValues() {
return toStringArray(selectedValues);
}
public URL getSelectedValueAsURL() {
String value = getSelectedValue();
if (value == null) {
return null;
}
try {
return new URL(value);
} catch (Exception e) {
}
return null;
}
public void setSelectedValue(URL value) {
setSelectedValue(value == null ? (String) null : value.toString());
}
public void setSelectedValues(String[] values) {
// Only update if there's a change
if (Arrays.equals(selectedValues, values)) {
return;
}
selectedValues = new String[(values != null) ? values.length : 0];
if (selectedValues.length > 0) {
// Check because arraycopy will throw a NPE if 'values' is null even
// if the copy length is ZERO :(
System.arraycopy(values, 0, selectedValues, 0, selectedValues.length);
}
updateSelectedValues(selectedValues);
}
public void setSelectedValues(URL[] values) {
setSelectedValues(urlArrayToStringArray(values));
}
public void addURLChangeListener(URLChooserChangeListener l) {
if (l != null && !listeners.contains(l)) {
listeners.add(l);
}
}
public void removeURLChangeListener(URLChooserChangeListener l) {
listeners.remove(l);
}
@Override
protected boolean isSelectedValueValid() {
return (getSelectedValueAsURL() != null);
}
public static void addNewWizardCategory(String category) {
newWizardCategories.add(category);
}
public static void removeNewWizardCategory(String category) {
newWizardCategories.remove(category);
}
@Override
protected void addNewMenuItems(Menu newMenu) {
for (Iterator iter = newWizardCategories.iterator(); iter.hasNext();) {
String categoryName = (String) iter.next();
IWizardCategory category = PlatformUI.getWorkbench().getNewWizardRegistry().findCategory(categoryName);
if (category == null) {
continue;
}
IWizardDescriptor[] W = category.getWizards();
for (IWizardDescriptor element : W) {
IWizardDescriptor wd = element;
if (isProjectWizard(wd)) {
continue;
}
if (!isNewWizardProvider(wd)) {
continue;
}
MenuItem miNewItem = new MenuItem(newMenu, SWT.NONE);
miNewItem.setText(wd.getLabel());
miNewItem.setData(wd.getId());
if (wd.getImageDescriptor() != null) {
miNewItem.setImage(wd.getImageDescriptor().createImage());
}
miNewItem.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent event) {
launchWizard((String) ((MenuItem) event.getSource()).getData());
}
});
}
}
}
protected void launchWizard(String id) {
IWizardDescriptor wd = PlatformUI.getWorkbench().getNewWizardRegistry().findWizard(id);
if (wd == null) {
return;
}
try {
INewWizard wizard = (INewWizard) wd.createWizard();
wizard.init(PlatformUI.getWorkbench(), new StructuredSelection());
if (m_customizationProvider != null) {
((NewWizardProvider) wizard).setNewParameterMap(m_customizationProvider.getCustomizationMap());
}
((NewWizardProvider) wizard).setUrlChooser(this);
WizardDialog dialog = new WizardDialog(parent.getShell(), wizard);
if (dialog.open() == Window.OK) {
// acceptUrl(((NewWizardProvider) wizard).getPrimaryPath());
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void acceptUrl(String path) {
URL url = null;
try {
for (Iterator iterator = actionProviders.iterator(); iterator.hasNext();) {
FileSystemProvider provider = (FileSystemProvider) iterator.next();
url = provider.convertToURL(path);
if (url != null) {
updateSelectedValues(new URL[] { url });
break;
}
}
if (url == null) {
url = new URL(path);
}
} catch (Exception e) {
e.printStackTrace();
}
}
private boolean isProjectWizard(IWizardDescriptor descriptor) {
String[] tags = descriptor.getTags();
for (String tag : tags) {
if (tag.equals("project")) {
return true;
}
}
return false;
}
private boolean isNewWizardProvider(IWizardDescriptor descriptor) {
// Don't like this but I can't see any other way of determining if the
// wizard
// supports the necessary interface in order for us to extract the new
// resource
// URL later on.
IWorkbenchWizard wizard = null;
try {
wizard = descriptor.createWizard();
if ((wizard == null) || !(wizard instanceof NewWizardProvider)) {
return false;
}
String[] ext = ((NewWizardProvider) wizard).getSupportedExtensions();
if (!extensionsSupportSubset(ext)) {
return false;
}
} catch (Exception e) {
e.printStackTrace();
return false;
} finally {
if (wizard != null) {
wizard.dispose();
}
}
return true;
}
/**
* Compares the list of extensions in the parameter with the extension filters set on the
* URLChooser. The ext parameter does NOT expect a "*." prefix...just the extension.
*
* @param ext
* @return
*/
private boolean extensionsSupportSubset(String[] ext) {
// Missing or filter with just 1 entry means *.*
if ((filter == null) || (filter.size() == 1)) {
return true;
}
for (Iterator iter = filter.keySet().iterator(); iter.hasNext();) {
String check = (String) iter.next();
for (String element : ext) {
if (check.toLowerCase().endsWith("." + element.toLowerCase())) {
return true;
}
}
}
return false;
}
private NewCustomizationProvider m_customizationProvider = null;
public void setNewCustomizationProvider(NewCustomizationProvider provider) {
m_customizationProvider = provider;
}
@Override
protected void execActionProvider(final ActionProvider provider) {
((FileSystemProvider) provider).setFilters(filter);
super.execActionProvider(provider);
}
// ------------------------------------------------------------------------
/**
* Method executes the default (1st) or last executed File System Provider's chooser.
*/
protected void execFileSystemProvider() {
execActionProvider();
}
@Override
protected void fireSelectionChanged() {
fireURLChanged();
}
protected void fireURLChanged() {
isTextModified = false; // this is critical or the text field fires this
// method again
List cloned = new ArrayList(listeners);
updateRecentOnURLChange();
for (Iterator iterator = cloned.iterator(); iterator.hasNext();) {
Object listener = iterator.next();
if (listener instanceof URLChooserChangeListener) {
try {
((URLChooserChangeListener) listener).urlChanged(toStringArray(selectedValues));
} catch (Throwable t) {
t.printStackTrace();
}
}
}
}
private void updateRecentOnURLChange() {
if ((style & HIDE_HISTORY) == 0) {
for (Object selectedValue : selectedValues) {
try {
URL url = null;
if (selectedValue instanceof URL) {
url = (URL) selectedValue;
} else {
url = new URL((String) selectedValue);
}
updateHistory(url);
urlChooserPrefHandler.addURL(historyContext, url);
} catch (MalformedURLException e) {
}
}
}
}
private String[] toStringArray(Object[] selectedValues) {
if (selectedValues == null) {
return null;
}
String[] strArray = new String[selectedValues.length];
for (int i = 0; i < strArray.length; i++) {
strArray[i] = selectedValues[i] != null ? selectedValues[i].toString() : null;
}
return strArray;
}
/**
* Takes a Map of extension/name pairs (extension = key, name = value) and sets them up as the
* filters.
*
* @param filterMap
*/
public void setFilters(URLChooserFilter filter) {
// if the filter contains *.* then first remove it and then add it back
// in at the end
this.filter = filter;
if (filter.containsKey("*.*")) {
filter.remove("*.*");
filter.put("*.*", "All Files");
}
}
public void addFileSystemProvider(FileSystemProvider provider) {
fileSystemProviders.put(provider.getID(), provider);
}
public void removeFileSystemProvider(FileSystemProvider provider) {
fileSystemProviders.remove(provider.getID());
}
public FileSystemProvider[] getFileSystemProviders() {
List list = new ArrayList(fileSystemProviders.values());
return (FileSystemProvider[]) list.toArray(new FileSystemProvider[0]);
}
public interface NewCustomizationProvider {
Map getCustomizationMap();
}
public static Map getDefaultCustomizationMap(IFile refEditorFile) {
Map map = new HashMap();
map.put(CP_FILE_NAME, refEditorFile.getName());
map.put(CP_PARENT_FOLDER, refEditorFile.getParent().getFullPath());
return map;
}
public static String[] urlArrayToStringArray(URL[] urls) {
String[] values = new String[urls.length];
for (int i = 0; i < urls.length; i++) {
values[i] = urls[i].toString();
}
return values;
}
public static URL[] stringArrayToURLArray(Object[] selectedValues) {
ArrayList list = new ArrayList();
for (Object selectedValue : selectedValues) {
try {
if (selectedValue instanceof URL) {
list.add(selectedValue);
} else {
list.add(new URL((String) selectedValue));
}
} catch (Exception e) {
e.printStackTrace();
}
}
return (URL[]) list.toArray(new URL[0]);
}
protected void populateRecentMenu() {
recentFiles.clear();
URL[] urls = urlChooserPrefHandler.getURLs(historyContext);
if (urls.length == 0 && recent != null) {
recent.setEnabled(false);
return;
}
URL[] filteredUrls = filterURLS(urls);
for (int i = 0; i < RECENT_FILES_COUNT && i < filteredUrls.length; i++) {
recentFiles.add(filteredUrls[i].toString());
}
updateRecentMenu();
}
private URL[] filterURLS(URL[] urls) {
if (filter == null) {
return urls;
}
String[] filterExtensions = filter.getFilterExtensions();
if (filterExtensions.length == 1 && "*.*".equals(filterExtensions[0])) {
return urls;
}
List filteredList = new ArrayList();
for (URL url2 : urls) {
String url = url2.toString();
if ((!url.startsWith("sonic") && !url.startsWith("file")) && !urlHasExtension(url)) {
filteredList.add(url2);
continue;
}
for (String filterExtension : filterExtensions) {
String extn = filterExtension.substring(filterExtension.indexOf("*") + 1, filterExtension.length());
if (url.endsWith(extn)) {
filteredList.add(url2);
break;
}
}
}
return (URL[]) filteredList.toArray(new URL[0]);
}
private boolean urlHasExtension(String url) {
int qIdx = url.lastIndexOf('?');
if (qIdx != -1) {
return false;
}
try {
URL _url = new URL(url);
String file = _url.getFile();
if (file != null) {
int lastDotIndex = file.lastIndexOf(".");
return lastDotIndex != -1;
}
} catch (MalformedURLException e) {
e.printStackTrace();
}
return false;
}
private void updateHistory(URL url) {
if (url != null) {
String urlStr = url.toString();
recentFiles.remove(urlStr);
recentFiles.add(0, urlStr);
if (recentFiles.size() > RECENT_FILES_COUNT) {
recentFiles.remove(recentFiles.get(recentFiles.size() - 1));
}
updateRecentMenu();
}
}
private void updateRecentMenu() {
if (recentMenu != null) {
MenuItem[] items = recentMenu.getItems();
for (MenuItem item : items) {
item.dispose();
}
_updateRecentMenu();
}
}
private void addRecentMenu() {
recent = new MenuItem(getMenu(), SWT.CASCADE);
recent.setText("Recent");
recent.setData("RECENT");
recentMenu = new Menu(getMenu());
recent.setMenu(recentMenu);
}
private void _updateRecentMenu() {
boolean showRecentFiles = false;
if (recentFiles.size() > 0 && !showRelativeFilePath) {
showRecentFiles = true;
}
recent.setEnabled(showRecentFiles);
for (Iterator iterator = recentFiles.iterator(); iterator.hasNext();) {
String urlStr = (String) iterator.next();
try {
final URL url = new URL(urlStr);
MenuItem mi = new MenuItem(recentMenu, SWT.NONE);
mi.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
setSelectedValue(url);
}
});
mi.setText(urlStr);
} catch (MalformedURLException e) {
}
}
}
@Override
protected void addCustomMenus() {
super.addCustomMenus();
if ((style & HIDE_HISTORY) == 0) {
addRecentMenu();
populateRecentMenu();
}
}
public void removeURL(URL url) {
urlChooserPrefHandler.removeURL(historyContext, url);
recentFiles.remove(url.toString());
updateRecentMenu();
}
public String getInputFile(URL selectedUrl) {
File file = new File(selectedUrl.getFile());
return file.getAbsolutePath();
}
private void loadFileSystemProviders() {
try {
addFileSystemProvider(new LocalFileSystemProvider());
addFileSystemProvider(new LocalDirectorySystemProvider());
WorkspaceChooserProvider provider = new WorkspaceChooserProvider();
provider.setSelectionValidator(new ISelectionValidator() {
public String isValid(Object selection) {
if (selection instanceof IPath) {
return ((IPath) selection).toFile().isFile() ? null : "Folders cannot be selected.";
}
return "Invalid selection.";
}
});
addFileSystemProvider(provider);
if (ResourceUtil.getCurrentActiveFile() != null) {
ResourceChooserProvider resourceProvider = new ResourceChooserProvider();
resourceProvider.setSelectionValidator(new ISelectionValidator() {
public String isValid(Object selection) {
if (selection instanceof IPath) {
return ((IPath) selection).toFile().isFile() ? null : "Folders cannot be selected.";
}
return "Invalid selection.";
}
});
addFileSystemProvider(resourceProvider);
}
} catch (Throwable t) {
t.printStackTrace();
}
}
}