/* * 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; } } } } }