Next: 4.8 Useful Make Options Up: 4. Wrapping Objective-C Libraries Previous: 4.6 The wrapper directory

Subsections


4.7 The .jigs File

4.7.1 Determining which .jigs file to read

After running the preprocessor on the header file, JIGS will read a configuration file to know which wrappers to generate, and how. By default, the file is called $(JAVA_WRAPPER_NAME).jigs. In the case described before, this file would be called Game.jigs. You can override this choice by setting the JIGS_FILE variable in your GNUmakefile, but you shouldn't usually need to.

4.7.2 Overview of the .jigs file

The .jigs file is the main file from which you can control the way the wrapping is done. In this file, you need to list all the classes of your library which you want to expose, and for each class, which methods. The .jigs file is in a format called property list - please refer to the GNUstep Base Library tutorials and documentation if you need more help on property lists. The .jigs file is a big dictionary. Each entry in the dictionary is to be thought of as of a different section; the key to the section is the section name, the value is an array or a dictionary containing the information referring to that section. For example, we have the classes section (containing an array of classes to be wrapped), the types section (containing a dictionary of mapping of custom C types to already known C types), the method name mapping section (containing a dictionary of mappings from objective-C method names to Java method names, to be used for the whole library). Here is an example:
{
  classes = (
    {
      "java name" = "gnu.gnustep.GreatGameLibrary.GGLSprite";
      "objective-c name" = GGLSprite;
      "instance methods" = (speed, "setSpeed:");
      initializers = (init);
     }
   );
  
  "method name mapping" = {
    speed = getSpeed;
  };
}

In this example, we only have the classes and method name mapping sections. We now examine in detail each section of the .jigs file.

4.7.3 The Classes section

