All Downloads are FREE. Search and download functionalities are using the official Maven repository.

eu.mihosoft.vrl.dialogs.FileDialogManager Maven / Gradle / Ivy

/* 
 * FileDialogManager.java
 * 
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2007–2018 by Michael Hoffer,
 * Copyright (c) 2015–2018 G-CSC, Uni Frankfurt,
 * Copyright (c) 2009–2015 Steinbeis Forschungszentrum (STZ Ölbronn)
 * 
 * This file is part of Visual Reflection Library (VRL).
 *
 * VRL is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * as published by the Free Software Foundation.
 * 
 * see: http://opensource.org/licenses/LGPL-3.0
 *      file://path/to/VRL/src/eu/mihosoft/vrl/resources/license/lgplv3.txt
 *
 * VRL is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * This version of VRL includes copyright notice and attribution requirements.
 * According to the LGPL this information must be displayed even if you modify
 * the source code of VRL. Neither the VRL Canvas attribution icon nor any
 * copyright statement/attribution may be removed.
 *
 * Attribution Requirements:
 *
 * If you create derived work you must do three things regarding copyright
 * notice and author attribution.
 *
 * First, the following text must be displayed on the Canvas:
 * "based on VRL source code". In this case the VRL canvas icon must be removed.
 * 
 * Second, the copyright notice must remain. It must be reproduced in any
 * program that uses VRL.
 *
 * Third, add an additional notice, stating that you modified VRL. A suitable
 * notice might read
 * "VRL source code modified by YourName 2012".
 * 
 * Note, that these requirements are in full accordance with the LGPL v3
 * (see 7. Additional Terms, b).
 *
 * Please cite the publication(s) listed below.
 *
 * Publications:
 *
 * M. Hoffer, C. Poliwoda, & G. Wittum. (2013). Visual reflection library:
 * a framework for declarative GUI programming on the Java platform.
 * Computing and Visualization in Science, 2013, 16(4),
 * 181–192. http://doi.org/10.1007/s00791-014-0230-y
 */
package eu.mihosoft.vrl.dialogs;

import eu.mihosoft.vrl.io.FileSaver;
import eu.mihosoft.vrl.io.FileLoader;
import eu.mihosoft.vrl.io.RestrictedFileSystemView;
import eu.mihosoft.vrl.io.VExtensionFileFilter;
import eu.mihosoft.vrl.system.VSysUtil;
import eu.mihosoft.vrl.visual.Canvas;
import eu.mihosoft.vrl.visual.VSwingUtil;
import java.awt.Component;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.*;
import javax.swing.filechooser.FileFilter;
import javax.swing.filechooser.FileNameExtensionFilter;

/**
 * A file dialog manager is responsible for showing a dialog for loading/saving
 * files and calling the corresponding file loader/saver. It also takes care of
 * already existing files and file extensions.
 *
 * @author Michael Hoffer <[email protected]>
 */
public class FileDialogManager {

    private static File CACHED_DIR;

    /**
     * @return the default dir
     */
    public static File getDefaultDir() {
        return CACHED_DIR;
    }

//    public static final String FILE_OR_FOLDER_LOADED_ACTION = "file-or-folder-selected";
//    //
//    protected File latestFileOrFolder;
//    
//    /**
//     * list of action listeners
//     */
//    private Collection actionListeners =
//            new ArrayList();
//
//    /**
//     * @return the changeListeners
//     */
//    public Collection getActionListeners() {
//        return actionListeners;
//    }
//    
//   
//    /**
//     * Fires an action.
//     *
//     * @param event the event
//     */
//    protected void fireAction(ActionEvent event) {
//        for (ActionListener l : actionListeners) {
//            l.actionPerformed(event);
//        }
//    }
    /**
     * Opens a load file dialog.
     *
     * @param parent the parent component of the dialog
     * @param loader the file loader to use for loading
     * @param filter the file extension filter
     * @return the loaded object or null if the file could not be
     * loaded
     */
    public Object loadFile(Canvas parent, FileLoader loader,
            FileFilter filter) {
        return loadFile(parent, loader, null, filter, false);
    }

