/******************************************************************************* * Copyright (c) 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 * Zend Technologies *******************************************************************************/ package org.eclipse.php.internal.debug.ui.launching; import java.util.ArrayList; import java.util.List; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IExtensionRegistry; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.SafeRunner; import org.eclipse.debug.ui.AbstractLaunchConfigurationTab; import org.eclipse.jface.util.SafeRunnable; import org.eclipse.php.internal.debug.ui.Logger; import org.eclipse.php.internal.debug.ui.PHPDebugUIPlugin; public class LaunchConfigurationsTabsRegistry { private static final String EXTENSION_POINT_NAME = "launchConfigurationTabs"; //$NON-NLS-1$ private static final String TAB_TAG = "launchConfigurationTab"; //$NON-NLS-1$ private static final String ID_ATTRIBUTE = "id"; //$NON-NLS-1$ private static final String GROUP_ID_ATTRIBUTE = "launchConfigurationTabGroupId"; //$NON-NLS-1$ private static final String CLASS_ATTRIBUTE = "class"; //$NON-NLS-1$ private static final String PLACE_AFTER_ATTRIBUTE = "placeAfter"; //$NON-NLS-1$ private static final String MODES_ATTRIBUTE = "modes"; //$NON-NLS-1$ // Hold a Dictionary of Lists that contains the factories used for the // creation of the tabs. private List factories = new ArrayList(5); private static LaunchConfigurationsTabsRegistry instance; /** * Returns an array on newly initialized AbstractLaunchConfigurationTab that * are registered for the requested configuration tab group. * * @param launchConfigurationTabGroupId * The group id for the requested launch configuration tabs. * @param mode * The launch mode requested. * @return An array of AbstractLaunchConfigurationTab. */ public static AbstractLaunchConfigurationTab[] getLaunchTabs(String launchConfigurationTabGroupId, String mode) { LaunchConfigurationsTabsRegistry registry = getInstance(); List fragments = registry.factories; List factoriesList = new ArrayList(); for (int i = 0; i < fragments.size(); i++) { TabFactory factory = (TabFactory) fragments.get(i); boolean modeOK = factory.getModes().length() == 0 || factory.getModes().indexOf(mode) > -1; // Sort out only the tabs that are related to the requested // configuration tab group and launch mode. if (factory.getGroupID().equals(launchConfigurationTabGroupId) && modeOK) { factoriesList.add(factory.createFragmentFactory()); } } AbstractLaunchConfigurationTab[] tabs = new AbstractLaunchConfigurationTab[factoriesList.size()]; factoriesList.toArray(tabs); return tabs; } private LaunchConfigurationsTabsRegistry() { IExtensionRegistry registry = Platform.getExtensionRegistry(); IConfigurationElement[] elements = registry.getConfigurationElementsFor(PHPDebugUIPlugin.ID, EXTENSION_POINT_NAME); ArrayList mightOverride = new ArrayList(5); for (int i = 0; i < elements.length; i++) { final IConfigurationElement element = elements[i]; if (TAB_TAG.equals(element.getName())) { String id = element.getAttribute(ID_ATTRIBUTE); String groupId = element.getAttribute(GROUP_ID_ATTRIBUTE); String placeAfter = element.getAttribute(PLACE_AFTER_ATTRIBUTE); String modes = element.getAttribute(MODES_ATTRIBUTE); if (element.getNamespaceIdentifier().equals(PHPDebugUIPlugin.ID) || element.getNamespaceIdentifier().startsWith("org.eclipse.php.server.")) { //$NON-NLS-1$ // Make sure that extentions that exists in this plugin will // appear ahead of all others // when the user-class calls for getLaunchTabs(). factories.add(0, new TabFactory(element, groupId, id, placeAfter, modes)); } else { boolean override = false; for (int j = 0; !override && j < factories.size(); j++) { // Check for overriding id's. // Override as needed when the element namespace // identifier is from // an external plugin. The procedure will take the first // overidden id. TabFactory factory = (TabFactory) factories.get(j); if (id.equals(factory.id)) { override = true; if (!element.getNamespaceIdentifier().startsWith("org.eclipse.php.")) { //$NON-NLS-1$ factories.remove(j); factories.add(new TabFactory(element, groupId, id, placeAfter, modes)); break; } } } if (!override) { mightOverride.add(new TabFactory(element, groupId, id, placeAfter, modes)); } } } } // Check again for overriding extensions. // This check is needed since we cannot trust the extension loading // order. for (int i = 0; i < mightOverride.size(); i++) { TabFactory tag = (TabFactory) mightOverride.get(i); int index = factories.indexOf(tag); if (index > -1) { // Found one that needs to be overidden. factories.set(index, tag); } else { // Did not found any to override, so simply add it. factories.add(tag); } } sortFragmentsByPlace(); } // Sort the factories according to the 'placeAfter' attribute private void sortFragmentsByPlace() { // Scan the factories and separate the factories that lacks the // place-after property from // those that have it. ArrayList rootsFragments = new ArrayList(factories.size()); ArrayList nonRootFragments = new ArrayList(factories.size()); for (int i = 0; i < factories.size(); i++) { TabFactory factory = (TabFactory) factories.get(i); if (factory.getPlaceAfter() == null || factory.getPlaceAfter().equals("")) { //$NON-NLS-1$ addAsList(rootsFragments, factory); } else { addAsList(nonRootFragments, factory); } } // Traverse over the non-root factories and position them. for (int i = 0; i < nonRootFragments.size(); i++) { TabFactory factory = getFactory(nonRootFragments, i); // try to move it to the roots factories first (order is important). boolean moved = placeFragment(rootsFragments, factory); if (!moved) { // in case we can't find it there, try to move it inside the // non-roots factories. moved = placeFragment(nonRootFragments, factory); } if (!moved) { // move it to the roots anyway, since there is an error in the // extention definitions. addAsList(rootsFragments, factory); Logger.log(Logger.WARNING, "Invalid 'placeAfter' id (" + factory.getPlaceAfter() + ')'); //$NON-NLS-1$ } } // At this stage, the root fragments should hold all the fragments // sorted. factories.clear(); for (int i = 0; i < rootsFragments.size(); i++) { List list = (List) rootsFragments.get(i); for (int j = 0; j < list.size(); j++) { factories.add(list.get(j)); } } } private boolean placeFragment(List factories, TabFactory factory) { String placeAfter = factory.getPlaceAfter(); for (int i = 0; i < factories.size(); i++) { List list = (List) factories.get(i); for (int j = 0; j < list.size(); j++) { TabFactory nextFactory = (TabFactory) list.get(j); if (nextFactory.getID().equals(placeAfter)) { // This list is the list we should add to list.add(factory); return true; } } } return false; } private TabFactory getFactory(List nonRootFragments, int i) { List list = (List) nonRootFragments.get(i); return (TabFactory) list.get(0); } // add an element to a List by wrapping it in another List. private void addAsList(List target, Object element) { List list = new ArrayList(3); list.add(element); target.add(list); } private static LaunchConfigurationsTabsRegistry getInstance() { if (instance == null) { instance = new LaunchConfigurationsTabsRegistry(); } return instance; } private class TabFactory { private IConfigurationElement element; private AbstractLaunchConfigurationTab factory; private String id; private String groupId; private String placeAfter; private String modes; public TabFactory(IConfigurationElement element, String groupId, String id, String placeAfter, String modes) { this.element = element; this.groupId = groupId; this.id = id; this.placeAfter = placeAfter; this.modes = modes; } public AbstractLaunchConfigurationTab createFragmentFactory() { SafeRunner.run(new SafeRunnable( "Error creation extension for extension-point org.eclipse.php.server.ui.serverTabs") { //$NON-NLS-1$ public void run() throws Exception { factory = (AbstractLaunchConfigurationTab) element.createExecutableExtension(CLASS_ATTRIBUTE); } }); return factory; } public String getID() { return id; } public String getPlaceAfter() { return placeAfter; } public String getGroupID() { return groupId; } public String getModes() { if (modes == null) { modes = ""; //$NON-NLS-1$ } return modes; } public boolean equals(Object other) { if (other == this) { return true; } if (other instanceof TabFactory) { return getID().equals(((TabFactory) other).getID()); } return false; } } }