Пример #1
0
    def should_ignore_line(self, current_line, content):
        ''' Wil ignore the line if it is:
        - an import (using)
        - defines a namespace. In this case, it will update the
          current_namespace value.

        :see:
            `AbstractStaticTypeFileParser.should_ignore_line`
        '''
        if has_any_keyword(current_line, ['using']):
            self.index += 1
            return True
        elif has_any_keyword(current_line, ['namespace']):
            if not '{' in current_line:
                # if the "{" isn't in the next line, then
                # it will be in the following..
                self.index += 1
            self.index += 1
            return True
        elif '}' in current_line and not has_any_keyword(
                current_line, CLASS_KEYWORDS):
            # it is closing the current namespace
            self.index += 1
            return True
        else:
            return False
Пример #2
0
    def is_method(self, current_line, current_class):
        ''' Overrides the is method to take into account that
        the line could be an operator definition, or the
        properties of an attribute.

        :parameters:
            content: list(str)
                the content of the file
            current_class: `pywebuml.models.Class`
                the current class that is being parsed.

        :returns:
            True if the current line is a method definition
            else False.
        '''
        if has_any_keyword(current_line, ['operator']):
            # Take into account if the line is an operator definition.
            #   public static bool operator !=(Foo lhs, Foo rhs)
            return True

        # check if it is a method defined in one line
        # or the properties (rare which has a method that uses (
        # of the attribute. For example:
        #    public OnDemand() { b = 3; }
        #    public int attr { get { Debug.Log("Foo"); value; } }
        elif has_any_keyword(current_line, ['get', 'set']):
            return False
        else:
            return super(CSharpClassParser,
                         self).is_method(current_line, current_class)
Пример #3
0
 def test_has_keyword(self):
     ''' Some basic tests for has keyword.
     '''
     self.assertTrue(has_any_keyword('this is KEYWORD', ['KEYWORD']))
     self.assertFalse(has_any_keyword('this is keyword', ['KEYWORD']))
     self.assertFalse(has_any_keyword('this is KEYWORDs', ['KEYWORD']))
     self.assertFalse(has_any_keyword('this isKEYWORD', ['KEYWORD']))
Пример #4
0
 def test_has_keyword_more_than_one(self):
     ''' Test what happens to has_keyword when using a list
     that has more than one element.
     '''
     self.assertTrue(has_any_keyword('KEY this is WORD', ['KEY', 'WORD']))
     self.assertTrue(has_any_keyword('KEY this is WORD', ['KEY', 'FOO']))
     self.assertTrue(has_any_keyword('KEY this is WORD', ['FOO', 'WORD']))
     self.assertFalse(has_any_keyword('KEY this is WORD', ['FOO', 'BAR']))
Пример #5
0
 def _get_data(self, content):
     ''' Parse the file to identify the package for each class.
     '''
     for line in content:
         if has_any_keyword(line, ['package']):
             package = remove_keywords(line, ['package'])
             package = package.replace(';', '')
             self.current_package = package
         elif has_any_keyword(line, ['import']):
             import_data = remove_keywords(line, ['import'])
             import_data = import_data.replace(';', '')
             if not import_data.endswith('*'):
                 class_name = import_data.rsplit('.', 1)[1]
                 self.classes_packages[class_name] = import_data
Пример #6
0
    def parse_namespaces(self, content):
        ''' Creates a list for each index, which namespace should be
        used. For example, if the list is
            [(0, 'global'), (3, 'unity'), (10, 'global')]

        for the class definitions between 0 and 2 the namespace that should
        be used is 'global', for the ones between 3 and 9 is **unity** and
        finally for all the ones between 10 and the end of the file is
        **global**.

        :parameters:
            content: list(str)
                the cleaned content of the file.
        '''
        if not has_any_keyword(content[0], ['namespace']):
            self.indexes_and_namespaces.append((0, 'global'))
        index = 0

        while True:
            if index >= len(content):
                break

            line = content[index]
            if has_any_keyword(line, ['namespace']):
                namespace = remove_keywords(line, ['namespace'])
                opened_blocks = 0
                if '{' in namespace:
                    opened_blocks = 1
                    # TODO missing test for the strip, and this case
                    namespace = namespace[: namespace.index('{')].strip()

                # go to the end of the namespace
                self.indexes_and_namespaces.append((index, namespace))
                while True:
                    index += 1
                    line = content[index]
                    opened_blocks += line.count('{') - line.count('}')
                    if opened_blocks == 0:
                        break

                if (index +1) < len(content) and not has_any_keyword(content[index+1], ['namespace']):
                    # if there are more lines in the file, and it doesn't
                    # defines a new namespace, then it has to
                    # restore the previous namespace
                    self.indexes_and_namespaces.append((index+1, 'global'))
            else:
                # this is the case of a previous namespace or
                # the global namespace.
                index += 1