    /**
     * Opens a load file dialog.
     *
     * @param parent the parent component of the dialog
     * @param loader the file loader to use for loading
     * @param directory the current directory to use or null if the
     * current directory shall not be defined (if the file is no directoy the
     * parent directory is used)
     * @param filter the file extension filter
     * @param restrict defines whether to restrict the dialog to the specified
     * directory and its subdirectories
     * @return the loaded object or null if the file could not be
     * loaded
     */
    public Object loadFile(Component parent, FileLoader loader, File directory,
            FileFilter filter, boolean restrict) {

        Object result = null;

        directory = chooseDefaultDir(directory);

        final JFileChooser fc = createFileChooser(restrict, filter);

        if (restrict) {
            fc.setFileSystemView(new RestrictedFileSystemView(directory));
        }

        fc.setFileHidingEnabled(true);

        if (directory != null && directory.exists()) {
            if (!directory.isDirectory()) {
                directory = directory.getParentFile();
            }
            if (directory != null && directory.canRead()) {
                fc.setCurrentDirectory(directory);
            }
        }

        int returnVal = fc.showOpenDialog(parent);

        if (returnVal == JFileChooser.APPROVE_OPTION) {
            File file = fc.getSelectedFile();

            boolean fileNotFound = false;

            if (file.exists()) {

                setDefaultDir(file);

                try {
                    result = loader.loadFile(file);
                } catch (IOException ex) {

                    Logger.getLogger(FileDialogManager.class.getName()).
                            log(Level.SEVERE, null, ex);

                    fileNotFound = true;
                }
            } else {
                fileNotFound = true;
            }

            if (fileNotFound) {

                FileNotFoundDialog.show(parent);
            }

        } else {
            System.out.println(
                    ">> Operation \"load file\" cancelled by user." + "\n");
        }

        if (VSysUtil.isMacOSX()) {
            VSwingUtil.forceNimbusLAF(null);
        }

        return result;
    }

    private JFileChooser createFileChooser(boolean restrict, FileFilter filter) {
        final JFileChooser fc;
        boolean nativeChooser = !restrict
                && (filter instanceof FileNameExtensionFilter
                || filter instanceof VExtensionFileFilter || filter == null);
        if (nativeChooser) {
            if (!VFileChooser.isNativeDialogsEnabled()) {
                if (VSysUtil.isMacOSX()) {
                    VSwingUtil.forceAppleLAF(null);
                }
            }
            fc = new VFileChooser();
        } else {
            if (VSysUtil.isMacOSX()) {
                VSwingUtil.forceAppleLAF(null);
            }
            fc = new JFileChooser();
        }

        if (filter != null) {
            fc.setFileFilter(filter);
        }

        return fc;
    }

    /**
     * Opens a load file dialog and returns the selected file.
     *
     * @param parent the parent component of the dialog
     * @param filter the file extension filter
     * @return the selected file or null if no file has been
     * selected
     */
    public File getLoadFile(Component parent, FileFilter filter) {
        return getLoadFile(parent, null, filter, false);
    }

    /**
     * Chooses the finally used default dir for all file dialogs in this class.
     *
     * @param directory the directory (may be null)
     * @return the finally used default dir or null
     */
    private static File chooseDefaultDir(File directory) {
        if (directory == null) {
            directory = getDefaultDir();
        }

        return directory;
    }

    /**
     * Defines the default directory that shall be used if no directory has been
     * specified.
     *
     * @param fileOrDir the default directory to set (if it is a file, the
     * parent dir wil be used)
     */
    public static void setDefaultDir(File fileOrDir) {

        if (fileOrDir == null) {
            return;
        }

        if (fileOrDir.isFile()) {
            fileOrDir = fileOrDir.getAbsoluteFile().getParentFile();
        }

        CACHED_DIR = fileOrDir;
    }

