Link to home
Start Free TrialLog in
Avatar of manajit
manajitFlag for India

asked on

Check if a file exists using xslt

Hi,

I am a novice in xslt and am using it for customizing my reports on selenium tool.

I would like to know a step by step method to be able to check if a file exists in a particular directory.  If it does then I will fill in a value fora particular column else the column will be left blank.

The key to getting the points is a detailed step by step procedure.
Avatar of CEHJ
CEHJ
Flag of United Kingdom of Great Britain and Northern Ireland image

Why would you try to use xslt for such a task? xslt is for document transformation
Unfortunately, this is not an easy thing to do or potentially not even possible at all, hence I agree with CEHJ that you should seriously think about other options too. However, in some (fairly specific) situations it may be possible...

If the files that you want to check existence for are ALL text files and the process that runs the XSLT has permission to "read" the file (ie. it's not enough to just be able to list a directories contents) you could do something like the following
XSLT
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/">
	<xsl:for-each select="//file">
		<xsl:value-of select="@path" />
		<xsl:text>&#x09;&#x09;</xsl:text>
		<xsl:value-of select="unparsed-text-available(@path)" />
		<xsl:text>&#x0d;&#x0a;</xsl:text>
	</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

Open in new window

You would use this with an input XML like...
<filelist>
	<file path="file:///C:/Temp/IMG_0146.JPG" />
	<file path="file:///C:/Temp/IMG_0147.JPG" />
	<file path="file:///C:/Temp/IMG_0148.JPG" />
	<file path="file:///C:/Temp/test1.txt" />
	<file path="file:///C:/Temp/test2.txt" />
	<file path="file:///C:/Temp/test3.txt" />
</filelist>

Open in new window

If the file paths were not in this specific URI format, you would need to use xpath/xslt functions to manipulate the file path before calling uparsed-text-available. Note, when I run this on my system, the first two jpeg files DO exist but the output is still false because they are not text files. However, for the text files I get the correct output based on whether those files are in that directory or not.


If the above is no good for you but you are using an XSLT engine that supports extension functions (there are a few but I pretty much only use Saxon which I know does support them) you can setup an XSLT to be able to call Java functions and in this case you might be able to call the File.exists method (or a custom class method that you create) to determine whether the file exists. If this is potentially a solution, let us know the name and version of your XSLT engine and we can assist further. (I'm not providing any detail solution here just yet because that solution can vary widely based on what engine you are using)


A final possibility is that if you are only interested in determining the existence of files that AREN'T part of the input XML (ie. rather than having an XML like my example above, you only need to know the existence of a file that you know the name of BEFORE running the XSLT, you can determine whether that file exists using normal Java methods and then pass the result of that (true/false) into the XSLT as a parameter. Again the exact method of passing parameters may depend on the engine that you are using, so let us know the name/version and I can provide more info.
Avatar of manajit

ASKER

Thank you mccarl for the detailed explanation.  It gives me a much better understanding.

So,  I would like to use the extension functions. However, I may sound extremely stupid here but how do I find out the xslt engine which is being used?

I am using selenium 2.33 for automation testing and using junit 4 jar files. I am using ant version 1.99. I believe the xslt is used by junit for the transformation.  I am using xslt version 1.0. Could you help me know how to go about finding which xslt engine is being used or what is it dependent on.

Thanks.
I may sound extremely stupid here but how do I find out the xslt engine which is being used?
No, that's not stupid at all. I find that this is one of the more confusing parts of Java and that is after working with it for years!

If you don't know than it is quite likely that you are using the in-built parser/transformer that comes with recent versions of Java itself and this is called Xerces/Xalan. The only way that you could be using a different version is if the implementation of a different parser is present on your classpath. So if you haven't purposefully used a different version (and any of the libraries that you are using haven't included other versions) you should be using the default in-built one. I haven't had any experience with Selenium so I can't say whether it does include it's own version of any particular parser.

Having said all that, there is probably only really two fairly common parsers in use, either the in-built Xerces/Xalan or the fairly popular Saxon, and so I would say that you are likely to be using one of the two. And after looking at the details, the comment I made earlier about things varyin widely is a bit off the mark because it isn't terribly different. Hence, what I have done is to get TWO solution, one for each version and you can then just try both and see which one works for you.

Firstly, with either version, calling of instance methods on objects is a little bit cumbersome because since XSLT isn't an OO language as such. Therefore, one way to make this a little bit more straightforward is to create a small helper class that has a static method that creates your object and calls the instance method on it. So, either of the below solution depend on this FileHelper class. (Note most of the details of this class are irrelevant as long as you match up those details with how this is called from the XSL file, it should be pretty self explanatory though)

FileHelper.java
package utils.files;
import java.io.File;

public class FileHelper {
    public static boolean isFileExists(String path) {
        return new File(path).exists();
    }
}

Open in new window


Also, both the below solution work based on the same input xml file (which is a little bit different to the example that I gave above, but hopefully it is closer to what you would already have in your input xml and so likely that you won't have to do any manipulation of the file path)
<filelist>
    <file path="C:\Temp\IMG_0146.JPG" />
    <file path="C:\Temp\IMG_0147.JPG" />
    <file path="C:\Temp\IMG_0148.JPG" />
    <file path="C:\Temp\test1.txt" />
    <file path="C:\Temp\test2.txt" />
    <file path="C:\Temp\test3.txt" />
</filelist>

Open in new window


Xerces/Xalan parser

The important points to note with the below;
Line 4 - the declaration of the "java" namespace prefix, which gives access to any Java class on the classpath
Line 5 - the exclude-result-prefixes (which is optional, it just cleans up the output if you are transforming to another xml file)
Line 11 - the format of the function call to call the static method on the FileHelper class
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" 
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
        xmlns:java="http://xml.apache.org/xalan/java" 
        exclude-result-prefixes="java">
<xsl:output method="text"/>
<xsl:template match="/">
    <xsl:for-each select="//file">
        <xsl:value-of select="@path" />
        <xsl:text>&#x09;&#x09;</xsl:text>
        <xsl:value-of select="java:utils.files.FileHelper.isFileExists(@path)" />
        <xsl:text>&#x0d;&#x0a;</xsl:text>
    </xsl:for-each>
</xsl:template>
</xsl:stylesheet>

Open in new window


Saxon parser

The important points to note with the below;
Line 4 - the declaration of the "file" namespace prefix, which gives access to just that FileHelper class
Line 5 - the exclude-result-prefixes (which is optional, it just cleans up the output if you are transforming to another xml file)
Line 11 - the format of the function call to call the static method on the FileHelper class as defined in the namespace declaration
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" 
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
        xmlns:file="java:utils.files.FileHelper" 
        exclude-result-prefixes="file">
<xsl:output method="text"/>
<xsl:template match="/">
    <xsl:for-each select="//file">
        <xsl:value-of select="@path" />
        <xsl:text>&#x09;&#x09;</xsl:text>
        <xsl:value-of select="file:isFileExists(@path)" />
        <xsl:text>&#x0d;&#x0a;</xsl:text>
    </xsl:for-each>
</xsl:template>
</xsl:stylesheet>

Open in new window



So they give two slightly different ways of calling Java code (and unfortunately they aren't compatible with each other). If neither of these two solutions work for you, can you post the full error messages/stack traces that you get and we can investigate further.
Avatar of manajit

ASKER

Thanks again for the detailed explanation.

However, I still continue with some issues.

I started with assuming that the engine is Xerces/Xalan parser.

We created the java code and compiled it.

Then I went on to change the declaration in my xsl to the following
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:lxslt="http://xml.apache.org/xslt"
    xmlns:redirect="http://xml.apache.org/xalan/redirect"
    xmlns:string="xalan://java.lang.String"
    extension-element-prefixes="redirect"
	xmlns:java="http://xml.apache.org/xalan/java"
	exclude-result-prefixes="java">
<xsl:output method="html" indent="yes" encoding="UTF-8"/>
<xsl:decimal-format decimal-separator="." grouping-separator=","/>

Open in new window


And made changes to the code as below
<xsl:when test="failure">	
			<xsl:if test="java:utils.files.FileHelper.isFileExists(string(concat('C:\ScreenShots\',@name,'.png')))">
				<td>
					<a title="Display Screen Shot" href="file://C:\ScreenShots\{@name}.png">
						<xsl:value-of select="@name" />
					</a>
				</td>
			</xsl:if>
        </xsl:when>

Open in new window


When I build it, I get the following error.
Buildfile: C:\Users\Nischitha\workspace\MediaGrid_TestSuite\build.xml

junitreport:
[junitreport] Processing C:\Users\Nischitha\workspace\MediaGrid_TestSuite\Report
s\TESTS-TestSuites.xml to C:\Users\NISCHI~1\AppData\Local\Temp\null112177962
[junitreport] Loading stylesheet C:\Users\Nischitha\workspace\MediaGrid_TestSuit
e\StyleDir\junit-frames.xsl
[junitreport] : Error! Cannot find class 'utils.files.FileHelper'.
[junitreport] : Error! Cannot find external method 'utils.files.FileHelper.isFil
eExists' (must be public).
[junitreport] : Error! Could not compile stylesheet
[junitreport] : Fatal Error! Could not compile stylesheet Cause: Cannot convert
data-type 'void' to 'boolean'.
[junitreport] Failed to process C:\Users\Nischitha\workspace\MediaGrid_TestSuite
\Reports\TESTS-TestSuites.xml

BUILD FAILED
C:\Users\Nischitha\workspace\MediaGrid_TestSuite\build.xml:120: Errors while app
lying transformations: Fatal error during transformation using C:\Users\Nischith
a\workspace\MediaGrid_TestSuite\StyleDir\junit-frames.xsl: Could not compile sty
lesheet

Open in new window


Can you please tell me, where is the problem and where does it actually look for the java file. Do let me know if you need any further information from my side.

Thanks.
Avatar of manajit

ASKER

Next, I also assumed that the engine may be Saxon parser.

The java code remained the same.

I changed the declaration to the following
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:lxslt="http://xml.apache.org/xslt"
    xmlns:redirect="http://xml.apache.org/xalan/redirect"
    xmlns:string="xalan://java.lang.String"
    extension-element-prefixes="redirect"
    xmlns:file="java:utils.files.FileHelper"
    exclude-result-prefixes="file">
<xsl:output method="html" indent="yes" encoding="UTF-8"/>
<xsl:decimal-format decimal-separator="." grouping-separator=","/>

Open in new window


And changed the code to
        <xsl:when test="failure">   
            <xsl:if test="file:isFileExists(string(concat('c:\ScreenShots\',@name,'.png')">
                <td>
                    <a title="Display Screen Shot" href="file://C:\ScreenShots\{@name}.png">
                        <xsl:value-of select="@name" />
                    </a>
                </td>
            </xsl:if>
        </xsl:when>

Open in new window


However, I get an error
C:\Users\Nischitha\workspace\MediaGrid_TestSuite>ant junitreport
Buildfile: C:\Users\Nischitha\workspace\MediaGrid_TestSuite\build.xml

junitreport:
[junitreport] Processing C:\Users\Nischitha\workspace\MediaGrid_TestSuite\Report
s\TESTS-TestSuites.xml to C:\Users\NISCHI~1\AppData\Local\Temp\null1533298108
[junitreport] Loading stylesheet C:\Users\Nischitha\workspace\MediaGrid_TestSuit
e\StyleDir\junit-frames.xsl
[junitreport] : Error! The first argument to the non-static Java function 'isFil
eExists' is not a valid object reference.
[junitreport] : Error! Could not compile stylesheet
[junitreport] : Fatal Error! Could not compile stylesheet Cause: Cannot convert
data-type 'void' to 'boolean'.
[junitreport] Failed to process C:\Users\Nischitha\workspace\MediaGrid_TestSuite
\Reports\TESTS-TestSuites.xml

BUILD FAILED
C:\Users\Nischitha\workspace\MediaGrid_TestSuite\build.xml:120: Errors while app
lying transformations: Fatal error during transformation using C:\Users\Nischith
a\workspace\MediaGrid_TestSuite\StyleDir\junit-frames.xsl: Could not compile sty
lesheet

Total time: 0 seconds

Open in new window


Any further help will be appreciated. :)
Ok, so you are using the Xerces/Xalan parser and transformer. The error message in the first post is the clue... Error! Cannot find class 'utils.files.FileHelper'. As I mentioned, the compiled class file for that class needs to be on the classpath of the JVM that is running the tests. It is a bit hard to see from my limited view of your environment, but it looks as though you are using Ant to manage building/running of these tests, is that true? Also, do you have other Java code that is in this "MediaGrid_TestSuite" project or is it just files that drive the running of the Selenium tests?

If the code is already being compiled by Ant than maybe all you need is to add a directive inside Ant's build.xml file to add a required directory to the classpath for running these tests. Sorry, it is a little hard to be exact in my help because I don't have your whole setup in front of me. If the above instruction aren't of much help for you, you could start by sending me the directory/subdirectory layout of your project (so I can get a feel for what type of files are where) and if you can post your "build.xml" file (and any other files that it includes/imports) so that I can see how it is being executed.
Avatar of manajit

ASKER

Yes, we are using ant to build the tests.
The tests are a part of the MediaGridTest package and all the java files are in the same package.

We also tried to change the package of the FileHelper, however, the issue remains the same that the xsl is not able to find this class/method.

I am attaching the build.xml which is used by ant and the directory structure as well.

Hope it helps you help me. :-)
Build.xml
directory.txt
ASKER CERTIFIED SOLUTION
Avatar of mccarl
mccarl
Flag of Australia image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of manajit

ASKER

One of the most patient and persistent expert I have come across.
Your welcome, and thank you for your kind comments, much appreciated! :)