Никола обнови решението на 30.04.2015 13:27 (преди над 9 години)
+class FSNode:
+ def __init__(self, relative_name, absolute_path, parent_node, file_system):
+ self.relative_name = relative_name
+ self.parent_node = parent_node
+ self.absolute_path = absolute_path
+ self.file_system = file_system
+
+
+class FileContent:
+ def __init__(self, content):
+ self.content = content
+ self.size = len(content) + 1
+ self.ref_count = 1
+
+ def content_size(self):
+ return self.size
+
+
+class File(FSNode):
+ def __init__(self, file_content, relative_name, absolute_path, parent_node, file_system):
+ super().__init__(relative_name, absolute_path, parent_node, file_system)
+ self.file_content = file_content
+ self.is_directory = False
+
+ @property
+ def size(self):
+ return self.file_content.size
+
+ @property
+ def content(self):
+ return self.file_content.content
+
+ def append(self, content):
+ self.file_system.file_update(self, content, False)
+
+ def truncate(self, content):
+ self.file_system.file_update(self, content, True)
+
+
+class SoftLink(File):
+ def __init__(self, link_path, relative_name, absolute_path, parent_node, file_system):
+ super().__init__(FileContent(''), relative_name, absolute_path, parent_node, file_system)
+ self.link_path = link_path
+ self.is_directory = False
+
+ def __getattr__(self, item):
+ try:
+ linked_file = self.file_system.get_node(self.link_path)
+ except NodeDoesNotExistError:
+ raise LinkPathError()
+ return getattr(linked_file, item)
+
+ @property
+ def content(self):
+ return self.__getattr__('content')
+
+
+class HardLink(File):
+ def __init__(self, file_content, relative_name, absolute_path, parent_node, file_system):
+ super().__init__(file_content, relative_name, absolute_path, parent_node, file_system)
+
+class Directory(FSNode):
+ def __init__(self, relative_name, absolute_path, parent_node, file_system):
+ super().__init__(relative_name, absolute_path, parent_node, file_system)
+ self.files = []
+ self.directories = []
+ self.nodes = []
+ self.is_directory = True
+
+
+class FileSystem:
+ def __init__(self, size):
+ self.dir_size = 1
+ self.nodes = [Directory('/', '/', None, self)]
+ self.size = size
+ self.available_size = size - 1
+
+ def get_node(self, path):
+ return self._find_node_by_path(path)
+
+ def create(self, path, directory=False, content=''):
+ try:
+ self._find_node_by_path(path)
+ raise DestinationNodeExistsError()
+ except NodeDoesNotExistError:
+ parent_absolute_path, node_name = self._get_parent_node_names_from_path(path)
+ try:
+ parent = self._find_node_by_path(parent_absolute_path) # TODO: check if parent is dir
+ except NodeDoesNotExistError:
+ raise DestinationNodeDoesNotExistError()
+ if directory is True:
+ return self._dir_create(node_name, path, parent)
+ else:
+ return self._file_create(content, node_name, path, parent)
+
+ def remove(self, path, directory=False, force=True): # TODO: somewhat undefined behaviour when / is deleted
+ node = self._find_node_by_path(path)
+ if isinstance(node, File):
+ self._remove_node(node, node.file_content.size)
+ elif isinstance(node, Directory):
+ if directory is False:
+ raise NonExplicitDirectoryDeletionError()
+ else:
+ if len(node.nodes) > 0 and force is False:
+ raise NonEmptyDirectoryDeletionError()
+ else:
+ for sub_node in list(node.nodes):
+ self.remove(sub_node.absolute_path, sub_node.is_directory)
+ self._remove_node(node, self.dir_size)
+
+ def move(self, source, destination):
+ try:
+ source_node = self._find_node_by_path(source)
+ except NodeDoesNotExistError:
+ raise SourceNodeDoesNotExistError()
+ try:
+ destination_dir = self._find_node_by_path(destination)
+ except NodeDoesNotExistError:
+ raise DestinationNodeDoesNotExistError()
+ if destination_dir.is_directory is not True:
+ raise DestinationNotADirectoryError()
+ parent_absolute_path, source_node_name = self._get_parent_node_names_from_path(source)
+ source_node_new_absolute_path = destination + ('' if destination[-1] is '/' else '/')\
+ + source_node_name
+ try:
+ self._find_node_by_path(source_node_new_absolute_path)
+ raise DestinationNodeExistsError()
+ except NodeDoesNotExistError:
+ self._move_node(source_node, destination_dir, source_node_new_absolute_path)
+
+ def link(self, source, destination, symbolic=True):
+ try:
+ source_node = self.get_node(source)
+ except NodeDoesNotExistError:
+ if symbolic is True:
+ raise
+ else:
+ raise SourceNodeDoesNotExistError
+ if source_node.is_directory is True and symbolic is False:
+ raise DirectoryHardLinkError()
+ parent_absolute_path, link_node_name = self._get_parent_node_names_from_path(destination)
+ parent = self.get_node(parent_absolute_path)
+ if symbolic is True:
+ return self._soft_link_create(source, link_node_name, destination, parent)
+ else:
+ return self._hard_link_create(source_node.file_content, link_node_name, destination, parent)
+
+
+ def _move_node(self, node, destination_dir, node_new_absolute_path):
+ self.__remove_node_from_parent(node)
+ self.__add_node_to_parent(node, destination_dir)
+ node.parent_node = destination_dir
+ node.absolute_path = node_new_absolute_path
+
+ def _remove_node(self, node, node_content_size):
+ if isinstance(node, File):
+ self.__manage_file_content_on_remove(node)
+ else:
+ self.available_size += self.dir_size
+ self.nodes.remove(node)
+ self.__remove_node_from_parent(node)
+
+ @staticmethod
+ def __remove_node_from_parent(node):
+ node.parent_node.files.remove(node) if isinstance(node, File)\
+ else node.parent_node.directories.remove(node)
+ node.parent_node.nodes.remove(node)
+
+ def __manage_file_content_on_remove(self, file_node):
+ file_node.file_content.ref_count -= 1
+ if file_node.file_content.ref_count is 0:
+ file_size = file_node.file_content.size
+ self.available_size += file_size
+ else:
+ self.available_size += self.dir_size
+
+ @staticmethod
+ def __add_node_to_parent(node, destination_dir):
+ destination_dir.files.append(node) if isinstance(node, File)\
+ else destination_dir.directories.append(node)
+ destination_dir.nodes.append(node)
+
+ def file_update(self, file_node, content, trunc=False):
+ if trunc is True:
+ self.available_size += file_node.file_content.size - 1
+ file_node.file_content.content = ''
+ self.__check_content_insertion(len(content))
+ content_size = len(content)
+ self.available_size -= content_size
+ file_node.file_content.content += content
+ file_node.file_content.size = len(file_node.file_content.content) + 1
+
+ def _file_create(self, content, relative_name, absolute_path, parent):
+ file_size = len(content) + 1
+ self.__check_content_insertion(file_size)
+ self.available_size -= file_size
+ new_file = File(FileContent(content), relative_name, absolute_path, parent, self)
+ self.__append_file_to_fs(new_file, parent)
+ return new_file
+
+ def _dir_create(self, relative_name, absolute_path, parent):
+ self.__check_content_insertion(self.dir_size)
+ self.available_size -= self.dir_size
+ new_dir = Directory(relative_name, absolute_path, parent, self)
+ self.nodes.append(new_dir)
+ parent.directories.append(new_dir)
+ parent.nodes.append(new_dir)
+ return new_dir
+
+ def _soft_link_create(self, link_path, relative_name, absolute_path, parent):
+ link_size = self.dir_size
+ self.__check_content_insertion(link_size) # TODO: this is not explicitly defined
+ self.available_size -= link_size
+ new_link = SoftLink(link_path, relative_name, absolute_path, parent, self)
+ self.__append_file_to_fs(new_link, parent)
+ return new_link
+
+ def _hard_link_create(self, file_content, relative_name, absolute_path, parent):
+ hard_link_size = self.dir_size
+ self.__check_content_insertion(hard_link_size)
+ self.available_size -= 1
+ file_content.ref_count += 1
+ new_hard_link = HardLink(file_content, relative_name, absolute_path, parent, self)
+ self.__append_file_to_fs(new_hard_link, parent)
+ return new_hard_link
+
+ def __append_file_to_fs(self, new_file, parent):
+ self.nodes.append(new_file)
+ parent.files.append(new_file)
+ parent.nodes.append(new_file)
+
+ def __check_content_insertion(self, length):
+ if self.available_size - length < 0:
+ raise NotEnoughSpaceError
+
+ def _find_node_by_path(self, path):
+ try:
+ return next(filter(lambda x: x.absolute_path == path, self.nodes))
+ except StopIteration:
+ raise NodeDoesNotExistError()
+
+ @staticmethod
+ def _get_node_names_from_path(path):
+ if path == '/':
+ return ['/']
+ else:
+ node_names = path.split('/')
+ node_names[0] = '/'
+ return node_names
+
+ @staticmethod
+ def _get_parent_node_names_from_path(path):
+ if path == '/':
+ return None, None
+ else:
+ parent_path = path.rsplit('/', 1)
+ return '/' if parent_path[0] is '' else parent_path[0], parent_path[1]
+
+
+class FileSystemError(Exception):
+ def __init__(self):
+ self.message = "Error"
+
+ def __str__(self):
+ return self.message
+
+
+class NodeDoesNotExistError(FileSystemError):
+ def __init__(self):
+ super().__init__()
+ self.message = "Node does not exist"
+
+
+class DestinationNodeDoesNotExistError(NodeDoesNotExistError, FileSystemError):
+ def __init__(self):
+ super().__init__()
+ self.message = "Destination node does not exist"
+
+
+class SourceNodeDoesNotExistError(NodeDoesNotExistError, FileSystemError):
+ def __init__(self):
+ super().__init__()
+ self.message = "Source node does not exist"
+
+
+class DestinationNodeExistsError(FileSystemError):
+ def __init__(self):
+ super().__init__()
+ self.message = "Destination node exists"
+
+
+class DestinationNotADirectoryError(FileSystemError):
+ def __init__(self):
+ super().__init__()
+ self.message = "Destination node is not directory"
+
+
+class NonExplicitDirectoryDeletionError(FileSystemError):
+ def __init__(self):
+ super().__init__()
+ self.message = "Non explicit directory deletion"
+
+
+class NotEnoughSpaceError(FileSystemError):
+ def __init__(self):
+ super().__init__()
+ self.message = "Insufficient space"
+
+
+class NonEmptyDirectoryDeletionError(FileSystemError):
+ def __init__(self):
+ super().__init__()
+ self.message = "Non empty directory deletion"
+
+
+class LinkPathError(FileSystemError):
+ def __init__(self):
+ super().__init__()
+ self.message = "No linked file"
+
+
+class DirectoryHardLinkError(FileSystemError):
+ def __init__(self):
+ super().__init__()
+ self.message = "Directory Hard Link"