001: /*******************************************************************************
002: * Copyright (c) 2000, 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.pde.internal.core;
011:
012: import java.util.ArrayList;
013: import java.util.Collections;
014: import java.util.HashMap;
015: import java.util.Iterator;
016: import java.util.ListIterator;
017: import java.util.Map;
018: import java.util.TreeMap;
019:
020: import org.eclipse.core.resources.IProject;
021: import org.eclipse.core.resources.IResource;
022: import org.eclipse.core.runtime.CoreException;
023: import org.eclipse.core.runtime.NullProgressMonitor;
024: import org.eclipse.jdt.core.IClasspathContainer;
025: import org.eclipse.jdt.core.IJavaProject;
026: import org.eclipse.jdt.core.JavaCore;
027: import org.eclipse.jdt.core.JavaModelException;
028: import org.eclipse.osgi.service.resolver.BundleDelta;
029: import org.eclipse.osgi.service.resolver.BundleDescription;
030: import org.eclipse.osgi.service.resolver.HostSpecification;
031: import org.eclipse.osgi.service.resolver.StateDelta;
032: import org.eclipse.pde.core.IModel;
033: import org.eclipse.pde.core.IModelProviderEvent;
034: import org.eclipse.pde.core.IModelProviderListener;
035: import org.eclipse.pde.core.build.IBuild;
036: import org.eclipse.pde.core.build.IBuildEntry;
037: import org.eclipse.pde.core.plugin.IPluginModel;
038: import org.eclipse.pde.core.plugin.IPluginModelBase;
039: import org.eclipse.pde.core.plugin.ModelEntry;
040:
041: public class PluginModelManager implements IModelProviderListener {
042:
043: /**
044: * Subclass of ModelEntry
045: * It adds methods that add/remove model from the entry.
046: * These methods must not be on ModelEntry itself because
047: * ModelEntry is an API class and we do not want clients to manipulate
048: * the ModelEntry
049: *
050: */
051: private class LocalModelEntry extends ModelEntry {
052:
053: /**
054: * Constructs a model entry that will keep track
055: * of all bundles in the workspace and target that share the same ID.
056: *
057: * @param id the bundle ID
058: */
059: public LocalModelEntry(String id) {
060: super (id);
061: }
062:
063: /**
064: * Adds a model to the entry.
065: * An entry keeps two lists: one for workspace models
066: * and one for target (external) models.
067: * If the model being added is associated with a workspace resource,
068: * it is added to the workspace list; otherwise, it is added to the external list.
069: *
070: * @param model model to be added to the entry
071: */
072: public void addModel(IPluginModelBase model) {
073: if (model.getUnderlyingResource() != null)
074: fWorkspaceEntries.add(model);
075: else
076: fExternalEntries.add(model);
077: }
078:
079: /**
080: * Removes the given model for the workspace list if the model is associated
081: * with workspace resource. Otherwise, it is removed from the external list.
082: *
083: * @param model model to be removed from the model entry
084: */
085: public void removeModel(IPluginModelBase model) {
086: if (model.getUnderlyingResource() != null)
087: fWorkspaceEntries.remove(model);
088: else
089: fExternalEntries.remove(model);
090: }
091: }
092:
093: private ExternalModelManager fExternalManager; // keeps track of changes in target models
094: private WorkspacePluginModelManager fWorkspaceManager; // keeps track of changes in the workspace
095: private PDEState fState; // keeps the combined view of the target and workspace
096:
097: private Map fEntries; // a master table keyed by plugin ID and the value is a ModelEntry
098: private ArrayList fListeners; // a list of listeners interested in changes to the plug-in models
099: private ArrayList fStateListeners; // a list of listeners interested in changes to the PDE/resolver State
100:
101: /**
102: * Initialize the workspace and external (target) model manager
103: * and add listeners to each one
104: */
105: public PluginModelManager() {
106: fWorkspaceManager = new WorkspacePluginModelManager();
107: fExternalManager = new ExternalModelManager();
108: fExternalManager.addModelProviderListener(this );
109: fWorkspaceManager.addModelProviderListener(this );
110: }
111:
112: /**
113: * React to changes in plug-ins in the workspace and/or target
114: */
115: public void modelsChanged(IModelProviderEvent e) {
116: PluginModelDelta delta = new PluginModelDelta();
117:
118: // Removes from the master table and the state all workspace plug-ins that have been
119: // removed (project closed/deleted) from the workspace.
120: // Also if the taget location changes, all models from the old target are removed
121: if ((e.getEventTypes() & IModelProviderEvent.MODELS_REMOVED) != 0) {
122: IModel[] removed = e.getRemovedModels();
123: for (int i = 0; i < removed.length; i++) {
124: IPluginModelBase model = (IPluginModelBase) removed[i];
125: String id = model.getPluginBase().getId();
126: if (id != null)
127: handleRemove(id, model, delta);
128: }
129: }
130:
131: // reset the state
132: if ((e.getEventTypes() & IModelProviderEvent.TARGET_CHANGED) != 0) {
133: Object newState = e.getEventSource();
134: if (newState instanceof PDEState) {
135: fState = (PDEState) newState;
136: }
137: }
138:
139: // Adds to the master table and the state newly created plug-ins in the workspace
140: // (ie. new plug-in project or a closed project that has just been re-opened).
141: // Also, if the target location changes, we add all plug-ins from the new target
142: if ((e.getEventTypes() & IModelProviderEvent.MODELS_ADDED) != 0) {
143: IModel[] added = e.getAddedModels();
144: for (int i = 0; i < added.length; i++) {
145: IPluginModelBase model = (IPluginModelBase) added[i];
146: String id = model.getPluginBase().getId();
147: if (id != null)
148: handleAdd(id, model, delta);
149: }
150: }
151:
152: // add workspace plug-ins to the new state
153: // and remove their target counterparts from the state.
154: if ((e.getEventTypes() & IModelProviderEvent.TARGET_CHANGED) != 0) {
155: IPluginModelBase[] models = fWorkspaceManager
156: .getPluginModels();
157: for (int i = 0; i < models.length; i++) {
158: addWorkspaceBundleToState(models[i]);
159: }
160: if (models.length > 0)
161: fState.resolveState(true);
162: }
163:
164: // Update the bundle description of plug-ins whose state has changed.
165: // A plug-in changes state if the MANIFEST.MF has been touched.
166: // or if a plug-in on the Target Platform has changed state (from checked to unchecked,
167: // and vice versa.
168: if ((e.getEventTypes() & IModelProviderEvent.MODELS_CHANGED) != 0) {
169: IModel[] changed = e.getChangedModels();
170: for (int i = 0; i < changed.length; i++)
171: handleChange((IPluginModelBase) changed[i], delta);
172: }
173:
174: if (fState != null) {
175: // if the target location has not changed, incrementally re-resolve the state after processing all the add/remove/modify changes
176: // Otherwise, the state is in a good resolved state
177: StateDelta stateDelta = (e.getEventTypes() & IModelProviderEvent.TARGET_CHANGED) != 0 ? null
178: : fState
179: .resolveState((e.getEventTypes() & ICoreConstants.ENVIRONMENT_CHANGED) != 0 ? false
180: : true);
181: // trigger a classpath update for all workspace plug-ins affected by the
182: // processed batch of changes
183: updateAffectedEntries(stateDelta);
184: fireStateDelta(stateDelta);
185:
186: }
187:
188: // notify all interested listeners in the changes made to the master table of entries
189: fireDelta(delta);
190: }
191:
192: /**
193: * Trigger a classpath update for all workspace plug-ins affected by the processed
194: * model changes
195: *
196: * @param delta a state delta containing a list of bundles affected by the processed
197: * changes
198: */
199: private void updateAffectedEntries(StateDelta delta) {
200: Map map = new HashMap();
201: if (delta == null) {
202: // if the delta is null, then the entire target changed.
203: // Therefore, we should update the classpath for all workspace plug-ins.
204: IPluginModelBase[] models = getWorkspaceModels();
205: for (int i = 0; i < models.length; i++) {
206: IProject project = models[i].getUnderlyingResource()
207: .getProject();
208: try {
209: if (project.hasNature(JavaCore.NATURE_ID)) {
210: map.put(JavaCore.create(project),
211: new RequiredPluginsClasspathContainer(
212: models[i]));
213: }
214: } catch (CoreException e) {
215: }
216: }
217: } else {
218: BundleDelta[] deltas = delta.getChanges();
219: for (int i = 0; i < deltas.length; i++) {
220: try {
221: // update classpath for workspace plug-ins that are housed in a
222: // Java project hand have been affected by the processd model changes.
223: IPluginModelBase model = findModel(deltas[i]
224: .getBundle());
225: IResource resource = model == null ? null : model
226: .getUnderlyingResource();
227: if (resource != null) {
228: IProject project = resource.getProject();
229: if (project.hasNature(JavaCore.NATURE_ID)) {
230: IJavaProject jProject = JavaCore
231: .create(project);
232: if (!map.containsKey(jProject)) {
233: map
234: .put(
235: jProject,
236: new RequiredPluginsClasspathContainer(
237: model));
238: }
239: }
240: }
241: } catch (CoreException e) {
242: }
243: }
244: // do secondary dependencies
245: IPluginModelBase[] models = getWorkspaceModels();
246: for (int i = 0; i < models.length; i++) {
247: IProject project = models[i].getUnderlyingResource()
248: .getProject();
249: try {
250: if (!project.hasNature(JavaCore.NATURE_ID))
251: continue;
252: IJavaProject jProject = JavaCore.create(project);
253: if (map.containsKey(jProject))
254: continue;
255: IBuild build = ClasspathUtilCore
256: .getBuild(models[i]);
257: if (build != null
258: && build
259: .getEntry(IBuildEntry.SECONDARY_DEPENDENCIES) != null) {
260: map.put(jProject,
261: new RequiredPluginsClasspathContainer(
262: models[i], build));
263: }
264: } catch (CoreException e) {
265: }
266: }
267: }
268:
269: if (map.size() > 0) {
270: try {
271: // update classpath for all affected workspace plug-ins in one operation
272: IJavaProject[] jProjects = (IJavaProject[]) map
273: .keySet().toArray(new IJavaProject[map.size()]);
274: IClasspathContainer[] containers = (IClasspathContainer[]) map
275: .values().toArray(
276: new IClasspathContainer[map.size()]);
277: JavaCore.setClasspathContainer(
278: PDECore.REQUIRED_PLUGINS_CONTAINER_PATH,
279: jProjects, containers, null);
280: } catch (JavaModelException e) {
281: }
282: }
283: }
284:
285: /**
286: * Notify all interested listeners in changes made to the master table
287: *
288: * @param delta the delta of changes
289: */
290: private void fireDelta(PluginModelDelta delta) {
291: if (fListeners != null) {
292: for (int i = 0; i < fListeners.size(); i++) {
293: ((IPluginModelListener) fListeners.get(i))
294: .modelsChanged(delta);
295: }
296: }
297: }
298:
299: /**
300: * Notify all interested listeners in changes made to the resolver State
301: *
302: * @param delta the delta from the resolver State.
303: */
304: private void fireStateDelta(StateDelta delta) {
305: if (fStateListeners != null) {
306: ListIterator li = fStateListeners.listIterator();
307: while (li.hasNext())
308: ((IStateDeltaListener) li.next()).stateResolved(delta);
309: }
310: }
311:
312: /**
313: * Notify all interested listeners the cached PDEState has changed
314: *
315: * @param newState the new PDEState.
316: */
317: private void fireStateChanged(PDEState newState) {
318: if (fStateListeners != null) {
319: ListIterator li = fStateListeners.listIterator();
320: while (li.hasNext())
321: ((IStateDeltaListener) li.next()).stateChanged(newState
322: .getState());
323: }
324: }
325:
326: /**
327: * Add a listener to the model manager
328: *
329: * @param listener the listener to be added
330: */
331: public void addPluginModelListener(IPluginModelListener listener) {
332: if (fListeners == null)
333: fListeners = new ArrayList();
334: if (!fListeners.contains(listener))
335: fListeners.add(listener);
336: }
337:
338: /**
339: * Add a StateDelta listener to model manager
340: *
341: * @param listener the listener to be added
342: */
343: public void addStateDeltaListener(IStateDeltaListener listener) {
344: if (fStateListeners == null)
345: fStateListeners = new ArrayList();
346: if (!fStateListeners.contains(listener))
347: fStateListeners.add(listener);
348: }
349:
350: /**
351: * Remove a listener from the model manager
352: *
353: * @param listener the listener to be removed
354: */
355: public void removePluginModelListener(IPluginModelListener listener) {
356: if (fListeners != null)
357: fListeners.remove(listener);
358: }
359:
360: /**
361: * Remove a StateDelta listener from the model manager
362: *
363: * @param listener the listener to be removed
364: */
365: public void removeStateDeltaListener(IStateDeltaListener listener) {
366: if (fStateListeners != null)
367: fStateListeners.remove(listener);
368: }
369:
370: /**
371: * Returns <code>true</code> if neither the workspace nor target contains plug-ins;
372: * <code>false</code> otherwise.
373: *
374: * @return <code>true</code> if neither the workspace nor target contains plug-ins;
375: * <code>false</code> otherwise.
376: */
377: public boolean isEmpty() {
378: return getEntryTable().size() == 0;
379: }
380:
381: /**
382: * Returns <code>true</code> if the master table has been initialized;
383: * <code>false</code> otherwise.
384: *
385: * @return <code>true</code> if the master table has been initialized;
386: * <code>false</code> otherwise.
387: */
388: public boolean isInitialized() {
389: return fEntries != null;
390: }
391:
392: /**
393: * Allow access to the table only through this getter.
394: * It always calls initialize to make sure the table is initialized.
395: * If more than one thread tries to read the table at the same time,
396: * and the table is not initialized yet, thread2 would wait.
397: * This way there are no partial reads.
398: */
399: private Map getEntryTable() {
400: initializeTable();
401: return fEntries;
402: }
403:
404: /**
405: * This method must be synchronized so that only one thread
406: * initializes the table, and the rest would block until
407: * the table is initialized.
408: *
409: */
410: private synchronized void initializeTable() {
411: if (fEntries != null)
412: return;
413: fEntries = Collections.synchronizedMap(new TreeMap());
414:
415: // Create a state that contains all bundles from the target and workspace
416: // If a workspace bundle has the same symbolic name as a target bundle,
417: // the target counterpart is subsequently removed from the state.
418: fState = new PDEState(fWorkspaceManager.getPluginPaths(),
419: fExternalManager.getPluginPaths(), true,
420: new NullProgressMonitor());
421:
422: // initialize the enabled/disabled state of target models
423: // based on whether the bundle is checked/unchecked on the Target Platform
424: // preference page.
425: fExternalManager.initializeModels(fState.getTargetModels());
426:
427: // add target models to the master table
428: boolean statechanged = addToTable(fExternalManager
429: .getAllModels());
430:
431: // a state is combined only if the workspace plug-ins have not changed
432: // since the last shutdown of the workbench
433: if (fState.isCombined()) {
434: IPluginModelBase[] models = fState.getWorkspaceModels();
435: // initialize the workspace models from the cached state
436: fWorkspaceManager.initializeModels(models);
437: // add workspace models to the master table
438: addToTable(models);
439: // resolve the state incrementally if some target models
440: // were removed
441: if (statechanged)
442: fState.resolveState(true);
443: } else {
444: // if we have no good cached state of workspace plug-ins,
445: // re-parse all/any workspace plug-ins
446: IPluginModelBase[] models = fWorkspaceManager
447: .getPluginModels();
448:
449: // add workspace plug-ins to the master table
450: addToTable(models);
451:
452: // add workspace plug-ins to the state
453: // and remove their target counterparts from the state.
454: for (int i = 0; i < models.length; i++) {
455: addWorkspaceBundleToState(models[i]);
456: }
457:
458: // resolve the state incrementally if any workspace plug-ins were found
459: if (models.length > 0)
460: fState.resolveState(true);
461: }
462: }
463:
464: /**
465: * Adds the given models to the corresponding ModelEntry in the master table
466: *
467: * @param models the models to be added to the master tabl
468: *
469: * @return <code>true</code> if changes were made to the state; <code>false</code> otherwise
470: */
471: private boolean addToTable(IPluginModelBase[] models) {
472: boolean stateChanged = false;
473: for (int i = 0; i < models.length; i++) {
474: String id = models[i].getPluginBase().getId();
475: if (id == null)
476: continue;
477: LocalModelEntry entry = (LocalModelEntry) fEntries.get(id);
478: // create a new entry for the given ID if none already exists
479: if (entry == null) {
480: entry = new LocalModelEntry(id);
481: fEntries.put(id, entry);
482: }
483: // add the model to the entry
484: entry.addModel(models[i]);
485:
486: // if the model is a disabled external (target) model, remove it from
487: // the state and set the stateChanged flag to true
488: if (models[i].getUnderlyingResource() == null
489: && !models[i].isEnabled()) {
490: fState.removeBundleDescription(models[i]
491: .getBundleDescription());
492: stateChanged = true;
493: }
494: }
495: return stateChanged;
496: }
497:
498: /**
499: * Add a workspace bundle to the state
500: *
501: * @param model the workspace model
502: */
503: private synchronized void addWorkspaceBundleToState(
504: IPluginModelBase model) {
505: String id = model.getPluginBase().getId();
506: if (id == null)
507: return;
508:
509: // remove target models by the same ID from the state, if any
510: ModelEntry entry = (ModelEntry) fEntries.get(id);
511: if (entry != null) {
512: IPluginModelBase[] models = entry.getExternalModels();
513: for (int i = 0; i < models.length; i++)
514: fState.removeBundleDescription(models[i]
515: .getBundleDescription());
516: }
517:
518: // add new bundle to the state
519: fState.addBundle(model, false);
520:
521: BundleDescription desc = model.getBundleDescription();
522: if (desc != null) {
523: // refresh host if a fragment is added to the state.
524: // this is necessary because the state will not re-resolve dynamically added fragments
525: // on its own
526: HostSpecification spec = desc.getHost();
527: if (spec != null
528: && ("true".equals(System.getProperty("pde.allowCycles")) //$NON-NLS-1$ //$NON-NLS-2$
529: || ClasspathUtilCore.isPatchFragment(desc)
530: || desc.getImportPackages().length > 0 || desc
531: .getRequiredBundles().length > 0)) {
532: BundleDescription host = (BundleDescription) spec
533: .getSupplier();
534: if (host != null) {
535: ModelEntry hostEntry = (ModelEntry) fEntries
536: .get(host.getName());
537: if (hostEntry != null) {
538: fState
539: .addBundle(hostEntry.getModel(host),
540: true);
541: }
542: }
543: }
544: }
545: }
546:
547: /**
548: * Adds a model to the master table and state
549: *
550: * @param id the key
551: * @param model the model being added
552: */
553: private void handleAdd(String id, IPluginModelBase model,
554: PluginModelDelta delta) {
555: LocalModelEntry entry = (LocalModelEntry) getEntryTable().get(
556: id);
557:
558: // add model to the corresponding ModelEntry. Create a new entry if necessary
559: if (entry == null) {
560: entry = new LocalModelEntry(id);
561: getEntryTable().put(id, entry);
562: delta.addEntry(entry, PluginModelDelta.ADDED);
563: } else {
564: delta.addEntry(entry, PluginModelDelta.CHANGED);
565: }
566: entry.addModel(model);
567:
568: // if the model added is a workspace model, add it to the state and
569: // remove all its external counterparts
570: if (model.getUnderlyingResource() != null) {
571: addWorkspaceBundleToState(model);
572: } else if (model.isEnabled() && !entry.hasWorkspaceModels()) {
573: // if a target model has went from an unchecked state to a checked state
574: // on the target platform preference page, re-add its bundle description
575: // to the state
576: BundleDescription desc = model.getBundleDescription();
577: if (desc.getContainingState().equals(fState))
578: fState.addBundleDescription(desc);
579: }
580: }
581:
582: /**
583: * Removes the model from the ModelEntry and the state. The entire model entry is removed
584: * once the last model it retains is removed.
585: *
586: * @param id the key
587: * @param model the model to be removed
588: */
589: private void handleRemove(String id, IPluginModelBase model,
590: PluginModelDelta delta) {
591: LocalModelEntry entry = (LocalModelEntry) getEntryTable().get(
592: id);
593: if (entry != null) {
594: // remove model from the entry
595: entry.removeModel(model);
596: // remove corresponding bundle description from the state
597: fState
598: .removeBundleDescription(model
599: .getBundleDescription());
600: if (!entry.hasExternalModels()
601: && !entry.hasWorkspaceModels()) {
602: // remove entire entry if it has no models left
603: getEntryTable().remove(id);
604: delta.addEntry(entry, PluginModelDelta.REMOVED);
605: return;
606: } else if (model.getUnderlyingResource() != null
607: && !entry.hasWorkspaceModels()) {
608: // re-add enabled external counterparts to the state, if the last workspace
609: // plug-in with a particular symbolic name is removed
610: IPluginModelBase[] external = entry.getExternalModels();
611: for (int i = 0; i < external.length; i++) {
612: if (external[i].isEnabled())
613: fState.addBundleDescription(external[i]
614: .getBundleDescription());
615: }
616: }
617: delta.addEntry(entry, PluginModelDelta.CHANGED);
618: }
619: }
620:
621: /**
622: * Update the state and master table to account for the change in the given model
623: *
624: * @param model the model that has changed
625: */
626: private void handleChange(IPluginModelBase model,
627: PluginModelDelta delta) {
628: BundleDescription desc = model.getBundleDescription();
629: String oldID = desc == null ? null : desc.getSymbolicName();
630: String newID = model.getPluginBase().getId();
631:
632: // if the model still has no symbolic name (ie. a MANIFEST.MF without the
633: // Bundle-SymbolicName header), keep ignoring it
634: if (oldID == null && newID == null)
635: return;
636:
637: // if the model used to lack a Bundle-SymbolicName header and now it has one,
638: // treat it as a regular model addition
639: if (oldID == null && newID != null) {
640: handleAdd(newID, model, delta);
641: } else if (oldID != null && newID == null) {
642: // if the model used to have a Bundle-SymbolicName header and now it lost it,
643: // treat it as a regular model removal
644: handleRemove(oldID, model, delta);
645: model.setBundleDescription(null);
646: } else if (oldID.equals(newID)) {
647: // if the workspace bundle's MANIFEST.MF was touched or
648: // if the a target plug-in has now become enabled/checked, update the model
649: // in the state
650: if (model.isEnabled())
651: fState.addBundle(model, true);
652: else
653: // if the target plug-in has become disabled/unchecked, remove its bundle
654: // description from the state
655: fState.removeBundleDescription(model
656: .getBundleDescription());
657: delta.addEntry(findEntry(oldID), PluginModelDelta.CHANGED);
658: } else {
659: // if the symbolic name of the bundle has completely changed,
660: // remove the model from the old entry, and add the model to the new entry
661: handleRemove(oldID, model, delta);
662: handleAdd(newID, model, delta);
663: }
664: }
665:
666: /**
667: * Returns a model entry containing all workspace and target plug-ins by the given ID
668: *
669: * @param id the plug-in ID
670: *
671: * @return a model entry containing all workspace and target plug-ins by the given ID
672: */
673: public ModelEntry findEntry(String id) {
674: if ("system.bundle".equals(id)) //$NON-NLS-1$
675: id = "org.eclipse.osgi"; //$NON-NLS-1$
676: return id == null ? null : (ModelEntry) getEntryTable().get(id);
677: }
678:
679: /**
680: * Returns the plug-in model for the best match plug-in with the given ID.
681: * A null value is returned if no such bundle is found in the workspace or target platform.
682: * <p>
683: * A workspace plug-in is always preferably returned over a target plug-in.
684: * A plug-in that is checked/enabled on the Target Platform preference page is always
685: * preferably returned over a target plug-in that is unchecked/disabled.
686: * </p>
687: * <p>
688: * In the case of a tie among workspace plug-ins or among target plug-ins,
689: * the plug-in with the highest version is returned.
690: * </p>
691: * <p>
692: * In the case of a tie among more than one suitable plug-in that have the same version,
693: * one of those plug-ins is randomly returned.
694: * </p>
695: *
696: * @param id the plug-in ID
697: * @return the plug-in model for the best match plug-in with the given ID
698: */
699: public IPluginModelBase findModel(String id) {
700: ModelEntry entry = findEntry(id);
701: return entry == null ? null : entry.getModel();
702: }
703:
704: /**
705: * Returns the plug-in model corresponding to the given project, or <code>null</code>
706: * if the project does not represent a plug-in project or if it contains a manifest file
707: * that is malformed or missing vital information.
708: *
709: * @param project the project
710: * @return a plug-in model corresponding to the project or <code>null</code> if the project
711: * is not a plug-in project
712: */
713: public IPluginModelBase findModel(IProject project) {
714: initializeTable();
715: return fWorkspaceManager.getPluginModel(project);
716: }
717:
718: /**
719: * Returns a plug-in model associated with the given bundle description
720: *
721: * @param desc the bundle description
722: *
723: * @return a plug-in model associated with the given bundle description or <code>null</code>
724: * if none exists
725: */
726: public IPluginModelBase findModel(BundleDescription desc) {
727: ModelEntry entry = findEntry(desc.getSymbolicName());
728: return entry == null ? null : entry.getModel(desc);
729: }
730:
731: /**
732: * Returns all plug-ins and fragments in the workspace as well as all plug-ins and fragments that are
733: * checked on the Target Platform preference page.
734: * <p>
735: * If a workspace plug-in/fragment has the same ID as a target plug-in/fragment, the target counterpart
736: * is skipped and not included.
737: * </p>
738: * <p>
739: * Equivalent to <code>getActiveModels(true)</code>
740: * </p>
741: *
742: * @return all plug-ins and fragments in the workspace as well as all plug-ins and fragments that are
743: * checked on the Target Platform preference page.
744: */
745: public IPluginModelBase[] getActiveModels() {
746: return getActiveModels(true);
747: }
748:
749: /**
750: * Returns all plug-ins and (possibly) fragments in the workspace as well as all plug-ins and (possibly)
751: * fragments that are checked on the Target Platform preference page.
752: * <p>
753: * If a workspace plug-in/fragment has the same ID as a target plug-in, the target counterpart
754: * is skipped and not included.
755: * </p>
756: * <p>
757: * The returned result includes fragments only if <code>includeFragments</code>
758: * is set to true
759: * </p>
760: * @param includeFragments a boolean indicating if fragments are desired in the returned
761: * result
762: * @return all plug-ins and (possibly) fragments in the workspace as well as all plug-ins and
763: * (possibly) fragments that are checked on the Target Platform preference page.
764: */
765: public IPluginModelBase[] getActiveModels(boolean includeFragments) {
766: int size = getEntryTable().size();
767: ArrayList result = new ArrayList(size);
768: Iterator iter = getEntryTable().values().iterator();
769: while (iter.hasNext()) {
770: ModelEntry entry = (ModelEntry) iter.next();
771: IPluginModelBase[] models = entry.getActiveModels();
772: for (int i = 0; i < models.length; i++) {
773: if (models[i] instanceof IPluginModel
774: || includeFragments)
775: result.add(models[i]);
776: }
777: }
778: return (IPluginModelBase[]) result
779: .toArray(new IPluginModelBase[result.size()]);
780: }
781:
782: /**
783: * Returns all plug-ins and fragments in the workspace as well as all target plug-ins and fragments, regardless
784: * whether or not they are checked or not on the Target Platform preference page.
785: * <p>
786: * If a workspace plug-in/fragment has the same ID as a target plug-in, the target counterpart
787: * is skipped and not included.
788: * </p>
789: * <p>
790: * Equivalent to <code>getAllModels(true)</code>
791: * </p>
792: *
793: * @return all plug-ins and fragments in the workspace as well as all target plug-ins and fragments, regardless
794: * whether or not they are checked on the Target Platform preference page.
795: */
796: public IPluginModelBase[] getAllModels() {
797: return getAllModels(true);
798: }
799:
800: /**
801: * Returns all plug-ins and (possibly) fragments in the workspace as well as all plug-ins
802: * and (possibly) fragments, regardless whether or not they are
803: * checked on the Target Platform preference page.
804: * <p>
805: * If a workspace plug-in/fragment has the same ID as a target plug-in/fragment, the target counterpart
806: * is skipped and not included.
807: * </p>
808: * <p>
809: * The returned result includes fragments only if <code>includeFragments</code>
810: * is set to true
811: * </p>
812: * @param includeFragments a boolean indicating if fragments are desired in the returned
813: * result
814: * @return ll plug-ins and (possibly) fragments in the workspace as well as all plug-ins
815: * and (possibly) fragments, regardless whether or not they are
816: * checked on the Target Platform preference page.
817: */
818: public IPluginModelBase[] getAllModels(boolean includeFragments) {
819: int size = getEntryTable().size();
820: ArrayList result = new ArrayList(size);
821: Iterator iter = getEntryTable().values().iterator();
822: while (iter.hasNext()) {
823: ModelEntry entry = (ModelEntry) iter.next();
824: IPluginModelBase[] models = entry.hasWorkspaceModels() ? entry
825: .getWorkspaceModels()
826: : entry.getExternalModels();
827: for (int i = 0; i < models.length; i++) {
828: if (models[i] instanceof IPluginModel
829: || includeFragments)
830: result.add(models[i]);
831: }
832: }
833: return (IPluginModelBase[]) result
834: .toArray(new IPluginModelBase[result.size()]);
835: }
836:
837: /**
838: * Returns all plug-in models in the target platform
839: *
840: * @return all plug-ins in the target platform
841: */
842: public IPluginModelBase[] getExternalModels() {
843: initializeTable();
844: return fExternalManager.getAllModels();
845: }
846:
847: /**
848: * Returns all plug-in models in the workspace
849: *
850: * @return all plug-in models in the workspace
851: */
852: public IPluginModelBase[] getWorkspaceModels() {
853: initializeTable();
854: return fWorkspaceManager.getPluginModels();
855: }
856:
857: /**
858: * Return the model manager that keeps track of plug-ins in the target platform
859: *
860: * @return the model manager that keeps track of plug-ins in the target platform
861: */
862: public ExternalModelManager getExternalModelManager() {
863: initializeTable();
864: return fExternalManager;
865: }
866:
867: /**
868: * Returns the state containing bundle descriptions for workspace plug-ins and target plug-ins
869: * that form the current PDE state
870: * @return
871: */
872: public PDEState getState() {
873: initializeTable();
874: return fState;
875: }
876:
877: /**
878: * Sets the PDE state. This method is meant to be called when the target platform
879: * location changes.
880: *
881: * @param state the new state
882: */
883: public void resetState(PDEState state) {
884: if (fState != null && fState.equals(state))
885: return;
886: // clear all models and add new ones
887: int type = IModelProviderEvent.TARGET_CHANGED;
888: IModel[] removed = fState.getTargetModels();
889: if (removed.length > 0)
890: type |= IModelProviderEvent.MODELS_REMOVED;
891: IModel[] added = state.getTargetModels();
892: if (added.length > 0)
893: type |= IModelProviderEvent.MODELS_ADDED;
894: modelsChanged(new ModelProviderEvent(state, type, added,
895: removed, new IModel[0]));
896:
897: fireStateChanged(state);
898: }
899:
900: /**
901: * Perform cleanup upon shutting down
902: */
903: public void shutdown() {
904: fWorkspaceManager.shutdown();
905: fExternalManager.shutdown();
906: if (fState != null)
907: fState.shutdown();
908: if (fListeners != null)
909: fListeners.clear();
910: if (fStateListeners != null)
911: fStateListeners.clear();
912: }
913:
914: public void addExtensionDeltaListener(
915: IExtensionDeltaListener listener) {
916: fWorkspaceManager.addExtensionDeltaListener(listener);
917: }
918:
919: public void removeExtensionDeltaListener(
920: IExtensionDeltaListener listener) {
921: fWorkspaceManager.removeExtensionDeltaListener(listener);
922: }
923:
924: }
|