Сторонние программные библиотеки иногда включают набор лицензий, которые разработчики могут выбрать при использовании библиотеки. Некоторые лицензии могут быть обнаружены с помощью ORT для создания идентификатора SPDX. Мы хотели бы отказаться от ручного процесса выбора отдельных лицензий каждый раз, откладывая вместо этого предопределенный список приоритетов.
Не все лицензии включены в список.
В этом разделе определяются исходные файлы.
Порядок записей определяет лицензию на выбор, когда выбор доступен:
<priorities>
<license>Apache-2.0</license>
<license>MIT</license>
<license>BSD-2-Clause</license>
<license>BSD-3-Clause</license>
<license>CDDL-1.0</license>
<license>EPL-2.0</license>
<license>MPL-2.0</license>
<license>LGPL-3.0</license>
</priorities>
Этот документ загружается с помощью:
<xsl:variable
name = "PRIORITY"
select = "document( resolve-uri( 'priorities.xml', base-uri( / ) ) )" />
Эти упрощенные лицензионные записи образуют входной документ:
<copyrights>
<copyright>
<title>Grizzly HTTP framework</title>
<licenses>
<license>CDDL-1.0</license>
<license>GPL-2.0-or-later</license>
</licenses>
</copyright>
<copyright>
<title>Java™ JSON Tools Jackson Coreutils</title>
<licenses>
<license>LGPL-3.0-or-later</license>
<license>Apache-2.0</license>
</licenses>
</copyright>
<copyright>
<title>Javassist</title>
<licenses>
<license>LGPL-2.1-only</license>
<license>MPL-1.1</license>
<license>Apache-2.0</license>
</licenses>
</copyright>
<copyright>
<title>Linux Kernel</title>
<licenses>
<license with = "Linux-syscall-note" order = "before">GPL-2.0-only</license>
</licenses>
</copyright>
<copyright>
<title>Eclipse Temurin™</title>
<licenses>
<license with = "Classpath-exception-2.0">GPL-2.0-only</license>
</licenses>
</copyright>
</copyrights>
Желаемый результат сводит лицензии к одной записи:
<copyrights>
<copyright>
<title>Grizzly HTTP framework</title>
<licenses>
<license>CDDL-1.0</license>
</licenses>
</copyright>
<copyright>
<title>Java™ JSON Tools Jackson Coreutils</title>
<licenses>
<license>Apache-2.0</license>
</licenses>
</copyright>
<copyright>
<title>Javassist</title>
<licenses>
<license>Apache-2.0</license>
</licenses>
</copyright>
<copyright>
<title>Linux Kernel</title>
<licenses>
<license with = "Linux-syscall-note" order = "before">GPL-2.0-only</license>
</licenses>
</copyright>
<copyright>
<title>Eclipse Temurin™</title>
<licenses>
<license with = "Classpath-exception-2.0">GPL-2.0-only</license>
</licenses>
</copyright>
</copyrights>
Концептуально я хотел бы отфильтровать лицензии на основе позиции каждой входной лицензии в списке приоритетов, а затем выбрать первую. В качестве серии преобразований соответствующий раздел входного документа может начинаться как:
<licenses>
<license>LGPL-2.1-only</license>
<license>MPL-1.1</license>
<license>Apache-2.0</license>
</licenses>
Затем мы могли бы назначить приоритет на основе позиции в списке приоритетов:
<licenses>
<license priority = "INFINITY">LGPL-2.1-only</license>
<license priority = "7">MPL-1.1</license>
<license priority = "1">Apache-2.0</license>
</licenses>
Затем отсортируйте по приоритету:
<licenses>
<license priority = "1">Apache-2.0</license>
<license priority = "7">MPL-1.1</license>
<license priority = "INFINITY">LGPL-2.1-only</license>
</licenses>
Затем выберите первого ребенка:
<licenses>
<license priority = "1">Apache-2.0</license>
</licenses>
Я считаю, что этот «алгоритм» гарантирует, что любая лицензия, не имеющая соответствующего приоритета, будет выбрана по умолчанию.
Эти ограничения будут выполнены до того, как произойдет шаг преобразования:
Наличие нескольких лицензий во входном документе без хотя бы одного совпадения в списке приоритетов лицензий является ошибкой. Я не думаю, что мы можем реально выбрать первую лицензию в таких случаях.
Отсутствие лицензии для каждой записи во входном документе является ошибкой (т. е. мы можем обеспечить это с помощью проверки схемы).
Как было бы целесообразно отфильтровать лицензии с помощью XSLT 3.0?
Вот мое предложение.
Чтобы упростить мой тест, я определил переменную PRIORITY
встроенной вместо использования document()
, но, конечно, вы можете придерживаться своего подхода чтения из внешнего файла.
Объяснение:
Шаблон, соответствующий licenses
, копирует только один из license
дочерних элементов.
Сначала он использует функцию XPath 3 sort
для сортировки лицензий. Последний параметр этой функции — это функция, которая сопоставляет элемент с ключом сортировки; предоставленная функция ищет элемент (то есть имя лицензии) в последовательности PRIORITY
лицензий и возвращает результирующую позицию или, если она не найдена на карте, возвращает бесконечность.
Затем копируется первая (с наивысшим приоритетом) лицензия из этой отсортированной последовательности.
<xsl:stylesheet
version = "3.0"
xmlns:xsl = "http://www.w3.org/1999/XSL/Transform"
xmlns:map = "http://www.w3.org/2005/xpath-functions/map">
<xsl:variable name = "PRIORITY">
<priorities>
<license>Apache-2.0</license>
<license>MIT</license>
<license>BSD-2-Clause</license>
<license>BSD-3-Clause</license>
<license>CDDL-1.0</license>
<license>EPL-2.0</license>
<license>MPL-2.0</license>
<license>LGPL-3.0</license>
</priorities>
</xsl:variable>
<xsl:mode on-no-match = "shallow-copy"/>
<xsl:template match = "licenses" xmlns:xs = "http://www.w3.org/2001/XMLSchema">
<xsl:copy>
<xsl:copy-of select = "
sort(
license,
(),
function($license) {
(index-of($PRIORITY//license, $license), xs:double('INF'))[1]
}
)[1]
"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Это ближе к тому, как я представлял, что буду подходить к этому, за исключением того, что я буду использовать ключ, чтобы учитывались только связанные лицензии из Priorities.xml.
Пытаться:
<xsl:template match = "copyright/licenses">
<licenses>
<xsl:copy-of
select = "$PRIORITY//license[. = current()/license][1]"/>
</licenses>
</xsl:template>
При этом используется возможность сравнения «один ко многим» с помощью =
.
Не все лицензии в исходном документе перечислены в файле приоритетов, поэтому, чтобы включить значение по умолчанию и сохранить атрибуты из исходного документа, попробуйте:
<xsl:template match = "copyright/licenses">
<licenses>
<xsl:copy-of
select = "(license[.=$PRIORITY//license[. = current()/license][1]], license)[1]" />
</licenses>
</xsl:template>
Вот реализация предложения Майкла Кея с использованием ключей, что, я считаю, было бы более эффективным.
Для лучшей читабельности я использовал переменные. Если вы предпочитаете лаконичный код, вы можете заменить каждую ссылку на переменную ее выражением select
.
<xsl:key name = "lic" match = "license" use = "." />
<xsl:template match = "licenses">
<xsl:copy>
<xsl:variable name = "related" select = "key('lic', license, $PRIORITY)" />
<xsl:variable name = "top" select = "key('lic', $related[1], current())" />
<xsl:copy-of select = "($top, license)[1]" />
</xsl:copy>
</xsl:template>
Вот решение с использованием ключей:
<xsl:stylesheet version = "3.0" xmlns:xsl = "http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration = "yes" indent = "yes"/>
<xsl:strip-space elements = "*"/>
<xsl:key name = "kLicenseByVal" match = "license" use = "."/>
<xsl:variable name = "vPriorities">
<priorities>
<license>Apache-2.0</license>
<license>MIT</license>
<license>BSD-2-Clause</license>
<license>BSD-3-Clause</license>
<license>CDDL-1.0</license>
<license>EPL-2.0</license>
<license>MPL-2.0</license>
<license>LGPL-3.0</license>
</priorities>
</xsl:variable>
<xsl:template match = "node()|@*">
<xsl:copy>
<xsl:apply-templates select = "node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match = "licenses">
<xsl:copy>
<xsl:apply-templates select = "@*"/>
<xsl:sequence select = "((* ! key('kLicenseByVal', ., $vPriorities))[1], *[1])[1]"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Когда это преобразование применяется к предоставленному XML-документу:
<copyrights>
<copyright>
<title>Grizzly HTTP framework</title>
<licenses>
<license>CDDL-1.0</license>
<license>GPL-2.0-or-later</license>
</licenses>
</copyright>
<copyright>
<title>Java™ JSON Tools Jackson Coreutils</title>
<licenses>
<license>LGPL-3.0-or-later</license>
<license>Apache-2.0</license>
</licenses>
</copyright>
<copyright>
<title>Javassist</title>
<licenses>
<license>LGPL-2.1-only</license>
<license>MPL-1.1</license>
<license>Apache-2.0</license>
</licenses>
</copyright>
<copyright>
<title>Linux Kernel</title>
<licenses>
<license with = "Linux-syscall-note" order = "before">GPL-2.0-only</license>
</licenses>
</copyright>
<copyright>
<title>Eclipse Temurin™</title>
<licenses>
<license with = "Classpath-exception-2.0">GPL-2.0-only</license>
</licenses>
</copyright>
</copyrights>
желаемый, правильный результат получается:
<copyrights>
<copyright>
<title>Grizzly HTTP framework</title>
<licenses>
<license>CDDL-1.0</license>
</licenses>
</copyright>
<copyright>
<title>Java™ JSON Tools Jackson Coreutils</title>
<licenses>
<license>Apache-2.0</license>
</licenses>
</copyright>
<copyright>
<title>Javassist</title>
<licenses>
<license>Apache-2.0</license>
</licenses>
</copyright>
<copyright>
<title>Linux Kernel</title>
<licenses>
<license with = "Linux-syscall-note" order = "before">GPL-2.0-only</license>
</licenses>
</copyright>
<copyright>
<title>Eclipse Temurin™</title>
<licenses>
<license with = "Classpath-exception-2.0">GPL-2.0-only</license>
</licenses>
</copyright>
</copyrights>
Взгляните на функцию
index-of()
.