config (ConfigHolder): The configuration holder
            is_component (bool): Whether it is a component or not
        """
        name = config.get_config_name()
        name_lower = name.lower()

        # Get pv name (create if doesn't exist)
        pv_name = self._get_pv_name(name_lower, is_component)

        # Get meta data from config
        meta = config.get_config_meta()
        meta.pv = pv_name

        # Add metas and update pvs appropriately
        if is_component:
            if name_lower is not DEFAULT_COMPONENT.lower():
                self._component_metas[name_lower] = meta
                self._update_component_pv(name_lower, config.get_config_details())
                self._update_component_dependencies_pv(name_lower.lower())
        else:
            if name_lower in self._config_metas.keys():
                # Config already exists
                self._remove_config_from_dependencies(name)

            self._config_metas[name_lower] = meta
            self._update_config_pv(name_lower, config.get_config_details())

            # Update component dependencies
            comps = config.get_component_names()
            for comp in comps:
                if comp in self._comp_dependecncies:
                    self._comp_dependecncies[comp.lower()].append(config.get_config_name())
                else:
                    self._comp_dependecncies[comp.lower()] = [config.get_config_name()]
                self._update_component_dependencies_pv(comp.lower())

    def _remove_config_from_dependencies(self, config):
        # Remove old config from dependencies list
        for comp, confs in self._comp_dependecncies.iteritems():
            if config in confs:
                self._comp_dependecncies[comp].remove(config)
                self._update_component_dependencies_pv(comp)

    def _get_pv_name(self, config_name, is_component=False):
        """Returns the name of the pv corresponding to config_name, this name is generated if not already created."""
        if not is_component:
            if config_name in self._config_metas:
                pv_name = self._config_metas[config_name].pv
            else:
                curr_pvs = [meta.pv for meta in self._config_metas.values()]
                pv_name = create_pv_name(config_name, curr_pvs, "CONFIG")
        else:
            if config_name in self._component_metas:
                pv_name = self._component_metas[config_name].pv
            else:
                curr_pvs = [meta.pv for meta in self._component_metas.values()]
                pv_name = create_pv_name(config_name, curr_pvs, "COMPONENT")
        return pv_name

    def _update_version_control_post_delete(self, folder, files):
        for config in files:
            self._vc.remove(os.path.join(folder, config))
        self._vc.commit("Deleted %s" % ', '.join(list(files)))

    def delete_configs(self, delete_list, are_comps=False):
        """Takes a list of configurations and removes them from the file system and any relevant PVs.

        Args:
            delete_list (list): The configurations/components to delete
            are_comps (bool): Whether they are components or not
        """
        with self._lock:
            # TODO: clean this up?
            print_and_log("Deleting: " + ', '.join(list(delete_list)), "INFO")
            lower_delete_list = set([x.lower() for x in delete_list])
            if not are_comps:
                if self.active_config_name.lower() in lower_delete_list:
                    raise InvalidDeleteException("Cannot delete currently active configuration")
                if not lower_delete_list.issubset(self._config_metas.keys()):
                    raise InvalidDeleteException("Delete list contains unknown configurations")
                for config in delete_list:
                    pv_name = BlockserverPVNames.get_config_details_pv(self._config_metas[config.lower()].pv)
                    self._ca_server.deletePV(pv_name)
                    del self._config_metas[config.lower()]
                    self._remove_config_from_dependencies(config)
                self._update_version_control_post_delete(self._conf_path, delete_list)  # Git is case sensitive
            else:
                if DEFAULT_COMPONENT.lower() in lower_delete_list:
                    raise InvalidDeleteException("Cannot delete default component")
                # Only allow comps to be deleted if they appear in no configs
                for comp in lower_delete_list:
                    if self._comp_dependecncies.get(comp):
                        raise InvalidDeleteException(comp + " is in use in: " + ', '.join(self._comp_dependecncies[comp]))
                if not lower_delete_list.issubset(self._component_metas.keys()):
                    raise InvalidDeleteException("Delete list contains unknown components")
                for comp in lower_delete_list:
                    self._ca_server.deletePV(BlockserverPVNames.get_component_details_pv(self._component_metas[comp].pv))
                    self._ca_server.deletePV(BlockserverPVNames.get_dependencies_pv(self._component_metas[comp].pv))
                    del self._component_metas[comp]
                self._update_version_control_post_delete(self._comp_path, delete_list)

    def get_dependencies(self, comp_name):
        """Get the names of any configurations that depend on this component.

        Args:
            comp_name (string): The name of the component

        Returns:
            list : The configurations that depend on the component
        """
        with self._lock:
            dependencies = self._comp_dependecncies.get(comp_name)
            if dependencies is None:
                return []
            else:
                return dependencies

    def recover_from_version_control(self):
        """A method to revert the configurations directory back to the state held in version control."""
        self._vc.update()
<commit_msg>Ticket 1053: merged master into current branch<commit_after>#This file is part of the ISIS IBEX application.
#Copyright (C) 2012-2016 Science & Technology Facilities Council.
#All rights reserved.
#
#This program is distributed in the hope that it will be useful.
#This program and the accompanying materials are made available under the
#terms of the Eclipse Public License v1.0 which accompanies this distribution.
#EXCEPT AS EXPRESSLY SET FORTH IN THE ECLIPSE PUBLIC LICENSE V1.0, THE PROGRAM 
#AND ACCOMPANYING MATERIALS ARE PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES 
#OR CONDITIONS OF ANY KIND.  See the Eclipse Public License v1.0 for more details.
#
#You should have received a copy of the Eclipse Public License v1.0
#along with this program; if not, you can obtain a copy from
#https://www.eclipse.org/org/documents/epl-v10.php or 
#http://opensource.org/licenses/eclipse-1.0.php

import os
import json
import re
from threading import RLock

from BlockServer.core.file_path_manager import FILEPATH_MANAGER
from BlockServer.fileIO.file_manager import ConfigurationFileManager
from BlockServer.core.macros import MACROS
from BlockServer.core.inactive_config_holder import InactiveConfigHolder
from server_common.utilities import print_and_log, compress_and_hex, create_pv_name
from BlockServer.fileIO.schema_checker import ConfigurationSchemaChecker
from BlockServer.core.constants import DEFAULT_COMPONENT
from BlockServer.core.pv_names import BlockserverPVNames
from BlockServer.core.macros import BLOCKSERVER


CONFIG_CHANGED_PV = "CURR_CONFIG_CHANGED"


class InvalidDeleteException(Exception):
    def __init__(self, value):
        super(InvalidDeleteException, self).__init__()
        self._value = value

    def __str__(self):
        return self._value


class ConfigListManager(object):
    """ Class to handle data on all available configurations and manage their associated