1 /* Writing Java ResourceBundles.
2 Copyright (C) 2001-2003, 2005-2010 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 3 of the License, or
8 (at your option) any later version.
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, see <http://www.gnu.org/licenses/>. */
24 #include "write-java.h"
34 #if !defined S_ISDIR && defined S_IFDIR
35 # define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
37 #if !S_IRUSR && S_IREAD
38 # define S_IRUSR S_IREAD
41 # define S_IRUSR 00400
43 #if !S_IWUSR && S_IWRITE
44 # define S_IWUSR S_IWRITE
47 # define S_IWUSR 00200
49 #if !S_IXUSR && S_IEXEC
50 # define S_IXUSR S_IEXEC
53 # define S_IXUSR 00100
59 #include "xvasprintf.h"
63 #include "msgl-iconv.h"
64 #include "plural-exp.h"
65 #include "po-charset.h"
69 #include "concat-filename.h"
70 #include "fwriteerror.h"
71 #include "clean-temp.h"
75 #define _(str) gettext (str)
78 /* Check that the resource name is a valid Java class name. To simplify
79 things, we allow only ASCII characters in the class name.
80 Return the number of dots in the class name, or -1 if not OK. */
82 check_resource_name (const char *name)
89 /* First character, see Character.isJavaIdentifierStart. */
90 if (!(c_isalpha (*p) || (*p == '$') || (*p == '_')))
92 /* Following characters, see Character.isJavaIdentifierPart. */
95 while (c_isalpha (*p) || (*p == '$') || (*p == '_') || c_isdigit (*p));
107 /* Return the Java hash code of a string mod 2^31.
108 The Java String.hashCode() function returns the same values across
109 Java implementations.
110 (See http://www.javasoft.com/docs/books/jls/clarify.html)
111 It returns a signed 32-bit integer. We add a mod 2^31 afterwards;
112 this removes one bit but greatly simplifies the following "mod hash_size"
113 and "mod (hash_size - 2)" operations. */
115 string_hashcode (const char *str)
117 const char *str_limit = str + strlen (str);
119 while (str < str_limit)
122 str += u8_mbtouc (&uc, (const unsigned char *) str, str_limit - str);
124 /* Single UCS-2 'char'. */
125 hash = 31 * hash + uc;
128 /* UTF-16 surrogate: two 'char's. */
129 ucs4_t uc1 = 0xd800 + ((uc - 0x10000) >> 10);
130 ucs4_t uc2 = 0xdc00 + ((uc - 0x10000) & 0x3ff);
131 hash = 31 * hash + uc1;
132 hash = 31 * hash + uc2;
135 return hash & 0x7fffffff;
139 /* Return the Java hash code of a (msgctxt, msgid) pair mod 2^31. */
141 msgid_hashcode (const char *msgctxt, const char *msgid)
144 return string_hashcode (msgid);
147 size_t msgctxt_len = strlen (msgctxt);
148 size_t msgid_len = strlen (msgid);
149 size_t combined_len = msgctxt_len + 1 + msgid_len;
153 combined = (char *) xmalloca (combined_len);
154 memcpy (combined, msgctxt, msgctxt_len);
155 combined[msgctxt_len] = MSGCTXT_SEPARATOR;
156 memcpy (combined + msgctxt_len + 1, msgid, msgid_len + 1);
158 result = string_hashcode (combined);
167 /* Compute a good hash table size for the given set of msgids. */
169 compute_hashsize (message_list_ty *mlp, bool *collisionp)
171 /* This is an O(n^2) algorithm, but should be sufficient because few
172 programs have more than 1000 messages in a single domain. */
173 #define XXN 3 /* can be tweaked */
174 #define XXS 3 /* can be tweaked */
175 unsigned int n = mlp->nitems;
176 unsigned int *hashcodes =
177 (unsigned int *) xmalloca (n * sizeof (unsigned int));
178 unsigned int hashsize;
179 unsigned int best_hashsize;
180 unsigned int best_score;
183 for (j = 0; j < n; j++)
184 hashcodes[j] = msgid_hashcode (mlp->item[j]->msgctxt, mlp->item[j]->msgid);
186 /* Try all numbers between n and 3*n. The score depends on the size of the
187 table -- the smaller the better -- and the number of collision lookups,
188 i.e. total number of times that 1 + (hashcode % (hashsize - 2))
189 is added to the index during lookup. If there are collisions, only odd
190 hashsize values are allowed. */
192 best_score = UINT_MAX;
193 for (hashsize = n; hashsize <= XXN * n; hashsize++)
198 /* Premature end of the loop if all future scores are known to be
199 larger than the already reached best_score. This relies on the
200 ascending loop and on the fact that score >= hashsize. */
201 if (hashsize >= best_score)
204 bitmap = XNMALLOC (hashsize, char);
205 memset (bitmap, 0, hashsize);
208 for (j = 0; j < n; j++)
210 unsigned int idx = hashcodes[j] % hashsize;
212 if (bitmap[idx] != 0)
214 /* Collision. Cannot deal with it if hashsize is even. */
215 if ((hashsize % 2) == 0)
216 /* Try next hashsize. */
220 unsigned int idx0 = idx;
221 unsigned int incr = 1 + (hashcodes[j] % (hashsize - 2));
222 score += 2; /* Big penalty for the additional division */
225 score++; /* Small penalty for each loop round */
230 /* Searching for a hole, we performed a whole round
231 across the table. This happens particularly
232 frequently if gcd(hashsize,incr) > 1. Try next
236 while (bitmap[idx] != 0);
242 /* Big hashsize also gives a penalty. */
243 score = XXS * score + hashsize;
245 /* If for any incr between 1 and hashsize - 2, an whole round
246 (idx0, idx0 + incr, ...) is occupied, and the lookup function
247 must deal with collisions, then some inputs would lead to
248 an endless loop in the lookup function. */
249 if (score > hashsize)
253 /* Since the set { idx0, idx0 + incr, ... } depends only on idx0
254 and gcd(hashsize,incr), we only need to conside incr that
256 for (incr = 1; incr <= hashsize / 2; incr++)
257 if ((hashsize % incr) == 0)
261 for (idx0 = 0; idx0 < incr; idx0++)
266 for (idx = idx0; idx < hashsize; idx += incr)
267 if (bitmap[idx] == 0)
273 /* A whole round is occupied. */
285 if (score < best_score)
288 best_hashsize = hashsize;
291 if (best_hashsize == 0 || best_score < best_hashsize)
296 /* There are collisions if and only if best_score > best_hashsize. */
297 *collisionp = (best_score > best_hashsize);
298 return best_hashsize;
302 struct table_item { unsigned int index; message_ty *mp; };
305 compare_index (const void *pval1, const void *pval2)
307 return (int)((const struct table_item *) pval1)->index
308 - (int)((const struct table_item *) pval2)->index;
311 /* Compute the list of messages and table indices, sorted according to the
313 static struct table_item *
314 compute_table_items (message_list_ty *mlp, unsigned int hashsize)
316 unsigned int n = mlp->nitems;
317 struct table_item *arr = XNMALLOC (n, struct table_item);
321 bitmap = XNMALLOC (hashsize, char);
322 memset (bitmap, 0, hashsize);
324 for (j = 0; j < n; j++)
326 unsigned int hashcode =
327 msgid_hashcode (mlp->item[j]->msgctxt, mlp->item[j]->msgid);
328 unsigned int idx = hashcode % hashsize;
330 if (bitmap[idx] != 0)
332 unsigned int incr = 1 + (hashcode % (hashsize - 2));
339 while (bitmap[idx] != 0);
344 arr[j].mp = mlp->item[j];
349 qsort (arr, n, sizeof (arr[0]), compare_index);
355 /* Write a string in Java Unicode notation to the given stream. */
357 write_java_string (FILE *stream, const char *str)
359 static const char hexdigit[] = "0123456789abcdef";
360 const char *str_limit = str + strlen (str);
362 fprintf (stream, "\"");
363 while (str < str_limit)
366 str += u8_mbtouc (&uc, (const unsigned char *) str, str_limit - str);
369 /* Single UCS-2 'char'. */
371 fprintf (stream, "\\n");
372 else if (uc == 0x000d)
373 fprintf (stream, "\\r");
374 else if (uc == 0x0022)
375 fprintf (stream, "\\\"");
376 else if (uc == 0x005c)
377 fprintf (stream, "\\\\");
378 else if (uc >= 0x0020 && uc < 0x007f)
379 fprintf (stream, "%c", (int) uc);
381 fprintf (stream, "\\u%c%c%c%c",
382 hexdigit[(uc >> 12) & 0x0f], hexdigit[(uc >> 8) & 0x0f],
383 hexdigit[(uc >> 4) & 0x0f], hexdigit[uc & 0x0f]);
387 /* UTF-16 surrogate: two 'char's. */
388 ucs4_t uc1 = 0xd800 + ((uc - 0x10000) >> 10);
389 ucs4_t uc2 = 0xdc00 + ((uc - 0x10000) & 0x3ff);
390 fprintf (stream, "\\u%c%c%c%c",
391 hexdigit[(uc1 >> 12) & 0x0f], hexdigit[(uc1 >> 8) & 0x0f],
392 hexdigit[(uc1 >> 4) & 0x0f], hexdigit[uc1 & 0x0f]);
393 fprintf (stream, "\\u%c%c%c%c",
394 hexdigit[(uc2 >> 12) & 0x0f], hexdigit[(uc2 >> 8) & 0x0f],
395 hexdigit[(uc2 >> 4) & 0x0f], hexdigit[uc2 & 0x0f]);
398 fprintf (stream, "\"");
402 /* Write a (msgctxt, msgid) pair as a string in Java Unicode notation to the
405 write_java_msgid (FILE *stream, message_ty *mp)
407 const char *msgctxt = mp->msgctxt;
408 const char *msgid = mp->msgid;
411 write_java_string (stream, msgid);
414 size_t msgctxt_len = strlen (msgctxt);
415 size_t msgid_len = strlen (msgid);
416 size_t combined_len = msgctxt_len + 1 + msgid_len;
419 combined = (char *) xmalloca (combined_len);
420 memcpy (combined, msgctxt, msgctxt_len);
421 combined[msgctxt_len] = MSGCTXT_SEPARATOR;
422 memcpy (combined + msgctxt_len + 1, msgid, msgid_len + 1);
424 write_java_string (stream, combined);
431 /* Write Java code that returns the value for a message. If the message
432 has plural forms, it is an expression of type String[], otherwise it is
433 an expression of type String. */
435 write_java_msgstr (FILE *stream, message_ty *mp)
437 if (mp->msgid_plural != NULL)
442 fprintf (stream, "new java.lang.String[] { ");
443 for (p = mp->msgstr, first = true;
444 p < mp->msgstr + mp->msgstr_len;
445 p += strlen (p) + 1, first = false)
448 fprintf (stream, ", ");
449 write_java_string (stream, p);
451 fprintf (stream, " }");
455 if (mp->msgstr_len != strlen (mp->msgstr) + 1)
458 write_java_string (stream, mp->msgstr);
463 /* Writes the body of the function which returns the local value for a key
466 write_lookup_code (FILE *stream, unsigned int hashsize, bool collisions)
468 fprintf (stream, " int hash_val = msgid.hashCode() & 0x7fffffff;\n");
469 fprintf (stream, " int idx = (hash_val %% %d) << 1;\n", hashsize);
472 fprintf (stream, " {\n");
473 fprintf (stream, " java.lang.Object found = table[idx];\n");
474 fprintf (stream, " if (found == null)\n");
475 fprintf (stream, " return null;\n");
476 fprintf (stream, " if (msgid.equals(found))\n");
477 fprintf (stream, " return table[idx + 1];\n");
478 fprintf (stream, " }\n");
479 fprintf (stream, " int incr = ((hash_val %% %d) + 1) << 1;\n",
481 fprintf (stream, " for (;;) {\n");
482 fprintf (stream, " idx += incr;\n");
483 fprintf (stream, " if (idx >= %d)\n", 2 * hashsize);
484 fprintf (stream, " idx -= %d;\n", 2 * hashsize);
485 fprintf (stream, " java.lang.Object found = table[idx];\n");
486 fprintf (stream, " if (found == null)\n");
487 fprintf (stream, " return null;\n");
488 fprintf (stream, " if (msgid.equals(found))\n");
489 fprintf (stream, " return table[idx + 1];\n");
490 fprintf (stream, " }\n");
494 fprintf (stream, " java.lang.Object found = table[idx];\n");
495 fprintf (stream, " if (found != null && msgid.equals(found))\n");
496 fprintf (stream, " return table[idx + 1];\n");
497 fprintf (stream, " return null;\n");
502 /* Tests whether a plural expression, evaluated according to the C rules,
503 can only produce the values 0 and 1. */
505 is_expression_boolean (struct expression *exp)
507 switch (exp->operation)
520 case greater_or_equal:
527 return (exp->val.num == 0 || exp->val.num == 1);
529 return is_expression_boolean (exp->val.args[1])
530 && is_expression_boolean (exp->val.args[2]);
537 /* Write Java code that evaluates a plural expression according to the C rules.
538 The variable is called 'n'. */
540 write_java_expression (FILE *stream, const struct expression *exp, bool as_boolean)
542 /* We use parentheses everywhere. This frees us from tracking the priority
543 of arithmetic operators. */
546 /* Emit a Java expression of type 'boolean'. */
547 switch (exp->operation)
550 fprintf (stream, "%s", exp->val.num ? "true" : "false");
553 fprintf (stream, "(!");
554 write_java_expression (stream, exp->val.args[0], true);
555 fprintf (stream, ")");
558 fprintf (stream, "(");
559 write_java_expression (stream, exp->val.args[0], false);
560 fprintf (stream, " < ");
561 write_java_expression (stream, exp->val.args[1], false);
562 fprintf (stream, ")");
565 fprintf (stream, "(");
566 write_java_expression (stream, exp->val.args[0], false);
567 fprintf (stream, " > ");
568 write_java_expression (stream, exp->val.args[1], false);
569 fprintf (stream, ")");
572 fprintf (stream, "(");
573 write_java_expression (stream, exp->val.args[0], false);
574 fprintf (stream, " <= ");
575 write_java_expression (stream, exp->val.args[1], false);
576 fprintf (stream, ")");
578 case greater_or_equal:
579 fprintf (stream, "(");
580 write_java_expression (stream, exp->val.args[0], false);
581 fprintf (stream, " >= ");
582 write_java_expression (stream, exp->val.args[1], false);
583 fprintf (stream, ")");
586 fprintf (stream, "(");
587 write_java_expression (stream, exp->val.args[0], false);
588 fprintf (stream, " == ");
589 write_java_expression (stream, exp->val.args[1], false);
590 fprintf (stream, ")");
593 fprintf (stream, "(");
594 write_java_expression (stream, exp->val.args[0], false);
595 fprintf (stream, " != ");
596 write_java_expression (stream, exp->val.args[1], false);
597 fprintf (stream, ")");
600 fprintf (stream, "(");
601 write_java_expression (stream, exp->val.args[0], true);
602 fprintf (stream, " && ");
603 write_java_expression (stream, exp->val.args[1], true);
604 fprintf (stream, ")");
607 fprintf (stream, "(");
608 write_java_expression (stream, exp->val.args[0], true);
609 fprintf (stream, " || ");
610 write_java_expression (stream, exp->val.args[1], true);
611 fprintf (stream, ")");
614 if (is_expression_boolean (exp->val.args[1])
615 && is_expression_boolean (exp->val.args[2]))
617 fprintf (stream, "(");
618 write_java_expression (stream, exp->val.args[0], true);
619 fprintf (stream, " ? ");
620 write_java_expression (stream, exp->val.args[1], true);
621 fprintf (stream, " : ");
622 write_java_expression (stream, exp->val.args[2], true);
623 fprintf (stream, ")");
633 fprintf (stream, "(");
634 write_java_expression (stream, exp, false);
635 fprintf (stream, " != 0)");
643 /* Emit a Java expression of type 'long'. */
644 switch (exp->operation)
647 fprintf (stream, "n");
650 fprintf (stream, "%lu", exp->val.num);
653 fprintf (stream, "(");
654 write_java_expression (stream, exp->val.args[0], false);
655 fprintf (stream, " * ");
656 write_java_expression (stream, exp->val.args[1], false);
657 fprintf (stream, ")");
660 fprintf (stream, "(");
661 write_java_expression (stream, exp->val.args[0], false);
662 fprintf (stream, " / ");
663 write_java_expression (stream, exp->val.args[1], false);
664 fprintf (stream, ")");
667 fprintf (stream, "(");
668 write_java_expression (stream, exp->val.args[0], false);
669 fprintf (stream, " %% ");
670 write_java_expression (stream, exp->val.args[1], false);
671 fprintf (stream, ")");
674 fprintf (stream, "(");
675 write_java_expression (stream, exp->val.args[0], false);
676 fprintf (stream, " + ");
677 write_java_expression (stream, exp->val.args[1], false);
678 fprintf (stream, ")");
681 fprintf (stream, "(");
682 write_java_expression (stream, exp->val.args[0], false);
683 fprintf (stream, " - ");
684 write_java_expression (stream, exp->val.args[1], false);
685 fprintf (stream, ")");
688 fprintf (stream, "(");
689 write_java_expression (stream, exp->val.args[0], true);
690 fprintf (stream, " ? ");
691 write_java_expression (stream, exp->val.args[1], false);
692 fprintf (stream, " : ");
693 write_java_expression (stream, exp->val.args[2], false);
694 fprintf (stream, ")");
700 case greater_or_equal:
705 fprintf (stream, "(");
706 write_java_expression (stream, exp, true);
707 fprintf (stream, " ? 1 : 0)");
716 /* Write the Java initialization statements for the Java 1.1.x case,
717 for items j, start_index <= j < end_index. */
719 write_java1_init_statements (FILE *stream, message_list_ty *mlp,
720 size_t start_index, size_t end_index)
724 for (j = start_index; j < end_index; j++)
726 fprintf (stream, " t.put(");
727 write_java_msgid (stream, mlp->item[j]);
728 fprintf (stream, ",");
729 write_java_msgstr (stream, mlp->item[j]);
730 fprintf (stream, ");\n");
735 /* Write the Java initialization statements for the Java 2 case,
736 for items j, start_index <= j < end_index. */
738 write_java2_init_statements (FILE *stream, message_list_ty *mlp,
739 const struct table_item *table_items,
740 size_t start_index, size_t end_index)
744 for (j = start_index; j < end_index; j++)
746 const struct table_item *ti = &table_items[j];
748 fprintf (stream, " t[%d] = ", 2 * ti->index);
749 write_java_msgid (stream, ti->mp);
750 fprintf (stream, ";\n");
751 fprintf (stream, " t[%d] = ", 2 * ti->index + 1);
752 write_java_msgstr (stream, ti->mp);
753 fprintf (stream, ";\n");
758 /* Write the Java code for the ResourceBundle subclass to the given stream.
759 Note that we use fully qualified class names and no "import" statements,
760 because applications can have their own classes called X.Y.ResourceBundle
763 write_java_code (FILE *stream, const char *class_name, message_list_ty *mlp,
766 const char *last_dot;
767 unsigned int plurals;
771 "/* Automatically generated by GNU msgfmt. Do not modify! */\n");
772 last_dot = strrchr (class_name, '.');
773 if (last_dot != NULL)
775 fprintf (stream, "package ");
776 fwrite (class_name, 1, last_dot - class_name, stream);
777 fprintf (stream, ";\npublic class %s", last_dot + 1);
780 fprintf (stream, "public class %s", class_name);
781 fprintf (stream, " extends java.util.ResourceBundle {\n");
783 /* Determine whether there are plural messages. */
785 for (j = 0; j < mlp->nitems; j++)
786 if (mlp->item[j]->msgid_plural != NULL)
791 unsigned int hashsize;
793 struct table_item *table_items;
794 const char *table_eltype;
796 /* Determine the hash table size and whether it leads to collisions. */
797 hashsize = compute_hashsize (mlp, &collisions);
799 /* Determines which indices in the table contain a message. The others
801 table_items = compute_table_items (mlp, hashsize);
803 /* Emit the table of pairs (msgid, msgstr). If there are plurals,
804 it is of type Object[], otherwise of type String[]. We use a static
805 code block because that makes less code: The Java compilers also
806 generate code for the 'null' entries, which is dumb. */
807 table_eltype = (plurals ? "java.lang.Object" : "java.lang.String");
808 fprintf (stream, " private static final %s[] table;\n", table_eltype);
810 /* With the Sun javac compiler, each assignment takes 5 to 8 bytes
811 of bytecode, therefore for each message, up to 16 bytes are needed.
812 Since the bytecode of every method, including the <clinit> method
813 that contains the static initializers, is limited to 64 KB, only ca,
814 65536 / 16 = 4096 messages can be initialized in a single method.
815 Account for other Java compilers and for plurals by limiting it to
817 const size_t max_items_per_method = 1000;
819 if (mlp->nitems > max_items_per_method)
825 for (k = 0, start_j = 0, end_j = start_j + max_items_per_method;
826 start_j < mlp->nitems;
827 k++, start_j = end_j, end_j = start_j + max_items_per_method)
829 fprintf (stream, " static void clinit_part_%u (%s[] t) {\n",
831 write_java2_init_statements (stream, mlp, table_items,
832 start_j, MIN (end_j, mlp->nitems));
833 fprintf (stream, " }\n");
836 fprintf (stream, " static {\n");
837 fprintf (stream, " %s[] t = new %s[%d];\n", table_eltype,
838 table_eltype, 2 * hashsize);
839 if (mlp->nitems > max_items_per_method)
844 for (k = 0, start_j = 0;
845 start_j < mlp->nitems;
846 k++, start_j += max_items_per_method)
847 fprintf (stream, " clinit_part_%u(t);\n", k);
850 write_java2_init_statements (stream, mlp, table_items,
852 fprintf (stream, " table = t;\n");
853 fprintf (stream, " }\n");
856 /* Emit the msgid_plural strings. Only used by msgunfmt. */
860 fprintf (stream, " public static final java.lang.String[] get_msgid_plural_table () {\n");
861 fprintf (stream, " return new java.lang.String[] { ");
863 for (j = 0; j < mlp->nitems; j++)
865 struct table_item *ti = &table_items[j];
866 if (ti->mp->msgid_plural != NULL)
869 fprintf (stream, ", ");
870 write_java_string (stream, ti->mp->msgid_plural);
874 fprintf (stream, " };\n");
875 fprintf (stream, " }\n");
880 /* Emit the lookup function. It is a common subroutine for
881 handleGetObject and ngettext. */
882 fprintf (stream, " public java.lang.Object lookup (java.lang.String msgid) {\n");
883 write_lookup_code (stream, hashsize, collisions);
884 fprintf (stream, " }\n");
887 /* Emit the handleGetObject function. It is declared abstract in
888 ResourceBundle. It implements a local version of gettext. */
889 fprintf (stream, " public java.lang.Object handleGetObject (java.lang.String msgid) throws java.util.MissingResourceException {\n");
892 fprintf (stream, " java.lang.Object value = lookup(msgid);\n");
893 fprintf (stream, " return (value instanceof java.lang.String[] ? ((java.lang.String[])value)[0] : value);\n");
896 write_lookup_code (stream, hashsize, collisions);
897 fprintf (stream, " }\n");
899 /* Emit the getKeys function. It is declared abstract in ResourceBundle.
900 The inner class is not avoidable. */
901 fprintf (stream, " public java.util.Enumeration getKeys () {\n");
902 fprintf (stream, " return\n");
903 fprintf (stream, " new java.util.Enumeration() {\n");
904 fprintf (stream, " private int idx = 0;\n");
905 fprintf (stream, " { while (idx < %d && table[idx] == null) idx += 2; }\n",
907 fprintf (stream, " public boolean hasMoreElements () {\n");
908 fprintf (stream, " return (idx < %d);\n", 2 * hashsize);
909 fprintf (stream, " }\n");
910 fprintf (stream, " public java.lang.Object nextElement () {\n");
911 fprintf (stream, " java.lang.Object key = table[idx];\n");
912 fprintf (stream, " do idx += 2; while (idx < %d && table[idx] == null);\n",
914 fprintf (stream, " return key;\n");
915 fprintf (stream, " }\n");
916 fprintf (stream, " };\n");
917 fprintf (stream, " }\n");
921 /* Java 1.1.x uses a different hash function. If compatibility with
922 this Java version is required, the hash table must be built at run time,
923 not at compile time. */
924 fprintf (stream, " private static final java.util.Hashtable table;\n");
926 /* With the Sun javac compiler, each 'put' call takes 9 to 11 bytes
927 of bytecode, therefore for each message, up to 11 bytes are needed.
928 Since the bytecode of every method, including the <clinit> method
929 that contains the static initializers, is limited to 64 KB, only ca,
930 65536 / 11 = 5958 messages can be initialized in a single method.
931 Account for other Java compilers and for plurals by limiting it to
933 const size_t max_items_per_method = 1500;
935 if (mlp->nitems > max_items_per_method)
941 for (k = 0, start_j = 0, end_j = start_j + max_items_per_method;
942 start_j < mlp->nitems;
943 k++, start_j = end_j, end_j = start_j + max_items_per_method)
945 fprintf (stream, " static void clinit_part_%u (java.util.Hashtable t) {\n",
947 write_java1_init_statements (stream, mlp,
948 start_j, MIN (end_j, mlp->nitems));
949 fprintf (stream, " }\n");
952 fprintf (stream, " static {\n");
953 fprintf (stream, " java.util.Hashtable t = new java.util.Hashtable();\n");
954 if (mlp->nitems > max_items_per_method)
959 for (k = 0, start_j = 0;
960 start_j < mlp->nitems;
961 k++, start_j += max_items_per_method)
962 fprintf (stream, " clinit_part_%u(t);\n", k);
965 write_java1_init_statements (stream, mlp, 0, mlp->nitems);
966 fprintf (stream, " table = t;\n");
967 fprintf (stream, " }\n");
970 /* Emit the msgid_plural strings. Only used by msgunfmt. */
973 fprintf (stream, " public static final java.util.Hashtable get_msgid_plural_table () {\n");
974 fprintf (stream, " java.util.Hashtable p = new java.util.Hashtable();\n");
975 for (j = 0; j < mlp->nitems; j++)
976 if (mlp->item[j]->msgid_plural != NULL)
978 fprintf (stream, " p.put(");
979 write_java_msgid (stream, mlp->item[j]);
980 fprintf (stream, ",");
981 write_java_string (stream, mlp->item[j]->msgid_plural);
982 fprintf (stream, ");\n");
984 fprintf (stream, " return p;\n");
985 fprintf (stream, " }\n");
990 /* Emit the lookup function. It is a common subroutine for
991 handleGetObject and ngettext. */
992 fprintf (stream, " public java.lang.Object lookup (java.lang.String msgid) {\n");
993 fprintf (stream, " return table.get(msgid);\n");
994 fprintf (stream, " }\n");
997 /* Emit the handleGetObject function. It is declared abstract in
998 ResourceBundle. It implements a local version of gettext. */
999 fprintf (stream, " public java.lang.Object handleGetObject (java.lang.String msgid) throws java.util.MissingResourceException {\n");
1002 fprintf (stream, " java.lang.Object value = table.get(msgid);\n");
1003 fprintf (stream, " return (value instanceof java.lang.String[] ? ((java.lang.String[])value)[0] : value);\n");
1006 fprintf (stream, " return table.get(msgid);\n");
1007 fprintf (stream, " }\n");
1009 /* Emit the getKeys function. It is declared abstract in
1011 fprintf (stream, " public java.util.Enumeration getKeys () {\n");
1012 fprintf (stream, " return table.keys();\n");
1013 fprintf (stream, " }\n");
1016 /* Emit the pluralEval function. It is a subroutine for ngettext. */
1019 message_ty *header_entry;
1020 const struct expression *plural;
1021 unsigned long int nplurals;
1023 header_entry = message_list_search (mlp, NULL, "");
1024 extract_plural_expression (header_entry ? header_entry->msgstr : NULL,
1025 &plural, &nplurals);
1027 fprintf (stream, " public static long pluralEval (long n) {\n");
1028 fprintf (stream, " return ");
1029 write_java_expression (stream, plural, false);
1030 fprintf (stream, ";\n");
1031 fprintf (stream, " }\n");
1034 /* Emit the getParent function. It is a subroutine for ngettext. */
1035 fprintf (stream, " public java.util.ResourceBundle getParent () {\n");
1036 fprintf (stream, " return parent;\n");
1037 fprintf (stream, " }\n");
1039 fprintf (stream, "}\n");
1044 msgdomain_write_java (message_list_ty *mlp, const char *canon_encoding,
1045 const char *resource_name, const char *locale_name,
1046 const char *directory,
1050 struct temp_dir *tmpdir;
1054 char *java_file_name;
1056 const char *java_sources[1];
1058 /* If no entry for this resource/domain, don't even create the file. */
1059 if (mlp->nitems == 0)
1064 /* Convert the messages to Unicode. */
1065 iconv_message_list (mlp, canon_encoding, po_charset_utf8, NULL);
1067 /* Create a temporary directory where we can put the Java file. */
1068 tmpdir = create_temp_dir ("msg", NULL, false);
1072 /* Assign a default value to the resource name. */
1073 if (resource_name == NULL)
1074 resource_name = "Messages";
1076 /* Prepare the list of subdirectories. */
1077 ndots = check_resource_name (resource_name);
1080 error (0, 0, _("not a valid Java class name: %s"), resource_name);
1084 if (locale_name != NULL)
1085 class_name = xasprintf ("%s_%s", resource_name, locale_name);
1087 class_name = xstrdup (resource_name);
1089 subdirs = (ndots > 0 ? (char **) xmalloca (ndots * sizeof (char *)) : NULL);
1092 const char *last_dir;
1095 last_dir = tmpdir->dir_name;
1097 for (i = 0; i < ndots; i++)
1099 const char *q = strchr (p, '.');
1101 char *part = (char *) xmalloca (n + 1);
1102 memcpy (part, p, n);
1104 subdirs[i] = xconcatenated_filename (last_dir, part, NULL);
1106 last_dir = subdirs[i];
1110 if (locale_name != NULL)
1112 char *suffix = xasprintf ("_%s.java", locale_name);
1113 java_file_name = xconcatenated_filename (last_dir, p, suffix);
1117 java_file_name = xconcatenated_filename (last_dir, p, ".java");
1120 /* Create the subdirectories. This is needed because some older Java
1121 compilers verify that the source of class A.B.C really sits in a
1122 directory whose name ends in /A/B. */
1126 for (i = 0; i < ndots; i++)
1128 register_temp_subdir (tmpdir, subdirs[i]);
1129 if (mkdir (subdirs[i], S_IRUSR | S_IWUSR | S_IXUSR) < 0)
1131 error (0, errno, _("failed to create \"%s\""), subdirs[i]);
1132 unregister_temp_subdir (tmpdir, subdirs[i]);
1138 /* Create the Java file. */
1139 register_temp_file (tmpdir, java_file_name);
1140 java_file = fopen_temp (java_file_name, "w");
1141 if (java_file == NULL)
1143 error (0, errno, _("failed to create \"%s\""), java_file_name);
1144 unregister_temp_file (tmpdir, java_file_name);
1148 write_java_code (java_file, class_name, mlp, assume_java2);
1150 if (fwriteerror_temp (java_file))
1152 error (0, errno, _("error while writing \"%s\" file"), java_file_name);
1156 /* Compile the Java file to a .class file.
1157 directory must be non-NULL, because when the -d option is omitted, the
1158 Java compilers create the class files in the source file's directory -
1159 which is in a temporary directory in our case. */
1160 java_sources[0] = java_file_name;
1161 if (compile_java_class (java_sources, 1, NULL, 0, "1.3", "1.1", directory,
1162 true, false, true, verbose > 0))
1166 compilation of Java class failed, please try --verbose or set $JAVAC"));
1169 compilation of Java class failed, please try to set $JAVAC"));
1178 free (java_file_name);
1179 for (i = 0; i < ndots; i++)
1185 cleanup_temp_dir (tmpdir);