    /**
     * Opens a load file dialog and returns the selected file.
     *
     * @param parent the parent component of the dialog
     * @param directory the current directory to use
     * @param filter the file extension filter
     * @param restrict defines whether to restrict the dialog to the specified
     * directory and its subdirectories
     * @return the selected file or null if no file has been
     * selected
     */
    public File getLoadFile(Component parent,
            File directory, FileFilter filter, boolean restrict) {

        directory = chooseDefaultDir(directory);

        File result = null;

        final JFileChooser fc = createFileChooser(restrict, filter);

        if (restrict) {
            fc.setFileSystemView(new RestrictedFileSystemView(directory));
            restrictNavigation(fc, directory);
        }

        fc.setCurrentDirectory(directory);

        int returnVal = fc.showOpenDialog(parent);

        if (returnVal == JFileChooser.APPROVE_OPTION) {
            File file = fc.getSelectedFile();

            setDefaultDir(file);

            result = file;

        } else {
            System.out.println(
                    ">> Operation \"open file\" cancelled by user." + "\n");
        }

        if (VSysUtil.isMacOSX()) {
            VSwingUtil.forceNimbusLAF(null);
        }

        return result;
    }

    /**
     * Opens a load file dialog and returns the selected files.
     *
     * @param parent the parent component of the dialog
     * @param directory the current directory to use
     * @param filter the file extension filter
     * @param restrict defines whether to restrict the dialog to the specified
     * directory and its subdirectories
     * @return the selected file or null if no file has been
     * selected
     */
    public File[] getLoadFiles(Component parent,
            File directory, FileFilter filter, boolean restrict) {
        File[] result = null;

        directory = chooseDefaultDir(directory);

        final JFileChooser fc = createFileChooser(restrict, filter);

        if (restrict) {
            fc.setFileSystemView(new RestrictedFileSystemView(directory));
        }

        fc.setCurrentDirectory(directory);
        fc.setMultiSelectionEnabled(true);
        int returnVal = fc.showOpenDialog(parent);

        if (returnVal == JFileChooser.APPROVE_OPTION) {
            File[] file = fc.getSelectedFiles();

            if (file.length > 0) {
                setDefaultDir(file[0]);
            }

            result = file;

        } else {
            System.out.println(
                    ">> Operation \"open file\" cancelled by user." + "\n");
        }

        if (VSysUtil.isMacOSX()) {
            VSwingUtil.forceNimbusLAF(null);
        }

        return result;
    }

    /**
     * Opens a load folder dialog and returns the selected folder.
     *
     * @param parent the parent component of the dialog
     * @param directory the current directory to use
     * @param restrict defines whether to restrict the dialog to the specified
     * directory and its subdirectories
     * @return the selected folder or null if no folder has been
     * selected
     */
    public File getLoadFolder(Component parent,
            File directory, boolean restrict) {
        return getLoadFileOrFolder(parent, directory, restrict, JFileChooser.DIRECTORIES_ONLY, null);
    }

    /**
     * Opens a load file/folder dialog and returns the selected folder.
     *
     * @param parent the parent component of the dialog
     * @param directory the current directory to use
     * @param restrict defines whether to restrict the dialog to the specified
     * @param loadType specifies whether to load files, floders both , see
     * {@link javax.swing.JFileChooser#DIRECTORIES_ONLY} etc. directory and its
     * subdirectories
     * @return the selected folder or null if no folder has been
     * selected
     */
    public File getLoadFileOrFolder(Component parent,
            File directory, boolean restrict, int loadType, FileFilter filter) {
        File result = null;

        directory = chooseDefaultDir(directory);

        final JFileChooser fc = createFileChooser(restrict, filter);

        if (restrict) {
            fc.setFileSystemView(new RestrictedFileSystemView(directory));
        }

        fc.setFileHidingEnabled(true);
        fc.setFileSelectionMode(loadType);
        fc.setCurrentDirectory(directory);
        int returnVal = fc.showOpenDialog(parent);

        if (returnVal == JFileChooser.APPROVE_OPTION) {
            File file = fc.getSelectedFile();

            setDefaultDir(file);

////            if (file.exists()) {
////                result = file;
////            } else {
////                FileNotFoundDialog.show(parent);
////            }
            result = file;

        } else {
            System.out.println(
                    ">> Operation \"load folder\" cancelled by user." + "\n");
        }

        if (VSysUtil.isMacOSX()) {
            VSwingUtil.forceNimbusLAF(null);
        }

//
//        fireAction(new ActionEvent(this, 0, FILE_OR_FOLDER_LOADED_ACTION));
        return result;
    }

