generator: Create a separate type for optional arguments
[libguestfs.git] / generator / generator_java.ml
1 (* libguestfs
2  * Copyright (C) 2009-2011 Red Hat Inc.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  *)
18
19 (* Please read generator/README first. *)
20
21 open Printf
22
23 open Generator_types
24 open Generator_utils
25 open Generator_pr
26 open Generator_docstrings
27 open Generator_optgroups
28 open Generator_actions
29 open Generator_structs
30 open Generator_c
31
32 (* Generate Java bindings GuestFS.java file. *)
33 let rec generate_java_java () =
34   generate_header CStyle LGPLv2plus;
35
36   pr "\
37 package com.redhat.et.libguestfs;
38
39 import java.util.HashMap;
40 import java.util.Map;
41 import com.redhat.et.libguestfs.LibGuestFSException;
42 import com.redhat.et.libguestfs.PV;
43 import com.redhat.et.libguestfs.VG;
44 import com.redhat.et.libguestfs.LV;
45 import com.redhat.et.libguestfs.Stat;
46 import com.redhat.et.libguestfs.StatVFS;
47 import com.redhat.et.libguestfs.IntBool;
48 import com.redhat.et.libguestfs.Dirent;
49
50 /**
51  * The GuestFS object is a libguestfs handle.
52  *
53  * @author rjones
54  */
55 public class GuestFS {
56   // Load the native code.
57   static {
58     System.loadLibrary (\"guestfs_jni\");
59   }
60
61   /**
62    * The native guestfs_h pointer.
63    */
64   long g;
65
66   /**
67    * Create a libguestfs handle.
68    *
69    * @throws LibGuestFSException
70    */
71   public GuestFS () throws LibGuestFSException
72   {
73     g = _create ();
74   }
75   private native long _create () throws LibGuestFSException;
76
77   /**
78    * Close a libguestfs handle.
79    *
80    * You can also leave handles to be collected by the garbage
81    * collector, but this method ensures that the resources used
82    * by the handle are freed up immediately.  If you call any
83    * other methods after closing the handle, you will get an
84    * exception.
85    *
86    * @throws LibGuestFSException
87    */
88   public void close () throws LibGuestFSException
89   {
90     if (g != 0)
91       _close (g);
92     g = 0;
93   }
94   private native void _close (long g) throws LibGuestFSException;
95
96   public void finalize () throws LibGuestFSException
97   {
98     close ();
99   }
100
101 ";
102
103   List.iter (
104     fun (name, (ret, args, optargs as style), _, flags, _, shortdesc, longdesc) ->
105       if not (List.mem NotInDocs flags); then (
106         let doc = replace_str longdesc "C<guestfs_" "C<g." in
107         let doc =
108           if optargs <> [] then
109             doc ^ "\n\nOptional arguments are supplied in the final Map<String,Object> parameter, which is a hash of the argument name to its value (cast to Object).  Pass an empty Map or null for no optional arguments."
110           else doc in
111         let doc =
112           if List.mem ProtocolLimitWarning flags then
113             doc ^ "\n\n" ^ protocol_limit_warning
114           else doc in
115         let doc =
116           match deprecation_notice flags with
117           | None -> doc
118           | Some txt -> doc ^ "\n\n" ^ txt in
119         let doc = pod2text ~width:60 name doc in
120         let doc = List.map (            (* RHBZ#501883 *)
121           function
122           | "" -> "<p>"
123           | nonempty -> nonempty
124         ) doc in
125         let doc = String.concat "\n   * " doc in
126
127         pr "  /**\n";
128         pr "   * %s\n" shortdesc;
129         pr "   * <p>\n";
130         pr "   * %s\n" doc;
131         pr "   * @throws LibGuestFSException\n";
132         pr "   */\n";
133       );
134       pr "  ";
135       generate_java_prototype ~public:true ~semicolon:false name style;
136       pr "\n";
137       pr "  {\n";
138       pr "    if (g == 0)\n";
139       pr "      throw new LibGuestFSException (\"%s: handle is closed\");\n"
140         name;
141       if optargs <> [] then (
142         pr "\n";
143         pr "    /* Unpack optional args. */\n";
144         pr "    Object _optobj;\n";
145         pr "    long _optargs_bitmask = 0;\n";
146         iteri (
147           fun i argt ->
148             let t, boxed_t, convert, n, default =
149               match argt with
150               | OBool n -> "boolean", "Boolean", ".booleanValue()", n, "false"
151               | OInt n -> "int", "Integer", ".intValue()", n, "0"
152               | OInt64 n -> "long", "Long", ".longValue()", n, "0"
153               | OString n -> "String", "String", "", n, "\"\"" in
154             pr "    %s %s = %s;\n" t n default;
155             pr "    _optobj = null;\n";
156             pr "    if (optargs != null)\n";
157             pr "      _optobj = optargs.get (\"%s\");\n" n;
158             pr "    if (_optobj != null) {\n";
159             pr "      %s = ((%s) _optobj)%s;\n" n boxed_t convert;
160             pr "      _optargs_bitmask |= %Ld;\n"
161               (Int64.shift_left Int64.one i);
162             pr "    }\n";
163         ) optargs
164       );
165       pr "\n";
166       (match ret with
167        | RErr ->
168            pr "    _%s " name;
169            generate_java_call_args ~handle:"g" style;
170            pr ";\n"
171        | RHashtable _ ->
172            pr "    String[] r = _%s " name;
173            generate_java_call_args ~handle:"g" style;
174            pr ";\n";
175            pr "\n";
176            pr "    HashMap rhash = new HashMap ();\n";
177            pr "    for (int i = 0; i < r.length; i += 2)\n";
178            pr "      rhash.put (r[i], r[i+1]);\n";
179            pr "    return rhash;\n"
180        | _ ->
181            pr "    return _%s " name;
182            generate_java_call_args ~handle:"g" style;
183            pr ";\n"
184       );
185       pr "  }\n";
186       pr "\n";
187       pr "  ";
188       generate_java_prototype ~privat:true ~native:true name style;
189       pr "\n";
190       pr "\n";
191   ) all_functions;
192
193   pr "}\n"
194
195 (* Generate Java call arguments, eg "(handle, foo, bar)" *)
196 and generate_java_call_args ~handle (_, args, optargs) =
197   pr "(%s" handle;
198   List.iter (fun arg -> pr ", %s" (name_of_argt arg)) args;
199   if optargs <> [] then (
200     pr ", _optargs_bitmask";
201     List.iter (fun arg -> pr ", %s" (name_of_optargt arg)) optargs
202   );
203   pr ")"
204
205 and generate_java_prototype ?(public=false) ?(privat=false) ?(native=false)
206     ?(semicolon=true) name (ret, args, optargs) =
207   if privat then pr "private ";
208   if public then pr "public ";
209   if native then pr "native ";
210
211   (* return type *)
212   (match ret with
213    | RErr -> pr "void ";
214    | RInt _ -> pr "int ";
215    | RInt64 _ -> pr "long ";
216    | RBool _ -> pr "boolean ";
217    | RConstString _ | RConstOptString _ | RString _
218    | RBufferOut _ -> pr "String ";
219    | RStringList _ -> pr "String[] ";
220    | RStruct (_, typ) ->
221        let name = camel_name_of_struct typ in
222        pr "%s " name;
223    | RStructList (_, typ) ->
224        let name = camel_name_of_struct typ in
225        pr "%s[] " name;
226    | RHashtable _ ->
227        if not native then
228          pr "Map<String,String> "
229        else
230          pr "String[] ";
231   );
232
233   if native then pr "_%s " name else pr "%s " name;
234   pr "(";
235   let needs_comma = ref false in
236   if native then (
237     pr "long g";
238     needs_comma := true
239   );
240
241   (* args *)
242   List.iter (
243     fun arg ->
244       if !needs_comma then pr ", ";
245       needs_comma := true;
246
247       match arg with
248       | Pathname n
249       | Device n | Dev_or_Path n
250       | String n
251       | OptString n
252       | FileIn n
253       | FileOut n
254       | Key n ->
255           pr "String %s" n
256       | BufferIn n ->
257           pr "byte[] %s" n
258       | StringList n | DeviceList n ->
259           pr "String[] %s" n
260       | Bool n ->
261           pr "boolean %s" n
262       | Int n ->
263           pr "int %s" n
264       | Int64 n | Pointer (_, n) ->
265           pr "long %s" n
266   ) args;
267
268   if optargs <> [] then (
269     if !needs_comma then pr ", ";
270     needs_comma := true;
271
272     if not native then
273       pr "Map<String, Object> optargs"
274     else (
275       pr "long _optargs_bitmask";
276       List.iter (
277         fun argt ->
278           match argt with
279           | OBool n -> pr ", boolean %s" n
280           | OInt n -> pr ", int %s" n
281           | OInt64 n -> pr ", long %s" n
282           | OString n -> pr ", String %s" n
283       ) optargs
284     )
285   );
286
287   pr ")\n";
288   pr "    throws LibGuestFSException";
289   if semicolon then pr ";"
290
291 and generate_java_struct jtyp cols () =
292   generate_header CStyle LGPLv2plus;
293
294   pr "\
295 package com.redhat.et.libguestfs;
296
297 /**
298  * Libguestfs %s structure.
299  *
300  * @author rjones
301  * @see GuestFS
302  */
303 public class %s {
304 " jtyp jtyp;
305
306   List.iter (
307     function
308     | name, FString
309     | name, FUUID
310     | name, FBuffer -> pr "  public String %s;\n" name
311     | name, (FBytes|FUInt64|FInt64) -> pr "  public long %s;\n" name
312     | name, (FUInt32|FInt32) -> pr "  public int %s;\n" name
313     | name, FChar -> pr "  public char %s;\n" name
314     | name, FOptPercent ->
315         pr "  /* The next field is [0..100] or -1 meaning 'not present': */\n";
316         pr "  public float %s;\n" name
317   ) cols;
318
319   pr "}\n"
320
321 and generate_java_c () =
322   generate_header CStyle LGPLv2plus;
323
324   pr "\
325 #include <stdio.h>
326 #include <stdlib.h>
327 #include <string.h>
328
329 #include \"com_redhat_et_libguestfs_GuestFS.h\"
330 #include \"guestfs.h\"
331
332 /* Note that this function returns.  The exception is not thrown
333  * until after the wrapper function returns.
334  */
335 static void
336 throw_exception (JNIEnv *env, const char *msg)
337 {
338   jclass cl;
339   cl = (*env)->FindClass (env,
340                           \"com/redhat/et/libguestfs/LibGuestFSException\");
341   (*env)->ThrowNew (env, cl, msg);
342 }
343
344 JNIEXPORT jlong JNICALL
345 Java_com_redhat_et_libguestfs_GuestFS__1create
346   (JNIEnv *env, jobject obj)
347 {
348   guestfs_h *g;
349
350   g = guestfs_create ();
351   if (g == NULL) {
352     throw_exception (env, \"GuestFS.create: failed to allocate handle\");
353     return 0;
354   }
355   guestfs_set_error_handler (g, NULL, NULL);
356   return (jlong) (long) g;
357 }
358
359 JNIEXPORT void JNICALL
360 Java_com_redhat_et_libguestfs_GuestFS__1close
361   (JNIEnv *env, jobject obj, jlong jg)
362 {
363   guestfs_h *g = (guestfs_h *) (long) jg;
364   guestfs_close (g);
365 }
366
367 ";
368
369   List.iter (
370     fun (name, (ret, args, optargs as style), _, _, _, _, _) ->
371       pr "JNIEXPORT ";
372       (match ret with
373        | RErr -> pr "void ";
374        | RInt _ -> pr "jint ";
375        | RInt64 _ -> pr "jlong ";
376        | RBool _ -> pr "jboolean ";
377        | RConstString _ | RConstOptString _ | RString _
378        | RBufferOut _ -> pr "jstring ";
379        | RStruct _ | RHashtable _ ->
380            pr "jobject ";
381        | RStringList _ | RStructList _ ->
382            pr "jobjectArray ";
383       );
384       pr "JNICALL\n";
385       pr "Java_com_redhat_et_libguestfs_GuestFS_";
386       pr "%s" (replace_str ("_" ^ name) "_" "_1");
387       pr "  (JNIEnv *env, jobject obj, jlong jg";
388       List.iter (
389         function
390         | Pathname n
391         | Device n | Dev_or_Path n
392         | String n
393         | OptString n
394         | FileIn n
395         | FileOut n
396         | Key n ->
397             pr ", jstring j%s" n
398         | BufferIn n ->
399             pr ", jbyteArray j%s" n
400         | StringList n | DeviceList n ->
401             pr ", jobjectArray j%s" n
402         | Bool n ->
403             pr ", jboolean j%s" n
404         | Int n ->
405             pr ", jint j%s" n
406         | Int64 n | Pointer (_, n) ->
407             pr ", jlong j%s" n
408       ) args;
409       if optargs <> [] then (
410         pr ", jlong joptargs_bitmask";
411         List.iter (
412           function
413           | OBool n -> pr ", jboolean j%s" n
414           | OInt n -> pr ", jint j%s" n
415           | OInt64 n -> pr ", jlong j%s" n
416           | OString n -> pr ", jstring j%s" n
417         ) optargs
418       );
419       pr ")\n";
420       pr "{\n";
421       pr "  guestfs_h *g = (guestfs_h *) (long) jg;\n";
422       (match ret with
423        | RErr -> pr "  int r;\n"
424        | RBool _
425        | RInt _ -> pr "  int r;\n"
426        | RInt64 _ -> pr "  int64_t r;\n"
427        | RConstString _ -> pr "  const char *r;\n"
428        | RConstOptString _ -> pr "  const char *r;\n"
429        | RString _ ->
430            pr "  jstring jr;\n";
431            pr "  char *r;\n"
432        | RStringList _
433        | RHashtable _ ->
434            pr "  jobjectArray jr;\n";
435            pr "  size_t r_len;\n";
436            pr "  jclass cl;\n";
437            pr "  jstring jstr;\n";
438            pr "  char **r;\n"
439        | RStruct (_, typ) ->
440            pr "  jobject jr;\n";
441            pr "  jclass cl;\n";
442            pr "  jfieldID fl;\n";
443            pr "  struct guestfs_%s *r;\n" typ
444        | RStructList (_, typ) ->
445            pr "  jobjectArray jr;\n";
446            pr "  jclass cl;\n";
447            pr "  jfieldID fl;\n";
448            pr "  jobject jfl;\n";
449            pr "  struct guestfs_%s_list *r;\n" typ
450        | RBufferOut _ ->
451            pr "  jstring jr;\n";
452            pr "  char *r;\n";
453            pr "  size_t size;\n"
454       );
455
456       List.iter (
457         function
458         | Pathname n
459         | Device n | Dev_or_Path n
460         | String n
461         | OptString n
462         | FileIn n
463         | FileOut n
464         | Key n ->
465             pr "  const char *%s;\n" n
466         | BufferIn n ->
467             pr "  char *%s;\n" n;
468             pr "  size_t %s_size;\n" n
469         | StringList n | DeviceList n ->
470             pr "  size_t %s_len;\n" n;
471             pr "  char **%s;\n" n
472         | Bool n
473         | Int n ->
474             pr "  int %s;\n" n
475         | Int64 n ->
476             pr "  int64_t %s;\n" n
477         | Pointer (t, n) ->
478             pr "  %s %s;\n" t n
479       ) args;
480
481       if optargs <> [] then (
482         pr "  struct guestfs_%s_argv optargs_s;\n" name;
483         pr "  const struct guestfs_%s_argv *optargs = &optargs_s;\n" name
484       );
485
486       let needs_i =
487         (match ret with
488          | RStringList _ | RStructList _ | RHashtable _ -> true
489          | RErr | RBool _ | RInt _ | RInt64 _ | RConstString _
490          | RConstOptString _
491          | RString _ | RBufferOut _ | RStruct _ -> false) ||
492           List.exists (function
493                        | StringList _ -> true
494                        | DeviceList _ -> true
495                        | _ -> false) args in
496       if needs_i then
497         pr "  size_t i;\n";
498
499       pr "\n";
500
501       (* Get the parameters. *)
502       List.iter (
503         function
504         | Pathname n
505         | Device n | Dev_or_Path n
506         | String n
507         | FileIn n
508         | FileOut n
509         | Key n ->
510             pr "  %s = (*env)->GetStringUTFChars (env, j%s, NULL);\n" n n
511         | OptString n ->
512             (* This is completely undocumented, but Java null becomes
513              * a NULL parameter.
514              *)
515             pr "  %s = j%s ? (*env)->GetStringUTFChars (env, j%s, NULL) : NULL;\n" n n n
516         | BufferIn n ->
517             pr "  %s = (char *) (*env)->GetByteArrayElements (env, j%s, NULL);\n" n n;
518             pr "  %s_size = (*env)->GetArrayLength (env, j%s);\n" n n
519         | StringList n | DeviceList n ->
520             pr "  %s_len = (*env)->GetArrayLength (env, j%s);\n" n n;
521             pr "  %s = guestfs_safe_malloc (g, sizeof (char *) * (%s_len+1));\n" n n;
522             pr "  for (i = 0; i < %s_len; ++i) {\n" n;
523             pr "    jobject o = (*env)->GetObjectArrayElement (env, j%s, i);\n"
524               n;
525             pr "    %s[i] = (char *) (*env)->GetStringUTFChars (env, o, NULL);\n" n;
526             pr "  }\n";
527             pr "  %s[%s_len] = NULL;\n" n n;
528         | Bool n
529         | Int n
530         | Int64 n ->
531             pr "  %s = j%s;\n" n n
532         | Pointer (t, n) ->
533             pr "  %s = (%s) j%s;\n" n t n
534       ) args;
535
536       if optargs <> [] then (
537         pr "  optargs_s.bitmask = joptargs_bitmask;\n";
538         List.iter (
539           function
540           | OBool n | OInt n | OInt64 n ->
541               pr "  optargs_s.%s = j%s;\n" n n
542           | OString n ->
543               pr "  optargs_s.%s = (*env)->GetStringUTFChars (env, j%s, NULL);\n"
544                 n n
545         ) optargs;
546       );
547
548       pr "\n";
549
550       (* Make the call. *)
551       if optargs = [] then
552         pr "  r = guestfs_%s " name
553       else
554         pr "  r = guestfs_%s_argv " name;
555       generate_c_call_args ~handle:"g" style;
556       pr ";\n";
557
558       pr "\n";
559
560       (* Release the parameters. *)
561       List.iter (
562         function
563         | Pathname n
564         | Device n | Dev_or_Path n
565         | String n
566         | FileIn n
567         | FileOut n
568         | Key n ->
569             pr "  (*env)->ReleaseStringUTFChars (env, j%s, %s);\n" n n
570         | OptString n ->
571             pr "  if (j%s)\n" n;
572             pr "    (*env)->ReleaseStringUTFChars (env, j%s, %s);\n" n n
573         | BufferIn n ->
574             pr "  (*env)->ReleaseByteArrayElements (env, j%s, (jbyte *) %s, 0);\n" n n
575         | StringList n | DeviceList n ->
576             pr "  for (i = 0; i < %s_len; ++i) {\n" n;
577             pr "    jobject o = (*env)->GetObjectArrayElement (env, j%s, i);\n"
578               n;
579             pr "    (*env)->ReleaseStringUTFChars (env, o, %s[i]);\n" n;
580             pr "  }\n";
581             pr "  free (%s);\n" n
582         | Bool _
583         | Int _
584         | Int64 _
585         | Pointer _ -> ()
586       ) args;
587
588       List.iter (
589         function
590         | OBool n | OInt n | OInt64 n -> ()
591         | OString n ->
592             pr "  (*env)->ReleaseStringUTFChars (env, j%s, optargs_s.%s);\n" n n
593       ) optargs;
594
595       pr "\n";
596
597       (* Check for errors. *)
598       (match errcode_of_ret ret with
599        | `CannotReturnError -> ()
600        | (`ErrorIsMinusOne|`ErrorIsNULL) as errcode ->
601            (match errcode with
602             | `ErrorIsMinusOne ->
603                 pr "  if (r == -1) {\n";
604             | `ErrorIsNULL ->
605                 pr "  if (r == NULL) {\n";
606            );
607            pr "    throw_exception (env, guestfs_last_error (g));\n";
608            (match ret with
609             | RErr ->
610                 pr "    return;\n"
611             | RInt _
612             | RInt64 _
613             | RBool _ ->
614                 pr "    return -1;\n"
615             | RConstString _ | RConstOptString _ | RString _
616             | RBufferOut _
617             | RStruct _ | RHashtable _
618             | RStringList _ | RStructList _ ->
619                 pr "    return NULL;\n"
620            );
621            pr "  }\n"
622       );
623
624       (* Return value. *)
625       (match ret with
626        | RErr -> ()
627        | RInt _ -> pr "  return (jint) r;\n"
628        | RBool _ -> pr "  return (jboolean) r;\n"
629        | RInt64 _ -> pr "  return (jlong) r;\n"
630        | RConstString _ -> pr "  return (*env)->NewStringUTF (env, r);\n"
631        | RConstOptString _ ->
632            pr "  return (*env)->NewStringUTF (env, r); /* XXX r NULL? */\n"
633        | RString _ ->
634            pr "  jr = (*env)->NewStringUTF (env, r);\n";
635            pr "  free (r);\n";
636            pr "  return jr;\n"
637        | RStringList _
638        | RHashtable _ ->
639            pr "  for (r_len = 0; r[r_len] != NULL; ++r_len) ;\n";
640            pr "  cl = (*env)->FindClass (env, \"java/lang/String\");\n";
641            pr "  jstr = (*env)->NewStringUTF (env, \"\");\n";
642            pr "  jr = (*env)->NewObjectArray (env, r_len, cl, jstr);\n";
643            pr "  for (i = 0; i < r_len; ++i) {\n";
644            pr "    jstr = (*env)->NewStringUTF (env, r[i]);\n";
645            pr "    (*env)->SetObjectArrayElement (env, jr, i, jstr);\n";
646            pr "    free (r[i]);\n";
647            pr "  }\n";
648            pr "  free (r);\n";
649            pr "  return jr;\n"
650        | RStruct (_, typ) ->
651            let jtyp = camel_name_of_struct typ in
652            let cols = cols_of_struct typ in
653            generate_java_struct_return typ jtyp cols
654        | RStructList (_, typ) ->
655            let jtyp = camel_name_of_struct typ in
656            let cols = cols_of_struct typ in
657            generate_java_struct_list_return typ jtyp cols
658        | RBufferOut _ ->
659            pr "  jr = (*env)->NewStringUTF (env, r); // XXX size\n";
660            pr "  free (r);\n";
661            pr "  return jr;\n"
662       );
663
664       pr "}\n";
665       pr "\n"
666   ) all_functions
667
668 and generate_java_struct_return typ jtyp cols =
669   pr "  cl = (*env)->FindClass (env, \"com/redhat/et/libguestfs/%s\");\n" jtyp;
670   pr "  jr = (*env)->AllocObject (env, cl);\n";
671   List.iter (
672     function
673     | name, FString ->
674         pr "  fl = (*env)->GetFieldID (env, cl, \"%s\", \"Ljava/lang/String;\");\n" name;
675         pr "  (*env)->SetObjectField (env, jr, fl, (*env)->NewStringUTF (env, r->%s));\n" name;
676     | name, FUUID ->
677         pr "  {\n";
678         pr "    char s[33];\n";
679         pr "    memcpy (s, r->%s, 32);\n" name;
680         pr "    s[32] = 0;\n";
681         pr "    fl = (*env)->GetFieldID (env, cl, \"%s\", \"Ljava/lang/String;\");\n" name;
682         pr "    (*env)->SetObjectField (env, jr, fl, (*env)->NewStringUTF (env, s));\n";
683         pr "  }\n";
684     | name, FBuffer ->
685         pr "  {\n";
686         pr "    int len = r->%s_len;\n" name;
687         pr "    char s[len+1];\n";
688         pr "    memcpy (s, r->%s, len);\n" name;
689         pr "    s[len] = 0;\n";
690         pr "    fl = (*env)->GetFieldID (env, cl, \"%s\", \"Ljava/lang/String;\");\n" name;
691         pr "    (*env)->SetObjectField (env, jr, fl, (*env)->NewStringUTF (env, s));\n";
692         pr "  }\n";
693     | name, (FBytes|FUInt64|FInt64) ->
694         pr "  fl = (*env)->GetFieldID (env, cl, \"%s\", \"J\");\n" name;
695         pr "  (*env)->SetLongField (env, jr, fl, r->%s);\n" name;
696     | name, (FUInt32|FInt32) ->
697         pr "  fl = (*env)->GetFieldID (env, cl, \"%s\", \"I\");\n" name;
698         pr "  (*env)->SetLongField (env, jr, fl, r->%s);\n" name;
699     | name, FOptPercent ->
700         pr "  fl = (*env)->GetFieldID (env, cl, \"%s\", \"F\");\n" name;
701         pr "  (*env)->SetFloatField (env, jr, fl, r->%s);\n" name;
702     | name, FChar ->
703         pr "  fl = (*env)->GetFieldID (env, cl, \"%s\", \"C\");\n" name;
704         pr "  (*env)->SetLongField (env, jr, fl, r->%s);\n" name;
705   ) cols;
706   pr "  free (r);\n";
707   pr "  return jr;\n"
708
709 and generate_java_struct_list_return typ jtyp cols =
710   pr "  cl = (*env)->FindClass (env, \"com/redhat/et/libguestfs/%s\");\n" jtyp;
711   pr "  jr = (*env)->NewObjectArray (env, r->len, cl, NULL);\n";
712   pr "  for (i = 0; i < r->len; ++i) {\n";
713   pr "    jfl = (*env)->AllocObject (env, cl);\n";
714   List.iter (
715     function
716     | name, FString ->
717         pr "    fl = (*env)->GetFieldID (env, cl, \"%s\", \"Ljava/lang/String;\");\n" name;
718         pr "    (*env)->SetObjectField (env, jfl, fl, (*env)->NewStringUTF (env, r->val[i].%s));\n" name;
719     | name, FUUID ->
720         pr "    {\n";
721         pr "      char s[33];\n";
722         pr "      memcpy (s, r->val[i].%s, 32);\n" name;
723         pr "      s[32] = 0;\n";
724         pr "      fl = (*env)->GetFieldID (env, cl, \"%s\", \"Ljava/lang/String;\");\n" name;
725         pr "      (*env)->SetObjectField (env, jfl, fl, (*env)->NewStringUTF (env, s));\n";
726         pr "    }\n";
727     | name, FBuffer ->
728         pr "    {\n";
729         pr "      int len = r->val[i].%s_len;\n" name;
730         pr "      char s[len+1];\n";
731         pr "      memcpy (s, r->val[i].%s, len);\n" name;
732         pr "      s[len] = 0;\n";
733         pr "      fl = (*env)->GetFieldID (env, cl, \"%s\", \"Ljava/lang/String;\");\n" name;
734         pr "      (*env)->SetObjectField (env, jfl, fl, (*env)->NewStringUTF (env, s));\n";
735         pr "    }\n";
736     | name, (FBytes|FUInt64|FInt64) ->
737         pr "    fl = (*env)->GetFieldID (env, cl, \"%s\", \"J\");\n" name;
738         pr "    (*env)->SetLongField (env, jfl, fl, r->val[i].%s);\n" name;
739     | name, (FUInt32|FInt32) ->
740         pr "    fl = (*env)->GetFieldID (env, cl, \"%s\", \"I\");\n" name;
741         pr "    (*env)->SetLongField (env, jfl, fl, r->val[i].%s);\n" name;
742     | name, FOptPercent ->
743         pr "    fl = (*env)->GetFieldID (env, cl, \"%s\", \"F\");\n" name;
744         pr "    (*env)->SetFloatField (env, jfl, fl, r->val[i].%s);\n" name;
745     | name, FChar ->
746         pr "    fl = (*env)->GetFieldID (env, cl, \"%s\", \"C\");\n" name;
747         pr "    (*env)->SetLongField (env, jfl, fl, r->val[i].%s);\n" name;
748   ) cols;
749   pr "    (*env)->SetObjectArrayElement (env, jfl, i, jfl);\n";
750   pr "  }\n";
751   pr "  guestfs_free_%s_list (r);\n" typ;
752   pr "  return jr;\n"
753
754 and generate_java_makefile_inc () =
755   generate_header HashStyle GPLv2plus;
756
757   pr "java_built_sources = \\\n";
758   List.iter (
759     fun (typ, jtyp) ->
760         pr "\tcom/redhat/et/libguestfs/%s.java \\\n" jtyp;
761   ) camel_structs;
762   pr "\tcom/redhat/et/libguestfs/GuestFS.java\n"