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  *     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.*;
19 import org.eclipse.osgi.internal.framework.EquinoxContainer;
20 import org.eclipse.osgi.service.resolver.*;
21 import org.osgi.framework.Constants;
22 import org.osgi.framework.wiring.BundleRevision;
23 
24 public class ImportPackageSpecificationImpl extends VersionConstraintImpl implements ImportPackageSpecification {
25 	private String resolution = ImportPackageSpecification.RESOLUTION_STATIC; // the default is static
26 	private String symbolicName;
27 	private VersionRange bundleVersionRange;
28 	private Map<String, Object> attributes;
29 	private Map<String, String> arbitraryDirectives;
30 
getDirectives()31 	public Map<String, Object> getDirectives() {
32 		synchronized (this.monitor) {
33 			Map<String, Object> result = new HashMap<>(5);
34 			if (resolution != null)
35 				result.put(Constants.RESOLUTION_DIRECTIVE, resolution);
36 			return result;
37 		}
38 	}
39 
getDirective(String key)40 	public Object getDirective(String key) {
41 		synchronized (this.monitor) {
42 			if (key.equals(Constants.RESOLUTION_DIRECTIVE))
43 				return resolution;
44 			return null;
45 		}
46 	}
47 
setDirective(String key, Object value)48 	Object setDirective(String key, Object value) {
49 		synchronized (this.monitor) {
50 			if (key.equals(Constants.RESOLUTION_DIRECTIVE))
51 				return resolution = (String) value;
52 			return null;
53 		}
54 	}
55 
setDirectives(Map<String, ?> directives)56 	void setDirectives(Map<String, ?> directives) {
57 		synchronized (this.monitor) {
58 			if (directives == null)
59 				return;
60 			resolution = (String) directives.get(Constants.RESOLUTION_DIRECTIVE);
61 		}
62 	}
63 
64 	@SuppressWarnings("unchecked")
setArbitraryDirectives(Map<String, ?> directives)65 	void setArbitraryDirectives(Map<String, ?> directives) {
66 		synchronized (this.monitor) {
67 			this.arbitraryDirectives = (Map<String, String>) directives;
68 		}
69 	}
70 
getArbitraryDirectives()71 	Map<String, String> getArbitraryDirectives() {
72 		synchronized (this.monitor) {
73 			return arbitraryDirectives;
74 		}
75 	}
76 
getBundleSymbolicName()77 	public String getBundleSymbolicName() {
78 		synchronized (this.monitor) {
79 			if (Constants.SYSTEM_BUNDLE_SYMBOLICNAME.equals(symbolicName)) {
80 				StateImpl state = (StateImpl) getBundle().getContainingState();
81 				return state == null ? EquinoxContainer.NAME : state.getSystemBundle();
82 			}
83 			return symbolicName;
84 		}
85 	}
86 
getBundleVersionRange()87 	public VersionRange getBundleVersionRange() {
88 		synchronized (this.monitor) {
89 			if (bundleVersionRange == null)
90 				return VersionRange.emptyRange;
91 			return bundleVersionRange;
92 		}
93 	}
94 
getAttributes()95 	public Map<String, Object> getAttributes() {
96 		synchronized (this.monitor) {
97 			return attributes;
98 		}
99 	}
100 
101 	@Override
isSatisfiedBy(BaseDescription supplier)102 	public boolean isSatisfiedBy(BaseDescription supplier) {
103 		return isSatisfiedBy(supplier, true);
104 	}
105 
isSatisfiedBy(BaseDescription supplier, boolean checkEE)106 	public boolean isSatisfiedBy(BaseDescription supplier, boolean checkEE) {
107 		if (!(supplier instanceof ExportPackageDescription))
108 			return false;
109 		ExportPackageDescriptionImpl pkgDes = (ExportPackageDescriptionImpl) supplier;
110 
111 		// If we are in strict mode, check to see if the export specifies friends.
112 		// If it does, are we one of the friends
113 		String[] friends = (String[]) pkgDes.getDirective(StateImpl.FRIENDS_DIRECTIVE);
114 		Boolean internal = (Boolean) pkgDes.getDirective(StateImpl.INTERNAL_DIRECTIVE);
115 		if (internal.booleanValue() || friends != null) {
116 			StateImpl state = (StateImpl) getBundle().getContainingState();
117 			boolean strict = state == null ? false : state.inStrictMode();
118 			if (strict) {
119 				if (internal.booleanValue())
120 					return false;
121 				boolean found = false;
122 				if (friends != null && getBundle().getSymbolicName() != null)
123 					for (String friend : friends) {
124 						if (getBundle().getSymbolicName().equals(friend)) {
125 							found = true;
126 						}
127 					}
128 				if (!found)
129 					return false;
130 			}
131 		}
132 		String exporterSymbolicName = getBundleSymbolicName();
133 		if (exporterSymbolicName != null) {
134 			BundleDescription exporter = pkgDes.getExporter();
135 			if (!exporterSymbolicName.equals(exporter.getSymbolicName()))
136 				return false;
137 			if (getBundleVersionRange() != null && !getBundleVersionRange().isIncluded(exporter.getVersion()))
138 				return false;
139 		}
140 
141 		String name = getName();
142 		// shortcut '*'
143 		// NOTE: wildcards are supported only in cases where this is a dynamic import
144 		if (!"*".equals(name) && !(name.endsWith(".*") && pkgDes.getName().startsWith(name.substring(0, name.length() - 1))) && !pkgDes.getName().equals(name)) //$NON-NLS-1$ //$NON-NLS-2$
145 			return false;
146 		if (getVersionRange() != null && !getVersionRange().isIncluded(pkgDes.getVersion()))
147 			return false;
148 
149 		Map<String, ?> importAttrs = getAttributes();
150 		if (importAttrs != null) {
151 			Map<String, ?> exportAttrs = pkgDes.getAttributes();
152 			if (exportAttrs == null)
153 				return false;
154 			for (String importKey : importAttrs.keySet()) {
155 				Object importValue = importAttrs.get(importKey);
156 				Object exportValue = exportAttrs.get(importKey);
157 				if (exportValue == null || !importValue.equals(exportValue))
158 					return false;
159 			}
160 		}
161 		String[] mandatory = (String[]) pkgDes.getDirective(Constants.MANDATORY_DIRECTIVE);
162 		if (!hasMandatoryAttributes(mandatory))
163 			return false;
164 		// finally check the ee index
165 		if (!checkEE)
166 			return true;
167 		if (((BundleDescriptionImpl) getBundle()).getEquinoxEE() < 0)
168 			return true;
169 		int eeIndex = ((Integer) pkgDes.getDirective(ExportPackageDescriptionImpl.EQUINOX_EE)).intValue();
170 		return eeIndex < 0 || eeIndex == ((BundleDescriptionImpl) getBundle()).getEquinoxEE();
171 	}
172 
173 	@Override
hasMandatoryAttributes(String[] checkMandatory)174 	protected boolean hasMandatoryAttributes(String[] checkMandatory) {
175 		if (checkMandatory != null) {
176 			Map<String, ?> importAttrs = getAttributes();
177 			for (String mandatory : checkMandatory) {
178 				if (Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE.equals(mandatory)) {
179 					if (getBundleSymbolicName() == null)
180 						return false;
181 				} else if (Constants.BUNDLE_VERSION_ATTRIBUTE.equals(mandatory)) {
182 					if (bundleVersionRange == null)
183 						return false;
184 				} else if (Constants.PACKAGE_SPECIFICATION_VERSION.equals(mandatory) || Constants.VERSION_ATTRIBUTE.equals(mandatory)) {
185 					if (getVersionRange() == null)
186 						return false;
187 				} else { // arbitrary attribute
188 					if (importAttrs == null)
189 						return false;
190 					if (importAttrs.get(mandatory) == null) {
191 						return false;
192 					}
193 				}
194 			}
195 		}
196 		return true;
197 	}
198 
setBundleSymbolicName(String symbolicName)199 	protected void setBundleSymbolicName(String symbolicName) {
200 		synchronized (this.monitor) {
201 			this.symbolicName = symbolicName;
202 		}
203 	}
204 
setBundleVersionRange(VersionRange bundleVersionRange)205 	protected void setBundleVersionRange(VersionRange bundleVersionRange) {
206 		synchronized (this.monitor) {
207 			this.bundleVersionRange = bundleVersionRange;
208 		}
209 	}
210 
211 	@SuppressWarnings("unchecked")
setAttributes(Map<String, ?> attributes)212 	protected void setAttributes(Map<String, ?> attributes) {
213 		synchronized (this.monitor) {
214 			this.attributes = (Map<String, Object>) attributes;
215 		}
216 	}
217 
218 	@Override
toString()219 	public String toString() {
220 		return "Import-Package: " + getName() + "; version=\"" + getVersionRange() + "\""; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
221 	}
222 
223 	@Override
getInternalDirectives()224 	protected Map<String, String> getInternalDirectives() {
225 		synchronized (this.monitor) {
226 			Map<String, String> result = new HashMap<>(5);
227 			if (arbitraryDirectives != null)
228 				result.putAll(arbitraryDirectives);
229 			if (resolution != null) {
230 				if (ImportPackageSpecification.RESOLUTION_STATIC.equals(resolution))
231 					result.put(Constants.RESOLUTION_DIRECTIVE, Constants.RESOLUTION_MANDATORY);
232 				else
233 					result.put(Constants.RESOLUTION_DIRECTIVE, resolution);
234 			}
235 			result.put(Constants.FILTER_DIRECTIVE, createFilterDirective());
236 			return result;
237 		}
238 	}
239 
createFilterDirective()240 	private String createFilterDirective() {
241 		StringBuilder filter = new StringBuilder();
242 		filter.append("(&"); //$NON-NLS-1$
243 		synchronized (this.monitor) {
244 			addFilterAttribute(filter, BundleRevision.PACKAGE_NAMESPACE, getName(), false);
245 			VersionRange range = getVersionRange();
246 			if (range != null && range != VersionRange.emptyRange)
247 				addFilterAttribute(filter, Constants.VERSION_ATTRIBUTE, range);
248 			if (symbolicName != null)
249 				addFilterAttribute(filter, Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE, symbolicName);
250 			if (bundleVersionRange != null)
251 				addFilterAttribute(filter, Constants.BUNDLE_VERSION_ATTRIBUTE, bundleVersionRange);
252 			if (attributes != null)
253 				addFilterAttributes(filter, attributes);
254 		}
255 		filter.append(')');
256 		return filter.toString();
257 	}
258 
259 	@Override
getInteralAttributes()260 	protected Map<String, Object> getInteralAttributes() {
261 		return Collections.<String, Object> emptyMap();
262 	}
263 
264 	@Override
getInternalNameSpace()265 	protected String getInternalNameSpace() {
266 		return BundleRevision.PACKAGE_NAMESPACE;
267 	}
268 }
269