KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > osgi > util > ManifestElement


1 /*******************************************************************************
2  * Copyright (c) 2003, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11
12 package org.eclipse.osgi.util;
13
14 import java.io.*;
15 import java.util.*;
16 import org.eclipse.osgi.framework.debug.Debug;
17 import org.eclipse.osgi.framework.internal.core.Msg;
18 import org.eclipse.osgi.framework.internal.core.Tokenizer;
19 import org.osgi.framework.BundleException;
20
21 /**
22  * This class represents a single manifest element. A manifest element must consist of a single
23  * {@link String} value. The {@link String} value may be split up into component values each
24  * separated by a semi-colon (';'). A manifest element may optionally have a set of
25  * attribute values associated with it. The general syntax of a manifest element is as follows:
26  * <p>
27  * <pre>
28  * ManifestElement ::= headervalues (';' attribute)*
29  * headervalues ::= headervalue (';' headervalue)*
30  * headervalue ::= <any string value that does not have ';'>
31  * attribute ::= key '=' value
32  * key ::= token
33  * value ::= token | quoted-string
34  * </pre>
35  * </p>
36  * <p>
37  * For example, The following is an example of a manifest element to the <tt>Export-Package</tt> header:
38  * </p>
39  * <p>
40  * <pre>
41  * org.osgi.framework; specification-version="1.2"; another-attr="examplevalue"
42  * </pre>
43  * </p>
44  * <p>
45  * This manifest element has a value of <tt>org.osgi.framework</tt> and it has two attributes,
46  * <tt>specification-version</tt> and <tt>another-attr</tt>.
47  * </p>
48  * <p>
49  * The following manifest element is an example of a manifest element that has multiple
50  * components to its value:
51  * </p>
52  * <p>
53  * <pre>
54  * code1.jar;code2.jar;code3.jar;attr1=value1;attr2=value2;attr3=value3
55  * </pre>
56  * </p>
57  * <p>
58  * This manifest element has a value of <tt>code1.jar;code2.jar;code3.jar</tt>.
59  * This is an example of a multiple component value. This value has three
60  * components: <tt>code1.jar</tt>, <tt>code2.jar</tt>, and <tt>code3.jar</tt>.
61  * </p>
62  * <p>
63  * This class is not intended to be subclassed by clients.
64  * </p>
65  *
66  * @since 3.0
67  */