    /**
     * Opens a save file/folder dialog and returns the selected folder.
     *
     * @param parent the parent component of the dialog
     * @param directory the current directory to use
     * @param restrict defines whether to restrict the dialog to the specified
     * @param saveType specifies whether to load files, folders both , see
     * {@link javax.swing.JFileChooser#DIRECTORIES_ONLY} etc. directory and its
     * subdirectories
     * @return the selected folder or null if no folder has been
     * selected
     */
    public File getSaveFileOrFolder(Component parent,
            File directory, boolean restrict, int saveType, FileFilter filter) {
        File result = null;

        directory = chooseDefaultDir(directory);

        final JFileChooser fc = createFileChooser(restrict, filter);

        if (restrict) {
            fc.setFileSystemView(new RestrictedFileSystemView(directory));
        }

        fc.setFileHidingEnabled(true);
        fc.setFileSelectionMode(saveType);
        fc.setCurrentDirectory(directory);
        int returnVal = fc.showSaveDialog(parent);

        if (returnVal == JFileChooser.APPROVE_OPTION) {
            File file = fc.getSelectedFile();

            setDefaultDir(file);

////            if (file.exists()) {
////                result = file;
////            } else {
////                FileNotFoundDialog.show(parent);
////            }
            result = file;

        } else {
            System.out.println(
                    ">> Operation \"load folder\" cancelled by user." + "\n");
        }

        if (VSysUtil.isMacOSX()) {
            VSwingUtil.forceNimbusLAF(null);
        }

//
//        fireAction(new ActionEvent(this, 0, FILE_OR_FOLDER_LOADED_ACTION));
        return result;
    }

    /**
     * Opens a save file dialog.
     *
     * @param parent the parent component of the dialog
     * @param o the object to save
     * @param fileSaver the file saver to use for saving
     * @param filter the file extension filter
     *
     */
    public void saveFile(Component parent, Object o,
            FileSaver fileSaver,
            FileFilter filter) {
        saveFile(parent, o, fileSaver, null, filter, false);
    }

    /**
     * Opens a save file dialog.
     *
     * @param parent the parent component of the dialog
     * @param o the object to save
     * @param fileSaver the file saver to use for saving
     * @param directory the current directory to use or null if the
     * current directory shall not be defined (if the file is no directoy the
     * parent directory is used)
     * @param restrict defines whether to restrict the dialog to the specified
     * directory and its subdirectories
     * @param filter the file extension filter
     *
     */
    public void saveFile(Component parent, Object o,
            FileSaver fileSaver, File directory,
            FileFilter filter, boolean restrict) {

        directory = chooseDefaultDir(directory);

        final JFileChooser fc = createFileChooser(restrict, filter);

        if (restrict) {
            fc.setFileSystemView(new RestrictedFileSystemView(directory));
        }

        fc.setFileHidingEnabled(true);

        if (directory != null && directory.exists()) {
            if (!directory.isDirectory()) {
                directory = directory.getParentFile();
            }
            if (directory != null && directory.canRead()) {
                fc.setCurrentDirectory(directory);
            }
        }

        int returnVal = fc.showSaveDialog(parent);

        if (returnVal == JFileChooser.APPROVE_OPTION) {
            File file = fc.getSelectedFile();

            setDefaultDir(file);

            String ext = getFileExtension(file);

            boolean writeFile = true;
            boolean writeOrOverwrite = true;

            ext = chooseDefaultOrCustomExtension(
                    parent, file, filter, fileSaver);

            // if we don't have a supported extension we add one
            if (!hasSupportedExtension(file, filter)) {
                file = addFileExtension(file, ext);
            }

            // if we still don't have a supported extension we
            // can't write the file
            if (!hasSupportedExtension(file, filter)) {
                writeFile = false;
            } else {
                writeOrOverwrite = writeEvenIfFileExists(parent, file);
            }

            // we write the file if we have a supported extension and if we
            // accept to overwrite the file if it already exists
            writeFile = writeOrOverwrite && writeFile;

            if (writeFile) {
                try {
                    System.out.println("Saving: " + file.getName() + "\n");
                    fileSaver.saveFile(o, file, ext);
                } catch (IOException ex) {
                    Logger.getLogger(this.getClass().
                            getName()).log(Level.SEVERE, null, ex);
//                    System.err.println( ex );
                    CantSaveFileDialog.show(parent);
                }
            }
        } else {
            System.out.println(
                    ">> Operation \"save file\" cancelled by user." + "\n");
        }

        if (VSysUtil.isMacOSX()) {
            VSwingUtil.forceNimbusLAF(null);
        }
    }