Пример #7
0
    def should_ignore_line(self, current_line, content):
        ''' Ignore the lines that are the package or import classes.
        '''
        if has_any_keyword(current_line, ['package', 'import']):
            self.index += 1
            return True

        return False
Пример #8
0
 def should_skip_language_methods(self, signature):
     ''' Skip all the methods that belong to the Java Object methods.
     '''
     if '(' in signature:
         # remove the params of the signature just to be sure, an take
         # into account that the keywords doesn't have (. Because
         # of this it won't find:
         #       public String toString()
         signature = signature[:signature.index('(')]
     return has_any_keyword(signature, JAVA_LANGUAGE_METHODS)
Пример #9
0
    def get_class_definition_data(self, class_definition, content):
        ''' Returns the class_name, interfaces, and base classes.
        In this case, it will use the content to search for the base
        and interfaces classes packages.

        :see:
            `AbstractStaticTypedClassParser`.get_class_definition_data
        '''
        class_name = []
        base_classes = []
        implemented_interfaces = []

        # TODO check which keyword is first: the extends or implements
        # TODO check if in Java is required that base clasess should
        # go before implemented classes.
        if has_any_keyword(class_definition, ['implements']):
            # the +12 is because len(' implements ') == 12
            implementations_data = class_definition[class_definition.
                                                    index(' implements ') +
                                                    12:]

            # remove the implements part of the class_definitions
            class_definition = class_definition[:class_definition.
                                                index(' implements ')]
            for implemented_class in implementations_data.split(','):
                implemented_interfaces.append(
                    self.get_class_package(implemented_class.strip(), content))

        if has_any_keyword(class_definition, ['extends']):
            # the +9 is becuase len(' extends ') == 9
            # TODO there could be a problem with this and template values....
            base_data = class_definition[class_definition.index(' extends ') +
                                         9:]
            class_definition = class_definition[:class_definition.
                                                index(' extends ')]
            for base_class in base_data.split(','):
                base_classes.append(
                    self.get_class_package(base_class.strip(), content))

        class_name = class_definition.strip()
        return class_definition, base_classes, implemented_interfaces
Пример #10
0
    def has_preprocessor_directive(self, content):
        ''' Identifies if the content of the file has a preprocesor directive.

        :parameters:
            content: list(str)
                the lines of the file.

        :returns:
            True if the file has a preprocesor directive.
        '''
        for line in content:
            if has_any_keyword(line, PREPROCESSOR_DIRECTIVES):
                return True

        return False
Пример #11
0
    def get_class_definition_data(self, class_definition, content):
        ''' Returns the interfaces, base class and class name of the
        class_definition. All the packages of the interfaces and of the
        base class will always be **global**.

        :see:
            `AbstractStaticTypedClassParser`.get_class_definition_data
        '''
        implemented_interfaces = []
        base_classes = []
        class_name = ''

        if ':' in class_definition:
            class_name = class_definition[:class_definition.index(':')]
            are_template_constrains = False

            # check if the class is a singlton, and
            # the template might have some constrains.
            if '<' in class_name:
                # in this case is a template that has some constrains
                # TODO falta testear...
                if has_any_keyword(class_name, ['where']):
                    #TODO keyword bug....
                    class_name = class_name[:class_name.index('where')].strip()
                    are_template_constrains = True

            if not are_template_constrains:
                class_extensions = class_definition[class_definition.
                                                    index(':') + 1:].strip()
                for extension in class_extensions.split(','):
                    extension = extension.strip()
                    if extension.startswith('I'):
                        # TODO falta checkear que la segunda palabra tambien sea
                        # una mayuscula
                        implemented_interfaces.append('global.%s' % extension)
                    else:
                        base_classes.append('global.%s' % extension)
        else:
            class_name = class_definition

        class_name = class_name.strip()
        return class_name, base_classes, implemented_interfaces
