001/*
002 * IzPack - Copyright 2001-2005 Julien Ponge, All Rights Reserved.
003 * 
004 * http://www.izforge.com/izpack/
005 * http://developer.berlios.de/projects/izpack/
006 * 
007 * Copyright 2004 Klaus Bartz
008 * 
009 * Licensed under the Apache License, Version 2.0 (the "License");
010 * you may not use this file except in compliance with the License.
011 * You may obtain a copy of the License at
012 * 
013 *     http://www.apache.org/licenses/LICENSE-2.0
014 *     
015 * Unless required by applicable law or agreed to in writing, software
016 * distributed under the License is distributed on an "AS IS" BASIS,
017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018 * See the License for the specific language governing permissions and
019 * limitations under the License.
020 */
021
022package com.izforge.izpack.panels;
023
024import java.awt.GridBagConstraints;
025import java.awt.Insets;
026import java.awt.event.ActionEvent;
027import java.awt.event.ActionListener;
028import java.io.BufferedReader;
029import java.io.File;
030import java.io.IOException;
031import java.io.InputStream;
032import java.io.InputStreamReader;
033
034import javax.swing.JLabel;
035
036import com.izforge.izpack.installer.InstallData;
037import com.izforge.izpack.installer.InstallerFrame;
038import com.izforge.izpack.installer.IzPanel;
039import com.izforge.izpack.installer.ResourceNotFoundException;
040import com.izforge.izpack.util.AbstractUIHandler;
041import com.izforge.izpack.util.Debug;
042import com.izforge.izpack.util.IoHelper;
043import com.izforge.izpack.util.MultiLineLabel;
044import com.izforge.izpack.util.OsVersion;
045import com.izforge.izpack.util.VariableSubstitutor;
046
047/**
048 * Base class for panels which asks for paths.
049 * 
050 * @author Klaus Bartz
051 * 
052 */
053public class PathInputPanel extends IzPanel implements ActionListener
054{
055
056    /**
057     * 
058     */
059    private static final long serialVersionUID = 3257566217698292531L;
060
061    /** Flag whether the choosen path must exist or not */
062    protected boolean mustExist = false;
063
064    /** Files which should be exist */
065    protected String[] existFiles = null;
066
067    /** The path which was chosen */
068    // protected String chosenPath;
069    /** The path selection sub panel */
070    protected PathSelectionPanel pathSelectionPanel;
071
072    protected String emptyTargetMsg;
073
074    protected String warnMsg;
075
076    protected static String defaultInstallDir = null;
077
078    /**
079     * The constructor.
080     * 
081     * @param parent The parent window.
082     * @param idata The installation data.
083     */
084    public PathInputPanel(InstallerFrame parent, InstallData idata)
085    {
086        super(parent, idata);
087        // Set default values
088        emptyTargetMsg = getI18nStringForClass("empty_target", "TargetPanel");
089        warnMsg = getI18nStringForClass("warn", "TargetPanel");
090        // if( this.class.)
091
092        // Customize the default GridBagConstraints.
093        GridBagConstraints gbConstraint = getDefaultGridBagConstraints();
094        gbConstraint.gridwidth = GridBagConstraints.REMAINDER;
095        this.setDefaultGridBagConstraints(gbConstraint);
096        String introText = getI18nStringForClass("intro", "PathInputPanel");
097        if (introText == null || introText.startsWith("PathInputPanel.intro")) introText = "";
098        // Intro
099        // Create and customize constraint for it.
100        // row 0 column 0
101        gbConstraint = getNextYGridBagConstraints();
102        // Create component and add it to this panel.
103        MultiLineLabel introLabel = createMultiLineLabel(introText);
104        add(introLabel, gbConstraint);
105        // Label for input
106        // Create and customize constraint for it.
107        // row 1 column 0; is the next Y
108        gbConstraint = getNextYGridBagConstraints();
109        gbConstraint.gridwidth = GridBagConstraints.RELATIVE;
110        gbConstraint.insets = new Insets(0, 0, 10, 0);
111        // Create component and add it to this panel.
112        JLabel infoLabel = createLabel("info", "TargetPanel", "open",
113                JLabel.LEFT);
114        add(infoLabel, gbConstraint);
115        // Create path selection components and add they to this panel.
116        pathSelectionPanel = new PathSelectionPanel(this, idata);
117        gbConstraint = getNextYGridBagConstraints();
118        gbConstraint.gridwidth = GridBagConstraints.REMAINDER;
119        gbConstraint.fill = GridBagConstraints.HORIZONTAL;
120        gbConstraint.insets = new Insets(0, 0, 0, 0);
121        add(pathSelectionPanel, gbConstraint);
122        createLayoutBottom();
123        // Place a footer as last component, if
124        completeGridBagLayout();
125    }
126
127    /**
128     * This method does nothing. It is called from ctor of PathInputPanel, to give in a derived
129     * class the possibility to add more components under the path input components.
130     */
131    public void createLayoutBottom()
132    {
133    }
134
135    /**
136     * Actions-handling method.
137     * 
138     * @param e The event.
139     */
140    public void actionPerformed(ActionEvent e)
141    {
142        Object source = e.getSource();
143        if (source == pathSelectionPanel.getPathInputField())
144        {
145            parent.navigateNext();
146        }
147
148    }
149
150    /**
151     * Indicates wether the panel has been validated or not.
152     * 
153     * @return Wether the panel has been validated or not.
154     */
155    public boolean isValidated()
156    {
157        String chosenPath = pathSelectionPanel.getPath();
158        boolean ok = true;
159
160        // We put a warning if the specified target is nameless
161        if (chosenPath.length() == 0)
162        {
163            if (isMustExist())
164            {
165                emitError(parent.langpack.getString("installer.error"), parent.langpack
166                        .getString("PathInputPanel.required"));
167                return false;
168            }
169            else
170            {
171                ok = emitWarning(parent.langpack.getString("installer.warning"), emptyTargetMsg);
172            }
173        }
174        if (!ok) return ok;
175
176        // Normalize the path
177        File path = new File(chosenPath).getAbsoluteFile();
178        chosenPath = path.toString();
179        pathSelectionPanel.setPath(chosenPath);
180        if (isMustExist())
181        {
182            if (!path.exists())
183            {
184                emitError(parent.langpack.getString("installer.error"), parent.langpack
185                        .getString(getI18nStringForClass("required", "PathInputPanel")));
186                return false;
187            }
188            if (!pathIsValid())
189            {
190                emitError(parent.langpack.getString("installer.error"), parent.langpack
191                        .getString(getI18nStringForClass("notValid", "PathInputPanel")));
192                return false;
193            }
194        }
195        else
196        {
197            // We assume, that we would install something into this dir
198            if (!isWriteable())
199            {
200                emitError(parent.langpack.getString("installer.error"), getI18nStringForClass(
201                        "notwritable", "TargetPanel"));
202                return false;
203            }
204            // We put a warning if the directory exists else we warn
205            // that it will be created
206            if (path.exists())
207            {
208                int res = askQuestion(parent.langpack.getString("installer.warning"), warnMsg,
209                        AbstractUIHandler.CHOICES_YES_NO, AbstractUIHandler.ANSWER_YES);
210                ok = res == AbstractUIHandler.ANSWER_YES;
211            }
212            else
213                this.emitNotification(getI18nStringForClass("createdir", "TargetPanel") + "\n"
214                        + chosenPath);
215        }
216        return ok;
217    }
218
219    /**
220     * Returns whether the chosen path is true or not. If existFiles are not null, the existence of
221     * it under the choosen path are detected. This method can be also implemented in derived
222     * classes to handle special verification of the path.
223     * 
224     * @return true if existFiles are exist or not defined, else false
225     */
226    protected boolean pathIsValid()
227    {
228        if (existFiles == null) return true;
229        for (int i = 0; i < existFiles.length; ++i)
230        {
231            File path = new File(pathSelectionPanel.getPath(), existFiles[i]).getAbsoluteFile();
232            if (!path.exists()) return false;
233        }
234        return true;
235    }
236
237    /**
238     * Returns the must exist state.
239     * 
240     * @return the must exist state
241     */
242    public boolean isMustExist()
243    {
244        return mustExist;
245    }
246
247    /**
248     * Sets the must exist state. If it is true, the path must exist.
249     * 
250     * @param b must exist state
251     */
252    public void setMustExist(boolean b)
253    {
254        mustExist = b;
255    }
256
257    /**
258     * Returns the array of strings which are described the files which must exist.
259     * 
260     * @return paths of files which must exist
261     */
262    public String[] getExistFiles()
263    {
264        return existFiles;
265    }
266
267    /**
268     * Sets the paths of files which must exist under the chosen path.
269     * 
270     * @param strings paths of files which must exist under the chosen path
271     */
272    public void setExistFiles(String[] strings)
273    {
274        existFiles = strings;
275    }
276
277    /**
278     * Loads up the "dir" resource associated with TargetPanel. Acceptable dir resource names:
279     * <code>
280     *   TargetPanel.dir.macosx
281     *   TargetPanel.dir.mac
282     *   TargetPanel.dir.windows
283     *   TargetPanel.dir.unix
284     *   TargetPanel.dir.xxx,
285     *     where xxx is the lower case version of System.getProperty("os.name"),
286     *     with any spaces replace with underscores
287     *   TargetPanel.dir (generic that will be applied if none of above is found)
288     *   </code>
289     * As with all IzPack resources, each the above ids should be associated with a separate
290     * filename, which is set in the install.xml file at compile time.
291     */
292    public static void loadDefaultInstallDir(InstallerFrame parentFrame,
293            InstallData idata)
294    {
295        // Load only once ...
296        if (getDefaultInstallDir() != null) return;
297        BufferedReader br = null;
298        try
299        {
300            InputStream in = null;
301
302            if (OsVersion.IS_WINDOWS)
303                in = parentFrame.getResource("TargetPanel.dir.windows");
304
305            else if (OsVersion.IS_OSX)
306                in = parentFrame.getResource("TargetPanel.dir.macosx");
307            else
308            {
309                String os = System.getProperty("os.name");
310                // first try to look up by specific os name
311                os = os.replace(' ', '_'); // avoid spaces in file names
312                os = os.toLowerCase(); // for consistency among TargetPanel res
313                // files
314                try
315                {
316                    in = parentFrame.getResource("TargetPanel.dir.".concat(os));
317                }
318                catch (ResourceNotFoundException rnfe)
319                {}
320                // if not specific os, try getting generic 'unix' resource file
321                if (in == null) in = parentFrame.getResource("TargetPanel.dir.unix");
322
323                // if all those failed, try to look up a generic dir file
324                if (in == null)
325                {
326                    try
327                    {
328                        in = parentFrame.getResource("TargetPanel.dir.unix");
329                    }
330                    catch (ResourceNotFoundException eee)
331                    {}
332                }
333
334            }
335
336            // if all above tests failed, there is no resource file,
337            // so use system default
338            if (in == null)
339            {
340                try
341                {
342                    in = parentFrame.getResource("TargetPanel.dir");
343                }
344                catch (ResourceNotFoundException eee)
345                {}
346            }
347
348            // now read the file, once we've identified which one to read
349            InputStreamReader isr = new InputStreamReader(in);
350            br = new BufferedReader(isr);
351            String line;
352            while ((line = br.readLine()) != null)
353            {
354                line = line.trim();
355                // use the first non-blank line
356                if (!line.equals("")) break;
357            }
358            defaultInstallDir = line;
359            VariableSubstitutor vs = new VariableSubstitutor(idata.getVariables());
360            defaultInstallDir = vs.substitute(defaultInstallDir, null);
361        }
362        catch (Exception e)
363        {
364            defaultInstallDir = null;
365            // leave unset to take the system default set by Installer class
366        }
367        finally
368        {
369            try
370            {
371                if (br != null) br.close();
372            }
373            catch (IOException ignored)
374            {}
375        }
376    }
377
378    /**
379     * This method determines whether the chosen dir is writeable or not.
380     * 
381     * @return whether the chosen dir is writeable or not
382     */
383    public boolean isWriteable()
384    {
385        File existParent = IoHelper.existingParent(new File(pathSelectionPanel.getPath()));
386        if (existParent == null) return false;
387        // On windows we cannot use canWrite because
388        // it looks to the dos flags which are not valid
389        // on NT or 2k XP or ...
390        if (OsVersion.IS_WINDOWS)
391        {
392            File tmpFile;
393            try
394            {
395                tmpFile = File.createTempFile("izWrTe", ".tmp", existParent);
396                tmpFile.deleteOnExit();
397            }
398            catch (IOException e)
399            {
400                Debug.trace(e.toString());
401                return false;
402            }
403            return true;
404        }
405        else
406            return existParent.canWrite();
407    }
408
409    /**
410     * Returns the default for the installation directory.
411     * 
412     * @return the default for the installation directory
413     */
414    public static String getDefaultInstallDir()
415    {
416        return defaultInstallDir;
417    }
418
419    /**
420     * Sets the default for the installation directory to the given string.
421     * 
422     * @param string path for default for the installation directory
423     */
424    public static void setDefaultInstallDir(String string)
425    {
426        defaultInstallDir = string;
427    }
428
429}