/* (c) 2014, 2016 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2013 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.web.wicket.browser; import java.io.File; import java.io.FileFilter; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; import org.apache.wicket.extensions.markup.html.repeater.util.SortParam; import org.apache.wicket.extensions.markup.html.repeater.util.SortableDataProvider; import org.apache.wicket.model.IModel; import org.apache.wicket.model.Model; public class FileProvider extends SortableDataProvider<File, String> { private static final long serialVersionUID = 2387540012977156321L; public static final String NAME = "name"; public static final String LAST_MODIFIED = "lastModified"; public static final String SIZE = "size"; /** * Compares the file names, makes sure directories are listed first */ private static final Comparator<File> FILE_NAME_COMPARATOR = new AbstractFileComparator() { @Override public int compareProperty(File o1, File o2) { // otherwise compare the name return o1.getName().compareToIgnoreCase(o2.getName()); } }; /** * Compares last modified time */ private static final Comparator<File> FILE_LM_COMPARATOR = new AbstractFileComparator() { @Override public int compareProperty(File o1, File o2) { long lm1 = o1.lastModified(); long lm2 = o2.lastModified(); if (lm1 == lm2) { return 0; } else { return lm1 < lm2 ? -1 : 1; } } }; /** * Compares file size */ private static final Comparator<File> FILE_SIZE_COMPARATOR = new AbstractFileComparator() { @Override public int compareProperty(File o1, File o2) { long l1 = o1.length(); long l2 = o2.length(); if (l1 == l2) { return 0; } else { return l1 < l2 ? -1 : 1; } } }; /** * The current directory */ IModel<File> directory; /** * An eventual file filter */ IModel<? extends FileFilter> fileFilter; public FileProvider(File directory) { this.directory = new Model<File>(directory); } public FileProvider(IModel<File> directory) { this.directory = directory; } @Override public Iterator<File> iterator(long first, long count) { List<File> files = getFilteredFiles(); // sorting Comparator<File> comparator = getComparator(getSort()); if (comparator != null) Collections.sort(files, comparator); // paging long last = first + count; if (last > files.size()) { last = files.size(); } return files.subList((int)first, (int)last).iterator(); } List<File> getFilteredFiles() { // grab the current directory File d = (File) directory.getObject(); if (d.isFile()) d = d.getParentFile(); // return a filtered view of the contents File[] files; if (fileFilter != null) files = d.listFiles(new HiddenFileFilter((FileFilter) fileFilter.getObject())); else files = d.listFiles(new HiddenFileFilter()); if(files != null) return Arrays.asList(files); else return Collections.emptyList(); } @Override public IModel<File> model(File object) { return new Model<File>(object); } @Override public long size() { return getFilteredFiles().size(); } private Comparator<File> getComparator(SortParam<String> sort) { if (sort == null) return FILE_NAME_COMPARATOR; // build base comparator Comparator<File> comparator = null; if (NAME.equals(sort.getProperty())) { comparator = FILE_NAME_COMPARATOR; } else if (LAST_MODIFIED.equals(sort.getProperty())) { comparator = FILE_LM_COMPARATOR; } else if (SIZE.equals(sort.getProperty())) { comparator = FILE_SIZE_COMPARATOR; } else { throw new IllegalArgumentException("Uknown sorting property " + sort.getProperty()); } // reverse comparison direction if needed if (sort.isAscending()) return comparator; else return new ReverseComparator(comparator); } public IModel<File> getDirectory() { return directory; } public void setDirectory(IModel<File> directory) { this.directory = directory; } public IModel<? extends FileFilter> getFileFilter() { return fileFilter; } public void setFileFilter(IModel<? extends FileFilter> fileFilter) { this.fileFilter = fileFilter; } /** * A base file comparator: makes sure directories go first, files later. The * subclass is used to perform comparison when both are files, or both * directories */ private static abstract class AbstractFileComparator implements Comparator<File> { @Override public final int compare(File o1, File o2) { // directories first if (o1.isDirectory()) if (!o2.isDirectory()) return -1; if (o2.isDirectory()) if (!o1.isDirectory()) return 1; return compareProperty(o1, o2); } protected abstract int compareProperty(File f1, File f2); } /** * A simple comparator inverter */ private static class ReverseComparator implements Comparator<File> { Comparator<File> comparator; public ReverseComparator(Comparator<File> comparator) { this.comparator = comparator; } @Override public int compare(File o1, File o2) { return comparator.compare(o2, o1); } } private static class HiddenFileFilter implements FileFilter { FileFilter delegate; public HiddenFileFilter() { // no delegate, just skip the hidden ones } public HiddenFileFilter(FileFilter delegate) { this.delegate = delegate; } @Override public boolean accept(File pathname) { if(pathname.isHidden()) { return false; } if(delegate != null) { return delegate.accept(pathname); } else { return true; } } } }