1/*
2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24import java.nio.file.*;
25import java.nio.file.attribute.*;
26import java.nio.file.spi.FileSystemProvider;
27import java.nio.channels.SeekableByteChannel;
28import java.net.URI;
29import java.util.*;
30import java.io.*;
31
32/**
33 * A "pass through" file system implementation that passes through, or delegates,
34 * everything to the default file system.
35 */
36
37class PassThroughFileSystem extends FileSystem {
38    private final FileSystemProvider provider;
39    private final FileSystem delegate;
40
41    PassThroughFileSystem(FileSystemProvider provider, FileSystem delegate) {
42        this.provider = provider;
43        this.delegate = delegate;
44    }
45
46    /**
47     * Creates a new "pass through" file system. Useful for test environments
48     * where the provider might not be deployed.
49     */
50    static FileSystem create() throws IOException {
51        FileSystemProvider provider = new PassThroughProvider();
52        Map<String,?> env = Collections.emptyMap();
53        URI uri = URI.create("pass:///");
54        return provider.newFileSystem(uri, env);
55    }
56
57    static Path unwrap(Path wrapper) {
58        if (wrapper == null)
59            throw new NullPointerException();
60        if (!(wrapper instanceof PassThroughPath))
61            throw new ProviderMismatchException();
62        return ((PassThroughPath)wrapper).delegate;
63    }
64
65    @Override
66    public FileSystemProvider provider() {
67        return provider;
68    }
69
70    @Override
71    public void close() throws IOException {
72        delegate.close();
73    }
74
75    @Override
76    public boolean isOpen() {
77        return delegate.isOpen();
78    }
79
80    @Override
81    public boolean isReadOnly() {
82        return delegate.isReadOnly();
83    }
84
85    @Override
86    public String getSeparator() {
87        return delegate.getSeparator();
88    }
89
90    @Override
91    public Iterable<Path> getRootDirectories() {
92        final Iterable<Path> roots = delegate.getRootDirectories();
93        return new Iterable<Path>() {
94            @Override
95            public Iterator<Path> iterator() {
96                final Iterator<Path> itr = roots.iterator();
97                return new Iterator<Path>() {
98                    @Override
99                    public boolean hasNext() {
100                        return itr.hasNext();
101                    }
102                    @Override
103                    public Path next() {
104                        return new PassThroughPath(delegate, itr.next());
105                    }
106                    @Override
107                    public void remove() {
108                        itr.remove();
109                    }
110                };
111            }
112        };
113    }
114
115    @Override
116    public Iterable<FileStore> getFileStores() {
117        // assume that unwrapped objects aren't exposed
118        return delegate.getFileStores();
119    }
120
121    @Override
122    public Set<String> supportedFileAttributeViews() {
123        // assume that unwrapped objects aren't exposed
124        return delegate.supportedFileAttributeViews();
125    }
126
127    @Override
128    public Path getPath(String first, String... more) {
129        return new PassThroughPath(this, delegate.getPath(first, more));
130    }
131
132    @Override
133    public PathMatcher getPathMatcher(String syntaxAndPattern) {
134        final PathMatcher matcher = delegate.getPathMatcher(syntaxAndPattern);
135        return new PathMatcher() {
136            @Override
137            public boolean matches(Path path) {
138                return matcher.matches(unwrap(path));
139            }
140        };
141    }
142
143    @Override
144    public UserPrincipalLookupService getUserPrincipalLookupService() {
145        // assume that unwrapped objects aren't exposed
146        return delegate.getUserPrincipalLookupService();
147    }
148
149    @Override
150    public WatchService newWatchService() throws IOException {
151        // to keep it simple
152        throw new UnsupportedOperationException();
153    }
154
155    static class PassThroughProvider extends FileSystemProvider {
156        private static final String SCHEME = "pass";
157        private static volatile PassThroughFileSystem delegate;
158
159        public PassThroughProvider() { }
160
161        @Override
162        public String getScheme() {
163            return SCHEME;
164        }
165
166        private void checkScheme(URI uri) {
167            if (!uri.getScheme().equalsIgnoreCase(SCHEME))
168                throw new IllegalArgumentException();
169        }
170
171        private void checkUri(URI uri) {
172            checkScheme(uri);
173            if (!uri.getSchemeSpecificPart().equals("///"))
174                throw new IllegalArgumentException();
175        }
176
177        @Override
178        public FileSystem newFileSystem(URI uri, Map<String,?> env)
179            throws IOException
180        {
181            checkUri(uri);
182            synchronized (PassThroughProvider.class) {
183                if (delegate != null)
184                    throw new FileSystemAlreadyExistsException();
185                PassThroughFileSystem result =
186                    new PassThroughFileSystem(this, FileSystems.getDefault());
187                delegate = result;
188                return result;
189            }
190        }
191
192        @Override
193        public FileSystem getFileSystem(URI uri) {
194            checkUri(uri);
195            FileSystem result = delegate;
196            if (result == null)
197                throw new FileSystemNotFoundException();
198            return result;
199        }
200
201        @Override
202        public Path getPath(URI uri) {
203            checkScheme(uri);
204            if (delegate == null)
205                throw new FileSystemNotFoundException();
206            uri = URI.create(delegate.provider().getScheme() + ":" +
207                             uri.getSchemeSpecificPart());
208            return new PassThroughPath(delegate, delegate.provider().getPath(uri));
209        }
210
211        @Override
212        public void setAttribute(Path file, String attribute, Object value, LinkOption... options)
213            throws IOException
214        {
215            Files.setAttribute(unwrap(file), attribute, value, options);
216        }
217
218        @Override
219        public Map<String,Object> readAttributes(Path file, String attributes, LinkOption... options)
220            throws IOException
221        {
222            return Files.readAttributes(unwrap(file), attributes, options);
223        }
224
225        @Override
226        public <V extends FileAttributeView> V getFileAttributeView(Path file,
227                                                                    Class<V> type,
228                                                                    LinkOption... options)
229        {
230            return Files.getFileAttributeView(unwrap(file), type, options);
231        }
232
233        @Override
234        public <A extends BasicFileAttributes> A readAttributes(Path file,
235                                                                Class<A> type,
236                                                                LinkOption... options)
237            throws IOException
238        {
239            return Files.readAttributes(unwrap(file), type, options);
240        }
241
242        @Override
243        public void delete(Path file) throws IOException {
244            Files.delete(unwrap(file));
245        }
246
247        @Override
248        public void createSymbolicLink(Path link, Path target, FileAttribute<?>... attrs)
249            throws IOException
250        {
251            Files.createSymbolicLink(unwrap(link), unwrap(target), attrs);
252        }
253
254        @Override
255        public void createLink(Path link, Path existing) throws IOException {
256            Files.createLink(unwrap(link), unwrap(existing));
257        }
258
259        @Override
260        public Path readSymbolicLink(Path link) throws IOException {
261            Path target = Files.readSymbolicLink(unwrap(link));
262            return new PassThroughPath(delegate, target);
263        }
264
265
266        @Override
267        public void copy(Path source, Path target, CopyOption... options) throws IOException {
268            Files.copy(unwrap(source), unwrap(target), options);
269        }
270
271        @Override
272        public void move(Path source, Path target, CopyOption... options) throws IOException {
273            Files.move(unwrap(source), unwrap(target), options);
274        }
275
276        private DirectoryStream<Path> wrap(final DirectoryStream<Path> stream) {
277            return new DirectoryStream<Path>() {
278                @Override
279                public Iterator<Path> iterator() {
280                    final Iterator<Path> itr = stream.iterator();
281                    return new Iterator<Path>() {
282                        @Override
283                        public boolean hasNext() {
284                            return itr.hasNext();
285                        }
286                        @Override
287                        public Path next() {
288                            return new PassThroughPath(delegate, itr.next());
289                        }
290                        @Override
291                        public void remove() {
292                            itr.remove();
293                        }
294                    };
295                }
296                @Override
297                public void close() throws IOException {
298                    stream.close();
299                }
300            };
301        }
302
303        @Override
304        public DirectoryStream<Path> newDirectoryStream(Path dir, DirectoryStream.Filter<? super Path> filter)
305            throws IOException
306        {
307            return wrap(Files.newDirectoryStream(unwrap(dir), filter));
308        }
309
310        @Override
311        public void createDirectory(Path dir, FileAttribute<?>... attrs)
312            throws IOException
313        {
314            Files.createDirectory(unwrap(dir), attrs);
315        }
316
317        @Override
318        public SeekableByteChannel newByteChannel(Path file,
319                                                  Set<? extends OpenOption> options,
320                                                  FileAttribute<?>... attrs)
321            throws IOException
322        {
323            return Files.newByteChannel(unwrap(file), options, attrs);
324        }
325
326
327        @Override
328        public boolean isHidden(Path file) throws IOException {
329            return Files.isHidden(unwrap(file));
330        }
331
332        @Override
333        public FileStore getFileStore(Path file) throws IOException {
334            return Files.getFileStore(unwrap(file));
335        }
336
337        @Override
338        public boolean isSameFile(Path file, Path other) throws IOException {
339            return Files.isSameFile(unwrap(file), unwrap(other));
340        }
341
342        @Override
343        public void checkAccess(Path file, AccessMode... modes)
344            throws IOException
345        {
346            // hack
347            if (modes.length == 0) {
348                if (Files.exists(unwrap(file)))
349                    return;
350                else
351                    throw new NoSuchFileException(file.toString());
352            }
353            throw new RuntimeException("not implemented yet");
354        }
355    }
356
357    static class PassThroughPath implements Path {
358        private final FileSystem fs;
359        private final Path delegate;
360
361        PassThroughPath(FileSystem fs, Path delegate) {
362            this.fs = fs;
363            this.delegate = delegate;
364        }
365
366        private Path wrap(Path path) {
367            return (path != null) ? new PassThroughPath(fs, path) : null;
368        }
369
370        @Override
371        public FileSystem getFileSystem() {
372            return fs;
373        }
374
375        @Override
376        public boolean isAbsolute() {
377            return delegate.isAbsolute();
378        }
379
380        @Override
381        public Path getRoot() {
382            return wrap(delegate.getRoot());
383        }
384
385        @Override
386        public Path getParent() {
387            return wrap(delegate.getParent());
388        }
389
390        @Override
391        public int getNameCount() {
392            return delegate.getNameCount();
393        }
394
395        @Override
396        public Path getFileName() {
397            return wrap(delegate.getFileName());
398        }
399
400        @Override
401        public Path getName(int index) {
402            return wrap(delegate.getName(index));
403        }
404
405        @Override
406        public Path subpath(int beginIndex, int endIndex) {
407            return wrap(delegate.subpath(beginIndex, endIndex));
408        }
409
410        @Override
411        public boolean startsWith(Path other) {
412            return delegate.startsWith(unwrap(other));
413        }
414
415        @Override
416        public boolean startsWith(String other) {
417            return delegate.startsWith(other);
418        }
419
420        @Override
421        public boolean endsWith(Path other) {
422            return delegate.endsWith(unwrap(other));
423        }
424
425        @Override
426        public boolean endsWith(String other) {
427            return delegate.endsWith(other);
428        }
429
430        @Override
431        public Path normalize() {
432            return wrap(delegate.normalize());
433        }
434
435        @Override
436        public Path resolve(Path other) {
437            return wrap(delegate.resolve(unwrap(other)));
438        }
439
440        @Override
441        public Path resolve(String other) {
442            return wrap(delegate.resolve(other));
443        }
444
445        @Override
446        public Path resolveSibling(Path other) {
447            return wrap(delegate.resolveSibling(unwrap(other)));
448        }
449
450        @Override
451        public Path resolveSibling(String other) {
452            return wrap(delegate.resolveSibling(other));
453        }
454
455        @Override
456        public Path relativize(Path other) {
457            return wrap(delegate.relativize(unwrap(other)));
458        }
459
460        @Override
461        public boolean equals(Object other) {
462            if (!(other instanceof PassThroughPath))
463                return false;
464            return delegate.equals(unwrap((PassThroughPath)other));
465        }
466
467        @Override
468        public int hashCode() {
469            return delegate.hashCode();
470        }
471
472        @Override
473        public String toString() {
474            return delegate.toString();
475        }
476
477        @Override
478        public URI toUri() {
479            String ssp = delegate.toUri().getSchemeSpecificPart();
480            return URI.create(fs.provider().getScheme() + ":" + ssp);
481        }
482
483        @Override
484        public Path toAbsolutePath() {
485            return wrap(delegate.toAbsolutePath());
486        }
487
488        @Override
489        public Path toRealPath(LinkOption... options) throws IOException {
490            return wrap(delegate.toRealPath(options));
491        }
492
493        @Override
494        public File toFile() {
495            return delegate.toFile();
496        }
497
498        @Override
499        public Iterator<Path> iterator() {
500            final Iterator<Path> itr = delegate.iterator();
501            return new Iterator<Path>() {
502                @Override
503                public boolean hasNext() {
504                    return itr.hasNext();
505                }
506                @Override
507                public Path next() {
508                    return wrap(itr.next());
509                }
510                @Override
511                public void remove() {
512                    itr.remove();
513                }
514            };
515        }
516
517        @Override
518        public int compareTo(Path other) {
519            return delegate.compareTo(unwrap(other));
520        }
521
522        @Override
523        public WatchKey register(WatchService watcher,
524                                      WatchEvent.Kind<?>[] events,
525                                      WatchEvent.Modifier... modifiers)
526        {
527            throw new UnsupportedOperationException();
528        }
529
530        @Override
531        public  WatchKey register(WatchService watcher,
532                                      WatchEvent.Kind<?>... events)
533        {
534            throw new UnsupportedOperationException();
535        }
536    }
537}
538