예제 #1
0
def process_color_definition(property_line, line_number):
    # tries to match the color regex and retrieves the resulting
    # set of groups from the match, in case there's no matching
    # the control flow is returned immediately to the caller
    line_match = COLOR_REGEX.match(property_line)
    line_groups = line_match.groups() if line_match else None
    if not line_groups: return property_line

    # unpacks the various line groups into the appropriate
    # variables to be used in the processing of the color
    property_name, pre_color, color, post_color = line_groups

    try:
        # runs the fixing color operation and then, re-joins the
        # various groups again into a single string (from list)
        color = fix_color(color)
        line_groups = [property_name, pre_color, color, post_color]
        property_line = "".join(line_groups)
    except Exception as exception:
        # converts the exception to string and then
        # prints a warning to the proper output
        exception_string = legacy.UNICODE(exception)
        extra.warn("%s near line %d" % (exception_string, line_number))

    # returns the property line
    return property_line
예제 #2
0
def get_property_index(property_line, property_order, line_number):
    """
    Retrieves the index for the property specified in the provided property
    line, this index may be used to correctly position the property.

    :type property_line: String
    :param property_line: The property line containing the property.
    :type property_order: List
    :param property_order: The list of property names in order.
    :type line_number: int
    :param line_number: The approximate line number for this processing.
    :rtype: int
    :return: The index of the property line, note that in case an index
    value is not found for the rule a large index is returned instead.
    """

    # splits the property name, to retrieve the property value
    # this first splits tries to find also the number of values
    # present in the current line
    property_line_splitted = property_line.split(":")

    # in case the property line did not correctly split must
    # returns immediately with the provided property order
    if len(property_line_splitted) < 2: return len(property_order)

    # runs a second split operation that limits the number of splits
    # in the line to two so that no extra problems are raised and then
    # retrieves the name of the current property
    property_line_splitted = property_line.split(":", 1)
    property_name, property_value = property_line_splitted
    property_name = property_name.strip()
    property_value = property_value.strip()

    # in case the property name or value are empty, raises an exception indicating
    # the problem to be handled at the top layers
    if not property_name: raise Exception("property name is empty")
    if not property_value: raise Exception("property value is empty")

    # in case the property is not in the order
    if not property_name in property_order:
        # warns about the missing property name and returns the
        # largest possible index value for any property name
        extra.warn(
            "Order for property %s not defined at line %d" %\
            (property_name, line_number)
        )
        return len(property_order)

    # in case the property value starts with a separator it's considered
    # that the value is a extra/duplicated separator
    if property_value[0] == ":":
        # warns about the extra values in the line, so that the duplicated
        # information may be properly removed and considered
        extra.warn("Extra values found at line %d" % line_number)

    # determines the index for the property name and returns
    # the value to the caller method
    property_index = property_order.index(property_name)
    return property_index
예제 #3
0
def process_property_line(property_line, line_number, indentation):
    # in case the property line is empty, when stripped the property
    # is considered to be empty (warning required) logs the message
    # and then returns the line itself (no processing)
    if not property_line.strip():
        extra.warn("Empty stylesheet property at line %s" % line_number)
        return property_line

    # in case no separator is present between the name of the property
    # the value of it a warning is raised and the value returned immediately
    # to avoid further (unavoidable) errors
    if not ":" in property_line:
        extra.warn("No value set for property at line %s" % line_number)
        return property_line

    # strips the line to the right so that no newline characters
    # exist (much simpler to manage lines without newlines)
    property_line = property_line.rstrip()
    property_line = property_line.rstrip(";")

    # runs the initial process of simplifying the property by running
    # the various rules for the current property name
    property_line = process_rules(property_line, line_number)

    # ensures the property line is correctly indented and
    # the property name and value are correctly separated
    padding = indentation * " " * 4
    property_line = PROPERTY_LINE_REGEX.sub(
        padding + PROPERTY_LINE_REPLACEMENT_VALUE,
        property_line
    )

    # ensures the property line and an ending semicolon
    # adding it to the property line in case it does not
    # exists (for processing)
    is_valid = property_line.endswith(";")
    if not is_valid: property_line += ";"

    # replaces the urls so that no double quotes are used in
    # them and instead the value is used directly
    property_line = URL_REGEX.sub(URL_REPLACEMENT_VALUE, property_line)

    # processes the color definitions, so that the complete
    # color value is used in the processing
    property_line = process_color_definition(property_line, line_number)

    # returns the processed property line
    return property_line
