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.xbean.osgi.bundle.util;
020    
021    import java.util.ArrayList;
022    import java.util.Dictionary;
023    import java.util.HashMap;
024    import java.util.List;
025    import java.util.Map;
026    import java.util.jar.Attributes;
027    import java.util.jar.Manifest;
028    
029    import org.apache.xbean.osgi.bundle.util.HeaderParser.HeaderElement;
030    import org.osgi.framework.Constants;
031    import org.osgi.framework.Version;
032    
033    /**
034     * @version $Rev: 937957 $, $Date: 2010-04-26 10:00:08 +0200 (Mon, 26 Apr 2010) $
035     */
036    public 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    }