eerohele / saxon-gradle

A Gradle plugin for running XSLT transformations with Saxon
MIT License
16 stars 6 forks source link

Support global configuration of options #26

Closed ndw closed 1 year ago

ndw commented 3 years ago

I'm trying to make these PRs independent, but they may not all merge perfectly.

For most projects, I want to use a common set of options across all transformations. For example, I want to set the various resolver classes, parser classes, and catalogs for every transformation. Putting those options in every task is tedious, redundant, and error prone.

This PR introduces a global configuration object:

saxon.configure {
    entityResolverClass "org.xmlresolver.Resolver"
    sourceSaxParser "org.xmlresolver.tools.ResolvingXMLReader"
}    

and reworks the way options and advancedOptions are processed so that the global configuration applies to all tasks, with the ability to override any option locally on a particular task.

To avoid a lot of redundant code, I moved all of the setter methods into a trait with the uninspired name OptionsConfig. This leaves the options map in SaxonXsltTask a little redundant. It might make sense to make input and output simply variables in the task, but I didn't do that.

I could imagine extending this so that the global configurations could be named. You could have a global set for "docbook" transformations, for example, and a different global set for "tei" transformations. But I haven't been inspired to do that just yet.

ndw commented 3 years ago

My implementation was clumsily using options and advancedOptions maps. This version isolates those properly and pushes all access through methods so that the globally configured options act as defaults. And I put the plugin options into the same framework.

I've also changed the way the classpath is handled. I confess I'm not sure how it used to work. I'm using the plugin in a Gradle build script where I need to be able to change the classpath to a specific configuration. If that was possible, I couldn't work out how. It seemed like the classpath was initialized to project.objects.fileCollection() when the object was created and that was always empty.

I made classpath a plugin configuration property so that it can be changed dynamically, with a default in the global configuration.

ndw commented 3 years ago

I decided I did want support for named configurations:

saxon.configure {
    classpath configurations.mytest
    entityResolverClass "org.xmlresolver.Resolver"
    uriResolverClass "org.xmlresolver.Resolver"
    sourceSaxParser "org.xmlresolver.tools.ResolvingXMLReader"
    stylesheetSaxParser "org.xmlresolver.tools.ResolvingXMLReader"
}

saxon.configure("docbook") {
    classpath configurations.docbooktest
    entityResolverClass "org.xmlresolver.Resolver"
    uriResolverClass "org.xmlresolver.Resolver"
    sourceSaxParser "org.xmlresolver.tools.ResolvingXMLReader"
    stylesheetSaxParser "org.xmlresolver.tools.ResolvingXMLReader"
    initializer 'org.docbook.xsltng.extensions.Register'
}

Used like so:

task test1(type: SaxonXsltTask) {
    stylesheet "identity.xsl"
    input "input.xml"
    output "result.xml"
}

task test1b(type: SaxonXsltTask) {
    pluginConfiguration "docbook"
    stylesheet "identity.xsl"
    input "docbook.xml"
    output "dbresult.xml"
}

The test1 task runs with the default global configuration, test1b runs with the "docbook" configuration.

I suppose named configurations could inherit from the default configuration, but I haven't done that (yet 😄 ).

eerohele commented 3 years ago

I understand why you'd want this, but I'm wondering whether you could achieve (more or less) the same thing by using the capabilities built into Gradle? For example:

import com.github.eerohele.SaxonXsltTask

def common = {
    stylesheet 'xsl/html5.xsl'
    input fileTree(dir: 'xml', include: '*.xml')
    config 'config/configuration.xml'
    catalog 'catalog.xml'
}

task xslt1(type: SaxonXsltTask) {
    configure common
    parameters(title: 'foo', padding: '1em')
}

task xslt2(type: SaxonXsltTask) {
    configure common
    parameters(title: 'bar', padding: '2em')
}
ndw commented 3 years ago

Possibly. I'm still a Gradle novice, I think, so I'm not always sure how the pieces fit together.

Is configure in this case, simply a method that takes a map of configuration settings, or is there more going on?

eerohele commented 3 years ago

Something like that, I guess:

https://docs.gradle.org/current/userguide/more_about_tasks.html

I'm a perpetual Gradle novice as well, so I'm not sure I can offer much more information than this. (Also, I haven't used Gradle myself in a long time, so I don't have a whole lot of interest in keeping up with the frankly quite incredible churn in the ecosystem.)

In any case, if possible, I would prefer to tackle this use case using Gradle's built-in features, since I'd really like to keep this plugin as small as possible. If you find Gradle's offering insufficient in some respect, however, we can revisit the issue. In that case, though, it'd be really great to have unit tests for the new functionality, too.