Exemple #1
0
def get_declared_license_keys_in_packages(codebase):
    """
    Return a list of declared license keys found in packages.

    A package manifest (such as Maven POM file or an npm package.json file)
    contains structured declared license information. This is further normalized
    as a license_expression. We extract the list of licenses from the normalized
    license expressions.
    """
    packages = chain.from_iterable(
        getattr(res, 'packages', []) or []
        for res in codebase.walk(topdown=True))

    licensing = Licensing()
    detected_good_licenses = []
    for package in packages:
        expression = package.get('license_expression')
        if expression:
            exp = licensing.parse(expression,
                                  validate=False,
                                  strict=False,
                                  simple=True)
            keys = licensing.license_keys(exp, unique=True)
            detected_good_licenses.extend(keys)
    return detected_good_licenses
def _parse_licence_expression(licensing: Licensing,
                              licence_expression: str) -> LicenseExpression:
    # Removing any unwanted characters so that the expression follows the laws:
    # > the valid characters are: letters and numbers, underscore, dot, colon or hyphen signs and spaces
    expression = re.sub(LICENCE_NON_ACCEPTED_CHARACTERS, "",
                        licence_expression)
    expression = re.sub(r"\([sS]ee [\w\s\.\-]*\)", "", expression)
    return licensing.parse(expression)
Exemple #3
0
def detect_using_name_mapping(declared):
    """
    Return a license expression detected from a declared_license.
    """
    declared = declared.lower()
    detected = get_declared_to_detected().get(declared)
    if detected:
        licensing = Licensing()
        return str(licensing.parse(detected, simple=True))
    def test_basic(self):
        setup()
        print("project:    " + str(project))
        print("deps:       " + str(project.dependencies_pile()))

        license_set = project.license_set()
        print("set:        " + str(license_set))

        dep_pile = project.dependencies_pile()

        tot_combinations = 1
        print("license_set: " + str(license_set))
        for proj in dep_pile:
            proj_license = proj['license']
            set_list = license_handler.license_expression_list(
                proj_license, False).set_list
            size = len(set_list)
            tot_combinations = tot_combinations * size
            print(" * project " + str(proj['name']) + " (" + str(size) + ")")
            for license_set in set_list:
                pass
                #print("   * " + str(license_set))
                #for lic in license_set:
                #    print("     * " + str(lic) + ": " )
        #print("dependencies:         " + str(len(dep_pile)))
        #print("expanded:             " + ','.join(map(str, project.expanded_projects())))
        print(str(project.project_combination_list()))
        #print("license combinations: " + str(self.projects_combinations(license_handler, dep_pile, True)))
        #print("license combinations: " + str(self.projects_combinations(license_handler, dep_pile, False)))

        #expanded_project = ProjectExpander(project, license_handler)
        #print("expanded_project: " + str(expanded_project))

        lic = Licensing()
        EXP = "MIT or MPL-1.1"
        parsed = lic.parse(EXP)
        simplified = parsed.simplify()
        #print("lic: " + parsed)
        print("lic: " + str(parsed))
        print("lic: " + str(lic.parse(EXP).simplify()))
        print("lic: " + str(parsed.simplify()))
        print("lic: " + str(simplified))
Exemple #5
0
    def get_licence(cls) -> tuple:
        """Get tuple of licence information of application package."""
        # Get string representation of licence in SPDX format
        licence = getattr(cls, "licence", None)

        default_dict = {
            "isDeprecatedLicenseId": False,
            "isFsfLibre": False,
            "isOsiApproved": False,
            "licenseId": "unknown",
            "name": "Unknown Licence",
            "referenceNumber": -1,
            "url": "",
        }
        if licence:
            # Parse licence string into object format
            licensing = Licensing(LICENSES.keys())
            parsed = licensing.parse(licence).simplify()
            readable = parsed.render_as_readable()

            # Collect flags about licence combination (drop to False if any licence is False)
            flags = {
                "isFsfLibre": True,
                "isOsiApproved": True,
            }

            # Fill information dictionaries with missing data
            licence_dicts = []
            for symbol in parsed.symbols:
                # Get licence base information, stripping the "or later" mark
                licence_dict = LICENSES.get(symbol.key.rstrip("+"), None)

                if licence_dict is None:
                    # Fall back to the default dict
                    licence_dict = default_dict
                else:
                    # Add missing licence link to SPDX data
                    licence_id = licence_dict["licenseId"]
                    licence_dict["url"] = f"https://spdx.org/licenses/{licence_id}.html"

                # Drop summed up flags to False if this licence is False
                flags["isFsfLibre"] = flags["isFsfLibre"] and licence_dict["isFsfLibre"]
                flags["isOsiApproved"] = flags["isOsiApproved"] and licence_dict["isOsiApproved"]

                licence_dicts.append(licence_dict)

            return (readable, flags, licence_dicts)
        else:
            # We could not find a valid licence
            return ("Unknown", [default_dict])
