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.
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.
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.
This function is called to format the source code.
public native String AStyleMain(String textIn, String options);
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.
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.
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.
This function is called to get the Artistic Style version number.
public native String AStyleGetVersion();
A Java String containing the Artistic Style version number.
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 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.
private void ErrorHandler(int errorNumber, String errorMessage)
errorNumber
The error number as defined by Artistic Style.
errorMessage
A Java String containing the error message as defined by Artistic Style.
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.
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