Add support for author nicknames
[jpcrr.git] / mnj / lua / OSLib.java
blob79d50b07698f83b75ffb806cb8900224d2f5859e
1 /* $Header: //info.ravenbrook.com/project/jili/version/1.1/code/mnj/lua/OSLib.java#1 $
2 * Copyright (c) 2006 Nokia Corporation and/or its subsidiary(-ies).
3 * All rights reserved.
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sublicense, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject
11 * to the following conditions:
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
20 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
21 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 // REFERENCES
26 // [C1990] "ISO Standard: Programming languages - C"; ISO 9899:1990;
28 package mnj.lua;
30 import java.util.Calendar;
31 import java.util.Date;
32 import java.util.TimeZone;
34 /**
35 * The OS Library. Can be opened into a {@link Lua} state by invoking
36 * the {@link #open} method.
38 public final class OSLib extends LuaJavaCallback
40 // Each function in the library corresponds to an instance of
41 // this class which is associated (the 'which' member) with an integer
42 // which is unique within this class. They are taken from the following
43 // set.
44 private static final int CLOCK = 1;
45 private static final int DATE = 2;
46 private static final int DIFFTIME = 3;
47 // EXECUTE = 4;
48 // EXIT = 5;
49 // GETENV = 6;
50 // REMOVE = 7;
51 // RENAME = 8;
52 private static final int SETLOCALE = 9;
53 private static final int TIME = 10;
55 /**
56 * Which library function this object represents. This value should
57 * be one of the "enums" defined in the class.
59 private int which;
61 /** Constructs instance, filling in the 'which' member. */
62 private OSLib(int which)
64 this.which = which;
67 /**
68 * Implements all of the functions in the Lua os library (that are
69 * provided). Do not call directly.
70 * @param L the Lua state in which to execute.
71 * @return number of returned parameters, as per convention.
73 public int luaFunction(Lua L)
75 switch (which)
77 case CLOCK:
78 return clock(L);
79 case DATE:
80 return date(L);
81 case DIFFTIME:
82 return difftime(L);
83 case SETLOCALE:
84 return setlocale(L);
85 case TIME:
86 return time(L);
88 return 0;
91 /**
92 * Opens the library into the given Lua state. This registers
93 * the symbols of the library in the table "os".
94 * @param L The Lua state into which to open.
96 public static void open(Lua L)
98 L.register("os");
100 r(L, "clock", CLOCK);
101 r(L, "date", DATE);
102 r(L, "difftime", DIFFTIME);
103 r(L, "setlocale", SETLOCALE);
104 r(L, "time", TIME);
107 /** Register a function. */
108 private static void r(Lua L, String name, int which)
110 OSLib f = new OSLib(which);
111 Object lib = L.getGlobal("os");
112 L.setField(lib, name, f);
115 private static final long T0 = System.currentTimeMillis();
117 /** Implements clock. Java provides no way to get CPU time, so we
118 * return the amount of wall clock time since this class was loaded.
120 private static int clock(Lua L)
122 double d = (double)System.currentTimeMillis();
123 d = d - T0;
124 d /= 1000;
126 L.pushNumber(d);
127 return 1;
130 /** Implements date. */
131 private static int date(Lua L)
133 long t;
134 if (L.isNoneOrNil(2))
136 t = System.currentTimeMillis();
138 else
140 t = (long)L.checkNumber(2);
143 String s = L.optString(1, "%c");
144 TimeZone tz = TimeZone.getDefault();
145 if (s.startsWith("!"))
147 tz = TimeZone.getTimeZone("GMT");
148 s = s.substring(1);
151 Calendar c = Calendar.getInstance(tz);
152 c.setTime(new Date(t));
154 if (s.equals("*t"))
156 L.push(L.createTable(0, 8)); // 8 = number of fields
157 setfield(L, "sec", c.get(Calendar.SECOND));
158 setfield(L, "min", c.get(Calendar.MINUTE));
159 setfield(L, "hour", c.get(Calendar.HOUR));
160 setfield(L, "day", c.get(Calendar.DAY_OF_MONTH));
161 setfield(L, "month", canonicalmonth(c.get(Calendar.MONTH)));
162 setfield(L, "year", c.get(Calendar.YEAR));
163 setfield(L, "wday", canonicalweekday(c.get(Calendar.DAY_OF_WEEK)));
164 // yday is not supported because CLDC 1.1 does not provide it.
165 // setfield(L, "yday", c.get("???"));
166 if (tz.useDaylightTime())
168 // CLDC 1.1 does not provide any way to determine isdst, so we set
169 // it to -1 (which in C means that the information is not
170 // available).
171 setfield(L, "isdst", -1);
173 else
175 // On the other hand if the timezone does not do DST then it
176 // can't be in effect.
177 setfield(L, "isdst", 0);
180 else
182 StringBuffer b = new StringBuffer();
183 int i = 0;
184 int l = s.length();
185 while (i < l)
187 char ch = s.charAt(i);
188 ++i;
189 if (ch != '%')
191 b.append(ch);
192 continue;
194 if (i >= l)
196 break;
198 ch = s.charAt(i);
199 ++i;
200 // Generally in order to save space, the abbreviated forms are
201 // identical to the long forms.
202 // The specifiers are from [C1990].
203 switch (ch)
205 case 'a': case 'A':
206 b.append(weekdayname(c));
207 break;
208 case 'b': case 'B':
209 b.append(monthname(c));
210 break;
211 case 'c':
212 b.append(c.getTime().toString());
213 break;
214 case 'd':
215 b.append(format(c.get(Calendar.DAY_OF_MONTH), 2));
216 break;
217 case 'H':
218 b.append(format(c.get(Calendar.HOUR), 2));
219 break;
220 case 'I':
222 int h = c.get(Calendar.HOUR);
223 h = (h+11)%12+1; // force into range 1-12
224 b.append(format(h, 2));
226 break;
227 case 'j':
228 case 'U': case 'W':
229 // Not supported because CLDC 1.1 doesn't provide it.
230 b.append('%');
231 b.append(ch);
232 break;
233 case 'm':
235 int m = canonicalmonth(c.get(Calendar.MONTH));
236 b.append(format(m, 2));
238 break;
239 case 'M':
240 b.append(format(c.get(Calendar.MINUTE), 2));
241 break;
242 case 'p':
244 int h = c.get(Calendar.HOUR);
245 b.append(h<12 ? "am" : "pm");
247 break;
248 case 'S':
249 b.append(format(c.get(Calendar.SECOND), 2));
250 break;
251 case 'w':
252 b.append(canonicalweekday(c.get(Calendar.DAY_OF_WEEK)));
253 break;
254 case 'x':
256 String u = c.getTime().toString();
257 // We extract fields from the result of Date.toString.
258 // The output of which is of the form:
259 // dow mon dd hh:mm:ss zzz yyyy
260 // except that zzz is optional.
261 b.append(u.substring(0, 11));
262 b.append(c.get(Calendar.YEAR));
264 break;
265 case 'X':
267 String u = c.getTime().toString();
268 b.append(u.substring(11, u.length()-5));
270 break;
271 case 'y':
272 b.append(format(c.get(Calendar.YEAR) % 100, 2));
273 break;
274 case 'Y':
275 b.append(c.get(Calendar.YEAR));
276 break;
277 case 'Z':
278 b.append(tz.getID());
279 break;
280 case '%':
281 b.append('%');
282 break;
284 } /* while */
285 L.pushString(b.toString());
287 return 1;
290 /** Implements difftime. */
291 private static int difftime(Lua L)
293 L.pushNumber((L.checkNumber(1) - L.optNumber(2, 0))/1000);
294 return 1;
297 // Incredibly, the spec doesn't give a numeric value and range for
298 // Calendar.JANUARY through to Calendar.DECEMBER.
300 * Converts from 0-11 to required Calendar value. DO NOT MODIFY THIS
301 * ARRAY.
303 private static final int[] MONTH =
305 Calendar.JANUARY,
306 Calendar.FEBRUARY,
307 Calendar.MARCH,
308 Calendar.APRIL,
309 Calendar.MAY,
310 Calendar.JUNE,
311 Calendar.JULY,
312 Calendar.AUGUST,
313 Calendar.SEPTEMBER,
314 Calendar.OCTOBER,
315 Calendar.NOVEMBER,
316 Calendar.DECEMBER
319 /** Implements setlocale. */
320 private static int setlocale(Lua L)
322 if (L.isNoneOrNil(1))
324 L.pushString("");
326 else
328 L.pushNil();
330 return 1;
333 /** Implements time. */
334 private static int time(Lua L)
336 if (L.isNoneOrNil(1)) // called without args?
338 L.pushNumber(System.currentTimeMillis());
339 return 1;
341 L.checkType(1, Lua.TTABLE);
342 L.setTop(1); // make sure table is at the top
343 Calendar c = Calendar.getInstance();
344 c.set(Calendar.SECOND, getfield(L, "sec", 0));
345 c.set(Calendar.MINUTE, getfield(L, "min", 0));
346 c.set(Calendar.HOUR, getfield(L, "hour", 12));
347 c.set(Calendar.DAY_OF_MONTH, getfield(L, "day", -1));
348 c.set(Calendar.MONTH, MONTH[getfield(L, "month", -1) - 1]);
349 c.set(Calendar.YEAR, getfield(L, "year", -1));
350 // ignore isdst field
351 L.pushNumber(c.getTime().getTime());
352 return 1;
355 private static int getfield(Lua L, String key, int d)
357 Object o = L.getField(L.value(-1), key);
358 if (Lua.isNumber(o))
359 return (int)L.toNumber(o);
360 if (d < 0)
361 return L.error("field '" + key + "' missing in date table");
362 return d;
365 private static void setfield(Lua L, String key, int value)
367 L.setField(L.value(-1), key, Lua.valueOfNumber(value));
370 /** Format a positive integer in a 0-filled field of width
371 * <var>w</var>.
373 private static String format(int i, int w)
375 StringBuffer b = new StringBuffer();
376 b.append(i);
377 while (b.length() < w)
379 b.insert(0, '0');
381 return b.toString();
384 private static String weekdayname(Calendar c)
386 String s = c.getTime().toString();
387 return s.substring(0, 3);
390 private static String monthname(Calendar c)
392 String s = c.getTime().toString();
393 return s.substring(4, 7);
397 * (almost) inverts the conversion provided by {@link #MONTH}. Converts
398 * from a {@link Calendar} value to a month in the range 1-12.
399 * @param m a value from the enum Calendar.JANUARY, Calendar.FEBRUARY, etc
400 * @return a month in the range 1-12, or the original value.
402 private static int canonicalmonth(int m)
404 for (int i=0; i<MONTH.length; ++i)
406 if (m == MONTH[i])
408 return i+1;
411 return m;
414 // DO NOT MODIFY ARRAY
415 private static final int[] WEEKDAY =
417 Calendar.SUNDAY,
418 Calendar.MONDAY,
419 Calendar.TUESDAY,
420 Calendar.WEDNESDAY,
421 Calendar.THURSDAY,
422 Calendar.FRIDAY,
423 Calendar.SATURDAY,
427 * Converts from a {@link Calendar} value to a weekday in the range
428 * 0-6 where 0 is Sunday (as per the convention used in [C1990]).
429 * @param w a value from the enum Calendar.SUNDAY, Calendar.MONDAY, etc
430 * @return a weekday in the range 0-6, or the original value.
432 private static int canonicalweekday(int w)
434 for (int i=0; i<WEEKDAY.length; ++i)
436 if (w == WEEKDAY[i])
438 return i;
441 return w;