Exemple #6
0
def is_valid_license_expression(expression):
    """Checks if the given string is a valid SPDX License Expression.

    >>> print(is_valid_license_expression('MIT or GPL-2.0'))
    MIT OR GPL-2.0

    :param expression: The expression that needs to be verified.
    :type expression: str
    :returns: str or None

    """
    licensing = Licensing()
    try:
        if(expression == ''):
            return expression
        else:
            parsed = licensing.parse(expression)
            return parsed.render('{symbol.key}')
    except:
        return None
Exemple #7
0
def parse_structured_copyright_file(
    copyright_file,
    skip_debian_packaging=True,
    simplify_licenses=True,
    unique=True,
):
    """
    Return a tuple of (declared license, detected license_expression,
    copyrights) strings computed from the `copyright_file` location. For each
    copyright file paragraph we treat the "name" as a license declaration. The
    text is used for detection and cross-reference with the declaration.

    If `skip_debian_packaging` is True, the Debian packaging license --if
    detected-- is skipped.

    If `simplify_licenses` is True the license expressions are simplified.

    If `unique` is True, repeated copyrights, detected or declared licenses are
    ignore, and only unique detections are returne.
    """
    if not copyright_file:
        return None, None, None

    deco = DebianCopyright.from_file(copyright_file)

    declared_licenses = []
    detected_licenses = []
    copyrights = []

    deco = fix_copyright(deco)

    licensing = Licensing()
    for paragraph in deco.paragraphs:

        if skip_debian_packaging and is_debian_packaging(paragraph):
            # Skipping packaging license and copyrights since they are not
            # relevant to the effective package license
            continue

        if isinstance(paragraph, (CopyrightHeaderParagraph, CopyrightFilesParagraph)):
            pcs = paragraph.copyright.statements or []
            for p in pcs:
                p = p.dumps()
                # avoid repeats
                if unique:
                    if p not in copyrights:
                        copyrights.append(p)
                else:
                    copyrights.append(p)

        if isinstance(paragraph, CatchAllParagraph):
            text = paragraph.dumps()
            if text:
                detected = get_normalized_expression(text, try_as_expression=False)
                if not detected:
                    detected = 'unknown'
                detected_licenses.append(detected)
        else:
            plicense = paragraph.license
            if not plicense:
                continue

            declared, detected = detect_declared_license(plicense.name)
            # avoid repeats
            if unique:
                if declared and declared not in declared_licenses:
                    declared_licenses.append(declared)
                if detected and detected not in detected_licenses:
                    detected_licenses.append(detected)
            else:
                declared_licenses.append(declared)
                detected_licenses.append(detected)

            # also detect in text
            text = paragraph.license.text
            if text:
                detected = get_normalized_expression(text, try_as_expression=False)
                if not detected:
                    detected = 'unknown'
                # avoid repeats
                if unique:
                    if detected not in detected_licenses:
                        detected_licenses.append(detected)
                else:
                    detected_licenses.append(detected)

    declared_license = '\n'.join(declared_licenses)

    if detected_licenses:
        detected_licenses = [licensing.parse(dl, simple=True) for dl in detected_licenses]

        if len(detected_licenses) > 1:
            detected_license = licensing.AND(*detected_licenses)
        else:
            detected_license = detected_licenses[0]

        if simplify_licenses:
            detected_license = detected_license.simplify()

        detected_license = str(detected_license)

    else:
        detected_license = 'unknown'

    copyrights = '\n'.join(copyrights)
    return declared_license, detected_license, copyrights
