001package co.codewizards.cloudstore.core.oio;
002
003import static co.codewizards.cloudstore.core.io.StreamUtil.*;
004
005import java.io.FileInputStream;
006import java.io.FileNotFoundException;
007import java.io.FileOutputStream;
008import java.io.FilenameFilter;
009import java.io.IOException;
010import java.io.RandomAccessFile;
011import java.net.URI;
012
013import org.slf4j.Logger;
014import org.slf4j.LoggerFactory;
015
016import co.codewizards.cloudstore.core.io.IInputStream;
017import co.codewizards.cloudstore.core.io.IOutputStream;
018import co.codewizards.cloudstore.core.util.IOUtil;
019
020/**
021 * @author Sebastian Schefczyk
022 *
023 */
024public class IoFile implements File {
025        private static final long serialVersionUID = 1L;
026
027        private static final Logger logger = LoggerFactory.getLogger(IoFile.class);
028
029
030        protected final java.io.File ioFile;
031
032        protected IoFile(final String pathname) {
033                this.ioFile = new java.io.File(pathname);
034//              _debug_assert_NioFile();
035        }
036
037        protected IoFile(final File parent, final String child) {
038                final java.io.File ioParent = parent.getIoFile();
039                this.ioFile = new java.io.File(ioParent, child);
040//              _debug_assert_NioFile();
041        }
042
043        protected IoFile(final String parent, final String child) {
044                this.ioFile = new java.io.File(parent, child);
045//              _debug_assert_NioFile();
046        }
047
048        protected IoFile(final URI uri) {
049                this.ioFile = new java.io.File(uri);
050//              _debug_assert_NioFile();
051        }
052
053        protected IoFile(final java.io.File ioFile) {
054                this.ioFile = ioFile;
055//              _debug_assert_NioFile();
056        }
057
058//      private final void _debug_assert_NioFile() {
059//              if (this.getClass() == IoFile.class)
060//                      throw new IllegalStateException("This should not be an instance of IoFile! " + ioFile);
061//
062//              if (! this.getClass().getSimpleName().equals("NioFile"))
063//                      throw new IllegalStateException("This should be an instance of NioFile! " + ioFile);
064//      }
065
066
067        @Override
068        public File getParentFile() {
069                final java.io.File parentFile = this.ioFile.getParentFile();
070                return parentFile != null ? new IoFile(parentFile) : null;
071        }
072
073        @Override
074        public String[] list() {
075                return this.ioFile.list();
076        }
077
078        @Override
079        public String[] list(final FilenameFilter filenameFilter) {
080                return this.ioFile.list(filenameFilter);
081        }
082
083        @Override
084        public File[] listFiles() {
085                final java.io.File[] ioFiles = this.ioFile.listFiles();
086                return IoFileUtil.convert(ioFiles);
087        }
088
089        @Override
090        public File[] listFiles(final java.io.FileFilter fileFilter) {
091                final java.io.File[] ioFiles = this.ioFile.listFiles(fileFilter);
092                return IoFileUtil.convert(ioFiles);
093        }
094
095        @Override
096        public File[] listFiles(final FileFilter fileFilter) {
097                final java.io.File[] ioFiles = this.ioFile.listFiles(new FileFilterWrapper(fileFilter));
098                return IoFileUtil.convert(ioFiles);
099        }
100
101        @Override
102        public File[] listFiles(final FilenameFilter fileFilter) {
103                final java.io.File[] ioFiles = this.ioFile.listFiles(fileFilter);
104                return IoFileUtil.convert(ioFiles);
105        }
106
107        @Override
108        public File getAbsoluteFile() {
109                return new IoFile(ioFile.getAbsoluteFile());
110        }
111
112        @Override
113        public boolean exists() {
114                return ioFile.exists();
115        }
116
117        @Override
118        public boolean existsNoFollow() {
119                return ioFile.exists();
120        }
121        @Override
122        public boolean createNewFile() throws IOException {
123                return ioFile.createNewFile();
124        }
125
126        @Override
127        public boolean canExecute() {
128                return ioFile.canExecute();
129        }
130
131        @Override
132        public boolean canRead() {
133                return ioFile.canRead();
134        }
135
136        @Override
137        public boolean canWrite() {
138                return ioFile.canWrite();
139        }
140
141        @Override
142        public boolean setExecutable(final boolean executable) {
143                return ioFile.setExecutable(executable);
144        }
145        @Override
146        public boolean setExecutable(final boolean executable, final boolean ownerOnly) {
147                return ioFile.setExecutable(executable, ownerOnly);
148        }
149
150        @Override
151        public boolean setReadable(final boolean readable) {
152                return ioFile.setReadable(readable);
153        }
154        @Override
155        public boolean setReadable(final boolean readable, final boolean ownerOnly) {
156                return ioFile.setReadable(readable, ownerOnly);
157        }
158
159        @Override
160        public boolean setWritable(final boolean writable) {
161                return ioFile.setWritable(writable);
162        }
163        @Override
164        public boolean setWritable(final boolean writable, final boolean ownerOnly) {
165                return ioFile.setWritable(writable, ownerOnly);
166        }
167
168        @Override
169        public int compareTo(final File otherFile) {
170                return ioFile.compareTo(otherFile.getIoFile());
171        }
172
173        @Override
174        public boolean delete() {
175                return ioFile.delete();
176        }
177
178        @Override
179        public void deleteOnExit() {
180                ioFile.deleteOnExit();
181        }
182
183        @Override
184        public void deleteRecursively() {
185                IoFileUtil.deleteRecursively(this);
186        }
187
188        @Override
189        public String getAbsolutePath() {
190                return ioFile.getAbsolutePath();
191        }
192
193        @Override
194        public File getCanonicalFile() throws IOException {
195                return new IoFile(ioFile.getCanonicalFile());
196        }
197
198        @Override
199        public String getCanonicalPath() throws IOException {
200                return ioFile.getCanonicalPath();
201        }
202
203        @Override
204        public long getFreeSpace() {
205                return ioFile.getFreeSpace();
206        }
207
208        @Override
209        public long length() {
210                return ioFile.length();
211        }
212
213        @Override
214        public boolean isRegularFileNoFollowLinks() {
215                return this.ioFile.isFile();
216        }
217
218        @Override
219        public boolean isRegularFileFollowLinks() {
220                return this.ioFile.isFile();
221        }
222
223        @Override
224        public boolean isDirectoryNoFollowSymLinks() {
225                return this.ioFile.isDirectory();
226        }
227
228        @Override
229        public boolean isDirectoryFollowSymLinks() {
230                return this.ioFile.isDirectory();
231        }
232
233        @Override
234        public boolean isSymbolicLink() {
235                // currently: no support for symlinks in this implementation
236                return false;
237        }
238
239        @Override
240        public String readSymbolicLinkToPathString() throws IOException {
241                throw new IllegalStateException("Impossible operation within this implementation: check use method 'isSymbolicLink' before!");
242        }
243
244        @Override
245        public long getLastModifiedNoFollow() {
246                // currently: no support for symlinks in this implementation => cannot do anything about follow/no-follow (that's exactly the reason for the nio-implementation!)
247                return lastModified();
248        }
249
250        @Override
251        public boolean renameTo(final File dest) {
252                return ioFile.renameTo(dest.getIoFile());
253        }
254
255        @Override
256        public boolean setLastModified(final long lastModified) {
257                return ioFile.setLastModified(lastModified);
258        }
259
260        @Override
261        public IOutputStream createOutputStream() throws FileNotFoundException {
262                return castStream(new FileOutputStream(ioFile));
263        }
264
265        @Override
266        public IInputStream createInputStream() throws FileNotFoundException {
267                return castStream(new FileInputStream(ioFile));
268        }
269
270        @Override
271        public IOutputStream createOutputStream(final boolean append) throws FileNotFoundException {
272                return castStream(new FileOutputStream(ioFile, append));
273        }
274
275        @Override
276        public String getName() {
277                return ioFile.getName();
278        }
279
280        @Override
281        public void createSymbolicLink(final String targetPath) throws IOException {
282                throw new IllegalStateException("Impossible operation within this implementation. Check whether symlinks are available here!");
283        }
284
285        @Override
286        public long lastModified() {
287                final long result = ioFile.lastModified();
288                return result;
289        }
290
291        @Override
292        public boolean isAbsolute() {
293                return ioFile.isAbsolute();
294        }
295
296        @Override
297        public String getPath() {
298                return ioFile.getPath();
299        }
300
301        @Override
302        public boolean mkdir() {
303                return ioFile.mkdir();
304        }
305
306        @Override
307        public boolean mkdirs() {
308                return ioFile.mkdirs();
309        }
310
311        @Override
312        public boolean isDirectory() {
313                return ioFile.isDirectory();
314        }
315
316        @Override
317        public void move(final File toFile) throws IOException {
318                if (toFile.exists())
319                        throw new IOException("toFile file did already exists!");
320                if (!this.ioFile.exists())
321                        throw new IllegalArgumentException("Source file did not exists!");
322                if (this.ioFile.getCanonicalPath().equals(toFile.getCanonicalPath()))
323                        return; //nothing to do!
324
325                final boolean renameTo = this.ioFile.renameTo(toFile.getIoFile());
326                // we try to do a simple rename, but this won't be successful between partitions or non-empty directories.
327                if (renameTo)
328                        return;
329
330                // 2nd solution: file: copy and delete
331                if (this.ioFile.isFile()) {
332                        IOUtil.copyFile(this, toFile);
333                        final boolean delete = this.delete();
334                        if (!delete)
335                                throw new IllegalStateException("Problem on moving file from '"
336                                                + this.ioFile.getCanonicalPath() +
337                                                "' to " + toFile.getIoFile().getCanonicalPath());
338                } else if (this.ioFile.isDirectory()) {
339                        /* If empty, but has failed the simple renameTo(), the destination
340                         * is probably on another partition (very unlikely on IOS).
341                         */
342                        if (this.ioFile.listFiles().length == 0) {
343                                throw new IllegalArgumentException("Should not occure!");
344                        } else { // non-empty directory
345                                /* remark: on IOS should be only one partition available to this app.
346                                 * And a copy/delete operation of a non-empty directory could
347                                 * last very long; as in Files.move(), this should be prevented.
348                                 * Best solution would be to rename recursively, because renaming
349                                 * also only works on same partition.
350                                 * Assuming there is only on partition, we assume renaming never fails.
351                                 * Partition-Detection: Its very hard to detect, whether to files are on the same
352                                 * partition, in a OS independent and without java.nio.Files.
353                                 * So we just assume one partition because of IOS. */
354                                IoFileUtil.moveRecursively(this, toFile);
355                        }
356                }
357        }
358
359        @Override
360        public void copyToCopyAttributes(final File toFile) throws IOException {
361                if (toFile.exists())
362                        throw new IOException("toFile file did already exists!");
363                if (!this.ioFile.exists())
364                        throw new IllegalArgumentException("Source file did not exists!");
365                if (this.ioFile.getCanonicalPath().equals(toFile.getCanonicalPath()))
366                        return; //nothing to do!
367
368                if (this.ioFile.isFile()) {
369                        IOUtil.copyFile(this, toFile);
370                } else {
371                        // java.nio.Files.copy is non-recursive, so must this implementation be!
372                        final boolean mkdir = toFile.mkdir();
373                        if (!mkdir)
374                                throw new IllegalStateException("Problem on moving directory from '"
375                                                + this.ioFile.getCanonicalPath() +
376                                                "'! Could not create directory " + toFile.getIoFile().getCanonicalPath());
377                }
378        }
379
380        @Override
381        public RandomAccessFile createRandomAccessFile(final String mode) throws FileNotFoundException {
382                return new RandomAccessFile(ioFile, mode);
383        }
384
385        @Override
386        public URI toURI() {
387                return ioFile.toURI();
388        }
389
390        @Override
391        public boolean isFile() {
392                return ioFile.isFile();
393        }
394
395        @Override
396        public boolean setLastModifiedNoFollow(final long lastModified) {
397                return ioFile.setLastModified(lastModified);
398        }
399
400        @Override
401        public String relativize(final File target) throws IOException {
402//              return IOUtil.getRelativePath(this, target); // TODO result is different, should have a look; the first shared folder was appended.
403                return IoFileRelativePathUtil.getRelativePath(this.ioFile, target.getIoFile());
404        }
405
406        @Override
407        public long getUsableSpace() {
408                return this.ioFile.getUsableSpace();
409        }
410
411        @Override
412        public java.io.File getIoFile() {
413                return ioFile;
414        }
415
416        @Override
417        public boolean equals(final Object obj) {
418                if ( !(obj instanceof IoFile) )
419                        return false;
420
421                final IoFile ioFile = (IoFile) obj;
422                return this.ioFile.equals(ioFile.ioFile);
423        }
424
425        @Override
426        public int hashCode() {
427                return ioFile.hashCode();
428        }
429
430        @Override
431        public String toString() {
432                return this.ioFile.toString();
433        }
434
435        @Override
436        public File createFile(String ... children) {
437                return OioFileFactory.createFile(this, children);
438        }
439
440//      private void writeObject(ObjectOutputStream out) throws IOException {
441//              if (!ioFile.isAbsolute())
442//                      logger.warn("File is not absolute! This may cause w");
443//
444//              out.defaultWriteObject();
445//      }
446//
447//      private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
448//
449//      }
450
451}