예제 #4
0
def check_duplicates(property_lines, line_number):
    # retrieves the complete set of names for the rules, this is
    # going to be used as the base unit for duplicate checking
    names = [line.strip().split(":", 1)[0] for line in property_lines]

    # starts the sequence/list that is going to store the already
    # visited sequence of names (for duplicate detection)
    visited = []

    # iterates over the complete set of names to detect any duplicated
    # value printing a warning message for such situation
    for name in names:
        if name in visited: extra.warn(
            "Duplicated property line '%s' at line %s" %\
            (name, line_number)
        )
        visited.append(name)
예제 #5
0
def cleanup_properties(
    input_buffer,
    windows_newline = True,
    fix_extra_newlines = True,
    property_order = (),
    rules_skip = ()
):
    """
    Cleans up the property lines. Sorts the css properties in the file,
    by the specified property order.

    Ensures that all property lines are correctly terminated with semicolon.

    :type input_buffer: StringBuffer
    :param input_buffer: The input buffer.
    :type windows_newline: bool
    :param windows_newline: If the windows newline should be used.
    :type windows_newline: bool
    :param windows_newline: If the extra newlines should be fixed.
    :type fix_extra_newlines: bool
    :param fix_extra_newlines: If the extra newlines should be fixed.
    :type property_order: List
    :param property_order: The list with the explicit order of property names.
    :type rules_skip: List
    :param rules_skip: The list of specific rules to skip.
    """

    # reads the input lines and counts the number of lines
    # in the buffer setting it as the number of "original" lines
    lines = input_buffer.readlines()
    number_original_lines = len(lines)

    # creates a string buffer that will hold the output of the
    # cleanup operation over the current stylesheet file
    output_buffer = legacy.StringIO()

    # initializes a series of state variables that will control
    # the way the parser/generator will work through the file
    rule_started = False
    line_number = 0
    open_rule_count = 0
    newlines = 0
    comments_started = 0
    needs_newline = False

    # for each of the input lines it's going to run the iteration
    # loop and match it against the proper rules
    for line in lines:
        # decodes the current line as a processing using unicode
        # characters is required for correct results
        line = line.decode("utf-8")

        # increments the line number as one more line is going
        # to be processes through the current iteration
        line_number += 1

        # updates the comparison key function
        get_comparison_key = lambda property_line: get_property_index(
            property_line,
            property_order,
            line_number
        )

        # in case the line contains a single line comment,
        # does nothing, will write the line as is
        if "/*" in line and "*/" in line and not "*/*/" in line:
            pass

        # in case the line contains the end of multiline comment
        elif "*/" in line:
            # in case the comment mode is currently not enabled prints
            # a warning as we're trying to close a comment without opening
            if not comments_started: extra.error(
                "Found closing comment without corresponding opening at line %d" % line_number
            )

            # in case there's more that one closing comment defined under
            # the same line prints an error as this is not allowed
            if line.count("*/") > 1: extra.error(
                "More that one closing comment at line %d" % line_number
            )

            # decrements the comments started counter and then
            # enables the needs newline flag
            comments_started = 0
            needs_newline = True

        # in case the line contains a the start of multiline comment
        elif "/*" in line:
            # in case the comment mode is already enabled (at least one)
            # prints a waning about the double opening
            if comments_started: extra.warn(
                "Found opening comment inside open comment at line %d" % line_number
            )

            # in case there's more that one opening comment defined under
            # the same line prints an error as this is not allowed
            if line.count("/*") > 1: extra.error(
                "More that one opening comment at line %d" % line_number
            )

            # increments the comments started counter
            comments_started += 1

        # in case this is a comment line (comment started)
        # does nothing, will just write the line as is
        elif comments_started:
            pass

        # in case the line contains an at rule specification
        elif AT_RULES_REGEX.match(line):
            # after an at rule, a newline must follow
            needs_newline = True

        # in case the line contains a full rule specification
        # does nothing, will just write line as is as there's
        # very few thing possible to optimize under this situation
        elif "{" in line and "}" in line:
            line = line.strip()
            needs_newline = True
            rule_started = False

        # in case this is a rule start line
        elif "{" in line:
            # increments the open rule count
            open_rule_count += 1

            # in case this is an actual rule, meaning that the open rule
            # count value is positive (assertion valid)
            if open_rule_count > 0:
                # resets the rule lines
                rule_lines = []

                # signals the rule started flag,
                # in case the rule is no to be skipped
                rule_started = not skip_rule(line, rules_skip)

            # replaces the various css tokens with proper separators
            # meaning that no extra spaces between them are allowed
            line = TOKEN_REGEX.sub(r" \1 ", line)

            # runs the space replacement operation so that the
            # extra spaces in the line (not required) are removed
            # and the line is simplified
            line = SPACE_REGEX.sub(" ", line)
            line = line.strip()

            # calculates the space padding that is going to be pre-pended
            # to the line and then adds it to the line note that the open
            # rule count is decremented by one (opened on this line)
            padding = (open_rule_count - 1) * 4 * " "
            line = padding + line

            # flushes the current line value to the output buffer and then
            # continues the current loop (avoids general operations)
            write_line(output_buffer, line, windows_newline)
            continue

        elif "}" in line:
            # decrements the open rule count
            open_rule_count -= 1

            # in case there is a mismatch in open and closed rules
            # must raise an exception indicating the problem
            if open_rule_count < 0: raise Exception("mismatched rules found")

            # strips the current line removing any extra (not expected)
            # values and simplifying the current line value
            padding = open_rule_count * 4 * " "
            line = line.strip()
            line = padding + line

            # in case the rule set does not contain any property
            # must log an information message about the empty rule
            if rule_started and not rule_lines:
                extra.warn("Empty stylesheet rule at line %d" % line_number)

            # updates the flag to signal the rule has ended
            rule_started = False

            # sorts the various property lines and the processes them
            property_lines = sorted(rule_lines, key = get_comparison_key)
            property_lines = process_property_lines(
                property_lines,
                line_number,
                indentation = open_rule_count + 1,
                avoid_empty = fix_extra_newlines
            )
            property_lines = sorted(property_lines, key = get_comparison_key)

            # writes the lines to the buffer, considering the windows newline
            # and then writes the line
            write_lines(
                output_buffer,
                property_lines,
                windows_newline = windows_newline,
                avoid_empty = fix_extra_newlines
            )
            write_line(output_buffer, line, windows_newline = windows_newline)

            # resets the newlines counter and then
            # enables the needs newline flag
            newlines = 0
            needs_newline = True
            rule_lines = []

            # skips further processing
            continue

        # in case this is part of rule selector declaration
        # the typical rule replacement operations are applied
        elif line.rstrip().endswith(","):
            line = TOKEN_REGEX.sub(r" \1 ", line)
            line = SPACE_REGEX.sub(" ", line)
            line = COMMA_REGEX.sub(r"\1", line)
            line = line.strip()

        # in case this line is part of a valid rule set
        elif rule_started:
            # appends the line to the rule set, and then
            # skips outputting the line to the buffer
            rule_lines.append(line)
            continue

        # in case the between rules mode is active
        elif not rule_started and not line.strip():
            # increments the newlines count
            newlines += 1

            # otherwise in case this is an extra newline, must either
            # remove it or print a warning message depending on mode
            if not needs_newline and newlines > 1:
                if fix_extra_newlines: continue
                else: extra.warn("Found extra newline at line %d" % line_number)

            # disables the needs newline flag
            needs_newline = False

        else:
            # warns about the statement outside a valid rule
            extra.warn("Found statement outside rule at line %d" % line_number)

        # "calculates" the padding string value taking into account the
        # current open rule count value and then removes any left space
        # compatible values and replaces them with proper indentation
        padding = open_rule_count * 4 * " "
        line = line.lstrip()
        line = padding + line

        # writes the line to the output buffer taking into
        # account the windows newline control flag
        write_line(output_buffer, line, windows_newline)

    # in case there is a mismatch in open and closed rules
    # must raise an exception indicating the problem
    if not open_rule_count == 0: raise Exception("mismatched rules found")

    # retrieves the output buffer value and then counts the
    # number of lines contained in it (assertion validation)
    output_buffer_value = output_buffer.getvalue()
    number_lines = output_buffer_value.count("\n")

    if not number_lines == number_original_lines and not fix_extra_newlines:
        raise Exception(
            "number of lines in processed file (%d) is different from original file (%d)" %\
            (number_lines, number_original_lines)
        )

    return output_buffer
