001: /*******************************************************************************
002: * Copyright (c) 2006, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.ui;
011:
012: import java.util.ArrayList;
013: import java.util.List;
014: import java.util.StringTokenizer;
015:
016: import org.eclipse.core.runtime.IAdaptable;
017: import org.eclipse.core.runtime.IAdapterManager;
018: import org.eclipse.core.runtime.IConfigurationElement;
019: import org.eclipse.core.runtime.IExecutableExtension;
020: import org.eclipse.core.runtime.Platform;
021: import org.eclipse.ui.internal.WorkbenchPlugin;
022: import org.osgi.framework.Bundle;
023: import org.osgi.service.packageadmin.ExportedPackage;
024: import org.osgi.service.packageadmin.PackageAdmin;
025: import org.osgi.util.tracker.ServiceTracker;
026:
027: /**
028: * Basic IWorkingSetElementAdapter implementation that allows plugins to decribe
029: * simple declarative element adapters.
030: * <p>
031: * The executable extension format for this class is as follows:<br/>
032: * <code><workingSet
033: * elementAdapterClass="org.eclipse.ui.BasicWorkingSetElementAdapter:class1.to.adapt.to[;option1=value1][;option2=value2],class2.to.adapt.to[;option1=value1][;option2=value2],...">
034: * ... </workingSet></code>
035: * </p>
036: * <p>
037: * The valid options are:<br/>
038: * <dl>
039: * <dt>adapt</dt>
040: * <dd>Values: <code>true</code> or <code>true</code>. Specifies whether
041: * or not the platform {@link org.eclipse.core.runtime.IAdapterManager} and the
042: * {@link org.eclipse.core.runtime.IAdaptable} interface should be consulted.</dd>
043: * </dl>
044: * </p>
045: *
046: * Please see the {@link #adaptElements(IWorkingSet, IAdaptable[])} method for
047: * details on behavior of this implementation.
048: *
049: * @since 3.3
050: */
051: public final class BasicWorkingSetElementAdapter implements
052: IWorkingSetElementAdapter, IExecutableExtension {
053:
054: private class Type {
055: private static final int NONE = 0;
056: private static final int ADAPT = 1;
057: String className;
058: int flags;
059: }
060:
061: private Type[] preferredTypes = new Type[0];
062:
063: private ServiceTracker packageTracker;
064:
065: /**
066: * When invoked this method will iterate over all classes specified as
067: * IExecutableExtension arguements to this class in order and compare with
068: * the elements. If the element is directly assignable to the provided class
069: * then it is added to the result array as is. If the class has specified
070: * "adapt=true" as an argument and there is an available adapter in the
071: * platform IAdapterManager then it is returned. Finally, if "adapt=true"
072: * and the class is already loaded (determined by inspecting exported
073: * bundles via the platform PackageAdmin) a direct query for the adapter is
074: * made on the object and if it is not <code>null</code> then it is
075: * returned.
076: * <p>
077: * A consequence of the above is that it is possible for this method to
078: * return differing results based on the state of bundles loaded within the
079: * system.
080: * </p>
081: *
082: * @see org.eclipse.ui.IWorkingSetElementAdapter#adaptElements(org.eclipse.ui.IWorkingSet,
083: * org.eclipse.core.runtime.IAdaptable[])
084: * @see org.eclipse.core.runtime.IAdapterManager#getAdapter(Object, String)
085: * @see org.osgi.service.packageadmin.PackageAdmin#getExportedPackage(String)
086: */
087: public IAdaptable[] adaptElements(IWorkingSet ws,
088: IAdaptable[] elements) {
089: List adaptedElements = new ArrayList();
090: for (int i = 0; i < elements.length; i++) {
091: IAdaptable adaptable = adapt(elements[i]);
092: if (adaptable != null)
093: adaptedElements.add(adaptable);
094: }
095:
096: return (IAdaptable[]) adaptedElements
097: .toArray(new IAdaptable[adaptedElements.size()]);
098: }
099:
100: /**
101: * Adapt the given adaptable. Compares the given adaptable against the list
102: * of desired types and returns the first type that generates a match.
103: *
104: * @param adaptable
105: * the adaptable to adapt
106: * @return the resultant adaptable. May be the same adaptable, a new
107: * adaptable, or <code>null</code>.
108: */
109: private IAdaptable adapt(IAdaptable adaptable) {
110: for (int i = 0; i < preferredTypes.length; i++) {
111: IAdaptable adaptedAdaptable = adapt(preferredTypes[i],
112: adaptable);
113: if (adaptedAdaptable != null)
114: return adaptedAdaptable;
115: }
116: return null;
117: }
118:
119: /**
120: * Adapt the given adaptable given the reference type.
121: *
122: * @param type
123: * the reference type
124: * @param adaptable
125: * the adaptable to adapt
126: * @return the resultant adaptable. May be the same adaptable, a new
127: * adaptable, or <code>null</code>.
128: */
129: private IAdaptable adapt(Type type, IAdaptable adaptable) {
130: IAdapterManager adapterManager = Platform.getAdapterManager();
131: Class[] directClasses = adapterManager
132: .computeClassOrder(adaptable.getClass());
133: for (int i = 0; i < directClasses.length; i++) {
134: Class clazz = directClasses[i];
135: if (clazz.getName().equals(type.className))
136: return adaptable;
137: }
138:
139: if ((type.flags & Type.ADAPT) != 0) {
140: Object adapted = adapterManager.getAdapter(adaptable,
141: type.className);
142: if (adapted instanceof IAdaptable)
143: return (IAdaptable) adapted;
144:
145: PackageAdmin admin = getPackageAdmin();
146: if (admin != null) {
147: int lastDot = type.className.lastIndexOf('.');
148: if (lastDot > 0) { // this lives in a package
149: String packageName = type.className.substring(0,
150: lastDot);
151: ExportedPackage[] packages = admin
152: .getExportedPackages(packageName);
153: if (packages != null && packages.length == 1) {
154: // if there is exactly one exporter of this
155: // package
156: // we can go further
157: if (packages[0].getExportingBundle().getState() == Bundle.ACTIVE) {
158: try {
159: // if the bundle is loaded we can safely get the
160: // class object and check for an adapter on the
161: // object directly
162: adapted = adaptable
163: .getAdapter(packages[0]
164: .getExportingBundle()
165: .loadClass(
166: type.className));
167: if (adapted instanceof IAdaptable)
168: return (IAdaptable) adapted;
169:
170: } catch (ClassNotFoundException e) {
171: WorkbenchPlugin.log(e);
172: }
173: }
174: }
175: }
176: }
177: }
178: return null;
179: }
180:
181: /*
182: * (non-Javadoc)
183: *
184: * @see org.eclipse.ui.IWorkingSetElementAdapter#dispose()
185: */
186: public void dispose() {
187: if (packageTracker != null)
188: packageTracker.close();
189: }
190:
191: /*
192: * (non-Javadoc)
193: *
194: * @see org.eclipse.core.runtime.IExecutableExtension#setInitializationData(org.eclipse.core.runtime.IConfigurationElement,
195: * java.lang.String, java.lang.Object)
196: */
197: public void setInitializationData(IConfigurationElement config,
198: String propertyName, Object data) {
199:
200: if (data instanceof String) {
201: List preferredTypes = new ArrayList(0);
202: for (StringTokenizer toker = new StringTokenizer(
203: (String) data, ","); toker.hasMoreTokens();) {//$NON-NLS-1$
204: String classNameAndOptions = toker.nextToken();
205: Type record = new Type();
206: parseOptions(classNameAndOptions, record);
207: preferredTypes.add(record);
208: }
209: this .preferredTypes = (Type[]) preferredTypes
210: .toArray(new Type[preferredTypes.size()]);
211: }
212: }
213:
214: /**
215: * Parse classname/option strings in the form:<br/>
216: * <code>some.package.Class[:option1=value1][:option2=value2]...
217: *
218: * @param classNameAndOptions the class name and possibly options to parse
219: * @param record the record to fill
220: */
221: private void parseOptions(String classNameAndOptions, Type record) {
222: for (StringTokenizer toker = new StringTokenizer(
223: classNameAndOptions, ";"); toker.hasMoreTokens();) { //$NON-NLS-1$
224: String token = toker.nextToken();
225: if (record.className == null)
226: record.className = token;
227: else {
228: for (StringTokenizer pair = new StringTokenizer(token,
229: "="); pair.hasMoreTokens();) {//$NON-NLS-1$
230: if (pair.countTokens() == 2) {
231: String param = pair.nextToken();
232: String value = pair.nextToken();
233: if ("adapt".equals(param)) { //$NON-NLS-1$
234: record.flags ^= "true".equals(value) ? Type.ADAPT : Type.NONE; //$NON-NLS-1$
235: }
236: }
237: }
238: }
239: }
240: }
241:
242: /**
243: * Prime the PackageAdmin service tracker and return the service (if
244: * available).
245: *
246: * @return the PackageAdmin service or null if it is not available
247: */
248: private PackageAdmin getPackageAdmin() {
249: if (packageTracker == null) {
250: packageTracker = new ServiceTracker(WorkbenchPlugin
251: .getDefault().getBundleContext(),
252: PackageAdmin.class.getName(), null);
253: packageTracker.open();
254: }
255:
256: return (PackageAdmin) packageTracker.getService();
257: }
258: }
|