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 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
022package com.izforge.izpack.util.os;
023
024import java.io.File;
025import java.io.UnsupportedEncodingException;
026
027import com.izforge.izpack.util.Librarian;
028import com.izforge.izpack.util.NativeLibraryClient;
029
030/*---------------------------------------------------------------------------*/
031/**
032 * This class represents a MS-Windows shell link, aka shortcut. It supports creation, modification
033 * and deletion as well as reporting on details of shell links. This class uses a number of native
034 * methods to access the MS-Windows registry and load save and manipulate link data. The native code
035 * is contained in the file <code>ShellLink.cpp</code>. <br>
036 * <br>
037 * For more detailed information on Windows shortcuts read the win32 documentation from Microsoft on
038 * the IShellLink interface. There are also useful articles on this topic on the MIcrosoft website.
039 * <br>
040 * <br>
041 * <A
042 * HREF=http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnmgmt/html/msdn_shellnk1.asp>Using
043 * Shell Links in Windows 95</A><br>
044 * <A
045 * HREF=http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/shell/reference/ifaces/ishelllink/ishelllink.asp>The
046 * IShellLink interface</a><br>
047 * <A
048 * HREF=http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/Shell/IFaces/IShellLink/IShellLink.asp>IShellLink</A>
049 * 
050 * @version 0.0.1 / 1/21/02
051 * @author Elmar Grom
052 */
053/*---------------------------------------------------------------------------*/
054public class ShellLink implements NativeLibraryClient
055{
056
057    // ------------------------------------------------------------------------
058    // Constant Definitions
059    // ------------------------------------------------------------------------
060    /**
061     * Note: each of the subclasses will convert these values as appropriate before calling the OS's
062     * routines. For example Win 98 & up will use SW_SNOWMINNOACTIVE (7) when passed HIDE (0) or
063     * MINIMIZED (2) <br>
064     * <br>
065     * and this conversion is done in Win_Shortcut.java
066     */
067    /**
068     * Hide the window when starting. This is particularly useful when launching from a *.bat file,
069     * because no DOS window and no button for the DOS window on the task bar will show! <br>
070     * <br>
071     * <b>Note:</b> this option is not available through the Windows 98+ UI!
072     */
073    public static final int HIDE = 0;
074
075    /**
076     * Show the window 'normal' when starting. Restores the window properties at the last shut-down.
077     */
078    public static final int NORMAL = 1;
079
080    /**
081     * Show the window minimized when starting. The window will not show but a corresponding button
082     * in the task bar will. <p>
083     *
084     * Newer IShellLink only allows Normal, MinNoActive, Maximized.
085     */
086    public static final int MINIMIZED = 2;
087
088    /** Show the window maximized when starting. */
089    public static final int MAXIMIZED = 3;
090
091    /**
092     * Show the window minimized when starting. Note: for win98 and newer, use MINNOACTIVE instead
093     * of MINIMIZED.
094     */
095    public static final int MINNOACTIVE = 7;
096
097    private static final int MIN_SHOW = 0;
098
099    private static final int MAX_SHOW = 7;
100
101    // ------------------------------------------------------
102    // Shortcut types
103    // specific to ShellLink (Shortcut has different numbers).
104    // ------------------------------------------------------
105    /** This type of shortcut shows on the desktop */
106    public static final int DESKTOP = 1;
107
108    /** This type of shortcut shows in the program menu */
109    public static final int PROGRAM_MENU = 2;
110
111    /** This type of shortcut shows in the start menu */
112    public static final int START_MENU = 3;
113
114    /** This type of shortcut is executed at OS launch time */
115    public static final int STARTUP = 4;
116
117    private static final int MIN_TYPE = 1;
118
119    private static final int MAX_TYPE = 4;
120
121    // ------------------------------------------------------
122    // Return values from nafive methods
123    // ------------------------------------------------------
124    /** Returned from native calls if the call was successful */
125    private static final int SL_OK = 1;
126
127    /** Unspecific return if a native call was not successful */
128    private static final int SL_ERROR = -1;
129
130    /**
131     * Return value from native initialization functions if already initialized
132     */
133    private static final int SL_INITIALIZED = -2;
134
135    /**
136     * Return value from native uninitialization functions if never initialized
137     */
138    private static final int SL_NOT_INITIALIZED = -3;
139
140    /**
141     * Return value from native uninitialization functions if there are no more interface handles
142     * available
143     */
144    private static final int SL_OUT_OF_HANDLES = -4;
145
146    /**
147     * Return value from native uninitialization functions if nohandle for the IPersist interface
148     * could be obtained
149     */
150    private static final int SL_NO_IPERSIST = -5;
151
152    /**
153     * Return value from native uninitialization functions if the save operation fort the link
154     * failed
155     */
156    private static final int SL_NO_SAVE = -6;
157
158    /**
159     * Return value if the function called had to deal with unexpected data types. This might be
160     * returned by registry functions if they receive an unexpected data type from the registry.
161     */
162    private static final int SL_WRONG_DATA_TYPE = -7;
163
164    // ------------------------------------------------------
165    // Miscellaneous constants
166    // ------------------------------------------------------
167    private static final int UNINITIALIZED = -1;
168
169    /** the extension that must be used for link files */
170    private static final String LINK_EXTENSION = ".lnk";
171
172    /** CURRENT_USER = 0; the constant to use for selecting the current user. */
173    public static final int CURRENT_USER = 0;
174
175    /** the constant to use for selecting the all users. */
176    public static final int ALL_USERS = 1;
177
178    // ------------------------------------------------------------------------
179    // Variable Declarations
180    // ------------------------------------------------------------------------
181    /**
182     * This handle links us to a specific native instance. Do not use or modify, the variable is for
183     * exclusive use by the native side.
184     */
185    private int nativeHandle = UNINITIALIZED;
186
187    /**
188     * Path to the location where links for the current user are stored. The exact content depends
189     * on the circumstances. It can be set during object construction or from native code. It will
190     * point to the location where links of the most recently requested type are stored.
191     */
192    private String currentUserLinkPath;
193
194    /**
195     * Path to the location where links for all users are stored. The exact content depends on the
196     * circumstances. It can be set during object construction or from native code. It will point to
197     * the location where links of the most recently requested type are stored.
198     */
199    private String allUsersLinkPath;
200
201    private String groupName = "";
202
203    private String linkName = "";
204
205    /**
206     * this is the fully qualified name of the link on disk. Note that this variable contains only
207     * valid data if the link was created from a disk file or after a successful save operation. At
208     * other times the content is upredicatable.
209     */
210    private String linkFileName = "";
211
212    /**
213     * Contains the directory where the link file is stored after any save operation that needs to
214     * create that directory. Otherwise it contains <code>null</code>.
215     */
216    private String linkDirectory = "";
217
218    private String arguments = "";
219
220    private String description = "";
221
222    private String iconPath = "";
223
224    private String targetPath = "";
225
226    private String workingDirectory = "";
227
228    /**
229     * there seems to be an error in JNI that causes an access violation if a String that is
230     * accessed from native code borders on another type of variable. This caused problems in
231     * <code>set()</code> For this reason, the dummy string is placed here. Observed with version:
232     * 
233     * <pre>
234     * 
235     *  
236     *   
237     *        java version &quot;1.3.0&quot;
238     *        Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.0-C)
239     *        Java HotSpot(TM) Client VM (build 1.3.0-C, mixed mode) 
240     *   
241     *  
242     * </pre>
243     */
244    private String dummyString = "";
245
246    private int hotkey = 0;
247
248    private int iconIndex = 0;
249
250    private int showCommand = NORMAL;
251
252    private int linkType = DESKTOP;
253
254    private int userType = CURRENT_USER;
255
256    private boolean initializeSucceeded = false;
257
258    // ------------------------------------------------------------------------
259    // Native Methods
260    // ------------------------------------------------------------------------
261    // For documentation on these methods see ShellLink.cpp
262    // ------------------------------------------------------------------------
263    private native int initializeCOM();
264
265    private native int releaseCOM();
266
267    private native int getInterface();
268
269    private native int releaseInterface();
270
271    private native int GetArguments();
272
273    private native int GetDescription();
274
275    private native int GetHotkey();
276
277    private native int GetIconLocation();
278
279    private native int GetPath();
280
281    private native int GetShowCommand();
282
283    private native int GetWorkingDirectory();
284
285    private native int Resolve();
286
287    private native int SetArguments();
288
289    private native int SetDescription();
290
291    private native int SetHotkey();
292
293    private native int SetIconLocation();
294
295    private native int SetPath();
296
297    private native int SetShowCommand();
298
299    private native int SetWorkingDirectory();
300
301    private native int saveLink(String name);
302
303    private native int loadLink(String name);
304
305    private native int GetFullLinkPath(int usertype, int linktype);
306
307    /**
308     * This method is used to free the library at the end of progam execution. After this call, any
309     * instance of this calss will not be usable any more!
310     */
311    private native void FreeLibrary(String name);
312
313    /**
314     * Creates an instance of <code>ShellLink</code> of a specific type. Initializes
315     * currentUserLinkPath and allUsersLinkPath.
316     * <p>
317     * 
318     * A LinkPath is empty if the combination of linkType and userType, are not valid.
319     * <p>
320     * 
321     * Note: If a linkPath is empty, the userType is reset to the other userType.
322     * <p>
323     * 
324     * If both linkPaths are empty, an IllegalArgumentException is thrown.
325     * 
326     * @param type The type of link desired. The following values can be set:<br>
327     * <ul>
328     * <li><code>ShellLink.DESKTOP</code>
329     * <li><code>ShellLink.PROGRAM_MENU</code>
330     * <li><code>ShellLink.START_MENU</code>
331     * <li><code>ShellLink.STARTUP</code>
332     * </ul>
333     * @param name The name that the link should display on a menu or on the desktop. Do not include
334     * a file extension.
335     * 
336     * @exception IllegalArgumentException if any of the call parameters are incorrect, or if no
337     * linkPaths are returned.
338     * @exception Exception if problems are encountered in initializing the native interface
339     */
340    public ShellLink(int type, String name) throws Exception, IllegalArgumentException
341    {
342        if ((type < MIN_TYPE) || (type > MAX_TYPE)) { throw (new IllegalArgumentException(
343                "the type parameter used an illegal value")); }
344        if (name == null) { throw (new IllegalArgumentException("the name parameter was null")); }
345
346        linkName = name;
347        linkType = type;
348
349        initialize(); // com
350
351        // set curretnUsersLinkPath, allUsersLinkPath, sets userType to valid.
352        setAllLinkPaths();
353    }
354
355    /*--------------------------------------------------------------------------*/
356    /**
357     * Creates an instance of <code>ShellLink</code> from an existing shell link on disk.
358     * 
359     * @param name the fully qualified file name of the link.
360     * @param userType the type of user for the link path.
361     * 
362     * @see #CURRENT_USER
363     * @see #ALL_USERS
364     * 
365     * @exception IllegalArgumentException if the name was null
366     * @exception Exception if problems are encountered in reading the file
367     */
368    public ShellLink(String name, int userType) throws Exception, IllegalArgumentException
369    {
370        if (name == null) { throw (new IllegalArgumentException("the name parameter was null")); }
371
372        this.userType = userType;
373
374        initialize(); // com
375
376        // store the individual parts of the path for later use
377        int pathEnd = name.lastIndexOf(File.separator);
378        int nameStart = pathEnd + 1;
379        int nameEnd = name.lastIndexOf('.');
380        if (nameEnd < 0) { throw (new Exception("illegal file name")); }
381        linkName = name.substring(nameStart, nameEnd);
382
383        if (userType == CURRENT_USER)
384        {
385            currentUserLinkPath = name.substring(0, pathEnd);
386        }
387        else
388        {
389            allUsersLinkPath = name.substring(0, pathEnd);
390        }
391
392        linkFileName = fullLinkName(userType);
393        if (loadLink(linkFileName) != SL_OK) { throw (new Exception(
394                "reading of the file did not succeed")); }
395
396        // get all settings from the native side
397        get();
398    }
399
400    /*--------------------------------------------------------------------------*/
401    /**
402     * Creates an instance of <code>ShellLink</code> from an existing shell link on disk.
403     * 
404     * @param type The type of link, one of the following values: <br>
405     * <ul>
406     * <li><code>ShellLink.DESKTOP</code>
407     * <li><code>ShellLink.PROGRAM_MENU</code>
408     * <li><code>ShellLink.START_MENU</code>
409     * <li><code>ShellLink.STARTUP</code>
410     * </ul>
411     * @param userType the type of user for the link path.
412     * @param group The program group (directory) of this link. If the link is not part of a program
413     * group, pass an empty string or null for this parameter. (...\\Desktop\\group).
414     * @param name The file name of this link. Do not include a file extension.
415     * 
416     * @see #CURRENT_USER
417     * @see #ALL_USERS
418     * 
419     * @exception IllegalArgumentException if any of the call parameters are incorrect
420     * @exception Exception if problems are encountered in initializing the native interface
421     */
422    public ShellLink(int type, int userType, String group, String name) throws Exception,
423            IllegalArgumentException
424    {
425        if ((type < MIN_TYPE) || (type > MAX_TYPE)) { throw (new IllegalArgumentException(
426                "the type parameter used an illegal value")); }
427        if (name == null) { throw (new IllegalArgumentException("the name parameter was null")); }
428
429        this.userType = userType;
430
431        initialize(); // com
432
433        // set the variables for currentUserLinkPath and allUsersLinkPath
434        setAllLinkPaths();
435
436        if (group != null)
437        {
438            groupName = group;
439        }
440        linkName = name;
441
442        // load the link
443        linkFileName = fullLinkName(userType);
444        if (loadLink(linkFileName) != SL_OK) { throw (new Exception(
445                "reading of the file did not succeed")); }
446
447        // get a settings from the native side
448        get();
449    }
450
451    /*--------------------------------------------------------------------------*/
452    /**
453     * Initializes COM and gets an instance of the IShellLink interface.
454     * 
455     * @exception Exception if problems are encountered
456     */
457    private void initialize() throws Exception
458    {
459        try
460        {
461            Librarian.getInstance().loadLibrary("ShellLink", this);
462        }
463        catch (UnsatisfiedLinkError exception)
464        {
465            throw (new Exception("could not locate native library"));
466        }
467
468        try
469        {
470            if (initializeCOM() != SL_OK)
471            {
472                throw (new Exception("could not initialize COM"));
473            }
474            else
475            {
476                initializeSucceeded = true;
477            }
478        }
479        catch (Throwable exception)
480        {
481            throw (new Exception("unidentified problem initializing COM\n" + exception.toString()));
482        }
483
484        int successCode = getInterface();
485        if (successCode != SL_OK)
486        {
487            releaseCOM();
488            initializeSucceeded = false;
489
490            if (successCode == SL_OUT_OF_HANDLES)
491            {
492                throw (new Exception(
493                        "could not get an instance of IShellLink, no more handles available"));
494            }
495            else
496            {
497                throw (new Exception(
498                        "could not get an instance of IShellLink, failed to co-create instance"));
499            }
500        }
501    }
502
503    /*--------------------------------------------------------------------------*/
504    /**
505     * Destructor, releases COM and frees native resources.
506     */
507    protected void finalize()
508    {
509        releaseInterface();
510
511        if (initializeSucceeded)
512        {
513            releaseCOM();
514            initializeSucceeded = false;
515        }
516    }
517
518    /*--------------------------------------------------------------------------*/
519    /**
520     * This method is used to free the library at the end of progam execution. After this call, any
521     * instance of this calss will not be usable any more! <b><i><u>Note that this method does NOT
522     * return!</u></i></b> <br>
523     * <br>
524     * <b>DO NOT CALL THIS METHOD DIRECTLY!</b><br>
525     * It is used by the librarian to free the native library before physically deleting it from its
526     * temporary loaction. A call to this method will freeze the application irrecoverably!
527     * 
528     * @param name the name of the library to free. Use only the name and extension but not the
529     * path.
530     * 
531     * @see com.izforge.izpack.util.NativeLibraryClient#freeLibrary
532     */
533    public void freeLibrary(String name)
534    {
535        int result = releaseInterface();
536
537        if (initializeSucceeded)
538        {
539            result = releaseCOM();
540            initializeSucceeded = false;
541        }
542
543        FreeLibrary(name);
544    }
545
546    /*--------------------------------------------------------------------------*/
547    /**
548     * Constructs and returns the full path for the link file.
549     * 
550     * @param userType the type of user for the link path.
551     * 
552     * @return the path to use for storing the link
553     * 
554     * @see #CURRENT_USER
555     * @see #ALL_USERS
556     */
557    private String fullLinkPath(int userType)
558    {
559        StringBuffer path = new StringBuffer();
560
561        // ----------------------------------------------------
562        // build the complete name
563        // ----------------------------------------------------
564        if (userType == CURRENT_USER)
565        {
566            path.append(currentUserLinkPath);
567        }
568        else
569        {
570            path.append(allUsersLinkPath);
571        }
572
573        if ((groupName != null) && (groupName.length() > 0))
574        {
575            path.append(File.separator);
576            path.append(groupName);
577        }
578
579        return (path.toString());
580    }
581
582    /*--------------------------------------------------------------------------*/
583    /**
584     * Constructs and returns the fully qualified name for the link file.
585     * 
586     * @param userType the type of user for the link path.
587     * 
588     * @return the fully qualified file name to use for storing the link
589     * 
590     * @see #CURRENT_USER
591     * @see #ALL_USERS
592     */
593    private String fullLinkName(int userType)
594    {
595        StringBuffer name = new StringBuffer();
596
597        name.append(fullLinkPath(userType));
598
599        name.append(File.separator);
600        name.append(linkName);
601        name.append(LINK_EXTENSION);
602
603        return (name.toString());
604    }
605
606    /*--------------------------------------------------------------------------*/
607    /**
608     * Sets all members on the native side.
609     * 
610     * @exception Exception if any problem is encountered during this operation.
611     */
612    private void set() throws Exception
613    {
614        if (SetArguments() != SL_OK) { throw (new Exception("could not set arguments")); }
615        if (SetDescription() != SL_OK) { throw (new Exception("could not set description")); }
616        if (SetHotkey() != SL_OK) { throw (new Exception("could not set hotkey")); }
617        if (SetIconLocation() != SL_OK) { throw (new Exception("could not set icon location")); }
618        if (SetPath() != SL_OK) { throw (new Exception("could not set target path")); }
619        if (SetShowCommand() != SL_OK) { throw (new Exception("could not set show command")); }
620        if (SetWorkingDirectory() != SL_OK) { throw (new Exception(
621                "could not set working directory")); }
622
623    }
624
625    /*--------------------------------------------------------------------------*/
626    /**
627     * Gets all members from the native side.
628     * 
629     * @exception Exception if any problem is encountered during this operation.
630     * 
631     */
632    private void get() throws Exception
633    {
634        if (GetArguments() != SL_OK) { throw (new Exception("could not get arguments")); }
635        if (GetDescription() != SL_OK) { throw (new Exception("could not get description")); }
636        if (GetHotkey() != SL_OK) { throw (new Exception("could not get hotkey")); }
637        if (GetIconLocation() != SL_OK) { throw (new Exception("could not get icon location")); }
638        if (GetPath() != SL_OK) { throw (new Exception("could not get target ath")); }
639        if (GetShowCommand() != SL_OK) { throw (new Exception("could not get show command")); }
640        if (GetWorkingDirectory() != SL_OK) { throw (new Exception(
641                "could not get working directory")); }
642    }
643
644    /*--------------------------------------------------------------------------*/
645    /**
646     * Sets the name of the program group this ShellLinbk should be placed in.
647     * 
648     * @param groupName the name of the program group
649     */
650    public void setProgramGroup(String groupName)
651    {
652        this.groupName = groupName;
653    }
654
655    /*--------------------------------------------------------------------------*/
656    /**
657     * Sets the command line arguments that will be passed to the target when the link is activated.
658     * 
659     * @param arguments the command line arguments
660     * 
661     * @see #getArguments
662     */
663    public void setArguments(String arguments)
664    {
665        this.arguments = arguments;
666    }
667
668    /*--------------------------------------------------------------------------*/
669    /**
670     * Sets the description string that is used to identify the link in a menu or on the desktop.
671     * 
672     * @param description the descriptiojn string
673     * 
674     * @see #getDescription
675     */
676    public void setDescription(String description)
677    {
678        this.description = description;
679    }
680
681    /*--------------------------------------------------------------------------*/
682    /**
683     * Sets the hotkey that can be used to activate the link.
684     * 
685     * @param hotkey a valid Windows virtual key code. Modifiers (e.g. for alt or shift key) are
686     * added in the upper byte. Note that only the lower 16 bits for tis parameter are used.
687     * 
688     * @see #getHotkey
689     */
690    public void setHotkey(int hotkey)
691    {
692        this.hotkey = hotkey;
693    }
694
695    /*--------------------------------------------------------------------------*/
696    /**
697     * Sets the location of the icon that is shown for the shortcut on the desktop.
698     * 
699     * @param path a fully qualified file name of a file that contains the icon.
700     * @param index the index of the specific icon to use in the file. If there is only one icon in
701     * the file, use an index of 0.
702     * 
703     * @see #getIconLocation
704     */
705    public void setIconLocation(String path, int index)
706    {
707        this.iconPath = path;
708        this.iconIndex = index;
709    }
710
711    /*--------------------------------------------------------------------------*/
712    /**
713     * Sets the absolute path to the shortcut target.
714     * 
715     * @param path the fully qualified file name of the target
716     * 
717     * @see #getTargetPath
718     */
719    public void setTargetPath(String path)
720    {
721        this.targetPath = path;
722    }
723
724    /*--------------------------------------------------------------------------*/
725    /**
726     * Sets the show command that is passed to the target application when the link is activated.
727     * The show command determines if the the window will be restored to the previous size,
728     * minimized, maximized or visible at all. <br>
729     * <br>
730     * <b>Note:</b><br>
731     * Using <code>HIDE</code> will cause the target window not to show at all. There is not even
732     * a button on the taskbar. This is a very useful setting when batch files are used to launch a
733     * Java application as it will then appear to run just like any native Windows application.<br>
734     * <b>Note1:</b><br>
735     * <code>HIDE</code> doesn't work in Win98 and newer systems.<br>
736     * use MINIMIZED (MINNOACTIVE), instead.<br>
737     * 
738     * @param show the show command. Valid settings are: <br>
739     * <ul>
740     * <li><code>ShellLink.HIDE</code> (deprecated)
741     * <li><code>ShellLink.NORMAL</code>
742     * <li><code>ShellLink.MINNOACTIVE</code>
743     * <li><code>ShellLink.MAXIMIZED</code>
744     * </ul>
745     * 
746     * @see #getShowCommand
747     */
748    public void setShowCommand(int show)
749    {
750        if ((show < MIN_SHOW) || (show > MAX_SHOW)) { throw (new IllegalArgumentException(
751                "illegal value for show command " + show)); }
752
753        this.showCommand = show;
754    }
755
756    /*--------------------------------------------------------------------------*/
757    /**
758     * Sets the working directory for the link target.
759     * 
760     * @param dir the working directory
761     * 
762     * @see #getWorkingDirectory
763     */
764    public void setWorkingDirectory(String dir)
765    {
766        this.workingDirectory = dir;
767    }
768
769    /*--------------------------------------------------------------------------*/
770    /**
771     * Sets the name shown in a menu or on the desktop for the link.
772     * 
773     * @param name The name that the link should display on a menu or on the desktop. Do not include
774     * a file extension.
775     */
776    public void setLinkName(String name)
777    {
778        linkName = name;
779    }
780
781    /*--------------------------------------------------------------------------*/
782    /**
783     * Sets the type of link
784     * 
785     * @param type The type of link desired. The following values can be set:<br>
786     * <ul>
787     * <li>{@link #DESKTOP}
788     * <li>{@link #PROGRAM_MENU}
789     * <li>{@link #START_MENU}
790     * <li>{@link #STARTUP}
791     * </ul>
792     * 
793     * @exception IllegalArgumentException if an an invalid type is passed
794     * @throws UnsupportedEncodingException 
795     */
796    public void setLinkType(int type) throws IllegalArgumentException, UnsupportedEncodingException
797    {
798        if ((type < MIN_TYPE) || (type > MAX_TYPE)) { throw (new IllegalArgumentException(
799                "illegal value for type")); }
800
801        linkType = type;
802
803        // set curretnUsersLinkPath, allUsersLinkPath, sets userType to valid.
804        setAllLinkPaths();
805    }
806
807    /**
808     * Returns the user type for the link. <br>
809     * <ul>
810     * <li>{@link #DESKTOP}
811     * <li>{@link #PROGRAM_MENU}
812     * <li>{@link #START_MENU}
813     * <li>{@link #STARTUP}
814     * </ul>
815     * <br>
816     * 
817     * @see #setLinkType
818     */
819    public int getLinkType()
820    {
821        return linkType;
822    }
823
824    /*--------------------------------------------------------------------------*/
825    /**
826     * Sets the (ShellLink) user type for link
827     * 
828     * @param type the type of user for the link.
829     * 
830     * @see #CURRENT_USER
831     * @see #ALL_USERS
832     * 
833     * @exception IllegalArgumentException if an an invalid type is passed
834     */
835    public void setUserType(int type) throws IllegalArgumentException
836    {
837        if ((type == CURRENT_USER) || (type == ALL_USERS))
838        {
839            userType = type;
840        }
841        else
842        {
843            throw (new IllegalArgumentException(type + " is not a recognized user type"));
844        }
845    }
846
847    /*--------------------------------------------------------------------------*/
848    /**
849     * Returns the (ShellLink) user type for the link. Either {@link #CURRENT_USER} or
850     * {@link #ALL_USERS}
851     * 
852     * @see #setUserType
853     */
854    public int getUserType()
855    {
856        return userType;
857    }
858
859    /*--------------------------------------------------------------------------*/
860    /**
861     * Returns the path where the links of the selected type are stroed. This method is useful for
862     * discovering which program groups already exist.
863     * 
864     * @param userType the type of user for the link path. One of {@link #CURRENT_USER} or
865     * {@link #ALL_USERS}
866     * 
867     * @return the path to the type of link set for this instance.
868     */
869    public String getLinkPath(int userType)
870    {
871        String result = null;
872        if (userType == CURRENT_USER)
873        {
874          result = currentUserLinkPath;           
875        }
876        else
877        {
878          result = allUsersLinkPath;            
879        }
880        return result;
881    }
882
883    /*--------------------------------------------------------------------------*/
884    /**
885     * Returns the command line that the link passes to the target.
886     * 
887     * @return the command line
888     * 
889     * @see #setArguments
890     */
891    public String getArguments()
892    {
893        return (arguments);
894    }
895
896    /*--------------------------------------------------------------------------*/
897    /**
898     * Returns the description for the link.
899     * 
900     * @return the description
901     * 
902     * @see #setDescription
903     */
904    public String getDescription()
905    {
906        return (description);
907    }
908
909    /*--------------------------------------------------------------------------*/
910    /**
911     * Retruns the hotkey that can be used to activate the link.
912     * 
913     * @return the virtual keycode for the hotkey
914     * 
915     * @see #setHotkey
916     */
917    public int getHotkey()
918    {
919        return (hotkey);
920    }
921
922    /*--------------------------------------------------------------------------*/
923    /**
924     * Returns the path and file name of the file that contains the icon that is associated with the
925     * link.
926     * 
927     * @return the path to the icon
928     * 
929     * @see #setIconLocation
930     */
931    public String getIconLocation()
932    {
933        return (iconPath);
934    }
935
936    /*--------------------------------------------------------------------------*/
937    /**
938     * Returns the index of the icon with the icon or resource file
939     * 
940     * @return the index
941     * 
942     * @see #setIconLocation
943     */
944    public int getIconIndex()
945    {
946        return (iconIndex);
947    }
948
949    /*--------------------------------------------------------------------------*/
950    /**
951     * Retruns the absolute path of the link target
952     * 
953     * @return the path
954     * 
955     * @see #setTargetPath
956     */
957    public String getTargetPath()
958    {
959        return (targetPath);
960    }
961
962    /*--------------------------------------------------------------------------*/
963    /**
964     * Returns the initial condition of the target window (HIDE, NORMAL, MINIMIZED, MAXIMIZED).
965     * 
966     * @return the target show command
967     * 
968     * @see #setShowCommand
969     */
970    public int getShowCommand()
971    {
972        return (showCommand);
973    }
974
975    /*--------------------------------------------------------------------------*/
976    /**
977     * Retruns the working deirectory for the link target.
978     * 
979     * @return the working directory
980     * 
981     * @see #setWorkingDirectory
982     */
983    public String getWorkingDirectory()
984    {
985        return (workingDirectory);
986    }
987
988    /*--------------------------------------------------------------------------*/
989    /**
990     * Returns the fully qualified file name under which the link is saved on disk. <b>Note:</b>
991     * this method returns valid results only if the instance was created from a file on disk or
992     * after a successful save operation.
993     * 
994     * @return the fully qualified file name for the shell link
995     */
996    public String getFileName()
997    {
998        return (linkFileName);
999    }
1000
1001    /*--------------------------------------------------------------------------*/
1002    /**
1003     * Returns the path of the directory where the link file is stored, if it was necessary during
1004     * the previous save operation to create the directory. This method returns <code>null</code>
1005     * if no save operation was carried out or there was no need to create a directory during the
1006     * previous save operation.
1007     * 
1008     * @return the path of the directory where the link file is stored or <code>null</code> if no
1009     * save operation was carried out or there was no need to create a directory during the previous
1010     * save operation.
1011     */
1012    public String getDirectoryCreated()
1013    {
1014        return (linkDirectory);
1015    }
1016
1017    /*--------------------------------------------------------------------------*/
1018    /**
1019     * Returns the name shown in a menu or on the desktop for the link.
1020     * 
1021     * @return the name
1022     */
1023    public String getLinkName()
1024    {
1025        return (linkName);
1026    }
1027
1028    /*--------------------------------------------------------------------------*/
1029    /**
1030     * Returns the path for currentusersLink
1031     * 
1032     * @return currentUsersLinkPath
1033     */
1034    public String getcurrentUserLinkPath()
1035    {
1036      return currentUserLinkPath;
1037    }
1038
1039    /*--------------------------------------------------------------------------*/
1040    /**
1041     * Returns the path for allusersLink
1042     * 
1043     * @return allusersLinkPath
1044     */
1045    public String getallUsersLinkPath()
1046    {
1047       return allUsersLinkPath;
1048    }
1049
1050    /*--------------------------------------------------------------------------*/
1051    /**
1052     * Saves this link.
1053     * 
1054     * @exception Exception if problems are encountered
1055     */
1056    public void save() throws Exception
1057    {
1058        // set all values on the native side
1059        set();
1060
1061        // make sure the target actually resolves
1062        int result = Resolve();
1063
1064        if (result != SL_OK) { throw (new Exception("cannot resolve target")); }
1065
1066        // make sure the directory exists
1067        File directory = new File(fullLinkPath(userType));
1068
1069        if (!directory.exists())
1070        {
1071            directory.mkdirs();
1072            linkDirectory = directory.getPath();
1073        }
1074        else
1075        {
1076            linkDirectory = "";
1077        }
1078
1079        // perform the save operation
1080        String saveTo = fullLinkName(userType);
1081
1082        result = saveLink(saveTo);
1083
1084        if (result == SL_NO_IPERSIST)
1085        {
1086            throw (new Exception("could not get handle for IPesist"));
1087        }
1088        else if (result == SL_NO_SAVE) { throw (new Exception("the save operation failed")); }
1089
1090        linkFileName = saveTo;
1091    }
1092
1093    /*--------------------------------------------------------------------------*/
1094    /**
1095     * Saves this link to any desired location.
1096     * 
1097     * @param name the fully qualified file name for the link
1098     * 
1099     * @exception IllegalArgumentException if the parameter was null
1100     * @exception Exception if the save operation could not be carried out
1101     */
1102    public void save(String name) throws Exception
1103    {
1104        if (name == null) { throw (new IllegalArgumentException("name was null")); }
1105
1106        // set all values on the native side
1107        set();
1108
1109        // make sure the target actually resolves
1110        if (Resolve() != SL_OK) { throw (new Exception("cannot resolve target")); }
1111
1112        // make sure the directory exists
1113        File directory = new File(name.substring(0, name.lastIndexOf(File.separatorChar)));
1114        if (!directory.exists())
1115        {
1116            directory.mkdirs();
1117            linkDirectory = directory.getPath();
1118        }
1119        else
1120        {
1121            linkDirectory = null;
1122        }
1123
1124        // perform the save operation
1125        if (saveLink(name) != SL_OK) { throw (new Exception("the save operation failed")); }
1126
1127        linkFileName = name;
1128    }
1129
1130    /*--------------------------------------------------------------------------*/
1131    /**
1132     * sets currentUsersLinkPath and allUsersLinkPath. If the path is empty, resets userType to a
1133     * valid userType for this type of link. If no linkPaths are valid, an IllegalArgumentException
1134     * is thrown.
1135     * 
1136     * @throws IllegalArgumentException
1137     * @throws UnsupportedEncodingException 
1138     */
1139    private void setAllLinkPaths() throws IllegalArgumentException
1140    {
1141        // sets currentUsersLinkPath and allUsersLinkPath
1142        GetFullLinkPath(CURRENT_USER, linkType);
1143        GetFullLinkPath(ALL_USERS, linkType);
1144
1145        // be sure userType is valid. Override initial choice if not.
1146        if (userType == CURRENT_USER && currentUserLinkPath.length() == 0)
1147        {
1148            userType = ALL_USERS;
1149        }
1150        else if (userType == ALL_USERS && allUsersLinkPath.length() == 0)
1151        {
1152            userType = CURRENT_USER;
1153        }
1154
1155        if ( allUsersLinkPath.length() == 0 && currentUserLinkPath.length() == 0) { throw (new IllegalArgumentException(
1156                "linkType " + linkType + " is invalid.")); }
1157    }
1158
1159}
1160/*---------------------------------------------------------------------------*/
1161