Exemple #8
0
class LicenseHandler:

    def __init__(self, translations_files, relicense_file, group_file):
        self.translations_files = translations_files
        self.relicense_file = relicense_file
        self.relicense_map = None
        self.group_file = group_file
        symbols = self.read_symbols(self.translations_files)
        self.licensing = Licensing(symbols)

    def read_symbols(self, translations_files):
        symbols_map = {}
        self.translations = []
        for translations_file in translations_files.split():
            translation_data = read_translations(translations_file)
            self.translations.append(translation_data)
            for lic_key in translation_data:
                if lic_key not in symbols_map:
                    symbols_map[lic_key] = set()
                for val in translation_data[lic_key]:
                    symbols_map[lic_key].add(val)

        return [LicenseSymbol(key=key, aliases=tuple(value))
                for key, value in symbols_map.items()]

    def translate_and_relicense(self, license_expression):
        transl = self.translate(license_expression)
        if not transl:
            transl = license_expression
        rel = self.expand_relicense(transl)

        return rel if rel else transl

    def expand_relicense(self, license_expression):
        if self.relicense_file is not None and self.relicense_file:
            self.relicense_map = read_relicense_file(self.relicense_file)
            expanded = relicense_license(
                self.relicense_map, license_expression)
            return expanded.strip()
        else:
            return license_expression.strip()

    def group(self, license_expression):
        return license_expression.strip()

    def translate(self, license_expression):
        license_expression = license_expression.replace(
            "&", AND_STRING).replace("|", OR_STRING)
        return str(self.simplify(license_expression))

    def simplify(self, license_expression):
        parsed = self.licensing.parse(license_expression)
        return parsed.simplify()

    def license_expression_list_json(self, license_expression, relicense=True):
        license = self.license_expression_list(license_expression, relicense)
        return {
            "license_expression": license_expression,
            "expanded": license.expanded,
            "grouped": license.grouped,
            "translated": license.translated,
            "simplified": license.simplified,
            "interim": license.interim,
            "set_list": license.set_list
        }

    def license_expression_list(self, license_expression, relicense=True):

        license = ManagedLicenseExpression(license_expression)
        license.translated = self.translate(license_expression)

        # We need str to skip verbose output
        license.simplified = str(self.simplify(license.translated))

        if relicense:
            license.expanded = self.expand_relicense(license.simplified)
        else:
            license.expanded = license.simplified

        license.grouped = self.group(license.expanded)

        license.interim = self.interim_license_expression_list(
            license.grouped, self.licensing)

        license.set_list = self.interim_license_expression_set_list(
            license.interim)

        return license

    def interim_license_expression_list(self, license_expression, licensing):
        """
        Transforms and boolean symbolic expression

        Turns an expression like this:
            G AND (A OR B)
        into:
            AND [G, OR [A, B]]
        The latter is an interim format.
        """
        encoded = encode_license_expression(license_expression)
        tokenizer = licensing.get_advanced_tokenizer()
        tokenized = tokenizer.tokenize(encoded)
        current_license = None
        current_licenses = []
        current_op = None
        paren_expr = None
        paren_count = 0
        for token in tokenized:
            tok = token.string
            if tok == '(':
                if paren_expr is None:
                    paren_expr = ""
                else:
                    paren_expr = paren_expr + " " + tok
                    paren_count = paren_count + 1
            elif tok == ')':
                if paren_count == 0:
                    current_license = self.interim_license_expression_list(
                        paren_expr, licensing)
                    paren_expr = None
                else:
                    paren_count = paren_count - 1
                    paren_expr = paren_expr + " " + tok
            elif tok == 'OR' or tok == 'AND':
                if paren_expr is not None:
                    paren_expr = paren_expr + " " + tok
                else:
                    if current_licenses is None:
                        raise FlictError(ReturnCodes.RET_INTERNAL_ERROR,
                                         "Internal failure. Failed creating interim license expression. current_licenses is None")
                    if current_op is None:
                        # first operator
                        current_op = tok
                        current_licenses.append(current_license)
                    elif current_op == tok:
                        # same operator
                        current_licenses.append(current_license)
                    else:
                        # different operator
                        raise FlictError(ReturnCodes.RET_INTERNAL_ERROR,
                                         "Internal failure. Failed creating interim license expression.")
            else:
                if paren_expr is not None:
                    paren_expr = paren_expr + " " + tok
                else:
                    current_license = tok

        current_licenses.append(current_license)
        if current_op is None:
            current_op = "AND"

        list = LicenseExpressionList(current_op, current_licenses)
        return list

    def _combinations(self, lel):
        if not isinstance(lel, LicenseExpressionList):
            return 1
        if lel.op == "AND":
            prod = 1
            for item in lel.list:
                prod = prod * self._combinations(item)
            return prod
        elif lel.op == "OR":
            sum = 0
            for item in lel.list:
                sum = sum + self._combinations(item)
            return sum
        else:
            FlictError(ReturnCodes.RET_INTERNAL_ERROR,
                       f"Internal failure. Failed identifying operator: {lel}")

    def interim_license_expression_set_list(self, interim_license_expression_list):
        """
        Transforms a boolean symbolic expression

        Turns an expression like this:
            AND [G, OR [A, B]]
        into:
            [
              { G, A },
              { G, B }
            ]
        The latter is an interim format.
        """
        expanded_list = []

        if not isinstance(interim_license_expression_list, LicenseExpressionList):
            # single license
            license_set = {decode_license_expression(interim_license_expression_list)}
            expanded_list.append(list(license_set))
            return expanded_list

        current_op = interim_license_expression_list.op
        for lep in interim_license_expression_list.list:
            if current_op is None:
                raise FlictError(ReturnCodes.RET_INTERNAL_ERROR,
                                 "Internal failure. No operator found")

            lep_list = self.interim_license_expression_set_list(lep)
            if current_op == "OR":
                expanded_list = self._manage_list_item_or(
                    expanded_list, lep_list)

            elif current_op == "AND":
                expanded_list = self._manage_list_item_and(
                    expanded_list, lep_list)
        return expanded_list

    def _manage_list_item_and(self, license_list, lep):
        if isinstance(lep, LicenseExpressionList):
            raise FlictError(ReturnCodes.RET_INTERNAL_ERROR,
                             f"Internal failure. Wrong type {lep} for: {lep}")

        # single license
        if len(license_list) == 0:
            return lep

        new_list = []
        for item in license_list:
            for lep_item in lep:
                new_item = list(set(item + lep_item))
                new_list.append(new_item)

        return new_list

    def _manage_list_item_or(self, license_list, lep):
        if isinstance(lep, LicenseExpressionList):
            raise FlictError(ReturnCodes.RET_INTERNAL_ERROR,
                             f"Internal failure. Wrong type {lep} for: {lep}")

        # single license
        if len(license_list) == 0:
            return lep

        new_list = license_list
        for lep_item in lep:
            new_list.append(lep_item)

        return new_list

    def relicensing_information(self):
        if self.relicense_map is None:
            self.relicense_map = read_relicense_file(self.relicense_file)
        return self.relicense_map

    def translation_information(self):
        return self.translations
