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 java.util.*;
022
023 import org.apache.felix.framework.util.Util;
024 import org.apache.felix.moduleloader.ICapability;
025 import org.apache.felix.moduleloader.IModule;
026 import org.osgi.framework.Constants;
027 import org.osgi.framework.Version;
028
029 public class Capability implements ICapability, Comparable
030 {
031 private final IModule m_module;
032 private final String m_namespace;
033 private final R4Directive[] m_directives;
034 private final R4Attribute[] m_attributes;
035 private final String[] m_uses;
036 private final String[][] m_includeFilter;
037 private final String[][] m_excludeFilter;
038 private volatile Map m_attrMap;
039
040 // Cached properties for performance reasons.
041 private final String m_pkgName;
042 private final Version m_pkgVersion;
043
044 public Capability(IModule module, String namespace, R4Directive[] dirs, R4Attribute[] attrs)
045 {
046 m_module = module;
047 m_namespace = namespace;
048 m_directives = dirs;
049 m_attributes = attrs;
050
051 // Find all export directives: uses, mandatory, include, and exclude.
052 String mandatory = "";
053 String[] uses = new String[0];
054 String[][] includeFilter = null, excludeFilter = null;
055 for (int dirIdx = 0; (m_directives != null) && (dirIdx < m_directives.length); dirIdx++)
056 {
057 if (m_directives[dirIdx].getName().equals(Constants.USES_DIRECTIVE))
058 {
059 // Parse these uses directive.
060 StringTokenizer tok = new StringTokenizer(m_directives[dirIdx].getValue(), ",");
061 uses = new String[tok.countTokens()];
062 for (int i = 0; i < uses.length; i++)
063 {
064 uses[i] = tok.nextToken().trim();
065 }
066 }
067 else if (m_directives[dirIdx].getName().equals(Constants.MANDATORY_DIRECTIVE))
068 {
069 mandatory = m_directives[dirIdx].getValue();
070 }
071 else if (m_directives[dirIdx].getName().equals(Constants.INCLUDE_DIRECTIVE))
072 {
073 String[] ss = ManifestParser.parseDelimitedString(m_directives[dirIdx].getValue(), ",");
074 includeFilter = new String[ss.length][];
075 for (int filterIdx = 0; filterIdx < ss.length; filterIdx++)
076 {
077 includeFilter[filterIdx] = Util.parseSubstring(ss[filterIdx]);
078 }
079 }
080 else if (m_directives[dirIdx].getName().equals(Constants.EXCLUDE_DIRECTIVE))
081 {
082 String[] ss = ManifestParser.parseDelimitedString(m_directives[dirIdx].getValue(), ",");
083 excludeFilter = new String[ss.length][];
084 for (int filterIdx = 0; filterIdx < ss.length; filterIdx++)
085 {
086 excludeFilter[filterIdx] = Util.parseSubstring(ss[filterIdx]);
087 }
088 }
089 }
090
091 // Set final values.
092 m_uses = uses;
093 m_includeFilter = includeFilter;
094 m_excludeFilter = excludeFilter;
095
096 // Parse mandatory directive and mark specified
097 // attributes as mandatory.
098 StringTokenizer tok = new StringTokenizer(mandatory, ", ");
099 while (tok.hasMoreTokens())
100 {
101 // Get attribute name.
102 String attrName = tok.nextToken().trim();
103 // Find attribute and mark it as mandatory.
104 boolean found = false;
105 for (int i = 0; (!found) && (i < m_attributes.length); i++)
106 {
107 if (m_attributes[i].getName().equals(attrName))
108 {
109 m_attributes[i] = new R4Attribute(
110 m_attributes[i].getName(),
111 m_attributes[i].getValue(), true);
112 found = true;
113 }
114 }
115 // If a specified mandatory attribute was not found,
116 // then error.
117 if (!found)
118 {
119 throw new IllegalArgumentException(
120 "Mandatory attribute '" + attrName + "' does not exist.");
121 }
122 }
123
124 // For performance reasons, find the package name and version properties.
125 String pkgName = null;
126 Version pkgVersion = Version.emptyVersion;
127 for (int i = 0; i < m_attributes.length; i++)
128 {
129 if (m_attributes[i].getName().equals(ICapability.PACKAGE_PROPERTY))
130 {
131 pkgName = (String) m_attributes[i].getValue();
132 }
133 else if (m_attributes[i].getName().equals(ICapability.VERSION_PROPERTY))
134 {
135 pkgVersion = (Version) m_attributes[i].getValue();
136 }
137 }
138
139 // Set final values.
140 m_pkgName = pkgName;
141 m_pkgVersion = pkgVersion;
142 }
143
144 public IModule getModule()
145 {
146 return m_module;
147 }
148
149 public String getNamespace()
150 {
151 return m_namespace;
152 }
153
154 // TODO: RB - Determine how to eliminate these non-generic methods;
155 // at least make sure they are not used in the generic resolver.
156 public String getPackageName()
157 {
158 return m_pkgName;
159 }
160
161 public Version getPackageVersion()
162 {
163 return m_pkgVersion;
164 }
165
166 public R4Directive[] getDirectives()
167 {
168 // TODO: RB - We should return copies of the arrays probably.
169 return m_directives;
170 }
171
172 public R4Attribute[] getAttributes()
173 {
174 // TODO: RB - We should return copies of the arrays probably.
175 return m_attributes;
176 }
177
178 public String[] getUses()
179 {
180 // TODO: RB - We should return copies of the arrays probably.
181 return m_uses;
182 }
183
184 public boolean isIncluded(String name)
185 {
186 if ((m_includeFilter == null) && (m_excludeFilter == null))
187 {
188 return true;
189 }
190
191 // Get the class name portion of the target class.
192 String className = Util.getClassName(name);
193
194 // If there are no include filters then all classes are included
195 // by default, otherwise try to find one match.
196 boolean included = (m_includeFilter == null);
197 for (int i = 0;
198 (!included) && (m_includeFilter != null) && (i < m_includeFilter.length);
199 i++)
200 {
201 included = Util.checkSubstring(m_includeFilter[i], className);
202 }
203
204 // If there are no exclude filters then no classes are excluded
205 // by default, otherwise try to find one match.
206 boolean excluded = false;
207 for (int i = 0;
208 (!excluded) && (m_excludeFilter != null) && (i < m_excludeFilter.length);
209 i++)
210 {
211 excluded = Util.checkSubstring(m_excludeFilter[i], className);
212 }
213 return included && !excluded;
214 }
215
216 // TODO: RB - Terminology mismatch property vs. attribute.
217 public Map getProperties()
218 {
219 if (m_attrMap == null)
220 {
221 m_attrMap = new Map() {
222
223 public int size()
224 {
225 // A name and version attribute is always present, since it has a
226 // default value.
227 return m_attributes.length + 2;
228 }
229
230 public boolean isEmpty()
231 {
232 // A version attribute is always present, since it has a
233 // default value.
234 return false;
235 }
236
237 public boolean containsKey(Object key)
238 {
239 return (get(key) != null);
240 }
241
242 public boolean containsValue(Object value)
243 {
244 // Check the package name.
245 if (m_pkgName.equals(value))
246 {
247 return true;
248 }
249
250 // Check the package version.
251 if (m_pkgVersion.equals(value))
252 {
253 return true;
254 }
255
256 // Check all attributes.
257 for (int i = 0; i < m_attributes.length; i++)
258 {
259 if (m_attributes[i].getValue().equals(value))
260 {
261 return true;
262 }
263 }
264
265 return false;
266 }
267
268 public Object get(Object key)
269 {
270 if (ICapability.PACKAGE_PROPERTY.equals(key))
271 {
272 return m_pkgName;
273 }
274 else if (ICapability.VERSION_PROPERTY.equals(key))
275 {
276 return m_pkgVersion;
277 }
278
279 for (int i = 0; i < m_attributes.length; i++)
280 {
281 if (m_attributes[i].getName().equals(key))
282 {
283 return m_attributes[i].getValue();
284 }
285 }
286
287 return null;
288 }
289
290 public Object put(Object key, Object value)
291 {
292 throw new UnsupportedOperationException("Map.put() not implemented.");
293 }
294
295 public Object remove(Object key)
296 {
297 throw new UnsupportedOperationException("Map.remove() not implemented.");
298 }
299
300 public void putAll(Map t)
301 {
302 throw new UnsupportedOperationException("Map.putAll() not implemented.");
303 }
304
305 public void clear()
306 {
307 throw new UnsupportedOperationException("Map.clear() not implemented.");
308 }
309
310 public Set keySet()
311 {
312 Set set = new HashSet();
313 set.add(ICapability.PACKAGE_PROPERTY);
314 set.add(ICapability.VERSION_PROPERTY);
315 for (int i = 0; i < m_attributes.length; i++)
316 {
317 set.add(m_attributes[i].getName());
318 }
319 return set;
320 }
321
322 public Collection values()
323 {
324 throw new UnsupportedOperationException("Map.values() not implemented.");
325 }
326
327 public Set entrySet()
328 {
329 throw new UnsupportedOperationException("Map.entrySet() not implemented.");
330 }
331 };
332 }
333 return m_attrMap;
334 }
335
336 public int compareTo(Object o)
337 {
338 Capability cap = (Capability) o;
339 Version thisVersion = null;
340 Version version = null;
341 if (getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
342 {
343 thisVersion = getPackageVersion();
344 version = cap.getPackageVersion();
345 }
346 else if (getNamespace().equals(ICapability.MODULE_NAMESPACE))
347 {
348 thisVersion = (Version) getProperties().get(Constants.BUNDLE_VERSION_ATTRIBUTE);
349 version = (Version) cap.getProperties().get(Constants.BUNDLE_VERSION_ATTRIBUTE);
350 }
351 if ((thisVersion != null) && (version != null))
352 {
353 int cmp = thisVersion.compareTo(version);
354 if (cmp < 0)
355 {
356 return 1;
357 }
358 else if (cmp > 0)
359 {
360 return -1;
361 }
362 else
363 {
364 long thisId = m_module.getBundle().getBundleId();
365 long id = cap.getModule().getBundle().getBundleId();
366 if (thisId < id)
367 {
368 return -1;
369 }
370 else if (thisId > id)
371 {
372 return 1;
373 }
374 return 0;
375 }
376 }
377 else
378 {
379 return -1;
380 }
381 }
382
383 public String toString()
384 {
385 StringBuffer sb = new StringBuffer();
386 sb.append(getNamespace());
387 for (int i = 0; (m_directives != null) && (i < m_directives.length); i++)
388 {
389 sb.append(";");
390 sb.append(m_directives[i].getName());
391 sb.append(":=\"");
392 sb.append(m_directives[i].getValue());
393 sb.append("\"");
394 }
395 for (int i = 0; (m_attributes != null) && (i < m_attributes.length); i++)
396 {
397 sb.append(";");
398 sb.append(m_attributes[i].getName());
399 sb.append("=\"");
400 sb.append(m_attributes[i].getValue());
401 sb.append("\"");
402 }
403 return sb.toString();
404 }
405 }