Probably the most important section in the file is the classes section, where you list all the classes you want to be wrapped, and how. This section is an array, each entry in the array being a different class to be wrapped. Here is an example of this section:
classes = (
  {
    "java name" = "gnu.gnustep.GreatGameLibrary.GGLScore";
    "objective-c name" = "GGLScore";

    "initializers" = ("init"); 
    "instance methods" = ("setPoints:", "points");
    "class methods" = ("bestScore", "bestTenScores");
  },

  {
    "java name" = "gnu.gnustep.GreatGameLibrary.GGLPlayer";
    "objective-c name" = "GGLPlayer";

    "class methods" = ("computerPlayer");
    "instance methods" = ("name", "score", "addPoints:");
    "initializers" = ("init", "initWithName:");                 
  }
);
In this example, we want to generate wrappers for two classes: GGLScore and GGLPlayer. Information about each class is represented by a dictionary. Any entry in this dictionary (except the `java name' entry) can be omitted, in which case a default value will be used. We briefly now review the use of each entry.

4.7.3.1 java name

This is a string - the name that is to be used in Java for the class you want to wrap. This is the only compulsory entry for each class. You need to specify the full Java name of the class, such as
"java name" = "gnu.gnustep.base.NSArray";
You can decide to use a different name in Java than the name the class has in Objective-C. It's generally better to avoid this, but it should work fine if you ever need to do it.

4.7.3.2 objective-c name

This is a string - the Objective-C name of the class you want to wrap, such as
"objective-c name" = "NSArray";
If this entry is not present, it is guessed by taking the last part of java name. For example, if java name is gnu.gnustep.base.NSArray, then JIGS will use NSArray as Objective-C name if you don't specify an Objective-C name.

4.7.3.3 initializers

This is an array - the list of init methods you want to wrap, such as
"initializers" = ("init", "initWithName:", "initWithArray:", "initWithName:array:");
Each method in the initializers list will be exposed as a constructor. The Objective-C name of each initializer is discarded in Java, so you must make sure that these methods have all different signatures.

4.7.3.4 class methods

This is an array - the list of class methods you want to wrap, such as
"class methods" = (
  "contentRectForFrameRect:styleMask:", 
  "frameRectForContentRect:styleMask:", 
  "minFrameWidthWithTitle:styleMask:",
  "removeFrameUsingName:");
Each of these methods is exposed as a static method in Java. Unless you use a method mapping (explained below), the Java name of each of these methods will be obtained by removing from the Objective-C name everything which comes after the first `:'. For example, the methods in the example would be wrapped as:
public static native NSRect contentRectForFrameRect (NSRect arg0, long arg1);
public static native NSRect frameRectForContentRect (NSRect arg0, long arg1);
public static native NSRect minFrameWidthWithTitle (String arg0, long arg1);
public static native void removeFrameUsingName (String arg0);

Please make sure you do not include any methods of the new family in the class methods list. new methods can not be wrapped because they allocate and return non-autoreleased objects - it should not be a problem anyway, because if you need that constructor in Java, you can get it by simply wrapping the corresponding init method.

4.7.3.5 instance methods

This is an array - the list of instance methods you want to wrap, such as
"instance methods" = (
  "arguments",
  "environment",
  "hostName",
  "processName",
  "globallyUniqueString",
  "setProcessName:");
Each of these methods is exposed as an instance method in Java. The Java names for these methods are obtained in the same way as for class methods.

Make sure you do not include any method of the copy and mutableCopy family in this list. Methods of these families can not be wrapped like the other ones because they return newly-created non-autoreleased objects. It is possible that in the future JIGS could provide a way to expose these methods; at present, you can not expose them - except the ones exposed by NSObject: NSObject exposes copy (as clone) and mutableCopy (as mutableClone) to Java, so if your Objective-C class implements copy and mutableCopy, they will automatically be made available to Java by the NSObject wrapper without any need to do anything.

4.7.3.6 method declarations

This is an array - a list of explicit method declarations which you can use to override the declarations found by the wrapper tool in the header file if you think the wrapper tool did not use the correct declaration - this usually happens only if in your library the same method is declared in different classes or protocols with different arguments or return types (this case is infrequent, but it happens sometime) - in this case the wrapper tool is likely not to pick up the correct declaration, because it is designed for the standard case, when the same signature is used across all classes. By adding the correct declaration for your class in the method declaration section, you can make sure that the declaration you want is used. For example, in the gui library the following explicit declaration is used when wrapping NSBox:
"method declarations" = (
   "- (void) setContentView: (NSView*)aView");
This is because NSScrollView uses a conflicting declaration for this method, taking a NSClipView * argument.

Please note that, while in Objective-C these problems are harmless, and usually the worst it can happen is that the compiler warns you at compile time, after you expose your methods to Java these problems become first-rate problems, because in Java casts are dramatic and your program can crash if a method expecting a NSClipView argument receives a generic NSView instead.

4.7.3.7 hardcoded constants

This is an array - a list of declaration of constants which will be added as static constants to the Java class, as in the following example:
"hardcoded constants" = ("int NotFound = 0x7fffffff");
This hardcoded constants section will generate the following code in the Java class:
public static int NotFound = 0x7fffffff;
This example is taken from the wrapping instructions for NSArray; it allows the Java programmer to access this constant simply as NSArray.NotFound. The value of this constant is of course the value of the C constant NSNotFound which is returned by many GNUstep Base Library methods when something is not found.

If your Objective-C library defines some constants, and some methods which you expose to Java return or accept these constants, you may want to make these constants available to Java as well by using the hardcoded constants section. Enumerations are exposed using the enumerations section, as explained in the next paragraphs.

4.7.3.8 enumerations

Starting from JIGS 1.5.0, enumeration types are automatically recognized in the header file, and treated by the wrapper tool exactly as if they were simple ints. Usually, that is not enough - you want also to have the enumeration constants available from Java as static integer constants of some class. You can use the hardcoded constants section to manually add them to the class; this is quite clumsy, because you have to enter all the enumeration constants manually in the jigs file, and if you change the enumeration values in the Objective-C code, you have to manually update the jigs file.

Recent versions of JIGS (1.5.0 and newer) offer a new better option, in the form of the enumerations section. For example, the following line inside a class section

enumerations = (NSComparisonResult);
will cause the following code to be automatically added to the generated Java class code:
 
public static int NSOrderedDescending = 1;
public static int NSOrderedSame = 0;
public static int NSOrderedAscending = -1;

Please notice that the wrapper tool has parsed your Objective-C header file, and extracted the constant values from there automatically! This means that you don't have to enter the constants in the first place, and if you ever change the enumeration, the Java wrappers will be immediately and automatically updated as soon as you recreate them.

4.7.3.9 file to include in java code

This is a string - a filename (relative to the current directory). When WrapCreator generates the Java class, it will read the contents of this file, and add them - without any change - inside the Java class.

For example, to insert a Java method of your own into the generated Java wrapper class, you could use the following entry:

"file to include in java code" = "MyClass.java.add";
Then, you need to put the Java code you want to add into the file MyClass.java.add. Here is a trivial example:
  /**
    * Returns the sum of the arguments.
    */
  public int test (int a, int b)
  {
    return a + b;
  }
This code would be inserted inside the Java class implementation when the Java wrapper is created.

4.7.3.10 file to include in preamble java code

This is a string - a filename (relative to the current directory). The only difference with file to include in java code (described above) is that in this case the code is included before the Java class, between the import statements and the class implementation, rather than inside the class implementation.

4.7.3.11 file to include in objective-c code

This is a string - a filename (relative to the current directory). When WrapCreator generates the Objective-C code, it will read the contents of this file, and add them - without any change - at the end of the Objective-C file for that class. In this way, you can add arbitrary functions and C or Objective-C code to the wrapper library.

The combination of this feature with the one to include arbitrary java code into the wrapper allows you to add hand-made wrapped methods to the java wrapper. This is a very advanced feature and is not documented here.

4.7.4 Method Name Mapping

This section is a dictionary - mapping Objective-C method names to Java method names, as in the following example:
"method name mapping" = {
  "null" = "nullValue";
  "addTimer:forMode:" = "addTimerForMode";
};
Try to avoid method mappings, because they are confusing! But sometimes you can't do without, as in the case of null which can't be exposed with this name to Java because null is a reserved keyword in Java. In this case, you use the method mapping. Please note that JIGS needs to keep a global table of method name mappings while running in order to map selectors between Java and Objective-C; for this reason, you need to use the same mappings in all the classes.

4.7.5 Prerequisite Libraries

Sometimes, the wrappers for your library need the wrappers of some other GNUstep Objective-C library to work correctly. For example, you can't use the wrappers for the GNUstep Gui Library without the wrappers for the GNUstep Base Library. If this is the case for your library, you need to add a prerequisite libraries section, containing a list of GNUstep libraries whose wrappers you want to be loaded at run-time when the wrappers for your library are initialized. For example, in gnustep-gui.jigs there is
"prerequisite libraries" = ("gnustep-base");
You will probably usually need at least this line. Please note that the library is specified using the simplest possible name; JIGS at run-time will add the correct prefixes (lib) and suffixes (things like _d for debug version - if needed - and .A for the wrappers) to load in the library.

4.7.6 Types

The wrapper tool can parse and manage a certain set of C types. For example, upon finding BOOL in the Objective-C headers, the wrapper tool knows how to manage it.

In the types section you can specify a list of C types specific to your library, and for each of them, a corresponding (known) C type. The wrapper tool will know it has to convert to/from Java the first C type in the same way as it does with the second. For example, in the gnustep-base.jigs file, there is the following line:

types = { NSTimeInterval = double; };
This simply tells to the wrapper tool that whenever it finds an argument of type NSTimeInterval in an Objective-C method declaration, it has to treat it in the same way as if it were a double.

Starting from JIGS 1.5.0, the wrapper tool recognizes enumerations when it parses your header file, and it automatically treats them as new types corresponding to int. This means that you don't need to do anything special to have JIGS wrap methods taking or returning enumerations - enumerations should be recognized automatically, and treated automatically as ints.


Next: 4.8 Useful Make Options Up: 4. Wrapping Objective-C Libraries Previous: 4.6 The wrapper directory
Nicola Pero 2001-07-24