68 public class ManifestElement {
69
70     /**
71      * The value of the manifest element.
72      */

73     protected String JavaDoc value;
74
75     /**
76      * The value components of the manifest element.
77      */

78     protected String JavaDoc[] valueComponents;
79
80     /**
81      * The table of attributes for the manifest element.
82      */

83     protected Hashtable attributes;
84
85     /**
86      * The table of directives for the manifest element.
87      */

88     protected Hashtable directives;
89
90     /**
91      * Constructs an empty manifest element with no value or attributes.
92      */

93     protected ManifestElement() {
94         super();
95     }
96
97     /**
98      * Returns the value of the manifest element. The value returned is the
99      * complete value up to the first attribute. For example, the
100      * following manifest element:
101      * <p>
102      * <pre>
103      * test1.jar;test2.jar;test3.jar;selection-filter="(os.name=Windows XP)"
104      * </pre>
105      * </p>
106      * <p>
107      * This manifest element has a value of <tt>test1.jar;test2.jar;test3.jar</tt>
108      * </p>
109      *
110      * @return the value of the manifest element.
111      */

112     public String JavaDoc getValue() {
113         return value;
114     }
115
116     /**
117      * Returns the value components of the manifest element. The value
118      * components returned are the complete list of value components up to
119      * the first attribute.
120      * For example, the folowing manifest element:
121      * <p>
122      * <pre>
123      * test1.jar;test2.jar;test3.jar;selection-filter="(os.name=Windows XP)"
124      * </pre>
125      * </p>
126      * <p>
127      * This manifest element has the value components array
128      * <tt>{ "test1.jar", "test2.jar", "test3.jar" }</tt>
129      * Each value component is delemited by a semi-colon (<tt>';'</tt>).
130      * </p>
131      *
132      * @return the String[] of value components
133      */

134     public String JavaDoc[] getValueComponents() {
135         return valueComponents;
136     }
137
138     /**
139      * Returns the value for the specified attribute or <code>null</code> if it does
140      * not exist. If the attribute has multiple values specified then the last value
141      * specified is returned. For example the following manifest element:
142      * <p>
143      * <pre>
144      * elementvalue; myattr="value1"; myattr="value2"
145      * </pre>
146      * </p>
147      * <p>
148      * specifies two values for the attribute key <tt>myattr</tt>. In this case <tt>value2</tt>
149      * will be returned because it is the last value specified for the attribute
150      * <tt>myattr</tt>.
151      * </p>
152      *
153      * @param key the attribute key to return the value for
154      * @return the attribute value or <code>null</code>
155      */

156     public String JavaDoc getAttribute(String JavaDoc key) {
157         return getTableValue(attributes, key);
158     }
159
160     /**
161      * Returns an array of values for the specified attribute or
162      * <code>null</code> if the attribute does not exist.
163      *
164      * @param key the attribute key to return the values for
165      * @return the array of attribute values or <code>null</code>
166      * @see #getAttribute(String)
167      */

168     public String JavaDoc[] getAttributes(String JavaDoc key) {
169         return getTableValues(attributes, key);
170     }
171
172     /**
173      * Returns an enumeration of attribute keys for this manifest element or
174      * <code>null</code> if none exist.
175      *
176      * @return the enumeration of attribute keys or null if none exist.
177      */

178     public Enumeration getKeys() {
179         return getTableKeys(attributes);
180     }
181
182     /**
183      * Add an attribute to this manifest element.
184      *
185      * @param key the key of the attribute
186      * @param value the value of the attribute
187      */

188     protected void addAttribute(String JavaDoc key, String JavaDoc value) {
189         attributes = addTableValue(attributes, key, value);
190     }
191
192     /**
193      * Returns the value for the specified directive or <code>null</code> if it
194      * does not exist. If the directive has multiple values specified then the
195      * last value specified is returned. For example the following manifest element:
196      * <p>
197      * <pre>
198      * elementvalue; mydir:="value1"; mydir:="value2"
199      * </pre>
200      * </p>
201      * <p>
202      * specifies two values for the directive key <tt>mydir</tt>. In this case <tt>value2</tt>
203      * will be returned because it is the last value specified for the directive <tt>mydir</tt>.
204      * </p>
205      *
206      * @param key the directive key to return the value for
207      * @return the directive value or <code>null</code>
208      */

209     public String JavaDoc getDirective(String JavaDoc key) {
210         return getTableValue(directives, key);
211     }
212
213     /**
214      * Returns an array of string values for the specified directives or
215      * <code>null</code> if it does not exist.
216      *
217      * @param key the directive key to return the values for
218      * @return the array of directive values or <code>null</code>
219      * @see #getDirective(String)
220      */

221     public String JavaDoc[] getDirectives(String JavaDoc key) {
222         return getTableValues(directives, key);
223     }
224
225     /**
226      * Return an enumeration of directive keys for this manifest element or
227      * <code>null</code> if there are none.
228      *
229      * @return the enumeration of directive keys or <code>null</code>
230      */

231     public Enumeration getDirectiveKeys() {
232         return getTableKeys(directives);
233     }
234
235     /**
236      * Add a directive to this manifest element.
237      *
238      * @param key the key of the attribute
239      * @param value the value of the attribute
240      */

241     protected void addDirective(String JavaDoc key, String JavaDoc value) {
242         directives = addTableValue(directives, key, value);
243     }
244
245     /*
246      * Return the last value associated with the given key in the specified table.
247      */

248     private String JavaDoc getTableValue(Hashtable table, String JavaDoc key) {
249         if (table == null)
250             return null;
251         Object JavaDoc result = table.get(key);
252         if (result == null)
253             return null;
254         if (result instanceof String JavaDoc)
255             return (String JavaDoc) result;
256
257         ArrayList valueList = (ArrayList) result;
258         //return the last value
259
return (String JavaDoc) valueList.get(valueList.size() - 1);
260     }
261
262     /*
263      * Return the values associated with the given key in the specified table.
264      */

265     private String JavaDoc[] getTableValues(Hashtable table, String JavaDoc key) {
266         if (table == null)
267             return null;
268         Object JavaDoc result = table.get(key);
269         if (result == null)
270             return null;
271         if (result instanceof String JavaDoc)
272             return new String JavaDoc[] {(String JavaDoc) result};
273         ArrayList valueList = (ArrayList) result;
274         return (String JavaDoc[]) valueList.toArray(new String JavaDoc[valueList.size()]);
275     }
276
277     /*
278      * Return an enumeration of table keys for the specified table.
279      */

280     private Enumeration getTableKeys(Hashtable table) {
281         if (table == null)
282             return null;
283         return table.keys();
284     }
285
286     /*
287      * Add the given key/value association to the specified table. If an entry already exists
288      * for this key, then create an array list from the current value (if necessary) and
289      * append the new value to the end of the list.
290      */

291     private Hashtable addTableValue(Hashtable table, String JavaDoc key, String JavaDoc value) {
292         if (table == null) {
293             table = new Hashtable(7);
294         }
295         Object JavaDoc curValue = table.get(key);
296         if (curValue != null) {
297             ArrayList newList;
298             // create a list to contain multiple values
299
if (curValue instanceof ArrayList) {
300                 newList = (ArrayList) curValue;
301             } else {
302                 newList = new ArrayList(5);
303                 newList.add(curValue);
304             }
305             newList.add(value);
306             table.put(key, newList);
307         } else {
308             table.put(key, value);
309         }
310         return table;
311     }
312
313     /**
314      * Parses a manifest header value into an array of ManifestElements. Each
315      * ManifestElement returned will have a non-null value returned by getValue().
316      *
317      * @param header the header name to parse. This is only specified to provide error messages
318      * when the header value is invalid.
319      * @param value the header value to parse.
320      * @return the array of ManifestElements that are represented by the header value; null will be
321      * returned if the value specified is null or if the value does not parse into
322      * one or more ManifestElements.
323      * @throws BundleException if the header value is invalid
324      */

325     public static ManifestElement[] parseHeader(String JavaDoc header, String JavaDoc value) throws BundleException {
326         if (value == null)
327             return (null);
328         ArrayList headerElements = new ArrayList(10);
329         Tokenizer tokenizer = new Tokenizer(value);
330         parseloop: while (true) {
331             String JavaDoc next = tokenizer.getString(";,"); //$NON-NLS-1$
332
if (next == null)
333                 throw new BundleException(NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, header, value));
334             ArrayList headerValues = new ArrayList();
335             StringBuffer JavaDoc headerValue = new StringBuffer JavaDoc(next);
336             headerValues.add(next);
337
338             if (Debug.DEBUG && Debug.DEBUG_MANIFEST)
339                 Debug.print("paserHeader: " + next); //$NON-NLS-1$
340
boolean directive = false;
341             char c = tokenizer.getChar();
342             // Header values may be a list of ';' separated values. Just append them all into one value until the first '=' or ','
343
while (c == ';') {
344                 next = tokenizer.getString(";,=:"); //$NON-NLS-1$
345
if (next == null)
346                     throw new BundleException(NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, header, value));
347                 c = tokenizer.getChar();
348                 while (c == ':') { // may not really be a :=
349
c = tokenizer.getChar();
350                     if (c != '=') {
351                         String JavaDoc restOfNext = tokenizer.getToken(";,=:"); //$NON-NLS-1$
352
if (restOfNext == null)
353                             throw new BundleException(NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, header, value));
354                         next += ":" + c + restOfNext; //$NON-NLS-1$
355
c = tokenizer.getChar();
356                     } else
357                         directive = true;
358                 }
359                 if (c == ';' || c == '\0') /* more */{
360                     headerValues.add(next);
361                     headerValue.append(";").append(next); //$NON-NLS-1$
362
if (Debug.DEBUG && Debug.DEBUG_MANIFEST)
363                         Debug.print(";" + next); //$NON-NLS-1$
364
}
365             }
366             // found the header value create a manifestElement for it.
367
ManifestElement manifestElement = new ManifestElement();
368             manifestElement.value = headerValue.toString();
369             manifestElement.valueComponents = (String JavaDoc[]) headerValues.toArray(new String JavaDoc[headerValues.size()]);
370
371             // now add any attributes/directives for the manifestElement.
372
while (c == '=' || c == ':') {
373                 while (c == ':') { // may not really be a :=
374
c = tokenizer.getChar();
375                     if (c != '=') {
376                         String JavaDoc restOfNext = tokenizer.getToken("=:"); //$NON-NLS-1$
377
if (restOfNext == null)
378                             throw new BundleException(NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, header, value));
379                         next += ":" + c + restOfNext; //$NON-NLS-1$
380
c = tokenizer.getChar();
381                     } else
382                         directive = true;
383                 }
384                 String JavaDoc val = tokenizer.getString(";,"); //$NON-NLS-1$
385
if (val == null)
386                     throw new BundleException(NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, header, value));
387
388                 if (Debug.DEBUG && Debug.DEBUG_MANIFEST)
389                     Debug.print(";" + next + "=" + val); //$NON-NLS-1$ //$NON-NLS-2$
390
try {
391                     if (directive)
392                         manifestElement.addDirective(next, val);
393                     else
394                         manifestElement.addAttribute(next, val);
395                     directive = false;
396                 } catch (Exception JavaDoc e) {
397                     throw new BundleException(NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, header, value));
398                 }
399                 c = tokenizer.getChar();
400                 if (c == ';') /* more */{
401                     next = tokenizer.getToken("=:"); //$NON-NLS-1$
402
if (next == null)
403                         throw new BundleException(NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, header, value));
404                     c = tokenizer.getChar();
405                 }
406             }
407             headerElements.add(manifestElement);
408             if (Debug.DEBUG && Debug.DEBUG_MANIFEST)
409                 Debug.println(""); //$NON-NLS-1$
410
if (c == ',') /* another manifest element */
411                 continue parseloop;
412             if (c == '\0') /* end of value */
413                 break parseloop;
414             throw new BundleException(NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, header, value));
415         }
416         int size = headerElements.size();
417         if (size == 0)
418             return (null);
419
420         ManifestElement[] result = (ManifestElement[]) headerElements.toArray(new ManifestElement[size]);
421         return (result);
422     }
423
424     /**
425      * Returns the result of converting a list of comma-separated tokens into an array.
426      *
427      * @return the array of string tokens or <code>null</code> if there are none
428      * @param stringList the initial comma-separated string
429      */

