Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 6bc33fb79738c0cbb0206e2329d656cbfa02890c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
/*******************************************************************************
 * Copyright (c) 2010, 2011 Wind River Systems and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     Wind River Systems - initial API and implementation
 *******************************************************************************/
package org.eclipse.cdt.dsf.ui.viewmodel.properties;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.eclipse.cdt.dsf.concurrent.DsfMultiStatus;
import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin;
import org.eclipse.core.runtime.IStatus;

/**
 * Status object for use with the IPropertiesUpdate.  This status class
 * allows setting a different status result for each property.  This allows
 * for better interpretation of status by the client of the update.
 * <p>
 * This status class derives from MultiStatus class such that the status
 * objects for each property can be accessed through the standard
 * {@link #getChildren()} method.  Also, multiple properties can reference
 * the same status object, meaning that the number of properties returned
 * by {@link #getProperties()} may be greater than the status objects
 * returned by <code>getChildren()</code>.
 * <p>
 * The properties status object does not have its own message, severity,
 * error status or exception.  All these attributes are calculated from
 * the child status objects.  If the status has more than one status child,
 * the String returned by {@link #getMessage()} is: "Multiple errors reported".
 *
 * @since 2.2
 */
public class PropertiesUpdateStatus extends DsfMultiStatus {

	final private Map<String, IStatus> fPropertiesStatus = new HashMap<>(1);
	private boolean fFirstStatusSet;

	public PropertiesUpdateStatus() {
		super(DsfUIPlugin.PLUGIN_ID, 0, "", null); //$NON-NLS-1$
	}

	/**
	 * Returns set of properties that have an additional status specified.
	 */
	public Set<String> getProperties() {
		return fPropertiesStatus.keySet();
	}

	/**
	 * Returns an additional status for the given property in a property
	 * update.  Returned value may be <code>null</code> if no additional
	 * status is given.
	 */
	public IStatus getStatus(String property) {
		return fPropertiesStatus.get(property);
	}

	/**
	 * Sets the given status for the given property.
	 */
	public void setStatus(String property, IStatus status) {
		IStatus child = findEquivalentChild(status);
		if (child != null) {
			status = child;
		} else {
			add(status);
		}

		fPropertiesStatus.put(property, status);
	}

	/**
	 * Sets the given status for the properties array.
	 */
	public void setStatus(String[] properties, IStatus status) {
		IStatus child = findEquivalentChild(status);
		if (child != null) {
			status = child;
		} else {
			add(status);
		}

		for (String property : properties) {
			fPropertiesStatus.put(property, status);
		}
	}

	/**
	 * Merges data in the new status into the base status data, and returns the
	 * resulting status. Only properties specified in the given set are merged.
	 * <p>
	 * The new status is considered to be more up to date than the base
	 * status and its data overrides the base status .  If the base status
	 * holds an error for a given property, which is found in the
	 * given set, and the new status does not, then the base error status is
	 * removed.
	 *
	 * @param baseStatus Properties into which the new status properties will
	 * be merged.
	 * @param newStatus Properties status to merge.
	 * @param properties The properties to consider in the new status.
	 * @return Resulting merged status object.
	 */
	public static PropertiesUpdateStatus mergePropertiesStatus(PropertiesUpdateStatus baseStatus,
			PropertiesUpdateStatus newStatus, Set<String> properties) {
		PropertiesUpdateStatus mergedStatus = new PropertiesUpdateStatus();
		// Copy the property status map from the base status.
		mergedStatus.fPropertiesStatus.putAll(baseStatus.fPropertiesStatus);

		// Add in the property statuses from the new status, but only for the
		// specified properties.
		for (String property : properties) {
			IStatus propertyStatus = newStatus.getStatus(property);
			if (propertyStatus != null) {
				mergedStatus.fPropertiesStatus.put(property, propertyStatus);
			} else {
				mergedStatus.fPropertiesStatus.remove(property);
			}
		}

		// Children of merged status should contain all statuses that are found in the fPropertiesStatus map, but
		// without duplicates.
		Set<IStatus> children = new HashSet<>(
				(baseStatus.getChildren().length + newStatus.getChildren().length) * 4 / 3);
		children.addAll(mergedStatus.fPropertiesStatus.values());
		for (IStatus child : children) {
			mergedStatus.add(child);
		}

		// Merged status should contain all children statuses that were added without a corresponding property to the
		// base status and to the new status.
		Collection<IStatus> baseStatusPropertyChildren = baseStatus.fPropertiesStatus.values();
		for (IStatus baseStatusChild : baseStatus.getChildren()) {
			if (!baseStatusPropertyChildren.contains(baseStatusChild)) {
				mergedStatus.add(baseStatusChild);
			}
		}
		Collection<IStatus> newStatusPropertyChildren = newStatus.fPropertiesStatus.values();
		for (IStatus newStatusChild : newStatus.getChildren()) {
			if (!newStatusPropertyChildren.contains(newStatusChild)) {
				mergedStatus.add(newStatusChild);
			}
		}

		return mergedStatus;
	}

