1 /******************************************************************************* 2 * Copyright (c) 2000, 2015 IBM Corporation and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * IBM Corporation - initial API and implementation 13 *******************************************************************************/ 14 15 package org.eclipse.ui.texteditor; 16 17 import java.util.ArrayList; 18 import java.util.Arrays; 19 import java.util.Comparator; 20 import java.util.HashMap; 21 import java.util.HashSet; 22 import java.util.Iterator; 23 import java.util.List; 24 import java.util.Map; 25 import java.util.Map.Entry; 26 import java.util.Set; 27 28 import org.osgi.framework.Bundle; 29 import org.osgi.framework.BundleException; 30 import org.osgi.framework.Constants; 31 32 import org.eclipse.osgi.util.ManifestElement; 33 34 import org.eclipse.core.runtime.Assert; 35 import org.eclipse.core.runtime.IConfigurationElement; 36 import org.eclipse.core.runtime.IExtension; 37 import org.eclipse.core.runtime.IStatus; 38 import org.eclipse.core.runtime.Platform; 39 import org.eclipse.core.runtime.Status; 40 41 import org.eclipse.ui.internal.texteditor.TextEditorPlugin; 42 43 /** 44 * Allows to sort an array based on their elements' configuration elements 45 * according to the prerequisite relation of their defining plug-ins. 46 * <p> 47 * This class may be subclassed. 48 * </p> 49 * 50 * @since 3.0 51 */ 52 public abstract class ConfigurationElementSorter { 53 54 /** 55 * Sorts the given array based on its elements' configuration elements 56 * according to the prerequisite relation of their defining plug-ins. 57 * 58 * @param elements the array to be sorted 59 */ sort(Object[] elements)60 public final void sort(Object[] elements) { 61 Arrays.sort(elements, new ConfigurationElementComparator(elements)); 62 } 63 64 /** 65 * Returns the configuration element for the given object. 66 * 67 * @param object the object 68 * @return the object's configuration element, must not be <code>null</code> 69 */ getConfigurationElement(Object object)70 public abstract IConfigurationElement getConfigurationElement(Object object); 71 72 /** 73 * Compare configuration elements according to the prerequisite relation 74 * of their defining plug-ins. 75 */ 76 private class ConfigurationElementComparator implements Comparator<Object> { 77 78 private Map<Object, String> fDescriptorMapping; 79 private Map<String, Set<String>> fPrereqsMapping; 80 ConfigurationElementComparator(Object[] elements)81 public ConfigurationElementComparator(Object[] elements) { 82 Assert.isNotNull(elements); 83 initialize(elements); 84 } 85 86 @Override compare(Object object0, Object object1)87 public int compare(Object object0, Object object1) { 88 89 if (dependsOn(object0, object1)) 90 return -1; 91 92 if (dependsOn(object1, object0)) 93 return +1; 94 95 return 0; 96 } 97 98 /** 99 * Returns whether one configuration element depends on the other element. 100 * This is done by checking the dependency chain of the defining plug-ins. 101 * 102 * @param element0 the first element 103 * @param element1 the second element 104 * @return <code>true</code> if <code>element0</code> depends on <code>element1</code>. 105 * @since 2.0 106 */ dependsOn(Object element0, Object element1)107 private boolean dependsOn(Object element0, Object element1) { 108 if (element0 == null || element1 == null) 109 return false; 110 111 String pluginDesc0= fDescriptorMapping.get(element0); 112 String pluginDesc1= fDescriptorMapping.get(element1); 113 114 // performance tuning - code below would give same result 115 if (pluginDesc0.equals(pluginDesc1)) 116 return false; 117 118 Set<String> prereqUIds0= fPrereqsMapping.get(pluginDesc0); 119 120 return prereqUIds0.contains(pluginDesc1); 121 } 122 123 /** 124 * Initialize this comparator. 125 * 126 * @param elements an array of Java editor hover descriptors 127 */ initialize(Object[] elements)128 private void initialize(Object[] elements) { 129 int length= elements.length; 130 fDescriptorMapping= new HashMap<>(length); 131 fPrereqsMapping= new HashMap<>(length); 132 Set<Bundle> fBundleSet= new HashSet<>(length); 133 134 for (int i= 0; i < length; i++) { 135 IConfigurationElement configElement= getConfigurationElement(elements[i]); 136 Bundle bundle= Platform.getBundle(configElement.getContributor().getName()); 137 fDescriptorMapping.put(elements[i], bundle.getSymbolicName()); 138 fBundleSet.add(bundle); 139 } 140 141 Iterator<Bundle> iter= fBundleSet.iterator(); 142 while (iter.hasNext()) { 143 Bundle bundle= iter.next(); 144 List<Bundle> toTest= new ArrayList<>(fBundleSet); 145 toTest.remove(bundle); 146 Set<String> prereqUIds= new HashSet<>(Math.max(0, toTest.size() - 1)); 147 fPrereqsMapping.put(bundle.getSymbolicName(), prereqUIds); 148 149 String requires = bundle.getHeaders().get(Constants.REQUIRE_BUNDLE); 150 ManifestElement[] manifestElements; 151 try { 152 manifestElements = ManifestElement.parseHeader(Constants.REQUIRE_BUNDLE, requires); 153 } catch (BundleException e) { 154 String uid= getExtensionPointUniqueIdentifier(bundle); 155 String message= "ConfigurationElementSorter for '" + uid + "': getting required plug-ins for '" + bundle.getSymbolicName() + "' failed"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ 156 Status status= new Status(IStatus.ERROR, TextEditorPlugin.PLUGIN_ID, IStatus.OK, message, e); 157 TextEditorPlugin.getDefault().getLog().log(status); 158 continue; 159 } 160 161 if (manifestElements == null) 162 continue; 163 164 int i= 0; 165 while (i < manifestElements.length && !toTest.isEmpty()) { 166 String prereqUId= manifestElements[i].getValue(); 167 for (int j= 0; j < toTest.size();) { 168 Bundle toTest_j= toTest.get(j); 169 if (toTest_j.getSymbolicName().equals(prereqUId)) { 170 toTest.remove(toTest_j); 171 prereqUIds.add(toTest_j.getSymbolicName()); 172 } else 173 j++; 174 } 175 i++; 176 } 177 } 178 } 179 180 /** 181 * Returns the unique extension point identifier for the 182 * configuration element which belongs to the given bundle. 183 * 184 * @param bundle the bundle 185 * @return the unique extension point identifier or "unknown" if not found 186 * @since 3.0.1 187 */ getExtensionPointUniqueIdentifier(Bundle bundle)188 private String getExtensionPointUniqueIdentifier(Bundle bundle) { 189 if (bundle != null) { 190 String bundleName= bundle.getSymbolicName(); 191 if (bundleName != null) { 192 Set<Entry<Object, String>> entries= fDescriptorMapping.entrySet(); 193 Iterator<Entry<Object, String>> iter= entries.iterator(); 194 while (iter.hasNext()) { 195 Entry<Object, String> entry= iter.next(); 196 if (bundleName.equals(entry.getValue())) { 197 IExtension extension = getConfigurationElement(entry.getKey()).getDeclaringExtension(); 198 return extension.getExtensionPointUniqueIdentifier(); 199 } 200 } 201 } 202 } 203 return "unknown"; //$NON-NLS-1$ 204 } 205 206 } 207 } 208