• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-WNDR4500v2-V1.0.0.60_1.0.38/ap/gpl/timemachine/gettext-0.17/gettext-tools/src/
1/* Writing Java ResourceBundles.
2   Copyright (C) 2001-2003, 2005-2007 Free Software Foundation, Inc.
3   Written by Bruno Haible <haible@clisp.cons.org>, 2001.
4
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.
9
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.
14
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/>.  */
17
18#ifdef HAVE_CONFIG_H
19# include <config.h>
20#endif
21#include <alloca.h>
22
23/* Specification.  */
24#include "write-java.h"
25
26#include <errno.h>
27#include <limits.h>
28#include <stdbool.h>
29#include <stdlib.h>
30#include <stdio.h>
31#include <string.h>
32
33#include <sys/stat.h>
34#if !defined S_ISDIR && defined S_IFDIR
35# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
36#endif
37#if !S_IRUSR && S_IREAD
38# define S_IRUSR S_IREAD
39#endif
40#if !S_IRUSR
41# define S_IRUSR 00400
42#endif
43#if !S_IWUSR && S_IWRITE
44# define S_IWUSR S_IWRITE
45#endif
46#if !S_IWUSR
47# define S_IWUSR 00200
48#endif
49#if !S_IXUSR && S_IEXEC
50# define S_IXUSR S_IEXEC
51#endif
52#if !S_IXUSR
53# define S_IXUSR 00100
54#endif
55
56#include "c-ctype.h"
57#include "error.h"
58#include "xerror.h"
59#include "xvasprintf.h"
60#include "javacomp.h"
61#include "message.h"
62#include "msgfmt.h"
63#include "msgl-iconv.h"
64#include "plural-exp.h"
65#include "po-charset.h"
66#include "xalloc.h"
67#include "xmalloca.h"
68#include "filename.h"
69#include "fwriteerror.h"
70#include "clean-temp.h"
71#include "unistr.h"
72#include "gettext.h"
73
74#define _(str) gettext (str)
75
76
77/* Check that the resource name is a valid Java class name.  To simplify
78   things, we allow only ASCII characters in the class name.
79   Return the number of dots in the class name, or -1 if not OK.  */
80static int
81check_resource_name (const char *name)
82{
83  int ndots = 0;
84  const char *p = name;
85
86  for (;;)
87    {
88      /* First character, see Character.isJavaIdentifierStart.  */
89      if (!(c_isalpha (*p) || (*p == '$') || (*p == '_')))
90	return -1;
91      /* Following characters, see Character.isJavaIdentifierPart.  */
92      do
93	p++;
94      while (c_isalpha (*p) || (*p == '$') || (*p == '_') || c_isdigit (*p));
95      if (*p == '\0')
96	break;
97      if (*p != '.')
98	return -1;
99      p++;
100      ndots++;
101    }
102  return ndots;
103}
104
105
106/* Return the Java hash code of a string mod 2^31.
107   The Java String.hashCode() function returns the same values across
108   Java implementations.
109   (See http://www.javasoft.com/docs/books/jls/clarify.html)
110   It returns a signed 32-bit integer.  We add a mod 2^31 afterwards;
111   this removes one bit but greatly simplifies the following "mod hash_size"
112   and "mod (hash_size - 2)" operations.  */
113static unsigned int
114string_hashcode (const char *str)
115{
116  const char *str_limit = str + strlen (str);
117  int hash = 0;
118  while (str < str_limit)
119    {
120      unsigned int uc;
121      str += u8_mbtouc (&uc, (const unsigned char *) str, str_limit - str);
122      if (uc < 0x10000)
123	/* Single UCS-2 'char'.  */
124	hash = 31 * hash + uc;
125      else
126	{
127	  /* UTF-16 surrogate: two 'char's.  */
128	  unsigned int uc1 = 0xd800 + ((uc - 0x10000) >> 10);
129	  unsigned int uc2 = 0xdc00 + ((uc - 0x10000) & 0x3ff);
130	  hash = 31 * hash + uc1;
131	  hash = 31 * hash + uc2;
132	}
133    }
134  return hash & 0x7fffffff;
135}
136
137
138/* Return the Java hash code of a (msgctxt, msgid) pair mod 2^31.  */
139static unsigned int
140msgid_hashcode (const char *msgctxt, const char *msgid)
141{
142  if (msgctxt == NULL)
143    return string_hashcode (msgid);
144  else
145    {
146      size_t msgctxt_len = strlen (msgctxt);
147      size_t msgid_len = strlen (msgid);
148      size_t combined_len = msgctxt_len + 1 + msgid_len;
149      char *combined;
150      unsigned int result;
151
152      combined = (char *) xmalloca (combined_len);
153      memcpy (combined, msgctxt, msgctxt_len);
154      combined[msgctxt_len] = MSGCTXT_SEPARATOR;
155      memcpy (combined + msgctxt_len + 1, msgid, msgid_len + 1);
156
157      result = string_hashcode (combined);
158
159      freea (combined);
160
161      return result;
162    }
163}
164
165
166/* Compute a good hash table size for the given set of msgids.  */
167static unsigned int
168compute_hashsize (message_list_ty *mlp, bool *collisionp)
169{
170  /* This is an O(n^2) algorithm, but should be sufficient because few
171     programs have more than 1000 messages in a single domain.  */
172#define XXN 3  /* can be tweaked */
173#define XXS 3  /* can be tweaked */
174  unsigned int n = mlp->nitems;
175  unsigned int *hashcodes =
176    (unsigned int *) xmalloca (n * sizeof (unsigned int));
177  unsigned int hashsize;
178  unsigned int best_hashsize;
179  unsigned int best_score;
180  size_t j;
181
182  for (j = 0; j < n; j++)
183    hashcodes[j] = msgid_hashcode (mlp->item[j]->msgctxt, mlp->item[j]->msgid);
184
185  /* Try all numbers between n and 3*n.  The score depends on the size of the
186     table -- the smaller the better -- and the number of collision lookups,
187     i.e. total number of times that 1 + (hashcode % (hashsize - 2))
188     is added to the index during lookup.  If there are collisions, only odd
189     hashsize values are allowed.  */
190  best_hashsize = 0;
191  best_score = UINT_MAX;
192  for (hashsize = n; hashsize <= XXN * n; hashsize++)
193    {
194      char *bitmap;
195      unsigned int score;
196
197      /* Premature end of the loop if all future scores are known to be
198	 larger than the already reached best_score.  This relies on the
199	 ascending loop and on the fact that score >= hashsize.  */
200      if (hashsize >= best_score)
201	break;
202
203      bitmap = XNMALLOC (hashsize, char);
204      memset (bitmap, 0, hashsize);
205
206      score = 0;
207      for (j = 0; j < n; j++)
208	{
209	  unsigned int idx = hashcodes[j] % hashsize;
210
211	  if (bitmap[idx] != 0)
212	    {
213	      /* Collision.  Cannot deal with it if hashsize is even.  */
214	      if ((hashsize % 2) == 0)
215		/* Try next hashsize.  */
216		goto bad_hashsize;
217	      else
218		{
219		  unsigned int idx0 = idx;
220		  unsigned int incr = 1 + (hashcodes[j] % (hashsize - 2));
221		  score += 2;	/* Big penalty for the additional division */
222		  do
223		    {
224		      score++;	/* Small penalty for each loop round */
225		      idx += incr;
226		      if (idx >= hashsize)
227			idx -= hashsize;
228		      if (idx == idx0)
229			/* Searching for a hole, we performed a whole round
230			   across the table.  This happens particularly
231			   frequently if gcd(hashsize,incr) > 1.  Try next
232			   hashsize.  */
233			goto bad_hashsize;
234		    }
235		  while (bitmap[idx] != 0);
236		}
237	    }
238	  bitmap[idx] = 1;
239	}
240
241      /* Big hashsize also gives a penalty.  */
242      score = XXS * score + hashsize;
243
244      /* If for any incr between 1 and hashsize - 2, an whole round
245	 (idx0, idx0 + incr, ...) is occupied, and the lookup function
246	 must deal with collisions, then some inputs would lead to
247	 an endless loop in the lookup function.  */
248      if (score > hashsize)
249	{
250	  unsigned int incr;
251
252	  /* Since the set { idx0, idx0 + incr, ... } depends only on idx0
253	     and gcd(hashsize,incr), we only need to conside incr that
254	     divides hashsize.  */
255	  for (incr = 1; incr <= hashsize / 2; incr++)
256	    if ((hashsize % incr) == 0)
257	      {
258		unsigned int idx0;
259
260		for (idx0 = 0; idx0 < incr; idx0++)
261		  {
262		    bool full = true;
263		    unsigned int idx;
264
265		    for (idx = idx0; idx < hashsize; idx += incr)
266		      if (bitmap[idx] == 0)
267			{
268			  full = false;
269			  break;
270			}
271		    if (full)
272		      /* A whole round is occupied.  */
273		      goto bad_hashsize;
274		  }
275	      }
276	}
277
278      if (false)
279	bad_hashsize:
280	score = UINT_MAX;
281
282      free (bitmap);
283
284      if (score < best_score)
285	{
286	  best_score = score;
287	  best_hashsize = hashsize;
288	}
289    }
290  if (best_hashsize == 0 || best_score < best_hashsize)
291    abort ();
292
293  freea (hashcodes);
294
295  /* There are collisions if and only if best_score > best_hashsize.  */
296  *collisionp = (best_score > best_hashsize);
297  return best_hashsize;
298}
299
300
301struct table_item { unsigned int index; message_ty *mp; };
302
303static int
304compare_index (const void *pval1, const void *pval2)
305{
306  return (int)((const struct table_item *) pval1)->index
307	 - (int)((const struct table_item *) pval2)->index;
308}
309
310/* Compute the list of messages and table indices, sorted according to the
311   indices.  */
312static struct table_item *
313compute_table_items (message_list_ty *mlp, unsigned int hashsize)
314{
315  unsigned int n = mlp->nitems;
316  struct table_item *arr = XNMALLOC (n, struct table_item);
317  char *bitmap;
318  size_t j;
319
320  bitmap = XNMALLOC (hashsize, char);
321  memset (bitmap, 0, hashsize);
322
323  for (j = 0; j < n; j++)
324    {
325      unsigned int hashcode =
326	msgid_hashcode (mlp->item[j]->msgctxt, mlp->item[j]->msgid);
327      unsigned int idx = hashcode % hashsize;
328
329      if (bitmap[idx] != 0)
330	{
331	  unsigned int incr = 1 + (hashcode % (hashsize - 2));
332	  do
333	    {
334	      idx += incr;
335	      if (idx >= hashsize)
336		idx -= hashsize;
337	    }
338	  while (bitmap[idx] != 0);
339	}
340      bitmap[idx] = 1;
341
342      arr[j].index = idx;
343      arr[j].mp = mlp->item[j];
344    }
345
346  free (bitmap);
347
348  qsort (arr, n, sizeof (arr[0]), compare_index);
349
350  return arr;
351}
352
353
354/* Write a string in Java Unicode notation to the given stream.  */
355static void
356write_java_string (FILE *stream, const char *str)
357{
358  static const char hexdigit[] = "0123456789abcdef";
359  const char *str_limit = str + strlen (str);
360
361  fprintf (stream, "\"");
362  while (str < str_limit)
363    {
364      unsigned int uc;
365      str += u8_mbtouc (&uc, (const unsigned char *) str, str_limit - str);
366      if (uc < 0x10000)
367	{
368	  /* Single UCS-2 'char'.  */
369	  if (uc == 0x000a)
370	    fprintf (stream, "\\n");
371	  else if (uc == 0x000d)
372	    fprintf (stream, "\\r");
373	  else if (uc == 0x0022)
374	    fprintf (stream, "\\\"");
375	  else if (uc == 0x005c)
376	    fprintf (stream, "\\\\");
377	  else if (uc >= 0x0020 && uc < 0x007f)
378	    fprintf (stream, "%c", uc);
379	  else
380	    fprintf (stream, "\\u%c%c%c%c",
381		     hexdigit[(uc >> 12) & 0x0f], hexdigit[(uc >> 8) & 0x0f],
382		     hexdigit[(uc >> 4) & 0x0f], hexdigit[uc & 0x0f]);
383	}
384      else
385	{
386	  /* UTF-16 surrogate: two 'char's.  */
387	  unsigned int uc1 = 0xd800 + ((uc - 0x10000) >> 10);
388	  unsigned int uc2 = 0xdc00 + ((uc - 0x10000) & 0x3ff);
389	  fprintf (stream, "\\u%c%c%c%c",
390		   hexdigit[(uc1 >> 12) & 0x0f], hexdigit[(uc1 >> 8) & 0x0f],
391		   hexdigit[(uc1 >> 4) & 0x0f], hexdigit[uc1 & 0x0f]);
392	  fprintf (stream, "\\u%c%c%c%c",
393		   hexdigit[(uc2 >> 12) & 0x0f], hexdigit[(uc2 >> 8) & 0x0f],
394		   hexdigit[(uc2 >> 4) & 0x0f], hexdigit[uc2 & 0x0f]);
395	}
396    }
397  fprintf (stream, "\"");
398}
399
400
401/* Write a (msgctxt, msgid) pair as a string in Java Unicode notation to the
402   given stream.  */
403static void
404write_java_msgid (FILE *stream, message_ty *mp)
405{
406  const char *msgctxt = mp->msgctxt;
407  const char *msgid = mp->msgid;
408
409  if (msgctxt == NULL)
410    write_java_string (stream, msgid);
411  else
412    {
413      size_t msgctxt_len = strlen (msgctxt);
414      size_t msgid_len = strlen (msgid);
415      size_t combined_len = msgctxt_len + 1 + msgid_len;
416      char *combined;
417
418      combined = (char *) xmalloca (combined_len);
419      memcpy (combined, msgctxt, msgctxt_len);
420      combined[msgctxt_len] = MSGCTXT_SEPARATOR;
421      memcpy (combined + msgctxt_len + 1, msgid, msgid_len + 1);
422
423      write_java_string (stream, combined);
424
425      freea (combined);
426    }
427}
428
429
430/* Write Java code that returns the value for a message.  If the message
431   has plural forms, it is an expression of type String[], otherwise it is
432   an expression of type String.  */
433static void
434write_java_msgstr (FILE *stream, message_ty *mp)
435{
436  if (mp->msgid_plural != NULL)
437    {
438      bool first;
439      const char *p;
440
441      fprintf (stream, "new java.lang.String[] { ");
442      for (p = mp->msgstr, first = true;
443	   p < mp->msgstr + mp->msgstr_len;
444	   p += strlen (p) + 1, first = false)
445	{
446	  if (!first)
447	    fprintf (stream, ", ");
448	  write_java_string (stream, p);
449	}
450      fprintf (stream, " }");
451    }
452  else
453    {
454      if (mp->msgstr_len != strlen (mp->msgstr) + 1)
455	abort ();
456
457      write_java_string (stream, mp->msgstr);
458    }
459}
460
461
462/* Writes the body of the function which returns the local value for a key
463   named 'msgid'.  */
464static void
465write_lookup_code (FILE *stream, unsigned int hashsize, bool collisions)
466{
467  fprintf (stream, "    int hash_val = msgid.hashCode() & 0x7fffffff;\n");
468  fprintf (stream, "    int idx = (hash_val %% %d) << 1;\n", hashsize);
469  if (collisions)
470    {
471      fprintf (stream, "    {\n");
472      fprintf (stream, "      java.lang.Object found = table[idx];\n");
473      fprintf (stream, "      if (found == null)\n");
474      fprintf (stream, "        return null;\n");
475      fprintf (stream, "      if (msgid.equals(found))\n");
476      fprintf (stream, "        return table[idx + 1];\n");
477      fprintf (stream, "    }\n");
478      fprintf (stream, "    int incr = ((hash_val %% %d) + 1) << 1;\n",
479	       hashsize - 2);
480      fprintf (stream, "    for (;;) {\n");
481      fprintf (stream, "      idx += incr;\n");
482      fprintf (stream, "      if (idx >= %d)\n", 2 * hashsize);
483      fprintf (stream, "        idx -= %d;\n", 2 * hashsize);
484      fprintf (stream, "      java.lang.Object found = table[idx];\n");
485      fprintf (stream, "      if (found == null)\n");
486      fprintf (stream, "        return null;\n");
487      fprintf (stream, "      if (msgid.equals(found))\n");
488      fprintf (stream, "        return table[idx + 1];\n");
489      fprintf (stream, "    }\n");
490    }
491  else
492    {
493      fprintf (stream, "    java.lang.Object found = table[idx];\n");
494      fprintf (stream, "    if (found != null && msgid.equals(found))\n");
495      fprintf (stream, "      return table[idx + 1];\n");
496      fprintf (stream, "    return null;\n");
497    }
498}
499
500
501/* Tests whether a plural expression, evaluated according to the C rules,
502   can only produce the values 0 and 1.  */
503static bool
504is_expression_boolean (struct expression *exp)
505{
506  switch (exp->operation)
507    {
508    case var:
509    case mult:
510    case divide:
511    case module:
512    case plus:
513    case minus:
514      return false;
515    case lnot:
516    case less_than:
517    case greater_than:
518    case less_or_equal:
519    case greater_or_equal:
520    case equal:
521    case not_equal:
522    case land:
523    case lor:
524      return true;
525    case num:
526      return (exp->val.num == 0 || exp->val.num == 1);
527    case qmop:
528      return is_expression_boolean (exp->val.args[1])
529	     && is_expression_boolean (exp->val.args[2]);
530    default:
531      abort ();
532    }
533}
534
535
536/* Write Java code that evaluates a plural expression according to the C rules.
537   The variable is called 'n'.  */
538static void
539write_java_expression (FILE *stream, const struct expression *exp, bool as_boolean)
540{
541  /* We use parentheses everywhere.  This frees us from tracking the priority
542     of arithmetic operators.  */
543  if (as_boolean)
544    {
545      /* Emit a Java expression of type 'boolean'.  */
546      switch (exp->operation)
547	{
548	case num:
549	  fprintf (stream, "%s", exp->val.num ? "true" : "false");
550	  return;
551	case lnot:
552	  fprintf (stream, "(!");
553	  write_java_expression (stream, exp->val.args[0], true);
554	  fprintf (stream, ")");
555	  return;
556	case less_than:
557	  fprintf (stream, "(");
558	  write_java_expression (stream, exp->val.args[0], false);
559	  fprintf (stream, " < ");
560	  write_java_expression (stream, exp->val.args[1], false);
561	  fprintf (stream, ")");
562	  return;
563	case greater_than:
564	  fprintf (stream, "(");
565	  write_java_expression (stream, exp->val.args[0], false);
566	  fprintf (stream, " > ");
567	  write_java_expression (stream, exp->val.args[1], false);
568	  fprintf (stream, ")");
569	  return;
570	case less_or_equal:
571	  fprintf (stream, "(");
572	  write_java_expression (stream, exp->val.args[0], false);
573	  fprintf (stream, " <= ");
574	  write_java_expression (stream, exp->val.args[1], false);
575	  fprintf (stream, ")");
576	  return;
577	case greater_or_equal:
578	  fprintf (stream, "(");
579	  write_java_expression (stream, exp->val.args[0], false);
580	  fprintf (stream, " >= ");
581	  write_java_expression (stream, exp->val.args[1], false);
582	  fprintf (stream, ")");
583	  return;
584	case equal:
585	  fprintf (stream, "(");
586	  write_java_expression (stream, exp->val.args[0], false);
587	  fprintf (stream, " == ");
588	  write_java_expression (stream, exp->val.args[1], false);
589	  fprintf (stream, ")");
590	  return;
591	case not_equal:
592	  fprintf (stream, "(");
593	  write_java_expression (stream, exp->val.args[0], false);
594	  fprintf (stream, " != ");
595	  write_java_expression (stream, exp->val.args[1], false);
596	  fprintf (stream, ")");
597	  return;
598	case land:
599	  fprintf (stream, "(");
600	  write_java_expression (stream, exp->val.args[0], true);
601	  fprintf (stream, " && ");
602	  write_java_expression (stream, exp->val.args[1], true);
603	  fprintf (stream, ")");
604	  return;
605	case lor:
606	  fprintf (stream, "(");
607	  write_java_expression (stream, exp->val.args[0], true);
608	  fprintf (stream, " || ");
609	  write_java_expression (stream, exp->val.args[1], true);
610	  fprintf (stream, ")");
611	  return;
612	case qmop:
613	  if (is_expression_boolean (exp->val.args[1])
614	      && is_expression_boolean (exp->val.args[2]))
615	    {
616	      fprintf (stream, "(");
617	      write_java_expression (stream, exp->val.args[0], true);
618	      fprintf (stream, " ? ");
619	      write_java_expression (stream, exp->val.args[1], true);
620	      fprintf (stream, " : ");
621	      write_java_expression (stream, exp->val.args[2], true);
622	      fprintf (stream, ")");
623	      return;
624	    }
625	  /*FALLTHROUGH*/
626	case var:
627	case mult:
628	case divide:
629	case module:
630	case plus:
631	case minus:
632	  fprintf (stream, "(");
633	  write_java_expression (stream, exp, false);
634	  fprintf (stream, " != 0)");
635	  return;
636	default:
637	  abort ();
638	}
639    }
640  else
641    {
642      /* Emit a Java expression of type 'long'.  */
643      switch (exp->operation)
644	{
645	case var:
646	  fprintf (stream, "n");
647	  return;
648	case num:
649	  fprintf (stream, "%lu", exp->val.num);
650	  return;
651	case mult:
652	  fprintf (stream, "(");
653	  write_java_expression (stream, exp->val.args[0], false);
654	  fprintf (stream, " * ");
655	  write_java_expression (stream, exp->val.args[1], false);
656	  fprintf (stream, ")");
657	  return;
658	case divide:
659	  fprintf (stream, "(");
660	  write_java_expression (stream, exp->val.args[0], false);
661	  fprintf (stream, " / ");
662	  write_java_expression (stream, exp->val.args[1], false);
663	  fprintf (stream, ")");
664	  return;
665	case module:
666	  fprintf (stream, "(");
667	  write_java_expression (stream, exp->val.args[0], false);
668	  fprintf (stream, " %% ");
669	  write_java_expression (stream, exp->val.args[1], false);
670	  fprintf (stream, ")");
671	  return;
672	case plus:
673	  fprintf (stream, "(");
674	  write_java_expression (stream, exp->val.args[0], false);
675	  fprintf (stream, " + ");
676	  write_java_expression (stream, exp->val.args[1], false);
677	  fprintf (stream, ")");
678	  return;
679	case minus:
680	  fprintf (stream, "(");
681	  write_java_expression (stream, exp->val.args[0], false);
682	  fprintf (stream, " - ");
683	  write_java_expression (stream, exp->val.args[1], false);
684	  fprintf (stream, ")");
685	  return;
686	case qmop:
687	  fprintf (stream, "(");
688	  write_java_expression (stream, exp->val.args[0], true);
689	  fprintf (stream, " ? ");
690	  write_java_expression (stream, exp->val.args[1], false);
691	  fprintf (stream, " : ");
692	  write_java_expression (stream, exp->val.args[2], false);
693	  fprintf (stream, ")");
694	  return;
695	case lnot:
696	case less_than:
697	case greater_than:
698	case less_or_equal:
699	case greater_or_equal:
700	case equal:
701	case not_equal:
702	case land:
703	case lor:
704	  fprintf (stream, "(");
705	  write_java_expression (stream, exp, true);
706	  fprintf (stream, " ? 1 : 0)");
707	  return;
708	default:
709	  abort ();
710	}
711    }
712}
713
714
715/* Write the Java code for the ResourceBundle subclass to the given stream.
716   Note that we use fully qualified class names and no "import" statements,
717   because applications can have their own classes called X.Y.ResourceBundle
718   or X.Y.String.  */
719static void
720write_java_code (FILE *stream, const char *class_name, message_list_ty *mlp,
721		 bool assume_java2)
722{
723  const char *last_dot;
724  unsigned int plurals;
725  size_t j;
726
727  fprintf (stream,
728	   "/* Automatically generated by GNU msgfmt.  Do not modify!  */\n");
729  last_dot = strrchr (class_name, '.');
730  if (last_dot != NULL)
731    {
732      fprintf (stream, "package ");
733      fwrite (class_name, 1, last_dot - class_name, stream);
734      fprintf (stream, ";\npublic class %s", last_dot + 1);
735    }
736  else
737    fprintf (stream, "public class %s", class_name);
738  fprintf (stream, " extends java.util.ResourceBundle {\n");
739
740  /* Determine whether there are plural messages.  */
741  plurals = 0;
742  for (j = 0; j < mlp->nitems; j++)
743    if (mlp->item[j]->msgid_plural != NULL)
744      plurals++;
745
746  if (assume_java2)
747    {
748      unsigned int hashsize;
749      bool collisions;
750      struct table_item *table_items;
751      const char *table_eltype;
752
753      /* Determine the hash table size and whether it leads to collisions.  */
754      hashsize = compute_hashsize (mlp, &collisions);
755
756      /* Determines which indices in the table contain a message.  The others
757	 are null.  */
758      table_items = compute_table_items (mlp, hashsize);
759
760      /* Emit the table of pairs (msgid, msgstr).  If there are plurals,
761	 it is of type Object[], otherwise of type String[].  We use a static
762	 code block because that makes less code:  The Java compilers also
763	 generate code for the 'null' entries, which is dumb.  */
764      table_eltype = (plurals ? "java.lang.Object" : "java.lang.String");
765      fprintf (stream, "  private static final %s[] table;\n", table_eltype);
766      fprintf (stream, "  static {\n");
767      fprintf (stream, "    %s[] t = new %s[%d];\n", table_eltype, table_eltype,
768	       2 * hashsize);
769      for (j = 0; j < mlp->nitems; j++)
770	{
771	  struct table_item *ti = &table_items[j];
772
773	  fprintf (stream, "    t[%d] = ", 2 * ti->index);
774	  write_java_msgid (stream, ti->mp);
775	  fprintf (stream, ";\n");
776	  fprintf (stream, "    t[%d] = ", 2 * ti->index + 1);
777	  write_java_msgstr (stream, ti->mp);
778	  fprintf (stream, ";\n");
779	}
780      fprintf (stream, "    table = t;\n");
781      fprintf (stream, "  }\n");
782
783      /* Emit the msgid_plural strings.  Only used by msgunfmt.  */
784      if (plurals)
785	{
786	  bool first;
787	  fprintf (stream, "  public static final java.lang.String[] get_msgid_plural_table () {\n");
788	  fprintf (stream, "    return new java.lang.String[] { ");
789	  first = true;
790	  for (j = 0; j < mlp->nitems; j++)
791	    {
792	      struct table_item *ti = &table_items[j];
793	      if (ti->mp->msgid_plural != NULL)
794		{
795		  if (!first)
796		    fprintf (stream, ", ");
797		  write_java_string (stream, ti->mp->msgid_plural);
798		  first = false;
799		}
800	    }
801	  fprintf (stream, " };\n");
802	  fprintf (stream, "  }\n");
803	}
804
805      if (plurals)
806	{
807	  /* Emit the lookup function.  It is a common subroutine for
808	     handleGetObject and ngettext.  */
809	  fprintf (stream, "  public java.lang.Object lookup (java.lang.String msgid) {\n");
810	  write_lookup_code (stream, hashsize, collisions);
811	  fprintf (stream, "  }\n");
812	}
813
814      /* Emit the handleGetObject function.  It is declared abstract in
815	 ResourceBundle.  It implements a local version of gettext.  */
816      fprintf (stream, "  public java.lang.Object handleGetObject (java.lang.String msgid) throws java.util.MissingResourceException {\n");
817      if (plurals)
818	{
819	  fprintf (stream, "    java.lang.Object value = lookup(msgid);\n");
820	  fprintf (stream, "    return (value instanceof java.lang.String[] ? ((java.lang.String[])value)[0] : value);\n");
821	}
822      else
823	write_lookup_code (stream, hashsize, collisions);
824      fprintf (stream, "  }\n");
825
826      /* Emit the getKeys function.  It is declared abstract in ResourceBundle.
827	 The inner class is not avoidable.  */
828      fprintf (stream, "  public java.util.Enumeration getKeys () {\n");
829      fprintf (stream, "    return\n");
830      fprintf (stream, "      new java.util.Enumeration() {\n");
831      fprintf (stream, "        private int idx = 0;\n");
832      fprintf (stream, "        { while (idx < %d && table[idx] == null) idx += 2; }\n",
833	       2 * hashsize);
834      fprintf (stream, "        public boolean hasMoreElements () {\n");
835      fprintf (stream, "          return (idx < %d);\n", 2 * hashsize);
836      fprintf (stream, "        }\n");
837      fprintf (stream, "        public java.lang.Object nextElement () {\n");
838      fprintf (stream, "          java.lang.Object key = table[idx];\n");
839      fprintf (stream, "          do idx += 2; while (idx < %d && table[idx] == null);\n",
840	       2 * hashsize);
841      fprintf (stream, "          return key;\n");
842      fprintf (stream, "        }\n");
843      fprintf (stream, "      };\n");
844      fprintf (stream, "  }\n");
845    }
846  else
847    {
848      /* Java 1.1.x uses a different hash function.  If compatibility with
849	 this Java version is required, the hash table must be built at run time,
850	 not at compile time.  */
851      fprintf (stream, "  private static final java.util.Hashtable table;\n");
852      fprintf (stream, "  static {\n");
853      fprintf (stream, "    java.util.Hashtable t = new java.util.Hashtable();\n");
854      for (j = 0; j < mlp->nitems; j++)
855	{
856	  fprintf (stream, "    t.put(");
857	  write_java_msgid (stream, mlp->item[j]);
858	  fprintf (stream, ",");
859	  write_java_msgstr (stream, mlp->item[j]);
860	  fprintf (stream, ");\n");
861	}
862      fprintf (stream, "    table = t;\n");
863      fprintf (stream, "  }\n");
864
865      /* Emit the msgid_plural strings.  Only used by msgunfmt.  */
866      if (plurals)
867	{
868	  fprintf (stream, "  public static final java.util.Hashtable get_msgid_plural_table () {\n");
869	  fprintf (stream, "    java.util.Hashtable p = new java.util.Hashtable();\n");
870	  for (j = 0; j < mlp->nitems; j++)
871	    if (mlp->item[j]->msgid_plural != NULL)
872	      {
873		fprintf (stream, "    p.put(");
874		write_java_msgid (stream, mlp->item[j]);
875		fprintf (stream, ",");
876		write_java_string (stream, mlp->item[j]->msgid_plural);
877		fprintf (stream, ");\n");
878	      }
879	  fprintf (stream, "    return p;\n");
880	  fprintf (stream, "  }\n");
881	}
882
883      if (plurals)
884	{
885	  /* Emit the lookup function.  It is a common subroutine for
886	     handleGetObject and ngettext.  */
887	  fprintf (stream, "  public java.lang.Object lookup (java.lang.String msgid) {\n");
888	  fprintf (stream, "    return table.get(msgid);\n");
889	  fprintf (stream, "  }\n");
890	}
891
892      /* Emit the handleGetObject function.  It is declared abstract in
893	 ResourceBundle.  It implements a local version of gettext.  */
894      fprintf (stream, "  public java.lang.Object handleGetObject (java.lang.String msgid) throws java.util.MissingResourceException {\n");
895      if (plurals)
896	{
897	  fprintf (stream, "    java.lang.Object value = table.get(msgid);\n");
898	  fprintf (stream, "    return (value instanceof java.lang.String[] ? ((java.lang.String[])value)[0] : value);\n");
899	}
900      else
901	fprintf (stream, "    return table.get(msgid);\n");
902      fprintf (stream, "  }\n");
903
904      /* Emit the getKeys function.  It is declared abstract in
905	 ResourceBundle.  */
906      fprintf (stream, "  public java.util.Enumeration getKeys () {\n");
907      fprintf (stream, "    return table.keys();\n");
908      fprintf (stream, "  }\n");
909    }
910
911  /* Emit the pluralEval function.  It is a subroutine for ngettext.  */
912  if (plurals)
913    {
914      message_ty *header_entry;
915      const struct expression *plural;
916      unsigned long int nplurals;
917
918      header_entry = message_list_search (mlp, NULL, "");
919      extract_plural_expression (header_entry ? header_entry->msgstr : NULL,
920				 &plural, &nplurals);
921
922      fprintf (stream, "  public static long pluralEval (long n) {\n");
923      fprintf (stream, "    return ");
924      write_java_expression (stream, plural, false);
925      fprintf (stream, ";\n");
926      fprintf (stream, "  }\n");
927    }
928
929  /* Emit the getParent function.  It is a subroutine for ngettext.  */
930  fprintf (stream, "  public java.util.ResourceBundle getParent () {\n");
931  fprintf (stream, "    return parent;\n");
932  fprintf (stream, "  }\n");
933
934  fprintf (stream, "}\n");
935}
936
937
938int
939msgdomain_write_java (message_list_ty *mlp, const char *canon_encoding,
940		      const char *resource_name, const char *locale_name,
941		      const char *directory,
942		      bool assume_java2)
943{
944  int retval;
945  struct temp_dir *tmpdir;
946  int ndots;
947  char *class_name;
948  char **subdirs;
949  char *java_file_name;
950  FILE *java_file;
951  const char *java_sources[1];
952
953  /* If no entry for this resource/domain, don't even create the file.  */
954  if (mlp->nitems == 0)
955    return 0;
956
957  retval = 1;
958
959  /* Convert the messages to Unicode.  */
960  iconv_message_list (mlp, canon_encoding, po_charset_utf8, NULL);
961
962  /* Create a temporary directory where we can put the Java file.  */
963  tmpdir = create_temp_dir ("msg", NULL, false);
964  if (tmpdir == NULL)
965    goto quit1;
966
967  /* Assign a default value to the resource name.  */
968  if (resource_name == NULL)
969    resource_name = "Messages";
970
971  /* Prepare the list of subdirectories.  */
972  ndots = check_resource_name (resource_name);
973  if (ndots < 0)
974    {
975      error (0, 0, _("not a valid Java class name: %s"), resource_name);
976      goto quit2;
977    }
978
979  if (locale_name != NULL)
980    class_name = xasprintf ("%s_%s", resource_name, locale_name);
981  else
982    class_name = xstrdup (resource_name);
983
984  subdirs = (ndots > 0 ? (char **) xmalloca (ndots * sizeof (char *)) : NULL);
985  {
986    const char *p;
987    const char *last_dir;
988    int i;
989
990    last_dir = tmpdir->dir_name;
991    p = resource_name;
992    for (i = 0; i < ndots; i++)
993      {
994	const char *q = strchr (p, '.');
995	size_t n = q - p;
996	char *part = (char *) xmalloca (n + 1);
997	memcpy (part, p, n);
998	part[n] = '\0';
999	subdirs[i] = concatenated_filename (last_dir, part, NULL);
1000	freea (part);
1001	last_dir = subdirs[i];
1002	p = q + 1;
1003      }
1004
1005    if (locale_name != NULL)
1006      {
1007	char *suffix = xasprintf ("_%s.java", locale_name);
1008	java_file_name = concatenated_filename (last_dir, p, suffix);
1009	free (suffix);
1010      }
1011    else
1012      java_file_name = concatenated_filename (last_dir, p, ".java");
1013  }
1014
1015  /* Create the subdirectories.  This is needed because some older Java
1016     compilers verify that the source of class A.B.C really sits in a
1017     directory whose name ends in /A/B.  */
1018  {
1019    int i;
1020
1021    for (i = 0; i < ndots; i++)
1022      {
1023	register_temp_subdir (tmpdir, subdirs[i]);
1024	if (mkdir (subdirs[i], S_IRUSR | S_IWUSR | S_IXUSR) < 0)
1025	  {
1026	    error (0, errno, _("failed to create \"%s\""), subdirs[i]);
1027	    unregister_temp_subdir (tmpdir, subdirs[i]);
1028	    goto quit3;
1029	  }
1030      }
1031  }
1032
1033  /* Create the Java file.  */
1034  register_temp_file (tmpdir, java_file_name);
1035  java_file = fopen_temp (java_file_name, "w");
1036  if (java_file == NULL)
1037    {
1038      error (0, errno, _("failed to create \"%s\""), java_file_name);
1039      unregister_temp_file (tmpdir, java_file_name);
1040      goto quit3;
1041    }
1042
1043  write_java_code (java_file, class_name, mlp, assume_java2);
1044
1045  if (fwriteerror_temp (java_file))
1046    {
1047      error (0, errno, _("error while writing \"%s\" file"), java_file_name);
1048      goto quit3;
1049    }
1050
1051  /* Compile the Java file to a .class file.
1052     directory must be non-NULL, because when the -d option is omitted, the
1053     Java compilers create the class files in the source file's directory -
1054     which is in a temporary directory in our case.  */
1055  java_sources[0] = java_file_name;
1056  if (compile_java_class (java_sources, 1, NULL, 0, "1.3", "1.1", directory,
1057			  true, false, true, verbose))
1058    {
1059      if (!verbose)
1060	error (0, 0, _("\
1061compilation of Java class failed, please try --verbose or set $JAVAC"));
1062      else
1063	error (0, 0, _("\
1064compilation of Java class failed, please try to set $JAVAC"));
1065      goto quit3;
1066    }
1067
1068  retval = 0;
1069
1070 quit3:
1071  {
1072    int i;
1073    free (java_file_name);
1074    for (i = 0; i < ndots; i++)
1075      free (subdirs[i]);
1076  }
1077  freea (subdirs);
1078  free (class_name);
1079 quit2:
1080  cleanup_temp_dir (tmpdir);
1081 quit1:
1082  return retval;
1083}
1084