1 /*******************************************************************************
2  * Copyright (c) 2003, 2016 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  *     Rob Harrop - SpringSource Inc. (bug 247522)
14  *******************************************************************************/
15 package org.eclipse.osgi.internal.resolver;
16 
17 import java.util.*;
18 import org.eclipse.osgi.service.resolver.*;
19 
20 /**
21  * This class is threadsafe.
22  *
23  */
24 final class StateDeltaImpl implements StateDelta {
25 
26 	private final State state;
27 
28 	private final Map<BundleDescription, BundleDelta> changes = new HashMap<>();
29 	private ResolverHookException error;
30 
StateDeltaImpl(State state)31 	public StateDeltaImpl(State state) {
32 		this.state = state;
33 	}
34 
getChanges()35 	public BundleDelta[] getChanges() {
36 		synchronized (this.changes) {
37 			return changes.values().toArray(new BundleDelta[changes.size()]);
38 		}
39 	}
40 
getChanges(int mask, boolean exact)41 	public BundleDelta[] getChanges(int mask, boolean exact) {
42 		synchronized (this.changes) {
43 			List<BundleDelta> result = new ArrayList<>();
44 			for (Iterator<BundleDelta> changesIter = changes.values().iterator(); changesIter.hasNext();) {
45 				BundleDelta change = changesIter.next();
46 				if (mask == change.getType() || (!exact && (change.getType() & mask) != 0))
47 					result.add(change);
48 			}
49 			return result.toArray(new BundleDelta[result.size()]);
50 		}
51 	}
52 
getState()53 	public State getState() {
54 		return state;
55 	}
56 
getResovlerHookException()57 	public ResolverHookException getResovlerHookException() {
58 		return error;
59 	}
60 
setResolverHookException(ResolverHookException error)61 	void setResolverHookException(ResolverHookException error) {
62 		this.error = error;
63 	}
64 
recordBundleAdded(BundleDescriptionImpl added)65 	void recordBundleAdded(BundleDescriptionImpl added) {
66 		synchronized (this.changes) {
67 			BundleDeltaImpl change = (BundleDeltaImpl) changes.get(added);
68 			if (change == null) {
69 				changes.put(added, new BundleDeltaImpl(added, BundleDelta.ADDED));
70 				return;
71 			}
72 			if (change.getType() == BundleDelta.REMOVED) {
73 				changes.remove(added);
74 				return;
75 			}
76 			int newType = change.getType();
77 			if ((newType & BundleDelta.REMOVED) != 0)
78 				newType &= ~BundleDelta.REMOVED;
79 			change.setType(newType | BundleDelta.ADDED);
80 			change.setBundle(added);
81 		}
82 	}
83 
recordBundleUpdated(BundleDescriptionImpl updated)84 	void recordBundleUpdated(BundleDescriptionImpl updated) {
85 		synchronized (this.changes) {
86 			BundleDeltaImpl change = (BundleDeltaImpl) changes.get(updated);
87 			if (change == null) {
88 				changes.put(updated, new BundleDeltaImpl(updated, BundleDelta.UPDATED));
89 				return;
90 			}
91 			if ((change.getType() & (BundleDelta.ADDED | BundleDelta.REMOVED)) != 0)
92 				return;
93 			change.setType(change.getType() | BundleDelta.UPDATED);
94 			change.setBundle(updated);
95 		}
96 	}
97 
recordBundleRemoved(BundleDescriptionImpl removed)98 	void recordBundleRemoved(BundleDescriptionImpl removed) {
99 		synchronized (this.changes) {
100 			BundleDeltaImpl change = (BundleDeltaImpl) changes.get(removed);
101 			if (change == null) {
102 				changes.put(removed, new BundleDeltaImpl(removed, BundleDelta.REMOVED));
103 				return;
104 			}
105 			if (change.getType() == BundleDelta.ADDED) {
106 				changes.remove(removed);
107 				return;
108 			}
109 			int newType = change.getType();
110 			if ((newType & BundleDelta.ADDED) != 0)
111 				newType &= ~BundleDelta.ADDED;
112 			change.setType(newType | BundleDelta.REMOVED);
113 		}
114 	}
115 
recordBundleRemovalPending(BundleDescriptionImpl removed)116 	void recordBundleRemovalPending(BundleDescriptionImpl removed) {
117 		synchronized (this.changes) {
118 			BundleDeltaImpl change = (BundleDeltaImpl) changes.get(removed);
119 			if (change == null) {
120 				changes.put(removed, new BundleDeltaImpl(removed, BundleDelta.REMOVAL_PENDING));
121 				return;
122 			}
123 			int newType = change.getType();
124 			if ((newType & BundleDelta.REMOVAL_COMPLETE) != 0)
125 				newType &= ~BundleDelta.REMOVAL_COMPLETE;
126 			change.setType(newType | BundleDelta.REMOVAL_PENDING);
127 		}
128 	}
129 
recordBundleRemovalComplete(BundleDescriptionImpl removed)130 	void recordBundleRemovalComplete(BundleDescriptionImpl removed) {
131 		synchronized (this.changes) {
132 			BundleDeltaImpl change = (BundleDeltaImpl) changes.get(removed);
133 			if (change == null) {
134 				changes.put(removed, new BundleDeltaImpl(removed, BundleDelta.REMOVAL_COMPLETE));
135 				return;
136 			}
137 			int newType = change.getType();
138 			if ((newType & BundleDelta.REMOVAL_PENDING) != 0)
139 				newType &= ~BundleDelta.REMOVAL_PENDING;
140 			change.setType(newType | BundleDelta.REMOVAL_COMPLETE);
141 		}
142 	}
143 
recordBundleResolved(BundleDescriptionImpl resolved, boolean result)144 	void recordBundleResolved(BundleDescriptionImpl resolved, boolean result) {
145 		synchronized (this.changes) {
146 			if (resolved.isResolved() == result)
147 				return; // do not record anything if nothing has changed
148 			BundleDeltaImpl change = (BundleDeltaImpl) changes.get(resolved);
149 			int newType = result ? BundleDelta.RESOLVED : BundleDelta.UNRESOLVED;
150 			if (change == null) {
151 				change = new BundleDeltaImpl(resolved, newType);
152 				changes.put(resolved, change);
153 				return;
154 			}
155 			// new type will have only one of RESOLVED|UNRESOLVED bits set
156 			newType = newType | (change.getType() & ~(BundleDelta.RESOLVED | BundleDelta.UNRESOLVED));
157 			change.setType(newType);
158 			change.setBundle(resolved);
159 		}
160 	}
161 }
162