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.HashSet;
014: import java.util.Iterator;
015: import java.util.Set;
016:
017: import org.eclipse.core.resources.IProject;
018: import org.eclipse.pde.core.IModel;
019: import org.eclipse.pde.core.IModelProviderEvent;
020: import org.eclipse.pde.core.IModelProviderListener;
021: import org.eclipse.pde.internal.core.ifeature.IFeatureModel;
022: import org.eclipse.pde.internal.core.util.VersionUtil;
023: import org.osgi.framework.Version;
024:
025: /**
026: * Manages all feature models in the workspace, and maximum one external feature
027: * model for a given id and version. While workspace model(s) exist with the
028: * same id and version as an external model, the external model is inactive (not
029: * exposed).
030: */
031:
032: public class FeatureModelManager {
033:
034: /**
035: * All models in workspace, and those external models that have no
036: * corresponding workspace model with the same id and version
037: */
038: private FeatureTable fActiveModels;
039:
040: /**
041: * External models masked by workspace models with the same id and version.
042: */
043: private FeatureTable fInactiveModels;
044:
045: private ExternalFeatureModelManager fExternalManager;
046:
047: private boolean fReloadExternalNeeded = false;
048:
049: private WorkspaceFeatureModelManager fWorkspaceManager;
050:
051: private IModelProviderListener fProviderListener;
052:
053: /**
054: * List of IFeatureModelListener
055: */
056: private ArrayList fListeners;
057:
058: public FeatureModelManager() {
059: fWorkspaceManager = new WorkspaceFeatureModelManager();
060: fListeners = new ArrayList();
061: }
062:
063: public synchronized void shutdown() {
064: if (fWorkspaceManager != null)
065: fWorkspaceManager
066: .removeModelProviderListener(fProviderListener);
067: if (fExternalManager != null) {
068: fExternalManager
069: .removeModelProviderListener(fProviderListener);
070: fExternalManager.shutdown();
071: }
072: }
073:
074: private synchronized void init() {
075: if (fActiveModels != null) {
076: if (fReloadExternalNeeded) {
077: fReloadExternalNeeded = false;
078: fExternalManager.reload();
079: }
080: return;
081: }
082:
083: fActiveModels = new FeatureTable();
084: fInactiveModels = new FeatureTable();
085:
086: fProviderListener = new IModelProviderListener() {
087: public void modelsChanged(IModelProviderEvent e) {
088: handleModelsChanged(e);
089: }
090: };
091: fWorkspaceManager.addModelProviderListener(fProviderListener);
092:
093: IFeatureModel[] models = fWorkspaceManager.getFeatureModels();
094: for (int i = 0; i < models.length; i++) {
095: // add all workspace models, including invalid or duplicate (save
096: // id, ver)
097: fActiveModels.add(models[i]);
098: }
099:
100: fExternalManager = new ExternalFeatureModelManager();
101: fExternalManager.addModelProviderListener(fProviderListener);
102: fReloadExternalNeeded = false;
103: fExternalManager.startup();
104: }
105:
106: /*
107: * @return all active features
108: */
109: public IFeatureModel[] getModels() {
110: init();
111: IFeatureModel[] allModels = fActiveModels.getAll();
112: ArrayList valid = new ArrayList(allModels.length);
113: for (int i = 0; i < allModels.length; i++) {
114: if (allModels[i].isValid()) {
115: valid.add(allModels[i]);
116: }
117: }
118: return (IFeatureModel[]) valid.toArray(new IFeatureModel[valid
119: .size()]);
120: }
121:
122: public IFeatureModel[] getWorkspaceModels() {
123: init();
124: return fWorkspaceManager.getFeatureModels();
125: }
126:
127: public IFeatureModel getFeatureModel(IProject project) {
128: init();
129: return fWorkspaceManager.getFeatureModel(project);
130: }
131:
132: /**
133: * Finds active model with a given id and version
134: *
135: * @param id
136: * @param version
137: * @return one IFeature model or null
138: */
139: public IFeatureModel findFeatureModel(String id, String version) {
140: init();
141: IFeatureModel[] models = fActiveModels.get(id, version);
142: for (int i = 0; i < models.length; i++) {
143: if (models[i].isValid()) {
144: return models[i];
145: }
146: }
147: if (models.length == 0 && "0.0.0".equals(version)) { //$NON-NLS-1$
148: return findFeatureModel(id);
149: }
150: return null;
151: }
152:
153: /**
154: * Finds active model with the given id and version. If feature is not
155: * found, but a feature with qualifier set to qualifier exists it will be
156: * returned.
157: *
158: * @param id
159: * @param version
160: * @return IFeatureModel or null
161: */
162: public IFeatureModel findFeatureModelRelaxed(String id,
163: String version) {
164: IFeatureModel model = findFeatureModel(id, version);
165: if (model != null) {
166: return model;
167: }
168: try {
169: Version pvi = Version.parseVersion(version);
170: return findFeatureModel(id, pvi.getMajor() + "." //$NON-NLS-1$
171: + pvi.getMinor() + "." //$NON-NLS-1$
172: + pvi.getMicro() + ".qualifier"); //$NON-NLS-1$
173:
174: } catch (IllegalArgumentException e) {
175: // handle the case where the version is not in proper format (bug 203795)
176: return null;
177: }
178: }
179:
180: /**
181: * Finds active models with a given id
182: *
183: * @param id
184: * @param version
185: * @return IFeature model[]
186: */
187: public IFeatureModel[] findFeatureModels(String id) {
188: init();
189: IFeatureModel[] models = fActiveModels.get(id);
190: ArrayList valid = new ArrayList(models.length);
191: for (int i = 0; i < models.length; i++) {
192: if (models[i].isValid()) {
193: valid.add(models[i]);
194: }
195: }
196: return (IFeatureModel[]) valid.toArray(new IFeatureModel[valid
197: .size()]);
198: }
199:
200: public IFeatureModel findFeatureModel(String id) {
201: IFeatureModel[] models = findFeatureModels(id);
202: IFeatureModel model = null;
203: for (int i = 0; i < models.length; i++) {
204: if (model == null) {
205: model = models[i];
206: } else {
207: String version = model.getFeature().getVersion();
208: String version2 = models[i].getFeature().getVersion();
209: Version vid = Version.parseVersion(version);
210: Version vid2 = Version.parseVersion(version2);
211: if (VersionUtil.isGreaterOrEqualTo(vid2, vid)) {
212: model = models[i];
213: }
214: }
215: }
216: return model;
217: }
218:
219: private void handleModelsChanged(IModelProviderEvent e) {
220: init();
221: IFeatureModelDelta delta = processEvent(e);
222:
223: Object[] entries = fListeners.toArray();
224: for (int i = 0; i < entries.length; i++) {
225: ((IFeatureModelListener) entries[i]).modelsChanged(delta);
226: }
227: }
228:
229: /**
230: * @param e
231: * @return
232: */
233: private synchronized IFeatureModelDelta processEvent(
234: IModelProviderEvent e) {
235: FeatureModelDelta delta = new FeatureModelDelta();
236: /*
237: * Set of Idvers for which there might be necessary to move a model
238: * between active models and inactive models
239: */
240: Set affectedIdVers = null;
241: if ((e.getEventTypes() & IModelProviderEvent.MODELS_REMOVED) != 0) {
242: IModel[] removed = e.getRemovedModels();
243: for (int i = 0; i < removed.length; i++) {
244: if (!(removed[i] instanceof IFeatureModel))
245: continue;
246: IFeatureModel model = (IFeatureModel) removed[i];
247: FeatureTable.Idver idver = fActiveModels.remove(model);
248: if (idver != null) {
249: // may need to activate another model
250: if (affectedIdVers == null)
251: affectedIdVers = new HashSet();
252: affectedIdVers.add(idver);
253: delta.add(model, IFeatureModelDelta.REMOVED);
254: } else {
255: fInactiveModels.remove(model);
256: }
257: }
258: }
259: if ((e.getEventTypes() & IModelProviderEvent.MODELS_ADDED) != 0) {
260: IModel[] added = e.getAddedModels();
261: for (int i = 0; i < added.length; i++) {
262: if (!(added[i] instanceof IFeatureModel))
263: continue;
264: IFeatureModel model = (IFeatureModel) added[i];
265: if (model.getUnderlyingResource() != null) {
266: FeatureTable.Idver idver = fActiveModels.add(model);
267: delta.add(model, IFeatureModelDelta.ADDED);
268: // may need to deactivate another model
269: if (affectedIdVers == null)
270: affectedIdVers = new HashSet();
271: affectedIdVers.add(idver);
272: } else {
273: if (!model.isValid()) {
274: // ignore invalid external models
275: continue;
276: }
277: String id = model.getFeature().getId();
278: String version = model.getFeature().getVersion();
279: if (fInactiveModels.get(id, version).length > 0) {
280: // ignore duplicate external models
281: continue;
282: }
283: IFeatureModel[] activeModels = fActiveModels.get(
284: id, version);
285: for (int j = 0; j < activeModels.length; j++) {
286: if (activeModels[j].getUnderlyingResource() == null) {
287: // ignore duplicate external models
288: continue;
289: }
290: }
291: FeatureTable.Idver idver = fInactiveModels
292: .add(model);
293: // may need to activate this model
294: if (affectedIdVers == null)
295: affectedIdVers = new HashSet();
296: affectedIdVers.add(idver);
297: }
298: }
299: }
300:
301: /* 1. Reinsert with a new id and version, if necessary */
302: if ((e.getEventTypes() & IModelProviderEvent.MODELS_CHANGED) != 0) {
303: IModel[] changed = e.getChangedModels();
304: for (int i = 0; i < changed.length; i++) {
305: if (!(changed[i] instanceof IFeatureModel))
306: continue;
307: IFeatureModel model = (IFeatureModel) changed[i];
308:
309: String id = model.getFeature().getId();
310: String version = model.getFeature().getVersion();
311:
312: FeatureTable.Idver oldIdver = fActiveModels.get(model);
313: if (oldIdver != null && !oldIdver.equals(id, version)) {
314: // version changed
315: FeatureTable.Idver idver = fActiveModels.add(model);
316: if (affectedIdVers == null)
317: affectedIdVers = new HashSet();
318: affectedIdVers.add(oldIdver);
319: affectedIdVers.add(idver);
320: }
321: /*
322: * no need to check inactive models, because external features
323: * do not chance or version
324: */
325:
326: }
327: }
328: /* 2. Move features between active and inactive tables if necessary */
329: adjustExternalVisibility(delta, affectedIdVers);
330: /*
331: * 3. Changed models that do result in FeatureModelDelta.ADDED or
332: * FeatureModelDelta.Removed fire FeatureModelDelta.CHANGED
333: */
334: if ((e.getEventTypes() & IModelProviderEvent.MODELS_CHANGED) != 0) {
335: IModel[] changed = e.getChangedModels();
336: for (int i = 0; i < changed.length; i++) {
337: if (!(changed[i] instanceof IFeatureModel))
338: continue;
339: IFeatureModel model = (IFeatureModel) changed[i];
340: if (!delta.contains(model, IFeatureModelDelta.ADDED
341: | IFeatureModelDelta.REMOVED)) {
342: delta.add(model, IFeatureModelDelta.CHANGED);
343: }
344:
345: }
346: }
347: return delta;
348: }
349:
350: /**
351: * @param delta
352: * @param affectedIdVers
353: */
354: private void adjustExternalVisibility(FeatureModelDelta delta,
355: Set affectedIdVers) {
356: if (affectedIdVers != null) {
357: for (Iterator it = affectedIdVers.iterator(); it.hasNext();) {
358: FeatureTable.Idver idver = (FeatureTable.Idver) it
359: .next();
360: IFeatureModel[] affectedModels = fActiveModels
361: .get(idver);
362: if (affectedModels.length > 1) {
363: /*
364: * there must have been at least one workspace and one
365: * external model
366: */
367: for (int j = 0; j < affectedModels.length; j++) {
368: if (affectedModels[j].getUnderlyingResource() == null) {
369: // move external to inactive
370: fActiveModels.remove(affectedModels[j]);
371: fInactiveModels.add(affectedModels[j]);
372: delta.add(affectedModels[j],
373: IFeatureModelDelta.REMOVED);
374: }
375: }
376: }
377:
378: if (affectedModels.length <= 0) {
379: // no workspace model
380: IFeatureModel[] models = fInactiveModels.get(idver);
381: if (models.length > 0) {
382: // external model exists, move it to active
383: fInactiveModels.remove(models[0]);
384: fActiveModels.add(models[0]);
385: delta.add(models[0], IFeatureModelDelta.ADDED);
386: }
387: }
388: }
389: }
390: }
391:
392: public void addFeatureModelListener(IFeatureModelListener listener) {
393: if (!fListeners.contains(listener))
394: fListeners.add(listener);
395: }
396:
397: public void removeFeatureModelListener(
398: IFeatureModelListener listener) {
399: if (fListeners.contains(listener))
400: fListeners.remove(listener);
401: }
402:
403: public void targetReloaded() {
404: fReloadExternalNeeded = true;
405: }
406:
407: public IFeatureModel getDeltaPackFeature() {
408: IFeatureModel model = findFeatureModel("org.eclipse.equinox.executable"); //$NON-NLS-1$
409: if (model == null)
410: model = findFeatureModel("org.eclipse.platform.launchers"); //$NON-NLS-1$
411: return model;
412: }
413:
414: }
|