1 /*******************************************************************************
2  *  Copyright (c) 2007, 2019 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 package org.eclipse.pde.core.plugin;
15 
16 import java.util.ArrayList;
17 import org.eclipse.core.runtime.PlatformObject;
18 import org.eclipse.osgi.service.resolver.BundleDescription;
19 
20 /**
21  * A ModelEntry object has an ID and keeps track of all workspace plug-ins and target
22  * plug-ins that have that ID.
23  * <p>
24  * This class is not meant to be extended or instantiated by clients.
25  * </p>
26  *
27  * @since 3.3
28  *
29  * @noextend This class is not intended to be subclassed by clients.
30  * @noinstantiate This class is not intended to be instantiated by clients.
31  */
32 public class ModelEntry extends PlatformObject {
33 
34 	private final String fId;
35 
36 	/**
37 	 * The list of workspace models with the same entry ID
38 	 */
39 	protected ArrayList<IPluginModelBase> fWorkspaceEntries = new ArrayList<>(1);
40 
41 	/**
42 	 * The list of external models with the same entry ID
43 	 */
44 	protected ArrayList<IPluginModelBase> fExternalEntries = new ArrayList<>(1);
45 
46 	/**
47 	 * Constructor
48 	 *
49 	 * @param id the entry ID
50 	 */
ModelEntry(String id)51 	public ModelEntry(String id) {
52 		fId = id;
53 	}
54 
55 	/**
56 	 * Returns all the workspace plug-ins that have the model entry ID
57 	 *
58 	 * @return an array of workspace plug-ins that have the model entry ID
59 	 */
getWorkspaceModels()60 	public IPluginModelBase[] getWorkspaceModels() {
61 		return fWorkspaceEntries.toArray(new IPluginModelBase[fWorkspaceEntries.size()]);
62 	}
63 
64 	/**
65 	 * Returns all plug-ins in the target platform that have the model entry ID.
66 	 * The returned result contains both plug-ins that are enabled (ie. checked
67 	 * on the <b>Plug-in Development &gt; Target Platform</b> preference page)
68 	 * and disabled.
69 	 *
70 	 * @return an array of plug-ins in the target platform that have the model
71 	 *         entry ID
72 	 */
getExternalModels()73 	public IPluginModelBase[] getExternalModels() {
74 		return fExternalEntries.toArray(new IPluginModelBase[fExternalEntries.size()]);
75 	}
76 
77 	/**
78 	 * Returns the plug-in model for the best match plug-in with the given ID.
79 	 * A null value is returned if no such bundle is found in the workspace or target platform.
80 	 * <p>
81 	 * A workspace plug-in is always preferably returned over a target plug-in.
82 	 * A plug-in that is checked/enabled on the Target Platform preference page is always
83 	 * preferably returned over a target plug-in that is unchecked/disabled.
84 	 * </p>
85 	 * <p>
86 	 * In the case of a tie among workspace plug-ins or among target plug-ins,
87 	 * the plug-in with the highest version is returned.
88 	 * </p>
89 	 * <p>
90 	 * In the case of a tie among more than one suitable plug-in that have the same version,
91 	 * one of those plug-ins is randomly returned.
92 	 * </p>
93 	 *
94 	 * @return the plug-in model for the best match plug-in with the given ID
95 	 */
getModel()96 	public IPluginModelBase getModel() {
97 		IPluginModelBase model = getBestCandidate(getWorkspaceModels());
98 		if (model == null) {
99 			model = getBestCandidate(getExternalModels());
100 		}
101 		return model;
102 	}
103 
getBestCandidate(IPluginModelBase[] models)104 	private IPluginModelBase getBestCandidate(IPluginModelBase[] models) {
105 		IPluginModelBase result = null;
106 		for (IPluginModelBase model : models) {
107 			if (model.getBundleDescription() == null) {
108 				continue;
109 			}
110 
111 			if (result == null) {
112 				result = model;
113 				continue;
114 			}
115 
116 			if (!result.isEnabled() && model.isEnabled()) {
117 				result = model;
118 				continue;
119 			}
120 
121 			BundleDescription current = result.getBundleDescription();
122 			BundleDescription candidate = model.getBundleDescription();
123 			if (!current.isResolved() && candidate.isResolved()) {
124 				result = model;
125 				continue;
126 			}
127 
128 			if (current.getVersion().compareTo(candidate.getVersion()) < 0) {
129 				result = model;
130 			}
131 		}
132 		return result;
133 	}
134 
135 	/**
136 	 * Returns all the plug-ins, with the model entry ID, that are currently
137 	 * active.
138 	 * <p>
139 	 * Workspace plug-ins are always active. Target plug-ins are only active if:
140 	 * </p>
141 	 * <ul>
142 	 * <li>they are checked on the <b>Plug-in Development &gt; Target
143 	 * Platform</b> preference page</li>
144 	 * <li>there does not exist a workspace plug-in that has the same ID</li>
145 	 * </ul>
146 	 *
147 	 * @return an array of the currently active plug-ins with the model entry ID
148 	 */
getActiveModels()149 	public IPluginModelBase[] getActiveModels() {
150 		if (!fWorkspaceEntries.isEmpty()) {
151 			return getWorkspaceModels();
152 		}
153 
154 		if (!fExternalEntries.isEmpty()) {
155 			ArrayList<IPluginModelBase> list = new ArrayList<>(fExternalEntries.size());
156 			for (int i = 0; i < fExternalEntries.size(); i++) {
157 				IPluginModelBase model = fExternalEntries.get(i);
158 				if (model.isEnabled()) {
159 					list.add(model);
160 				}
161 			}
162 			return list.toArray(new IPluginModelBase[list.size()]);
163 		}
164 		return new IPluginModelBase[0];
165 	}
166 
167 	/**
168 	 * Returns the model entry ID
169 	 *
170 	 * @return the model entry ID
171 	 */
getId()172 	public String getId() {
173 		return fId;
174 	}
175 
176 	/**
177 	 * Return the plug-in model associated with the given bundle description or
178 	 * <code>null</code> if none is found.
179 	 *
180 	 * @param desc  the given bundle description
181 	 *
182 	 * @return the plug-in model associated with the given bundle description if such a
183 	 * model exists.
184 	 */
getModel(BundleDescription desc)185 	public IPluginModelBase getModel(BundleDescription desc) {
186 		if (desc == null) {
187 			return null;
188 		}
189 
190 		for (int i = 0; i < fWorkspaceEntries.size(); i++) {
191 			IPluginModelBase model = fWorkspaceEntries.get(i);
192 			if (desc.equals(model.getBundleDescription())) {
193 				return model;
194 			}
195 		}
196 		for (int i = 0; i < fExternalEntries.size(); i++) {
197 			IPluginModelBase model = fExternalEntries.get(i);
198 			if (desc.equals(model.getBundleDescription())) {
199 				return model;
200 			}
201 		}
202 		return null;
203 	}
204 
205 	/**
206 	 * Returns <code>true</code> if there are workspace plug-ins associated with the ID
207 	 * of this model entry; <code>false</code>otherwise.
208 	 *
209 	 * @return <code>true</code> if there are workspace plug-ins associated with the ID
210 	 * of this model entry; <code>false</code>otherwise.
211 	 */
hasWorkspaceModels()212 	public boolean hasWorkspaceModels() {
213 		return !fWorkspaceEntries.isEmpty();
214 	}
215 
216 	/**
217 	 * Returns <code>true</code> if there are target plug-ins associated with the ID
218 	 * of this model entry; <code>false</code>otherwise.
219 	 *
220 	 * @return <code>true</code> if there are target plug-ins associated with the ID
221 	 * of this model entry; <code>false</code>otherwise.
222 	 */
hasExternalModels()223 	public boolean hasExternalModels() {
224 		return !fExternalEntries.isEmpty();
225 	}
226 
227 }
228