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.nio.charset.Charset;
024import java.text.DateFormat;
025import java.util.ArrayList;
026import java.util.Collection;
027import java.util.Date;
028import java.util.List;
029import java.util.StringTokenizer;
030
031import org.apache.commons.vfs2.Capability;
032import org.apache.commons.vfs2.FileContent;
033import org.apache.commons.vfs2.FileObject;
034import org.apache.commons.vfs2.FileSystemException;
035import org.apache.commons.vfs2.FileSystemManager;
036import org.apache.commons.vfs2.FileType;
037import org.apache.commons.vfs2.FileUtil;
038import org.apache.commons.vfs2.Selectors;
039import org.apache.commons.vfs2.VFS;
040import org.apache.commons.vfs2.operations.FileOperationProvider;
041
042/**
043 * A simple command-line shell for performing file operations.
044 * <p>
045 * See <a href="https://wiki.apache.org/commons/VfsExampleShell">Commons VFS Shell Examples</a> in Apache Commons Wiki.
046 */
047public final class Shell {
048    private final FileSystemManager mgr;
049    private FileObject cwd;
050    private final BufferedReader reader;
051
052    private Shell() throws IOException {
053        mgr = VFS.getManager();
054        cwd = mgr.toFileObject(new File(System.getProperty("user.dir")));
055        reader = new BufferedReader(new InputStreamReader(System.in, Charset.defaultCharset()));
056    }
057
058    public static void main(final String[] args) {
059        try {
060            new Shell().go();
061        } catch (final Exception e) {
062            e.printStackTrace();
063            System.exit(1);
064        }
065        System.exit(0);
066    }
067
068    private void go() throws Exception {
069        System.out.println("VFS Shell " + getVersion(Shell.class));
070        while (true) {
071            final String[] cmd = nextCommand();
072            if (cmd == null) {
073                return;
074            }
075            if (cmd.length == 0) {
076                continue;
077            }
078            final String cmdName = cmd[0];
079            if (cmdName.equalsIgnoreCase("exit") || cmdName.equalsIgnoreCase("quit")) {
080                return;
081            }
082            try {
083                handleCommand(cmd);
084            } catch (final Exception e) {
085                System.err.println("Command failed:");
086                e.printStackTrace(System.err);
087            }
088        }
089    }
090
091    /**
092     * Handles a command.
093     */
094    private void handleCommand(final String[] cmd) throws Exception {
095        final String cmdName = cmd[0];
096        if (cmdName.equalsIgnoreCase("cat")) {
097            cat(cmd);
098        } else if (cmdName.equalsIgnoreCase("cd")) {
099            cd(cmd);
100        } else if (cmdName.equalsIgnoreCase("cp")) {
101            cp(cmd);
102        } else if (cmdName.equalsIgnoreCase("help") || cmdName.equals("?")) {
103            help();
104        } else if (cmdName.equalsIgnoreCase("ls")) {
105            ls(cmd);
106        } else if (cmdName.equalsIgnoreCase("pwd")) {
107            pwd();
108        } else if (cmdName.equalsIgnoreCase("rm")) {
109            rm(cmd);
110        } else if (cmdName.equalsIgnoreCase("touch")) {
111            touch(cmd);
112        } else if (cmdName.equalsIgnoreCase("info")) {
113            info(cmd);
114        } else {
115            System.err.println("Unknown command \"" + cmdName + "\" (Try 'help').");
116        }
117    }
118
119    private void info(String[] cmd) throws Exception {
120        if (cmd.length > 1) {
121            info(cmd[1]);
122        } else {
123            System.out.println(
124                    "Default manager: \"" + mgr.getClass().getName() + "\" " + "version " + getVersion(mgr.getClass()));
125            String[] schemes = mgr.getSchemes();
126            List<String> virtual = new ArrayList<>();
127            List<String> physical = new ArrayList<>();
128            for (int i = 0; i < schemes.length; i++) {
129                Collection<Capability> caps = mgr.getProviderCapabilities(schemes[i]);
130                if (caps != null) {
131                    if (caps.contains(Capability.VIRTUAL) || caps.contains(Capability.COMPRESS)
132                            || caps.contains(Capability.DISPATCHER)) {
133                        virtual.add(schemes[i]);
134                    } else {
135                        physical.add(schemes[i]);
136                    }
137                }
138            }
139            if (!physical.isEmpty()) {
140                System.out.println("  Provider Schemes: " + physical);
141            }
142            if (!virtual.isEmpty()) {
143                System.out.println("   Virtual Schemes: " + virtual);
144            }
145        }
146    }
147
148    private void info(String scheme) throws Exception {
149        System.out.println("Provider Info for scheme \"" + scheme + "\":");
150        Collection<Capability> caps;
151        caps = mgr.getProviderCapabilities(scheme);
152        if (caps != null && !caps.isEmpty()) {
153            System.out.println("  capabilities: " + caps);
154        }
155        FileOperationProvider[] ops = mgr.getOperationProviders(scheme);
156        if (ops != null && ops.length > 0) {
157            System.out.println("  operations: " + ops);
158        }
159    }
160
161    /**
162     * Does a 'help' command.
163     */
164    private void help() {
165        System.out.println("Commands:");
166        System.out.println("cat <file>         Displays the contents of a file.");
167        System.out.println("cd [folder]        Changes current folder.");
168        System.out.println("cp <src> <dest>    Copies a file or folder.");
169        System.out.println("help               Shows this message.");
170        System.out.println("info [scheme]      Displays information about providers.");
171        System.out.println("ls [-R] [path]     Lists contents of a file or folder.");
172        System.out.println("pwd                Displays current folder.");
173        System.out.println("rm <path>          Deletes a file or folder.");
174        System.out.println("touch <path>       Sets the last-modified time of a file.");
175        System.out.println("exit, quit         Exits this program.");
176    }
177
178    /**
179     * Does an 'rm' command.
180     */
181    private void rm(final String[] cmd) throws Exception {
182        if (cmd.length < 2) {
183            throw new Exception("USAGE: rm <path>");
184        }
185
186        final FileObject file = mgr.resolveFile(cwd, cmd[1]);
187        file.delete(Selectors.SELECT_SELF);
188    }
189
190    /**
191     * Does a 'cp' command.
192     */
193    private void cp(final String[] cmd) throws Exception {
194        if (cmd.length < 3) {
195            throw new Exception("USAGE: cp <src> <dest>");
196        }
197
198        final FileObject src = mgr.resolveFile(cwd, cmd[1]);
199        FileObject dest = mgr.resolveFile(cwd, cmd[2]);
200        if (dest.exists() && dest.getType() == FileType.FOLDER) {
201            dest = dest.resolveFile(src.getName().getBaseName());
202        }
203
204        dest.copyFrom(src, Selectors.SELECT_ALL);
205    }
206
207    /**
208     * Does a 'cat' command.
209     */
210    private void cat(final String[] cmd) throws Exception {
211        if (cmd.length < 2) {
212            throw new Exception("USAGE: cat <path>");
213        }
214
215        // Locate the file
216        final FileObject file = mgr.resolveFile(cwd, cmd[1]);
217
218        // Dump the contents to System.out
219        FileUtil.writeContent(file, System.out);
220        System.out.println();
221    }
222
223    /**
224     * Does a 'pwd' command.
225     */
226    private void pwd() {
227        System.out.println("Current folder is " + cwd.getName());
228    }
229
230    /**
231     * Does a 'cd' command. If the taget directory does not exist, a message is printed to <code>System.err</code>.
232     */
233    private void cd(final String[] cmd) throws Exception {
234        final String path;
235        if (cmd.length > 1) {
236            path = cmd[1];
237        } else {
238            path = System.getProperty("user.home");
239        }
240
241        // Locate and validate the folder
242        final FileObject tmp = mgr.resolveFile(cwd, path);
243        if (tmp.exists()) {
244            cwd = tmp;
245        } else {
246            System.out.println("Folder does not exist: " + tmp.getName());
247        }
248        System.out.println("Current folder is " + cwd.getName());
249    }
250
251    /**
252     * Does an 'ls' command.
253     */
254    private void ls(final String[] cmd) throws FileSystemException {
255        int pos = 1;
256        final boolean recursive;
257        if (cmd.length > pos && cmd[pos].equals("-R")) {
258            recursive = true;
259            pos++;
260        } else {
261            recursive = false;
262        }
263
264        final FileObject file;
265        if (cmd.length > pos) {
266            file = mgr.resolveFile(cwd, cmd[pos]);
267        } else {
268            file = cwd;
269        }
270
271        if (file.getType() == FileType.FOLDER) {
272            // List the contents
273            System.out.println("Contents of " + file.getName());
274            listChildren(file, recursive, "");
275        } else {
276            // Stat the file
277            System.out.println(file.getName());
278            final FileContent content = file.getContent();
279            System.out.println("Size: " + content.getSize() + " bytes.");
280            final DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM);
281            final String lastMod = dateFormat.format(new Date(content.getLastModifiedTime()));
282            System.out.println("Last modified: " + lastMod);
283        }
284    }
285
286    /**
287     * Does a 'touch' command.
288     */
289    private void touch(final String[] cmd) throws Exception {
290        if (cmd.length < 2) {
291            throw new Exception("USAGE: touch <path>");
292        }
293        final FileObject file = mgr.resolveFile(cwd, cmd[1]);
294        if (!file.exists()) {
295            file.createFile();
296        }
297        file.getContent().setLastModifiedTime(System.currentTimeMillis());
298    }
299
300    /**
301     * Lists the children of a folder.
302     */
303    private void listChildren(final FileObject dir, final boolean recursive, final String prefix)
304            throws FileSystemException {
305        final FileObject[] children = dir.getChildren();
306        for (final FileObject child : children) {
307            System.out.print(prefix);
308            System.out.print(child.getName().getBaseName());
309            if (child.getType() == FileType.FOLDER) {
310                System.out.println("/");
311                if (recursive) {
312                    listChildren(child, recursive, prefix + "    ");
313                }
314            } else {
315                System.out.println();
316            }
317        }
318    }
319
320    /**
321     * Returns the next command, split into tokens.
322     */
323    private String[] nextCommand() throws IOException {
324        System.out.print("> ");
325        final String line = reader.readLine();
326        if (line == null) {
327            return null;
328        }
329        final ArrayList<String> cmd = new ArrayList<>();
330        final StringTokenizer tokens = new StringTokenizer(line);
331        while (tokens.hasMoreTokens()) {
332            cmd.add(tokens.nextToken());
333        }
334        return cmd.toArray(new String[cmd.size()]);
335    }
336
337    private static String getVersion(Class<?> cls) {
338        try {
339            return cls.getPackage().getImplementationVersion();
340        } catch (Exception ignored) {
341            return "N/A";
342        }
343    }
344}