blob: 353ac9277c87ccb7045c0a05a4bda45283e3814d [file] [log] [blame]
/*
* Copyright (c) 2015 Eike Stepper (Berlin, Germany) 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:
* Eike Stepper - initial API and implementation
*/
package org.eclipse.emf.cdo.ui;
import org.eclipse.emf.cdo.internal.ui.bundle.OM;
import org.eclipse.net4j.util.ReflectUtil.ExcludeFromDump;
import org.eclipse.net4j.util.StringUtil;
import org.eclipse.net4j.util.WrappedException;
import org.eclipse.net4j.util.container.Container;
import org.eclipse.net4j.util.om.OMPlatform;
import org.eclipse.emf.common.util.URI;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IPartListener;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author Eike Stepper
* @since 4.4
*/
public interface CDOEditorOpener
{
public static final int DEFAULT_PRIORITY = 500;
public String getID();
public String getName();
public ImageDescriptor getIcon();
/**
* Returns the priority of this editor opener. Usually used to choose between several editor openers that
* match the same repository URI.
*/
public int getPriority();
/**
* Returns the regular expression that determines if the editor opener can open a certain URI.
*/
public String getRegex();
/**
* Checks if the URI matches the regular expression of this editor opener.
*/
public boolean matchesRegex(URI uri);
public IEditorPart openEditor(IWorkbenchPage page, URI uri);
/**
* @author Eike Stepper
*/
public static abstract class Default implements CDOEditorOpener
{
private String id;
private String name;
private ImageDescriptor icon;
private String regex;
private int priority = DEFAULT_PRIORITY;
@ExcludeFromDump
private transient Pattern pattern;
public Default()
{
}
public Default(String id, String name, ImageDescriptor icon, String regex, int priority)
{
this.id = id;
this.name = name;
this.icon = icon;
this.regex = regex;
this.priority = priority;
}
public String getID()
{
return id;
}
public String getName()
{
return name;
}
public ImageDescriptor getIcon()
{
return icon;
}
public final int getPriority()
{
return priority;
}
public final String getRegex()
{
return regex;
}
public final boolean matchesRegex(URI uri)
{
synchronized (regex)
{
if (pattern == null)
{
pattern = Pattern.compile(regex);
}
}
Matcher matcher = pattern.matcher(uri.toString());
return matcher.matches();
}
public IEditorPart openEditor(final IWorkbenchPage page, URI uri)
{
final Set<IEditorPart> editors = new HashSet<IEditorPart>();
final IEditorPart[] editor = { null };
IPartListener partListener = new IPartListener()
{
public void partClosed(IWorkbenchPart part)
{
if (part == editor[0])
{
try
{
// view.close();
}
catch (Exception ex)
{
OM.LOG.error(ex);
}
finally
{
page.removePartListener(this);
}
}
}
public void partOpened(IWorkbenchPart part)
{
// Do nothing.
}
public void partDeactivated(IWorkbenchPart part)
{
// Do nothing.
}
public void partBroughtToTop(IWorkbenchPart part)
{
// Do nothing.
}
public void partActivated(IWorkbenchPart part)
{
// Do nothing.
}
};
page.addPartListener(partListener);
editor[0] = doOpenEditor(page, uri);
if (!editors.contains(editor))
{
// The editor must have been open already and someone else will handle close.
page.removePartListener(partListener);
}
return editor[0];
}
protected abstract IEditorPart doOpenEditor(IWorkbenchPage page, URI uri);
@Override
public String toString()
{
return id + "[" + regex + "]";
}
}
/**
* @author Eike Stepper
* @since 4.4
* @noextend This class is not intended to be subclassed by clients.
* @noinstantiate This class is not intended to be instantiated by clients.
*/
public static class Registry extends Container<CDOEditorOpener>
{
public static final Registry INSTANCE = new Registry();
private static final String EXT_POINT = "editorOpeners"; //$NON-NLS-1$
private final Map<String, CDOEditorOpener> editorOpeners = new HashMap<String, CDOEditorOpener>();
public Registry()
{
}
public IEditorPart openEditor(IWorkbenchPage page, URI uri)
{
if (uri == null)
{
return null;
}
for (CDOEditorOpener editorOpener : getEditorOpeners(uri))
{
IEditorPart editor = editorOpener.openEditor(page, uri);
if (editor != null)
{
return editor;
}
}
return null;
}
public CDOEditorOpener getEditorOpener(String id)
{
synchronized (editorOpeners)
{
return editorOpeners.get(id);
}
}
public CDOEditorOpener[] getEditorOpeners(URI uri)
{
List<CDOEditorOpener> result = new ArrayList<CDOEditorOpener>();
synchronized (editorOpeners)
{
for (CDOEditorOpener editorOpener : editorOpeners.values())
{
if (editorOpener.matchesRegex(uri))
{
result.add(editorOpener);
}
}
}
// Sort highest priority first
Collections.sort(result, new Comparator<CDOEditorOpener>()
{
public int compare(CDOEditorOpener o1, CDOEditorOpener o2)
{
return -Integer.valueOf(o1.getPriority()).compareTo(o2.getPriority());
}
});
return result.toArray(new CDOEditorOpener[result.size()]);
}
public void addEditorOpener(CDOEditorOpener editorOpener)
{
boolean added;
synchronized (editorOpeners)
{
String id = editorOpener.getID();
added = !editorOpeners.containsKey(id);
if (added)
{
editorOpeners.put(id, editorOpener);
}
}
if (added)
{
fireElementAddedEvent(editorOpener);
}
}
public void removeEditorOpener(CDOEditorOpener editorOpener)
{
boolean removed;
synchronized (editorOpeners)
{
String id = editorOpener.getID();
removed = editorOpeners.remove(id) != null;
}
if (removed)
{
fireElementRemovedEvent(editorOpener);
}
}
public CDOEditorOpener[] getElements()
{
synchronized (editorOpeners)
{
return editorOpeners.values().toArray(new CDOEditorOpener[editorOpeners.size()]);
}
}
@Override
public boolean isEmpty()
{
synchronized (editorOpeners)
{
return editorOpeners.isEmpty();
}
}
@Override
protected void doActivate() throws Exception
{
super.doActivate();
if (OMPlatform.INSTANCE.isOSGiRunning())
{
try
{
readExtensions();
}
catch (Throwable t)
{
OM.LOG.error(t);
}
}
}
public void readExtensions()
{
IExtensionRegistry registry = Platform.getExtensionRegistry();
IConfigurationElement[] configurationElements = registry.getConfigurationElementsFor(OM.BUNDLE_ID, EXT_POINT);
for (IConfigurationElement element : configurationElements)
{
try
{
EditorOpenerDescriptor descriptor = new EditorOpenerDescriptor(element);
addEditorOpener(descriptor);
}
catch (Exception ex)
{
OM.LOG.error(ex);
}
}
}
/**
* @author Eike Stepper
*/
public static final class EditorOpenerDescriptor extends CDOEditorOpener.Default
{
private IConfigurationElement element;
public EditorOpenerDescriptor(IConfigurationElement element)
{
super(getID(element), getName(element), getIcon(element), getRegex(element), getPriority(element));
this.element = element;
if (StringUtil.isEmpty(element.getAttribute("class"))) //$NON-NLS-1$
{
throw new IllegalArgumentException(MessageFormat.format("Class not defined for extension {0}", element)); //$NON-NLS-1$
}
}
@Override
protected IEditorPart doOpenEditor(IWorkbenchPage page, URI uri)
{
return getEditorOpener().openEditor(page, uri);
}
private CDOEditorOpener getEditorOpener()
{
try
{
return (CDOEditorOpener)element.createExecutableExtension("class"); //$NON-NLS-1$
}
catch (CoreException ex)
{
throw WrappedException.wrap(ex);
}
}
private static String getID(IConfigurationElement element)
{
String value = element.getAttribute("id"); //$NON-NLS-1$
if (StringUtil.isEmpty(value))
{
throw new IllegalArgumentException(MessageFormat.format("ID not defined for extension {0}", element)); //$NON-NLS-1$
}
return value;
}
private static String getName(IConfigurationElement element)
{
String value = element.getAttribute("name"); //$NON-NLS-1$
if (StringUtil.isEmpty(value))
{
throw new IllegalArgumentException(MessageFormat.format("Name not defined for extension {0}", element)); //$NON-NLS-1$
}
return value;
}
private static ImageDescriptor getIcon(IConfigurationElement element)
{
String icon = element.getAttribute("icon"); //$NON-NLS-1$
if (icon != null)
{
try
{
return AbstractUIPlugin.imageDescriptorFromPlugin(element.getNamespaceIdentifier(), icon);
}
catch (Exception ex)
{
//$FALL-THROUGH$
}
}
return null;
}
private static String getRegex(IConfigurationElement element)
{
String value = element.getAttribute("regex"); //$NON-NLS-1$
if (StringUtil.isEmpty(value))
{
throw new IllegalArgumentException(MessageFormat.format("Regex not defined for extension {0}", element)); //$NON-NLS-1$
}
return value;
}
private static int getPriority(IConfigurationElement element)
{
try
{
String value = element.getAttribute("priority"); //$NON-NLS-1$
return Integer.parseInt(value);
}
catch (Exception ex)
{
return DEFAULT_PRIORITY;
}
}
}
}
}