    /**
     * Opens a save file dialog and returns the selected file.
     *
     * @param parent the parent component of the dialog
     * @param directory the current directory to use
     * @param filter the file extension filter
     * @param restrict defines whether to restrict the dialog to the specified
     * directory and its subdirectories
     * @return the selected file or null if no file has been
     * selected
     */
    public File getSaveFile(Component parent,
            File directory, FileFilter filter, boolean restrict) {
        File result = null;

        directory = chooseDefaultDir(directory);

        final JFileChooser fc = createFileChooser(restrict, filter);

        if (restrict) {
            fc.setFileSystemView(new RestrictedFileSystemView(directory));
        }

        fc.setFileHidingEnabled(true);
        fc.setCurrentDirectory(directory);

        int returnVal = fc.showSaveDialog(parent);

        if (returnVal == JFileChooser.APPROVE_OPTION) {
            File file = fc.getSelectedFile();

            setDefaultDir(file);

//            if (file.exists()) {
//                result = file;
//            } else {
//                FileNotFoundDialog.show(parent);
//            }
            result = file;

        } else {
            System.out.println(
                    ">> Operation \"save file\" cancelled by user." + "\n");
        }

        if (VSysUtil.isMacOSX()) {
            VSwingUtil.forceNimbusLAF(null);
        }

        return result;
    }

    /**
     * Opens a save file dialog and returns the selected files.
     *
     * @param parent the parent component of the dialog
     * @param directory the current directory to use
     * @param filter the file extension filter
     * @param restrict defines whether to restrict the dialog to the specified
     * directory and its subdirectories
     * @return the selected files or null if no file has been
     * selected
     */
    public File[] getSaveFiles(Component parent,
            File directory, FileFilter filter, boolean restrict) {
        File[] result = null;

        directory = chooseDefaultDir(directory);

        final JFileChooser fc = createFileChooser(restrict, filter);

        if (restrict) {
            fc.setFileSystemView(new RestrictedFileSystemView(directory));
        }

        // allow multiple selections
        fc.setMultiSelectionEnabled(true);
        fc.setFileHidingEnabled(true);
        fc.setCurrentDirectory(directory);
        fc.setFileFilter(filter);
        int returnVal = fc.showSaveDialog(parent);

        if (returnVal == JFileChooser.APPROVE_OPTION) {
            File file[] = fc.getSelectedFiles();

            if (file.length > 0) {
                setDefaultDir(file[0]);
            }

//            if (file.exists()) {
//                result = file;
//            } else {
//                FileNotFoundDialog.show(parent);
//            }
            result = file;

        } else {
            System.out.println(
                    ">> Operation \"save file\" cancelled by user." + "\n");
        }

        if (VSysUtil.isMacOSX()) {
            VSwingUtil.forceNimbusLAF(null);
        }

        return result;
    }

    /**
     * Opens a save folder dialog and returns the selected folder.
     *
     * @param parent the parent component of the dialog
     * @param directory the current directory to use
     * @param restrict defines whether to restrict the dialog to the specified
     * directory and its subdirectories
     * @return the selected folder or null if no folder has been
     * selected
     */
    public File getSaveFolder(Component parent,
            File directory, boolean restrict) {
        File result = null;

        directory = chooseDefaultDir(directory);

        final JFileChooser fc = createFileChooser(restrict, null);

        if (restrict) {
            fc.setFileSystemView(new RestrictedFileSystemView(directory));
        }

        fc.setFileHidingEnabled(true);
        fc.setCurrentDirectory(directory);
        fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
        int returnVal = fc.showSaveDialog(parent);

        if (returnVal == JFileChooser.APPROVE_OPTION) {
            File file = fc.getSelectedFile();

            setDefaultDir(file);

//            if (file.exists()) {
//                result = file;
//            } else {
//                FileNotFoundDialog.show(parent);
//            }
            result = file;

        } else {
            System.out.println("Save dialog cancelled by user." + "\n");
        }

        if (VSysUtil.isMacOSX()) {
            VSwingUtil.forceNimbusLAF(null);
        }

        return result;
    }

