1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37 package jnlp.sample.jardiff;
38
39 import java.io.File;
40 import java.io.IOException;
41 import java.io.InputStream;
42 import java.io.InputStreamReader;
43 import java.io.LineNumberReader;
44 import java.io.OutputStream;
45 import java.util.ArrayList;
46 import java.util.Enumeration;
47 import java.util.HashMap;
48 import java.util.HashSet;
49 import java.util.List;
50 import java.util.Map;
51 import java.util.MissingResourceException;
52 import java.util.ResourceBundle;
53 import java.util.Set;
54 import java.util.jar.JarEntry;
55 import java.util.jar.JarFile;
56 import java.util.jar.JarOutputStream;
57 import java.util.zip.ZipEntry;
58
59
60
61
62
63
64
65
66
67
68 public class JarDiffPatcher
69 implements JarDiffConstants, Patcher
70 {
71 private static final int DEFAULT_READ_SIZE = 2048;
72
73 private static byte[] newBytes = new byte[DEFAULT_READ_SIZE];
74
75 private static byte[] oldBytes = new byte[DEFAULT_READ_SIZE];
76
77 private static ResourceBundle _resources = JarDiff.getResources();
78
79 public static ResourceBundle getResources()
80 {
81 return JarDiff.getResources();
82 }
83
84 public void applyPatch( Patcher.PatchDelegate delegate, String oldJarPath, String jarDiffPath, OutputStream result )
85 throws IOException
86 {
87 File oldFile = new File( oldJarPath );
88 File diffFile = new File( jarDiffPath );
89 JarOutputStream jos = new JarOutputStream( result );
90 JarFile oldJar = new JarFile( oldFile );
91 JarFile jarDiff = new JarFile( diffFile );
92 Set<String> ignoreSet = new HashSet<String>();
93 Map<String, String> renameMap = new HashMap<String, String>();
94
95 determineNameMapping( jarDiff, ignoreSet, renameMap );
96
97
98 String[] keys = renameMap.keySet().toArray( new String[renameMap.size()] );
99
100
101 Set<String> oldjarNames = new HashSet<String>();
102
103 Enumeration<JarEntry> oldEntries = oldJar.entries();
104 if ( oldEntries != null )
105 {
106 while ( oldEntries.hasMoreElements() )
107 {
108 oldjarNames.add( ( oldEntries.nextElement() ).getName() );
109 }
110 }
111
112
113
114
115
116
117
118 double size = oldjarNames.size() + keys.length + jarDiff.size();
119 double currentEntry = 0;
120
121
122 oldjarNames.removeAll( ignoreSet );
123 size -= ignoreSet.size();
124
125
126 Enumeration<JarEntry> entries = jarDiff.entries();
127 if ( entries != null )
128 {
129 while ( entries.hasMoreElements() )
130 {
131 JarEntry entry = entries.nextElement();
132
133 if ( !INDEX_NAME.equals( entry.getName() ) )
134 {
135
136 updateDelegate( delegate, currentEntry, size );
137 currentEntry++;
138
139 writeEntry( jos, entry, jarDiff );
140
141
142
143 boolean wasInOld = oldjarNames.remove( entry.getName() );
144
145
146
147 if ( wasInOld )
148 {
149 size--;
150 }
151
152 }
153 else
154 {
155
156 size--;
157 }
158 }
159 }
160
161
162 for ( String newName : keys )
163 {
164
165
166 String oldName = renameMap.get( newName );
167
168
169 JarEntry oldEntry = oldJar.getJarEntry( oldName );
170
171 if ( oldEntry == null )
172 {
173 String moveCmd = MOVE_COMMAND + oldName + " " + newName;
174 handleException( "jardiff.error.badmove", moveCmd );
175 }
176
177
178 JarEntry newEntry = new JarEntry( newName );
179 newEntry.setTime( oldEntry.getTime() );
180 newEntry.setSize( oldEntry.getSize() );
181 newEntry.setCompressedSize( oldEntry.getCompressedSize() );
182 newEntry.setCrc( oldEntry.getCrc() );
183 newEntry.setMethod( oldEntry.getMethod() );
184 newEntry.setExtra( oldEntry.getExtra() );
185 newEntry.setComment( oldEntry.getComment() );
186
187 updateDelegate( delegate, currentEntry, size );
188 currentEntry++;
189
190 writeEntry( jos, newEntry, oldJar.getInputStream( oldEntry ) );
191
192
193
194 boolean wasInOld = oldjarNames.remove( oldName );
195
196
197
198 if ( wasInOld )
199 {
200 size--;
201 }
202
203 }
204
205
206 for ( String name : oldjarNames )
207 {
208 JarEntry entry = oldJar.getJarEntry( name );
209
210 updateDelegate( delegate, currentEntry, size );
211 currentEntry++;
212
213 writeEntry( jos, entry, oldJar );
214 }
215
216 updateDelegate( delegate, currentEntry, size );
217
218 jos.finish();
219 }
220
221 private void updateDelegate( Patcher.PatchDelegate delegate, double currentSize, double size )
222 {
223 if ( delegate != null )
224 {
225 delegate.patching( (int) ( currentSize / size ) );
226 }
227 }
228
229 private void determineNameMapping( JarFile jarDiff, Set<String> ignoreSet, Map<String, String> renameMap )
230 throws IOException
231 {
232 InputStream is = jarDiff.getInputStream( jarDiff.getEntry( INDEX_NAME ) );
233
234 if ( is == null )
235 {
236 handleException( "jardiff.error.noindex", null );
237 }
238 LineNumberReader indexReader = new LineNumberReader( new InputStreamReader( is, "UTF-8" ) );
239 String line = indexReader.readLine();
240
241 if ( line == null || !line.equals( VERSION_HEADER ) )
242 {
243 handleException( "jardiff.error.badheader", line );
244 }
245
246 while ( ( line = indexReader.readLine() ) != null )
247 {
248 if ( line.startsWith( REMOVE_COMMAND ) )
249 {
250 List<String> sub = getSubpaths( line.substring( REMOVE_COMMAND.length() ) );
251
252 if ( sub.size() != 1 )
253 {
254 handleException( "jardiff.error.badremove", line );
255 }
256 ignoreSet.add( sub.get( 0 ) );
257 }
258 else if ( line.startsWith( MOVE_COMMAND ) )
259 {
260 List<String> sub = getSubpaths( line.substring( MOVE_COMMAND.length() ) );
261
262 if ( sub.size() != 2 )
263 {
264 handleException( "jardiff.error.badmove", line );
265 }
266
267 if ( renameMap.put( sub.get( 1 ), sub.get( 0 ) ) != null )
268 {
269
270 handleException( "jardiff.error.badmove", line );
271 }
272 }
273 else if ( line.length() > 0 )
274 {
275 handleException( "jardiff.error.badcommand", line );
276 }
277 }
278 }
279
280 private void handleException( String errorMsg, String line )
281 throws IOException
282 {
283 try
284 {
285 throw new IOException( getResources().getString( errorMsg ) + " " + line );
286 }
287 catch ( MissingResourceException mre )
288 {
289 System.err.println( "Fatal error: " + errorMsg );
290 new Throwable().printStackTrace( System.err );
291 System.exit( -1 );
292 }
293 }
294
295 private List<String> getSubpaths( String path )
296 {
297 int index = 0;
298 int length = path.length();
299 List<String> sub = new ArrayList<String>();
300
301 while ( index < length )
302 {
303 while ( index < length && Character.isWhitespace( path.charAt( index ) ) )
304 {
305 index++;
306 }
307 if ( index < length )
308 {
309 int start = index;
310 int last = start;
311 String subString = null;
312
313 while ( index < length )
314 {
315 char aChar = path.charAt( index );
316 if ( aChar == '\\' && ( index + 1 ) < length && path.charAt( index + 1 ) == ' ' )
317 {
318
319 if ( subString == null )
320 {
321 subString = path.substring( last, index );
322 }
323 else
324 {
325 subString += path.substring( last, index );
326 }
327 last = ++index;
328 }
329 else if ( Character.isWhitespace( aChar ) )
330 {
331 break;
332 }
333 index++;
334 }
335 if ( last != index )
336 {
337 if ( subString == null )
338 {
339 subString = path.substring( last, index );
340 }
341 else
342 {
343 subString += path.substring( last, index );
344 }
345 }
346 sub.add( subString );
347 }
348 }
349 return sub;
350 }
351
352 private void writeEntry( JarOutputStream jos, JarEntry entry, JarFile file )
353 throws IOException
354 {
355 writeEntry( jos, entry, file.getInputStream( entry ) );
356 }
357
358 private void writeEntry( JarOutputStream jos, JarEntry entry, InputStream data )
359 throws IOException
360 {
361
362 jos.putNextEntry( new ZipEntry( entry.getName() ) );
363
364
365 int size = data.read( newBytes );
366
367 while ( size != -1 )
368 {
369 jos.write( newBytes, 0, size );
370 size = data.read( newBytes );
371 }
372 data.close();
373 }
374 }
375
376