<commit_before>#  Copyright 2008-2009 Nokia Siemens Networks Oyj
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.

import os
import copy

from robotide import utils
from robotide.errors import NoRideError, DataError, SerializationError
from robotide.publish import RideSaving, RideSaved
from robotide.robotapi import TestSuiteData, ResourceFileData, InitFileData,\
    UserErrorHandler
from robotide.writer import FileWriter

from tables import InitFileSettingTable, SuiteSettingTable,\
        ResourceSettingTable, VariableTable, TestCaseTable, UserKeywordTable
from tcuk import UserKeyword


def TestSuiteFactory(path, namespace=None):
    if not os.path.exists(path):
        if '__init__' in path:
            return _TestSuiteFactory(_EmptyInitFile(path), namespace)
        return _TestSuiteFactory(_EmptyTestSuite(path), namespace)
    if _is_empty_dir(path):
        return _TestSuiteFactory(_EmptyInitFile(path), namespace)
    if _is_empty_file(path):
        return _TestSuiteFactory(_EmptyTestSuite(path), namespace)
    return _TestSuiteFactory(TestSuiteData(path), namespace)

def _is_empty_dir(path):
    return os.path.isdir(path) and not os.listdir(path)

def _is_empty_file(path):
    return os.path.isfile(path) and os.path.getsize(path) == 0

def _TestSuiteFactory(data, namespace, parent=None):
    if hasattr(data, 'initfile'):
        return InitFile(data, namespace, parent)
    return TestCaseFile(data, namespace, parent)


def ResourceFileFactory(path, namespace, create_empty=True):
    if path and os.path.isfile(path):
        data = ResourceFileData(path)
    elif create_empty:
        data = _EmptyResourceFile(path)
    else:
        return None
    return ResourceFile(data, namespace)


class _AbstractDataFile(object):
    imports = property(lambda self: self.settings.imports)
    is_directory_suite = False

    def __init__(self, data, namespace):
        self.source = data.source.decode('UTF-8')
        self._stat = self._get_stat(self.source)
        self.variables = VariableTable(self, data.variables)
        kws = [ kw for kw in data.user_keywords
                if not isinstance(kw, UserErrorHandler)]
        self.keywords = UserKeywordTable(self, kws)
        self.dirty = False
        self.suites = []
        self.tests = []
        self.namespace = namespace
        self.datafile = self # Needed by editors that edit suites and tc/uks

    def __deepcopy__(self, memo):
        # We don't want to copy data file when test case or uk is copied.
        return self

    def _get_stat(self, path):
        if os.path.isfile(path):
            stat = os.stat(path)
            return (stat.st_mtime, stat.st_size)
        return (0, 0)

    def get_resources(self):
        resources = []
        for name in self.get_resource_imports():
            #TODO: Extract variable resolving
            name = self.variables.replace_scalar(name)
            for res in resources:
                name = res.variables.replace_scalar(name)
            resource = self.namespace.get_resource_file(self.source, name)
            if resource:
                resources.append(resource)
        return resources

    def set_dirty(self):
        self.dirty = True

    def refresh_namespace(self):
        # TODO ns was None with new import...
        if self.namespace:
            self.namespace.clear_caches()

    def get_user_keywords(self):
        kws = copy.copy(self.keywords)
        for res in self.get_resources():
            kws.extend(res.get_user_keywords())
        return kws

    def get_keyword_details(self, name):
        return self.namespace.get_keyword_details(self, name)

    def is_library_keyword(self, name):
        return self.namespace.is_library_keyword(self, name)

    def get_keywords(self, source_for_own_kws=None):
        kws =  self.get_own_keywords(source_for_own_kws) + \
               self.imports.get_keywords()
        return self._remove_duplicates(kws)

    def get_own_keywords(self, source=None):
        return self.keywords

    def get_own_variables(self):
        return [ ('<this file>', var) for var in self.variables ]

    def get_variables(self):
        return [ (self.name, var) for var in self.variables ] + \
               self.imports.get_variables()

    def _remove_duplicates(self, keywords):
        return list(set(keywords))

    def replace_variables(self, value):
        value = self.variables.replace_scalar(value)
        for res in self.get_resources():
            value = res.replace_variables(value)
        for vars in self._get_variable_files():
            try:
                value = vars.replace_scalar(value)
            except DataError:
                pass
        return value

    def validate_keyword_name(self, value):
        return self.keywords.validate_name(value)

    def validate_scalar_variable_name(self, name):
        return self.variables.validate_scalar_variable_name(name)

    def validate_list_variable_name(self, name):
        return self.variables.validate_list_variable_name(name)

    def _get_variable_files(self):
        varfiles = []
        for var_settings in self.get_variable_imports():
            # TODO: There is need for namespace object which could be used for
            # all variable replacing
            name = self.variables.replace_scalar(var_settings.name)
            args = self.variables.replace_list(var_settings.args)
            varfile = self.namespace.get_varfile(self.source, name, args)
            if varfile:
                varfiles.append(varfile)
        return varfiles

    def get_resource_imports(self):
        return self.settings.imports.get_resource_imports()

    def get_library_imports(self):
        return self.settings.imports.get_library_imports()

    def get_variable_imports(self):
        return self.settings.imports.get_variable_imports()

    def has_been_modified_on_disk(self):
        return self._get_stat(self.source) != self._stat

    def new_keyword(self, name):
        self.dirty = True
        return self.keywords.new_keyword(name)

    def add_test_or_user_keyword(self, item):
        item.longname = '%s.%s' %(self.longname, item.name)
        item.datafile = self
        if isinstance(item, UserKeyword):
            self.keywords.append(item)
        else:
            self.tests.append(item)
        self.dirty = True

    def has_format(self):
        return True

    def get_format(self):
        format = os.path.splitext(self.source)[1][1:].upper()
        if format in ['HTML', 'HTM', 'XHTML']:
            return 'HTML'
        return format

    def serialize(self, force=False, format=None, recursive=False):
        RideSaving(path=self.source).publish()
        if recursive:
            for s in self.suites:
                s.serialize(force, format, recursive)
        old_source = self._change_format_if_needed(format)
        if not (self.dirty or force):
            return
        self._validate_serialization()
        try:<commit_msg>Added cache refresh after adding new kw, issue 416<commit_after>#  Copyright 2008-2009 Nokia Siemens Networks Oyj
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.