Пример #12
0
    def _has_preprocessor_value(self, line, preprocessors_to_search):
        ''' Identifies if the line has any of the preprocesosr values,
        even if they have a space.

        :parameters:
            line: str
                the current line of the file
            preprocessors_to_search: list(str)
                the list of preprocessors directives to search in the line
                it will take into account that they can have an space. They
                must have the #.
                For example:
                    # region public

        :returns:
            Ture if any preprocesor is found.
        '''
        keywords = line.split(' ')
        if not '#' in line:
            return False
        elif has_any_keyword(line, preprocessors_to_search):
            return True
        else:
            keywords = line.split(' ')
            if len(keywords) == 1:
                # in this case the line is a preprocessor directive but
                # isnt the one I am looking for.
                return False

            if not '#' in keywords:
                # in this case the # is used as a value. For example:
                #   public string foo = '#';
                return False

            symbol_position = keywords.index('#')

            directives = map(lambda directive: directive.replace('#', ''),
                             preprocessors_to_search)
            return keywords[symbol_position + 1] in directives
Пример #13
0
    def parse(self, class_model, content, index, package_manager):
        ''' Parse the method of the class of the content.

        :parameters:
            class_model:  ``pywebuml.model.Class``
                the current class that is being parsed.
            content: list(str)
                the content of the file
            index: int
                from where start parsing
            package_manager: `pywebuml.parsers.static_typed.package_manager`
                the package manager to use to get the referenced class
                complete path.

        :return:
            a tuple (pywembuml.models.Method, int) that has the parsed
            method and the index value from where it should continue reading.

        :raises:
           - `pywebuml.initialize.parser.exceptions.ClossingArrayException`
              if it can't find the closing } of the array definition

           - `pywebuml.initialize.parser.exceptions.EmptyAttributeNameException`
              if it cant get the name of the attribute.
        '''
        current_position = index
        current_line = content[index]

        visibility = get_visibility(current_line)
        is_static = has_any_keyword(current_line, ['static'])
        is_final = has_any_keyword(current_line, CONST_KEYWORDS)

        current_line = remove_keywords(current_line, MODIFIERS)

        # iterate the property methods of the attribute.
        if self.should_update_index(current_line, content, current_position,
                                    class_model):
            opened_keys = 0
            opened_parenthesis = 0
            found_equals = False

            # before starting checking unitl which position, the index should
            # move, it check from where it should start.
            if (current_line.endswith('=') or '=' not in current_line) \
                    and not '{' in current_line:
                # in this case, the line should be changed because the value
                # is in the current line. For example:
                #       private i =
                #               3;
                current_position += 1

            elif '{' not in current_line and content[current_position +
                                                     1].startswith('{'):
                # This is the case of an array definition. For example
                #       private int[] k = new int[]
                #           { 1, 2, 3 }
                current_position += 1

            while True:
                if current_position == len(content):
                    # TODO change the value of this because this isn't only for arrays...
                    raise ClossingArrayException(index)

                line = content[current_position]
                opened_keys = opened_keys + line.count('{') - line.count('}')
                opened_parenthesis = opened_parenthesis + line.count(
                    '(') - line.count(')')
                current_position += 1

                if opened_keys == 0 and opened_parenthesis == 0 and (
                        line.endswith(';') or line.endswith('}')):
                    # this is for cases like:
                    #   private String text = "This is a long text" +
                    #           "this continues here" +
                    #           "and finally ends here";
                    # the endswith } is for taking into account C# properties.
                    # TODO remove this from here it take it into c# parser.
                    break

        current_line = self.clean_line(current_line)
        if self.should_skip_language_attributes(current_line):
            if current_position == index:
                current_position += 1
            return (current_position, None)

        # can't use current_line.split(' '), becuase there can be more than
        # one space between the type and variable definition
        variable_type, name = get_type_and_name(current_line, index,
                                                class_model)
        referenced_class_package = self.get_referenced_type(
            variable_type, package_manager)

        if index == current_position:
            # in this case the variable is of 1 line...
            #       private int j;
            current_position += 1

        LOGGER.debug("Found attribute: %s", name)
        return (current_position,
                Attribute(name, variable_type, class_model, visibility,
                          is_static, is_final, referenced_class_package))
