# Copyright (C) 2003 Doug Gregor. Permission to copy, use, modify, sell and
# distribute this software is granted provided this copyright notice appears in
# all copies. This software is provided "as is" without express or implied
# warranty, and with no claim as to its suitability for any purpose.

# This module defines rules to apply an XSLT stylesheet to an XML file using the
# xsltproc driver, part of libxslt.

import "class" : new ;
import common ;
import feature ;
import generators ;
import modules ;
import os ;
import path ;
import regex ;
import sequence ;
import toolset ;
import virtual-target ;

feature.feature "xsl:param" : : free ;
feature.feature "xsl:path" : : free ;
feature.feature catalog : : free ;


# Initialize xsltproc support. The parameters are:
#   xsltproc: The xsltproc executable
#
rule init ( xsltproc ? )
{
    if $(xsltproc)
    {
        modify-config ;
        .xsltproc = $(xsltproc) ;
        check-xsltproc ;
    }
}


rule freeze-config ( )
{
    if ! $(.config-frozen)
    {
        .config-frozen = true ;
        .xsltproc ?= [ modules.peek : XSLTPROC ] ;
        .xsltproc ?= xsltproc ;
        check-xsltproc ;
        .is-cygwin = [ .is-cygwin $(.xsltproc) ] ;
    }
}


rule modify-config ( )
{
    if $(.config-frozen)
    {
        import errors ;
        errors.user-error
            "xsltproc: Cannot change xsltproc command after it has been used." ;
    }
}


rule check-xsltproc ( )
{
    if $(.xsltproc)
    {
        local status = [ SHELL "\"$(.xsltproc)\" -V" : no-output : exit-status ]
            ;
        if $(status[2]) != 0
        {
            import errors ;
            errors.user-error "xsltproc: Could not run \"$(.xsltproc)\" -V." ;
        }
    }
}

rule name ( )
{
    freeze-config ;
    return $(.xsltproc) ;
}

# Returns a non-empty string if a cygwin xsltproc binary was specified.
#
rule is-cygwin ( )
{
    freeze-config ;
    return $(.is-cygwin) ;
}


rule .is-cygwin ( xsltproc )
{
    if [ os.on-windows ]
    {
        local file = [ path.make [ modules.binding $(__name__) ] ] ;
        local dir = [ path.native [ path.join [ path.parent $(file) ] xsltproc ]
            ] ;
        if [ os.name ] = CYGWIN
        {
            dir = $(dir:W) ;
        }
        local command =
            "\"$(xsltproc)\" \"$(dir)\\test.xsl\" \"$(dir)\\test.xml\" 2>&1" ;
        local status = [ SHELL $(command) : no-output : exit-status ] ;
        if $(status[2]) != "0"
        {
            return true ;
        }
    }
}

class xsltproc-action : action
{
    rule adjust-properties ( property-set )
    {
        local s = [ $(self.targets[1]).creating-subvariant ] ;
        if $(s)
        {
            return [ $(property-set).add-raw
                [ $(s).implicit-includes "xsl:path" : XML ] ] ;
        }
        else
        {
            return $(property-set) ;
        }
    }
}

class xsltproc-generator : generator
{
    rule action-class ( )
    {
        return xsltproc-action ;
    }
}

rule register-generator ( id : source-types + : target-types + : requirements * )
{
    if ! $(id) in $(.known-rules)
    {
        .known-rules += $(id) ;
        flags $(id) ;
    }
    generators.register [ new xsltproc-generator $(id) :
        $(source-types) : $(target-types) : $(requirements) ] ;
}

IMPORT xsltproc : register-generator : : generators.register-xslt ;

rule flags ( rulename )
{
    toolset.uses-features $(rulename) : <xsl:param> <catalog> : unchecked ;
    toolset.flags $(rulename) XSL-PATH : <xsl:path> : unchecked ;
    toolset.flags $(rulename) FLAGS : <flags> : unchecked ;
}

rule compute-xslt-flags ( target : properties * )
{
    local flags ;
    # Translate <xsl:param> into command line flags.
    for local param in [ feature.get-values <xsl:param> : $(properties) ]
    {
        local namevalue = [ regex.split $(param) "=" ] ;
        flags += --stringparam $(namevalue[1]) \"$(namevalue[2])\" ;
    }

    return $(flags) ;
}


local rule .xsltproc ( target : source stylesheet : properties * : dirname ? :
    action )
{
    freeze-config ;
    STYLESHEET on $(target) = $(stylesheet) ;
    FLAGS on $(target) += [ compute-xslt-flags $(target) : $(properties) ] ;
    NAME on $(target) = $(.xsltproc) ;

    for local catalog in [ feature.get-values <catalog> : $(properties) ]
    {
        CATALOG = [ common.variable-setting-command XML_CATALOG_FILES :
            $(catalog:T) ] ;
    }

    if [ os.on-windows ] && ! [ is-cygwin ]
    {
        action = $(action).windows ;
    }

    $(action) $(target) : $(source) ;
}


rule xslt ( target : source stylesheet : properties * )
{
    return [ .xsltproc $(target) : $(source) $(stylesheet) : $(properties) : :
        xslt-xsltproc ] ;
}


rule xslt-dir ( target : source stylesheet : properties * : dirname )
{
    return [ .xsltproc $(target) : $(source) $(stylesheet) : $(properties) :
        $(dirname) : xslt-xsltproc-dir ] ;
}

_ = " " ;

actions xslt-xsltproc.windows
{
    $(CATALOG) "$(NAME:E=xsltproc)" $(FLAGS) --path$(_)"$(XSL-PATH:W)" --xinclude -o "$(<)" "$(STYLESHEET:W)" "$(>:W)"
}


actions xslt-xsltproc bind STYLESHEET
{
    $(CATALOG) "$(NAME:E=xsltproc)" $(FLAGS) --path$(_)"$(XSL-PATH:T)" --xinclude -o "$(<)" "$(STYLESHEET:T)" "$(>:T)"
}


actions xslt-xsltproc-dir.windows bind STYLESHEET
{
    $(CATALOG) "$(NAME:E=xsltproc)" $(FLAGS) --path$(_)"$(XSL-PATH:W)" --xinclude -o "$(<:D)/" "$(STYLESHEET:W)" "$(>:W)"
}


actions xslt-xsltproc-dir bind STYLESHEET
{
    $(CATALOG) "$(NAME:E=xsltproc)" $(FLAGS) --path$(_)"$(XSL-PATH:T)" --xinclude -o "$(<:D)/" "$(STYLESHEET:T)" "$(>:T)"
}
