1 /*
   2  * $Id$
   3  *
   4  * Copyright (c) 2002, 2009, Oracle and/or its affiliates. All rights reserved.
   5  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   6  *
   7  * This code is free software; you can redistribute it and/or modify it
   8  * under the terms of the GNU General Public License version 2 only, as
   9  * published by the Free Software Foundation.  Oracle designates this
  10  * particular file as subject to the "Classpath" exception as provided
  11  * by Oracle in the LICENSE file that accompanied this code.
  12  *
  13  * This code is distributed in the hope that it will be useful, but WITHOUT
  14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  15  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  16  * version 2 for more details (a copy is included in the LICENSE file that
  17  * accompanied this code).
  18  *
  19  * You should have received a copy of the GNU General Public License version
  20  * 2 along with this work; if not, write to the Free Software Foundation,
  21  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  22  *
  23  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  24  * or visit www.oracle.com if you need additional information or have any
  25  * questions.
  26  */
  27 package com.sun.javatest.mrep;
  28 
  29 import java.awt.Component;
  30 import java.io.File;
  31 
  32 import javax.swing.Icon;
  33 import javax.swing.JFileChooser;
  34 import javax.swing.filechooser.FileFilter;
  35 import javax.swing.filechooser.FileView;
  36 
  37 import com.sun.javatest.report.Report;
  38 import com.sun.javatest.tool.IconFactory;
  39 import com.sun.javatest.tool.UIFactory;
  40 import com.sun.javatest.util.FileInfoCache;
  41 
  42 /**
  43  * A custom JFileChooser, for a user to choose a report directory.
  44  */
  45 //
  46 // see also com.sun.javatest.tool.WorkDirChooser
  47 //
  48 class ReportDirChooser extends JFileChooser
  49 {
  50     // the following is a workaround to force the Report class to
  51     // be safely loaded, before the JFileChooser starts running its background
  52     // thread, exposing a JVM bug in class loading.
  53     static {
  54         Class<?> reportClass = Report.class;
  55     }
  56 
  57     /**
  58      * Create a ReportDirChooser, initially showing the user's current directory.
  59      */
  60     public ReportDirChooser() {
  61         this(new File(System.getProperty("user.dir")));
  62     }
  63 
  64     /**
  65      * Create a ReportDirChooser, initially showing a given directory.
  66      * @param initialDir the initial directory to be shown
  67      */
  68     public ReportDirChooser(File initialDir) {
  69         super(initialDir);
  70         uif = new UIFactory(this, null); // no helpBroker required
  71 
  72         icon = IconFactory.getSelectableFolderIcon();
  73 
  74         // we want a filter that only selects directories
  75         setAcceptAllFileFilterUsed(false);
  76         setFileFilter(new RDC_FileFilter());
  77 
  78         // we want a file view that displays special icons for
  79         // report directories and sets directories non-traversable,
  80         // so that double clicking in the list view will select
  81         // (ie choose) them
  82         setFileView(new RDC_FileView());
  83 
  84         // we need FILES in the mode so that non-traversable
  85         // items appear in the list
  86         // -- see BasicDirectoryModel.LoadFilesThread
  87         // we need DIRECTORIES in the mode so that directories
  88         // (eg report directories) can be selected (ie chosen)
  89         // -- see BasicFileChooserUI.ApproveSelectionAction
  90         setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
  91 
  92         // these parameters are still not ideal ...
  93         // approveSelection(File) below gets called for all directories:
  94         // we still have to redispatch according to whether it is a report
  95         // directory or not.
  96     }
  97 
  98     /**
  99      * A constant to indicate that a new report directory is to be created.
 100      * @see #setMode
 101      */
 102     public static final int NEW = 0;
 103 
 104     /**
 105      * A constant to indicate that an existing report directory is to be opened.
 106      * @see #setMode
 107      */
 108     public static final int OPEN = 1;
 109 
 110     /**
 111      * Set whether the chooser is to be used to create a new report directory
 112      * or to open an existing report directory.
 113      * @param mode a constant to indicate how the chooser is to operate
 114      * @see #NEW
 115      * @see #OPEN
 116      */
 117     public void setMode(int mode) {
 118         this.mode = mode;
 119         switch (mode) {
 120         case NEW:
 121             setApproveButtonText(uif.getI18NString("rdc.new.btn"));
 122             setApproveButtonToolTipText(uif.getI18NString("rdc.new.tip"));
 123             setDialogTitle(uif.getI18NString("rdc.new.title"));
 124             break;
 125         case OPEN:
 126             setApproveButtonText(uif.getI18NString("rdc.open.btn"));
 127             setApproveButtonToolTipText(uif.getI18NString("rdc.open.tip"));
 128             setDialogTitle(uif.getI18NString("rdc.open.title"));
 129             break;
 130         default:
 131             throw new IllegalStateException();
 132         }
 133     }
 134 
 135     /**
 136      * Get the report directory that was most recently selected by the user.
 137      * @return the report directory that was most recently selected by the user
 138      * @see #showDialog
 139      */
 140     public File getSelectedReportDirectory() {
 141         return reportDir;
 142     }
 143 
 144     /**
 145      * Show a dialog to allow the user to select a report directory.
 146      * If a report directory is selected, it can be accessed via getSelectedReportDirectory.
 147      * @param parent the component to be used at the parent of this dialog
 148      * @return an integer signifying how the dialog was dismissed
 149      * (APPROVE_OPTION or CANCEL_OPTION).
 150      * @see #APPROVE_OPTION
 151      * @see #CANCEL_OPTION
 152      * @see #getSelectedReportDirectory
 153      */
 154     public int showDialog(Component parent) {
 155         return showDialog(parent, getApproveButtonText());
 156     }
 157 
 158     public void approveSelection() {
 159         // the validity of the selection depends on whether the
 160         // selected directory is to be created or opened.
 161         File file = getSelectedFile();
 162         if (mode == NEW)
 163             approveNewSelection(file);
 164         else
 165             approveOpenSelection(file);
 166     }
 167 
 168     //-------------------------------------------------------------------------
 169 
 170     private void approveNewSelection(File file) {
 171         if (file.exists()) {
 172             if (isReportDirectory(file) || isEmptyDirectory(file)) {
 173                 // create new report in existing or empty report directory
 174                 reportDir = file;
 175                 super.approveSelection();
 176             }
 177             else if (file.isDirectory()) {
 178                 // the directory exists, but is neither a report dir nor empty: open it
 179                 setCurrentDirectory(file);
 180                 setSelectedFile(null);
 181                 setSelectedFiles(null);
 182             }
 183             else {
 184                 uif.showError("rdc.notADir", file);
 185             }
 186         }
 187         else {
 188             // create new report in new report directory
 189             reportDir = file;
 190             super.approveSelection();
 191         }
 192     }
 193 
 194     //-------------------------------------------------------------------------
 195 
 196     private void approveOpenSelection(File file) {
 197         if (file.exists()) {
 198             if (isReportDirectory(file)) {
 199                 reportDir = file;
 200                 super.approveSelection();
 201             }
 202             else if (isDirectory(file)) {
 203                 // the directory exists, but is not a report dir: open it
 204                 setCurrentDirectory(file);
 205                 setSelectedFile(null);
 206                 setSelectedFiles(null);
 207             }
 208             else
 209                 uif.showError("rdc.notADir", file);
 210         }
 211         else
 212             uif.showError("rdc.cantOpen", file);
 213     }
 214 
 215     //-------------------------------------------------------------------------
 216 
 217     private boolean isDirectory(File f) {
 218         return (f.isDirectory());
 219     }
 220 
 221     private boolean isReportDirectory(File f) {
 222         if (isIgnoreable(f))
 223             return false;
 224 
 225         Boolean b =  cache.get(f);
 226         if (b == null) {
 227             boolean v = Report.isReportDirectory(f);
 228             String[] l = f.list();
 229             if (l != null) {
 230                 for (int i = 0; i < l.length; i++) {
 231                     if (l[i].endsWith(".html")) {
 232                         v = true;
 233                     }
 234                 }
 235             }
 236             cache.put(f, v);
 237             return v;
 238         }
 239         else
 240             return b;
 241     }
 242 
 243     private boolean isEmptyDirectory(File f) {
 244         return (f.isDirectory() && f.list().length == 0);
 245     }
 246 
 247     private boolean isIgnoreable(File f) {
 248         // Take care not touch the floppy disk drive on Windows
 249         // because if there is no disk in it, the user will get a dialog.
 250         // Root directories (such as A:) have an empty name,
 251         // so use that to avoid touching the file itself.
 252         // This means we can't put a work directory in the root of
 253         // the file system, but that is a lesser inconvenience
 254         // than those floppy dialogs!
 255         return (f.getName().equals(""));
 256     }
 257 
 258     private FileInfoCache cache = new FileInfoCache();
 259 
 260     private int mode;
 261     private File reportDir;
 262     private UIFactory uif;
 263     private Icon icon;
 264 
 265     private class RDC_FileView extends FileView {
 266         public String getDescription(File f) {
 267             return null;
 268         }
 269 
 270         public Icon getIcon(File f) {
 271             return (isReportDirectory(f) ? icon : null);
 272         }
 273 
 274         public String getName(File f) {
 275             // Take care to get names of file system roots correct
 276             String name = f.getName();
 277             return (name.length() == 0 ? f.getPath() : name);
 278         }
 279 
 280         public String getTypeDescription(File f) {
 281             return null;
 282         }
 283 
 284         public Boolean isTraversable(File f) {
 285             return (isDirectory(f) && !isReportDirectory(f) ? Boolean.TRUE : Boolean.FALSE);
 286         }
 287     }
 288 
 289     private class RDC_FileFilter extends FileFilter {
 290         public boolean accept(File f) {
 291             return (isDirectory(f));
 292         }
 293 
 294         public String getDescription() {
 295             return uif.getI18NString("rdc.ft");
 296         }
 297     }
 298 }