/*
 * Copyright (C) 2004 Sun Microsystems, Inc. All rights reserved. Use is
 * subject to license terms.
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the Lesser GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA.
 */ 

package org.jdesktop.jdic.filetypes.internal;

import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.jdesktop.jdic.filetypes.Action;
import org.jdesktop.jdic.filetypes.RegisterFailedException;


/**
 * Utility class for accessing the system registry and association info for Windows.
 */
public class WinRegistryUtil {
    
    /* Draft to illustrate the Registry items relationship
     HKEY_CLASSES_ROOT
     |           Key Nmae              Valuename         Value
     |_.html //file extension             ""            htmlfile    //clsID
     |                                  Content Type    text/html   //MIME type (optional)
     |                                            
     ... ...
     |_htmlfile //clsID                   ""              HTML Document //Description
     |   |
     |   |_DefaultIcon                    ""              "c:\program files\..." //Default Icon file name
     |   |
     |   |_shell //shell command list     ""              opennew       //Default shell command (optional)
     |       |
     |       |_Edit //shell command       ""              Edit(&E)      //Command Description
     |       |   |
     |       |   |_Command // content     ""              "c:\Program files\..." //real command
     |       |
     |       |_opennew                    ""              Open (&O)
     |           |
     |           |_Command                ""              "c:\Program files\..."
     ... ...
     |_MIME //MIME Info
     |   |
     |   |_Database
     |       |
     |       |_Content Type
     |           |
     |           |_text/html  //MIME     Extension       .htm        //corresponding file extension           
     | 
     */

    // Constant value to generate a classID name 
    private static final int MAX_CLSID_NUMBER = 1000;
  
    // general constants
    private final static int ERROR_SUCCESS         = WinRegistryWrapper.ERROR_SUCCESS;
    private final static int ERROR_ITEM_EXISTED    = WinRegistryWrapper.ERROR_ITEM_EXIST;
    private final static int ERROR_ITEM_NOTEXISTED = WinRegistryWrapper.ERROR_ITEM_NOTEXIST;
    private final static int MAX_KEY_LENGTH        = WinRegistryWrapper.MAX_KEY_LENGTH;
    private final static String VN_DEFAULT         = "";             // default value name
  
    // Constants for USERKEY & SYSKEY
    private final static String SYS_USER_KN_PREFIX = "SOFTWARE\\Classes";
        
    // Constants for file extension
    private final static String VN_CONTENT         = "Content type";
    
    // Constants under clsID item
    private final static String KN_DEFAULTICON         = "DefaultIcon"; // key name to denote the default icon file name
    private final static String VN_DEFAULTGENERATOR    = "Generated By"; //key name to specify the generator
    private final static String VALUE_DEFAULTGENERATOR = "Generated By Java-Association"; //Value to specify the generator
    private final static String KN_SHELL               = "shell";             // key name for the shell command list
    private final static String KN_COMMAND             = "command";         // key name to denote the actual command string
    private final static String KN_CURVER              = "CurVer";          // key name for CurVer
  
    // Constants reletive to the MIME information    
    private final static String KN_MIME      = "MIME\\Database\\Content Type";            // key name to locate mime type
    private final static String VN_EXTENSION = "Extension";  // value name to indicate mime type corresponding file extension
    
    // Constants for the Windows 2000 user added file extension
    private final static String USER_FILE_EXT_KEY_PREFIX = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts";
    private final static String USER_FILE_EXT_VALUENAME  = "Application";
    private final static String USER_FILE_EXT_APP_PREFIX = "Applications";
    
  
    // Constants for the level designation
    private final static int USER_LEVEL = AppConstants.USER_LEVEL;
    private final static int SYSTEM_LEVEL = AppConstants.SYSTEM_LEVEL;
    private final static int ROOT_LEVEL = AppConstants.DEFAULT_LEVEL;
    
    // Constants for the OS Name
    private final static String osName = System.getProperty("os.name");
    private final static String WIN2KOS = "Windows 2000";

    private WinRegistryUtil() {}
    
    /**
     * Gets the corresponding Windows Registry folder name according to the regLevel.
     *
     * @param regLevel given regLeve
     * @return corresponding windows registry folder constants
     */  
    private static int getHKeyByLevel(int regLevel) {
        int hKey;

        switch (regLevel) {
        case USER_LEVEL:    /* USER_LEVEL -->HKEY_CURRENT_USER */
            hKey = WinRegistryWrapper.HKEY_CURRENT_USER;
            break;

        case SYSTEM_LEVEL:  /* SYSTEM_LEVEL-->HKEY_LOCAL_MACHINE */
            hKey = WinRegistryWrapper.HKEY_LOCAL_MACHINE;
            break;

        case ROOT_LEVEL:    /* ROOT_LEVEL-->HKEY_CLASSES_ROOT */
            hKey = WinRegistryWrapper.HKEY_CLASSES_ROOT;
            break;
            
        default:            /* Default will be HKEY_CLASSES_ROOT */
            hKey = WinRegistryWrapper.HKEY_CLASSES_ROOT;
            break;
        } 
        return hKey;
    }
  
    /**
     *  Constructs a new class ID name based on the given fileExt name
     *  by adding a sequence number at the rear of the given fileExt.
     *
     *  @param fileExt given file extension (not null)
     *  @param regLevel given regLevel
     *  @return the constructed class ID name string, or null if construction
     *  failed
     */
    private static String genClassID(String fileExt, int regLevel) {
        boolean isClsIDExist = true;
        String appendix, temClsID, temClsIDKey, temFileExt;

        temFileExt = fileExt.trim();
        if (temFileExt.charAt(0) == '.') {
            temFileExt = temFileExt.substring(1);
        }
        int i = 1;

        while ((isClsIDExist) && (i < MAX_CLSID_NUMBER)) {
            appendix = Integer.toString(i);
            // construct the temporary ClsID
            temClsID = "class" + temFileExt + appendix;
            if (temClsID != null) {
                // Get the corresponding windows registry key 
                temClsIDKey = getClsIDKey(temClsID, regLevel);
                if (temClsIDKey != null) {
                    // Check if this key exists
                    isClsIDExist = isSubKeyExist(temClsIDKey, regLevel);
                    if (!isClsIDExist) {
                        // Not exists, return the key
                        return temClsID; 
                    }
                }
            }
            i++;
        }
        
        return null;
    }  
  
    /**
     * Wrapper method for WinRegistryWrapper.WinRegSubKeyExist.
     *
     * @param subKey name of the key (not null)
     * @param regLevel given regLevel
     * @return true if the subKey exists
     */
    private static boolean isSubKeyExist(String subKey, int regLevel) {
        int hKey = getHKeyByLevel(regLevel);

        if (WinRegistryWrapper.WinRegSubKeyExist(hKey, subKey)
                == ERROR_ITEM_EXISTED) {
            return true;
        } else {
            return false;
        }
    }
  
    /**
     * Returns true if the specified subkey and value exist.
     *
     * @param subKey given key name (not null).
     * @param valueName given value name (not null).
     * @param regLevel given regLevel.
     * @return true if the given key contains the specified valuename.
     */ 
    private static boolean isValueExist(String subKey, String valueName, int regLevel) {
        if (isSubKeyExist(subKey, regLevel)) {
            int hKey = getHKeyByLevel(regLevel);

            if (WinRegistryWrapper.WinRegValueExist(hKey, subKey, valueName)
                    == ERROR_ITEM_EXISTED) {
                return true;
            }
        }
        
        return false;
    }

    /**
     * Returns the corresponding mime key in the Windows registry table.
     *
     * @param mimeType specified mime type (not null)
     * @regLeve given regLevel
     * @return corresponding mime type key
     */  
    private static String getMimeTypeKey(String mimeType, int regLevel) {
        // MIME\\Database\\Content Type\\***
        String mimeSubKey = KN_MIME + "\\" + mimeType;   
       
        if (regLevel != ROOT_LEVEL) {
            // software\\classes\\MIME\\Database\\Content Type\\***
            mimeSubKey = SYS_USER_KN_PREFIX + "\\" + mimeSubKey; 
        }
        return mimeSubKey;
    }
  
    /**
     * Returns the corresponding file extension key in the Windows registry table.
     *
     * @param fileExt given file extension (not null)
     * @param regLevel given reglevel
     * @return corresponding file extension key
     */  
    private static String getFileExtKey(String fileExt, int regLevel) {
        String fileExtKey = fileExt;   

        if (regLevel != ROOT_LEVEL) {
            // software\\classes\\***
            fileExtKey = SYS_USER_KN_PREFIX + "\\" + fileExtKey; 
        }
        return fileExtKey;
    }
  
    /**
     * Returns the corresponding class ID key in the Windows registry table.
     *
     * @param clsID given class ID (not null)
     * @param regLevel given regLevel
     * @return corresponding class ID key
     */  
    private static String getClsIDKey(String clsID, int regLevel) {
        String clsIDKey = clsID;   

        if (regLevel != ROOT_LEVEL) {
            // software\\classes\\***
            clsIDKey = SYS_USER_KN_PREFIX + "\\" + clsIDKey; 
        }
        return clsIDKey;
    }

    /**
     * Returns the corresponding icon key name of the specified file extension.
     *
     * @param fileExt given file extension (not null)
     * @param regLevel given regLevel
     * @return corresponding icon key
     */  
    private static String getIconKey(String fileExt, int regLevel) {
        // Retrieve the relevant class ID
        String clsID = getClassIDByFileExt(fileExt, regLevel);

        if (clsID != null) {
            //* clsID\\DefaultIcon
            String iconKey = clsID + "\\" + KN_DEFAULTICON;
            if (regLevel != ROOT_LEVEL) {
                // software\\classes\\clsID\\DefaultIcon
                iconKey = SYS_USER_KN_PREFIX + "\\" + iconKey; 
            }
            return iconKey;
        }
        return null; 
    }
  
    /**
     * Wrapper method for WinRegistryWrapper.WinRegCreateKeyEx.
     *
     * @param subKey name of the key (not null)
     * @param regLevel given regLevel
     * @throws RegisterFailedException if the operation fails.
     */
    private static void regCreateKeyEx(String subKey, int regLevel) 
        throws RegisterFailedException {
        // Retrieve the relevant key class
        int hKey = getHKeyByLevel(regLevel);
        // Create the subKey under hKey
        if (WinRegistryWrapper.WinRegCreateKeyEx(hKey, subKey)
                != ERROR_SUCCESS) {
            throw new RegisterFailedException("Key " + subKey
                    + " creation error!"); 
        }
    }
  
    /**
     * Wrapper method for WinRegistryWrapper.WinRegDeleteKey.
     *
     * @param subKey name of the key (not null)
     * @param regLevel given registry level
     * @throws RegisterFailedException if the operation fails.
     */
    private static void regDeleteKey(String subKey, int regLevel) 
        throws RegisterFailedException {
        // Retrieves the relevant windows registry key handle
        int hKey = getHKeyByLevel(regLevel);
        
        if (WinRegistryWrapper.WinRegDeleteKey(hKey, subKey)
                != ERROR_SUCCESS) {
            throw new RegisterFailedException("Key " + subKey
                    + " delete error."); 
        }
    }
  
    /**
     * Wrapper method for WinRegistryWrapper.WinRegQueryValueEx.
     *
     * @param subKey name of the key (not null)
     * @param valueName name of the value (not null)
     * @param regLevel given regLevel
     * @return content of the specified <subKey, valueName>
     */
    private static String regQueryValueEx(String subKey, String valueName, int regLevel) {
        // if the value for the given subkey exist, retieve
        if (isValueExist(subKey, valueName, regLevel)) {
            int hKey = getHKeyByLevel(regLevel);

            return WinRegistryWrapper.WinRegQueryValueEx(hKey, subKey,
                    valueName);
        }
        return null;
    }
  
    /**
     * Wrapper method for WinRegistryWrapper.WinRegSetValueEx.
     *
     * @param subKey name of the key (not null)
     * @param valueName name of the value (not null)
     * @param value content of the value (not null)
     * @param regLevel given regLevel
     * @throws RegisterFailedException if the operation fails.
     */
    private static void regSetValueEx(String subKey, String valueName, String value, 
        int regLevel) throws RegisterFailedException {
        // if the given subkey does not exists, create it first
        if (!isSubKeyExist(subKey, regLevel)) {
            regCreateKeyEx(subKey, regLevel);
        }
        int hKey = getHKeyByLevel(regLevel);

        if (WinRegistryWrapper.WinRegSetValueEx(hKey, subKey, valueName,
                value) != ERROR_SUCCESS) {
            throw new RegisterFailedException("Value:" + " valueName"
                    + " setting error");
        }
    }
  
    /**
     * Wrapper method for WinRegistryWrapper.WinRegGetSubKeys.
     *
     * @param subKey name of the key (not null)
     * @param regLevel given regLevel
     * @return string array containing the sub keys
     * @exception RegisterFailedException
     */
    private static String[] regGetSubKeys(String subKey, int regLevel) {
        int hKey = getHKeyByLevel(regLevel);

        return WinRegistryWrapper.WinRegGetSubKeys(hKey, subKey,
                MAX_KEY_LENGTH);
    }
  
    /**
     * Gets the default value for a specified key.
     *
     * @param subKey Name of the key (not null)
     * @param regLevel given regLevel
     * @return content of the default value
     * @exception RegisterFailedException
     */
    private static String getDefaultValue(String subKey, int regLevel) {
        int hKey = getHKeyByLevel(regLevel);

        return WinRegistryWrapper.WinRegQueryValueEx(hKey, subKey,
                VN_DEFAULT); 
    }
  
    /**
     * Sets the default value for a specified key.
     *
     * @param subKey Name of the key (not null)
     * @param value Value to be set (not null)
     * @param regLevel given regLevel
     * @throws RegisterFailedException if the operation fails.
     */
    private static void setDefaultValue(String subKey, String value, int regLevel) 
        throws RegisterFailedException {
        // if the specified subKey does not exist, create it first
        if (!isSubKeyExist(subKey, regLevel)) {
            regCreateKeyEx(subKey, regLevel);
        }
        int hKey = getHKeyByLevel(regLevel);

        if (WinRegistryWrapper.WinRegSetValueEx(hKey, subKey, VN_DEFAULT,
                value) != ERROR_SUCCESS) {
            throw new RegisterFailedException("Set default value"
                    + " for key " + subKey + " error.");
        }
        WinRegistryWrapper.WinRegFlushKey(hKey, subKey);
    }
  
    /**
     * Writes an action into the registry table 
     * (From the specified registry folder).
     *
     * @param action given action to be added (not null)
     * @param clsID given class ID (not null)
     * @param regLevel given regLevel
     * @throws RegisterFailedException if the operation fails
     */
    private static void addActionByClsID(Action action, String clsID, int regLevel) 
        throws RegisterFailedException {
        String verb = action.getVerb();
        String desc = action.getDescription();
        String cmd = action.getCommand();
    
        String clsIDKey = getClsIDKey(clsID, regLevel);
        String shellKey = clsIDKey + "\\" + KN_SHELL;
        String verbKey = shellKey + "\\" + verb;
        String cmdKey = verbKey + "\\" + KN_COMMAND;
    
        if (cmdKey != null) {
            regCreateKeyEx(cmdKey, regLevel);
            if (cmd != null) {
                setDefaultValue(cmdKey, cmd, regLevel);
                if ((desc != null) && (verbKey != null)) {
                    setDefaultValue(verbKey, desc, regLevel);
                }
            }
        }
    
    }
  
    /**
     * Returns the mime type associated with the given file extension
     * (From the given registry folder).
     *
     * @param fileExt given file extension (not null)
     * @param regLevel given regLevel
     * @return corresponding mime type, or null if not exists
     */
    public static String getMimeTypeByFileExt(String fileExt, int regLevel) {
        String fileExtKey = getFileExtKey(fileExt, regLevel);
        if (fileExtKey != null) {
            return regQueryValueEx(fileExtKey, VN_CONTENT, regLevel);    
        } else {
            return null;
        }
    }
  
    /**
     * Returns the mime type information associated with the given file extension
     * (From HKEY_ROOT registry level).
     *
     * @param fileExt given file extension (not null)
     * @return corresponding mime type, or null if not exists
     */
    public static String getMimeTypeByFileExt(String fileExt) {
        return (getMimeTypeByFileExt(fileExt, ROOT_LEVEL));
    }

    /**
     * Sets the mime type associated with the given file extension.
     *
     * @param mimeType given mime type (not null)
     * @param fileExt given file extension (not null)
     * @param regLevel given reglevel
     * @throws RegisterFailedException if the given operation fails.
     */
    public static void setMimeTypeByFileExt(String mimeType, String fileExt, 
        int regLevel) throws RegisterFailedException {
        String fileExtKey = getFileExtKey(fileExt, regLevel);
        if (fileExtKey != null) {
            // set the content value
            regSetValueEx(fileExtKey, VN_CONTENT, mimeType, regLevel);
        }
    }
  
    /**
     * Returns the file extensione associated with the given mime type
     * (From the given registry folder).
     *
     * @param mimeType given mime type (not null)
     * @param regLevel given reglevel
     * @return corresponding file extension, or null if none
     */
    public static String getFileExtByMimeType(String mimeType, int regLevel) {
        String mimeSubKey = getMimeTypeKey(mimeType, regLevel);
        if (mimeSubKey != null) {
            return regQueryValueEx(mimeSubKey, VN_EXTENSION, regLevel);    
        } else {
            return null;
        }
    }
  
    /**
     * Returns the file extension associated with the given mime type
     * (From HKEY_ROOT registry folder).
     *
     * @param mimeType given mime type (not null)
     * @return corresponding file extension, or null if none
     */
    public static String getFileExtByMimeType(String mimeType) {
        return (getFileExtByMimeType(mimeType, ROOT_LEVEL));
    }

    /**
     * Sets the file extensione associated with the given mime type.
     *
     * @param fileExt given file extension (not null)
     * @param mimeType given mime type (not null)
     * @param regLevel given regLevel
     * @throws RegisterFailedException if the operation fails.
     */
    public static void setFileExtByMimeType(String fileExt, String mimeType, int regLevel) 
        throws RegisterFailedException {
        String mimeSubKey = getMimeTypeKey(mimeType, regLevel);
        if (mimeSubKey != null) {
            regSetValueEx(mimeSubKey, VN_EXTENSION, fileExt, regLevel);
        }
    }
  
    /**
     * Returns the icon file name associated with the given file extension.
     *
     * @param fileExt given file extension (not null)
     * @param regLevel given regLevel
     * @return corresponding icon file, or null if none
     */
    public static String getIconFileNameByFileExt(String fileExt, int regLevel) {
        // Retrieve the icon key
		String iconKey = getIconKey(fileExt, regLevel);
		if (iconKey == null) {
			return null;
		}
		String unDealedFileName = getDefaultValue(iconKey, regLevel);
		if (unDealedFileName == null) {
			return null;
		}
		return ExpandEnvironmentStrings(unDealedFileName);
	}
    
    /**
	 * Retrievs the icon file of the specified file extension (From HKEY_ROOT
	 * folder).
	 * 
	 * @param fileExt
	 *            given file extension (not null)
	 * @return corresponding icon file, or null if none
	 */
    public static String getIconFileNameByFileExt(String fileExt) {
        return (getIconFileNameByFileExt(fileExt, ROOT_LEVEL));
    }

    /**
     * Sets the icon file name associated with the given file extension.
     *
     * @param iconFileName given icon file name (not null)
     * @param fileExt given file extension name (not null)
     * @param regLevel given regLevel
     * @throws RegisterFailedException if the operation fails.
     */
    public static void setIconFileNameByFileExt(String iconFileName, String fileExt, 
        int regLevel) throws RegisterFailedException {
        // Get the icon key
        String iconKey = getIconKey(fileExt, regLevel);
        if (iconKey == null) { 
            // If the classID do not created yet, create it first
            String temClassID = genClassID(fileExt, regLevel);
            if (temClassID != null) {
               setClassIDByFileExt(fileExt, temClassID, regLevel);
               iconKey = getIconKey(fileExt, regLevel);
            }
        }
        if (iconKey != null) {
            // Set the default value of the iconkye as the iconfile
            setDefaultValue(iconKey, iconFileName, regLevel);
        }
    }

    /**
     * Returns the description associated with the given file extension.
     *
     * @param fileExt given file extension (not null)
     * @param regLevel given regLevel
     * @return corresponding description about the file extension, or null if none
     */
    public static String getDescriptionByFileExt(String fileExt, int regLevel) {
        // Retrievs the class ID
        String classID = getClassIDByFileExt(fileExt, regLevel);  
        if (classID != null) {
            String clsIDKey = getClsIDKey(classID, regLevel);
            if (clsIDKey != null) {
                // The default value of the class ID key is the description
                return getDefaultValue(clsIDKey, regLevel);
            }
        } 
        return null;
    }
  
    /**
     * Returns the description of the given file extension
     * (From HKEY_ROOT registry folder).
     *
     * @param fileExt given file extension (not null)
     * @return corresponding description about the file extension, or null if none
     */
    public static String getDescriptionByFileExt(String fileExt) {
        return (getDescriptionByFileExt(fileExt, ROOT_LEVEL));
    }

    /**
     * Sets the Description associated with the given file extension.
     *
     * @param description given description (not null)
     * @param fileExt given file extension name (not null)
     * @param regLevel given registeration level
     * @throws RegisterFailedException if the operation fails.
     */
    public static void setDescriptionByFileExt(String description, String fileExt, 
        int regLevel) throws RegisterFailedException {
        String classID = getClassIDByFileExt(fileExt, regLevel);
        if (classID == null) { 
            // If the classID does not exist, create it first
            classID = genClassID(fileExt, regLevel);
            if (classID != null) {
                setClassIDByFileExt(fileExt, classID, regLevel);
            }
        }
        if (classID != null) {
            String clsIDKey = getClsIDKey(classID, regLevel);
            if (clsIDKey != null) {
                // Default value of the class ID key will be the description
                setDefaultValue(clsIDKey, description, regLevel);
            }
        }
    }

    /**
     * Marks the generator value field of the classID key of the given file extension.
     *
     * @param fileExt given file extension name (not null)
     * @param regLevel given registeration level
     * @throws RegisterFailedException if the operation fails.
     */
    public static void markGeneratorByFileExt(String fileExt, int regLevel) 
        throws RegisterFailedException {
        //Get the corresponding class ID key
        String clsID = getClassIDByFileExt(fileExt, regLevel);
        String clsIDKey = getClsIDKey(clsID, regLevel);
        if (clsIDKey != null) {
            regSetValueEx(clsIDKey, VN_DEFAULTGENERATOR, VALUE_DEFAULTGENERATOR, regLevel);
        }
    }
    
    /**
     * Returns the action list associated with the given file extension
     * (From specified registiry folder).
     *
     * @param fileExt given file extension (not null)
     * @return the action list
     */
    public static List getActionListByFileExt(String fileExt, int regLevel) {
        List actionList = null; 
    
        // Retrievs the relevant class ID
        String clsID = getClassIDByFileExt(fileExt, regLevel);

        if (clsID!= null) {
            String clsIDKey = getClsIDKey(clsID, regLevel);
            String shellKey = clsIDKey + "\\" + KN_SHELL;
            String verbs[] = null;
            if (shellKey != null) {
                verbs = regGetSubKeys(shellKey, regLevel);
            }

            if (verbs != null) {
                int verbsNum = verbs.length;
                // Construct relevant actions one by one
                if (verbsNum > 0) {
                    actionList = new ArrayList();
                    for (int i = 0; i < verbsNum; i++) {
                        String verbKey = shellKey + "\\" + verbs[i];
                        String cmdKey = verbKey + "\\" + KN_COMMAND;
                        if (cmdKey != null) {
                            Action oneAction;
                            String temCmd = getDefaultValue(cmdKey, regLevel);
                            //In case cmd is a null string, we shall replace it with a empty string
                            if (temCmd == null) {
                                temCmd = "";
                            } else {
                            	temCmd = ExpandEnvironmentStrings(temCmd);
                            }                            
                            oneAction = new Action(verbs[i], temCmd, getDefaultValue(verbKey, regLevel));
                            actionList.add(oneAction);
                        }
                    }
                }
            } 
        } 
        
        return actionList;
    }
  
    /**
     * Returns the action list associated with the specified file extension 
     * (From HKEY_ROOT).
     *
     * @param fileExt given file extension (not null)
     * @return the action list
     */
    public static List getActionListByFileExt(String fileExt) {
        List rootActionList = getActionListByFileExt(fileExt, ROOT_LEVEL);
        List userDefinedList = getUserAddedActionListByFileExt(fileExt);
        if (userDefinedList != null) {
            return userDefinedList;
        } else {
            return rootActionList;
        }
    }
    
    /**
     * Returns the action list associated with the user defined file extension
     * <p>
     * <B>Note:</B> Windows 2000 will save user added file extension under a
     * special place.
     * For example, user add a new file extension .aoo and specify notepad.exe 
     * as the default application for this file type.
     * The file extension information will be stored at 
     * HKEY_CURRENT_USER\software\Microsoft\Windows\CurrentVersion\
     * Explorer\FileExts\.aoo
     * with data as
     * Application  REG_SZ  notepad.exe
     * the notepad.exe will be stored at
     * HKEY_CLASSES_ROOT\Applications\notepad.exe
     * <p>
     * 
     * @param fileExt given file extension(no null)
     * @return the action list
     */
    private static List getUserAddedActionListByFileExt(String fileExt) {
        String fileExtKey = USER_FILE_EXT_KEY_PREFIX + "\\" + fileExt;
        String valueName = USER_FILE_EXT_VALUENAME;
        int hKey = WinRegistryWrapper.HKEY_CURRENT_USER;
        //Get application name, e.g. notepad.exe
        String appName = WinRegistryWrapper.WinRegQueryValueEx(hKey, fileExtKey, valueName);
        //Get the corresponding application's shell keys
        String verbs[] = null;
        String appShellKey = USER_FILE_EXT_APP_PREFIX + "\\" + appName + "\\" + KN_SHELL;
        hKey = WinRegistryWrapper.HKEY_CLASSES_ROOT;
        verbs = WinRegistryWrapper.WinRegGetSubKeys(hKey, appShellKey, 255);
        
        //Construct relevant actions one by one        
        List actionList = null;
        if (verbs != null) {
            int verbsNum = verbs.length;
            if (verbsNum > 0) {
                actionList = new ArrayList();
                for (int i = 0; i < verbsNum; i++) {
                    String verbKey = appShellKey + "\\" + verbs[i];
                    String cmdKey = verbKey + "\\" + KN_COMMAND;
                    if (cmdKey != null) {
                        Action oneAction;
                        String temCmd = getDefaultValue(cmdKey, ROOT_LEVEL);
                        //In case cmd is a null string, we shall replace it with a empty string
                        if (temCmd == null) {
                            temCmd = "";
                        }else {
                    	    temCmd = ExpandEnvironmentStrings(temCmd);
                        }
                        oneAction = new Action(verbs[i], temCmd, getDefaultValue(verbKey, ROOT_LEVEL));
                        actionList.add(oneAction);
                    }
                }
            }
        } 
        
        return actionList;
    }
          
    /**
     * Sets the action list associated with the given file extension.
     *
     * @param actionList given action list (not null)
     * @param fileExt given file extension (not null)
     * @param regLevel given regLevel
     * @throws RegisterFailedException if the operation fails.
     */
    public static void setActionListByFileExt(List actionList, String fileExt, 
        int regLevel) throws RegisterFailedException {
        // Retrieves the corresponding class ID
        String clsID = getClassIDByFileExt(fileExt, regLevel);
        if (clsID == null) { 
            // If the classID does not exist, create it first
            clsID = genClassID(fileExt, regLevel);
            if (clsID != null) {
                // Bundle the file extension and class ID
                setClassIDByFileExt(fileExt, clsID, regLevel);
            }
        }
        
        Action oneAction;
        if (clsID != null) {
            if (actionList != null) {
                Iterator actionIter = actionList.iterator();
                // Add action to under the class ID key one by one
                while (actionIter.hasNext()) {
                    oneAction = (Action) actionIter.next();
                    if ((oneAction != null) && (clsID != null)) {
                        addActionByClsID(oneAction, clsID, regLevel);
                    }
                }  
            }
        }
    }
  
    /**
     * Returns the mime type associated with the given URL
     * by checking the content of the URL.
     *
     * @param url given URL (not null)
     * @return corresponding mime type
     */
    public static String getMimeTypeByURL(URL url) {
        return WinRegistryWrapper.WinFindMimeFromData(url);
    }

    /**
     * Expands environment-variable strings and replaces them with their defined values
     * E.g: "%SystemRoot%\\system32\\NOTEPAD.EXE %1" -> "C:\\system32\\NOTEPAD.EXE %1".
     *
     * @param cmdString given command string (not null) 
     * @return String
     */
    public static String ExpandEnvironmentStrings(String cmdString) {
        return WinRegistryWrapper.WinExpandEnvironmentStrings(cmdString);
    }    
  
    /**
     * Returns true if the specified mime type exists in the Windows registry table.
     *
     * @param mimeType given mime type (not null)
     * @param regLevel given regLevel  
     * @return true if the mime type exists in the registry table
     */
    public static boolean isMimeTypeExist(String mimeType, int regLevel) {
        // Retrieve the relevant mime type key
        String mimeTypeKey = getMimeTypeKey(mimeType, regLevel);
        if (mimeTypeKey != null) {
            // Checks if the mime type key exists
            return isSubKeyExist(mimeTypeKey, regLevel);
        } else {
            return false;
        }
    }
  
    /**
     * Returns true if the mime type exists in the default win registry folder.
     *
     * @param mimeType given mimeType (not null)
     * @return true if the mime type exists in the registry table
     */
    public static boolean isMimeTypeExist(String mimeType) {
        return (isMimeTypeExist(mimeType, ROOT_LEVEL));
    }
  
    /**
     * Returns true if the specified file extension exists in the given registry folder.
     *
     * @param fileExt given file extension (not null)
     * @param regLevel given regLevel
     * @return true if the file extension exists in the registry table
     */
    public static boolean isFileExtExist(String fileExt, int regLevel) {
        // Retrieve the relevant file extension key
        String fileExtKey = getFileExtKey(fileExt, regLevel);
        if (fileExtKey != null) {
            // check if this key exists
            //For windows 2000, we still need to check if the file ext in
            //Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts
            return (isSubKeyExist(fileExtKey, regLevel) || isWin2kUserDefinedFileExtExist(fileExt));
        } else {
            return false;
        }
    }
    
    /**
     * Returns true if the specified file extension exists in the
     *  \\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts in Windows 2000.
     *
     * @param fileExt The given file extension
     * @return  true if the file extension exists.
     */
    public static boolean isWin2kUserDefinedFileExtExist(String fileExt) {
        boolean win2kFileDefinedByUser = false;
        if (osName.equalsIgnoreCase(WIN2KOS)) {
            String fileExtKey = USER_FILE_EXT_KEY_PREFIX + "\\" + fileExt;
            win2kFileDefinedByUser = isSubKeyExist(fileExtKey, USER_LEVEL);
        }
        return win2kFileDefinedByUser;
    }
    
    /**
     * Checks if the speicifed file extension exists in the default registry folder.
     *
     * @param fileExt given file extension (not null) 
     * @return true if the file extension exists in the registry table
     */
    public static boolean isFileExtExist(String fileExt) {
        return (isFileExtExist(fileExt, ROOT_LEVEL));
    }

    /**
     * Adds a new file extension entry in the Windows registry table under the specified folder.
     *
     * @param fileExt name of the new file extension (not null)
     * @param regLevel given regLevel
     * @throws RegisterFailedException if the operation fails.
     */
    public static void addFileExt(String fileExt, int regLevel) 
        throws RegisterFailedException {
        // Retrieve the relevant file extension key
        String fileExtKey = getFileExtKey(fileExt, regLevel);
        if (fileExtKey != null) {
            // Create this key in the windows registry table
            regCreateKeyEx(fileExtKey, regLevel);
        }
    }
  
    /**
     * Removes the specified file extension entry from the given folder in the Windows registry table.
     *
     * @param fileExt given file extension (not null)
     * @param regLevel given regLevel
     * @throws RegisterFailedException if the operation fails.
     */
    public static void removeFileExt(String fileExt, int regLevel) 
        throws RegisterFailedException {
        if (isFileExtExist(fileExt, regLevel)) {
            // Retrieve the relevant file extension key in Windows regtable
            String fileExtKey = getFileExtKey(fileExt, regLevel);
            //Check if we should remove the classID key
            String clsID = getClassIDByFileExt(fileExt, regLevel);
            if (clsID != null) {
                String clsIDKey = getClsIDKey(clsID, regLevel);
                if (clsIDKey != null) {
                        regDeleteKey(clsIDKey, regLevel);
                }
            }
            if (fileExtKey != null) {
                // Delete the key from registry table
                if (isSubKeyExist(fileExtKey, regLevel)) {
                    regDeleteKey(fileExtKey, regLevel);
                }
            }
            //For windows 2000, we still need to check if the file ext in
            //Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts
            if (isWin2kUserDefinedFileExtExist(fileExt)) {
                fileExtKey = USER_FILE_EXT_KEY_PREFIX + "\\" + fileExt;
                regDeleteKey(fileExtKey, USER_LEVEL);
            }
        } 
    }

    /**
     * Adds a new MIME type entrty in the Windows registry table under the given folder.
     *
     * @param mimeType given mime type (not null)
     * @param regLevel given regLevel
     * @throws RegisterFailedException if the operation fails.
     */
    public static void addMimeType(String mimeType, int regLevel) 
        throws RegisterFailedException {
        // Retrieve the relevant mime type key
        String temMimeKey = getMimeTypeKey(mimeType, regLevel);
        if (temMimeKey != null) {
            // Create this mime key
            regCreateKeyEx(temMimeKey, regLevel);
        }
    }
  
    /**
     * Removes the specified mime type entry in the Windows registy table from the given folder.
     *
     * @param mimeType given mime type (not null)
     * @param regLevel given regLevel
     * @throws RegisterFailedException if the operation fails.
     */
    public static void removeMimeType(String mimeType, int regLevel) 
        throws RegisterFailedException {
        if (isMimeTypeExist(mimeType, regLevel)) {
            // Retrieve the relvant mime type key
            String mimeKey = getMimeTypeKey(mimeType, regLevel);
            if (mimeKey != null) {
                // Delete the key from the registry table
                regDeleteKey(mimeKey, regLevel);  
            }
        } 
    }

    /**
     * Sets the class ID value of the specified file extension under the given folder.
     *
     * @param fileExt given file extension (not null)
     * @param classID given class ID value (not null)
     * @param regLevel given regLevel
     * @throws RegisterFailedException if the operation fails.
     */
    public static void setClassIDByFileExt(String fileExt, String classID, int regLevel) 
        throws RegisterFailedException {
        String fileExtKey = getFileExtKey(fileExt, regLevel);
        String clsIDKey = getClsIDKey(classID, regLevel);
        if (fileExtKey != null) {
            /* If the file extension does not exist in the registry table
             * Add it first
             */
            if (!isSubKeyExist(fileExtKey, regLevel)) {
                addFileExt(fileExt, regLevel);
            }
            /* If the classID does not exist in the registry table
             * Add it first
             */
            if (!isSubKeyExist(clsIDKey, regLevel)) {
                if (clsIDKey != null) {
                    regCreateKeyEx(clsIDKey, regLevel);
                }
            }
            setDefaultValue(fileExtKey, classID, regLevel);
        }
    }
  
    /**
     * Gets the class ID value of the specified fileExt.
     *
     * @param fileExt given file extension (not null)
     * @param regLevel given regLevel
     * @return class ID of the this file extension
     * @exception RegisterFailedException
     */    
    public static String getClassIDByFileExt(String fileExt, int regLevel) {
        //Retrieves the relevant file extension key
        String fileExtKey = getFileExtKey(fileExt, regLevel);
        if (fileExtKey != null) {
           if (isSubKeyExist(fileExtKey, regLevel)) {
           	   //in case of having CurVer key
           	   return getCurVerClassID(getDefaultValue(fileExtKey,regLevel),regLevel);
           }
        }
        return null;
    }
    
    /**
     * Gets the real classID in case of having CurVer key
     * @param defaultVerClassID default version classID
     * @param regLevel given regLevel
     * @return CurVerClassID or given classID
     */
    private static String getCurVerClassID(String defaultVerClassID,
			                               int regLevel) {
		String curVerClassIDKey = defaultVerClassID + "\\" + KN_CURVER;

		if (regLevel != ROOT_LEVEL) {
			// software\\classes\\***
			curVerClassIDKey = SYS_USER_KN_PREFIX + "\\" + curVerClassIDKey;
		}
		
		if (isSubKeyExist(curVerClassIDKey, regLevel)) {
			//key CurVer exists
			return getDefaultValue(curVerClassIDKey, regLevel);
		} else {
			//key CurVer doesn't exist
			return defaultVerClassID;
		}
	}
  
    /**
     * Sets the mutual reference of the mimeType and fileExt.
     *
     * @param fileExt given file extension (not null)
     * @param mimeType given mime type (not null)
     * @param regLevel given regLevel
     * @throws RegisterFailedException if the operation fails.
     */
    public static void setMutualRef(String fileExt, String mimeType, int regLevel) 
        throws RegisterFailedException {
        String mimeKey = getMimeTypeKey(mimeType, regLevel);
        String fileExtKey = getFileExtKey(fileExt, regLevel);

        if ((mimeKey != null) && (fileExtKey != null)) {
            if ((isSubKeyExist(fileExtKey, regLevel))
                    && (isSubKeyExist(mimeKey, regLevel))) {
                setMimeTypeByFileExt(mimeType, fileExt, regLevel);
                setFileExtByMimeType(fileExt, mimeType, regLevel); 
            }
        }
    }
  
}