Пример #14
0
    def parse_content(self, filepath, content):
        ''' It will parse the content of the file.

        If the file has a compiler directive, then it will be ignore
        it by returning an empty list, because it is very difficult
        to take into account the changes that affect the compiler.


        :parameters:
            filepath: str
                the path and name of the file. The path will
                be realive to where the command was executed.
            content: list(str)
                the content of the file already modified
                by the base parser.

        :returns:
            a list of pywebuml.models.Class with it's
            methods and attributes.
        '''
        res = []
        if not content:
            # the file is empty
            return []
        content = remove_comments(content)

        if not content:
            # the whole file was a comment.
            return []

        content = self.clean_content(content)
        current_line = None
        self.class_parser = self.get_class_parser(content)

        while True:
            current_line = content[self.index]

            if self.should_ignore_line(current_line, content):
                pass
            elif has_any_keyword(current_line, CLASS_KEYWORDS):
                # To take into account that some methods or enums, could
                # be defined before the class.
                self.index = self.parse_class_definition(filepath, content, self.index, res)
            else:
                # iterate the method or enum that is outside the class
                opened = current_line.count('{') - current_line.count('}')
                current_position = self.index
                opened_keys = 0
                while True:
                    line = content[current_position]
                    opened_keys = opened_keys + line.count('{') - line.count('}')
                    current_position += 1

                    if opened_keys == 0:
                        break
                self.index = current_position


            if self.index >= len(content):
                break

        return res
Пример #15
0
 def is_other_type(self, current_line):
     ''' Returns true if the class is partial.
     '''
     return has_any_keyword(current_line, ['partial'])
Пример #16
0
    def parse(self, class_model, content, index):
        ''' Parse the method of the class of the content.

        :parameters:
            class_model:  ``pywebuml.model.Class``
                the current class that is being parsed.
            content: list(str)
                the content of the file
            index: int
                from where start parsing

        :return:
            a tuple (pywembuml.models.Method, int) that has the parsed
            method and the index value from where it should continue reading.

        :raises:
        - MissingClosingParenthesis: if it cant find the closing ) of
                                         the method signature

        '''
        current_position = index
        current_line = content[index]

        visibility = get_visibility(current_line)
        is_static = has_any_keyword(current_line, ['static'])
        is_abstract = has_any_keyword(current_line, ABSTRACT_KEYWORDS) \
                        or isinstance(class_model, Interface)

        current_line = remove_keywords(current_line, [visibility])
        current_line = remove_keywords(current_line, MODIFIERS)

        signature = current_line.strip()
        if not ')' in current_line:
            # in this case the signature of the method is in only one
            # line. For example:
            #   public void Foo(int a, int b, int c,
            #                   int d)
            current_position += 1
            while True:

                if current_position >= len(content):
                    raise MissingClosingParenthesis(index)

                signature += content[current_position].strip()
                if ')' in content[current_position]:
                    break
                current_position += 1

        if not is_abstract:
            # now move the index to the end of the method code
            # the abstract method only have thier definitions, so this
            # isn't necessary

            # take into account that the definition of the method can
            # only be of one line.
            if '{' in content[current_position] and \
                    (content[current_position].count('{') ==
                            content[current_position].count('}')):
                current_position += 1
            else:
                opened_keys = 0
                if '{' in content[current_position]:
                    opened_keys = 1

                started_method = False
                current_position += 1
                while True:
                    if current_position == len(content):
                        raise MissingClosingMethod(index)

                    line = content[current_position]
                    started_method = started_method or opened_keys > 0
                    opened_keys = opened_keys + line.count('{') - line.count(
                        '}')
                    current_position += 1
                    # take into account that the next line of the method definition
                    # could be another thing that isn't the openin. For example:
                    #   public Foo()
                    #       : base(1)
                    #   {
                    #       // do something
                    #   }
                    if opened_keys == 0 and started_method:
                        break
        else:
            current_position += 1

        # remove all the code of the signature
        if '{' in signature:
            signature = signature[:signature.index('{')].strip()

        signature = self.clean_language_things(signature)

        if self.should_skip_language_methods(signature):
            return (current_position, None)

        # remove the parameters of the signature
        tmp = signature[:signature.index('(')].strip()
        if len(tmp.split(' ')) == 1:
            # the the method is a constructor, and the name
            # is the value
            name = tmp
            return_type = 'void'
        else:
            return_type, name = get_type_and_name(tmp, index, class_model)

        LOGGER.debug("Found method: %s", name)
        return (
            current_position,
            Method(name, class_model, signature, visibility, is_static,
                   is_abstract),
        )
Пример #17
0
 def should_skip_language_methods(self, signature):
     ''' Skip the method if it is an operator.
     '''
     return has_any_keyword(signature, ['operator'])
Пример #18
0
    def parse(self, filepath, content, index, owner=None):
        ''' Parse the content of the class.

        The index must be setted in the first line of the class definition.

        The result will be a list with more than one element if there is an
        inner class. If there is more than one class definition in the file,
        then this method should be used more than once.

        :parameters:
            filepath: str
                the folder and name of the file. For example:
                    ./examples/mycode.cs
            content: list(str)
                the content of the file.
            owner: pywebuml.model.Class
                the owner of this class. This will only be used if the class
                as inner.

        :return:
            a tuple with the index and the list of class representation. I
        '''
        res = []
        started_definition = index
        if not owner:
            base_class_package = self.package_manager.get_class_package(index)
        else:
            base_class_package = owner.package

        index, current_class = self.parse_class_signature(
            filepath, content, index, base_class_package, owner)
        res.append(current_class)
        position = index - 1

        current_line = content[position]
        if '{' in current_line and '}' in current_line and \
            has_any_keyword(current_line, CLASS_KEYWORDS):
            # then the class definition is of 1 line
            # For example:
            #   public class Foo { }
            return (index, res)

        while True:
            if index == len(content):
                raise MissingClosingClassDefinition(started_definition)

            current_line = content[index]
            if current_line in ('}', '};'):
                # the closing key of the class definition
                # must comprare to equals because the current line could be a
                # one line method, attr or inner class definition
                index += 1
                break

            if has_any_keyword(current_line, CLASS_KEYWORDS):
                index, inner_res = self.parse(filepath, content, index,
                                              current_class)
                res.extend(inner_res)

            elif self.is_method(current_line, current_class):
                index = self.parse_method(content, index, current_class)
            else:
                index = self.parse_attribute(content, index, current_class)

        return (index, res)
Пример #19
0
    def parse_class_signature(self,
                              filepath,
                              content,
                              index,
                              base_class_package,
                              owner=None):
        ''' Returns the `pywebuml.models.Class`  object without any attribute
        or method, but with its implementations and extensions.

        :parameters:
            content: list(str)
                the content of the file
            index: int
                from which position of the content start getting the info.
            base_class_package: str
                the base class package that will be used with the class name
                to get the complete package of the class.
            owner: `pywebuml.models.Class`
                if the class is an inner class, then this is the
                parent class.

        :returns:
            a ``pywebuml.models.Class`` taking into account the
            content of the file.
        '''
        current_line = content[index]

        self.is_interface = has_any_keyword(current_line, ['interface'])
        self.is_abstract = has_any_keyword(current_line, ['abstract'])
        self.is_enum = has_any_keyword(current_line, ['enum'])
        is_other_type = self.is_other_type(current_line)

        class_data = remove_keywords(current_line, CLASS_KEYWORDS)
        class_data = remove_keywords(class_data, MODIFIERS)

        visibility = get_visibility(class_data)
        class_data = remove_keywords(class_data, [visibility])

        # if the class definition doesn't end in that line then
        # it might have some other base_classes in the following lines
        # for example:
        #   public class Foo : A, B, C
        #                      D, E, F
        #                      G {
        finish = False
        current_line = class_data
        class_definition = ''
        current_position = index

        while True:
            current_position += 1
            class_definition += current_line

            # found the end of the class definition
            if '{' in current_line:
                break

            if current_position == len(content):
                raise MissingOpeningClassDefinition()

            current_line = content[current_position].strip()

        # removes that it at the right of the  { becuase
        # that belongs to the class implementation
        class_definition = class_definition[:class_definition.index('{')]

        class_name, base_classes, implemented_interfaces = \
                    self.get_class_definition_data(class_definition, content)

        index = current_position
        LOGGER.debug("Found class: %s", class_name)
        package = '%s.%s' % (base_class_package, class_name)

        # TODO ver como manejar el tema de partial
        if is_other_type:
            klass = self.get_other_type_class_model(package, class_name,
                                                    filepath, self.language,
                                                    owner)
        else:
            klass = self.get_class_model(package, class_name, filepath,
                                         self.language, owner)

        for base_package in base_classes:
            ClassInheritence(klass, base_package)

        for interface_package in implemented_interfaces:
            ClassImplements(klass, interface_package)

        return (index, klass)