Exemple #9
0
def get_normalized_expression(query_string):
    """
    Given a text `query_string` return a single detected license expression.
    `query_string` is typically the value of a license field as found in package
    manifests.
    Return None if there is the `query_string` is empty. Return "unknown" as a
    license expression if there is a `query_string` but nothing was detected.

    For example::
    >>> get_normalized_expression('mit')
    'mit'
    >>> get_normalized_expression('mit or asasa or Apache-2.0')
    'apache-2.0 AND unknown'
    >>> get_normalized_expression('mit or asasa or Apache-2.0')
    'apache-2.0 AND unknown'
    >>> get_normalized_expression('mit asasa or Apache-2.0')
    'apache-2.0 AND unknown'
    >>> assert get_normalized_expression('') is None
    >>> assert get_normalized_expression(None) is None
    """
    if not query_string or not query_string.strip():
        return

    if TRACE:
        logger_debug('get_normalized_expression: query_string: "{}"'.format(
            query_string))

    from licensedcode.cache import get_index
    idx = get_index()
    licensing = Licensing()

    # we match twice in a cascade: as an expression, then as plain text if we
    # did not succeed.
    matches = None
    try:
        matched_as_expression = True
        matches = idx.match(query_string=query_string, as_expression=True)
        if matches_have_unknown(matches, licensing):
            # rematch also if we have unknowns
            matched_as_expression = False
            matches = idx.match(query_string=query_string, as_expression=False)

    except Exception:
        matched_as_expression = False
        matches = idx.match(query_string=query_string, as_expression=False)

    if not matches:
        # we have a query_string text but there was no match: return an unknown
        # key
        return 'unknown'

    if TRACE:
        logger_debug('get_normalized_expression: matches:', matches)

    # join the possible multiple detected license expression with an AND
    expression_objects = [m.rule.license_expression_object for m in matches]
    if len(expression_objects) == 1:
        combined_expression_object = expression_objects[0]
    else:
        combined_expression_object = licensing.AND(*expression_objects)

    if matched_as_expression:
        # then just return the expression(s)
        return str(combined_expression_object)

    # Otherwise, verify that we consumed 100% of the query string e.g. that we
    # have no unknown leftover.

    # 1. have all matches 100% coverage?
    all_matches_have_full_coverage = all(m.coverage() == 100 for m in matches)

    # TODO: have all matches a high enough score?

    # 2. are all declared license tokens consumed?
    query = matches[0].query
    # the query object should be the same for all matches. Is this always true??
    for mt in matches:
        if mt.query != query:
            # FIXME: the expception may be swallowed in callers!!!
            raise Exception(
                'Inconsistent package.declared_license: text with multiple "queries".'
                'Please report this issue to the scancode-toolkit team.\n'
                '{}'.format(query_string))

    query_len = len(query.tokens)
    matched_qspans = [m.qspan for m in matches]
    matched_qpositions = Span.union(*matched_qspans)
    len_all_matches = len(matched_qpositions)
    declared_license_is_fully_matched = query_len == len_all_matches

    if not all_matches_have_full_coverage or not declared_license_is_fully_matched:
        # We inject an 'unknown' symbol in the expression
        unknown = licensing.parse('unknown', simple=True)
        combined_expression_object = licensing.AND(combined_expression_object,
                                                   unknown)

    return str(combined_expression_object)