import os
import copy

from robotide import utils
from robotide.errors import NoRideError, DataError, SerializationError
from robotide.publish import RideSaving, RideSaved
from robotide.robotapi import TestSuiteData, ResourceFileData, InitFileData,\
    UserErrorHandler
from robotide.writer import FileWriter

from tables import InitFileSettingTable, SuiteSettingTable,\
        ResourceSettingTable, VariableTable, TestCaseTable, UserKeywordTable
from tcuk import UserKeyword


def TestSuiteFactory(path, namespace=None):
    if not os.path.exists(path):
        if '__init__' in path:
            return _TestSuiteFactory(_EmptyInitFile(path), namespace)
        return _TestSuiteFactory(_EmptyTestSuite(path), namespace)
    if _is_empty_dir(path):
        return _TestSuiteFactory(_EmptyInitFile(path), namespace)
    if _is_empty_file(path):
        return _TestSuiteFactory(_EmptyTestSuite(path), namespace)
    return _TestSuiteFactory(TestSuiteData(path), namespace)

def _is_empty_dir(path):
    return os.path.isdir(path) and not os.listdir(path)

def _is_empty_file(path):
    return os.path.isfile(path) and os.path.getsize(path) == 0

def _TestSuiteFactory(data, namespace, parent=None):
    if hasattr(data, 'initfile'):
        return InitFile(data, namespace, parent)
    return TestCaseFile(data, namespace, parent)


def ResourceFileFactory(path, namespace, create_empty=True):
    if path and os.path.isfile(path):
        data = ResourceFileData(path)
    elif create_empty:
        data = _EmptyResourceFile(path)
    else:
        return None
    return ResourceFile(data, namespace)