	/**
	 * Adds the given status object as a child of this status.  If there's an
	 * equivalent child status already, the new status is ignored.
	 */
	@Override
	public void add(IStatus status) {
		if (findEquivalentChild(status) != null) {
			return;
		}

		super.add(status);

		boolean firstSet;
		synchronized (this) {
			firstSet = fFirstStatusSet;
			fFirstStatusSet = true;
		}

		if (!firstSet) {
			setMessage(status.getMessage());
		} else {
			setMessage(MessagesForProperties.PropertiesUpdateStatus_message);
		}
	}

	/**
	 * Finds a child status that is equivalent to the given status.
	 */
	private IStatus findEquivalentChild(IStatus status) {
		if (getChildren().length != 0) {
			for (IStatus child : getChildren()) {
				if (areEquivalent(child, status)) {
					return child;
				}
			}
		}
		return null;
	}

	/**
	 * Compares two status objects to determine if they are equivalent.
	 */
	private boolean areEquivalent(IStatus s1, IStatus s2) {
		if ((s1 == null && s2 != null) || (s1 != null && s2 == null)) {
			return false;
		}
		if (s1 == null) {
			return true;
		}
		if ((s1.getSeverity() != s2.getSeverity()) || !s1.getPlugin().equals(s2.getPlugin())
				|| (s1.getCode() != s2.getCode())) {
			return false;
		}
		if ((s1.getException() == null && s1.getException() != null)
				|| (s1.getException() != null && s1.getException() == null)
				|| (s1.getException() != null && !s1.getException().equals(s2.getException()))) {
			return false;
		}
		return s1.getMessage().equals(s2.getMessage());
	}

	/**
	 * Convenience method that returns and optionally creates a properties
	 * update status object for the given update.
	 */
	public static PropertiesUpdateStatus getPropertiesStatus(IPropertiesUpdate update) {
		IStatus updateStatus = update.getStatus();
		if (updateStatus instanceof PropertiesUpdateStatus) {
			return (PropertiesUpdateStatus) updateStatus;
		} else {
			PropertiesUpdateStatus propertiesStatus = new PropertiesUpdateStatus();
			update.setStatus(propertiesStatus);
			if (!updateStatus.isOK()) {
				propertiesStatus.add(updateStatus);
			}
			return propertiesStatus;
		}
	}

	/**
	 * Convenience method that returns and optionally creates a properties
	 * update status object for the given update.
	 */
	public static PropertiesUpdateStatus makePropertiesStatus(IStatus updateStatus) {
		if (updateStatus instanceof PropertiesUpdateStatus) {
			return (PropertiesUpdateStatus) updateStatus;
		} else {
			PropertiesUpdateStatus propertiesStatus = new PropertiesUpdateStatus();
			if (!updateStatus.isOK()) {
				propertiesStatus.add(updateStatus);
			}
			return propertiesStatus;
		}
	}
}

Back to the top