001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.vfs2.example;
018
019import java.io.BufferedReader;
020import java.io.File;
021import java.io.IOException;
022import java.io.InputStreamReader;
023import java.net.URL;
024import java.nio.charset.Charset;
025import java.text.DateFormat;
026import java.util.ArrayList;
027import java.util.Arrays;
028import java.util.Collection;
029import java.util.Date;
030import java.util.List;
031import java.util.StringTokenizer;
032
033import org.apache.commons.lang3.ArrayUtils;
034import org.apache.commons.lang3.SystemProperties;
035import org.apache.commons.vfs2.Capability;
036import org.apache.commons.vfs2.FileContent;
037import org.apache.commons.vfs2.FileObject;
038import org.apache.commons.vfs2.FileSystemException;
039import org.apache.commons.vfs2.FileSystemManager;
040import org.apache.commons.vfs2.FileType;
041import org.apache.commons.vfs2.Selectors;
042import org.apache.commons.vfs2.VFS;
043import org.apache.commons.vfs2.impl.StandardFileSystemManager;
044import org.apache.commons.vfs2.operations.FileOperationProvider;
045import org.apache.commons.vfs2.util.FileObjectUtils;
046
047/**
048 * A simple command-line shell for performing file operations.
049 * <p>
050 * See <a href="https://wiki.apache.org/commons/VfsExampleShell">Commons VFS Shell Examples</a> in Apache Commons Wiki.
051 */
052public final class Shell {
053
054    private static String getVersion(final Class<?> cls) {
055        try {
056            return cls.getPackage().getImplementationVersion();
057        } catch (final Exception ignored) {
058            return "N/A";
059        }
060    }
061    /**
062     * Invokes this example from the command line.
063     *
064     * @param args Arguments TODO
065     */
066    public static void main(final String[] args) {
067        try {
068            new Shell().go();
069        } catch (final Exception e) {
070            e.printStackTrace();
071            System.exit(1);
072        }
073        System.exit(0);
074    }
075    private final FileSystemManager mgr;
076
077    private FileObject cwd;
078
079    private final BufferedReader reader;
080
081    private Shell() throws IOException {
082        final String providers = System.getProperty("providers");
083        final URL providersUrl = providers != null ? Shell.class.getResource("/" + providers) : null;
084
085        if (providersUrl != null) {
086            mgr = new StandardFileSystemManager();
087            System.out.println("Custom providers configuration used: " + providersUrl);
088            ((StandardFileSystemManager) mgr).setConfiguration(providersUrl);
089            ((StandardFileSystemManager) mgr).init();
090        } else {
091            mgr = VFS.getManager();
092        }
093
094        cwd = mgr.toFileObject(new File(System.getProperty("user.dir")));
095        reader = new BufferedReader(new InputStreamReader(System.in, Charset.defaultCharset()));
096    }
097
098    /**
099     * Does a 'cat' command.
100     */
101    private void cat(final String[] cmd) throws Exception {
102        if (cmd.length < 2) {
103            throw new Exception("USAGE: cat <path>");
104        }
105
106        // Locate the file
107        final FileObject file = mgr.resolveFile(cwd, cmd[1]);
108
109        // Dump the contents to System.out
110        FileObjectUtils.writeContent(file, System.out);
111        System.out.println();
112    }
113
114    /**
115     * Does a 'cd' command. If the taget directory does not exist, a message is printed to {@code System.err}.
116     */
117    private void cd(final String[] cmd) throws Exception {
118        final String path;
119        if (cmd.length > 1) {
120            path = cmd[1];
121        } else {
122            path = SystemProperties.getUserHome();
123        }
124
125        // Locate and validate the folder
126        final FileObject tmp = mgr.resolveFile(cwd, path);
127        if (tmp.exists()) {
128            cwd = tmp;
129        } else {
130            System.out.println("Folder does not exist: " + tmp.getName());
131        }
132        System.out.println("Current folder is " + cwd.getName());
133    }
134
135    /**
136     * Does a 'cp' command.
137     */
138    private void cp(final String[] cmd) throws Exception {
139        if (cmd.length < 3) {
140            throw new Exception("USAGE: cp <src> <dest>");
141        }
142
143        final FileObject src = mgr.resolveFile(cwd, cmd[1]);
144        FileObject dest = mgr.resolveFile(cwd, cmd[2]);
145        if (dest.exists() && dest.getType() == FileType.FOLDER) {
146            dest = dest.resolveFile(src.getName().getBaseName());
147        }
148
149        dest.copyFrom(src, Selectors.SELECT_ALL);
150    }
151
152    private void go() throws Exception {
153        System.out.println("VFS Shell " + getVersion(Shell.class));
154        while (true) {
155            final String[] commands = nextCommand();
156            if (commands == null) {
157                return;
158            }
159            if (commands.length == 0) {
160                continue;
161            }
162            final String cmdName = commands[0];
163            if (cmdName.equalsIgnoreCase("exit") || cmdName.equalsIgnoreCase("quit")) {
164                return;
165            }
166            try {
167                handleCommand(commands);
168            } catch (final Exception e) {
169                System.err.println("Command failed:");
170                e.printStackTrace(System.err);
171            }
172        }
173    }
174
175    /**
176     * Handles a command.
177     */
178    private void handleCommand(final String[] cmd) throws Exception {
179        final String cmdName = cmd[0];
180        if (cmdName.equalsIgnoreCase("cat")) {
181            cat(cmd);
182        } else if (cmdName.equalsIgnoreCase("cd")) {
183            cd(cmd);
184        } else if (cmdName.equalsIgnoreCase("cp")) {
185            cp(cmd);
186        } else if (cmdName.equalsIgnoreCase("help") || cmdName.equals("?")) {
187            help();
188        } else if (cmdName.equalsIgnoreCase("ls")) {
189            ls(cmd);
190        } else if (cmdName.equalsIgnoreCase("pwd")) {
191            pwd();
192        } else if (cmdName.equalsIgnoreCase("pwfs")) {
193            pwfs();
194        } else if (cmdName.equalsIgnoreCase("rm")) {
195            rm(cmd);
196        } else if (cmdName.equalsIgnoreCase("touch")) {
197            touch(cmd);
198        } else if (cmdName.equalsIgnoreCase("info")) {
199            info(cmd);
200        } else {
201            System.err.println("Unknown command \"" + cmdName + "\" (Try 'help').");
202        }
203    }
204
205    /**
206     * Does a 'help' command.
207     */
208    private void help() {
209        System.out.println("Commands:");
210        System.out.println("cat <file>         Displays the contents of a file.");
211        System.out.println("cd [folder]        Changes current folder.");
212        System.out.println("cp <src> <dest>    Copies a file or folder.");
213        System.out.println("help               Shows this message.");
214        System.out.println("info [scheme]      Displays information about providers.");
215        System.out.println("ls [-R] [path]     Lists contents of a file or folder.");
216        System.out.println("pwd                Displays current folder.");
217        System.out.println("pwfs               Displays current file system.");
218        System.out.println("rm <path>          Deletes a file or folder.");
219        System.out.println("touch <path>       Sets the last-modified time of a file.");
220        System.out.println("exit, quit         Exits this program.");
221    }
222
223    private void info(final String scheme) throws Exception {
224        System.out.println("Provider Info for scheme \"" + scheme + "\":");
225        final Collection<Capability> caps;
226        caps = mgr.getProviderCapabilities(scheme);
227        if (caps != null && !caps.isEmpty()) {
228            System.out.println("  capabilities: " + caps);
229        }
230        final FileOperationProvider[] ops = mgr.getOperationProviders(scheme);
231        if (ops != null && ops.length > 0) {
232            System.out.println("  operations: " + Arrays.toString(ops));
233        }
234    }
235
236    private void info(final String[] cmd) throws Exception {
237        if (cmd.length > 1) {
238            info(cmd[1]);
239        } else {
240            System.out.println("Default manager: \"" + mgr.getClass().getName() + "\" " + "version " + getVersion(mgr.getClass()));
241            final String[] schemes = mgr.getSchemes();
242            final List<String> virtual = new ArrayList<>();
243            final List<String> physical = new ArrayList<>();
244            for (final String scheme : schemes) {
245                final Collection<Capability> caps = mgr.getProviderCapabilities(scheme);
246                if (caps != null) {
247                    if (caps.contains(Capability.VIRTUAL) || caps.contains(Capability.COMPRESS) || caps.contains(Capability.DISPATCHER)) {
248                        virtual.add(scheme);
249                    } else {
250                        physical.add(scheme);
251                    }
252                }
253            }
254            if (!physical.isEmpty()) {
255                System.out.println("  Provider Schemes: " + physical);
256            }
257            if (!virtual.isEmpty()) {
258                System.out.println("   Virtual Schemes: " + virtual);
259            }
260        }
261    }
262
263    /**
264     * Lists the children of a folder.
265     */
266    private void listChildren(final FileObject dir, final boolean recursive, final String prefix) throws FileSystemException {
267        final FileObject[] children = dir.getChildren();
268        for (final FileObject child : children) {
269            System.out.print(prefix);
270            System.out.print(child.getName().getBaseName());
271            if (child.getType() == FileType.FOLDER) {
272                System.out.println("/");
273                if (recursive) {
274                    listChildren(child, true, prefix + "    ");
275                }
276            } else {
277                System.out.println();
278            }
279        }
280    }
281
282    /**
283     * Does an 'ls' command.
284     */
285    private void ls(final String[] cmd) throws FileSystemException {
286        int pos = 1;
287        final boolean recursive;
288        if (cmd.length > pos && cmd[pos].equals("-R")) {
289            recursive = true;
290            pos++;
291        } else {
292            recursive = false;
293        }
294
295        final FileObject file;
296        if (cmd.length > pos) {
297            file = mgr.resolveFile(cwd, cmd[pos]);
298        } else {
299            file = cwd;
300        }
301
302        if (file.getType() == FileType.FOLDER) {
303            // List the contents
304            System.out.println("Contents of " + file.getName());
305            listChildren(file, recursive, "");
306        } else {
307            // Stat the file
308            System.out.println(file.getName());
309            final FileContent content = file.getContent();
310            System.out.println("Size: " + content.getSize() + " bytes.");
311            final DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM);
312            final String lastMod = dateFormat.format(new Date(content.getLastModifiedTime()));
313            System.out.println("Last modified: " + lastMod);
314        }
315    }
316
317    /**
318     * Returns the next command, split into tokens.
319     */
320    private String[] nextCommand() throws IOException {
321        System.out.print("> ");
322        final String line = reader.readLine();
323        if (line == null) {
324            return null;
325        }
326        final ArrayList<String> cmd = new ArrayList<>();
327        final StringTokenizer tokens = new StringTokenizer(line);
328        while (tokens.hasMoreTokens()) {
329            cmd.add(tokens.nextToken());
330        }
331        return cmd.toArray(ArrayUtils.EMPTY_STRING_ARRAY);
332    }
333
334    /**
335     * Does a 'pwd' command.
336     */
337    private void pwd() {
338        System.out.println("Current folder is " + cwd.getName());
339    }
340
341    /**
342     * Does a 'pwfs' command.
343     */
344    private void pwfs() {
345        System.out.println("FileSystem of current folder is " + cwd.getFileSystem() + " (root: " + cwd.getFileSystem().getRootURI() + ")");
346    }
347
348    /**
349     * Does an 'rm' command.
350     */
351    private void rm(final String[] cmd) throws Exception {
352        if (cmd.length < 2) {
353            throw new Exception("USAGE: rm <path>");
354        }
355
356        final FileObject file = mgr.resolveFile(cwd, cmd[1]);
357        file.delete(Selectors.SELECT_SELF);
358    }
359
360    /**
361     * Does a 'touch' command.
362     */
363    private void touch(final String[] cmd) throws Exception {
364        if (cmd.length < 2) {
365            throw new Exception("USAGE: touch <path>");
366        }
367        final FileObject file = mgr.resolveFile(cwd, cmd[1]);
368        if (!file.exists()) {
369            file.createFile();
370        }
371        file.getContent().setLastModifiedTime(System.currentTimeMillis());
372    }
373}