430     public static String JavaDoc[] getArrayFromList(String JavaDoc stringList) {
431         String JavaDoc[] result = getArrayFromList(stringList, ","); //$NON-NLS-1$
432
return result.length == 0 ? null : result;
433     }
434
435     /**
436      * Returns the result of converting a list of tokens into an array. The tokens
437      * are split using the specified separator.
438      *
439      * @return the array of string tokens. If there are none then an empty array
440      * is returned.
441      * @param stringList the initial string list
442      * @param separator the separator to use to split the list into tokens.
443      * @since 3.2
444      */

445     public static String JavaDoc[] getArrayFromList(String JavaDoc stringList, String JavaDoc separator) {
446         if (stringList == null || stringList.trim().length() == 0)
447             return new String JavaDoc[0];
448         ArrayList list = new ArrayList();
449         StringTokenizer tokens = new StringTokenizer(stringList, separator);
450         while (tokens.hasMoreTokens()) {
451             String JavaDoc token = tokens.nextToken().trim();
452             if (token.length() != 0)
453                 list.add(token);
454         }
455         return (String JavaDoc[]) list.toArray(new String JavaDoc[list.size()]);
456     }
457
458     /**
459      * Parses a bundle manifest and puts the header/value pairs into the supplied Map.
460      * Only the main section of the manifest is parsed (up to the first blank line). All
461      * other sections are ignored. If a header is duplicated then only the last
462      * value is stored in the map.
463      * <p>
464      * The supplied input stream is consumed by this method and will be closed.
465      * If the supplied Map is null then a Map is created to put the header/value pairs into.
466      * </p>
467      * @param manifest an input stream for a bundle manifest.
468      * @param headers a map used to put the header/value pairs from the bundle manifest. This value may be null.
469      * @throws BundleException if the manifest has an invalid syntax
470      * @throws IOException if an error occurs while reading the manifest
471      * @return the map with the header/value pairs from the bundle manifest
472      */

