Artistic Style Developer Information

Calling Artistic Style as a Java Native Interface

 

Artistic Style can be compiled as a shared library (DLL) with a Java Native Interface (JNI). This will allow the library to be called from a Java program.

In a JNI shared library, the name of the Java calling class is included in the name of the function call. The name of the calling class in the Artistic Style JNI is AStyleInterface. To use the shared library without modification the name of the Java class that calls Artistic Style needs to be AStyleInterface. Calling the shared library from another class will require a modification to Artistic Style.  To determine the function name being called run the Java program "javah". It will generate a C/C++ header file (.h) that contains the function names. These must be the same names that are defined in the Artistic Style shared library.

Since Java does not use function pointers the name of callback functions must be included in the shared library. Artistic Style expects a callback function named ErrorHandler.  To use the shared library without modification the name of the Java callback function needs to be ErrorHandler and it must be in the class AStyleInterface. Using a different callback function or class name will require a modification to Artistic Style.

A shared library that uses the JNI can still be called from a C, C++, or C# program. You just need to use the proper calling procedure.

Compile Options

To compile Artistic Style for use in a JNI the compile option ASTYLE_JNI must be defined. Then Artistic Style will accept the files and options as parameters from a function call. It is the responsibility of the Java program to read the source files and accept the options from the user via a graphical interface or other method. These are then passed via the function call described below. After the source files are formatted they will be returned to the Java program which must then save the source file and take other appropriate action.

Static Constructor

A static constructor is required to load the native Artistic Style shared library. The method name contains only the keyword "static" and will be executed when the program begins. It should include a call to either System.load or System.loadLibrary. Since it is calling an external library it is best to place the load method in a try/catch block.

AStyleMain Function

This function is called to format the source code.

Syntax

public native String AStyleMain(String textIn, String options);

Parameters

textIn
A Java String containing the source file to be formatted.

options
A Java String containing the formatting options. They should be in the same format as in the default options file. The options may be set apart by new-lines, commas, tabs or spaces. The long options do not need the "--" prefix. Comments may be used but they must be terminated by a new-line "\n" character.

If the file is not a C/C++ file, the file mode option "mode=java" or "mode=cs" must be included. Otherwise the default mode of C/C++ is used.

Return Value

If the function succeeds, the return value is a Java String containing the formatted source code.

If the function fails, the return value is an empty Java String. The returned String should be checked for a length of zero to determine if an error occurred. Before the source is returned, an error message will be sent to the ErrorHandler method.

The function will NOT fail for an invalid option in the formatting options. In this case, an error message is sent to the error handling function and the formatted source code is returned without using the invalid option.

Remarks

The ASyleMain function must be called from the class AStyleInterface. The name of the function in Artistic Style is Java_AStyleInterface_AStyleMain. If the class name is changed, or the method name is changed, the function names in the Artistic Style shared library must also be changed.

The call to AStyleMain should be placed in a try/catch block that contains an UnsatisfiedLinkError to handle not finding the entry point.

AStyleGetVersion Function

This function is called to get the Artistic Style version number.

Syntax

public native String AStyleGetVersion();

Return Value

A Java String containing the Artistic Style version number.

Remarks

The AStyleGetVersion function must be called from the class AStyleInterface. The name of the function in Artistic Style is Java_AStyleInterface_AStyleGetVersion. If the class name is changed, or the method name is changed, the function names in the Artistic Style shared library must also be changed.

The call to AStyleMain should be placed in a try/catch block that contains an UnsatisfiedLinkError to handle not finding the entry point.

ErrorHandler Function

ErrorHandler is called by the Artistic Style static library when an error occurs. It should display an error message and then either abort or continue the program depending on the error.

Syntax

private void ErrorHandler(int errorNumber, String errorMessage)

Parameters

errorNumber
The error number as defined by Artistic Style.

errorMessage
A Java String containing the error message as defined by Artistic Style.

Remarks

Error messages numbered 100-199 are errors that prevent the file from being formatted. A zero length String is returned to the calling program. Error messages numbered 200-299 are errors that do NOT prevent the file from being formatted. A formatted String is returned. This will occur if an invalid option is sent to AStyleMain. The calling program has the option of accepting or rejecting the formatted file.

The name of the callback error handling method must be ErrorHandler and it must be in the same class as the method AStyleMain. Artistic Style uses the JNI function GetMethodID which contains the method name ErrorHandler. Changing the method name or changing the class designation requires changing the Artistic Style shared library.

Example

