/*******************************************************************************
* 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.ui.wizards;
import java.util.*;
import org.eclipse.core.runtime.*;
import org.eclipse.jface.util.SafeRunnable;
import org.eclipse.php.internal.ui.PHPUiPlugin;
import org.eclipse.php.ui.wizards.ICompositeFragmentFactory;
/**
* Server wizard fragments registry for all the wizardAndCompositeFragments
* extentions.
*/
public class WizardFragmentsFactoryRegistry {
private static final String EXTENSION_POINT_NAME = "wizardAndCompositeFragments"; //$NON-NLS-1$
private static final String FRAGMENT_TAG = "wizardAndCompositeFragment"; //$NON-NLS-1$
private static final String ID_ATTRIBUTE = "id"; //$NON-NLS-1$
private static final String FRAGMENTS_GROUP_ID = "fragmentsGroupID"; //$NON-NLS-1$
private static final String CLASS_ATTRIBUTE = "class"; //$NON-NLS-1$
private static final String PLACE_AFTER_ATTRIBUTE = "placeAfter"; //$NON-NLS-1$
// Hold a Dictionary of Lists that contains the factories used for the
// creation of the fragments.
// This structure will be deleted from the memory once all the factories
// were created.
private Map<String, List<FragmentsFactory>> fragments;
private static WizardFragmentsFactoryRegistry instance;
private Map<String, Map<String, ICompositeFragmentFactory>> factories;
public static Map<String, ICompositeFragmentFactory> getFragmentsFactories(String fragmentsGroupID) {
WizardFragmentsFactoryRegistry registry = getInstance();
Map<String, ICompositeFragmentFactory> factories = registry.factories.get(fragmentsGroupID);
if (factories == null) {
factories = new LinkedHashMap<>();
List<FragmentsFactory> fragments = registry.fragments.get(fragmentsGroupID);
for (int i = 0; i < fragments.size(); i++) {
FragmentsFactory factory = fragments.get(i);
factories.put(factory.getID(), factory.createFragmentFactory());
}
registry.factories.put(fragmentsGroupID, factories);
// Clear the fragments mapping, since it is no longer needed.
registry.fragments.remove(fragmentsGroupID);
}
return factories;
}
private WizardFragmentsFactoryRegistry() {
factories = new HashMap<>();
fragments = new HashMap<>();
IExtensionRegistry registry = Platform.getExtensionRegistry();
IConfigurationElement[] elements = registry.getConfigurationElementsFor(PHPUiPlugin.ID, EXTENSION_POINT_NAME);
for (int i = 0; i < elements.length; i++) {
final IConfigurationElement element = elements[i];
if (FRAGMENT_TAG.equals(element.getName())) {
String id = element.getAttribute(ID_ATTRIBUTE);
String groupID = element.getAttribute(FRAGMENTS_GROUP_ID);
String placeAfter = element.getAttribute(PLACE_AFTER_ATTRIBUTE);
List<FragmentsFactory> list = fragments.get(groupID);
if (list == null) {
list = new ArrayList<>();
fragments.put(groupID, list);
}
if (element.getNamespaceIdentifier().equals(PHPUiPlugin.ID)) {
// Make sure that extentions that exists in this plugin will
// appear ahead of all others
// when the user-class calls for getFragmentsFactories().
list.add(0, new FragmentsFactory(element, id, placeAfter));
} else {
list.add(new FragmentsFactory(element, id, placeAfter));
}
}
}
// Sort all the fragment groups
Iterator<String> keys = fragments.keySet().iterator();
while (keys.hasNext()) {
sortFragmentsByPlace(fragments.get(keys.next()));
}
}
// Sort the fragments according to the 'place-after' attribute
private void sortFragmentsByPlace(List<FragmentsFactory> fragments) {
// Scan the fragments and separate the fragments that lacks the
// place-after property from
// those that have it.
List<List<FragmentsFactory>> rootsFragments = new ArrayList<>();
List<List<FragmentsFactory>> nonRootFragments = new ArrayList<>();
for (int i = 0; i < fragments.size(); i++) {
FragmentsFactory factory = fragments.get(i);
if (factory.getPlaceAfter() == null || factory.getPlaceAfter().equals("")) { //$NON-NLS-1$
addAsList(rootsFragments, factory);
} else {
addAsList(nonRootFragments, factory);
}
}
// Traverse over the non-root fragments and position them.
for (int i = 0; i < nonRootFragments.size(); i++) {
List<FragmentsFactory> fragmentsGroup = nonRootFragments.get(i);
// try to move it to the roots fragments first (order is important).
boolean moved = placeFragment(rootsFragments, fragmentsGroup);
if (!moved) {
// in case we can't find it there, try to move it inside the
// non-roots fragments.
moved = placeFragment(nonRootFragments, fragmentsGroup);
}
if (!moved) {
// move it to the roots anyway, since there is an error in the
// extention definitions.
FragmentsFactory invalidFactory = getFactory(nonRootFragments, i);
addAsList(rootsFragments, invalidFactory);
PHPUiPlugin.log(new Status(IStatus.WARNING, PHPUiPlugin.ID, 0,
"Invalid 'placeAfter' id (" + invalidFactory.getPlaceAfter() + ')', null)); //$NON-NLS-1$
}
}
// At this stage, the root fragments should hold all the fragments
// sorted.
fragments.clear();
for (int i = 0; i < rootsFragments.size(); i++) {
List<FragmentsFactory> list = rootsFragments.get(i);
for (int j = 0; j < list.size(); j++) {
fragments.add(list.get(j));
}
}
}
private boolean placeFragment(List<List<FragmentsFactory>> targetFactories, List<FragmentsFactory> factoriesGroup) {
if (factoriesGroup == null || factoriesGroup.size() == 0) {
return true;
}
FragmentsFactory factory = factoriesGroup.get(0);
String placeAfter = factory.getPlaceAfter();
for (int i = 0; i < targetFactories.size(); i++) {
List<FragmentsFactory> list = targetFactories.get(i);
for (int j = 0; j < list.size(); j++) {
FragmentsFactory nextFactory = list.get(j);
if (nextFactory.getID().equals(placeAfter)) {
// This list is the list we should add to
if (list.size() > j + 1) {
list.addAll(j + 1, factoriesGroup);
} else {
// add it to the end
list.addAll(factoriesGroup);
}
return true;
}
}
}
return false;
}
private FragmentsFactory getFactory(List<List<FragmentsFactory>> nonRootFragments, int i) {
List<FragmentsFactory> list = nonRootFragments.get(i);
return list.get(0);
}
// add an element to a List by wrapping it in another List.
private void addAsList(List<List<FragmentsFactory>> target, FragmentsFactory element) {
List<FragmentsFactory> list = new ArrayList<>();
list.add(element);
target.add(list);
}
private static WizardFragmentsFactoryRegistry getInstance() {
if (instance == null) {
instance = new WizardFragmentsFactoryRegistry();
}
return instance;
}
private class FragmentsFactory {
private IConfigurationElement element;
private ICompositeFragmentFactory factory;
private String id;
private String placeAfter;
public FragmentsFactory(IConfigurationElement element, String id, String placeAfter) {
this.element = element;
this.id = id;
this.placeAfter = placeAfter;
}
public ICompositeFragmentFactory createFragmentFactory() {
SafeRunner.run(new SafeRunnable(
"Error creation extension for extension-point org.eclipse.php.server.ui.wizardAndCompositeFragments") { //$NON-NLS-1$
@Override
public void run() throws Exception {
factory = (ICompositeFragmentFactory) element.createExecutableExtension(CLASS_ATTRIBUTE);
}
});
return factory;
}
public String getID() {
return id;
}
public String getPlaceAfter() {
return placeAfter;
}
}
}