0

My xsl sheet transforms plain text to svg. I get the plain text from a user input to my servlet, and after that I want to pass the input as a parameter to my xsl sheet. My last question was how to pass the parameter and is answered here (Pass user input to XSL stylesheet), but I realized I don't know how to call the transformation. My xsl doesn't take a xml input, so I can't use transformer.transform for instance. How is it possible to run the transformation with the parameter and get the result as a string that I can return to the user?

Any help would be appreciated. If you need more details let me know. Thanks!

Edit: If you are interested,the idea is that I can transform a plain text notation to svg and it works that the first part of the transformation transforms the plain text to a xml notation and after that the xml to svg(XSLT 2.0: Transform notation in plain text to svg). It works fine for simple symbols when the plain text is in a variable. But I want to expand a webservice with the the functionality and it should be possible that a user gives the input, that is why I want to pass a parameter to my xsl. The problem is that I donn't know how to call the transformation from java that I can get the result to return it to the user.

Here is the xsl:

<?xml version="1.0" encoding="UTF-8"?>
<!-- Ansatz mit Hilfe von 
https://stackoverflow.com/questions/34682331/xslt-2-0-transform-notation-in-plain-text-to-svg -->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:local="local"
    exclude-result-prefixes="xs"
    version="2.0"
    xmlns:svg="http://www.w3.org/2000/svg">
    <xsl:output indent="yes"/>
<!-- 
<xsl:variable name="drawing-text">
GRAPHREP
PEN color:$000000 w:2pt
FILL color:$ff7f00
ROUNDRECT x:0pt y:0pt w:114pt h:70pt rx:20pt ry:20pt

</xsl:variable>
 -->
