1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.felix.bundleplugin;
20
21 import java.io.IOException;
22 import java.io.OutputStream;
23 import java.util.Arrays;
24 import java.util.HashSet;
25 import java.util.Map;
26 import java.util.Set;
27 import java.util.TreeMap;
28 import java.util.TreeSet;
29 import java.util.jar.Attributes;
30 import java.util.jar.Manifest;
31
32 import org.apache.felix.utils.manifest.Parser;
33 import org.osgi.framework.Constants;
34
35 public class ManifestWriter {
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59 static byte[] CONTINUE = new byte[] {
60 '\r', '\n', ' '
61 };
62
63 static Set<String> NICE_HEADERS = new HashSet<String>(
64 Arrays.asList(
65 Constants.IMPORT_PACKAGE,
66 Constants.DYNAMICIMPORT_PACKAGE,
67 Constants.IMPORT_SERVICE,
68 Constants.REQUIRE_CAPABILITY,
69 Constants.EXPORT_PACKAGE,
70 Constants.EXPORT_SERVICE,
71 Constants.PROVIDE_CAPABILITY
72 )
73 );
74
75
76
77
78
79
80
81
82
83
84
85 public static void outputManifest(Manifest manifest, OutputStream out, boolean nice) throws IOException {
86 writeEntry(out, "Manifest-Version", "1.0", nice);
87 attributes(manifest.getMainAttributes(), out, nice);
88
89 TreeSet<String> keys = new TreeSet<String>();
90 for (Object o : manifest.getEntries().keySet())
91 keys.add(o.toString());
92
93 for (String key : keys) {
94 write(out, 0, "\r\n");
95 writeEntry(out, "Name", key, nice);
96 attributes(manifest.getAttributes(key), out, nice);
97 }
98 out.flush();
99 }
100
101
102
103
104 private static void writeEntry(OutputStream out, String name, String value, boolean nice) throws IOException {
105 if (nice && NICE_HEADERS.contains(name)) {
106 int n = write(out, 0, name + ": ");
107 String[] parts = Parser.parseDelimitedString(value, ",");
108 if (parts.length > 1) {
109 write(out, 0, "\r\n ");
110 n = 1;
111 }
112 for (int i = 0; i < parts.length; i++) {
113 if (i < parts.length - 1) {
114 write(out, n, parts[i] + ",");
115 write(out, 0, "\r\n ");
116 } else {
117 write(out, n, parts[i]);
118 write(out, 0, "\r\n");
119 }
120 n = 1;
121 }
122 } else {
123 int n = write(out, 0, name + ": ");
124 write(out, n, value);
125 write(out, 0, "\r\n");
126 }
127 }
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142 private static int write(OutputStream out, int i, String s) throws IOException {
143 byte[] bytes = s.getBytes("UTF8");
144 return write(out, i, bytes);
145 }
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163 private static int write(OutputStream out, int width, byte[] bytes) throws IOException {
164 int w = width;
165 for (int i = 0; i < bytes.length; i++) {
166 if (w >= 72) {
167 out.write(CONTINUE);
168 w = 1;
169 }
170 out.write(bytes[i]);
171 w++;
172 }
173 return w;
174 }
175
176
177
178
179
180
181
182
183
184
185
186 private static void attributes(Attributes value, OutputStream out, boolean nice) throws IOException {
187 TreeMap<String,String> map = new TreeMap<String,String>(String.CASE_INSENSITIVE_ORDER);
188 for (Map.Entry<Object,Object> entry : value.entrySet()) {
189 map.put(entry.getKey().toString(), entry.getValue().toString());
190 }
191
192 map.remove("Manifest-Version");
193
194
195 for (Map.Entry<String,String> entry : map.entrySet()) {
196 writeEntry(out, entry.getKey(), entry.getValue(), nice);
197 }
198 }
199
200 private static Manifest clean(Manifest org) {
201
202 Manifest result = new Manifest();
203 for (Map.Entry< ? , ? > entry : org.getMainAttributes().entrySet()) {
204 String nice = clean((String) entry.getValue());
205 result.getMainAttributes().put(entry.getKey(), nice);
206 }
207 for (String name : org.getEntries().keySet()) {
208 Attributes attrs = result.getAttributes(name);
209 if (attrs == null) {
210 attrs = new Attributes();
211 result.getEntries().put(name, attrs);
212 }
213
214 for (Map.Entry< ? , ? > entry : org.getAttributes(name).entrySet()) {
215 String nice = clean((String) entry.getValue());
216 attrs.put(entry.getKey(), nice);
217 }
218 }
219 return result;
220 }
221
222 private static String clean(String s) {
223 StringBuilder sb = new StringBuilder(s);
224 boolean changed = false;
225 boolean replacedPrev = false;
226 for ( int i=0; i<sb.length(); i++) {
227 char c = s.charAt(i);
228 switch(c) {
229 case 0:
230 case '\n':
231 case '\r':
232 changed = true;
233 if ( !replacedPrev ) {
234 sb.replace(i, i+1, " ");
235 replacedPrev= true;
236 } else
237 sb.delete(i, i+1);
238 break;
239 default:
240 replacedPrev = false;
241 break;
242 }
243 }
244 if ( changed)
245 return sb.toString();
246 else
247 return s;
248 }
249
250 }