001/*
002 * $Id: CheckedHelloPanel.java,v 1.4 2005/09/04 02:06:46 jponge Exp $
003 * IzPack - Copyright 2001-2005 Julien Ponge, All Rights Reserved.
004 * 
005 * http://www.izforge.com/izpack/ http://developer.berlios.de/projects/izpack/
006 * 
007 * Copyright 2005 Klaus Bartz
008 * 
009 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
010 * in compliance with the License. You may obtain a copy of the License at
011 * 
012 * http://www.apache.org/licenses/LICENSE-2.0
013 * 
014 * Unless required by applicable law or agreed to in writing, software distributed under the License
015 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
016 * or implied. See the License for the specific language governing permissions and limitations under
017 * the License.
018 */
019
020package com.izforge.izpack.panels;
021
022import com.coi.tools.os.win.RegDataContainer;
023import com.coi.tools.os.win.RegistryImpl;
024import com.izforge.izpack.installer.InstallData;
025import com.izforge.izpack.installer.InstallerFrame;
026import com.izforge.izpack.util.AbstractUIHandler;
027import com.izforge.izpack.util.os.RegistryDefaultHandler;
028import com.izforge.izpack.util.os.RegistryHandler;
029
030/**
031 * An extended hello panel class which detects whether the product was already installed or not.
032 * This class should be only used if the RegistryInstallerListener will be also used. Current the
033 * check will be only performed on Windows operating system. This class can be used also as example
034 * how to use the registry stuff to get informations from the current system.
035 * 
036 * @author Klaus Bartz
037 */
038public class CheckedHelloPanel extends HelloPanel
039{
040
041    /** Flag to break installation or not. */
042    protected boolean abortInstallation;
043
044    /**
045     * The constructor.
046     * 
047     * @param parent The parent.
048     * @param idata The installation data.
049     */
050    public CheckedHelloPanel(InstallerFrame parent, InstallData idata)
051    {
052        super(parent, idata);
053        abortInstallation = isRegistered();
054    }
055
056    /**
057     * This method should only be called if this product was allready installed. It resolves the
058     * install path of the first already installed product and asks the user whether to install
059     * twice or not.
060     * 
061     * @return whether a multiple Install should be performed or not.
062     * @throws Exception
063     */
064    protected boolean multipleInstall() throws Exception
065    {
066        // Let us play a little bit with the regstry...
067        // Just for fun we would resolve the path of the already
068        // installed application.
069        // First we need a handler. There is no overhead at a
070        // secound call of getInstance, therefore we do not buffer
071        // the handler in this class.
072        RegistryHandler rh = RegistryDefaultHandler.getInstance();
073        int oldVal = rh.getRoot(); // Only for security...
074        // We know, that the product is already installed, else we
075        // would not in this method. Now we search for the path...
076        String uninstallName = rh.getUninstallName();
077        String oldInstallPath = "<not found>";
078        while (true) // My goto alternative :-)
079        {
080
081            if (uninstallName == null) break; // Should never be...
082            // First we "create" the reg key.
083            String keyName = RegistryHandler.UNINSTALL_ROOT + uninstallName;
084            rh.setRoot(RegistryImpl.HKEY_LOCAL_MACHINE);
085            if (!rh.valueExist(keyName, "UninstallString"))
086            // We assume that the application was installed with
087                    // IzPack. Therefore there should be the value "UninstallString"
088                    // which contains the uninstaller call. If not we can do nothing.
089                    break;
090            // Now we would get the value. A value can have different types.
091            // Therefore we get an container which can handle all possible types.
092            // There are different ways to handle. Use normally only one of the
093            // ways; at this point more are used to demonstrate the different ways.
094
095            // 1. If we are secure about the type, we can extract the value immediately.
096            String valString = rh.getValue(keyName, "UninstallString").getStringData();
097
098            // 2. If we are not so much interessted at the type, we can get the value
099            // as Object. A DWORD is then a Long Object not a long primitive type.
100            Object valObj = rh.getValue(keyName, "UninstallString").getDataAsObject();
101
102            // 3. If we are not secure about the type we should differ between possible
103            // types.
104            RegDataContainer val = rh.getValue(keyName, "UninstallString");
105            int typeOfVal = val.getType();
106            switch (typeOfVal)
107            {
108            case RegDataContainer.REG_EXPAND_SZ:
109            case RegDataContainer.REG_SZ:
110                valString = val.getStringData();
111                break;
112            case RegDataContainer.REG_BINARY:
113            case RegDataContainer.REG_DWORD:
114            case RegDataContainer.REG_LINK:
115            case RegDataContainer.REG_MULTI_SZ:
116                throw new Exception("Bad data type of chosen registry value " + keyName);
117            default:
118                throw new Exception("Unknown data type of chosen registry value " + keyName);
119            }
120            // That's all with registry this time... Following preparation of
121            // the received value.
122            // It is [java path] -jar [uninstaller path]
123            int start = valString.lastIndexOf("-jar") + 5;
124            if (start < 5 || start >= valString.length())
125            // we do not know what todo with it.
126                    break;
127            String uPath = valString.substring(start).trim();
128            if (uPath.startsWith("\"")) uPath = uPath.substring(1).trim();
129            int end = uPath.indexOf("uninstaller");
130            if (end < 0)
131            // we do not know what todo with it.
132                    break;
133            oldInstallPath = uPath.substring(0, end - 1);
134            // Much work for such a peanuts...
135            break; // That's the problem with the goto alternative. Forget this
136            // break produces an endless loop.
137        }
138
139        rh.setRoot(oldVal); // Only for security...
140
141        // The text will be to long for one line. Therefore we should use
142        // the multi line label. Unfortunately it has no icon. Nothing is
143        // perfect...
144        String noLuck = parent.langpack.getString("CheckedHelloPanel.productAlreadyExist0")
145                + oldInstallPath
146                + parent.langpack.getString("CheckedHelloPanel.productAlreadyExist1");
147        return (askQuestion(parent.langpack.getString("installer.error"), noLuck,
148                AbstractUIHandler.CHOICES_YES_NO) == AbstractUIHandler.ANSWER_YES);
149    }
150
151    /**
152     * Returns wether the handled application is already registered or not. The validation will be
153     * made only on systems which contains a registry (Windows).
154     * 
155     * @return wether the handled application is already registered or not
156     */
157    protected boolean isRegistered()
158    {
159        boolean retval = false;
160        try
161        {
162            // Get the default registry handler.
163            RegistryHandler rh = RegistryDefaultHandler.getInstance();
164            if (rh != null)
165            {
166                rh.verify(idata);
167                retval = rh.isProductRegistered();
168
169            }
170            // else we are on a os which has no registry or the
171            // needed dll was not bound to this installation. In
172            // both cases we forget the "already exist" check.
173
174        }
175        catch (Exception e)
176        { // Will only be happen if registry handler is good, but an
177            // exception at performing was thrown. This is an error...
178            e.printStackTrace();
179        }
180        return (retval);
181    }
182
183    /**
184     * Indicates wether the panel has been validated or not.
185     * 
186     * @return true if the internal abort flag is not set, else false
187     */
188    public boolean isValidated()
189    {
190        return (!abortInstallation);
191    }
192
193    /*
194     * (non-Javadoc)
195     * 
196     * @see com.izforge.izpack.installer.IzPanel#panelActivate()
197     */
198    public void panelActivate()
199    {
200        if (abortInstallation)
201        {
202            parent.lockNextButton();
203            try
204            {
205                if (multipleInstall())
206                {
207                    setUniqueUninstallKey();
208                    abortInstallation = false;
209                    parent.unlockNextButton();
210                }
211            }
212            catch (Exception e)
213            {
214                // TODO Auto-generated catch block
215                e.printStackTrace();
216            }
217
218        }
219        RegistryHandler rh = RegistryDefaultHandler.getInstance();
220        if( rh != null )
221            idata.setVariable("UNINSTALL_NAME", rh.getUninstallName());
222    }
223
224    /**
225     * @throws Exception
226     *  
227     */
228    private void setUniqueUninstallKey() throws Exception
229    {
230        // Let us play a little bit with the regstry again...
231        // Now we search for an unique uninstall key.
232        // First we need a handler. There is no overhead at a
233        // secound call of getInstance, therefore we do not buffer
234        // the handler in this class.
235        RegistryHandler rh = RegistryDefaultHandler.getInstance();
236        int oldVal = rh.getRoot(); // Only for security...
237        // We know, that the product is already installed, else we
238        // would not in this method. First we get the
239        // "default" uninstall key.
240        String uninstallName = rh.getUninstallName();
241        int uninstallModifier = 1;
242        while (true)
243        {
244            if (uninstallName == null) break; // Should never be...
245            // Now we define a new uninstall name.
246            String newUninstallName = uninstallName + "(" + Integer.toString(uninstallModifier)
247                    + ")";
248            // Then we "create" the reg key with it.
249            String keyName = RegistryHandler.UNINSTALL_ROOT + newUninstallName;
250            rh.setRoot(RegistryImpl.HKEY_LOCAL_MACHINE);
251            if (!rh.keyExist(keyName))
252            { // That's the name for which we searched.
253                // Change the uninstall name in the reg helper.
254                rh.setUninstallName(newUninstallName);
255                // Now let us inform the user.
256                emitNotification(parent.langpack
257                        .getString("CheckedHelloPanel.infoOverUninstallKey")
258                        + newUninstallName);
259                // Now a little hack if the registry spec file contains
260                // the pack "UninstallStuff".
261                break;
262            }
263            uninstallModifier++;
264        }
265    }
266
267}