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 2003 Jonathan Halliday
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.installer;
023
024import java.io.File;
025import java.io.InputStream;
026import java.io.ObjectInputStream;
027import java.util.ArrayList;
028import java.util.Enumeration;
029import java.util.Iterator;
030import java.util.List;
031import java.util.Locale;
032import java.util.Properties;
033
034import com.izforge.izpack.CustomData;
035import com.izforge.izpack.Info;
036import com.izforge.izpack.Pack;
037import com.izforge.izpack.util.Debug;
038import com.izforge.izpack.util.IoHelper;
039import com.izforge.izpack.util.OsConstraint;
040import com.izforge.izpack.util.OsVersion;
041import com.izforge.izpack.util.VariableSubstitutor;
042
043/**
044 * Common utility functions for the GUI and text installers. (Do not import swing/awt classes to
045 * this class.)
046 * 
047 * @author Jonathan Halliday
048 * @author Julien Ponge
049 */
050public class InstallerBase
051{
052
053    /**
054     * The base name of the XML file that specifies the custom langpack. Searched is for the file
055     * with the name expanded by _ISO3.
056     */
057    protected static final String LANG_FILE_NAME = "CustomLangpack.xml";
058
059    /**
060     * Loads the installation data. Also sets environment variables to <code>installdata</code>.
061     * All system properties are available as $SYSTEM_<variable> where <variable> is the actual
062     * name _BUT_ with all separators replaced by '_'. Properties with null values are never stored.
063     * Example: $SYSTEM_java_version or $SYSTEM_os_name
064     * 
065     * @param installdata Where to store the installation data.
066     * 
067     * @exception Exception Description of the Exception
068     */
069    public void loadInstallData(AutomatedInstallData installdata) throws Exception
070    {
071        // Usefull variables
072        InputStream in;
073        ObjectInputStream objIn;
074        int size;
075        int i;
076
077        // We load the variables
078        Properties variables = null;
079        in = InstallerBase.class.getResourceAsStream("/vars");
080        if (null != in)
081        {
082            objIn = new ObjectInputStream(in);
083            variables = (Properties) objIn.readObject();
084            objIn.close();
085        }
086
087        // We load the Info data
088        in = InstallerBase.class.getResourceAsStream("/info");
089        objIn = new ObjectInputStream(in);
090        Info inf = (Info) objIn.readObject();
091        objIn.close();
092
093        // We put the Info data as variables
094        installdata.setVariable(ScriptParser.APP_NAME, inf.getAppName());
095        installdata.setVariable(ScriptParser.APP_URL, inf.getAppURL());
096        installdata.setVariable(ScriptParser.APP_VER, inf.getAppVersion());
097
098        // We read the panels order data
099        in = InstallerBase.class.getResourceAsStream("/panelsOrder");
100        objIn = new ObjectInputStream(in);
101        List panelsOrder = (List) objIn.readObject();
102        objIn.close();
103
104        // We read the packs data
105        in = InstallerBase.class.getResourceAsStream("/packs.info");
106        objIn = new ObjectInputStream(in);
107        size = objIn.readInt();
108        ArrayList availablePacks = new ArrayList();
109        ArrayList allPacks = new ArrayList();
110        for (i = 0; i < size; i++)
111        {
112            Pack pk = (Pack) objIn.readObject();
113            allPacks.add(pk);
114            if (OsConstraint.oneMatchesCurrentSystem(pk.osConstraints)) availablePacks.add(pk);
115        }
116        objIn.close();
117
118        // We determine the operating system and the initial installation path
119        String dir;
120        String installPath;
121        if (OsVersion.IS_WINDOWS)
122        {
123            dir = buildWindowsDefaultPath();
124        }
125        else if (OsVersion.IS_OSX)
126        {
127            dir = "/Applications";
128        }
129        else
130        {
131            if (new File("/usr/local/").canWrite())
132            {
133                dir = "/usr/local";
134            }
135            else
136            {
137                dir = System.getProperty("user.home");
138            }
139        }
140        installdata.setVariable("APPLICATIONS_DEFAULT_ROOT", dir);
141        dir = dir + File.separator;
142        installdata.setVariable(ScriptParser.JAVA_HOME, System.getProperty("java.home"));
143        installdata.setVariable(ScriptParser.USER_HOME, System.getProperty("user.home"));
144        installdata.setVariable(ScriptParser.USER_NAME, System.getProperty("user.name"));
145        installdata.setVariable(ScriptParser.FILE_SEPARATOR, File.separator);
146
147        Enumeration e = System.getProperties().keys();
148        while (e.hasMoreElements())
149        {
150            String varName = (String) e.nextElement();
151            String varValue = System.getProperty(varName);
152            if (varValue != null)
153            {
154                varName = varName.replace('.', '_');
155                installdata.setVariable("SYSTEM_" + varName, varValue);
156            }
157        }
158
159        if (null != variables)
160        {
161            Enumeration enumeration = variables.keys();
162            String varName;
163            String varValue;
164            while (enumeration.hasMoreElements())
165            {
166                varName = (String) enumeration.nextElement();
167                varValue = variables.getProperty(varName);
168                installdata.setVariable(varName, varValue);
169            }
170        }
171
172        installdata.info = inf;
173        installdata.panelsOrder = panelsOrder;
174        installdata.availablePacks = availablePacks;
175        installdata.allPacks = allPacks;
176
177        // get list of preselected packs
178        Iterator pack_it = availablePacks.iterator();
179        while (pack_it.hasNext())
180        {
181            Pack pack = (Pack) pack_it.next();
182            if (pack.preselected) installdata.selectedPacks.add(pack);
183        }
184        // Set the installation path in a default manner
185        installPath = dir + inf.getAppName();
186        if (inf.getInstallationSubPath() != null)
187        { // A subpath was defined, use it.
188            installPath = IoHelper.translatePath(dir + inf.getInstallationSubPath(),
189                    new VariableSubstitutor(installdata.getVariables()));
190        }
191        installdata.setInstallPath(installPath);
192        // Load custom action data.
193        loadCustomData(installdata);
194
195    }
196
197    /**
198     * Add the contents of a custom langpack (if exist) to the previos loaded comman langpack. If
199     * not exist, trace an info and do nothing more.
200     * 
201     * @param idata install data to be used
202     */
203    protected void addCustomLangpack(AutomatedInstallData idata)
204    {
205        // We try to load and add a custom langpack.
206        try
207        {
208            idata.langpack.add(ResourceManager.getInstance().getInputStream(LANG_FILE_NAME));
209        }
210        catch (Throwable exception)
211        {
212            Debug.trace("No custom langpack available.");
213            return;
214        }
215        Debug.trace("Custom langpack for " + idata.localeISO3 + " available.");
216    }
217
218    /**
219     * Builds the default path for Windows (i.e Program Files/...).
220     * 
221     * @return The Windows default installation path.
222     */
223    private String buildWindowsDefaultPath()
224    {
225        StringBuffer dpath = new StringBuffer("");
226        try
227        {
228            // We load the properties
229            Properties props = new Properties();
230            props
231                    .load(InstallerBase.class
232                            .getResourceAsStream("/com/izforge/izpack/installer/win32-defaultpaths.properties"));
233
234            // We look for the drive mapping
235            String drive = System.getProperty("user.home");
236            if (drive.length() > 3) drive = drive.substring(0, 3);
237
238            // Now we have it :-)
239            dpath.append(drive);
240
241            // Ensure that we have a trailing backslash (in case drive was
242            // something
243            // like "C:")
244            if (drive.length() == 2) dpath.append("\\");
245
246            String language = Locale.getDefault().getLanguage();
247            String country = Locale.getDefault().getCountry();
248            String language_country = language + "_" + country;
249
250            // Try the most specific combination first
251            if (null != props.getProperty(language_country))
252            {
253                dpath.append(props.getProperty(language_country));
254            }
255            else if (null != props.getProperty(language))
256            {
257                dpath.append(props.getProperty(language));
258            }
259            else
260            {
261                dpath.append(props.getProperty(Locale.ENGLISH.getLanguage()));
262            }
263        }
264        catch (Exception err)
265        {
266            dpath = new StringBuffer("C:\\Program Files");
267        }
268
269        return dpath.toString();
270    }
271
272    /**
273     * Loads custom data like listener and lib references if exist and fills the installdata.
274     * 
275     * @param installdata installdata into which the custom action data should be stored
276     * @throws Exception
277     */
278    private void loadCustomData(AutomatedInstallData installdata) throws Exception
279    {
280        // Usefull variables
281        InputStream in;
282        ObjectInputStream objIn;
283        int i;
284        // Load listeners if exist.
285        String[] streamNames = AutomatedInstallData.CUSTOM_ACTION_TYPES;
286        List[] out = new List[streamNames.length];
287        for (i = 0; i < streamNames.length; ++i)
288            out[i] = new ArrayList();
289        in = InstallerBase.class.getResourceAsStream("/customData");
290        if (in != null)
291        {
292            objIn = new ObjectInputStream(in);
293            Object listeners = objIn.readObject();
294            objIn.close();
295            Iterator keys = ((List) listeners).iterator();
296            while (keys != null && keys.hasNext())
297            {
298                CustomData ca = (CustomData) keys.next();
299
300                if (ca.osConstraints != null
301                        && !OsConstraint.oneMatchesCurrentSystem(ca.osConstraints))
302                { // OS constraint defined, but not matched; therefore ignore
303                    // it.
304                    continue;
305                }
306                switch (ca.type)
307                {
308                case CustomData.INSTALLER_LISTENER:
309                    Class clazz = Class.forName(ca.listenerName);
310                    if (clazz == null)
311                        throw new InstallerException("Custom action " + ca.listenerName
312                                + " not bound!");
313                    out[ca.type].add(clazz.newInstance());
314                    break;
315                case CustomData.UNINSTALLER_LISTENER:
316                case CustomData.UNINSTALLER_JAR:
317                    out[ca.type].add(ca);
318                    break;
319                case CustomData.UNINSTALLER_LIB:
320                    out[ca.type].add(ca.contents);
321                    break;
322                }
323
324            }
325            // Add the current custem action data to the installdata hash map.
326            for (i = 0; i < streamNames.length; ++i)
327                installdata.customData.put(streamNames[i], out[i]);
328        }
329        // uninstallerLib list if exist
330
331    }
332}