package org.geoserver.web.wicket.browser; import java.io.File; import java.io.FileFilter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import javax.swing.filechooser.FileSystemView; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior; import org.apache.wicket.markup.html.form.DropDownChoice; import org.apache.wicket.markup.html.form.IChoiceRenderer; import org.apache.wicket.markup.html.panel.Panel; import org.apache.wicket.model.IModel; import org.apache.wicket.model.Model; import org.apache.wicket.model.util.SetModel; import org.geoserver.web.wicket.ParamResourceModel; import org.vfny.geoserver.global.GeoserverDataDirectory; @SuppressWarnings("serial") public class GeoServerFileChooser extends Panel { static File USER_HOME = null; static { // try to safely determine the user home location try { File hf = null; String home = System.getProperty("user.home"); if(home != null) { hf = new File(home); } if(hf != null && hf.exists()) { USER_HOME = hf; } } catch(Throwable t) { // that's ok, we might not be able to get the user home } } FileBreadcrumbs breadcrumbs; FileDataView fileTable; IModel file; public GeoServerFileChooser(String id, IModel file) { super(id, file); this.file = file; // build the roots ArrayList<File> roots = new ArrayList<File>(Arrays.asList(File.listRoots())); Collections.sort(roots); // TODO: find a better way to deal with the data dir File dataDirectory = GeoserverDataDirectory.getGeoserverDataDirectory(); roots.add(0, dataDirectory); // add the home directory as well if it was possible to determine it at all if(USER_HOME != null) { roots.add(1, USER_HOME); } // find under which root the selection should be placed File selection = (File) file.getObject(); // first check if the file is a relative reference into the data dir if(selection != null) { File relativeToDataDir = GeoserverDataDirectory.findDataFile(selection.getPath()); if(relativeToDataDir != null) { selection = relativeToDataDir; } } // select the proper root File selectionRoot = null; if(selection != null && selection.exists()) { for (File root : roots) { if(isSubfile(root, selection.getAbsoluteFile())) { selectionRoot = root; break; } } // if the file is not part of the known search paths, give up // and switch back to the data directory if(selectionRoot == null) { selectionRoot = dataDirectory; file = new Model(selectionRoot); } else { if(!selection.isDirectory()) { file = new Model(selection.getParentFile()); } else { file = new Model(selection); } } } else { selectionRoot = dataDirectory; file = new Model(selectionRoot); } this.file = file; setDefaultModel(file); // the root chooser final DropDownChoice choice = new DropDownChoice("roots", new Model(selectionRoot), new Model(roots), new FileRootsRenderer()); choice.add(new AjaxFormComponentUpdatingBehavior("onchange") { @Override protected void onUpdate(AjaxRequestTarget target) { File selection = (File) choice.getModelObject(); breadcrumbs.setRootFile(selection); updateFileBrowser(selection, target); } }); choice.setOutputMarkupId(true); add(choice); // the breadcrumbs breadcrumbs = new FileBreadcrumbs("breadcrumbs", new Model(selectionRoot), file) { @Override protected void pathItemClicked(File file, AjaxRequestTarget target) { updateFileBrowser(file, target); } }; breadcrumbs.setOutputMarkupId(true); add(breadcrumbs); // the file tables fileTable = new FileDataView("fileTable", new FileProvider(file)) { @Override protected void linkNameClicked(File file, AjaxRequestTarget target) { updateFileBrowser(file, target); } }; fileTable.setOutputMarkupId(true); add(fileTable); } void updateFileBrowser(File file, AjaxRequestTarget target) { if(file.isDirectory()) { directoryClicked(file, target); } else if(file.isFile()) { fileClicked(file, target); } } /** * Called when a file name is clicked. By default it does nothing */ protected void fileClicked(File file, AjaxRequestTarget target) { // do nothing, subclasses will override } /** * Action undertaken as a directory is clicked. Default behavior is to drill down into * the directory. * @param file * @param target */ protected void directoryClicked(File file, AjaxRequestTarget target) { // explicitly change the root model, inform the other components the model has changed GeoServerFileChooser.this.file.setObject(file); fileTable.getProvider().setDirectory(new Model(file)); breadcrumbs.setSelection(file); target.addComponent(fileTable); target.addComponent(breadcrumbs); } private boolean isSubfile(File root, File selection) { if(selection == null || "".equals(selection.getPath())) return false; if(selection.equals(root)) return true; return isSubfile(root, selection.getParentFile()); } /** * * @param fileFilter */ public void setFilter(IModel<? extends FileFilter> fileFilter) { fileTable.provider.setFileFilter(fileFilter); } /** * Set the file table fixed height. Set it to null if you don't want fixed height * with overflow, and to a valid CSS measure if you want it instead. * Default value is "25em" * @param height */ public void setFileTableHeight(String height) { fileTable.setTableHeight(height); } // /** // * If the file is in the data directory builds a data dir relative path, otherwise // * returns an absolute path // * @param file // * @return // */ // public String getRelativePath(File file) { // File dataDirectory = GeoserverDataDirectory.getGeoserverDataDirectory(); // if(isSubfile(dataDirectory, file)) { // File curr = file; // String path = null; // // paranoid check to avoid infinite loops // while(curr != null && !curr.equals(dataDirectory)){ // if(path == null) { // path = curr.getName(); // } else { // path = curr.getName() + "/" + path; // } // curr = curr.getParentFile(); // } // return "file:" + path; // } // // return "file://" + file.getAbsolutePath(); // } // class FileRootsRenderer implements IChoiceRenderer { public Object getDisplayValue(Object o) { File f = (File) o; if(f == USER_HOME) { return new ParamResourceModel("userHome", GeoServerFileChooser.this).getString(); } else if(f.equals(GeoserverDataDirectory.getGeoserverDataDirectory())) { return new ParamResourceModel("dataDirectory", GeoServerFileChooser.this).getString(); } try { return FileSystemView.getFileSystemView().getSystemDisplayName(f); } catch(Exception e) { // on windows we can get the occasional NPE due to // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6973685 return f.getName(); } } public String getIdValue(Object o, int count) { File f = (File) o; return "" + count; } } }