1 /* Writing Java ResourceBundles.
2 Copyright (C) 2001-2003, 2005 Free Software Foundation, Inc.
3 Written by Bruno Haible <haible@clisp.cons.org>, 2001.
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2, or (at your option)
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software Foundation,
17 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
25 #include "write-java.h"
35 #if STAT_MACROS_BROKEN
38 #if !defined S_ISDIR && defined S_IFDIR
39 # define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
41 #if !S_IRUSR && S_IREAD
42 # define S_IRUSR S_IREAD
45 # define S_IRUSR 00400
47 #if !S_IWUSR && S_IWRITE
48 # define S_IWUSR S_IWRITE
51 # define S_IWUSR 00200
53 #if !S_IXUSR && S_IEXEC
54 # define S_IXUSR S_IEXEC
57 # define S_IXUSR 00100
65 /* mingw's mkdir() function has 1 argument, but we pass 2 arguments.
66 Therefore we have to disable the argument count checking. */
67 # define mkdir ((int (*)()) mkdir)
76 #include "msgl-iconv.h"
78 #include "plural-exp.h"
79 #include "po-charset.h"
83 #include "fatal-signal.h"
84 #include "fwriteerror.h"
86 #include "utf8-ucs4.h"
89 #define _(str) gettext (str)
92 /* Check that the resource name is a valid Java class name. To simplify
93 things, we allow only ASCII characters in the class name.
94 Return the number of dots in the class name, or -1 if not OK. */
96 check_resource_name (const char *name
)
103 /* First character, see Character.isJavaIdentifierStart. */
104 if (!(c_isalpha (*p
) || (*p
== '$') || (*p
== '_')))
106 /* Following characters, see Character.isJavaIdentifierPart. */
109 while (c_isalpha (*p
) || (*p
== '$') || (*p
== '_') || c_isdigit (*p
));
121 /* Return the Java hash code of a string mod 2^31.
122 The Java String.hashCode() function returns the same values across
123 Java implementations.
124 (See http://www.javasoft.com/docs/books/jls/clarify.html)
125 It returns a signed 32-bit integer. We add a mod 2^31 afterwards;
126 this removes one bit but greatly simplifies the following "mod hash_size"
127 and "mod (hash_size - 2)" operations. */
129 string_hashcode (const char *str
)
131 const char *str_limit
= str
+ strlen (str
);
133 while (str
< str_limit
)
136 str
+= u8_mbtouc (&uc
, (const unsigned char *) str
, str_limit
- str
);
138 /* Single UCS-2 'char'. */
139 hash
= 31 * hash
+ uc
;
142 /* UTF-16 surrogate: two 'char's. */
143 unsigned int uc1
= 0xd800 + ((uc
- 0x10000) >> 10);
144 unsigned int uc2
= 0xdc00 + ((uc
- 0x10000) & 0x3ff);
145 hash
= 31 * hash
+ uc1
;
146 hash
= 31 * hash
+ uc2
;
149 return hash
& 0x7fffffff;
153 /* Compute a good hash table size for the given set of msgids. */
155 compute_hashsize (message_list_ty
*mlp
, bool *collisionp
)
157 /* This is an O(n^2) algorithm, but should be sufficient because few
158 programs have more than 1000 messages in a single domain. */
159 #define XXN 3 /* can be tweaked */
160 #define XXS 3 /* can be tweaked */
161 unsigned int n
= mlp
->nitems
;
162 unsigned int *hashcodes
=
163 (unsigned int *) xallocsa (n
* sizeof (unsigned int));
164 unsigned int hashsize
;
165 unsigned int best_hashsize
;
166 unsigned int best_score
;
169 for (j
= 0; j
< n
; j
++)
170 hashcodes
[j
] = string_hashcode (mlp
->item
[j
]->msgid
);
172 /* Try all numbers between n and 3*n. The score depends on the size of the
173 table -- the smaller the better -- and the number of collision lookups,
174 i.e. total number of times that 1 + (hashcode % (hashsize - 2))
175 is added to the index during lookup. If there are collisions, only odd
176 hashsize values are allowed. */
178 best_score
= UINT_MAX
;
179 for (hashsize
= n
; hashsize
<= XXN
* n
; hashsize
++)
184 /* Premature end of the loop if all future scores are known to be
185 larger than the already reached best_score. This relies on the
186 ascending loop and on the fact that score >= hashsize. */
187 if (hashsize
>= best_score
)
190 bitmap
= (char *) xmalloc (hashsize
);
191 memset (bitmap
, 0, hashsize
);
194 for (j
= 0; j
< n
; j
++)
196 unsigned int idx
= hashcodes
[j
] % hashsize
;
198 if (bitmap
[idx
] != 0)
200 /* Collision. Cannot deal with it if hashsize is even. */
201 if ((hashsize
% 2) == 0)
202 /* Try next hashsize. */
206 unsigned int idx0
= idx
;
207 unsigned int incr
= 1 + (hashcodes
[j
] % (hashsize
- 2));
208 score
+= 2; /* Big penalty for the additional division */
211 score
++; /* Small penalty for each loop round */
216 /* Searching for a hole, we performed a whole round
217 across the table. This happens particularly
218 frequently if gcd(hashsize,incr) > 1. Try next
222 while (bitmap
[idx
] != 0);
228 /* Big hashsize also gives a penalty. */
229 score
= XXS
* score
+ hashsize
;
231 /* If for any incr between 1 and hashsize - 2, an whole round
232 (idx0, idx0 + incr, ...) is occupied, and the lookup function
233 must deal with collisions, then some inputs would lead to
234 an endless loop in the lookup function. */
235 if (score
> hashsize
)
239 /* Since the set { idx0, idx0 + incr, ... } depends only on idx0
240 and gcd(hashsize,incr), we only need to conside incr that
242 for (incr
= 1; incr
<= hashsize
/ 2; incr
++)
243 if ((hashsize
% incr
) == 0)
247 for (idx0
= 0; idx0
< incr
; idx0
++)
252 for (idx
= idx0
; idx
< hashsize
; idx
+= incr
)
253 if (bitmap
[idx
] == 0)
259 /* A whole round is occupied. */
271 if (score
< best_score
)
274 best_hashsize
= hashsize
;
277 if (best_hashsize
== 0 || best_score
< best_hashsize
)
282 /* There are collisions if and only if best_score > best_hashsize. */
283 *collisionp
= (best_score
> best_hashsize
);
284 return best_hashsize
;
288 struct table_item
{ unsigned int index
; message_ty
*mp
; };
291 compare_index (const void *pval1
, const void *pval2
)
293 return (int)((const struct table_item
*) pval1
)->index
294 - (int)((const struct table_item
*) pval2
)->index
;
297 /* Compute the list of messages and table indices, sorted according to the
299 static struct table_item
*
300 compute_table_items (message_list_ty
*mlp
, unsigned int hashsize
)
302 unsigned int n
= mlp
->nitems
;
303 struct table_item
*arr
=
304 (struct table_item
*) xmalloc (n
* sizeof (struct table_item
));
308 bitmap
= (char *) xmalloc (hashsize
);
309 memset (bitmap
, 0, hashsize
);
311 for (j
= 0; j
< n
; j
++)
313 unsigned int hashcode
= string_hashcode (mlp
->item
[j
]->msgid
);
314 unsigned int idx
= hashcode
% hashsize
;
316 if (bitmap
[idx
] != 0)
318 unsigned int incr
= 1 + (hashcode
% (hashsize
- 2));
325 while (bitmap
[idx
] != 0);
330 arr
[j
].mp
= mlp
->item
[j
];
335 qsort (arr
, n
, sizeof (arr
[0]), compare_index
);
341 /* Write a string in Java Unicode notation to the given stream. */
343 write_java_string (FILE *stream
, const char *str
)
345 static const char hexdigit
[] = "0123456789abcdef";
346 const char *str_limit
= str
+ strlen (str
);
348 fprintf (stream
, "\"");
349 while (str
< str_limit
)
352 str
+= u8_mbtouc (&uc
, (const unsigned char *) str
, str_limit
- str
);
355 /* Single UCS-2 'char'. */
357 fprintf (stream
, "\\n");
358 else if (uc
== 0x000d)
359 fprintf (stream
, "\\r");
360 else if (uc
== 0x0022)
361 fprintf (stream
, "\\\"");
362 else if (uc
== 0x005c)
363 fprintf (stream
, "\\\\");
364 else if (uc
>= 0x0020 && uc
< 0x007f)
365 fprintf (stream
, "%c", uc
);
367 fprintf (stream
, "\\u%c%c%c%c",
368 hexdigit
[(uc
>> 12) & 0x0f], hexdigit
[(uc
>> 8) & 0x0f],
369 hexdigit
[(uc
>> 4) & 0x0f], hexdigit
[uc
& 0x0f]);
373 /* UTF-16 surrogate: two 'char's. */
374 unsigned int uc1
= 0xd800 + ((uc
- 0x10000) >> 10);
375 unsigned int uc2
= 0xdc00 + ((uc
- 0x10000) & 0x3ff);
376 fprintf (stream
, "\\u%c%c%c%c",
377 hexdigit
[(uc1
>> 12) & 0x0f], hexdigit
[(uc1
>> 8) & 0x0f],
378 hexdigit
[(uc1
>> 4) & 0x0f], hexdigit
[uc1
& 0x0f]);
379 fprintf (stream
, "\\u%c%c%c%c",
380 hexdigit
[(uc2
>> 12) & 0x0f], hexdigit
[(uc2
>> 8) & 0x0f],
381 hexdigit
[(uc2
>> 4) & 0x0f], hexdigit
[uc2
& 0x0f]);
384 fprintf (stream
, "\"");
388 /* Write Java code that returns the value for a message. If the message
389 has plural forms, it is an expression of type String[], otherwise it is
390 an expression of type String. */
392 write_java_msgstr (FILE *stream
, message_ty
*mp
)
394 if (mp
->msgid_plural
!= NULL
)
399 fprintf (stream
, "new java.lang.String[] { ");
400 for (p
= mp
->msgstr
, first
= true;
401 p
< mp
->msgstr
+ mp
->msgstr_len
;
402 p
+= strlen (p
) + 1, first
= false)
405 fprintf (stream
, ", ");
406 write_java_string (stream
, p
);
408 fprintf (stream
, " }");
412 if (mp
->msgstr_len
!= strlen (mp
->msgstr
) + 1)
415 write_java_string (stream
, mp
->msgstr
);
420 /* Writes the body of the function which returns the local value for a key
423 write_lookup_code (FILE *stream
, unsigned int hashsize
, bool collisions
)
425 fprintf (stream
, " int hash_val = msgid.hashCode() & 0x7fffffff;\n");
426 fprintf (stream
, " int idx = (hash_val %% %d) << 1;\n", hashsize
);
429 fprintf (stream
, " {\n");
430 fprintf (stream
, " java.lang.Object found = table[idx];\n");
431 fprintf (stream
, " if (found == null)\n");
432 fprintf (stream
, " return null;\n");
433 fprintf (stream
, " if (msgid.equals(found))\n");
434 fprintf (stream
, " return table[idx + 1];\n");
435 fprintf (stream
, " }\n");
436 fprintf (stream
, " int incr = ((hash_val %% %d) + 1) << 1;\n",
438 fprintf (stream
, " for (;;) {\n");
439 fprintf (stream
, " idx += incr;\n");
440 fprintf (stream
, " if (idx >= %d)\n", 2 * hashsize
);
441 fprintf (stream
, " idx -= %d;\n", 2 * hashsize
);
442 fprintf (stream
, " java.lang.Object found = table[idx];\n");
443 fprintf (stream
, " if (found == null)\n");
444 fprintf (stream
, " return null;\n");
445 fprintf (stream
, " if (msgid.equals(found))\n");
446 fprintf (stream
, " return table[idx + 1];\n");
447 fprintf (stream
, " }\n");
451 fprintf (stream
, " java.lang.Object found = table[idx];\n");
452 fprintf (stream
, " if (found != null && msgid.equals(found))\n");
453 fprintf (stream
, " return table[idx + 1];\n");
454 fprintf (stream
, " return null;\n");
459 /* Tests whether a plural expression, evaluated according to the C rules,
460 can only produce the values 0 and 1. */
462 is_expression_boolean (struct expression
*exp
)
464 switch (exp
->operation
)
477 case greater_or_equal
:
484 return (exp
->val
.num
== 0 || exp
->val
.num
== 1);
486 return is_expression_boolean (exp
->val
.args
[1])
487 && is_expression_boolean (exp
->val
.args
[2]);
494 /* Write Java code that evaluates a plural expression according to the C rules.
495 The variable is called 'n'. */
497 write_java_expression (FILE *stream
, struct expression
*exp
, bool as_boolean
)
499 /* We use parentheses everywhere. This frees us from tracking the priority
500 of arithmetic operators. */
503 /* Emit a Java expression of type 'boolean'. */
504 switch (exp
->operation
)
507 fprintf (stream
, "%s", exp
->val
.num
? "true" : "false");
510 fprintf (stream
, "(!");
511 write_java_expression (stream
, exp
->val
.args
[0], true);
512 fprintf (stream
, ")");
515 fprintf (stream
, "(");
516 write_java_expression (stream
, exp
->val
.args
[0], false);
517 fprintf (stream
, " < ");
518 write_java_expression (stream
, exp
->val
.args
[1], false);
519 fprintf (stream
, ")");
522 fprintf (stream
, "(");
523 write_java_expression (stream
, exp
->val
.args
[0], false);
524 fprintf (stream
, " > ");
525 write_java_expression (stream
, exp
->val
.args
[1], false);
526 fprintf (stream
, ")");
529 fprintf (stream
, "(");
530 write_java_expression (stream
, exp
->val
.args
[0], false);
531 fprintf (stream
, " <= ");
532 write_java_expression (stream
, exp
->val
.args
[1], false);
533 fprintf (stream
, ")");
535 case greater_or_equal
:
536 fprintf (stream
, "(");
537 write_java_expression (stream
, exp
->val
.args
[0], false);
538 fprintf (stream
, " >= ");
539 write_java_expression (stream
, exp
->val
.args
[1], false);
540 fprintf (stream
, ")");
543 fprintf (stream
, "(");
544 write_java_expression (stream
, exp
->val
.args
[0], false);
545 fprintf (stream
, " == ");
546 write_java_expression (stream
, exp
->val
.args
[1], false);
547 fprintf (stream
, ")");
550 fprintf (stream
, "(");
551 write_java_expression (stream
, exp
->val
.args
[0], false);
552 fprintf (stream
, " != ");
553 write_java_expression (stream
, exp
->val
.args
[1], false);
554 fprintf (stream
, ")");
557 fprintf (stream
, "(");
558 write_java_expression (stream
, exp
->val
.args
[0], true);
559 fprintf (stream
, " && ");
560 write_java_expression (stream
, exp
->val
.args
[1], true);
561 fprintf (stream
, ")");
564 fprintf (stream
, "(");
565 write_java_expression (stream
, exp
->val
.args
[0], true);
566 fprintf (stream
, " || ");
567 write_java_expression (stream
, exp
->val
.args
[1], true);
568 fprintf (stream
, ")");
571 if (is_expression_boolean (exp
->val
.args
[1])
572 && is_expression_boolean (exp
->val
.args
[2]))
574 fprintf (stream
, "(");
575 write_java_expression (stream
, exp
->val
.args
[0], true);
576 fprintf (stream
, " ? ");
577 write_java_expression (stream
, exp
->val
.args
[1], true);
578 fprintf (stream
, " : ");
579 write_java_expression (stream
, exp
->val
.args
[2], true);
580 fprintf (stream
, ")");
590 fprintf (stream
, "(");
591 write_java_expression (stream
, exp
, false);
592 fprintf (stream
, " != 0)");
600 /* Emit a Java expression of type 'long'. */
601 switch (exp
->operation
)
604 fprintf (stream
, "n");
607 fprintf (stream
, "%lu", exp
->val
.num
);
610 fprintf (stream
, "(");
611 write_java_expression (stream
, exp
->val
.args
[0], false);
612 fprintf (stream
, " * ");
613 write_java_expression (stream
, exp
->val
.args
[1], false);
614 fprintf (stream
, ")");
617 fprintf (stream
, "(");
618 write_java_expression (stream
, exp
->val
.args
[0], false);
619 fprintf (stream
, " / ");
620 write_java_expression (stream
, exp
->val
.args
[1], false);
621 fprintf (stream
, ")");
624 fprintf (stream
, "(");
625 write_java_expression (stream
, exp
->val
.args
[0], false);
626 fprintf (stream
, " %% ");
627 write_java_expression (stream
, exp
->val
.args
[1], false);
628 fprintf (stream
, ")");
631 fprintf (stream
, "(");
632 write_java_expression (stream
, exp
->val
.args
[0], false);
633 fprintf (stream
, " + ");
634 write_java_expression (stream
, exp
->val
.args
[1], false);
635 fprintf (stream
, ")");
638 fprintf (stream
, "(");
639 write_java_expression (stream
, exp
->val
.args
[0], false);
640 fprintf (stream
, " - ");
641 write_java_expression (stream
, exp
->val
.args
[1], false);
642 fprintf (stream
, ")");
645 fprintf (stream
, "(");
646 write_java_expression (stream
, exp
->val
.args
[0], true);
647 fprintf (stream
, " ? ");
648 write_java_expression (stream
, exp
->val
.args
[1], false);
649 fprintf (stream
, " : ");
650 write_java_expression (stream
, exp
->val
.args
[2], false);
651 fprintf (stream
, ")");
657 case greater_or_equal
:
662 fprintf (stream
, "(");
663 write_java_expression (stream
, exp
, true);
664 fprintf (stream
, " ? 1 : 0)");
673 /* Write the Java code for the ResourceBundle subclass to the given stream.
674 Note that we use fully qualified class names and no "import" statements,
675 because applications can have their own classes called X.Y.ResourceBundle
678 write_java_code (FILE *stream
, const char *class_name
, message_list_ty
*mlp
,
681 const char *last_dot
;
682 unsigned int plurals
;
686 "/* Automatically generated by GNU msgfmt. Do not modify! */\n");
687 last_dot
= strrchr (class_name
, '.');
688 if (last_dot
!= NULL
)
690 fprintf (stream
, "package ");
691 fwrite (class_name
, 1, last_dot
- class_name
, stream
);
692 fprintf (stream
, ";\npublic class %s", last_dot
+ 1);
695 fprintf (stream
, "public class %s", class_name
);
696 fprintf (stream
, " extends java.util.ResourceBundle {\n");
698 /* Determine whether there are plural messages. */
700 for (j
= 0; j
< mlp
->nitems
; j
++)
701 if (mlp
->item
[j
]->msgid_plural
!= NULL
)
706 unsigned int hashsize
;
708 struct table_item
*table_items
;
709 const char *table_eltype
;
711 /* Determine the hash table size and whether it leads to collisions. */
712 hashsize
= compute_hashsize (mlp
, &collisions
);
714 /* Determines which indices in the table contain a message. The others
716 table_items
= compute_table_items (mlp
, hashsize
);
718 /* Emit the table of pairs (msgid, msgstr). If there are plurals,
719 it is of type Object[], otherwise of type String[]. We use a static
720 code block because that makes less code: The Java compilers also
721 generate code for the 'null' entries, which is dumb. */
722 table_eltype
= (plurals
? "java.lang.Object" : "java.lang.String");
723 fprintf (stream
, " private static final %s[] table;\n", table_eltype
);
724 fprintf (stream
, " static {\n");
725 fprintf (stream
, " %s[] t = new %s[%d];\n", table_eltype
, table_eltype
,
727 for (j
= 0; j
< mlp
->nitems
; j
++)
729 struct table_item
*ti
= &table_items
[j
];
731 fprintf (stream
, " t[%d] = ", 2 * ti
->index
);
732 write_java_string (stream
, ti
->mp
->msgid
);
733 fprintf (stream
, ";\n");
734 fprintf (stream
, " t[%d] = ", 2 * ti
->index
+ 1);
735 write_java_msgstr (stream
, ti
->mp
);
736 fprintf (stream
, ";\n");
738 fprintf (stream
, " table = t;\n");
739 fprintf (stream
, " }\n");
741 /* Emit the msgid_plural strings. Only used by msgunfmt. */
745 fprintf (stream
, " public static final java.lang.String[] get_msgid_plural_table () {\n");
746 fprintf (stream
, " return new java.lang.String[] { ");
748 for (j
= 0; j
< mlp
->nitems
; j
++)
750 struct table_item
*ti
= &table_items
[j
];
751 if (ti
->mp
->msgid_plural
!= NULL
)
754 fprintf (stream
, ", ");
755 write_java_string (stream
, ti
->mp
->msgid_plural
);
759 fprintf (stream
, " };\n");
760 fprintf (stream
, " }\n");
765 /* Emit the lookup function. It is a common subroutine for
766 handleGetObject and ngettext. */
767 fprintf (stream
, " public java.lang.Object lookup (java.lang.String msgid) {\n");
768 write_lookup_code (stream
, hashsize
, collisions
);
769 fprintf (stream
, " }\n");
772 /* Emit the handleGetObject function. It is declared abstract in
773 ResourceBundle. It implements a local version of gettext. */
774 fprintf (stream
, " public java.lang.Object handleGetObject (java.lang.String msgid) throws java.util.MissingResourceException {\n");
777 fprintf (stream
, " java.lang.Object value = lookup(msgid);\n");
778 fprintf (stream
, " return (value instanceof java.lang.String[] ? ((java.lang.String[])value)[0] : value);\n");
781 write_lookup_code (stream
, hashsize
, collisions
);
782 fprintf (stream
, " }\n");
784 /* Emit the getKeys function. It is declared abstract in ResourceBundle.
785 The inner class is not avoidable. */
786 fprintf (stream
, " public java.util.Enumeration getKeys () {\n");
787 fprintf (stream
, " return\n");
788 fprintf (stream
, " new java.util.Enumeration() {\n");
789 fprintf (stream
, " private int idx = 0;\n");
790 fprintf (stream
, " { while (idx < %d && table[idx] == null) idx += 2; }\n",
792 fprintf (stream
, " public boolean hasMoreElements () {\n");
793 fprintf (stream
, " return (idx < %d);\n", 2 * hashsize
);
794 fprintf (stream
, " }\n");
795 fprintf (stream
, " public java.lang.Object nextElement () {\n");
796 fprintf (stream
, " java.lang.Object key = table[idx];\n");
797 fprintf (stream
, " do idx += 2; while (idx < %d && table[idx] == null);\n",
799 fprintf (stream
, " return key;\n");
800 fprintf (stream
, " }\n");
801 fprintf (stream
, " };\n");
802 fprintf (stream
, " }\n");
806 /* Java 1.1.x uses a different hash function. If compatibility with
807 this Java version is required, the hash table must be built at run time,
808 not at compile time. */
809 fprintf (stream
, " private static final java.util.Hashtable table;\n");
810 fprintf (stream
, " static {\n");
811 fprintf (stream
, " java.util.Hashtable t = new java.util.Hashtable();\n");
812 for (j
= 0; j
< mlp
->nitems
; j
++)
814 fprintf (stream
, " t.put(");
815 write_java_string (stream
, mlp
->item
[j
]->msgid
);
816 fprintf (stream
, ",");
817 write_java_msgstr (stream
, mlp
->item
[j
]);
818 fprintf (stream
, ");\n");
820 fprintf (stream
, " table = t;\n");
821 fprintf (stream
, " }\n");
823 /* Emit the msgid_plural strings. Only used by msgunfmt. */
826 fprintf (stream
, " public static final java.util.Hashtable get_msgid_plural_table () {\n");
827 fprintf (stream
, " java.util.Hashtable p = new java.util.Hashtable();\n");
828 for (j
= 0; j
< mlp
->nitems
; j
++)
829 if (mlp
->item
[j
]->msgid_plural
!= NULL
)
831 fprintf (stream
, " p.put(");
832 write_java_string (stream
, mlp
->item
[j
]->msgid
);
833 fprintf (stream
, ",");
834 write_java_string (stream
, mlp
->item
[j
]->msgid_plural
);
835 fprintf (stream
, ");\n");
837 fprintf (stream
, " return p;\n");
838 fprintf (stream
, " }\n");
843 /* Emit the lookup function. It is a common subroutine for
844 handleGetObject and ngettext. */
845 fprintf (stream
, " public java.lang.Object lookup (java.lang.String msgid) {\n");
846 fprintf (stream
, " return table.get(msgid);\n");
847 fprintf (stream
, " }\n");
850 /* Emit the handleGetObject function. It is declared abstract in
851 ResourceBundle. It implements a local version of gettext. */
852 fprintf (stream
, " public java.lang.Object handleGetObject (java.lang.String msgid) throws java.util.MissingResourceException {\n");
855 fprintf (stream
, " java.lang.Object value = table.get(msgid);\n");
856 fprintf (stream
, " return (value instanceof java.lang.String[] ? ((java.lang.String[])value)[0] : value);\n");
859 fprintf (stream
, " return table.get(msgid);\n");
860 fprintf (stream
, " }\n");
862 /* Emit the getKeys function. It is declared abstract in
864 fprintf (stream
, " public java.util.Enumeration getKeys () {\n");
865 fprintf (stream
, " return table.keys();\n");
866 fprintf (stream
, " }\n");
869 /* Emit the pluralEval function. It is a subroutine for ngettext. */
872 message_ty
*header_entry
;
873 struct expression
*plural
;
874 unsigned long int nplurals
;
876 header_entry
= message_list_search (mlp
, "");
877 extract_plural_expression (header_entry
? header_entry
->msgstr
: NULL
,
880 fprintf (stream
, " public static long pluralEval (long n) {\n");
881 fprintf (stream
, " return ");
882 write_java_expression (stream
, plural
, false);
883 fprintf (stream
, ";\n");
884 fprintf (stream
, " }\n");
887 /* Emit the getParent function. It is a subroutine for ngettext. */
888 fprintf (stream
, " public java.util.ResourceBundle getParent () {\n");
889 fprintf (stream
, " return parent;\n");
890 fprintf (stream
, " }\n");
892 fprintf (stream
, "}\n");
896 /* Asynchronously cleaning up temporary files, when we receive any of the
897 usually occurring signals whose default action is to terminate the
903 unsigned int subdir_count
;
904 const char * const *subdir
;
905 const char *file_name
;
908 /* The signal handler. It gets called asynchronously. */
914 /* First cleanup the files in the subdirectory. */
916 const char *filename
= cleanup_list
.file_name
;
918 if (filename
!= NULL
)
922 /* Then cleanup the subdirectories. */
923 for (i
= cleanup_list
.subdir_count
; i
> 0; )
925 const char *filename
= cleanup_list
.subdir
[--i
];
930 /* Then cleanup the main temporary directory. */
932 const char *filename
= cleanup_list
.tmpdir
;
934 if (filename
!= NULL
)
941 msgdomain_write_java (message_list_ty
*mlp
, const char *canon_encoding
,
942 const char *resource_name
, const char *locale_name
,
943 const char *directory
,
952 char *java_file_name
;
954 const char *java_sources
[1];
956 /* If no entry for this resource/domain, don't even create the file. */
957 if (mlp
->nitems
== 0)
962 /* Convert the messages to Unicode. */
963 iconv_message_list (mlp
, canon_encoding
, po_charset_utf8
, NULL
);
965 cleanup_list
.tmpdir
= NULL
;
966 cleanup_list
.subdir_count
= 0;
967 cleanup_list
.file_name
= NULL
;
969 static bool cleanup_already_registered
= false;
970 if (!cleanup_already_registered
)
972 at_fatal_signal (&cleanup
);
973 cleanup_already_registered
= true;
977 /* Create a temporary directory where we can put the Java file. */
978 template = (char *) xallocsa (PATH_MAX
);
979 if (path_search (template, PATH_MAX
, NULL
, "msg", 1))
982 _("cannot find a temporary directory, try setting $TMPDIR"));
985 block_fatal_signals ();
986 tmpdir
= mkdtemp (template);
987 cleanup_list
.tmpdir
= tmpdir
;
988 unblock_fatal_signals ();
992 _("cannot create a temporary directory using template \"%s\""),
997 /* Assign a default value to the resource name. */
998 if (resource_name
== NULL
)
999 resource_name
= "Messages";
1001 /* Prepare the list of subdirectories. */
1002 ndots
= check_resource_name (resource_name
);
1005 error (0, 0, _("not a valid Java class name: %s"), resource_name
);
1009 if (locale_name
!= NULL
)
1012 (char *) xmalloc (strlen (resource_name
) + 1 + strlen (locale_name
) + 1);
1013 sprintf (class_name
, "%s_%s", resource_name
, locale_name
);
1016 class_name
= xstrdup (resource_name
);
1018 subdirs
= (ndots
> 0 ? (char **) xallocsa (ndots
* sizeof (char *)) : NULL
);
1021 const char *last_dir
;
1026 for (i
= 0; i
< ndots
; i
++)
1028 const char *q
= strchr (p
, '.');
1030 char *part
= (char *) xallocsa (n
+ 1);
1031 memcpy (part
, p
, n
);
1033 subdirs
[i
] = concatenated_pathname (last_dir
, part
, NULL
);
1035 last_dir
= subdirs
[i
];
1039 if (locale_name
!= NULL
)
1041 char *suffix
= (char *) xmalloc (1 + strlen (locale_name
) + 5 + 1);
1042 sprintf (suffix
, "_%s.java", locale_name
);
1043 java_file_name
= concatenated_pathname (last_dir
, p
, suffix
);
1047 java_file_name
= concatenated_pathname (last_dir
, p
, ".java");
1050 /* Create the subdirectories. This is needed because some older Java
1051 compilers verify that the source of class A.B.C really sits in a
1052 directory whose name ends in /A/B. */
1056 cleanup_list
.subdir
= (const char * const *) subdirs
;
1057 cleanup_list
.subdir_count
= 0;
1058 for (i
= 0; i
< ndots
; i
++)
1060 cleanup_list
.subdir_count
= i
+ 1;
1061 if (mkdir (subdirs
[i
], S_IRUSR
| S_IWUSR
| S_IXUSR
) < 0)
1063 error (0, errno
, _("failed to create \"%s\""), subdirs
[i
]);
1071 /* Create the Java file. */
1072 cleanup_list
.file_name
= java_file_name
;
1073 java_file
= fopen (java_file_name
, "w");
1074 if (java_file
== NULL
)
1076 error (0, errno
, _("failed to create \"%s\""), java_file_name
);
1080 write_java_code (java_file
, class_name
, mlp
, assume_java2
);
1082 if (fwriteerror (java_file
))
1084 error (0, errno
, _("error while writing \"%s\" file"), java_file_name
);
1089 /* Compile the Java file to a .class file.
1090 directory must be non-NULL, because when the -d option is omitted, the
1091 Java compilers create the class files in the source file's directory -
1092 which is in a temporary directory in our case. */
1093 java_sources
[0] = java_file_name
;
1094 if (compile_java_class (java_sources
, 1, NULL
, 0, directory
, true, false,
1098 compilation of Java class failed, please try --verbose or set $JAVAC"));
1105 unlink (java_file_name
);
1107 cleanup_list
.file_name
= NULL
;
1110 for (i
= ndots
; i
-- > 0; )
1114 cleanup_list
.subdir_count
= 0;
1117 free (java_file_name
);
1118 for (i
= 0; i
< ndots
; i
++)
1126 cleanup_list
.tmpdir
= NULL
;
1128 /* Here we could unregister the cleanup() handler. */