The following example formats source files by calling the JNI functions in the Artistic Style shared library. It is a console application, but the procedure for a GUI is the same. The two classes can be copied and pasted into source files. Or they can be downloaded with test data from the "Developer Information" page. The Artistic Style source code must be compiled as a shared library (DLL) using the option ASTYLE_JNI. The shared library must be copied to the directory that contains the Java class files (it is loaded from the current directory). The directory of the files to be formatted is an absolute path in the function getProjectDirectory() in Example.java. This will need to be changed to reflect your directory structure. When the program is run be sure the current working directory is the directory with the class files and shared library.

 

         
// Example.java

/*
* Example opens the source files, calls the AStyleInterface methods
* to format the files, and saves the reformatted source. The files
* are in a test-data directory. The option mode=java must be included
* for java files.
*/

import java.io.*;

public class Example
{   /*
    * Main function for this example.
    */
    public static void main(String[] args)
    {   // files to pass to AStyle
        String fileName[] =  {  "AStyleDev/test-data/ASBeautifier.cpp",
                                "AStyleDev/test-data/ASFormatter.cpp",
                                "AStyleDev/test-data/astyle.h"
                             };

        // options to pass to AStyle
        // mode=java is required for Java files
        String options = "-A2tOP";

        // create an object
        AStyleInterface astyle = new AStyleInterface();

        // get Artistic Style version
        // does not need to terminate on an error
        String version = astyle.getVersion();
        if (version.length() != 0)
            System.out.println("Example Java - AStyle " + version);

        // process the files
        for (int i = 0; i < fileName.length; i++)
        {   // get the text to format
            String filePath = getProjectDirectory(fileName[i]);
            String textIn = getText(filePath);

            // call the Artistic Style formatting function
            String textOut = astyle.formatSource(textIn, options);
            // does not need to terminate on an error
            // an error message has been displayed by the error handler
            if (textOut.length() == 0)
            {   System.out.println("Cannot format "  + filePath);
                continue;
            }

            // return the formatted text
            System.out.println("Formatted " + fileName[i]);
            setText(textOut, filePath);
        }

        return;
    }

    /*
    * Error message function for this example.
    */
    private static void error(String message)
    {   System.out.println(message);
        System.out.println("The program has terminated!");
        System.exit(1);
    }

    /*
    * Prepend the project directory to the subpath.
    * This may need to be changed for your directory structure.
    */
    private static String getProjectDirectory(String subPath)
    {   String homeDirectory = System.getProperty("user.home");
        String projectPath = homeDirectory + "/Projects/" + subPath;
        return projectPath;
    }

    /*
    * Get the text to be formatted.
    * Usually the text would be obtained from an edit control.
    */
    private static String getText(String filePath)
    {   // create input buffers
        File inFile = new File(filePath);
        final int readSize =  131072;    // 128 KB
        StringBuffer bufferIn = new StringBuffer(readSize);
        char fileIn[] = new char[readSize];

        // read file data
        try
        {   BufferedReader in =
                new BufferedReader(new FileReader(inFile));
            // use read to preserve the current line endings
            int charsIn = in.read(fileIn, 0, readSize);
            while (charsIn != -1)
            {   bufferIn.append(fileIn, 0, charsIn);
                charsIn = in.read(fileIn, 0, readSize);
            }
            in.close();
        }
        catch (Exception e)
        {   if (e instanceof FileNotFoundException)
                error("Cannot open input file " + filePath);
            else if (e instanceof IOException)
                error("Error reading file " + filePath);
            else
                error(e.getMessage() + " " + filePath);
        }

        return bufferIn.toString();
    }

    /*
    * Return the formatted text.
    * Usually the text would be returned to an edit control.
    */
    private static void setText(String textOut, String filePath)
    {   // create a backup file
        String origfilePath = filePath +  ".orig";
        File origFile = new File(origfilePath);
        File outFile = new File(filePath);
        origFile.delete();                  // remove a pre-existing file
        if (!outFile.renameTo(origFile))
            error("Cannot create backup file " + origfilePath);

        // write the output file - same name as input
        try
        {   BufferedWriter out =
                new BufferedWriter(new FileWriter(filePath));
            out.write(textOut, 0, textOut.length());
            out.close();
        }
        catch (IOException e)
        {   error("Cannot write to output " + filePath);
        }
    }

}   // class Example

 

        
// AStyleInterface.java

/**
* AStyleInterface contains methods to call the Artistic Style formatter.
* Changing the class name requires changing Artistic Style.
*/

