OSDN Git Service

Add new source files
[armadillo/armadillo1.git] / src / jp / sfjp / armadillo / ArmadilloCommands.java
1 package jp.sfjp.armadillo;
2
3 import java.io.*;
4 import java.lang.reflect.*;
5 import java.util.*;
6 import java.util.zip.*;
7 import jp.sfjp.armadillo.archive.*;
8 import jp.sfjp.armadillo.archive.cab.*;
9 import jp.sfjp.armadillo.archive.lzh.*;
10 import jp.sfjp.armadillo.archive.tar.*;
11 import jp.sfjp.armadillo.archive.zip.ZipFile;
12 import jp.sfjp.armadillo.file.*;
13 import jp.sfjp.armadillo.io.*;
14
15 public final class ArmadilloCommands {
16
17     private static Logger log = Logger.getLogger(ArmadilloCommands.class);
18
19     private ArmadilloCommands() { // empty
20     }
21
22     public static String getVersionString() {
23         return getVersionString(false);
24     }
25
26     public static String getVersionString(boolean throwsError) {
27         try {
28             InputStream is = ArmadilloCommands.class.getResourceAsStream("version");
29             if (is != null)
30                 try {
31                     final Scanner r = new Scanner(is);
32                     if (r.hasNextLine())
33                         return r.nextLine();
34                 }
35                 finally {
36                     is.close();
37                 }
38         }
39         catch (Exception ex) {
40             if (throwsError)
41                 throw new IllegalStateException(ex);
42             // ignore
43         }
44         return "";
45     }
46
47     public static void createArchive(File srcdir, File dst0) throws IOException {
48         createArchive(srcdir, dst0, ProgressNotifier.NullObject);
49     }
50
51     public static void createArchive(File srcdir, File dst0, ProgressNotifier notifier) throws IOException {
52         File dst = getAlternativeFileIfExists(dst0);
53         ArchiveType type = ArchiveType.of(dst.getName());
54         List<File> files = getAllChildrenFile(srcdir);
55         ArchiveCreator ac = getArchiveCreator(dst, type);
56         try {
57             for (final File file : files) {
58                 final String pathForEntryName = getRelativePath(srcdir, file);
59                 if (pathForEntryName.length() == 0)
60                     continue;
61                 ArchiveEntry entry = ac.newEntry(pathForEntryName);
62                 final long fileSize = file.length();
63                 entry.setSize(fileSize);
64                 entry.setLastModified(file.lastModified());
65                 ac.addEntry(entry, file);
66                 if (fileSize > 0)
67                     notifier.notifyProgress(fileSize);
68             }
69         }
70         finally {
71             ac.close();
72         }
73     }
74
75     public static void extractAll(File src) throws IOException {
76         extractAll(src, ProgressNotifier.NullObject);
77     }
78
79     public static void extractAll(File src, File dstdir) throws IOException {
80         extractAll(src, dstdir, ProgressNotifier.NullObject);
81     }
82
83     public static void extractAll(File src, ProgressNotifier notifier) throws IOException {
84         extractAll(src, getAlternativeFileIfExists(getDestinationDirectory(src)), notifier);
85     }
86
87     public static void extractAll(File src, File dstdir, ProgressNotifier notifier) throws IOException {
88         Set<String> targetSet = Collections.emptySet();
89         extract(src, dstdir, targetSet, notifier);
90     }
91
92     public static void extract(File src, Set<String> targetSet, ProgressNotifier notifier) throws IOException {
93         extract(src, getAlternativeFileIfExists(getDestinationDirectory(src)), targetSet, notifier);
94     }
95
96     public static void extract(File src,
97                                File dstdir,
98                                Set<String> targetSet,
99                                ProgressNotifier notifier) throws IOException {
100         if (dstdir.exists())
101             throw new IOException("the directory already exists: " + dstdir);
102         final boolean all = targetSet.isEmpty();
103         int targetCount = targetSet.size();
104         ArchiveType type = ArchiveType.of(src.getName());
105         Set<ArchiveEntry> dirEntrySet = new HashSet<ArchiveEntry>();
106         ArchiveExtractor ax = getArchiveExtractor(src, type);
107         try {
108             assert !dstdir.exists();
109             if (!dstdir.mkdirs())
110                 throw new IllegalStateException("failed to mkdirs");
111             while (true) {
112                 ArchiveEntry entry = ax.nextEntry();
113                 if (entry == ArchiveEntry.NULL)
114                     break;
115                 if (entry.isDirectory())
116                     dirEntrySet.add(entry);
117                 if (!all && !targetSet.contains(entry.getName()))
118                     continue;
119                 if (!all)
120                     --targetCount;
121                 final File newfile = getRelativeFile(dstdir, entry);
122                 if (entry.isDirectory()) {
123                     if (!newfile.exists())
124                         newfile.mkdirs();
125                     continue;
126                 }
127                 if (newfile.exists())
128                     throw new IOException("file '" + newfile + "' already exists");
129                 final File parent = newfile.getParentFile();
130                 if (!parent.exists())
131                     if (!parent.mkdirs())
132                         throw new IOException("can't mkdir '" + parent + "'");
133                 final long inc = (entry.getCompressedSize() > 0)
134                         ? entry.getCompressedSize()
135                         : (long)(entry.getSize() * 0.18f);
136                 final long inc1 = (long)(inc * 0.3f);
137                 final long inc2 = inc - inc1;
138                 assert inc1 + inc2 == inc;
139                 notifier.notifyProgress(inc1);
140                 final long writtenSize;
141                 if (newfile.exists()) // second check
142                     throw new IOException("file '" + newfile + "' already exists");
143                 OutputStream fos = new FileOutputStream(newfile);
144                 try {
145                     writtenSize = ax.extract(fos);
146                 }
147                 catch (Exception ex) {
148                     throw new IllegalStateException("illegal state", ex);
149                 }
150                 finally {
151                     fos.close();
152                 }
153                 assert writtenSize == entry.getSize() : String.format("%s: written=%d, entry=%d",
154                                                                       entry.getName(),
155                                                                       writtenSize,
156                                                                       entry.getSize());
157                 newfile.setLastModified(entry.getLastModified());
158                 notifier.notifyProgress(inc2);
159                 if (!all && targetCount <= 0)
160                     break;
161             }
162             notifier.notifyFinished();
163             /*
164              * Finally, set timestamp on all directories.
165              */
166             for (final ArchiveEntry entry : dirEntrySet) {
167                 File dir = new File(dstdir, entry.getName());
168                 if (dir.exists())
169                     dir.setLastModified(entry.getLastModified());
170             }
171         }
172         finally {
173             ax.close();
174         }
175     }
176
177     public static boolean validate(File src) throws IOException {
178         ArchiveType type = ArchiveType.of(src.getName());
179         ArchiveExtractor ax = getArchiveExtractor(src, type);
180         try {
181             while (true) {
182                 ArchiveEntry entry = ax.nextEntry();
183                 if (entry == ArchiveEntry.NULL)
184                     break;
185                 assert entry != null : "return null entry: extractor="
186                                        + ax.getClass().getSimpleName();
187                 if (entry.isDirectory())
188                     continue;
189                 VolumetricOutputStream vos = new VolumetricOutputStream();
190                 try {
191                     if (ax.extract(vos) != entry.getSize())
192                         return false;
193                 }
194                 finally {
195                     vos.close();
196                 }
197             }
198         }
199         catch (IOException ex) {
200             throw ex;
201         }
202         catch (Exception ex) {
203             log.error(ex);
204             return false;
205         }
206         finally {
207             ax.close();
208         }
209         return true;
210     }
211
212     public static List<ArchiveEntry> extractArchiveEntries(File src, ArchiveType type) throws IOException {
213         List<ArchiveEntry> a = new ArrayList<ArchiveEntry>();
214         ArchiveExtractor ax = getArchiveExtractor(src, type);
215         try {
216             while (true) {
217                 ArchiveEntry entry = ax.nextEntry();
218                 if (entry == ArchiveEntry.NULL)
219                     break;
220                 a.add(entry);
221             }
222         }
223         finally {
224             ax.close();
225         }
226         return a;
227     }
228
229     public static long transferAll(InputStream is, OutputStream os) throws IOException {
230         long totalSize = 0;
231         byte[] buffer = new byte[8192];
232         for (int readLength; (readLength = is.read(buffer)) >= 0;) {
233             assert readLength != 0 : "Read Zero";
234             os.write(buffer, 0, readLength);
235             totalSize += readLength;
236         }
237         os.flush();
238         return totalSize;
239     }
240
241     public static String getRelativePath(File dir, File f) throws IOException {
242         final String filePath = f.getCanonicalFile().getAbsolutePath();
243         final String dirPath = dir.getCanonicalFile().getAbsolutePath();
244         if (dirPath.equals(filePath))
245             return "";
246         if (!filePath.startsWith(dirPath))
247             throw new IllegalStateException(String.format("not relative: dir=%s, f=%s", dir, f));
248         final int dirLen = dirPath.length() + (dirPath.endsWith("/") ? 0 : 1);
249         final String s = filePath.substring(dirLen).replace('\\', '/');
250         if (f.isDirectory() && !filePath.endsWith("/"))
251             return s + "/";
252         else
253             return s;
254     }
255
256     public static File getRelativeFile(File dir, ArchiveEntry entry) {
257         final String name = entry.getName();
258         if (name.startsWith("/"))
259             return new File(dir, name);
260         else if (name.matches("(?i)[A-Z]\\:[\\\\/].*"))
261             return new File(dir, name.substring(2));
262         return new File(dir, name);
263     }
264
265     public static List<File> getAllChildrenFile(File dir) {
266         if (!dir.isDirectory())
267             throw new IllegalArgumentException("file is not dir: " + dir);
268         List<File> a = new ArrayList<File>();
269         getAllChildrenFile(a, dir);
270         return a;
271     }
272
273     private static void getAllChildrenFile(List<File> a, File f) {
274         a.add(f);
275         if (f.isDirectory()) {
276             File[] children = f.listFiles();
277             if (children != null)
278                 for (File child : children)
279                     getAllChildrenFile(a, child);
280         }
281     }
282
283     public static long getTotalSize(File dir) {
284         TotalFileSizeFindAction action = new TotalFileSizeFindAction();
285         Find.find(dir, action);
286         return action.totalSize;
287     }
288
289     static final class TotalFileSizeFindAction implements FindAction {
290
291         long totalSize;
292
293         @Override
294         public void act(File file) {
295             totalSize += file.length();
296         }
297
298     }
299
300     public static File getDestinationDirectory(File f) {
301         return getAlternativeFileIfExists(new File(f.getPath() + ".tmp"));
302     }
303
304     public static File getAlternativeFileIfExists(File f) {
305         if (!f.exists())
306             return f;
307         File parent = f.getParentFile();
308         final String name = f.getName();
309         final String mainName;
310         final String ext;
311         if (f.isDirectory()) {
312             mainName = name;
313             ext = "";
314         }
315         else {
316             final int index = name.indexOf('.');
317             if (index == 0)
318                 throw new IllegalArgumentException("dot file not supported at getAlternativeFileIfExists");
319             if (index > 0) {
320                 mainName = name.substring(0, index);
321                 ext = name.substring(index + 1);
322             }
323             else {
324                 mainName = name;
325                 ext = "";
326             }
327         }
328         for (int i = 1; i <= 1000; i++) {
329             final String newName = String.format("%s(%d).%s", mainName, i, ext);
330             File newFile = new File(parent, newName);
331             if (!newFile.exists())
332                 return newFile;
333         }
334         throw new RuntimeException("over 100 times to try to resolve alternative file.");
335     }
336
337     public static ArchiveCreator getArchiveCreator(File dst, ArchiveType type) throws IOException {
338         switch (type) {
339             case TAR:
340                 return new TarArchiveCreator(new BufferedOutputStream(new FileOutputStream(dst)));
341             case TARGZ:
342                 return new TarArchiveCreator(new GZIPOutputStream(new BufferedOutputStream(new FileOutputStream(dst))));
343             case TARZ:
344             case TARBZ2:
345             case TARXZ:
346                 return new TarArchiveCreator(getOutputStream(dst, type));
347             case ZIP:
348                 return new ZipFile(dst, true);
349             case LZH:
350                 return new LzhFile(dst, true);
351             case CAB:
352                 return new CabArchiveCreator(new BufferedOutputStream(new FileOutputStream(dst)));
353             default:
354         }
355         throw new IllegalStateException("File: " + dst + ", ArchiverType: " + type);
356     }
357
358     private static OutputStream getOutputStream(File file, ArchiveType type) throws IOException {
359         try {
360             final String cname = System.getProperty("plugin.stream.o." + type, "");
361             if (cname.length() == 0)
362                 throw new UnsupportedOperationException("File: " + file + ", ArchiverType: " + type);
363             final Class<?> c = Class.forName(cname);
364             Constructor<?> ctor = c.getConstructor(new Class<?>[]{InputStream.class});
365             return (OutputStream)ctor.newInstance(new BufferedOutputStream(new FileOutputStream(file)));
366         }
367         catch (IOException ex) {
368             throw ex;
369         }
370         catch (Exception ex) {
371             throw new UnsupportedOperationException("File: " + file + ", ArchiverType: " + type, ex);
372         }
373     }
374
375     public static ArchiveExtractor getArchiveExtractor(File src, ArchiveType type) throws IOException {
376         switch (type) {
377             case TAR:
378                 return new TarArchiveExtractor(new BufferedInputStream(new FileInputStream(src)));
379             case TARGZ:
380                 return new TarArchiveExtractor(new GZIPInputStream(new BufferedInputStream(new FileInputStream(src))));
381             case TARZ:
382             case TARBZ2:
383             case TARXZ:
384                 return new TarArchiveExtractor(new BufferedInputStream(getInputStream(src, type)));
385             case ZIP:
386                 return new ZipFile(src, true);
387             case LZH:
388                 return new LzhFile(src, true);
389             case CAB:
390                 return new CabArchiveExtractor(new BufferedInputStream(new FileInputStream(src)));
391             default:
392         }
393         throw new IllegalStateException("File: " + src + ", ArchiverType: " + type);
394     }
395
396     private static InputStream getInputStream(File file, ArchiveType type) throws IOException {
397         try {
398             final String cname = System.getProperty("plugin.stream.i." + type, "");
399             if (cname.length() == 0)
400                 throw new UnsupportedOperationException("File: " + file + ", ArchiverType: " + type);
401             final Class<?> c = Class.forName(cname);
402             Constructor<?> ctor = c.getConstructor(new Class<?>[]{InputStream.class});
403             return (InputStream)ctor.newInstance(new FileInputStream(file));
404         }
405         catch (IOException ex) {
406             throw ex;
407         }
408         catch (Exception ex) {
409             throw new UnsupportedOperationException("File: " + file + ", ArchiverType: " + type, ex);
410         }
411     }
412
413 }