001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019 package org.apache.felix.framework.util.manifestparser;
020
021 import org.apache.felix.framework.util.MapToDictionary;
022 import org.apache.felix.framework.util.VersionRange;
023 import org.apache.felix.moduleloader.ICapability;
024 import org.apache.felix.moduleloader.IRequirement;
025 import org.osgi.framework.*;
026
027 public class Requirement implements IRequirement
028 {
029 private final String m_namespace;
030 private final R4Directive[] m_directives;
031 private final R4Attribute[] m_attributes;
032 private final boolean m_isOptional;
033
034 private final String m_targetName;
035 private final VersionRange m_targetVersionRange;
036 private volatile Filter m_filter;
037
038 public Requirement(String namespace, String filterStr) throws InvalidSyntaxException
039 {
040 m_namespace = namespace;
041 m_filter = FrameworkUtil.createFilter(filterStr);
042 m_directives = null;
043 m_attributes = null;
044 m_isOptional = false;
045 m_targetName = null;
046 m_targetVersionRange = null;
047 }
048
049 public Requirement(String namespace, R4Directive[] directives, R4Attribute[] attributes)
050 {
051 m_namespace = namespace;
052 m_directives = directives;
053 m_attributes = attributes;
054 m_filter = null;
055
056 // Find all import directives: resolution.
057 boolean optional = false;
058 for (int i = 0; (m_directives != null) && (i < m_directives.length); i++)
059 {
060 if (m_directives[i].getName().equals(Constants.RESOLUTION_DIRECTIVE))
061 {
062 optional = m_directives[i].getValue().equals(Constants.RESOLUTION_OPTIONAL);
063 }
064 }
065 m_isOptional = optional;
066
067 String targetName = null;
068 VersionRange targetVersionRange = VersionRange.infiniteRange;
069 for (int i = 0; i < m_attributes.length; i++)
070 {
071 if (m_namespace.equals(ICapability.MODULE_NAMESPACE))
072 {
073 if (m_attributes[i].getName().equals(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE))
074 {
075 targetName = (String) m_attributes[i].getValue();
076 }
077 else if (m_attributes[i].getName().equals(Constants.BUNDLE_VERSION_ATTRIBUTE))
078 {
079 targetVersionRange = (VersionRange) m_attributes[i].getValue();
080 }
081 }
082 else if (m_namespace.equals(ICapability.PACKAGE_NAMESPACE))
083 {
084 if (m_attributes[i].getName().equals(ICapability.PACKAGE_PROPERTY))
085 {
086 targetName = (String) m_attributes[i].getValue();
087 }
088 else if (m_attributes[i].getName().equals(ICapability.VERSION_PROPERTY))
089 {
090 targetVersionRange = (VersionRange) m_attributes[i].getValue();
091 }
092 }
093 else if (m_namespace.equals(ICapability.HOST_NAMESPACE))
094 {
095 if (m_attributes[i].getName().equals(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE))
096 {
097 targetName = (String) m_attributes[i].getValue();
098 }
099 else if (m_attributes[i].getName().equals(Constants.BUNDLE_VERSION_ATTRIBUTE))
100 {
101 targetVersionRange = (VersionRange) m_attributes[i].getValue();
102 }
103 }
104 }
105 m_targetName = targetName;
106 m_targetVersionRange = targetVersionRange;
107 }
108
109 public String getNamespace()
110 {
111 return m_namespace;
112 }
113
114 public Filter getFilter()
115 {
116 if (m_filter == null)
117 {
118 m_filter = convertToFilter();
119 }
120 return m_filter;
121 }
122
123 // TODO: RB - We need to verify that the resolver code does not
124 // touch these implementation-specific methods.
125
126 public String getTargetName()
127 {
128 return m_targetName;
129 }
130
131 public VersionRange getTargetVersionRange()
132 {
133 return m_targetVersionRange;
134 }
135
136 public R4Directive[] getDirectives()
137 {
138 // TODO: RB - We should return copies of the arrays probably.
139 return m_directives;
140 }
141
142 public R4Attribute[] getAttributes()
143 {
144 // TODO: RB - We should return copies of the arrays probably.
145 return m_attributes;
146 }
147
148 public boolean isMultiple()
149 {
150 return false;
151 }
152
153 public boolean isOptional()
154 {
155 return m_isOptional;
156 }
157
158 public String getComment()
159 {
160 return "Comment for " + toString();
161 }
162
163 public boolean isSatisfied(ICapability capability)
164 {
165 // If the requirement was constructed with a filter, then
166 // we must use that filter for evaluation.
167 if ((m_attributes == null) && (m_filter != null))
168 {
169 return m_namespace.equals(capability.getNamespace()) &&
170 getFilter().match(new MapToDictionary(capability.getProperties()));
171 }
172 // Otherwise, if the requirement was constructed with attributes, then
173 // perform the evaluation manually instead of using the filter for
174 // performance reasons.
175 else if (m_attributes != null)
176 {
177 return capability.getNamespace().equals(getNamespace()) &&
178 doAttributesMatch((Capability) capability);
179 }
180
181 return false;
182 }
183
184 private boolean doAttributesMatch(Capability ec)
185 {
186 // Grab the capability's attributes.
187 R4Attribute[] capAttrs = ec.getAttributes();
188
189 // Cycle through all attributes of this import package
190 // and make sure its values match the attribute values
191 // of the specified export package.
192 for (int reqAttrIdx = 0; reqAttrIdx < m_attributes.length; reqAttrIdx++)
193 {
194 // Get current attribute from this import package.
195 R4Attribute reqAttr = m_attributes[reqAttrIdx];
196
197 // Check if the export package has the same attribute.
198 boolean found = false;
199 for (int capAttrIdx = 0;
200 (!found) && (capAttrIdx < capAttrs.length);
201 capAttrIdx++)
202 {
203 // Get current attribute for the export package.
204 R4Attribute capAttr = capAttrs[capAttrIdx];
205 // Check if the attribute names are equal.
206 if (reqAttr.getName().equals(capAttr.getName()))
207 {
208 // We only recognize version types. If the value of the
209 // attribute is a version/version range, then we use the
210 // "in range" comparison, otherwise we simply use equals().
211 if (capAttr.getValue() instanceof Version)
212 {
213 if (!((VersionRange) reqAttr.getValue()).isInRange((Version) capAttr.getValue()))
214 {
215 return false;
216 }
217 }
218 else if (capAttr.getValue() instanceof Object[])
219 {
220 Object[] values = (Object[]) capAttr.getValue();
221 boolean matched = false;
222 for (int valIdx = 0; !matched && (valIdx < values.length); valIdx++)
223 {
224 if (reqAttr.getValue().equals(values[valIdx]))
225 {
226 matched = true;
227 }
228 }
229 if (!matched)
230 {
231 return false;
232 }
233 }
234 else if (!reqAttr.getValue().equals(capAttr.getValue()))
235 {
236 return false;
237 }
238 found = true;
239 }
240 }
241 // If the attribute was not found, then return false.
242 if (!found)
243 {
244 return false;
245 }
246 }
247
248 // Now, cycle through all attributes of the export package and verify that
249 // all mandatory attributes are present in this import package.
250 for (int capAttrIdx = 0; capAttrIdx < capAttrs.length; capAttrIdx++)
251 {
252 // Get current attribute for this package.
253 R4Attribute capAttr = capAttrs[capAttrIdx];
254
255 // If the export attribute is mandatory, then make sure
256 // this import package has the attribute.
257 if (capAttr.isMandatory())
258 {
259 boolean found = false;
260 for (int reqAttrIdx = 0;
261 (!found) && (reqAttrIdx < m_attributes.length);
262 reqAttrIdx++)
263 {
264 // Get current attribute from specified package.
265 R4Attribute reqAttr = m_attributes[reqAttrIdx];
266
267 // Check if the attribute names are equal
268 // and set found flag.
269 if (capAttr.getName().equals(reqAttr.getName()))
270 {
271 found = true;
272 }
273 }
274 // If not found, then return false.
275 if (!found)
276 {
277 return false;
278 }
279 }
280 }
281
282 return true;
283 }
284
285 private Filter convertToFilter()
286 {
287 StringBuffer sb = new StringBuffer();
288 if ((m_attributes != null) && (m_attributes.length > 1))
289 {
290 sb.append("(&");
291 }
292 for (int i = 0; (m_attributes != null) && (i < m_attributes.length); i++)
293 {
294 // If this is a package import, then convert wild-carded
295 // dynamically imported package names to an OR comparison.
296 if (m_namespace.equals(ICapability.PACKAGE_NAMESPACE) &&
297 m_attributes[i].getName().equals(ICapability.PACKAGE_PROPERTY) &&
298 m_attributes[i].getValue().toString().endsWith(".*"))
299 {
300 int idx = m_attributes[i].getValue().toString().indexOf(".*");
301 sb.append("(|(package=");
302 sb.append(m_attributes[i].getValue().toString().substring(0, idx));
303 sb.append(")(package=");
304 sb.append(m_attributes[i].getValue().toString());
305 sb.append("))");
306 }
307 else if (m_attributes[i].getValue() instanceof VersionRange)
308 {
309 VersionRange vr = (VersionRange) m_attributes[i].getValue();
310 if (vr.isLowInclusive())
311 {
312 sb.append("(");
313 sb.append(m_attributes[i].getName());
314 sb.append(">=");
315 sb.append(vr.getLow().toString());
316 sb.append(")");
317 }
318 else
319 {
320 sb.append("(!(");
321 sb.append(m_attributes[i].getName());
322 sb.append("<=");
323 sb.append(vr.getLow().toString());
324 sb.append("))");
325 }
326
327 if (vr.getHigh() != null)
328 {
329 if (vr.isHighInclusive())
330 {
331 sb.append("(");
332 sb.append(m_attributes[i].getName());
333 sb.append("<=");
334 sb.append(vr.getHigh().toString());
335 sb.append(")");
336 }
337 else
338 {
339 sb.append("(!(");
340 sb.append(m_attributes[i].getName());
341 sb.append(">=");
342 sb.append(vr.getHigh().toString());
343 sb.append("))");
344 }
345 }
346 }
347 else
348 {
349 sb.append("(");
350 sb.append(m_attributes[i].getName());
351 sb.append("=");
352 sb.append(m_attributes[i].getValue().toString());
353 sb.append(")");
354 }
355 }
356
357 if ((m_attributes != null) && (m_attributes.length > 1))
358 {
359 sb.append(")");
360 }
361
362 try
363 {
364 return FrameworkUtil.createFilter(sb.toString());
365 }
366 catch (InvalidSyntaxException ex)
367 {
368 // This should never happen, so we can safely ignore.
369 }
370
371 return null;
372 }
373
374 public String toString()
375 {
376 return getNamespace() + "; " + getFilter().toString();
377 }
378 }