OpenOffice and Java Macros

Here are a few pointers about creating Java macros for OpenOffice and embedding them in odf files. If the samples don't work here are a few basic things to try:

Remember to Set Macro Security to medium setting so that it ask if it's allowed to run macros and then re-open your document so that you have permission to run macros. Security settings can be changed in:

Tools
   Options
      Security
         Macro Security
            Medium

If you get errors like: java.lang.UnsupportedClassVersionError: Bad version number in .class file it means that the JVM (Java) configured for Open Office is to old for the code, typically you want to use version 1.6.X or newer. To change the java used by Open Office go to:

Tools
   Options
      Java

This should show you a list of all the available Java Virtual Machines, select a more recent one, confirm and restart Open Office. If you get errors classPathNotFound errors it means that required .jar libraries are not included, sometimes they are just missing from the classpath. When running Java Macro's the classpath is made three parts, internal Open Office classpath, the classpath options specified in the Java Virtual Machine selection section (see change JVM above) and parcel-descriptor.xml which accompanies your java macro. Although all the libraries under "OpenOffice Install Folder"/program/classes should be included by default it's often not, to remidy this simply add it as a classpath folder under the JVM selector section (see above). If you want to be able to call a macro from more than just the Run Macro menu you will need to overload the function name specified in percel-descriptor.xml to accept different format, here is an example enabling java macros to be called from A menu or a frame button. This is a modified version of the function in this example:

Orginal Coce: http://user.services.openoffice.org/en/forum/viewtopic.php?f=45&t=1844"
public class Calc {
  // When called from the Macro menu
  public static void fillSpreadsheet(XScriptContext xSc) {
    XModel model = xSc.getDocument();
    fillSheet(getActiveSpreadsheet(model));
  }
  // When called from the Toolbar Menu
   public static void fillSpreadsheet(XScriptContext xSc, Short ignored) {
      fillSpreadsheet(xSc);
   } 
   // When called from a Button or other ActionEvent
   public static void fillSpreadsheet(XScriptContext xSc, com.sun.star.awt.ActionEvent e) {
        fillSpreadsheet(xSc);
    }

I also think it's important to mention how ODF files with java macros are made and how to bundle the two. ODF files are .zip files, you can access/edit them by treating them like any other zip file (.jar files are also zip files btw). In this example there is an extra directory structure in the ODF file for the Java Macro, the structure is as follows where calc is the name of the script and Calc.odf is the name of the document:

Calc.odf
   Scripts
      java
         calc
            calc.jar
            parcel-descriptor.xml

Here is the structure of calc.jar (zip file):

calc.jar
   Calc.class
       META-INF
       MANIFEST.MF

If you want to know the minimum safe JVM version to use you can find it inside the MANIFEST.MF file, typcial contents would be:

Manifest-Version: 1.0
Created-By: 1.6.0_01 (Sun Microsystems Inc.)

Here is the typical content of parcel-descriptor.xml

<parcel language="Java" xmlns:parcel="scripting.dtd">

<script language="Java">

<locale lang="en">

<displayname value="Calc Fill Spreadsheet"/>

<description>

Fill a spreadsheet with some data

</description>

</locale>

<functionname value="Calc.fillSpreadsheet"/>

<logicalname value="Calc.fillSpreadsheet"/>

<languagedepprops>

<prop name="classpath" value="calc.jar"/>

</languagedepprops>

</script>

</parcel>

And the complete calc.java source file:

public class Calc {
  // When called from the Macro menu
  public static void fillSpreadsheet(XScriptContext xSc) {
    XModel model = xSc.getDocument();
    fillSheet(getActiveSpreadsheet(model));
  }
  // When called from the Toolbar Menu
   public static void fillSpreadsheet(XScriptContext xSc, Short ignored) {
      fillSpreadsheet(xSc);
   } 
   // When called from a Button or other ActionEvent
   public static void fillSpreadsheet(XScriptContext xSc, com.sun.star.awt.ActionEvent e) {
        fillSpreadsheet(xSc);
    }
  
  private static XSpreadsheet getActiveSpreadsheet(XModel model) {
    if (model == null)
      return null;

    // Get active spreadsheet
    XController controller = model.getCurrentController();
    XSpreadsheetView view = (XSpreadsheetView) UnoRuntime.queryInterface(XSpreadsheetView.class, controller);
    return (view != null)? view.getActiveSheet(): null;
  }