<xsl:param name="drawing-text" />

    <!--matches sequences of UPPER-CASE letters -->
    <xsl:variable name="label-pattern" select="'[A-Z]+'"/>
    <!--matches the "attributes" in the line i.e. w:2pt,
        has two capture groups (1) => attribute name, (2) => attribute value -->
    <xsl:variable name="attribute-pattern" select="'\s?(\S+):(\S+)'"/> 
    <!--matches a line of data for the drawing text, 
        has two capture groups (1) => label, (2) attribute data-->
    <xsl:variable name="line-pattern" select="concat('(', $label-pattern, ')\s(.*)\n?')"/>
    <!-- Text in quotes holen-->
    <xsl:variable name="text-pattern" select="'&quot;(.*?)&quot;'"/>

    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="/">
        <svg width="640" height="480">
            <g>
                <!-- Find the text patterns indicating the shape -->
                <!--Replaced unparsed-text() with local variable for testing                
                select="unparsed-text('drawing.txt')" -->
                <xsl:analyze-string select="$drawing-text"
                    regex="{concat('(', $label-pattern, ')\n((', $line-pattern, ')+)\n?')}">
                    <xsl:matching-substring>
                        <!--Convert text to XML -->
                        <xsl:variable name="drawing-markup" as="element()">
                            <!--Create an element for this group, using first matched pattern as the element name 
                                (i.e. GRAPHREP => <GRAPHREP>) -->
                            <xsl:element name="{regex-group(1)}">
                                <!--split the second matched group for this shape into lines by breaking on newline-->
                                <xsl:variable name="lines" select="tokenize(regex-group(2), '\n')"/>
                                <xsl:for-each select="$lines">
                                    <!--for each line, run through this process to create an element with attributes
                                        (e.g. FILL color:$frf7f00 => <FILL color=""/>
                                    -->
                                    <xsl:analyze-string select="." regex="{$line-pattern}">
                                        <xsl:matching-substring>
                                            <!--create an element using the UPPER-CASE label starting the line -->
                                            <xsl:element name="{regex-group(1)}">
                                                <!-- capture each of the attributes -->
                                                <xsl:analyze-string select="regex-group(2)" regex="\s?(\S+):(\S+)">
                                                  <xsl:matching-substring>
                                                  <!--convert foo:bar into attribute foo="bar", 
                                                            translate $ => # 
                                                            and remove the letters 'p' and 't' by translating into nothing"-->
                                                  <xsl:attribute name="{regex-group(1)}" select="translate(regex-group(2), '$pt', '#')"/>
                                                  </xsl:matching-substring>
                                                  <xsl:non-matching-substring/>
                                                </xsl:analyze-string>
                                            </xsl:element>
                                        </xsl:matching-substring>
                                        <xsl:non-matching-substring/>
                                    </xsl:analyze-string>
                                </xsl:for-each>
                            </xsl:element>
                        </xsl:variable>
                        <!--Uncomment the copy-of below if you want to see the intermediate XML $drawing-markup-->
                        <!--<xsl:copy-of select="$drawing-markup"/>-->

                        <!-- Transform XML into SVG -->
                        <xsl:apply-templates select="$drawing-markup"/>

                    </xsl:matching-substring>
                    <xsl:non-matching-substring/>
                </xsl:analyze-string>
            </g>
        </svg>
    </xsl:template>

    <!--==========================================-->
    <!-- Templates to convert the $drawing-markup -->
    <!--==========================================-->

    <!--for supported shapes, create the element using
        lower-case value, and change rectangle to rect
        for the svg element name   !!! if abfrage ob text-->
    <xsl:template match="GRAPHREP[ELLIPSE | RECTANGLE | ROUNDRECT | LINE | TEXT]">
        <xsl:if test="ELLIPSE | RECTANGLE | ROUNDRECT | LINE">
            <xsl:element name="{replace(lower-case(local-name(ELLIPSE | RECTANGLE | ROUNDRECT | LINE)), 'rectangle|roundrect', 'rect', 'i')}">
                <xsl:attribute name="id" select="concat('id_', generate-id())"/>
                <xsl:apply-templates />
            </xsl:element>
        </xsl:if>
        <xsl:if test="TEXT">
            <xsl:element name="{lower-case(local-name(TEXT))}">
                <xsl:attribute name="id" select="concat('id_', generate-id())"/>
                <xsl:apply-templates />
                <!-- Da muss der text aus den quotes rein -->
            </xsl:element>
         </xsl:if>
    </xsl:template>

    <xsl:template match="ELLIPSE | RECTANGLE | ROUNDRECT | LINE | TEXT"/>

    <!-- Just process the content of GRAPHREP.
        If there are multiple shapes and you want a new 
        <svg><g></g></svg> for each shape, 
        then move it from the template for "/" into this template-->
    <xsl:template match="GRAPHREP/*">
        <xsl:apply-templates select="@*"/>
    </xsl:template>

    <xsl:template match="PEN" priority="1">
        <!--TODO: test if these attributes exist, if they do, do not create these defaults.
            Hard-coding for now, to match desired output, since I don't know what the text
            attributes would be, but could wrap each with <xsl:if test="not(@dasharray)">-->
        <xsl:attribute name="stroke-dasharray" select="'null'"/>
        <xsl:attribute name="stroke-linjoin" select="'null'"/>
        <xsl:attribute name="stroke-linecap" select="'null'"/>
        <xsl:apply-templates select="@*"/>
    </xsl:template>

    <!-- conterts @color => @stroke -->
    <xsl:template match="PEN/@color">
        <xsl:attribute name="stroke" select="."/>
    </xsl:template>

    <!--converts @w => @stroke-width -->
    <xsl:template match="PEN/@w">
        <xsl:attribute name="stroke-width" select="."/>
    </xsl:template>

    <!--converts @color => @fill and replaces $ with # -->
    <xsl:template match="FILL/@color">
        <xsl:attribute name="fill" select="translate(., '$', '#')"/>
    </xsl:template>

    <!--converts @h => @font-size !!noch mit text verbinden -->
    <xsl:template match="FONT/@h">
        <xsl:attribute name="font-size" select="."/>
    </xsl:template>

    <!--converts @color => @fill !!noch mit text verbinden -->
    <xsl:template match="FONT/@color">
        <xsl:attribute name="fill" select="translate(., '$', '#')"/>
    </xsl:template>

    <!-- converts @x => @cx with hard-coded values. 
        May want to use value from text, but matching your example-->
    <xsl:template match="ELLIPSE/@x | ELLIPSE/@y">
        <!--not sure if there was a relationship between ELLIPSE x:0pt y:0pt, and why 0pt would be 250, 
            but just an example...-->
        <xsl:attribute name="c{name()}" select="250"/>
    </xsl:template>

    <xsl:template match="RECTANGLE/@w | ROUNDRECT/@w">
        <xsl:attribute name="{name()}idth" select="."/>
    </xsl:template>

    <xsl:template match="RECTANGLE/@h | ROUNDRECT/@h">
        <xsl:attribute name="{name()}eight" select="."/>
    </xsl:template>

    <xsl:template match="LINE/@x | LINE/@y">
        <xsl:attribute name="{name()}" select="."/>
    </xsl:template>

    <xsl:template match="TEXT/@x | TEXT/@y">
        <xsl:attribute name="{name()}" select="."/>
    </xsl:template>

</xsl:stylesheet>
Community
  • 1
  • 1
Randy Random
  • 69
  • 2
  • 8
  • Don't you have any `` in your XSLT? It is an XML transformation language and *requires* an XML input. I'm curious to see your XSLT! – Kenney Feb 13 '16 at 17:09
  • You can run a transformation on an empty XML document instance if your XSLT's output is not driven by the XML but actually by, say, `` or calls to `document()`. – Tomalak Feb 13 '16 at 17:13
  • If all the data required to produce the output is in the passed parameter, why don't you use a dummy XML document as the input? It could be anything, e.g. a file containing only ``. – michael.hor257k Feb 13 '16 at 17:13
  • Which XSLT 2.0 processor do you use? It depends on its API how you call it and define your initial template. See http://saxonica.com/html/documentation9.6/using-xsl/embedding/s9api-transformation.html for Saxon 9.6 for example. – Martin Honnen Feb 13 '16 at 17:29
  • I use the Saxon9 API. Thx I will look into it. – Randy Random Feb 13 '16 at 17:42

2 Answers2

3

In XSLT 2.0 you can use a named template (e.g. by changing <xsl:template match="/"> to <xsl:template match="/" name="main">) and then, for instance with Saxon 9.6 you could use the method http://saxonica.com/html/documentation9.6/javadoc/net/sf/saxon/s9api/XsltTransformer.html#setInitialTemplate%28net.sf.saxon.s9api.QName%29 to set myTransformer.setInitialTemplate(new QName("main")). See http://saxonica.com/html/documentation9.6/using-xsl/embedding/s9api-transformation.html on details on how to use its API.

The Java JAXP API is geared towards XSLT 1.0 and does not allow you to run a transformation without an input document, there, you would indeed need to provide a dummy document.

Martin Honnen
  • 160,499
  • 6
  • 90
  • 110
1

There is no XSL transformation without XML input (in XSLT 1.0). As MartinHonnen pointed out it is possible in XSLT2.0. (XSLT1.0 is still widely used however since 2.0 is not supported natively in web browsers)

As Tomalak and Michael said you could use an empty XML document as input to the transformation and then pass the parameter.

Perhaps it would be event better to create an XML Document with your input data as the content, e.g. <dummy><input1>data from request</input1></dummy>

In the XSL document you would then create a new document, like so

<xsl:template match="/dummy">
This was the input: <xsl:value-of select="input1"/>
</xsl:template>

Note this is just one example there are many ways to achieve this.

Finally, if all you want your XSL for is to create an XML document with your input, probably you should look into creating the document directly without using XSL.

Checkout this tutorial for how to create the doc from scratch: http://examples.javacodegeeks.com/core-java/xml/dom/create-dom-document-from-scratch/

robert
  • 978
  • 7
  • 17
  • 1
    XSLT 2.0 allows you to run a stylesheet starting with a named template, you don't need an input document in that case. – Martin Honnen Feb 13 '16 at 17:27
  • @MartinHonnen good to know +1. Regarding this question though it feels cumbersome to use XSL (unless there is an existing sheet that has to be used). – robert Feb 13 '16 at 17:30
  • I guess the solution in http://stackoverflow.com/questions/34682331/xslt-2-0-transform-notation-in-plain-text-to-svg is the existing stylesheet to be used. – Martin Honnen Feb 13 '16 at 17:36