blob: 6291e46d838985d7bb6e1f2f9d3f46853db4db5f [file] [log] [blame]
/*
* Copyright (c) 2018 Ed Merks (Berlin, Germany) and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* Ed Merks - initial API and implementation
*/
package org.eclipse.oomph.setup.presentation;
import org.eclipse.oomph.internal.ui.GeneralDragAdapter;
import org.eclipse.oomph.internal.ui.OomphTransferDelegate;
import org.eclipse.oomph.p2.provider.P2EditPlugin;
import org.eclipse.oomph.setup.Project;
import org.eclipse.oomph.setup.Scope;
import org.eclipse.oomph.setup.SetupTask;
import org.eclipse.oomph.setup.Stream;
import org.eclipse.oomph.setup.internal.core.SetupContext;
import org.eclipse.oomph.setup.internal.core.util.SetupCoreUtil;
import org.eclipse.oomph.setup.ui.SetupUIPlugin;
import org.eclipse.oomph.setup.ui.wizards.SetupWizard;
import org.eclipse.oomph.ui.DockableDialog;
import org.eclipse.oomph.ui.DockableDialog.Factory;
import org.eclipse.oomph.ui.FilteredTreeWithoutWorkbench;
import org.eclipse.oomph.ui.OomphDialog;
import org.eclipse.oomph.ui.UIUtil;
import org.eclipse.oomph.util.CollectionUtil;
import org.eclipse.oomph.util.IOUtil;
import org.eclipse.oomph.util.OS;
import org.eclipse.oomph.util.OomphPlugin.Preference;
import org.eclipse.oomph.util.PropertiesUtil;
import org.eclipse.oomph.util.ReflectUtil;
import org.eclipse.oomph.util.StringUtil;
import org.eclipse.emf.common.CommonPlugin;
import org.eclipse.emf.common.notify.AdapterFactory;
import org.eclipse.emf.common.ui.ImageURIRegistry;
import org.eclipse.emf.common.ui.viewer.ColumnViewerInformationControlToolTipSupport;
import org.eclipse.emf.common.ui.viewer.IStyledLabelDecorator;
import org.eclipse.emf.common.util.ECollections;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.SegmentSequence;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.edit.provider.ComposedAdapterFactory;
import org.eclipse.emf.edit.provider.IItemFontProvider;
import org.eclipse.emf.edit.provider.ItemProvider;
import org.eclipse.emf.edit.ui.provider.AdapterFactoryContentProvider;
import org.eclipse.emf.edit.ui.provider.AdapterFactoryLabelProvider;
import org.eclipse.emf.edit.ui.provider.DecoratingColumLabelProvider;
import org.eclipse.emf.edit.ui.provider.DiagnosticDecorator;
import org.eclipse.emf.edit.ui.provider.ExtendedImageRegistry;
import org.eclipse.core.resources.IStorage;
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.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.text.AbstractHoverInformationControlManager;
import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ILabelProviderListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.StyledString;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.browser.LocationAdapter;
import org.eclipse.swt.browser.LocationEvent;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorReference;
import org.eclipse.ui.IPersistableElement;
import org.eclipse.ui.IStorageEditorInput;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.browser.IWebBrowser;
import org.eclipse.ui.browser.IWorkbenchBrowserSupport;
import org.eclipse.ui.dialogs.PatternFilter;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipInputStream;
/**
* @author Ed Merks
*/
public class OpenDiscoveredType extends OomphDialog
{
public static final String TITLE = "Open Dicovered Type";
public static final String MESSAGE = "Search Git repositories by entering the fully qualified name of a Java class or package";
private static final String GIT_INDICES = PropertiesUtil.getProperty("oomph.git.index", "http://download.eclipse.org/oomph/git/git-index.zip");
private final DockableDialog.Dockable dockable = new DockableDialog.Dockable(this);
private final IWorkbenchBrowserSupport browserSupport = PlatformUI.getWorkbench().getBrowserSupport();
private Item selectedItem;
private String selectedLink;
private ExtendedFilteredTree filteredTree;
private TreeViewer masterViewer;
private TreeViewer detailsViewer;
private Job detailsLoadJob;
private Map<String, URI> repositoryProjectSetups;
final private ItemFilter filter = new ItemFilter();
public OpenDiscoveredType(Shell parentShell)
{
super(parentShell, TITLE, 700, 500, SetupEditorPlugin.INSTANCE, true);
setShellStyle(getShellStyle() ^ SWT.APPLICATION_MODAL | SWT.MODELESS | SWT.RESIZE | SWT.MAX | (OS.INSTANCE.isWin() ? SWT.MIN : SWT.NONE));
setBlockOnOpen(false);
}
@Override
public String getHelpPath()
{
return SetupEditorPlugin.INSTANCE.getSymbolicName() + "/html/OpenInTypeBrowserHelp.html";
}
public DockableDialog.Dockable getDockable()
{
return dockable;
}
@Override
protected String getShellText()
{
return TITLE;
}
@Override
protected String getDefaultMessage()
{
return MESSAGE + ".";
}
@Override
protected String getImagePath()
{
return "full/wizban/BrowseType";
}
@Override
protected int getContainerMargin()
{
return 10;
}
private void setSelectedLink(String selectedLink)
{
Button button = getButton(IDialogConstants.OK_ID);
button.setEnabled(selectedLink != null);
this.selectedLink = selectedLink;
}
private void setSelectedItem(Item item)
{
selectedItem = item;
setSelectedLink(item != null && item.getName() != null && item.getName().startsWith("http") ? item.getName() : null);
}
@Override
protected void createUI(Composite composite)
{
getShell().setImage(SetupEditorPlugin.INSTANCE.getSWTImage("BrowseType"));
SashForm sashForm = new SashForm(composite, SWT.SMOOTH | SWT.VERTICAL);
sashForm.setLayout(new GridLayout());
sashForm.setLayoutData(new GridData(GridData.FILL_BOTH));
Composite treeComposite = new Composite(sashForm, SWT.NONE);
treeComposite.setLayout(UIUtil.createGridLayout(1));
Composite filterComposite = new Composite(treeComposite, SWT.NONE);
filterComposite.setLayout(UIUtil.createGridLayout(2));
filterComposite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
Composite filterPlaceholder = new Composite(filterComposite, SWT.NONE);
filterPlaceholder.setLayout(UIUtil.createGridLayout(1));
filterPlaceholder.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
ToolBar filterToolBar = new ToolBar(filterComposite, SWT.FLAT | SWT.RIGHT);
final ToolItem collapseAllButton = new ToolItem(filterToolBar, SWT.NONE);
collapseAllButton.setToolTipText("Collapse All");
collapseAllButton.setImage(SetupEditorPlugin.INSTANCE.getSWTImage("collapse-all"));
collapseAllButton.addSelectionListener(new SelectionAdapter()
{
@Override
public void widgetSelected(SelectionEvent e)
{
masterViewer.collapseAll();
}
});
filter.setIncludeLeadingWildcard(true);
filteredTree = new ExtendedFilteredTree(treeComposite, SWT.BORDER, filter, filter);
filteredTree.setExpansionCount(100);
Control filterControl = filteredTree.getChildren()[0];
filterControl.setParent(filterPlaceholder);
final AdapterFactory adapterFactory = new ComposedAdapterFactory();
masterViewer = filteredTree.getViewer();
ColumnViewerInformationControlToolTipSupport toolTipSupport = new ColumnViewerInformationControlToolTipSupport(masterViewer, new LocationAdapter()
{
@Override
public void changing(LocationEvent event)
{
if (event.location != null && !event.location.startsWith("about:"))
{
event.doit = false;
openProjectImporter(URI.createURI(event.location));
}
}
});
masterViewer.setLabelProvider(new DelegatingStyledCellLabelProvider(new ToolTipLabelProvider(adapterFactory, masterViewer, toolTipSupport)));
masterViewer.setContentProvider(new AdapterFactoryContentProvider(adapterFactory));
masterViewer.setUseHashlookup(true);
final Tree masterTree = masterViewer.getTree();
masterTree.setLayoutData(new GridData(GridData.FILL_BOTH));
masterViewer.addDoubleClickListener(new IDoubleClickListener()
{
public void doubleClick(DoubleClickEvent event)
{
IStructuredSelection selection = (IStructuredSelection)masterViewer.getSelection();
Item item = (Item)selection.getFirstElement();
boolean expanded = masterViewer.getExpandedState(item);
masterViewer.setExpandedState(item, !expanded);
if (item.getType() == Item.Type.PACKAGE)
{
LOOP: while (item != null)
{
for (Item child : item.getItems())
{
if (filter.isElementVisible(masterViewer, child))
{
item = child;
if (child.getType() == Item.Type.CLASS)
{
break LOOP;
}
masterViewer.setExpandedState(child, true);
continue LOOP;
}
}
break;
}
masterViewer.setSelection(new StructuredSelection(item), true);
}
}
});
masterViewer.addSelectionChangedListener(new ISelectionChangedListener()
{
public void selectionChanged(SelectionChangedEvent event)
{
IStructuredSelection selection = (IStructuredSelection)event.getSelection();
Item item = (Item)selection.getFirstElement();
loadDetails(item);
setSelectedItem(null);
}
});
Composite detailsComposite = new Composite(sashForm, SWT.NONE);
detailsComposite.setLayout(new FillLayout());
detailsComposite.setForeground(masterTree.getForeground());
detailsComposite.setBackground(masterTree.getBackground());
detailsViewer = new TreeViewer(detailsComposite);
detailsViewer.setLabelProvider(new DelegatingStyledCellLabelProvider(new ToolTipLabelProvider(adapterFactory, detailsViewer, null)));
detailsViewer.setContentProvider(new AdapterFactoryContentProvider(adapterFactory));
detailsViewer.addSelectionChangedListener(new ISelectionChangedListener()
{
public void selectionChanged(SelectionChangedEvent event)
{
IStructuredSelection selection = (IStructuredSelection)event.getSelection();
Item item = (Item)selection.getFirstElement();
setSelectedItem(item);
}
});
detailsViewer.addDoubleClickListener(new IDoubleClickListener()
{
public void doubleClick(DoubleClickEvent event)
{
if (selectedLink != null)
{
openURL(getOpenLinkStyle());
}
}
});
List<? extends OomphTransferDelegate> dndDelegates = Arrays
.asList(new OomphTransferDelegate[] { new OomphTransferDelegate.URLTransferDelegate(), new OomphTransferDelegate.TextTransferDelegate() });
Transfer[] dndTransfers = new Transfer[] { dndDelegates.get(0).getTransfer(), dndDelegates.get(1).getTransfer() };
detailsViewer.addDragSupport(DND.DROP_COPY | DND.DROP_MOVE | DND.DROP_LINK, dndTransfers,
new GeneralDragAdapter(detailsViewer, new GeneralDragAdapter.DraggedObjectsFactory()
{
public List<Object> createDraggedObjects(ISelection selection) throws Exception
{
List<Object> result = new ArrayList<Object>();
for (Object object : ((IStructuredSelection)selection).toArray())
{
if (object instanceof Item)
{
Item item = (Item)object;
String name = item.getName();
if (name != null && name.startsWith("http"))
{
result.add(name);
}
}
}
return result;
}
}, dndDelegates));
Menu menu = detailsViewer.getControl().getMenu();
MenuManager contextMenu = (MenuManager)menu.getData(MenuManager.MANAGER_KEY);
contextMenu.addMenuListener(new IMenuListener()
{
public void menuAboutToShow(IMenuManager manager)
{
manager.add(new Separator());
if (selectedLink != null)
{
final boolean internalWebBrowserAvailable = browserSupport.isInternalWebBrowserAvailable();
final OpenLinkStyle openLinkStyle = getOpenLinkStyle();
manager.add(new Action("Open in External Browser")
{
{
if (internalWebBrowserAvailable)
{
setChecked(openLinkStyle == OpenLinkStyle.EXTERNAL);
}
}
@Override
public void run()
{
openURL(OpenLinkStyle.EXTERNAL);
}
});
if (internalWebBrowserAvailable)
{
manager.add(new Action("Open in Editor")
{
{
setChecked(openLinkStyle == OpenLinkStyle.EDITOR);
}
@Override
public void run()
{
openURL(OpenLinkStyle.EDITOR);
}
});
manager.add(new Action("Open in View")
{
{
setChecked(openLinkStyle == OpenLinkStyle.VIEW);
}
@Override
public void run()
{
openURL(OpenLinkStyle.VIEW);
}
});
}
if (selectedItem.rawLinks != null && selectedItem.rawLinks.get(selectedLink) != null)
{
manager.add(new Action("Open in Java Editor")
{
{
setChecked(openLinkStyle == OpenLinkStyle.JAVA);
}
@Override
public void run()
{
openURL(OpenLinkStyle.JAVA);
}
});
}
if (repositoryProjectSetups != null)
{
manager.add(new Separator());
for (final URI projectSetupURI : getProjectSetups(selectedItem))
{
manager.add(new Action("Open " + projectSetupURI.lastSegment() + " in Import Projects Wizard")
{
@Override
public void run()
{
openProjectImporter(projectSetupURI);
}
});
}
}
}
}
});
sashForm.setWeights(new int[] { 14, 5 });
UIUtil.asyncExec(composite, new Runnable()
{
public void run()
{
setSelectedItem(null);
}
});
loadModel();
}
private void openProjectImporter(URI projectSetupURI)
{
SetupWizard.Importer wizard = new SetupWizard.Importer();
wizard.setProject(projectSetupURI);
wizard.openDialog(getParentShell());
}
private List<URI> getProjectSetups(Item item)
{
List<URI> result = new ArrayList<URI>();
if (repositoryProjectSetups != null)
{
for (String repo : item.getRepos())
{
URI uri = repositoryProjectSetups.get(repo);
if (uri == null && repo.endsWith(".git"))
{
uri = repositoryProjectSetups.get(repo.substring(0, repo.length() - 4));
}
if (uri != null && !result.contains(uri))
{
result.add(uri);
}
}
}
return result;
}
private void decorate(Item item)
{
EList<Item> items = item.getItems();
Set<String> decorations = new HashSet<String>();
List<Item> visibleChildren = new ArrayList<Item>();
for (Item child : items)
{
if (filter.isElementVisible(masterViewer, child))
{
visibleChildren.add(child);
decorate(child);
decorations.add(child.getDecoration());
}
}
if (visibleChildren.isEmpty())
{
Set<String> labels = new LinkedHashSet<String>();
for (URI projectSetupURI : getProjectSetups(item))
{
labels.add(projectSetupURI.lastSegment());
}
if (!labels.isEmpty())
{
StringBuilder decoration = new StringBuilder();
for (String label : labels)
{
if (decoration.length() != 0)
{
decoration.append(' ');
}
decoration.append(label);
}
item.setDecoration(decoration.toString());
}
else
{
item.setDecoration(null);
}
}
else if (decorations.size() == 1 && !decorations.contains(null))
{
if (item.getType() != Item.Type.ROOT)
{
item.setDecoration(decorations.iterator().next());
for (Item child : visibleChildren)
{
child.setDecoration(null);
}
}
}
else
{
item.setDecoration(null);
}
}
protected void loadModel()
{
Item root = Item.createRootItem();
root.getChildren().add(Item.createPlaceholderItem());
masterViewer.setInput(root);
Job job = new Job("Git Browsing Index Loader")
{
@Override
protected IStatus run(IProgressMonitor monitor)
{
final Item root = Item.createRootItem();
Item packageItem = null;
SegmentSequence qualifiedName = null;
ResourceSet resourceSet = SetupCoreUtil.createResourceSet();
for (String gitIndex : StringUtil.explode(GIT_INDICES, " ", (char)0))
{
ZipInputStream zipInputStream = null;
try
{
String link = null;
String rawLink = null;
String repo = null;
String repoLink = null;
String rawRepoLink = null;
String sourceFolder = null;
String sourceFolderLink = null;
String rawSourceFolderLink = null;
String packageName = null;
String className = null;
zipInputStream = new ZipInputStream(resourceSet.getURIConverter().createInputStream(URI.createURI(gitIndex)));
if (zipInputStream.getNextEntry() != null)
{
List<String> lines = IOUtil.readLines(zipInputStream, "UTF-8");
for (String line : lines)
{
if (line.startsWith(" "))
{
className = line.trim();
Item classItem = packageItem.getItem(className);
if (classItem == null)
{
classItem = Item.create(Item.Type.CLASS, qualifiedName.append(className).toString());
classItem.itemFilter = filter;
packageItem.getChildren().add(classItem);
}
classItem.addLink(rawSourceFolderLink, sourceFolderLink, repo, repoLink);
}
else if (line.startsWith(" "))
{
packageName = line.trim();
SegmentSequence segments = SegmentSequence.create(".", packageName);
qualifiedName = SegmentSequence.create(".");
Item item = root;
for (String segment : segments.segments())
{
qualifiedName = qualifiedName.append(segment);
packageItem = item.getItem(segment);
if (packageItem == null)
{
packageItem = Item.create(Item.Type.PACKAGE, qualifiedName.toString());
packageItem.itemFilter = filter;
item.getChildren().add(packageItem);
}
packageItem.addLink(sourceFolderLink, repo, repoLink);
item = packageItem;
}
}
else if (line.startsWith(" "))
{
sourceFolder = line.trim();
sourceFolderLink = repoLink.replace("${1}", sourceFolder + "/${1}");
if (rawRepoLink == null)
{
rawSourceFolderLink = null;
}
else
{
rawSourceFolderLink = rawRepoLink.replace("${1}", sourceFolder + "/${1}");
}
}
else if (line.startsWith(" "))
{
repo = line.trim();
repoLink = link.replace("${0}", repo);
if (rawLink == null)
{
rawRepoLink = null;
}
else
{
rawRepoLink = rawLink.replace("${0}", repo);
}
}
else
{
List<String> links = StringUtil.explode(line, " ", (char)0);
link = links.get(0);
if (links.size() > 1)
{
rawLink = links.get(1);
}
else
{
rawLink = null;
}
}
}
}
}
catch (Exception ex)
{
IOUtil.close(zipInputStream);
}
}
root.sort();
UIUtil.asyncExec(masterViewer.getControl(), new Runnable()
{
public void run()
{
masterViewer.setInput(root);
filteredTree.textChanged();
}
});
Map<String, URI> remoteURIs = new HashMap<String, URI>();
Resource resource = resourceSet.getResource(SetupContext.INDEX_SETUP_URI, true);
for (Iterator<EObject> it = resource.getAllContents(); it.hasNext();)
{
EObject eObject = it.next();
EClass eClass = eObject.eClass();
if ("GitCloneTask".equals(eClass.getName()))
{
EStructuralFeature eStructuralFeature = eClass.getEStructuralFeature("remoteURI");
if (eStructuralFeature != null)
{
String remoteURI = (String)eObject.eGet(eStructuralFeature);
if (remoteURI != null)
{
Scope scope = ((SetupTask)eObject).getScope();
if (scope instanceof Stream)
{
scope = scope.getParentScope();
}
if (scope instanceof Project)
{
URI uri = EcoreUtil.getURI(scope);
URI oldURI = remoteURIs.put(remoteURI, uri);
if (oldURI != null)
{
if (oldURI.toString().contains(remoteURI))
{
remoteURIs.put(remoteURI, oldURI);
}
else
{
int index = remoteURI.indexOf("/");
if (index != -1)
{
if (oldURI.toString().toLowerCase().contains(remoteURI.substring(0, index).toLowerCase()))
{
remoteURIs.put(remoteURI, oldURI);
}
}
}
}
}
}
}
}
}
repositoryProjectSetups = remoteURIs;
if (!masterViewer.getControl().isDisposed())
{
try
{
decorate(root);
}
catch (RuntimeException ex)
{
// Ignore.
}
}
UIUtil.asyncExec(masterViewer.getControl(), new Runnable()
{
public void run()
{
masterViewer.refresh();
}
});
return Status.OK_STATUS;
}
};
job.setSystem(true);
job.schedule();
}
protected void loadDetails(final Item item)
{
if (detailsLoadJob != null)
{
detailsLoadJob.cancel();
}
if (item == null)
{
detailsViewer.setInput(null);
}
else
{
Item root = Item.createRootItem();
root.getChildren().add(Item.createPlaceholderItem());
detailsViewer.setInput(root);
}
detailsLoadJob = new Job("Detail Loader")
{
@Override
protected IStatus run(IProgressMonitor monitor)
{
if (item != null)
{
final Item input = Item.createRootItem();
Item javaItem = Item.create(item.getType(), item.getName());
input.getChildren().add(javaItem);
Map<String, Set<String>> unfilteredGroups = new HashMap<String, Set<String>>();
collectUnfilteredGroups(unfilteredGroups, item);
EList<Object> children = javaItem.getChildren();
Item.Type type = item.getType();
Item.Type linkType = type == Item.Type.CLASS ? Item.Type.CLASS_LINK : Item.Type.PACKAGE_LINK;
int count = 0;
Map<String, String> rawLinks = type == Item.Type.CLASS ? new HashMap<String, String>() : null;
Map<String, Map<String, Set<String>>> groupLinks = item.getGroupLinks(unfilteredGroups, rawLinks);
for (Entry<String, Map<String, Set<String>>> groupEntry : groupLinks.entrySet())
{
String repoLink = groupEntry.getKey();
Item groupLinkItem = Item.create(Item.Type.FOLDER_LINK, repoLink);
++count;
children.add(groupLinkItem);
EList<Item> groupChildren = groupLinkItem.getItems();
Map<String, Set<String>> links = groupEntry.getValue();
for (Map.Entry<String, Set<String>> entry : links.entrySet())
{
String link = entry.getKey();
Item linkItem = Item.create(linkType, link);
linkItem.setText(link.replace(repoLink, ""));
++count;
groupChildren.add(linkItem);
for (String repo : entry.getValue())
{
linkItem.addLink(repo, repo, "");
groupLinkItem.addLink(repo, repo, "");
linkItem.rawLinks = rawLinks;
}
}
decorate(groupLinkItem);
if (monitor.isCanceled())
{
break;
}
}
if (!monitor.isCanceled())
{
input.sort();
final int limit = count;
UIUtil.asyncExec(detailsViewer.getControl(), new Runnable()
{
public void run()
{
detailsViewer.setInput(input);
detailsViewer.expandToLevel(limit > 1000 ? 2 : 3);
}
});
}
}
if (!masterViewer.getControl().isDisposed())
{
try
{
Item root = (Item)masterViewer.getInput();
decorate(root);
}
catch (RuntimeException ex)
{
// Ignore.
}
}
UIUtil.asyncExec(masterViewer.getControl(), new Runnable()
{
public void run()
{
masterViewer.refresh(true);
}
});
return Status.OK_STATUS;
}
public void collectUnfilteredGroups(Map<String, Set<String>> groups, Item item)
{
boolean hasVisibleChildren = false;
for (Item child : item.getItems())
{
if (filter.select(detailsViewer, item, child))
{
hasVisibleChildren = true;
collectUnfilteredGroups(groups, child);
}
}
if (!hasVisibleChildren)
{
for (Map.Entry<String, Map<String, Set<String>>> entry : item.groupLinks.entrySet())
{
CollectionUtil.addAll(groups, entry.getKey(), entry.getValue().keySet());
}
}
}
};
detailsLoadJob.setSystem(true);
detailsLoadJob.schedule();
}
@Override
protected void okPressed()
{
super.okPressed();
openURL(getOpenLinkStyle());
}
private void setOpenLinkStyle(OpenLinkStyle style)
{
Preference instancePreference = SetupEditorPlugin.INSTANCE.getInstancePreference(selectedItem.getType() + ".open.style");
instancePreference.set(style.name());
}
private OpenLinkStyle getOpenLinkStyle()
{
Preference instancePreference = SetupEditorPlugin.INSTANCE.getInstancePreference(selectedItem.getType() + ".open.style");
String name = instancePreference.get("VIEW");
try
{
return OpenLinkStyle.valueOf(OpenLinkStyle.class, name);
}
catch (Exception ex)
{
return OpenLinkStyle.VIEW;
}
}
private void openURL(OpenLinkStyle openLinkStyle)
{
try
{
setOpenLinkStyle(openLinkStyle);
String id = "org.eclipse.oomph.setup.type.browser";
int style = 0;
switch (openLinkStyle)
{
case EDITOR:
{
id += ".editor";
style = IWorkbenchBrowserSupport.AS_EDITOR;
break;
}
case EXTERNAL:
{
style = IWorkbenchBrowserSupport.AS_EXTERNAL;
id += ".external";
break;
}
case VIEW:
{
style = IWorkbenchBrowserSupport.AS_VIEW;
id += ".view";
break;
}
case JAVA:
{
final String rawLink = selectedItem.rawLinks.get(selectedLink);
class OpenJavaEditorJob extends Job
{
private IOException ioException;
private byte[] bytes;
public OpenJavaEditorJob()
{
super("Load " + rawLink);
}
@Override
protected IStatus run(IProgressMonitor monitor)
{
InputStream in = null;
try
{
URL url = new URL(rawLink);
ByteArrayOutputStream out = new ByteArrayOutputStream();
in = url.openStream();
IOUtil.copy(in, out);
bytes = out.toByteArray();
}
catch (IOException ex)
{
ioException = ex;
}
finally
{
IOUtil.closeSilent(in);
}
class StorageEditorInput implements IStorageEditorInput
{
@SuppressWarnings("all")
public Object getAdapter(Class adapter)
{
return null;
}
public String getToolTipText()
{
return rawLink;
}
public IPersistableElement getPersistable()
{
return null;
}
public String getName()
{
return URI.createURI(rawLink).lastSegment();
}
public ImageDescriptor getImageDescriptor()
{
return null;
}
public boolean exists()
{
return true;
}
public IStorage getStorage() throws CoreException
{
return new IStorage()
{
@SuppressWarnings("all")
public Object getAdapter(Class adapter)
{
return null;
}
public boolean isReadOnly()
{
return true;
}
public String getName()
{
return URI.createURI(rawLink).lastSegment();
}
public IPath getFullPath()
{
return new Path(rawLink);
}
public InputStream getContents() throws CoreException
{
if (ioException != null)
{
SetupUIPlugin.INSTANCE.coreException(ioException);
return null;
}
return new ByteArrayInputStream(bytes);
}
};
}
}
UIUtil.asyncExec(getShell(), new Runnable()
{
public void run()
{
try
{
final IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
for (IEditorReference editorReference : page.getEditorReferences())
{
IEditorInput editorInput = editorReference.getEditorInput();
if (editorInput instanceof StorageEditorInput && rawLink.equals(((StorageEditorInput)editorInput).getToolTipText()))
{
IEditorPart editor = editorReference.getEditor(true);
if (editor != null)
{
page.activate(editor);
return;
}
}
}
page.openEditor(new StorageEditorInput(), "org.eclipse.jdt.ui.CompilationUnitEditor", true,
IWorkbenchPage.MATCH_ID | IWorkbenchPage.MATCH_INPUT);
}
catch (PartInitException ex)
{
SetupEditorPlugin.INSTANCE.log(ex);
}
}
});
return Status.OK_STATUS;
}
}
new OpenJavaEditorJob().schedule();
return;
}
}
IWorkbenchBrowserSupport support = PlatformUI.getWorkbench().getBrowserSupport();
IWebBrowser browser = support.createBrowser(style | IWorkbenchBrowserSupport.LOCATION_BAR | IWorkbenchBrowserSupport.NAVIGATION_BAR, id, selectedLink,
selectedLink);
browser.openURL(new URL(selectedLink));
}
catch (Exception ex)
{
SetupEditorPlugin.INSTANCE.log(ex);
}
}
/**
* @author Ed Merks
*/
private final class ExtendedFilteredTree extends FilteredTreeWithoutWorkbench
{
private ExtendedFilteredTree(Composite parent, int style, PatternFilter patternFilter, ExpansionFilter expansionFilter)
{
super(parent, style, patternFilter, expansionFilter);
}
@Override
protected void refreshed()
{
masterViewer.setSelection(masterViewer.getSelection());
}
@Override
public void textChanged()
{
super.textChanged();
}
}
/**
* @author Ed Merks
*/
private enum OpenLinkStyle
{
EXTERNAL, EDITOR, VIEW, JAVA
}
/**
* @author Ed Merks
*/
private final class ItemFilter extends PatternFilter implements FilteredTreeWithoutWorkbench.ExpansionFilter
{
private Pattern pattern;
private Pattern packagePattern;
@Override
public void setPattern(String patternString)
{
if (detailsLoadJob != null)
{
// Wait for the details job to not be running anymore.
detailsLoadJob.cancel();
for (int i = 0; i < 100 && detailsLoadJob.getState() != Job.NONE; ++i)
{
try
{
Thread.sleep(100);
}
catch (InterruptedException ex)
{
// Ignore.
}
}
}
super.setPattern(patternString);
if (patternString == null)
{
pattern = null;
}
else
{
StringBuilder patternLiteral = new StringBuilder();
if (StringUtil.trimLeft(patternString).equals(patternString))
{
patternLiteral.append(".*?");
}
List<Integer> packageIndices = new ArrayList<Integer>();
boolean previousUpperCase = false;
boolean grouped = false;
for (int i = Character.offsetByCodePoints(patternString, 0, 0), length = patternString.length(); i < length; i = Character
.offsetByCodePoints(patternString, i, 1))
{
int codePoint = patternString.codePointAt(i);
if (Character.isJavaIdentifierStart(codePoint) || Character.isJavaIdentifierPart(codePoint))
{
boolean upperCase = Character.isUpperCase(codePoint);
boolean bothUpperCase = previousUpperCase && upperCase;
if (bothUpperCase)
{
if (grouped)
{
patternLiteral.append(')');
}
int lowerCaseCodePoint = Character.toLowerCase(codePoint);
patternLiteral.append("(?:[^\\p{Lu}&&[^").appendCodePoint(lowerCaseCodePoint).append("]]*(").appendCodePoint(codePoint).append(")|(")
.appendCodePoint(lowerCaseCodePoint).append("))");
if (grouped)
{
patternLiteral.append('(');
}
}
if (!grouped)
{
grouped = true;
patternLiteral.append('(');
}
if (!bothUpperCase)
{
patternLiteral.appendCodePoint(codePoint);
}
previousUpperCase = upperCase;
}
else if (codePoint == '*')
{
if (i != 0)
{
if (grouped)
{
patternLiteral.append(").*?(");
}
else
{
patternLiteral.append(".*?");
}
previousUpperCase = false;
}
}
else if (codePoint == '?')
{
if (grouped)
{
patternLiteral.append(").(");
}
else
{
patternLiteral.append(".");
}
previousUpperCase = false;
}
else if (!Character.isWhitespace(codePoint))
{
if (grouped)
{
patternLiteral.append(')');
packageIndices.add(patternLiteral.length());
patternLiteral.append('(');
}
patternLiteral.append(Pattern.quote(new String(Character.toChars(codePoint))));
if (!grouped)
{
grouped = true;
patternLiteral.append('(');
}
previousUpperCase = false;
}
else if (i + 1 != length)
{
previousUpperCase = false;
}
}
if (grouped)
{
patternLiteral.append(')');
}
if (previousUpperCase)
{
patternLiteral.append("[^\\p{Lu}]*");
}
if (StringUtil.trimRight(patternString).equals(patternString))
{
patternLiteral.append(".*");
}
else
{
patternLiteral.append('$');
}
pattern = Pattern.compile(patternLiteral.toString());
if (!packageIndices.isEmpty())
{
int last = packageIndices.size() - 1;
patternLiteral.delete(packageIndices.get(last), patternLiteral.length());
for (int i = last; i > 0; --i)
{
patternLiteral.insert(packageIndices.get(i), "?");
}
packagePattern = Pattern.compile(patternLiteral.toString());
}
else
{
packagePattern = null;
}
}
}
@Override
protected boolean wordMatches(String text)
{
if (pattern == null)
{
return true;
}
if (text == null)
{
return false;
}
return pattern.matcher(text).matches();
}
@Override
protected boolean isLeafMatch(Viewer viewer, Object element)
{
// Match against the fully qualified name of the item, not just the label text.
Item item = (Item)element;
String text = item.getName();
if (text == null)
{
text = item.getText();
}
return wordMatches(text);
}
public boolean shouldExpand(Object element)
{
return !isLeafMatch(null, element);
}
}
/**
* @author Ed Merks
*/
private static class Item extends ItemProvider
{
/**
* @author Ed Merks
*/
private enum Type
{
PLACEHOLDER, //
ROOT, //
PACKAGE
{
@Override
public String getText(String name)
{
int index = name.lastIndexOf('.');
return index == -1 ? name : name.substring(index + 1);
}
},
CLASS
{
@Override
public String getText(String name)
{
int index = name.lastIndexOf('.');
return index == -1 ? name : name.substring(index + 1);
}
},
FOLDER_LINK, //
PACKAGE_LINK, //
CLASS_LINK;
public String getText(String name)
{
return name;
}
}
private static final org.eclipse.emf.edit.provider.StyledString.Style BOLD_STYLER = org.eclipse.emf.edit.provider.StyledString.Style.newBuilder()
.setFont(IItemFontProvider.BOLD_FONT).toStyle();
private static final Comparator<String> STRING_COMPARATOR = CommonPlugin.INSTANCE.getComparator();
private static final Comparator<Item> COMPARATOR = new Comparator<Item>()
{
public int compare(Item item1, Item item2)
{
int result = item1.getType().compareTo(item2.getType());
return result == 0 ? STRING_COMPARATOR.compare(item1.getText(), item2.getText()) : result;
}
};
private static final Image FOLDER_IMAGE = SetupEditorPlugin.INSTANCE.getSWTImage("obj16/folder");
private static final Image JAVA_CLASS_IMAGE = SetupEditorPlugin.INSTANCE.getSWTImage("full/obj16/JavaCompilationUnit");
private static final Image JAVA_PACKAGE_IMAGE = ExtendedImageRegistry.INSTANCE.getImage(P2EditPlugin.INSTANCE.getImage("full/obj16/Requirement_Package"));
private static final Image SETUP_IMAGE = ExtendedImageRegistry.INSTANCE.getImage(SetupEditorPlugin.INSTANCE.getImage("full/obj16/SetupModelFile"));
private Type type;
private String name;
private String decoration;
private Map<String, Map<String, Set<String>>> groupLinks = new TreeMap<String, Map<String, Set<String>>>();
private Map<String, String> rawLinks;
private ItemFilter itemFilter;
private Item(Type type, String name)
{
super(type.getText(name), getImage(type));
this.type = type;
this.name = name;
}
public String getDecoration()
{
return decoration;
}
public void setDecoration(String decoration)
{
this.decoration = decoration;
}
public void addLink(String rawSourceFolderLink, String sourceFolderLink, String gitRepo, String repoLink)
{
if (rawSourceFolderLink != null)
{
if (rawLinks == null)
{
rawLinks = new HashMap<String, String>();
}
rawLinks.put(sourceFolderLink, rawSourceFolderLink);
}
addLink(sourceFolderLink, gitRepo, repoLink);
}
public void addLink(String sourceFolderLink, String gitRepo, String repoLink)
{
Map<String, Set<String>> map = groupLinks.get(repoLink);
if (map == null)
{
map = new TreeMap<String, Set<String>>();
groupLinks.put(repoLink, map);
}
CollectionUtil.add(map, sourceFolderLink, gitRepo);
}
public Map<String, Map<String, Set<String>>> getGroupLinks(Map<String, Set<String>> filter, Map<String, String> rawLinks)
{
Map<String, Map<String, Set<String>>> result = new LinkedHashMap<String, Map<String, Set<String>>>();
for (Map.Entry<String, Map<String, Set<String>>> groupEntry : groupLinks.entrySet())
{
String key = groupEntry.getKey();
Set<String> sourceLinks = filter.get(key);
if (sourceLinks != null)
{
Map<String, Set<String>> links = new LinkedHashMap<String, Set<String>>();
String path = type == Type.CLASS ? name.replace('.', '/') + ".java" : name.replace('.', '/');
for (Map.Entry<String, Set<String>> entry : groupEntry.getValue().entrySet())
{
String link = entry.getKey();
if (sourceLinks.contains(link))
{
String javaLink = link.replace("${1}", path);
links.put(javaLink, entry.getValue());
if (rawLinks != null && this.rawLinks != null)
{
String rawLink = this.rawLinks.get(link);
if (rawLink != null)
{
String rawJavaLink = rawLink.replace("${1}", path);
rawLinks.put(javaLink, rawJavaLink);
}
}
}
}
if (!links.isEmpty())
{
String repoLink = key.replace("${1}", "/").replace("://", ":////").replace("//", "/");
result.put(repoLink, links);
}
}
}
return result;
}
public Set<String> getRepos()
{
Set<String> result = new TreeSet<String>();
for (Map.Entry<String, Map<String, Set<String>>> groupEntry : groupLinks.entrySet())
{
for (Map.Entry<String, Set<String>> entry : groupEntry.getValue().entrySet())
{
result.addAll(entry.getValue());
}
}
return result;
}
public Type getType()
{
return type;
}
public String getName()
{
return name;
}
public static Item create(Type type, String name)
{
return new Item(type, name);
}
public static Item createRootItem()
{
return new Item(Type.ROOT, "root");
}
public static Item createPlaceholderItem()
{
return new Item(Type.PLACEHOLDER, "Loading...");
}
@Override
public Object getStyledText(Object object)
{
if (itemFilter != null && itemFilter.pattern != null)
{
Matcher matcher = itemFilter.pattern.matcher(name);
boolean patternMatches = matcher.matches();
if (!patternMatches && type == Type.PACKAGE && itemFilter.packagePattern != null)
{
matcher = itemFilter.packagePattern.matcher(name);
patternMatches = matcher.matches();
}
if (patternMatches)
{
// System.err.println("" + text);
org.eclipse.emf.edit.provider.StyledString styledLabel = new org.eclipse.emf.edit.provider.StyledString();
int groupCount = matcher.groupCount();
StringBuilder styleRun = null;
boolean previousMatches = false;
for (int i = name.length() - text.length(); i < name.length(); ++i)
{
boolean matches = false;
for (int j = 1; j <= groupCount; ++j)
{
int start = matcher.start(j);
int end = matcher.end(j);
if (i >= start && i < end)
{
matches = true;
break;
}
}
if (previousMatches != matches)
{
if (styleRun != null)
{
if (previousMatches)
{
styledLabel.append(styleRun.toString(), BOLD_STYLER);
}
else
{
styledLabel.append(styleRun.toString());
}
styleRun = null;
}
previousMatches = matches;
}
if (styleRun == null)
{
styleRun = new StringBuilder();
}
styleRun.append(name.charAt(i));
// System.err.print(matches ? '^' : ' ');
}
if (styleRun != null)
{
if (previousMatches)
{
styledLabel.append(styleRun.toString(), BOLD_STYLER);
}
else
{
styledLabel.append(styleRun.toString());
}
}
// System.err.println();
if (decoration != null)
{
styledLabel.append(" - " + decoration, org.eclipse.emf.edit.provider.StyledString.Style.DECORATIONS_STYLER);
}
return styledLabel;
}
}
if (decoration != null)
{
org.eclipse.emf.edit.provider.StyledString styledLabel = new org.eclipse.emf.edit.provider.StyledString();
styledLabel.append(getText());
styledLabel.append(" - " + decoration, org.eclipse.emf.edit.provider.StyledString.Style.DECORATIONS_STYLER);
return styledLabel;
}
return super.getStyledText(object);
}
@Override
public Item getParent()
{
return (Item)super.getParent();
}
public EList<Item> getItems()
{
@SuppressWarnings("unchecked")
EList<Item> children = (EList<Item>)(EList<?>)getChildren();
return children;
}
public Item getItem(String text)
{
for (Item item : getItems())
{
if (text.equals(item.getText()))
{
return item;
}
}
return null;
}
public void sort()
{
EList<Item> children = getItems();
ECollections.sort(children, COMPARATOR);
for (Item child : children)
{
child.sort();
}
}
private static Image getImage(Type type)
{
switch (type)
{
case CLASS:
case CLASS_LINK:
{
return JAVA_CLASS_IMAGE;
}
case FOLDER_LINK:
{
return FOLDER_IMAGE;
}
case PACKAGE:
case PACKAGE_LINK:
{
return JAVA_PACKAGE_IMAGE;
}
default:
case PLACEHOLDER:
{
return null;
}
}
}
}
/**
* @author Ed Merks
*/
private class ToolTipLabelProvider extends DecoratingColumLabelProvider.StyledLabelProvider
{
final private ColumnViewerInformationControlToolTipSupport toolTipSupport;
public ToolTipLabelProvider(AdapterFactory adapterFactory, Viewer viewer, ColumnViewerInformationControlToolTipSupport toolTipSupport)
{
super(new AdapterFactoryLabelProvider.StyledLabelProvider(adapterFactory, viewer), new IStyledLabelDecorator()
{
public void removeListener(ILabelProviderListener listener)
{
}
public boolean isLabelProperty(Object element, String property)
{
return true;
}
public void dispose()
{
}
public void addListener(ILabelProviderListener listener)
{
}
public String decorateText(String text, Object element)
{
return text;
}
public Image decorateImage(Image image, Object element)
{
return image;
}
public StyledString decorateStyledText(StyledString styledString, Object element)
{
return styledString;
}
});
this.toolTipSupport = toolTipSupport;
}
@Override
public String getToolTipText(Object element)
{
Item item = (Item)element;
StringBuilder result = new StringBuilder();
result.append("<span style='white-space: nowrap;'>");
result.append(DiagnosticDecorator.enquote("<img style='padding-right: 2pt; margin-top: 2px; margin-bottom: -2pt;' src='"
+ ImageURIRegistry.INSTANCE.getImageURI(ExtendedImageRegistry.INSTANCE.getImage(item.getImage())) + "'/> "));
result.append(URI.decode(item.getName()));
result.append("</span>");
List<URI> projectSetups = getProjectSetups(item);
String decoration = item.getDecoration();
for (Item parent = item.getParent(); decoration == null && parent != null; parent = parent.getParent())
{
decoration = parent.getDecoration();
}
if (decoration != null && decoration.indexOf(' ') == -1)
{
for (Iterator<URI> it = projectSetups.iterator(); it.hasNext();)
{
URI uri = it.next();
if (!decoration.equals(uri.lastSegment()))
{
it.remove();
}
}
}
if (!projectSetups.isEmpty())
{
Set<URI> uris = new HashSet<URI>();
for (URI uri : projectSetups)
{
uris.add(uri.trimFragment());
}
if (uris.size() == 1)
{
projectSetups.clear();
projectSetups.add(uris.iterator().next().trimFragment().appendFragment("/"));
}
}
if (projectSetups.size() == 1)
{
URI projectSetupURI = projectSetups.get(0);
result.append("<br/>");
result.append("<span style='white-space: nowrap;'>");
result.append(DiagnosticDecorator.enquote(
"<img style='padding-right: 2pt; margin-top: 2px; margin-bottom: -2pt;' src='" + ImageURIRegistry.INSTANCE.getImageURI(Item.SETUP_IMAGE)) + "'/> ");
result.append("<a href=\"");
result.append(projectSetupURI);
result.append("\">");
result.append(projectSetupURI.lastSegment());
result.append("</a>");
result.append("</span>");
}
String toolTip = result.toString();
AbstractHoverInformationControlManager hoverInformationControlManager = ReflectUtil.getValue("hoverInformationControlManager", toolTipSupport);
Point size = UIUtil.caclcuateSize(toolTip);
hoverInformationControlManager.setSizeConstraints(size.x + 4, size.y + 1, true, false);
return result.toString();
}
}
/**
* Returns the instance for this workbench window, if there is one.
*/
public static OpenDiscoveredType getFor(IWorkbenchWindow workbenchWindow)
{
return DockableDialog.getFor(OpenDiscoveredType.class, workbenchWindow);
}
/**
* Close the instance for this workbench window, if there is one.
*/
public static void closeFor(IWorkbenchWindow workbenchWindow)
{
DockableDialog.closeFor(OpenDiscoveredType.class, workbenchWindow);
}
/**
* Reopen or create the instance for this workbench window.
*/
public static OpenDiscoveredType openFor(final IWorkbenchWindow workbenchWindow)
{
Factory<OpenDiscoveredType> factory = new Factory<OpenDiscoveredType>()
{
public OpenDiscoveredType create(IWorkbenchWindow workbenchWindow)
{
return new OpenDiscoveredType(workbenchWindow.getShell());
}
};
return DockableDialog.openFor(OpenDiscoveredType.class, factory, workbenchWindow);
}
}