def cli(licenses_file):
    """
    Create rules from a structured text file

    For instance:
        ----------------------------------------
        license_expression: lgpl-2.1
        relevance: 100
        is_license_notice: yes
        ---
        This program is free software; you can redistribute it and/or modify
        it under the terms of the GNU Lesser General Public License
        version 2.1 as published by the Free Software Foundation;
        ----------------------------------------
    """

    rule_data = load_data(licenses_file)
    rules_tokens = set()

    licenses = cache.get_licenses_db()
    licensing = Licensing(licenses.values())

    print()
    for data, text in rule_data:
        rdat = '\n'.join(data)
        rtxt = '\n'.join(text)
        existing = rule_exists(rtxt)
        if existing:
            print('Skipping existing rule:', existing, 'with text:\n', rtxt[:50].strip(), '...')
            continue

        # validate YAML syntax
        parsed = saneyaml.load(rdat)
        if parsed.get('is_negative'):
            license_expression = 'not-a-license'
        else:
            _, _, license_expression = data[0].partition(': ')
            license_expression = license_expression.strip()
            if not license_expression:
                raise Exception('Missing license_expression for text:', rtxt)
            licensing.parse(license_expression, validate=True, simple=True)

        base_loc = find_rule_base_loc(license_expression)

        data_file = base_loc + '.yml'
        with io.open(data_file, 'w', encoding='utf-8') as o:
            o.write(rdat)

        text_file = base_loc + '.RULE'
        with io.open(text_file, 'w', encoding='utf-8') as o:
            o.write(rtxt)
        rule = models.Rule(data_file=data_file, text_file=text_file)
        rule_tokens = tuple(rule.tokens())
        if rule_tokens in rules_tokens:
            # cleanup
            os.remove(text_file)
            os.remove(data_file)
            print('Skipping already added rule with text for:', license_expression)
        else:
            rules_tokens.add(rule_tokens)
            rule.dump()
            models.update_ignorables(rule, verbose=True)
            print('Rule added:', rule.identifier)