class _AbstractDataFile(object):
    imports = property(lambda self: self.settings.imports)
    is_directory_suite = False

    def __init__(self, data, namespace):
        self.source = data.source.decode('UTF-8')
        self._stat = self._get_stat(self.source)
        self.variables = VariableTable(self, data.variables)
        kws = [ kw for kw in data.user_keywords
                if not isinstance(kw, UserErrorHandler)]
        self.keywords = UserKeywordTable(self, kws)
        self.dirty = False
        self.suites = []
        self.tests = []
        self.namespace = namespace
        self.datafile = self # Needed by editors that edit suites and tc/uks

    def __deepcopy__(self, memo):
        # We don't want to copy data file when test case or uk is copied.
        return self

    def _get_stat(self, path):
        if os.path.isfile(path):
            stat = os.stat(path)
            return (stat.st_mtime, stat.st_size)
        return (0, 0)

    def get_resources(self):
        resources = []
        for name in self.get_resource_imports():
            #TODO: Extract variable resolving
            name = self.variables.replace_scalar(name)
            for res in resources:
                name = res.variables.replace_scalar(name)
            resource = self.namespace.get_resource_file(self.source, name)
            if resource:
                resources.append(resource)
        return resources

    def set_dirty(self):
        self.dirty = True

    def refresh_namespace(self):
        # TODO ns was None with new import...
        if self.namespace:
            self.namespace.clear_caches()

    def get_user_keywords(self):
        kws = copy.copy(self.keywords)
        for res in self.get_resources():
            kws.extend(res.get_user_keywords())
        return kws

    def get_keyword_details(self, name):
        return self.namespace.get_keyword_details(self, name)

    def is_library_keyword(self, name):
        return self.namespace.is_library_keyword(self, name)

    def get_keywords(self, source_for_own_kws=None):
        kws =  self.get_own_keywords(source_for_own_kws) + \
               self.imports.get_keywords()
        return self._remove_duplicates(kws)

    def get_own_keywords(self, source=None):
        return self.keywords

    def get_own_variables(self):
        return [ ('<this file>', var) for var in self.variables ]

    def get_variables(self):
        return [ (self.name, var) for var in self.variables ] + \
               self.imports.get_variables()

    def _remove_duplicates(self, keywords):
        return list(set(keywords))

    def replace_variables(self, value):
        value = self.variables.replace_scalar(value)
        for res in self.get_resources():
            value = res.replace_variables(value)
        for vars in self._get_variable_files():
            try:
                value = vars.replace_scalar(value)
            except DataError:
                pass
        return value

    def validate_keyword_name(self, value):
        return self.keywords.validate_name(value)

    def validate_scalar_variable_name(self, name):
        return self.variables.validate_scalar_variable_name(name)

    def validate_list_variable_name(self, name):
        return self.variables.validate_list_variable_name(name)

    def _get_variable_files(self):
        varfiles = []
        for var_settings in self.get_variable_imports():
            # TODO: There is need for namespace object which could be used for
            # all variable replacing
            name = self.variables.replace_scalar(var_settings.name)
            args = self.variables.replace_list(var_settings.args)
            varfile = self.namespace.get_varfile(self.source, name, args)
            if varfile:
                varfiles.append(varfile)
        return varfiles

    def get_resource_imports(self):
        return self.settings.imports.get_resource_imports()

    def get_library_imports(self):
        return self.settings.imports.get_library_imports()

    def get_variable_imports(self):
        return self.settings.imports.get_variable_imports()

    def has_been_modified_on_disk(self):
        return self._get_stat(self.source) != self._stat

    def new_keyword(self, name):
        self.dirty = True
        self.refresh_namespace()
        return self.keywords.new_keyword(name)

    def add_test_or_user_keyword(self, item):
        item.longname = '%s.%s' %(self.longname, item.name)
        item.datafile = self
        if isinstance(item, UserKeyword):
            self.keywords.append(item)
        else:
            self.tests.append(item)
        self.dirty = True

    def has_format(self):
        return True

    def get_format(self):
        format = os.path.splitext(self.source)[1][1:].upper()
        if format in ['HTML', 'HTM', 'XHTML']:
            return 'HTML'
        return format

    def serialize(self, force=False, format=None, recursive=False):
        RideSaving(path=self.source).publish()
        if recursive:
            for s in self.suites:
                s.serialize(force, format, recursive)
        old_source = self._change_format_if_needed(format)
        if not (self.dirty or force):
            return
        self._validate_serialization()
        try:
{'fraction_non_alphanumeric': 0.08563462559745087, 'fraction_numerical': 0.002588953797132236, 'mean_word_length': 2.769076807605704, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7529112', 'n_tokens_mistral': 4363, 'n_tokens_neox': 4219, 'n_words': 1102}