예제 #6
0
def convert_encoding_walker(arguments, directory_name, names):
    """
    Walker method to be used by the path walker for the encoding conversion.

    :type arguments: Tuple
    :param arguments: The arguments tuple sent by the walker method.
    :type directory_name: String
    :param directory_name: The name of the current directory in the walk.
    :type names: List
    :param names: The list of names in the current directory.
    """

    # unpacks the arguments tuple
    source_encoding, target_encoding, windows_newline,\
    replacements_list, file_extensions, file_exclusion = arguments

    # tries to run the handle ignore operation for the current set of names and
    # in case there's a processing returns the control flow immediately as no
    # more handling is meant to occur for the current operation (ignored)
    if extra.handle_ignore(names): return

    # removes the complete set of names that are meant to be excluded from the
    # current set names to be visit (avoid visiting them)
    for exclusion in file_exclusion:
        if not exclusion in names: continue
        names.remove(exclusion)

    # retrieves the valid names for the names list (removes directory entries)
    valid_complete_names = [directory_name + "/" + name\
        for name in names if not os.path.isdir(directory_name + "/" + name)]

    # filters the names with non valid file extensions
    valid_complete_names = [os.path.normpath(name) for name in valid_complete_names\
        if file_extensions == None or os.path.split(name)[-1].split(".")[-1] in file_extensions]

    # creates the string based value of the windows newline taking into
    # account the boolean value of it
    windows_newline_s = "windows newline" if windows_newline else "unix newline"

    # iterates over all the valid complete names with extension filter
    # to convert the respective file into the target encoding
    for valid_complete_name in valid_complete_names:
        # prints a message about the file that is not going to be converted
        # into the proper target encoding as defined in the specification
        extra.echo(
            "Convert encoding in file: %s (%s to %s) (%s)" %\
            (
                valid_complete_name,
                source_encoding,
                target_encoding,
                windows_newline_s
            )
        )

        try:
            # converts the encoding for the provided (path) name according to
            # a set of defined options, for various reasons this operation may
            # fail if such thing happens the operation is skipped
            convert_encoding(
                valid_complete_name,
                source_encoding,
                target_encoding,
                windows_newline,
                replacements_list
            )
        except Exception:
            extra.warn(
                "Failed converting encoding in file: %s (%s to %s)" %\
                (
                    valid_complete_name,
                    source_encoding,
                    target_encoding
                )
            )