Exemple #11
0
def cli(licenses_file):
    """
    Create rules from a text file with delimited blocks of metadata and texts.

    As an example a file would contains one of more blocks such as this:

\b
        ----------------------------------------
        license_expression: lgpl-2.1
        relevance: 100
        is_license_notice: yes
        ---
        This program is free software; you can redistribute it and/or modify
        it under the terms of the GNU Lesser General Public License
        version 2.1 as published by the Free Software Foundation;
        ----------------------------------------
    """

    rules_data = load_data(licenses_file)
    rules_tokens = all_rule_tokens()

    licenses = cache.get_licenses_db()
    licensing = Licensing(licenses.values())

    print()
    errors = validate_license_rules(rules_data, licensing)
    if errors:
        print('Invalid rules: exiting....')
        for error in errors:
            print(error)
            print()

        raise Exception('Invalid rules: exiting....')

    print()
    for rule in rules_data:
        is_negative = rule.data.get('is_negative')
        is_false_positive = rule.data.get('is_false_positive')
        existing = rule_exists(rule.text)
        if existing and not is_negative:
            print('Skipping existing non-negative rule:', existing,
                  'with text:\n', rule.text[:50].strip(), '...')
            continue

        if is_negative:
            base_name = 'not-a-license'
        else:
            license_expression = rule.data.get('license_expression')
            license_expression = str(
                licensing.parse(license_expression, validate=True,
                                simple=True))
            base_name = license_expression
            if is_false_positive:
                base_name = 'false-positive_' + base_name

        base_loc = find_rule_base_loc(base_name)

        data_file = base_loc + '.yml'
        with io.open(data_file, 'w', encoding='utf-8') as o:
            o.write(rule.raw_data)

        text_file = base_loc + '.RULE'
        with io.open(text_file, 'w', encoding='utf-8') as o:
            o.write(rule.text)

        rulerec = models.Rule(data_file=data_file, text_file=text_file)
        rule_tokens = tuple(rulerec.tokens())
        if rule_tokens in rules_tokens:
            # cleanup
            os.remove(text_file)
            os.remove(data_file)
            print('Skipping already added rule with text for:', base_name)
        else:
            rules_tokens.add(rule_tokens)
            rulerec.dump()
            models.update_ignorables(rulerec, verbose=False)
            print(
                'Rule added:',
                'file://' + rulerec.data_file,
                '\n',
                'file://' + rulerec.text_file,
            )
