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.io.File;
025import java.util.StringTokenizer;
026
027import com.izforge.izpack.installer.InstallData;
028import com.izforge.izpack.installer.InstallerFrame;
029import com.izforge.izpack.util.AbstractUIHandler;
030import com.izforge.izpack.util.FileExecutor;
031import com.izforge.izpack.util.OsVersion;
032
033/**
034 * Panel which asks for the JDK path.
035 * 
036 * @author Klaus Bartz
037 * 
038 */
039public class JDKPathPanel extends PathInputPanel
040{
041
042    private static final long serialVersionUID = 3257006553327810104L;
043
044    private static final String[] testFiles = new String[] { "lib" + File.separator + "tools.jar"};
045
046    private String detectedVersion;
047
048    private String minVersion = null;
049
050    private String maxVersion = null;
051
052    private String variableName;
053
054    /**
055     * The constructor.
056     * 
057     * @param parent The parent window.
058     * @param idata The installation data.
059     */
060    public JDKPathPanel(InstallerFrame parent, InstallData idata)
061    {
062        super(parent, idata);
063        setMustExist(true);
064        if(!OsVersion.IS_OSX)
065            setExistFiles(JDKPathPanel.testFiles);
066        setMinVersion(idata.getVariable("JDKPathPanel.minVersion"));
067        setMaxVersion(idata.getVariable("JDKPathPanel.maxVersion"));
068        setVariableName("JDKPath");
069    }
070
071    /**
072     * Indicates wether the panel has been validated or not.
073     * 
074     * @return Wether the panel has been validated or not.
075     */
076    public boolean isValidated()
077    {
078        if (super.isValidated())
079        {
080            if (verifyVersion())
081            {
082                idata.setVariable(getVariableName(), pathSelectionPanel.getPath());
083                return (true);
084            }
085            // Bad version detected.
086            String min = getMinVersion();
087            String max = getMaxVersion();
088            StringBuffer message = new StringBuffer();
089            message.append(parent.langpack.getString("JDKPathPanel.badVersion1")).append(
090                    getDetectedVersion()).append(
091                    parent.langpack.getString("JDKPathPanel.badVersion2"));
092            if (min != null && max != null)
093                message.append(min).append(" - ").append(max);
094            else if (min != null)
095                message.append(" >= ").append(min);
096            else if (max != null) message.append(" <= ").append(max);
097
098            message.append(parent.langpack.getString("JDKPathPanel.badVersion3"));
099            if (askQuestion(parent.langpack.getString("installer.warning"), message.toString(),
100                    AbstractUIHandler.CHOICES_YES_NO, AbstractUIHandler.ANSWER_NO) == AbstractUIHandler.ANSWER_YES)
101            {
102                idata.setVariable(getVariableName(), pathSelectionPanel.getPath());
103                return (true);
104            }
105        }
106        return (false);
107    }
108
109    /** Called when the panel becomes active. */
110    public void panelActivate()
111    {
112        // Resolve the default for chosenPath
113        super.panelActivate();
114        String chosenPath;
115        // The variable will be exist if we enter this panel
116        // second time. We would maintain the previos
117        // selected path.
118        if (idata.getVariable(getVariableName()) != null)
119            chosenPath = idata.getVariable(getVariableName());
120        else
121            // Try the JAVA_HOME as child dir of the jdk path
122            chosenPath = (new File(idata.getVariable("JAVA_HOME"))).getParent();
123        // Set the path for method pathIsValid ...
124        pathSelectionPanel.setPath(chosenPath);
125
126        if (!pathIsValid() || !verifyVersion()) chosenPath = "";
127        // Set the default to the path selection panel.
128        pathSelectionPanel.setPath(chosenPath);
129        String var = idata.getVariable("JDKPathPanel.skipIfValid");
130        // Should we skip this panel?
131        if (chosenPath.length() > 0 && var != null && var.equalsIgnoreCase("yes"))
132        {
133            idata.setVariable(getVariableName(), chosenPath);
134            parent.skipPanel();
135        }
136
137    }
138
139    private final boolean verifyVersion()
140    {
141        String min = getMinVersion();
142        String max = getMaxVersion();
143        // No min and max, version always ok.
144        if (min == null && max == null) return (true);
145
146        if (!pathIsValid()) return (false);
147        // No get the version ...
148        // We cannot look to the version of this vm because we should
149        // test the given JDK VM.
150        String[] params = {
151                pathSelectionPanel.getPath() + File.separator + "bin" + File.separator + "java",
152                "-version"};
153        String[] output = new String[2];
154        FileExecutor fe = new FileExecutor();
155        fe.executeCommand(params, output);
156        // "My" VM writes the version on stderr :-(
157        String vs = (output[0].length() > 0) ? output[0] : output[1];
158        if (min != null)
159        {
160            if (!compareVersions(vs, min, true, 4, 4, "__NO_NOT_IDENTIFIER_")) return (false);
161        }
162        if (max != null)
163            if (!compareVersions(vs, max, false, 4, 4, "__NO_NOT_IDENTIFIER_")) return (false);
164        return (true);
165    }
166
167    private final boolean compareVersions(String in, String template, boolean isMin,
168            int assumedPlace, int halfRange, String useNotIdentifier)
169    {
170        StringTokenizer st = new StringTokenizer(in, " \t\n\r\f\"");
171        int i;
172        int currentRange = 0;
173        String[] interestedEntries = new String[halfRange + halfRange];
174        for (i = 0; i < assumedPlace - halfRange; ++i)
175            if (st.hasMoreTokens()) st.nextToken(); // Forget this entries.
176
177        for (i = 0; i < halfRange + halfRange; ++i)
178        { // Put the interesting Strings into an intermediaer array.
179            if (st.hasMoreTokens())
180            {
181                interestedEntries[i] = st.nextToken();
182                currentRange++;
183            }
184        }
185
186        for (i = 0; i < currentRange; ++i)
187        {
188            if (useNotIdentifier != null && interestedEntries[i].indexOf(useNotIdentifier) > -1)
189                continue;
190            if (Character.getType(interestedEntries[i].charAt(0)) != Character.DECIMAL_DIGIT_NUMBER)
191                continue;
192            break;
193        }
194        if (i == currentRange)
195        {
196            detectedVersion = "<not found>";
197            return (false);
198        }
199        detectedVersion = interestedEntries[i];
200        StringTokenizer current = new StringTokenizer(interestedEntries[i], "._-");
201        StringTokenizer needed = new StringTokenizer(template, "._-");
202        while (needed.hasMoreTokens())
203        {
204            // Current can have no more tokens if needed has more
205            // and if a privious token was not accepted as good version.
206            // e.g. 1.4.2_02 needed, 1.4.2 current. The false return
207            // will be right here. Only if e.g. needed is 1.4.2_00 the
208            // return value will be false, but zero should not b e used
209            // at the last version part.
210            if (!current.hasMoreTokens()) return (false);
211            String cur = current.nextToken();
212            String nee = needed.nextToken();
213            int curVal = 0;
214            int neededVal = 0;
215            try
216            {
217                curVal = Integer.parseInt(cur);
218                neededVal = Integer.parseInt(nee);
219            }
220            catch (NumberFormatException nfe)
221            { // A number format exception will be raised if
222                // there is a non numeric part in the version,
223                // e.g. 1.5.0_beta. The verification runs only into
224                // this deep area of version number (fourth sub place)
225                // if all other are equal to the given limit. Then
226                // it is right to return false because e.g.
227                // the minimal needed version will be 1.5.0.2.
228                return (false);
229            }
230            if (curVal < neededVal) if (isMin)
231                return (false);
232            else
233                return (true);
234            if (Integer.parseInt(cur) > Integer.parseInt(nee)) if (isMin)
235                return (true);
236            else
237                return (false);
238        }
239        return (true);
240    }
241
242    /**
243     * Returns the current detected version.
244     * 
245     * @return the current detected version
246     */
247    public String getDetectedVersion()
248    {
249        return detectedVersion;
250    }
251
252    /**
253     * Returns the current used maximum version.
254     * 
255     * @return the current used maximum version
256     */
257    public String getMaxVersion()
258    {
259        return maxVersion;
260    }
261
262    /**
263     * Returns the current used minimum version.
264     * 
265     * @return the current used minimum version
266     */
267    public String getMinVersion()
268    {
269        return minVersion;
270    }
271
272    /**
273     * Sets the given value as current detected version.
274     * 
275     * @param string version string to be used as detected version
276     */
277    protected void setDetectedVersion(String string)
278    {
279        detectedVersion = string;
280    }
281
282    /**
283     * Sets the given value as maximum for version control.
284     * 
285     * @param string version string to be used as maximum
286     */
287    protected void setMaxVersion(String string)
288    {
289        maxVersion = string;
290    }
291
292    /**
293     * Sets the given value as minimum for version control.
294     * 
295     * @param string version string to be used as minimum
296     */
297    protected void setMinVersion(String string)
298    {
299        minVersion = string;
300    }
301
302    /**
303     * Returns the name of the variable which should be used for the path.
304     * 
305     * @return the name of the variable which should be used for the path
306     */
307    public String getVariableName()
308    {
309        return variableName;
310    }
311
312    /**
313     * Sets the name for the variable which should be set with the path.
314     * 
315     * @param string variable name to be used
316     */
317    public void setVariableName(String string)
318    {
319        variableName = string;
320    }
321
322    /*
323     * (non-Javadoc)
324     * 
325     * @see com.izforge.izpack.installer.IzPanel#getSummaryBody()
326     */
327    public String getSummaryBody()
328    {
329        return (idata.getVariable(getVariableName()));
330    }
331}