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 */
019package org.apache.xbean.osgi.bundle.util;
020
021import java.util.ArrayList;
022import java.util.Dictionary;
023import java.util.HashMap;
024import java.util.List;
025import java.util.Map;
026import java.util.jar.Attributes;
027import java.util.jar.Manifest;
028
029import org.apache.xbean.osgi.bundle.util.HeaderParser.HeaderElement;
030import org.osgi.framework.Constants;
031import org.osgi.framework.Version;
032
033/**
034 * @version $Rev: 937957 $, $Date: 2010-04-26 10:00:08 +0200 (Mon, 26 Apr 2010) $
035 */
036public class BundleDescription  {
037
038    private Map headers;
039    
040    public BundleDescription(Manifest manifest) {
041        this.headers = manifestToMap(manifest);
042    }
043    
044    public BundleDescription(Dictionary dictionary) {
045        this.headers = new DictionaryMap(dictionary);
046    }
047    
048    public BundleDescription(Map headers) {
049        this.headers = headers;
050    }
051   
052    /**
053     * Returns a list of packages that are listed in <i>Import-Package</i> header.
054     */
055    public List<ImportPackage> getImportPackage() {
056        String headerValue = (String) headers.get(Constants.IMPORT_PACKAGE);
057        List<ImportPackage> imports = new ArrayList<ImportPackage>();
058        List<HeaderElement> elements = HeaderParser.parseHeader(headerValue);
059        for (HeaderElement element : elements) {
060            ImportPackage p = new ImportPackage(element.getName(), element.getAttributes(), element.getDirectives());
061            imports.add(p);
062        }
063        return imports;
064    }
065    
066    /**
067     * Returns a list of packages that are listed in <i>Export-Package</i> header.
068     */
069    public List<ExportPackage> getExportPackage() {
070        String headerValue = (String) headers.get(Constants.EXPORT_PACKAGE);
071        List<ExportPackage> exports = new ArrayList<ExportPackage>();
072        List<HeaderElement> elements = HeaderParser.parseHeader(headerValue);
073        for (HeaderElement element : elements) {
074            ExportPackage p = new ExportPackage(element.getName(), element.getAttributes(), element.getDirectives());
075            exports.add(p);
076        }
077        return exports;        
078    }
079    
080    /**
081     * Returns a list of packages that are listed in <i>Import-Package</i> header
082     * and are <b>not</b> listed in <i>Export-Package</i> header.
083     */
084    public List<ImportPackage> getExternalImports() {
085        List<ImportPackage> imports = getImportPackage();
086        List<ExportPackage> exports = getExportPackage();
087        List<ImportPackage> realImports = new ArrayList<ImportPackage>();
088        for (ImportPackage p : imports) {
089            if (!isExported(exports, p)) {
090                realImports.add(p);
091            }
092        }
093        return realImports;
094    }
095    
096    private static boolean isExported(List<ExportPackage> exports, ImportPackage p) {
097        for (ExportPackage export : exports) {            
098            if (export.getName().equals(p.getName())) {
099                return true;
100            }
101        }
102        return false;
103    }
104    
105    /**
106     * Returns a list of bundle names that are listed in <i>Require-Bundle</i> header.
107     */
108    public List<RequireBundle> getRequireBundle() {
109        String headerValue = (String) headers.get(Constants.REQUIRE_BUNDLE);
110        List<RequireBundle> requireBundles = new ArrayList<RequireBundle>();
111        List<HeaderElement> elements = HeaderParser.parseHeader(headerValue);
112        for (HeaderElement element : elements) {
113            RequireBundle p = new RequireBundle(element.getName(), element.getAttributes(), element.getDirectives());
114            requireBundles.add(p);
115        }
116        return requireBundles;   
117    }
118    
119    /**
120     * Returns <i>Fragment-Host</i> header.
121     */
122    public FragmentHost getFragmentHost() {
123        String headerValue = (String) headers.get(Constants.FRAGMENT_HOST);
124        List<HeaderElement> elements = HeaderParser.parseHeader(headerValue);
125        if (elements.size() == 1) {
126            HeaderElement element = elements.get(0);
127            return new FragmentHost(element.getName(), element.getAttributes(), element.getDirectives());
128        }
129        return null;
130    }
131    
132    /**
133     * Returns a list of packages that are listed in <i>DynamicImport-Package</i> header.
134     */
135    public List<HeaderEntry> getDynamicImportPackage() {
136        String headerValue = (String) headers.get(Constants.DYNAMICIMPORT_PACKAGE);
137        return parseStandardHeader(headerValue);
138    }
139    
140    /**
141     * Returns a list of paths that are listed in <i>Bundle-ClassPath</i> header.
142     */
143    public List<HeaderEntry> getBundleClassPath() {
144        String headerValue = (String) headers.get(Constants.BUNDLE_CLASSPATH);
145        return parseStandardHeader(headerValue);
146    }
147    
148    public SymbolicName getSymbolicName() {
149        String headerValue = (String) headers.get(Constants.BUNDLE_SYMBOLICNAME);
150        List<HeaderElement> elements = HeaderParser.parseHeader(headerValue);
151        if (elements.size() == 1) {
152            HeaderElement element = elements.get(0);
153            return new SymbolicName(element.getName(), element.getAttributes(), element.getDirectives());
154        }
155        return null;
156    }
157    
158    public Version getVersion() {
159        String headerValue = (String) headers.get(Constants.BUNDLE_VERSION);
160        return getVersionRange(headerValue).getLow();
161    }
162    
163    public Map getHeaders() {
164        return headers;
165    }
166    
167    private List<HeaderEntry> parseStandardHeader(String headerValue) {
168        List<HeaderEntry> imports = new ArrayList<HeaderEntry>();
169        List<HeaderElement> elements = HeaderParser.parseHeader(headerValue);
170        for (HeaderElement element : elements) {
171            HeaderEntry p = new HeaderEntry(element.getName(), element.getAttributes(), element.getDirectives());
172            imports.add(p);
173        }
174        return imports;
175    }
176    
177    private static Map<String, String> manifestToMap(Manifest manifest) {
178        Attributes attributes = manifest.getMainAttributes();
179        Map<String, String> headers = new HashMap<String, String>();
180        for (Map.Entry<Object, Object> entry : attributes.entrySet()) {
181            String key = entry.getKey().toString();
182            String value = entry.getValue().toString();
183            headers.put(key, value);
184        }
185        return headers;
186    }
187    
188    private static VersionRange getVersionRange(String version) {
189        if (version == null) {
190            version = "0.0.0";
191        }
192        return VersionRange.parse(version);
193    }
194    
195    public static class HeaderEntry {
196    
197        private String name;
198        private Map<String, String> attributes;
199        private Map<String, String> directives;
200        
201        public HeaderEntry(String name, 
202                          Map<String, String> attributes, 
203                          Map<String, String> directives) {
204            this.name = name;
205            this.attributes = attributes;
206            this.directives = directives;
207        }
208        
209        public String getName() {
210            return name;
211        }
212        
213        public Map<String, String> getAttributes() {
214            return attributes;
215        }
216        
217        public Map<String, String> getDirectives() {
218            return directives;
219        }       
220        
221        public String toString() {
222            StringBuilder builder = new StringBuilder();
223            builder.append("Name: ").append(name);
224            builder.append(", Attributes: ").append(attributes);
225            builder.append(", Directives: ").append(directives);
226            return builder.toString();
227        }
228        
229    }
230    
231    public static class ExportPackage extends HeaderEntry {
232
233        private Version version;
234        
235        public ExportPackage(String name,
236                             Map<String, String> attributes,
237                             Map<String, String> directives) {
238            super(name, attributes, directives);
239            version = BundleDescription.getVersionRange(attributes.get(Constants.VERSION_ATTRIBUTE)).getLow();
240        }
241        
242        public Version getVersion() {
243            return version;
244        }
245    }
246        
247    public static class ImportPackage extends HeaderEntry {
248
249        private boolean optional;
250        private VersionRange versionRange;
251        
252        public ImportPackage(String name,
253                             Map<String, String> attributes,
254                             Map<String, String> directives) {
255            super(name, attributes, directives);
256            
257            String resolution = directives.get(Constants.RESOLUTION_DIRECTIVE);
258            optional = Constants.RESOLUTION_OPTIONAL.equals(resolution);
259            
260            versionRange = BundleDescription.getVersionRange(attributes.get(Constants.VERSION_ATTRIBUTE));
261        }
262        
263        public boolean isOptional() {
264            return optional;
265        }
266        
267        public boolean isMandatory() {
268            return !optional;
269        }
270        
271        public VersionRange getVersionRange() {
272            return versionRange;
273        }
274    }
275    
276    public static class SymbolicName extends HeaderEntry {
277
278        public SymbolicName(String name,
279                            Map<String, String> attributes,
280                            Map<String, String> directives) {
281            super(name, attributes, directives);
282        }
283        
284    }
285    
286    public static class RequireBundle extends HeaderEntry {
287
288        private boolean optional;
289        private VersionRange versionRange;
290        
291        public RequireBundle(String name,
292                             Map<String, String> attributes,
293                             Map<String, String> directives) {
294            super(name, attributes, directives);
295
296            String resolution = directives.get(Constants.RESOLUTION_DIRECTIVE);
297            optional = Constants.RESOLUTION_OPTIONAL.equals(resolution);
298            
299            versionRange = BundleDescription.getVersionRange(attributes.get(Constants.BUNDLE_VERSION_ATTRIBUTE));
300        }
301        
302        public boolean isOptional() {
303            return optional;
304        }
305        
306        public boolean isMandatory() {
307            return !optional;
308        }
309        
310        public VersionRange getVersionRange() {
311            return versionRange;
312        }
313    }
314    
315    public static class FragmentHost extends HeaderEntry {
316
317        private VersionRange versionRange;
318        
319        public FragmentHost(String name,
320                             Map<String, String> attributes,
321                             Map<String, String> directives) {
322            super(name, attributes, directives);
323            versionRange = BundleDescription.getVersionRange(attributes.get(Constants.BUNDLE_VERSION_ATTRIBUTE));
324        }
325        
326        public VersionRange getVersionRange() {
327            return versionRange;
328        }
329    }
330}