    /**
     * Opens a save file dialog and returns the selected file.
     *
     * @param parent the parent component of the dialog
     * @param filter the file extension filter
     * @return the selected file or null if no file has been
     * selected
     */
    public File getSaveFile(Component parent, FileFilter filter) {
        return getSaveFile(parent, null, filter, false);
    }

    /**
     * Returns the extension of the given file.
     *
     * @param file the file
     * @return the extension of the file
     */
    private String getFileExtension(File file) {
        String ext = "";
        String s = file.getName();
        int i = s.lastIndexOf('.');

        if (i > 0 && i < s.length() - 1) {
            ext = s.substring(i + 1).toLowerCase();
        }

        return ext;
    }

    /**
     * Indicates whether the given file has an extension.
     *
     * @param file the file to check
     * @return true if the file has an extension;
     * false ozherwise
     */
    private boolean hasExtension(File file) {
        return getFileExtension(file).length() > 0;
    }

    /**
     * Indicates whether the given file has a supported file extension, i.e., if
     * the file filter contains the extension.
     *
     * @param file the file to check
     * @param filter the file extension filter
     * @return true if the extension is supported;
     * false ozherwise
     */
    private boolean hasSupportedExtension(File file, FileFilter filter) {
        return filter.accept(file) && !file.isDirectory();
    }

    /**
     * Returns either the default extension that is defined for the file or a
     * manually chosen one, depending on whether the manually extension is given
     * or supported. If the manually chosen extension is not supported a dialog
     * will open and ask if the default extension shall be used.
     *
     * @param parent the parent component of the dialog
     * @param file the file
     * @param filter the file extension filter
     * @param fileSaver the file saver
     * @return the chosen file extension
     */
    private String chooseDefaultOrCustomExtension(Component parent,
            File file, FileFilter filter,
            FileSaver fileSaver) {

        String ext = getFileExtension(file);

        String defaultExt = fileSaver.getDefaultExtension();

        if (!hasExtension(file)) {
            ext = defaultExt;
            file = addFileExtension(file, ext);
        }

        if (!filter.accept(file)) {
            if (UseDefaultFormatDialog.show(parent, defaultExt, ext)) {
//                String filePath = file.getPath();
                ext = defaultExt;
            } else {
                ext = "";
            }
        }

        return ext;
    }

    /**
     * Adds a file extension to the file name.
     *
     * @param file the file that is to be extended
     * @param ext the extension
     * @return the file with extension
     */
    private File addFileExtension(File file, String ext) {
        return new File(file.getPath() + "." + ext);
    }

    /**
     * Opens the dialog and asks if already exting files shall be overwritten.
     *
     * @param parent the parent of the dialog
     * @param file the file to save
     * @return true if the file shall be overwritten;
     * false otherwise
     */
    private boolean writeEvenIfFileExists(Component parent, File file) {
        boolean result = true;

        if (file.isFile()) {
            // shall i overwrite the file
            result = OverwriteFileDialog.show(parent);
        }
        return result;
    }

    @SuppressWarnings("unchecked") // we must be compatible with 1.6
    public static void restrictNavigation(JFileChooser fc, File dir) {

        // delete combobox items
        for (Component c : VSwingUtil.getAllChildren(fc, JComboBox.class)) {
            JComboBox box = (JComboBox) c;

            if (box.getItemCount() == 0) {
                break;
            }

            if (box.getItemAt(0) instanceof File) {
                box.setModel(new DefaultComboBoxModel(new Object[]{dir}));
            }
        }

        // delete combobox items
        for (Component c : VSwingUtil.getAllChildren(fc, JTextField.class)) {
            JTextField tf = (JTextField) c;

            tf.setEditable(false);
        }
    }
//    /**
//     * @return the latestFileOrFolder
//     */
//    public File getLatestFileOrFolder() {
//        return latestFileOrFolder;
//    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy