/// <summary>
        /// Find those priority levels in the database that are not being used by any
        /// job or job type.
        /// </summary>
        /// <returns>The total number of orphaned items found</returns>
        private int UpdateOrphanedPriorityTypes()
        {
            Dictionary <int, string> usedTypes = new Dictionary <int, string>();
            IJTXDatabase3            wmxDb     = this.WmxDatabase;

            // Check all the jobs for priorities currently in use
            IJTXJobSet allJobs = wmxDb.JobManager.GetAllJobs();

            for (int i = 0; i < allJobs.Count; i++)
            {
                IJTXJob3 job = allJobs.get_Item(i) as IJTXJob3;
                if (!usedTypes.ContainsKey(job.Priority.Value))
                {
                    usedTypes[job.Priority.Value] = job.Priority.Name;
                }
            }

            // Check the template job types for default priorities in use
            IJTXJobTypeSet allJobTypes = wmxDb.ConfigurationManager.JobTypes;

            for (int i = 0; i < allJobTypes.Count; i++)
            {
                // TODO: Skip unused job types

                IJTXJobType3 jobType = allJobTypes.get_Item(i) as IJTXJobType3;
                if (!usedTypes.ContainsKey(jobType.DefaultPriority.Value))
                {
                    usedTypes[jobType.DefaultPriority.Value] = jobType.DefaultPriority.Name;
                }
            }

            // Loop over all of the priorities.  For anything whose name is not contained
            // in the "used" list, add it to the "unused" list.  If all of the items are
            // used, don't bother trying to add to the unused list.
            IJTXPrioritySet allTypes = wmxDb.ConfigurationManager.Priorities;

            if (usedTypes.Count != allTypes.Count)
            {
                for (int i = 0; i < allTypes.Count; i++)
                {
                    IJTXPriority priority = allTypes.get_Item(i) as IJTXPriority;
                    if (!usedTypes.ContainsKey(priority.Value))
                    {
                        m_unusedPriorities[priority.Value] = priority.Name;
                    }
                }
            }

            return(m_unusedPriorities.Count);
        }
        /// <summary>
        /// Find those workflows in the database that are not being used by any job type.
        /// </summary>
        /// <returns>The total number of orphaned items found</returns>
        private int UpdateOrphanedWorkflows()
        {
            Dictionary <int, int> usedWorkflows = new Dictionary <int, int>();
            IJTXDatabase3         wmxDb         = this.WmxDatabase;
            IJTXConfiguration3    configMgr     = wmxDb.ConfigurationManager as IJTXConfiguration3;
            IJTXJobTypeSet        jobTypes      = configMgr.JobTypes;

            // Iterate through each item in the set, building up the list of used types
            for (int i = 0; i < jobTypes.Count; i++)
            {
                // TODO: Do not consider items that we know are not in use

                IJTXJobType3 type = jobTypes.get_Item(i) as IJTXJobType3;
                // job type don't always need to have a workflow
                if (type.Workflow != null)
                {
                    usedWorkflows[type.Workflow.ID] = type.Workflow.ID;
                }
            }

            // Get the complete list of this type of object in the database
            IJTXWorkflowSet workflows = configMgr.Workflows;

            // Loop through the complete list of this object type.  If any of the IDs
            // are not in the "used" list, add that object to the "unused" list.
            // If all of the items are used, don't bother trying to add to the
            // unused list.
            if (usedWorkflows.Count != workflows.Count)
            {
                for (int i = 0; i < workflows.Count; i++)
                {
                    IJTXWorkflow workflow = workflows.get_Item(i) as IJTXWorkflow;
                    if (!usedWorkflows.ContainsKey(workflow.ID))
                    {
                        m_unusedWorkflows[workflow.ID] = workflow.Name;
                    }
                }
            }

            return(m_unusedWorkflows.Count);
        }
        /// <summary>
        /// Pre validates the given set of values.
        /// This is where you populate derived parameters based on input, among other things.
        /// </summary>
        /// <param name="paramValues"></param>
        /// <param name="pEnvMgr"></param>
        public override void UpdateParameters(IArray paramValues, IGPEnvironmentManager pEnvMgr)
        {
            try
            {
                UpdateParametersCommon(paramValues, pEnvMgr);
            }
            catch (WmxDefaultDbNotSetException)
            {
                // If the default DB wasn't set, stop executing
                return;
            }
            catch (NullReferenceException)
            {
                // If one of the parameters was null, stop executing
                return;
            }

            // Get the parameters as a map for easier access
            WmauParameterMap  paramMap          = new WmauParameterMap(paramValues);
            IGPParameter3     jobTypeParam      = paramMap.GetParam(C_PARAM_JOB_TYPE);
            IGPParameterEdit3 jobTypeParamEdit  = paramMap.GetParamEdit(C_PARAM_JOB_TYPE);
            IGPParameter3     dataWorkspace     = paramMap.GetParam(C_PARAM_DATA_WORKSPACE);
            IGPParameterEdit3 dataWorkspaceEdit = paramMap.GetParamEdit(C_PARAM_DATA_WORKSPACE);
            IGPParameter3     parentVersion     = paramMap.GetParam(C_PARAM_PARENT_VERSION);
            IGPParameterEdit3 parentVersionEdit = paramMap.GetParamEdit(C_PARAM_PARENT_VERSION);

            // Set the domains for any parameters that need them
            if (jobTypeParam.Domain == null)
            {
                jobTypeParamEdit.Domain = Common.WmauGpDomainBuilder.BuildJobTypeDomain(this.WmxDatabase);
            }
            if (dataWorkspace.Domain == null)
            {
                dataWorkspaceEdit.Domain = Common.WmauGpDomainBuilder.BuildWorkspaceDomain(
                    this.WmxDatabase,
                    new string[] { C_OPT_NONE });
            }

            // Only update the domain for the parent version field if it hasn't yet been
            // populated, or if the value of the data workspace parameter has changed
            string newDataWorkspace = dataWorkspace.Value.GetAsText();

            if (parentVersion.Domain == null ||
                !m_dataWorkspaceName.Equals(newDataWorkspace))
            {
                IGPDomain    versionDomain = null;
                string       newJobType    = jobTypeParam.Value.GetAsText();
                IJTXJobType3 jobType       = this.WmxDatabase.ConfigurationManager.GetJobType(newJobType) as IJTXJobType3;

                if (newDataWorkspace.Equals(C_OPT_NONE) ||
                    newDataWorkspace.Equals(string.Empty))
                {
                    // Case 1: the only acceptable option for the parent version is "none".
                    IGPCodedValueDomain cvDomain = new GPCodedValueDomainClass();
                    cvDomain.AddStringCode(C_OPT_NONE, C_OPT_NONE);
                    versionDomain = cvDomain as IGPDomain;
                }
                else
                {
                    // Case 2: we need to retrieve the version list for an existing
                    // data workspace
                    try
                    {
                        versionDomain = Common.WmauGpDomainBuilder.BuildVersionsDomain(
                            this.WmxDatabase, newDataWorkspace, new string[] { C_OPT_NONE });
                    }
                    catch (WmauException)
                    {
                        // Use the "null" as an error value; we'll check for this later
                        versionDomain = null;
                    }
                }
                parentVersionEdit.Domain = versionDomain;
            }
        }
        /// <summary>
        /// Find those map documents embedded in the database that are not being
        /// referenced in any way
        /// </summary>
        /// <returns>The total number of orphaned items found</returns>
        private int UpdateOrphanedMapDocuments()
        {
            SortedList <string, int> unusedItems = new SortedList <string, int>();
            IJTXDatabase3            wmxDb       = this.WmxDatabase;
            IJTXConfiguration3       configMgr   = wmxDb.ConfigurationManager as IJTXConfiguration3;
            IJTXConfigurationEdit2   configEdit  = wmxDb.ConfigurationManager as IJTXConfigurationEdit2;

            IJTXMapSet allMaps = configMgr.JTXMaps;
            Dictionary <string, int> allMapNames = new Dictionary <string, int>();

            for (int i = 0; i < allMaps.Count; i++)
            {
                IJTXMap map = allMaps.get_Item(i);
                allMapNames[map.Name] = map.ID;
            }

            Dictionary <string, int> usedItems = new Dictionary <string, int>();

            // Find the map types that are associated with job types
            IJTXJobTypeSet allJobTypes = configMgr.JobTypes;

            for (int i = 0; i < allJobTypes.Count; i++)
            {
                // TODO: Skip orphaned job types

                IJTXJobType3 jobType = allJobTypes.get_Item(i) as IJTXJobType3;
                if (jobType.AOIMap != null)
                {
                    usedItems[jobType.AOIMap.Name] = jobType.AOIMap.ID;
                }
                if (jobType.JobMap != null)
                {
                    usedItems[jobType.JobMap.Name] = jobType.JobMap.ID;
                }
            }

            // If necessary, find the map types launched by custom steps.  Look for
            // the "/mxd:" argument as an identifier.
            IJTXStepTypeSet allStepTypes = wmxDb.ConfigurationManager.StepTypes;

            for (int i = 0; i < allStepTypes.Count; i++)
            {
                IJTXStepType2 stepType = allStepTypes.get_Item(i) as IJTXStepType2;

                // Skip orphaned step types
                if (m_unusedStepTypes.Keys.Contains(stepType.ID))
                {
                    continue;
                }

                for (int j = 0; j < stepType.Arguments.Length; j++)
                {
                    string stepArg = stepType.Arguments[j].ToString();
                    if (stepArg.StartsWith(C_MAP_DOC_FLAG))
                    {
                        string suffix = stepArg.Substring(C_MAP_DOC_FLAG.Length);
                        suffix = suffix.Trim(new char[] { '"' });
                        if (allMapNames.Keys.Contains(suffix))
                        {
                            usedItems[suffix] = allMapNames[suffix];
                        }
                    }
                }
            }

            // Add in the map document that's used as the template map document
            // (if one exists)
            IJTXConfigurationProperties configProps = this.WmxDatabase.ConfigurationManager as IJTXConfigurationProperties;
            string mapIdStr = configProps.GetProperty(Constants.JTX_PROPERTY_MAPVIEW_MAP_GUID);

            if (mapIdStr != null && !mapIdStr.Equals(string.Empty))
            {
                for (int i = 0; i < allMaps.Count; i++)
                {
                    IJTXMap        tempMap   = allMaps.get_Item(i);
                    IJTXIdentifier tempMapId = tempMap as IJTXIdentifier;
                    if (tempMapId.GUID.Equals(mapIdStr))
                    {
                        usedItems[tempMap.Name] = tempMap.ID;
                        break;
                    }
                }
            }

            // Loop over all the map documents in the DB, looking for anything
            // that we didn't identify as "in use"
            foreach (string name in allMapNames.Keys)
            {
                if (!usedItems.ContainsKey(name))
                {
                    m_unusedMapDocs[name] = allMapNames[name];
                }
            }

            return(m_unusedMapDocs.Count);
        }
        /// <summary>
        /// Find those users in the database who are not being referenced in any way
        /// </summary>
        /// <returns>The total number of orphaned items found</returns>
        private int UpdateOrphanedUsers()
        {
            SortedList <string, string> unusedItems = new SortedList <string, string>();
            IJTXDatabase3      wmxDb     = this.WmxDatabase;
            IJTXConfiguration3 configMgr = wmxDb.ConfigurationManager as IJTXConfiguration3;
            IJTXJobManager     jobMgr    = wmxDb.JobManager;
            IJTXUserSet        allUsers  = configMgr.Users;

            Dictionary <string, string> usedItems = new Dictionary <string, string>();

            // Get all of the users who are members of a group
            IJTXUserGroupSet allGroups = configMgr.UserGroups;

            for (int i = 0; i < allGroups.Count; i++)
            {
                IJTXUserGroup2 group = allGroups.get_Item(i) as IJTXUserGroup2;
                for (int j = 0; j < group.Users.Count; j++)
                {
                    IJTXUser3 user = group.Users.get_Item(j) as IJTXUser3;
                    usedItems[user.UserName] = user.FullName;
                }
            }

            // If necessary, add in the users who have jobs assigned to them
            if (usedItems.Count < allUsers.Count)
            {
                IJTXJobSet allJobs = jobMgr.GetAllJobs();
                for (int i = 0; i < allJobs.Count; i++)
                {
                    IJTXJob3 job = allJobs.get_Item(i) as IJTXJob3;
                    if (job.AssignedType == jtxAssignmentType.jtxAssignmentTypeUser)
                    {
                        IJTXUser3 user = configMgr.GetUser(job.AssignedTo) as IJTXUser3;

                        // It's possible for a user to have a job assigned, but have
                        // already been removed from the DB.  Throw an exception in
                        // this case, as the DB needs to be cleaned up.
                        if (user == null)
                        {
                            throw new WmauException(WmauErrorCodes.C_USER_NOT_FOUND_ERROR);
                        }
                        usedItems[user.UserName] = user.FullName;
                    }
                }
            }

            // If necessary, add in the users who have a job type's default assignment
            // set to them
            if (usedItems.Count < allUsers.Count)
            {
                IJTXJobTypeSet allJobTypes = configMgr.JobTypes;
                for (int i = 0; i < allJobTypes.Count; i++)
                {
                    // TODO: Exclude orphaned job types

                    IJTXJobType3 jobType = allJobTypes.get_Item(i) as IJTXJobType3;
                    if (jobType.DefaultAssignedType == jtxAssignmentType.jtxAssignmentTypeUser)
                    {
                        IJTXUser3 user = configMgr.GetUser(jobType.DefaultAssignedTo) as IJTXUser3;

                        // It's possible for a user to have a job assigned, but have
                        // already been removed from the DB.  Throw an exception in
                        // this case, as the DB needs to be cleaned up.
                        if (user == null)
                        {
                            throw new WmauException(WmauErrorCodes.C_USER_NOT_FOUND_ERROR);
                        }
                        usedItems[user.UserName] = user.FullName;
                    }
                }
            }

            // If necessary, add in the users who have steps assigned to them
            // by default
            if (usedItems.Count < allUsers.Count)
            {
                IJTXWorkflowSet allWorkflows = configMgr.Workflows;
                for (int i = 0; i < allWorkflows.Count; i++)
                {
                    // Skip over unused workflows
                    IJTXWorkflow workflow = allWorkflows.get_Item(i);
                    if (m_unusedWorkflows.Keys.Contains(workflow.ID))
                    {
                        continue;
                    }

                    // Examine the other items
                    IJTXWorkflowConfiguration workflowCfg = allWorkflows.get_Item(i) as IJTXWorkflowConfiguration;
                    int[] workflowStepIds = workflowCfg.GetAllSteps();
                    foreach (int j in workflowStepIds)
                    {
                        IJTXStep3 step = workflowCfg.GetStep(j) as IJTXStep3;
                        if (step.AssignedType == jtxAssignmentType.jtxAssignmentTypeUser)
                        {
                            IJTXUser3 user = configMgr.GetUser(step.AssignedTo) as IJTXUser3;

                            // It's possible for a user to have a job assigned, but have
                            // already been removed from the DB.  Throw an exception in
                            // this case, as the DB needs to be cleaned up.
                            if (user == null)
                            {
                                throw new WmauException(WmauErrorCodes.C_USER_NOT_FOUND_ERROR);
                            }
                            usedItems[user.UserName] = user.FullName;
                        }
                    }
                }
            }

            // Loop over all the users in the DB, looking for anything
            // that we didn't identify as "in use"
            for (int i = 0; i < allUsers.Count; i++)
            {
                IJTXUser3 item = allUsers.get_Item(i) as IJTXUser3;
                if (!usedItems.ContainsKey(item.UserName))
                {
                    m_unusedUsers[item.UserName] = item.FullName;
                }
            }

            return(m_unusedUsers.Count);
        }