예제 #7
0
def pydev_file(file_path, fix = True):
    """
    Runs the pydev configuration file normalization that consists
    in the definition in order of each of the xml lines.

    This operation should fail with an exception in case the
    structure of the xml document is not the expected one.

    :type file_path: String
    :param file_path: The path to the file that contains the
    pydev configuration specification in xml.
    :type fix: bool
    :param fix: If any "fixable" error in the pydev project
    file should be automatically fixes using the known heuristics,
    this is a dangerous option as errors may be created.
    """

    paths = []
    properties = dict()
    buffer = []

    xmldoc = xml.dom.minidom.parse(file_path)
    nodes = xmldoc.getElementsByTagName("pydev_property")

    for node in nodes:
        value = text_value(node)
        name = node.attributes["name"].value
        properties[name] = value

    nodes = xmldoc.getElementsByTagName("pydev_pathproperty")
    nodes = nodes[0].childNodes if nodes else []

    for node in nodes:
        value = text_value(node)
        if not value: continue
        paths.append(value)

    for key in legacy.keys(properties):
        if key in VALID_PROPERTIES: continue
        raise RuntimeError("Invalid property '%s'" % key)

    if fix: paths, properties = fix_values(paths, properties)

    python_version = properties.get("org.python.pydev.PYTHON_PROJECT_VERSION", None)
    if not python_version: extra.warn("No python version defined")
    elif not python_version == "python 2.6": extra.warn("Python version not 2.6")

    for path in paths:
        if path.startswith("/${PROJECT_DIR_NAME}"): continue
        extra.warn("Project directory path not normalized '%s'" % path)

    property_keys = legacy.keys(properties)
    property_keys.sort()

    buffer.append("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n")
    buffer.append("<?eclipse-pydev version=\"1.0\"?><pydev_project>\n")
    if paths: buffer.append("<pydev_pathproperty name=\"org.python.pydev.PROJECT_SOURCE_PATH\">\n")
    for path in paths:
        buffer.append("<path>%s</path>\n" % path)
    if paths: buffer.append("</pydev_pathproperty>\n")
    for key in property_keys:
        value = properties[key]
        buffer.append("<pydev_property name=\"%s\">%s</pydev_property>\n" % (key, value))
    buffer.append("</pydev_project>\n")

    result = "".join(buffer)
    result = result.encode("utf-8")

    file = open(file_path, "wb")
    try: file.write(result)
    finally: file.close()