Exemple #12
0
class LicenseHandler:

    def __init__(self, translations_files, relicense_file, group_file):
        self.translations_files = translations_files
        self.relicense_file = relicense_file
        self.relicense_map = None
        self.group_file = group_file
        symbols = self.read_symbols(self.translations_files)
        self.licensing = Licensing(symbols)
        #print("symbols: " + str(symbols))
        
    def read_symbols(self, translations_files):
        symbols = []
        symbols_map = {}
        for translations_file in translations_files.split():
            #print("reading translation file: " + str(translations_file))
            translation_data = read_translations(translations_file)
            for lic_key in translation_data:
                #print("lic_key:  \"" + str(lic_key) + "\"")
                #print("  lic_alias:  " + str(translation_data[lic_key] ))
                if lic_key not in symbols_map:
                    symbols_map[lic_key] = set()
                for val in translation_data[lic_key]:
                    symbols_map[lic_key].add(val)
                
                #lic_aliases = tuple(translation_data[lic_key])
                #symbols.append(LicenseSymbol(key=key, aliases=lic_aliases))

        for key, value in symbols_map.items():
            #print("Adding to symbols: " + key)
            #print(" - " + str(value))
            symbols.append(LicenseSymbol(key=key, aliases=tuple(value)))

        # debugging
        #print("Symbols")
        #for sym in symbols:
            #print(" sym: " + (str(sym.key)))
            #print("    aliases :  " + (str(sym.aliases)))
            #l = Licensing([sym])
            #print("    licensing: " + (str(l)))
            
        
        #print("symbols: " + str(symbols))
        return symbols
        
    def translate_and_relicense(self, license_expression):
        license_expression = license_expression.replace("&", " and ").replace("|", " or ")
        
        transl = self.translate(license_expression)
        if transl == None or transl == "":
            transl = license_expression
        #print("translate_and_relicenseself: " + license_expression + " ==> " + transl)
        rel = self.expand_relicense(transl)
        if rel == None:
            rel = transl
        #print("translate_and_relicenseself: " + rel)
        return rel
        
    def expand_relicense(self, license_expression):
        if self.relicense_file != None and self.relicense_file != "":
            self.relicense_map = read_relicense_file(self.relicense_file)
            expanded = relicense_license(self.relicense_map, license_expression)
            return expanded.strip()
        else:
            return license_expression.strip()

    def group(self, license_expression):
        return license_expression.strip()

    def translate(self, license_expression):
        return str(self.simplify(license_expression))

    def simplify(self, license_expression):
        parsed = self.licensing.parse(license_expression)
        #parsed = self.licensing._parse_and_simplify(license_expression)
        #print("simplified: " + str(parsed.simplify()))
        #return parsed.simplify()
        return parsed

    def license_expression_list_json(self, license_expression, relicense=True):
        license = self.license_expression_list(license_expression, relicense)
        output = {}
        output["license_expression"] = license_expression
        output["expanded"] = license.expanded
        output["grouped"] = license.grouped
        output["translated"] = license.translated
        output["simplified"] = license.simplified
        output["interim"] = license.interim
        output["set_list"] = license.set_list
        return output

        
    def license_expression_list(self, license_expression, relicense=True):

        license = ManagedLicenseExpression(license_expression)
        license.translated = self.translate(license_expression)
        
        if relicense:
            license.expanded = self.expand_relicense(license.translated)
        else:
            license.expanded = license.translated

            
        license.grouped = self.group(license.expanded)

        # We need str to skip verbose output
        license.simplified = str(self.simplify(license.grouped))
        
        license.interim = self.interim_license_expression_list(license.simplified, self.licensing)
        
        license.set_list = self.interim_license_expression_set_list(license.interim)

        return license


    
    def interim_license_expression_list(self, license_expression, licensing):
        """
        Turns an expression like this:
            G AND (A OR B)
        into:
            AND [G, OR [A, B]]
        The latter is an interim format.
        """
        #print("")
        #print("parse(" + str(license_expression) + ")")
        tokenizer = licensing.get_advanced_tokenizer()
        tokenized = tokenizer.tokenize(str(license_expression))
        current_license=None
        current_licenses=[]
        current_op=None
        paren_expr = None
        paren_count=0
        for token in tokenized:
            tok = token.string
            if tok == '(':
                #print("(")
                if paren_expr == None:
                    paren_expr = ""
                else:
                    paren_expr = paren_expr + " " + tok
                    paren_count = paren_count + 1
            elif tok == ')':
                #print("about to parse: \"" + paren_expr + "\"  count: " + str(paren_count))
                if paren_count == 0:
                    current_license = self.interim_license_expression_list(paren_expr, licensing)
                    #print("got:            \"" + str(current_license) + "\"")
                    paren_expr=None
                else:
                    paren_count = paren_count - 1
                    paren_expr = paren_expr + " " + tok                
            elif tok == 'OR' or tok == 'AND':
                if paren_expr != None:
                    #print("TEMP " + tok)
                    paren_expr = paren_expr + " " + tok
                else:
                    #print("OPERATOR " + tok + " (" + str(current_op) + ")")
                    if current_licenses == None:
                        print("ERROR......")
                        print("ERROR......")
                        print("ERROR......")
                        exit(24)
                    if current_op == None:
                        # first operator
                        current_op = tok
                        #print("=cop: " + tok + "   " + current_license)
                        current_licenses.append(current_license)
                    elif current_op == tok:
                        # same operator
                        #print("-cop: " + tok + "   " + current_license)
                        current_licenses.append(current_license)
                    else:
                        # different operator
                        print("-------------------------------------------- Store me: " + current_op + " " + str(current_licenses))
                        exit(12)
            else:
                #print("tok: \"" + tok + "\"")
                if paren_expr != None:
                    #print("TEMP " + tok)
                    paren_expr = paren_expr + " " + tok
                else:
                    #print("license: " + tok)
                    current_license = tok

        current_licenses.append(current_license)
        if current_op == None:
            current_op = "AND"
        list = LicenseExpressionList(current_op, current_licenses)
        #print("DONE: " + str(license_expression) + " => " + str(list))
        return list

    def _combinations(self, lel):
        #print("lel : " + str(lel))
        if not isinstance(lel, LicenseExpressionList):
            return 1
        if lel.op == "AND":
            prod = 1
            for l in lel.list:
                prod = prod * _combinations(l)
            return prod
        elif lel.op == "OR":
            sum = 0
            for l in lel.list:
                sum = sum + _combinations(l)
            return sum
        else:
            print("ERROR: NO OP")
            exit(11)

    def interim_license_expression_set_list(self, interim_license_expression_list):
        """
        Turns an expression like this:
            AND [G, OR [A, B]]
        into:
            [ 
              { G, A },
              { G, B }
            ]
        The latter is an interim format.
        """    
        expanded_list=[]

        #print("Count: " + str(_combinations(interim_license_expression_list)))
        if not isinstance(interim_license_expression_list, LicenseExpressionList):
            # single license
            license_set= { interim_license_expression_list }
            expanded_list.append(list(license_set))
            license_verbose_debug("LEAF, returning " +  str(expanded_list))
            license_verbose_debug("cop: " + interim_license_expression_list )
            #print("managed____ \""  + str(expanded_list) + "\"  <---- MIDDLE")
            return expanded_list

        current_op = interim_license_expression_list.op;
        license_verbose_debug("cop: " + current_op )
        for lep in interim_license_expression_list.list:
            license_verbose_debug(" ------ lep ----- " + str(lep))
            if current_op == None:
                print("ERROR: NO OP")
                exit(11)

            elif current_op == "OR":
                lep_list = self.interim_license_expression_set_list(lep)
                expanded_list = self._manage_list_item_or(expanded_list, lep_list)

            elif current_op == "AND":
                lep_list = self.interim_license_expression_set_list(lep)
                expanded_list = self._manage_list_item_and(expanded_list, lep_list)
        #print("managed____ \""  + str(expanded_list) + "\"  <---- FINAL")
        return expanded_list

    def _manage_list_item_and(self, license_list, lep):
        license_verbose_debug(" * Andy" )
        if isinstance(lep, LicenseExpressionList):
            print(" -------------====== Andy ====-----------------" )
            exit(77)
            # TODO : implement below (for lep)
            print("AND Count 0: " + str(_combinations(lep)))
            for inner_lep in lep.list:
                print("¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤ AND Count A: " + str(_combinations(inner_lep)))
                set_list = self.interim_license_expression_set_list(inner_lep)
            return None
        else:
            # single license
            if len(license_list) == 0:
                license_verbose_debug("wooops: " + str(license_list))
                license_verbose_debug("wooops: " + str(lep))
                license_list = lep
            else:
                license_verbose_debug("daisy: " + str(license_list))
                license_verbose_debug("daisy: " + str(lep))
                license_verbose_debug(" -------------====== Andy ====----------------- SINGLE: " + str(license_list) )
                new_list=[]
                for item in license_list:
                    license_verbose_debug("  item: " + str(item) + " <--- "  + str(lep) )
                    for lep_item in lep:
                        license_verbose_debug("    item: " + str(item) + " <--- "  + str(lep_item) )
                        new_item =list(set(item + lep_item))
                        license_verbose_debug("    item: " + str(item)  )
                        new_list.append(new_item)
                    license_verbose_debug("    list: " + str(new_list)  )
                license_list = new_list
            return license_list


    def _manage_list_item_or(self, license_list, lep):
        license_verbose_debug(" * Orleans: " + (str(lep)))
        if isinstance(lep, LicenseExpressionList):
            # TODO : implement below (for lep)
            license_verbose_debug(" -------------====== ORLEANS ====----------------- : " + str(lep.license_list) )
            exit(77)
            for inner_lep in lep.license_list:
                print("        ====----------------- : " + str(inner_lep) )
                print("OR Count A: " + str(_combinations(inner_lep)))
                set_list = self.interim_license_expression_set_list(inner_lep)
                print("OR Count B: " + str(len(set_list)))
                license_list.append(inner_lep)
        else:
            # single license
            license_verbose_debug("HERE I AM .... \"" + str(lep) + "\"")
            if len(license_list) == 0:
                new_list=lep
                license_verbose_debug("topsss: " + str(license_list) + " size: " + str(len(license_list)))
                license_verbose_debug("topsss: " + str(lep) + " size: " + str(len(lep)))
                license_verbose_debug("topsss: " + str(new_list) + " size: " + str(len(new_list)))
            else:
                new_list = license_list
                license_verbose_debug("dapsss: " + str(license_list))
                license_verbose_debug("dappss: " + str(lep))
                for lep_item in lep:
                    license_verbose_debug("    item: " + str(license_list) + " <--- "  + str(lep_item) )
                    new_list.append(lep_item)

        return new_list