import java.io.File;

class AStyleInterface
{   static private String libraryName = null;

    /**
    * Call the AStyleMain function in Artistic Style.
    * @param   textIn   A string containing the source code to be formatted.
    * @param   options  A string of options to Artistic Style.
    * @return  A String containing the formatted source from Artistic Style,
    *         or an empty string on error.
    */
    public String formatSource(String textIn, String options)
    {   // Return the allocated string
        // Memory space is allocated by OnAStyleMemAlloc, a callback function from AStyle
        String textOut = new String("");
        try
        {   textOut = AStyleMain(textIn, options);
        }
        catch (UnsatisfiedLinkError e)
        {   //~ System.out.println(e.getMessage());
            error("Cannot call the Java AStyleMain function");
        }
        return textOut;
    }

    /**
    * Call the AStyleGetVersion function in Artistic Style.
    * @return  A String containing the version number from Artistic Style.
    */
    public String getVersion()
    {   String version = new String();
        try
        {   version = AStyleGetVersion();
        }
        catch (UnsatisfiedLinkError e)
        {   //~ System.out.println(e.getMessage());
            error("Cannot call the Java AStyleGetVersion function");
        }
        return version;
    }

    /**
    * Error message function for this example.
    */
    private static void error(String message)
    {   System.out.println(message);
        System.out.println("The program has terminated!");
        System.exit(1);
    }

    // methods to load Artistic Style -----------------------------------------

    /**
    * Static constructor to load the native Artistic Style library.
    * Does not need to terminate if the shared library fails to load.
    * But the exception must be handled when a function is called.
    */
    static
    {   // load shared library from the classpath
        String astyleDirectory = System.getProperty("user.dir");
        String astyleName = getLibraryName(astyleDirectory);
        String astylePath = astyleDirectory
                            + System.getProperty("file.separator")
                            + astyleName;

        try
        {   System.load(astylePath);
        }
        catch (UnsatisfiedLinkError e)
        {   System.out.println(e.getMessage());
            error("Cannot load native library " + astylePath);
        }
    }

    /**
    * Called by static constructor to get the shared library name.
    * This will get any version of the library in the classpath.
    * Usually a specific version would be obtained, in which case a constant
    * could be used for the library name.
    * @param  astyleDirectory  The directory containing the shared library.
    * @return  The name of the shared library found in the directory.
    */
    static private String getLibraryName(String astyleDirectory)
    {   // get the shared library extension for the platform
        String fileExt = System.mapLibraryName("");
        int dot = fileExt.indexOf(".");
        fileExt = fileExt.substring(dot);
        // get a library name in the classpath
        File directory = new File(astyleDirectory);
        for (File filePath : directory.listFiles())
        {   String fileName = filePath.getName().toLowerCase();
            if (filePath.isFile()
                    && (fileName.startsWith("astyle")
                        || fileName.startsWith("libastyle"))
                    &&  fileName.contains("j")
                    && fileName.contains(fileExt))
            {   libraryName = filePath.getName();
                break;
            }
        }
        if (libraryName == null)
        {   error("Cannot find astyle native library in "
                  + astyleDirectory
                  + System.getProperty("file.separator"));
        }
        return libraryName;
    }

    // methods to call Artistic Style -----------------------------------------

    /**
    * Calls the Java AStyleMain function in Artistic Style.
    * The function name is constructed from method names in this program.
    * @param   textIn   A string containing the source code to be formatted.
    * @param   options  A string of options to Artistic Style.
    * @return  A String containing the formatted source from Artistic Style.
    */
    public native String AStyleMain(String textIn, String options);

    /**
    * Calls the Java AStyleGetVersion function in Artistic Style.
    * The function name is constructed from method names in this program.
    *
    * @return    A String containing the version number of Artistic Style.
    */
    public native String AStyleGetVersion();

    /**
    * Error handler for messages from Artistic Style.
    * This method is called only if there are errors when AStyleMain is called.
    * This is for debugging and there should be no errors when the calling
    * parameters are correct.
    * Changing the method name requires changing Artistic Style.
    * Signature: (ILjava/lang/String;)V.
    *  @param  errorNumber   The error number from Artistic Style.
    *  @param  errorMessage  The error message from Artistic Style.
    */
    private void ErrorHandler(int errorNumber, String errorMessage)
    {   System.out.println("AStyle error "
                           + String.valueOf(errorNumber)
                           + " - " + errorMessage);
    }

}   // class AStyleInterface