  private static void fillSheet(XSpreadsheet sheet) {
    if (sheet == null)
      return;

    try {
        //
        // Create a TEXT CELL
        //

        // Get cell by position
        XCell cell = sheet.getCellByPosition(0, 0);

        // Access the cell text
        XText cellText = (XText) UnoRuntime.queryInterface(XText.class, cell);

        // Get the text cursor of the cell text
        XTextCursor textCursor = cellText.createTextCursor();

        // Insert a string in the cell through the text cursor and overwrite the cell content
        // Using 'false' as third parameter adds the inserted string
        cellText.insertString(textCursor, "Java macro example", true);


        //
        // Access and modify VALUE CELLS
        //

        // Get cell by position
        cell = sheet.getCellByPosition(0, 2);

        // Set cell value
        cell.setValue(2345);

        // Get cell value and multiply the value by 4
        double nDblValue = cell.getValue() * 4;

        // Set cell value with result
        sheet.getCellByPosition(0, 3).setValue(nDblValue);


        //
        // Create FORMULA CELLS
        //

        // Get cell by position
        cell = sheet.getCellByPosition(0, 5);

        // Set formula string
        cell.setFormula("=1024/128");

        // Get cell by name
        cell = sheet.getCellByPosition(1, 3);

        // Set formula string
        cell.setFormula("=A3+A4");


        //
        // Fill CELL RANGE
        //

        // Get cell range by name
        XCellRange cellRange = sheet.getCellRangeByName("C3:D4");

        XCellRangeData data = (XCellRangeData) UnoRuntime.queryInterface(XCellRangeData.class, cellRange);
        Object[][] values =
          {
            {new Double(3.3), new Integer(128)},
            {new Double(4.4), new String("Entry")}
          };
        data.setDataArray(values);
    } catch (com.sun.star.lang.IndexOutOfBoundsException e) {
      e.printStackTrace();
    }
  }

  public static void main(String[] args) {
    // This example works with the following arguments:
    // 1) A valid name of an OOo Calc document like "file:///c:/temp/calc.ods"
    // 2) "private:factory/scalc" to create a new OOo Calc document
    // 3) No argument at all to get the current OOo Calc document
    String loadUrl = (args.length > 0)? args[0]: null;
    try {
      XComponentContext context = Bootstrap.bootstrap();
      XMultiComponentFactory multiComponentFactory = context.getServiceManager();
      Object desktopFrame = multiComponentFactory.createInstanceWithContext("com.sun.star.frame.Desktop", context);
      XComponentLoader componentloader = (XComponentLoader) UnoRuntime.queryInterface(XComponentLoader.class,desktopFrame);

      Object component;
      if (loadUrl != null) {
        // Load the document
        try {
          component = componentloader.loadComponentFromURL(loadUrl, "_blank", 0, new PropertyValue[0]);
        } catch (Exception e) {
          component = null;
        }
      } else {
        // Get the current document
        XDesktop desktop = (XDesktop) UnoRuntime.queryInterface(XDesktop.class,desktopFrame);
        component = desktop.getCurrentComponent();
      }

      // Get the active spreadsheet of the componente
      XModel model = (XModel)UnoRuntime.queryInterface(XModel.class, component);
      XSpreadsheet activeSpreadsheet = getActiveSpreadsheet(model);
     
      if(activeSpreadsheet == null) {
        // Either the document couldn't be loaded, the loaded document wasn't
        // a spreadsheet, there wasn't a current document or the current
        // document wasn't a spreadsheet.
        // Whatever the reason has been, we create a new spreadsheet.
        component = componentloader.loadComponentFromURL("private:factory/scalc", "_blank", 0, new PropertyValue[0]);
        model = (XModel) UnoRuntime.queryInterface(XModel.class, component);
        activeSpreadsheet = getActiveSpreadsheet(model);
      }

      fillSheet(activeSpreadsheet);
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      System.exit(0);
    }
  }
}

Keywords for google to direct lost people here: cant find method xscriptcontext com.sun.star.script.provider.XScriptContext com.sun.star.awt.ActionEvent parcel-descriptor.xml" Scripting framework errror java macro java macro button java macro menujava form button vnd.sun.star.script calc.Calc.fillSpreadsheet language=Java

Download Sample ODF file with a Macro Here

To top



Creative Commons License
All content on this site is licensed under a Creative Commons License.