001: /*
002: * IzPack - Copyright 2001-2008 Julien Ponge, All Rights Reserved.
003: *
004: * http://izpack.org/
005: * http://izpack.codehaus.org/
006: *
007: * Copyright 2002 Elmar Grom
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:
022: package com.izforge.izpack.util;
023:
024: import java.io.BufferedReader;
025: import java.io.File;
026: import java.io.InputStream;
027: import java.io.InputStreamReader;
028: import java.util.StringTokenizer;
029:
030: /*---------------------------------------------------------------------------*/
031: /**
032: * The <code>TargetFactory</code> serves as a central mechanism to instantiate OS specific class
033: * flavors, provide OS specific file extension types, default install directories and similar
034: * functionality. In addition it provides services that are related to OS versions and flavors. For
035: * a tutorial on using some of the features in this class see the <A
036: * HREF=doc-files/TargetFactory.html>TargetFactory Tutorial</A>.
037: *
038: * @version 0.0.1 / 1/3/2002
039: * @author Elmar Grom
040: */
041: /*---------------------------------------------------------------------------*/
042: /*
043: * $ @design
044: *
045: * Reports actually observed on some systems:
046: *
047: * OS OS Name Version Architecture Native Report (ver)
048: * ----------------------------------------------------------------------------------------------------------
049: * Windows 95 Windows 98 Windows 98 4.10 x86 Windows 98 [Version 4.10.1998] Windows-ME Windows Me
050: * 4.90 x86 Windows Millennium [Version 4.90.3000] Windows-NT 3.5 Windows-NT 4.0 Windows NT 4.0 x86
051: * Windows NT Version 4.0 Windows 2000 Windows 2000 5.0 x86 Microsoft Windows 2000 [Version
052: * 5.00.2195] Windows-XP Windows 2000 5.1 x86 Microsoft Windows XP [Version 5.1.2600] Windows-XP
053: * Windows XP 5.1 x86 Mac Mac OS-X Linux Linux 2.4.7-10 i386 Linux Linux 2.4.18-4GB i386 Solaris
054: *
055: * ---------------------------------------------------------------------------
056: */
057: public class TargetFactory {
058:
059: // ------------------------------------------------------------------------
060: // Constant Definitions
061: // ------------------------------------------------------------------------
062:
063: // Basic operating systems
064:
065: /** Identifies Microsoft Windows. */
066: public static final int WINDOWS = 0;
067:
068: /** Identifies generic UNIX operating systems */
069: public static final int UNIX = 2;
070:
071: /** Used to report a non specific operating system. */
072: public static final int GENERIC = 3;
073:
074: // operating system favors
075:
076: /** This is the basic flavor for every operating system. */
077: public static final int STANDARD = 0;
078:
079: /**
080: * Used to identify the Windows-NT class of operating systems in terms of an OS flavor. It is
081: * reported for Windows-NT, 2000 and XP.
082: */
083: public static final int NT = 1;
084:
085: /** Used to identify the OS X flavor of the Mac OS */
086: public static final int X = 2;
087:
088: // system architecture
089:
090: /** Identifies Intel X86 based processor types. */
091: public static final int X86 = 0;
092:
093: /** Nonspecific processor architecture, other than X86. */
094: public static final int OTHER = 1;
095:
096: /**
097: * The extensions used for native libraries on various operating systems. The string positions
098: * correspond to the basic operating system indexes. The following values are legal to use :
099: * <br>
100: * <br>
101: * <ul>
102: * <li>WINDOWS
103: * <li>MAC
104: * <li>UNIX
105: * <li>GENERIC
106: * </ul>
107: */
108: static final String[] LIBRARY_EXTENSION = { "dll", "so", "", "" };
109:
110: /**
111: * The os specific class prefixes for classes that implement different versions for the various
112: * operating systems. The string positions correspond to the basic operating system indexes. The
113: * following values are legal to use : <br>
114: * <br>
115: * <ul>
116: * <li>WINDOWS
117: * <li>MAC
118: * <li>UNIX
119: * <li>GENERIC
120: * </ul>
121: */
122: static final String[] CLASS_PREFIX = { "Win_", "Mac_", "Unix_", "" };
123:
124: /**
125: * The os favor specific class prefixes for classes the implement different versions for various
126: * os favors. The string positions correspond to the flavor indexes. The following values are
127: * legal to use : <br>
128: * <br>
129: * <ul>
130: * <li>STANDARD
131: * <li>NT
132: * <li>X
133: * </ul>
134: */
135: static final String[] CLASS_FLAVOR_PREFIX = { "", "NT_", "X_" };
136:
137: /**
138: * The list of processor architecture specific prefixes. The string positions correspond to the
139: * architecture indexes. The following values are leegal to use : <br>
140: * <br>
141: * <ul>
142: * <li>X86
143: * <li>OTHER
144: * </ul>
145: */
146: static final String[] CLASS_ARCHITECTURE_PREFIX = { "X86_", // Intel X86
147: // architecture
148: "U_" // unknown
149: };
150:
151: /**
152: * The list of default install path fragments. Depending on the operating system, a path
153: * fragment might represent either a part of the default install path or the entire path to use.
154: * For MS-Windows it is always only a part of the full install path. The string positions
155: * correspond to the basic operating system indexes. The following values are leegal to use :
156: * <br>
157: * <br>
158: * <ul>
159: * <li>WINDOWS
160: * <li>MAC
161: * <li>UNIX
162: * <li>GENERIC
163: * </ul>
164: */
165: static final String[] INSTALL_PATH_FRAGMENT = {
166: "Program Files" + File.separator,
167: "/Applications" + File.separator,
168: "/usr/local" + File.separator,
169: File.separator + "apps" + File.separator };
170:
171: /**
172: * This is a list of keys to use when looking for resources that define the default install path
173: * to use. The list is organized as two dimensional array of <code>String</code>s. To access
174: * the array, denote the first dimension with the operating system index and the second
175: * dimension with the flavor index. For example to access the key for Windows-NT use
176: * <code>INSTALL_PATH_RESOURCE_KEY[WINDOWS][NT]</code> The array uses a sparse population,
177: * that is, not all array locations actually contain a key. Only locations for which a real
178: * operating system/flavor combination exists are populated. For example, there is no such thing
179: * as <code>INSTALL_PATH_RESOURCE_KEY[UNIX][X]</code>
180: */
181: static final String[][] INSTALL_PATH_RESOURCE_KEY = {
182: // Standard NT X
183: { "TargetPanel.dir.windows", "TargetPanel.dir.windows", "" }, // Windows
184: { "TargetPanel.dir.mac", "", "TargetPanel.dir.macosx" }, // Mac
185: { "TargetPanel.dir.unix", "", "" }, // UNIX
186: { "TargetPanel.dir", "", "" } // Generic
187: };
188:
189: /** The delimiter characters used to tokenize version numbers */
190: private static final String VERSION_DELIMITER = ".-";
191:
192: // ------------------------------------------------------------------------
193: // Variable Declarations
194: // ------------------------------------------------------------------------
195: /**
196: * The reference to the single instance of <code>TargetFactory</code>. Used in static methods
197: * in place of <code>this</code>.
198: */
199: private static TargetFactory me = null;
200:
201: /** identifies the operating system we are running on */
202: private int os = -1;
203:
204: /** identifies the operating system favor */
205: private int osFlavor = -1;
206:
207: /** identifies the hardware architecture we are running on */
208: private int architecture = -1;
209:
210: /** represents the version number of the target system */
211: private String version = "";
212:
213: /*--------------------------------------------------------------------------*/
214: /**
215: * Constructor
216: */
217: /*--------------------------------------------------------------------------*/
218: /*
219: * $ @design
220: *
221: * Identify the following about the target system: - OS type - architecture - version
222: *
223: * and store this information for later use.
224: * --------------------------------------------------------------------------
225: */
226: private TargetFactory() {
227: version = System.getProperty("os.version");
228:
229: // ----------------------------------------------------
230: // test for Windows
231: // ----------------------------------------------------
232: if (OsVersion.IS_WINDOWS) {
233: os = WINDOWS;
234: osFlavor = STANDARD;
235: architecture = X86;
236: String osName = OsVersion.OS_NAME.toLowerCase();
237:
238: if (osName.indexOf("nt") > -1) {
239: osFlavor = NT;
240: } else if (osName.indexOf("2000") > -1) {
241: osFlavor = NT;
242: } else if (osName.indexOf("xp") > -1) {
243: osFlavor = NT;
244: }
245: }
246: // ----------------------------------------------------
247: // test for Mac OS
248: // ----------------------------------------------------
249: else if (OsVersion.IS_OSX) {
250: os = X;
251: osFlavor = STANDARD;
252: architecture = OTHER;
253: }
254: // ----------------------------------------------------
255: // what's left should be unix
256: // ----------------------------------------------------
257: else {
258: os = UNIX;
259: osFlavor = STANDARD;
260: architecture = OTHER;
261: String osName = OsVersion.OS_NAME.toLowerCase();
262:
263: if (osName.indexOf("x86") > -1) {
264: architecture = X86;
265: }
266: }
267: }
268:
269: /*--------------------------------------------------------------------------*/
270: /**
271: * Returns an instance of <code>TargetFactory</code> to use.
272: *
273: * @return an instance of <code>TargetFactory</code>.
274: */
275: /*--------------------------------------------------------------------------*/
276: public static TargetFactory getInstance() {
277: if (me == null) {
278: me = new TargetFactory();
279: }
280:
281: return me;
282: }
283:
284: /*--------------------------------------------------------------------------*/
285: /**
286: * This method returns an OS and OS flavor specific instance of the requested class. <br>
287: * <br>
288: * <b>Class Naming Rules</b><br>
289: * Class versions must be named with the OS and OS flavor as prefix. The prefixes are simply
290: * concatenated, with the OS prefix first and the flavor prefix second. Use the following OS
291: * specific prefixes:<br>
292: * <br>
293: * <TABLE BORDER=1>
294: * <TR>
295: * <TH>Operating System</TH>
296: * <TH>Prefix</TH>
297: * </TR>
298: * <TR>
299: * <TD>Microsoft Windows</TD>
300: * <TD>Win_</TD>
301: * </TR>
302: * <TR>
303: * <TD>Mac OS</TD>
304: * <TD>Mac_</TD>
305: * </TR>
306: * <TR>
307: * <TD>UNIX</TD>
308: * <TD>UNIX_</TD>
309: * </TR>
310: * </TABLE><br>
311: * For the different OS flavors, use these prefixes:<br>
312: * <br>
313: * <TABLE BORDER=1>
314: * <TR>
315: * <TH>OS Flavor</TH>
316: * <TH>Prefix</TH>
317: * </TR>
318: * <TR>
319: * <TD>NT</TD>
320: * <TD>NT_</TD>
321: * </TR>
322: * <TR>
323: * <TD>Mac OS X</TD>
324: * <TD>X_</TD>
325: * </TR>
326: * </TABLE> <br>
327: * <br>
328: * <b>Naming Example:</b> <br>
329: * <br>
330: * For the class <code>MyClass</code>, the specific version for Windows NT must be in the
331: * same package as <code>MyClass</code> and the name must be <code>Win_NT_MyClass</code>. A
332: * version that should be instantiated for any non-NT flavor would be called
333: * <code>Win_MyClass</code>. This would also be the version instantiated on Windows NT if the
334: * version <code>Win_NT_MyClass</code> does not exist. <br>
335: * <br>
336: * <b>The Loading Process</b> <br>
337: * <br>
338: * The process is completed after the first successful attempt to load a class. <br>
339: * <ol>
340: * <li>load a version that is OS and OS-Flavor specific
341: * <li>load a version that is OS specific
342: * <li>load the base version (without OS or OS-Flavor prefix)
343: * </ol>
344: * <br>
345: * See the <A HREF=doc-files/TargetFactory.html>TargetFactory Tutorial</A> for more
346: * information.<br>
347: * <br>
348: *
349: * @param name the fully qualified name of the class to load without the extension.
350: *
351: * @return An instance of the requested class. Note that specific initialization that can not be
352: * accomplished in the default constructor still needs to be performed before the object can be
353: * used.
354: *
355: * @exception Exception if all attempts to instantiate class fail
356: */
357: /*--------------------------------------------------------------------------*/
358: public Object makeObject(String name) throws Exception {
359: int nameStart = name.lastIndexOf('.') + 1;
360: String packageName = name.substring(0, nameStart);
361: String className = name.substring(nameStart, name.length());
362: String actualName;
363:
364: try {
365: actualName = packageName + CLASS_PREFIX[os]
366: + CLASS_FLAVOR_PREFIX[osFlavor] + className;
367: Class temp = Class.forName(actualName);
368: return temp.newInstance();
369: } catch (Throwable exception1) {
370: try {
371: Class temp = Class.forName(packageName
372: + CLASS_PREFIX[os] + className);
373: return temp.newInstance();
374: } catch (Throwable exception2) {
375: try {
376: actualName = name;
377: Class temp = Class.forName(actualName);
378: return temp.newInstance();
379: } catch (Throwable exception3) {
380: throw new Exception("can not instantiate class "
381: + name);
382: }
383: }
384: }
385: }
386:
387: /*--------------------------------------------------------------------------*/
388: /**
389: * Returns true if the version in the parameter string is higher than the version of the target
390: * os.
391: *
392: * @param version the version number to compare to
393: *
394: * @return <code>false</code> if the version of the target system is higher, otherwise
395: * <code>true</code>
396: */
397: /*--------------------------------------------------------------------------*/
398: /*
399: * $ @design
400: *
401: * Version numbers are assumed to be constructed as follows: - a list of one or more numbers,
402: * separated by periods as in X.X.X. ... or periods and dashes as in X.X.X-Y. ... - the numbers
403: * follow the decimal number system - the left most number is of highest significance
404: *
405: * The process compares each set of numbers, beginning at the most significant and working down
406: * the ranks (this is working left to right). The process is stopped as soon as the pair of
407: * numbers compaired is not equal. If the numer for the target system is higher, flase is
408: * returned, otherwise true.
409: * --------------------------------------------------------------------------
410: */
411: public boolean versionIsHigher(String version) throws Exception {
412: StringTokenizer targetVersion = new StringTokenizer(
413: this .version, VERSION_DELIMITER);
414: StringTokenizer compareVersion = new StringTokenizer(version,
415: VERSION_DELIMITER);
416:
417: int target;
418: int compare;
419:
420: while (targetVersion.hasMoreTokens()
421: && compareVersion.hasMoreTokens()) {
422: try {
423: target = Integer.parseInt(targetVersion.nextToken());
424: compare = Integer.parseInt(compareVersion.nextToken());
425: } catch (Throwable exception) {
426: throw new Exception("error in version string");
427: }
428:
429: if (compare > target) {
430: return true;
431: } else if (target > compare) {
432: return false;
433: }
434: }
435:
436: return false;
437: }
438:
439: /*--------------------------------------------------------------------------*/
440: /**
441: * Returns the index number for the target operating system that was detected.
442: *
443: * @return an index number for the OS
444: *
445: * @see #WINDOWS
446: * @see #UNIX
447: * @see #GENERIC
448: */
449: /*--------------------------------------------------------------------------*/
450: public int getOS() {
451: return os;
452: }
453:
454: /*--------------------------------------------------------------------------*/
455: /**
456: * Returns the index number for the operating system flavor that was detected on the target
457: * system.
458: *
459: * @return an index for the OS flavor
460: *
461: * @see #STANDARD
462: * @see #NT
463: * @see #X
464: */
465: /*--------------------------------------------------------------------------*/
466: public int getOSFlavor() {
467: return osFlavor;
468: }
469:
470: /*--------------------------------------------------------------------------*/
471: /**
472: * Returns an index number that identified the processor architecture of the target system.
473: *
474: * @return an index for the processor architecture
475: *
476: * @see #X86
477: * @see #OTHER
478: */
479: /*--------------------------------------------------------------------------*/
480: public int getArchitecture() {
481: return architecture;
482: }
483:
484: /*--------------------------------------------------------------------------*/
485: /**
486: * Returns the file extension customarily used on the target OS for dynamically loadable
487: * libraries.
488: *
489: * @return a <code>String</code> containing the customary library extension for the target OS.
490: * Note that the string might be empty if there no such specific extension for the target OS.
491: */
492: /*--------------------------------------------------------------------------*/
493: public String getNativeLibraryExtension() {
494: return LIBRARY_EXTENSION[os];
495: }
496:
497: /*--------------------------------------------------------------------------*/
498: /**
499: * Returns the system dependent default install path. This is typically used to suggest an
500: * istall path to the end user, when performing an installation. The default install path is
501: * assembled form the OS specific path fragment specified in <code>INSTALL_PATH_FRAGMENT</code>,
502: * possibly a drive letter and the application name. The user the option to define resources
503: * that define default paths which differ from the path fragments defined here. The following
504: * resource names will be recognized by this method: <br>
505: * <br>
506: * <ul>
507: * <li><code>TargetPanel.dir.windows</code>
508: * <li><code>TargetPanel.dir.macosx</code>
509: * <li><code>TargetPanel.dir.unix</code>
510: * <li><code>TargetPanel.dir</code> plus the all lower case version of
511: * <code>System.getProperty ("os.name")</code>, with all spaces replaced by an underscore
512: * ('_').
513: * <li><code>TargetPanel.dir</code>
514: * </ul>
515: *
516: * @param appName the name of the application to install. If no specific resource has been set,
517: * then this name will be appended to the OS specific default path fragment.
518: *
519: * @return the default install path for the target system
520: */
521: /*--------------------------------------------------------------------------*/
522: /*
523: * $ @design
524: *
525: * First try to read a path string from a resource file. This approach allows the user to
526: * customize the default install path that is suggested to the end user by IzPack. There are a
527: * number of choices for the naming of this resource, so we need to go through a few steps in
528: * order to exhaust the different possibilities. If this was not successful we use the default
529: * install path that is defined for the operating system we are running on. This path should be
530: * expanded by the application name to form the full path that to returne.
531: * --------------------------------------------------------------------------
532: */
533: public String getDefaultInstallPath(String appName) {
534: String path = null;
535: InputStream input;
536: String keyFragment = "/res/"
537: + INSTALL_PATH_RESOURCE_KEY[GENERIC][STANDARD];
538:
539: // ----------------------------------------------------
540: // attempt to get an input stream through a resource
541: // based on a key which is specific to the target OS
542: // ----------------------------------------------------
543: input = getClass().getResourceAsStream(
544: "/res/" + INSTALL_PATH_RESOURCE_KEY[os][osFlavor]);
545:
546: // ----------------------------------------------------
547: // attempt to get an input stream through a resource
548: // based on a key which is made specific to the target
549: // OS by using the string returned by
550: // System.getProperty ("os.name").toLowerCase ()
551: // ----------------------------------------------------
552: if (input == null) {
553: String key = OsVersion.OS_NAME.toLowerCase().replace(' ',
554: '_'); // avoid
555: // spaces
556: // in
557: // file
558: // names
559: key = keyFragment + key.toLowerCase(); // for consistency among
560: // TargetPanel res files
561: input = TargetFactory.class.getResourceAsStream(key);
562: }
563:
564: // ----------------------------------------------------
565: // attempt to get an input stream through a resource
566: // based on a key which is not specific to any target OS
567: // ----------------------------------------------------
568: if (input == null) {
569: input = TargetFactory.class
570: .getResourceAsStream(keyFragment);
571: }
572:
573: // ----------------------------------------------------
574: // If we got an input stream try to read the path
575: // from the file
576: // ----------------------------------------------------
577: if (input != null) {
578: InputStreamReader streamReader;
579: BufferedReader reader = null;
580: String line;
581:
582: try {
583: streamReader = new InputStreamReader(input);
584: reader = new BufferedReader(streamReader);
585: line = reader.readLine();
586:
587: while (line != null) {
588: line = line.trim();
589: if (!"".equals(line)) {
590: break;
591: }
592: line = reader.readLine();
593: }
594: path = line;
595: } catch (Throwable exception) {
596: } finally {
597: try {
598: if (reader != null)
599: reader.close();
600: } catch (Throwable exception) {
601: }
602: }
603: }
604:
605: // ----------------------------------------------------
606: // if we were unable to obtain a path from a resource,
607: // use the default for the traget operating system.
608: // ----------------------------------------------------
609: if (path == null || "".equals(path)) {
610: path = "";
611:
612: // --------------------------------------------------
613: // if we run on windows, we need a valid drive letter
614: // to put in front of the path. The drive that
615: // contains the user's home directory is usually the
616: // drive that also contains the install directory,
617: // so this seems the best choice here.
618: // --------------------------------------------------
619: if (os == WINDOWS) {
620: String home = System.getProperty("user.home");
621: // take everything up to and including the first '\'
622: path = home.substring(0, home
623: .indexOf(File.separatorChar) + 1);
624: }
625:
626: path = path + INSTALL_PATH_FRAGMENT[os] + appName;
627: }
628:
629: return path;
630: }
631:
632: /**
633: * Gets a prefix alias for the current platform. "Win_" on Windows Systems "Win_NT_" on WinNT4,
634: * 2000, XP Mac on Mac Mac_X on macosx and Unix_
635: *
636: * @return a prefix alias for the current platform
637: */
638:
639: public static String getCurrentOSPrefix() {
640: String OSName = System.getProperty("os.name").toLowerCase();
641: String OSArch = System.getProperty("os.arch").toLowerCase();
642: int OS = 0;
643: int OSFlavor = 0;
644: int OSarchitecture = 0;
645: // ----------------------------------------------------
646: // test for Windows
647: // ----------------------------------------------------
648: if (OSName.indexOf("windows") > -1) {
649: OS = WINDOWS;
650: OSFlavor = STANDARD;
651: OSarchitecture = X86;
652:
653: if (OSName.indexOf("nt") > -1) {
654: OSFlavor = NT;
655: } else if (OSName.indexOf("2000") > -1) {
656: OSFlavor = NT;
657: } else if (OSName.indexOf("xp") > -1) {
658: OSFlavor = NT;
659: }
660: }
661: // ----------------------------------------------------
662: // test for Mac OS
663: // ----------------------------------------------------
664: else if (OSName.indexOf("mac") > -1) {
665: OS = GENERIC;
666: OSFlavor = STANDARD;
667: OSarchitecture = OTHER;
668:
669: if (OSName.indexOf("macosx") > -1) {
670: OSFlavor = X;
671: }
672: }
673: // ----------------------------------------------------
674: // what's left should be unix
675: // ----------------------------------------------------
676: else {
677: OS = UNIX;
678: OSFlavor = STANDARD;
679: OSarchitecture = OTHER;
680:
681: if (OSArch.indexOf("86") > -1) {
682: OSarchitecture = X86;
683: }
684: }
685:
686: return (CLASS_PREFIX[OS] + CLASS_FLAVOR_PREFIX[OSFlavor]);
687: }
688:
689: }
690: /*---------------------------------------------------------------------------*/
|