473     public static Map parseBundleManifest(InputStream manifest, Map headers) throws IOException, BundleException {
474         if (headers == null)
475             headers = new HashMap();
476         BufferedReader br;
477         try {
478             br = new BufferedReader(new InputStreamReader(manifest, "UTF8")); //$NON-NLS-1$
479
} catch (UnsupportedEncodingException e) {
480             br = new BufferedReader(new InputStreamReader(manifest));
481         }
482         try {
483             String JavaDoc header = null;
484             StringBuffer JavaDoc value = new StringBuffer JavaDoc(256);
485             boolean firstLine = true;
486
487             while (true) {
488                 String JavaDoc line = br.readLine();
489                 /* The java.util.jar classes in JDK 1.3 use the value of the last
490                  * encountered manifest header. So we do the same to emulate
491                  * this behavior. We no longer throw a BundleException
492                  * for duplicate manifest headers.
493                  */

494
495                 if ((line == null) || (line.length() == 0)) /* EOF or empty line */
496                 {
497                     if (!firstLine) /* flush last line */
498                     {
499                         headers.put(header, value.toString().trim());
500                     }
501                     break; /* done processing main attributes */
502                 }
503
504                 if (line.charAt(0) == ' ') /* continuation */
505                 {
506                     if (firstLine) /* if no previous line */
507                     {
508                         throw new BundleException(NLS.bind(Msg.MANIFEST_INVALID_SPACE, line));
509                     }
510                     value.append(line.substring(1));
511                     continue;
512                 }
513
514                 if (!firstLine) {
515                     headers.put(header, value.toString().trim());
516                     value.setLength(0); /* clear StringBuffer */
517                 }
518
519                 int colon = line.indexOf(':');
520                 if (colon == -1) /* no colon */
521                 {
522                     throw new BundleException(NLS.bind(Msg.MANIFEST_INVALID_LINE_NOCOLON, line));
523                 }
524                 header = line.substring(0, colon).trim();
525                 value.append(line.substring(colon + 1));
526                 firstLine = false;
527             }
528         } finally {
529             try {
530                 br.close();
531             } catch (IOException ee) {
532                 // do nothing
533
}
534         }
535         return headers;
536     }
537 }
538
Popular Tags