KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > caucho > xsl > XslNumberFormat


1 /*
2  * Copyright (c) 1998-2006 Caucho Technology -- all rights reserved
3  *
4  * This file is part of Resin(R) Open Source
5  *
6  * Each copy or derived work must preserve the copyright notice and this
7  * notice unmodified.
8  *
9  * Resin Open Source is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * Resin Open Source is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
17  * of NON-INFRINGEMENT. See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Resin Open Source; if not, write to the
22  * Free SoftwareFoundation, Inc.
23  * 59 Temple Place, Suite 330
24  * Boston, MA 02111-1307 USA
25  *
26  * @author Scott Ferguson
27  */

28
29 package com.caucho.xsl;
30
31 import com.caucho.util.CharBuffer;
32 import com.caucho.util.IntArray;
33
34 import java.util.ArrayList JavaDoc;
35
36 /**
37  * Formatting for the xsl:number action.
38  */

39 public class XslNumberFormat {
40   private String JavaDoc head;
41   private String JavaDoc tail;
42
43   private String JavaDoc []separators;
44   private int []formats;
45   private int []zeroSize;
46
47   private String JavaDoc format;
48   private String JavaDoc lang;
49   private boolean isAlphabetic;
50   private String JavaDoc groupSeparator;
51   private int groupSize;
52
53   /**
54    * Create a new number formatting object.
55    *
56    * @param format format string as specified by the XSLT draft
57    * @param lang language for alphanumeric numbering
58    * @param isAlphabetic resolves ambiguity for alphanumeric numbering
59    * @param groupSeparator separator between number grouping
60    * @param groupSize digits between group separator
61    */

62   public XslNumberFormat(String JavaDoc format, String JavaDoc lang, boolean isAlphabetic,
63                          String JavaDoc groupSeparator, int groupSize)
64   {
65     this.format = format;
66     this.lang = lang;
67     this.isAlphabetic = isAlphabetic;
68     if (groupSize <= 0) {
69       groupSize = 3;
70       groupSeparator = "";
71     }
72     if (groupSeparator == null)
73       groupSeparator = "";
74     this.groupSeparator = groupSeparator;
75     this.groupSize = groupSize;
76
77     if (format == null)
78       format = "1";
79
80     int headIndex = format.length();
81
82     ArrayList JavaDoc separators = new ArrayList JavaDoc();
83     IntArray zeroSizes = new IntArray();
84     IntArray formats = new IntArray();
85
86     CharBuffer cb = new CharBuffer();
87     int i = 0;
88     while (i < format.length()) {
89       char ch;
90
91       // scan the separator
92
cb.clear();
93       for (; i < format.length(); i++) {
94     ch = format.charAt(i);
95     if (Character.isLetterOrDigit(ch))
96       break;
97     cb.append(ch);
98       }
99
100       // head and tail separators are just sticked on the ends
101
if (head == null)
102     head = cb.toString();
103       else if (i >= format.length())
104     tail = cb.toString();
105       else
106     separators.add(cb.toString());
107
108       if (i >= format.length())
109     break;
110
111       // scan the format code
112
int zeroSize = 1;
113       int code = '0';
114       for (; i < format.length(); i++) {
115     ch = format.charAt(i);
116     if (! Character.isLetterOrDigit(ch))
117       break;
118
119     if (! Character.isDigit(ch)) {
120       if (code != '0' || zeroSize != 1)
121         code = 0;
122       else
123         code = ch;
124     }
125     else if (Character.digit(ch, 10) == 0 && zeroSize >= 0)
126       zeroSize++;
127     else if (Character.digit(ch, 10) == 1)
128       code = ch - 1;
129     else
130       code = 0;
131       }
132       if (code == 0)
133     code = '0';
134
135       zeroSizes.add(zeroSize);
136       formats.add(code);
137     }
138
139     // default format is '1'
140
if (formats.size() == 0) {
141       tail = head;
142       head = "";
143       formats.add('0');
144       zeroSizes.add(0);
145     }
146
147     // default separator is '.'
148
if (separators.size() == 0)
149       separators.add(".");
150     if (separators.size() < formats.size())
151       separators.add(separators.get(separators.size() - 1));
152
153     this.separators = (String JavaDoc []) separators.toArray(new String JavaDoc[separators.size()]);
154     this.zeroSize = zeroSizes.toArray();
155     this.formats = formats.toArray();
156
157     if (head == null)
158       head = "";
159     if (tail == null)
160       tail = "";
161   }
162   
163   public String JavaDoc getFormat()
164   {
165     return format;
166   }
167   
168   public String JavaDoc getLang()
169   {
170     return lang;
171   }
172   
173   public boolean isAlphabetic()
174   {
175     return isAlphabetic;
176   }
177   
178   public String JavaDoc getGroupSeparator()
179   {
180     return groupSeparator;
181   }
182   
183   public int getGroupSize()
184   {
185     return groupSize;
186   }
187
188   /**
189    * Converts the array of numbers into a formatted number
190    *
191    * @param numbers array of numbers
192    */

193   void format(XslWriter out, IntArray numbers)
194   {
195     CharBuffer buf = new CharBuffer();
196     
197     buf.append(head);
198
199     int i;
200     for (i = numbers.size() - 1; i >= 0; i--) {
201       int index = numbers.size() - i - 1;
202       if (index >= formats.length)
203     index = formats.length - 1;
204
205       char code = (char) formats[index];
206       int zeroCount = zeroSize[index];
207
208       int count = numbers.get(i);
209
210       switch (code) {
211       case 'i':
212     romanize(buf, "mdclxvi", count);
213     break;
214
215       case 'I':
216     romanize(buf, "MDCLXVI", count);
217     break;
218       
219       case 'a':
220     formatAlpha(buf, 'a', count);
221     break;
222       
223       case 'A':
224     formatAlpha(buf, 'A', count);
225     break;
226       
227       default:
228     formatDecimal(buf, code, zeroCount, count);
229     break;
230       }
231
232       if (i > 0)
233     buf.append(separators[index]);
234     }
235
236     buf.append(tail);
237
238     out.print(buf.toString());
239   }
240
241   /**
242    * Formats a roman numeral. Numbers bigger than 5000 are formatted as
243    * a decimal.
244    *
245    * @param cb buffer to accumulate the result
246    * @param xvi roman characters, e.g. "mdclxvi" and "MDCLXVI"
247    * @param count the number to convert,
248    */

249   private void romanize(CharBuffer cb, String JavaDoc xvi, int count)
250   {
251     if (count <= 0)
252       throw new RuntimeException JavaDoc();
253
254     if (count > 5000) {
255       cb.append(count);
256       return;
257     }
258
259     for (; count > 1000; count -= 1000)
260       cb.append(xvi.charAt(0));
261
262     romanize(cb, xvi.charAt(0), xvi.charAt(1), xvi.charAt(2), count / 100);
263     count %= 100;
264     romanize(cb, xvi.charAt(2), xvi.charAt(3), xvi.charAt(4), count / 10);
265     count %= 10;
266     romanize(cb, xvi.charAt(4), xvi.charAt(5), xvi.charAt(6), count);
267   }
268
269   /**
270    * Convert a single decimal digit to a roman number
271    *
272    * @param cb buffer to accumulate the result.
273    * @param x character for the tens number
274    * @param v character for the fives number
275    * @param i character for the ones number
276    * @param count digit to convert
277    */

278   private void romanize(CharBuffer cb, char x, char v, char i, int count)
279   {
280     switch (count) {
281     case 0:
282       break;
283     case 1:
284       cb.append(i);
285       break;
286     case 2:
287       cb.append(i);
288       cb.append(i);
289       break;
290     case 3:
291       cb.append(i);
292       cb.append(i);
293       cb.append(i);
294       break;
295     case 4:
296       cb.append(i);
297       cb.append(v);
298       break;
299     case 5:
300       cb.append(v);
301       break;
302     case 6:
303       cb.append(v);
304       cb.append(i);
305       break;
306     case 7:
307       cb.append(v);
308       cb.append(i);
309       cb.append(i);
310       break;
311     case 8:
312       cb.append(v);
313       cb.append(i);
314       cb.append(i);
315       cb.append(i);
316       break;
317     case 9:
318       cb.append(i);
319       cb.append(x);
320       break;
321
322     default:
323       throw new RuntimeException JavaDoc();
324     }
325   }
326
327   /**
328    * Format an alphabetic number. Only English encodings are supported.
329    *
330    * @param cb buffer to accumulate results
331    * @param a starting character
332    * @param count number to convert
333    */

334   private void formatAlpha(CharBuffer cb, char a, int count)
335   {
336     if (count <= 0)
337       throw new RuntimeException JavaDoc();
338     
339     int index = cb.length();
340     while (count > 0) {
341       count--;
342       cb.insert(index, (char) (a + count % 26));
343
344       count /= 26;
345     }
346   }
347
348   /**
349    * Format a decimal number.
350    *
351    * @param cb buffer to accumulate results
352    * @param code code for the zero digit
353    * @param zeroCount minimum digits
354    * @param count number to convert
355    */

356   private void formatDecimal(CharBuffer cb, int code,
357                  int zeroCount, int count)
358   {
359     int digits = 0;
360     int index = cb.length();
361     while (count > 0) {
362       if (digits > 0 && digits % groupSize == 0)
363     cb.insert(index, groupSeparator);
364       cb.insert(index, (char) (code + count % 10));
365       count /= 10;
366       digits++;
367     }
368     while (cb.length() - index < zeroCount) {
369       cb.insert(index, (char) code);
370     }
371   }
372 }
373
Popular Tags