1 /*******************************************************************************
2  * Copyright (c) 2003, 2012 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  *     Danail Nachev -  ProSyst - bug 218625
14  *     Rob Harrop - SpringSource Inc. (bug 247522)
15  *******************************************************************************/
16 package org.eclipse.osgi.internal.resolver;
17 
18 import java.util.Collections;
19 import java.util.Map;
20 import org.eclipse.osgi.internal.framework.EquinoxContainer;
21 import org.eclipse.osgi.internal.resolver.BaseDescriptionImpl.BaseCapability;
22 import org.eclipse.osgi.service.resolver.*;
23 import org.eclipse.osgi.service.resolver.VersionRange;
24 import org.eclipse.osgi.util.ManifestElement;
25 import org.osgi.framework.*;
26 import org.osgi.framework.namespace.AbstractWiringNamespace;
27 import org.osgi.framework.wiring.*;
28 import org.osgi.resource.Capability;
29 import org.osgi.resource.Namespace;
30 
31 abstract class VersionConstraintImpl implements VersionConstraint {
32 
33 	protected final Object monitor = new Object();
34 
35 	private String name;
36 	private VersionRange versionRange;
37 	private BundleDescription bundle;
38 	private BaseDescription supplier;
39 	private volatile Object userObject;
40 
getName()41 	public String getName() {
42 		synchronized (this.monitor) {
43 			if (Constants.SYSTEM_BUNDLE_SYMBOLICNAME.equals(name)) {
44 				StateImpl state = (StateImpl) getBundle().getContainingState();
45 				return state == null ? EquinoxContainer.NAME : state.getSystemBundle();
46 			}
47 			return name;
48 		}
49 	}
50 
getVersionRange()51 	public VersionRange getVersionRange() {
52 		synchronized (this.monitor) {
53 			if (versionRange == null)
54 				return VersionRange.emptyRange;
55 			return versionRange;
56 		}
57 	}
58 
getBundle()59 	public BundleDescription getBundle() {
60 		synchronized (this.monitor) {
61 			return bundle;
62 		}
63 	}
64 
isResolved()65 	public boolean isResolved() {
66 		synchronized (this.monitor) {
67 			return supplier != null;
68 		}
69 	}
70 
getSupplier()71 	public BaseDescription getSupplier() {
72 		synchronized (this.monitor) {
73 			return supplier;
74 		}
75 	}
76 
isSatisfiedBy(BaseDescription candidate)77 	public boolean isSatisfiedBy(BaseDescription candidate) {
78 		synchronized (this.monitor) {
79 			return false;
80 		}
81 	}
82 
setName(String name)83 	protected void setName(String name) {
84 		synchronized (this.monitor) {
85 			this.name = name;
86 		}
87 	}
88 
setVersionRange(VersionRange versionRange)89 	protected void setVersionRange(VersionRange versionRange) {
90 		synchronized (this.monitor) {
91 			this.versionRange = versionRange;
92 		}
93 	}
94 
setBundle(BundleDescription bundle)95 	protected void setBundle(BundleDescription bundle) {
96 		synchronized (this.monitor) {
97 			this.bundle = bundle;
98 		}
99 	}
100 
setSupplier(BaseDescription supplier)101 	protected void setSupplier(BaseDescription supplier) {
102 		synchronized (this.monitor) {
103 			this.supplier = supplier;
104 		}
105 	}
106 
getInternalNameSpace()107 	protected abstract String getInternalNameSpace();
108 
getInternalDirectives()109 	protected abstract Map<String, String> getInternalDirectives();
110 
getInteralAttributes()111 	protected abstract Map<String, Object> getInteralAttributes();
112 
hasMandatoryAttributes(String[] mandatory)113 	protected abstract boolean hasMandatoryAttributes(String[] mandatory);
114 
getRequirement()115 	public BundleRequirement getRequirement() {
116 		String namespace = getInternalNameSpace();
117 		if (namespace == null)
118 			return null;
119 		return new BundleRequirementImpl(namespace);
120 	}
121 
getUserObject()122 	public Object getUserObject() {
123 		return userObject;
124 	}
125 
setUserObject(Object userObject)126 	public void setUserObject(Object userObject) {
127 		this.userObject = userObject;
128 	}
129 
130 	class BundleRequirementImpl implements BundleRequirement {
131 		private final String namespace;
132 
BundleRequirementImpl(String namespace)133 		public BundleRequirementImpl(String namespace) {
134 			this.namespace = namespace;
135 		}
136 
getNamespace()137 		public String getNamespace() {
138 			return namespace;
139 		}
140 
getDirectives()141 		public Map<String, String> getDirectives() {
142 			return Collections.unmodifiableMap(getInternalDirectives());
143 		}
144 
getAttributes()145 		public Map<String, Object> getAttributes() {
146 			return Collections.unmodifiableMap(getInteralAttributes());
147 		}
148 
getRevision()149 		public BundleRevision getRevision() {
150 			return getBundle();
151 		}
152 
matches(BundleCapability capability)153 		public boolean matches(BundleCapability capability) {
154 			return isSatisfiedBy(((BaseCapability) capability).getBaseDescription());
155 		}
156 
157 		@Override
hashCode()158 		public int hashCode() {
159 			return System.identityHashCode(VersionConstraintImpl.this);
160 		}
161 
getVersionConstraint()162 		private VersionConstraintImpl getVersionConstraint() {
163 			return VersionConstraintImpl.this;
164 		}
165 
166 		@Override
equals(Object obj)167 		public boolean equals(Object obj) {
168 			if (this == obj)
169 				return true;
170 			if (!(obj instanceof BundleRequirementImpl))
171 				return false;
172 			return ((BundleRequirementImpl) obj).getVersionConstraint() == VersionConstraintImpl.this;
173 		}
174 
175 		@Override
toString()176 		public String toString() {
177 			return getNamespace() + BaseDescriptionImpl.toString(getAttributes(), false) + BaseDescriptionImpl.toString(getDirectives(), true);
178 		}
179 
matches(Capability capability)180 		public boolean matches(Capability capability) {
181 			if (capability instanceof BundleCapability)
182 				return matches((BundleCapability) capability);
183 			// now we must do the generic thing
184 			if (!namespace.equals(capability.getNamespace()))
185 				return false;
186 			String filterSpec = getDirectives().get(Namespace.REQUIREMENT_FILTER_DIRECTIVE);
187 			try {
188 				if (filterSpec != null && !FrameworkUtil.createFilter(filterSpec).matches(capability.getAttributes()))
189 					return false;
190 			} catch (InvalidSyntaxException e) {
191 				return false;
192 			}
193 			return hasMandatoryAttributes(ManifestElement.getArrayFromList(capability.getDirectives().get(AbstractWiringNamespace.CAPABILITY_MANDATORY_DIRECTIVE)));
194 		}
195 
getResource()196 		public BundleRevision getResource() {
197 			return getRevision();
198 		}
199 	}
200 
addFilterAttributes(StringBuilder filter, Map<String, ?> attributes)201 	static StringBuilder addFilterAttributes(StringBuilder filter, Map<String, ?> attributes) {
202 		for (Map.Entry<String, ?> entry : attributes.entrySet()) {
203 			addFilterAttribute(filter, entry.getKey(), entry.getValue());
204 		}
205 		return filter;
206 	}
207 
addFilterAttribute(StringBuilder filter, String attr, Object value)208 	static StringBuilder addFilterAttribute(StringBuilder filter, String attr, Object value) {
209 		return addFilterAttribute(filter, attr, value, true);
210 	}
211 
addFilterAttribute(StringBuilder filter, String attr, Object value, boolean escapeWildCard)212 	static StringBuilder addFilterAttribute(StringBuilder filter, String attr, Object value, boolean escapeWildCard) {
213 		if (value instanceof VersionRange) {
214 			VersionRange range = (VersionRange) value;
215 			filter.append(range.toFilterString(attr));
216 		} else {
217 			filter.append('(').append(attr).append('=').append(escapeValue(value, escapeWildCard)).append(')');
218 		}
219 		return filter;
220 	}
221 
escapeValue(Object o, boolean escapeWildCard)222 	private static String escapeValue(Object o, boolean escapeWildCard) {
223 		String value = o.toString();
224 		boolean escaped = false;
225 		int inlen = value.length();
226 		int outlen = inlen << 1; /* inlen * 2 */
227 
228 		char[] output = new char[outlen];
229 		value.getChars(0, inlen, output, inlen);
230 
231 		int cursor = 0;
232 		for (int i = inlen; i < outlen; i++) {
233 			char c = output[i];
234 			switch (c) {
235 				case '*' :
236 					if (!escapeWildCard)
237 						break;
238 				case '\\' :
239 				case '(' :
240 				case ')' :
241 					output[cursor] = '\\';
242 					cursor++;
243 					escaped = true;
244 					break;
245 			}
246 
247 			output[cursor] = c;
248 			cursor++;
249 		}
250 
251 		return escaped ? new String(output, 0, cursor) : value;
252 	}
253 }
254