1 package com.tapina.robe.swi;
2
3 import com.tapina.robe.runtime.Environment;
4 import com.tapina.robe.runtime.MemoryMap;
5 import com.tapina.robe.runtime.RawDataBlock;
6
7 import java.lang.reflect.Method;
8 import java.nio.ByteBuffer;
9 import java.nio.CharBuffer;
10 import java.nio.charset.Charset;
11 import java.text.SimpleDateFormat;
12 import java.util.*;
13 import java.util.logging.Logger;
14
15 /***
16 * Created by IntelliJ IDEA.
17 * User: gareth
18 * Date: Sep 9, 2003
19 * Time: 7:51:48 AM
20 */
21 public final class Territory extends SWIHandler {
22 private static Territory ourInstance;
23
24 public synchronized static Territory getInstance() {
25 if (ourInstance == null) {
26 ourInstance = new Territory();
27 }
28 return ourInstance;
29 }
30
31 private Territory() {
32 }
33
34 public static int getBase() {
35 return 0x43040;
36 }
37
38 private static final String[] methodNames = new String[] {
39 "", "", "", "", "", "", "", "",
40 "", "", "", "ConvertDateAndTime", "", "", "", "",
41 "", "", "", "", "", "", "CharacterPropertyTable", "LowerCaseTable",
42 "UpperCaseTable", "", "", "", "", "", "", "",
43 "", "", "", "", "", "", "", "",
44 "", "", "", "", "", "", "", "",
45 "", "", "", "", "", "", "", "",
46 "", "", "", "", "", "", "", ""
47 };
48
49 public static Method getMethod(Integer offset) throws NoSuchMethodException {
50 return getMethod(methodNames[offset.intValue()]);
51 }
52
53 private static final Method getMethod(String name) throws NoSuchMethodException {
54 return Territory.class.getMethod(name, METHOD_PARAMETERS);
55 }
56
57 private final static String[] languages = new String[] {
58 null, "en", null, null, "it", "es", "fr", "de", "pt"
59 };
60
61 private final static String[] countries = new String[] {
62 null, "GB", null, null, "IT", "ES", "FR", "DE", "PT", "Esperanto", "GR",
63 "SE", "FI", null, "DK", "NO", "IS", "CA", "CA", "CA", "TK",
64 "Arabic", "IE", "HK", "RU", "RU", "IL", "MX", "LatinAm", null, null,
65 null, null, null, null, null, null, null, null, null, null,
66 null, null, null, null, null, null, null, "US"
67 };
68
69 private final static Locale getLocaleForTerritory(int territoryNumber) {
70 return new Locale(languages[territoryNumber], countries[territoryNumber]);
71 }
72
73 private final static String[] charsets = new String[] {
74 null, "ISO-8859-1"
75 };
76
77 private final static Charset getCharsetForTerritory(int territoryNumber) {
78 return Charset.forName(charsets[territoryNumber]);
79 }
80
81 private final static Map characterPropertyTableDataBlockMap = new HashMap(30);
82 private final static Map lowerCaseTableDataBlockMap = new HashMap(30);
83 private final static Map upperCaseTableDataBlockMap = new HashMap(30);
84
85 /***
86 * Format the date and time in the given territory
87 * @param env
88 */
89 public final void ConvertDateAndTime(Environment env) {
90 final int[] R = env.getCpu().R;
91 final MemoryMap memoryMap = env.getMemoryMap();
92 String out = convertDateAndTime(R[0], OS.convertToDate(memoryMap.get5ByteValue(R[1])),
93 memoryMap.getString0(R[4]));
94 R[0] = R[2];
95 if (out.length() > R[3] - 1) {
96 out = out.substring(0, R[3] - 1);
97 }
98 memoryMap.storeString0(R[2], out);
99 R[1] = R[2] + out.length();
100 R[2] = R[3] - out.length();
101 R[3] = R[4];
102 }
103
104 public final String convertDateAndTime(int territoryNumber, Date date, String format) {
105 if (territoryNumber == -1) {
106 territoryNumber = getDefaultTerritoryNumber();
107 }
108 format = convertDateFormatString(format);
109 final SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format, getLocaleForTerritory(territoryNumber));
110 return simpleDateFormat.format(date);
111 }
112
113 private final String convertDateFormatString(String acornFormat) {
114 StringBuffer outputFormat = new StringBuffer(acornFormat.length());
115 boolean inQuotes = false;
116 for (int i = 0; i < acornFormat.length(); i++) {
117 final char c = acornFormat.charAt(i);
118 switch (c) {
119 case '\'':
120 if (inQuotes) {
121 outputFormat.append('\'');
122 inQuotes = false;
123 }
124 outputFormat.append("\'\'");
125 break;
126 case '%':
127 if (inQuotes) {
128 outputFormat.append('\'');
129 inQuotes = false;
130 }
131 if (acornFormat.charAt(i + 1) == '%') {
132 i++;
133 } else {
134
135 if (acornFormat.charAt(i + 1) == '0') {
136 outputFormat.append('\0');
137 i++;
138 } else {
139 boolean suppressZeros = acornFormat.charAt(i + 1) == 'Z';
140 if (suppressZeros) {
141 i++;
142 }
143 final String token = acornFormat.substring(i + 1, i + 3);
144 if (token.equals("DY")) {
145 outputFormat.append(suppressZeros? "d" : "dd");
146 } else if (token.equals("ST")) {
147 log.warning("No Java equivalent of '%ST' in format string");
148 outputFormat.append("' '");
149 } else if (token.equals("MN")) {
150 outputFormat.append(suppressZeros? "M" : "MM");
151 } else if (token.equals("MO")) {
152 outputFormat.append("MMMM");
153 } else if (token.equals("M3")) {
154 outputFormat.append("MMM");
155 } else if (token.equals("WK")) {
156 outputFormat.append(suppressZeros? "w" : "ww");
157 } else if (token.equals("DN")) {
158 outputFormat.append(suppressZeros? "D" : "DDD");
159 } else if (token.equals("WN")) {
160 outputFormat.append("F");
161 } else if (token.equals("W3")) {
162 outputFormat.append("EEE");
163 } else if (token.equals("WE")) {
164 outputFormat.append("EEEE");
165 } else if (token.equals("CE")) {
166 if (acornFormat.substring(i + 3, i + 6).equals("%YR")) {
167 outputFormat.append("yyyy");
168 i += 3;
169 } else {
170 log.warning("Cannot output century without year (%YR)");
171 }
172 } else if (token.equals("YR")) {
173 outputFormat.append("yy");
174 } else if (token.equals("24")) {
175 outputFormat.append(suppressZeros? "H": "HH");
176 } else if (token.equals("12")) {
177 outputFormat.append(suppressZeros? "h" : "hh");
178 } else if (token.equals("AM") || token.equals("PM")) {
179 outputFormat.append("a");
180 } else if (token.equals("MI")) {
181 outputFormat.append(suppressZeros? "m" : "mm");
182 } else if (token.equals("SE")) {
183 outputFormat.append(suppressZeros ? "s" : "ss");
184 } else if (token.equals("CS")) {
185 log.warning("Centiseconds substituted with milliseconds");
186 outputFormat.append(suppressZeros ? "S" : "SS");
187 } else if (token.equals("TZ")) {
188 outputFormat.append("zzz");
189 }
190 i += 2;
191 }
192 break;
193 }
194 default:
195 if (!inQuotes) {
196 outputFormat.append('\'');
197 inQuotes = true;
198 }
199 outputFormat.append(c);
200 }
201 }
202 if (inQuotes) {
203 outputFormat.append('\'');
204 }
205 return outputFormat.toString();
206 }
207
208 /***
209 * Return an data block of bytes which are the lower-case representations of
210 * the first 256 characters in the default character set for the given territory.
211 * @param env
212 */
213 public final void LowerCaseTable(Environment env) {
214 final int[] R = env.getCpu().R;
215 if (R[0] == -1) {
216 R[0] = getDefaultTerritoryNumber();
217 }
218 final Integer territoryNumber = new Integer(R[0]);
219 final RawDataBlock dataBlock;
220 if (!lowerCaseTableDataBlockMap.containsKey(territoryNumber)) {
221 final char[] table = getLowerCaseTable(territoryNumber.intValue());
222 dataBlock = createCharDataBlock(env, territoryNumber, table);
223 lowerCaseTableDataBlockMap.put(territoryNumber, dataBlock);
224 } else {
225 dataBlock = (RawDataBlock) lowerCaseTableDataBlockMap.get(territoryNumber);
226 }
227 R[0] = dataBlock.getAddress();
228 }
229
230 /***
231 * Return an data block of bytes which are the upper-case representations of
232 * the first 256 characters in the default character set for the given territory.
233 * @param env
234 */
235 public final void UpperCaseTable(Environment env) {
236 final int[] R = env.getCpu().R;
237 if (R[0] == -1) {
238 R[0] = getDefaultTerritoryNumber();
239 }
240 final Integer territoryNumber = new Integer(R[0]);
241 final RawDataBlock dataBlock;
242 if (!upperCaseTableDataBlockMap.containsKey(territoryNumber)) {
243 final char[] table = getUpperCaseTable(territoryNumber.intValue());
244 dataBlock = createCharDataBlock(env, territoryNumber, table);
245 upperCaseTableDataBlockMap.put(territoryNumber, dataBlock);
246 } else {
247 dataBlock = (RawDataBlock) upperCaseTableDataBlockMap.get(territoryNumber);
248 }
249 R[0] = dataBlock.getAddress();
250 }
251
252 private RawDataBlock createCharDataBlock(Environment env, final Integer territoryNumber, final char[] table) {
253 final RawDataBlock charBlock = env.getMemoryMap().createSystemDataBlock(table.length);
254 final Charset charset = getCharsetForTerritory(territoryNumber.intValue());
255 charset.encode(CharBuffer.wrap(table)).get(charBlock.getBytes());
256 return charBlock;
257 }
258
259 public final void CharacterPropertyTable(Environment env) {
260 final int[] R = env.getCpu().R;
261 if (R[0] == -1) {
262 R[0] = getDefaultTerritoryNumber();
263 }
264 final Integer territoryNumber = new Integer(R[0]);
265 final RawDataBlock dataBlock;
266 if (!characterPropertyTableDataBlockMap.containsKey(territoryNumber)) {
267 final BitSet bits = getCharacterPropertyTable(territoryNumber.intValue(), R[1]);
268 dataBlock = env.getMemoryMap().createSystemDataBlock(32);
269 final byte[] bytes = dataBlock.getBytes();
270
271
272
273 for (int bit = bits.nextSetBit(0); bit >= 0; bit = bits.nextSetBit(bit + 1)) {
274 bytes[bit >> 3] |= (1 << (bit & 3));
275 }
276 characterPropertyTableDataBlockMap.put(territoryNumber, dataBlock);
277 } else {
278 dataBlock = (RawDataBlock) characterPropertyTableDataBlockMap.get(territoryNumber);
279 }
280 R[0] = dataBlock.getAddress();
281 }
282
283 public static final int CONTROL_CODE = 0;
284 public static final int UPPERCASE = 1;
285 public static final int LOWERCASE = 2;
286 public static final int ALPHABETIC = 3;
287 public static final int PUNCTUATION = 4;
288 public static final int WHITE_SPACE = 5;
289 public static final int DIGIT = 6;
290 public static final int HEX_DIGIT = 7;
291 public static final int ACCENT = 8;
292 public static final int FORWARDS = 9;
293 public static final int BACKWARDS = 10;
294
295 private abstract static class CharacterTest {
296 Logger log = Logger.getLogger(CharacterTest.class.getName());
297 abstract boolean test(char ch);
298 }
299 private static final CharacterTest[] CHARACTER_TESTS = new CharacterTest[] {
300 new CharacterTest() {
301 boolean test(char ch) {
302 return Character.isISOControl(ch);
303 }
304 },
305 new CharacterTest() {
306 boolean test(char ch) {
307 return Character.isUpperCase(ch);
308 }
309 },
310 new CharacterTest() {
311 boolean test(char ch) {
312 return Character.isLowerCase(ch);
313 }
314 },
315 new CharacterTest() {
316 boolean test(char ch) {
317 return Character.isLetter(ch);
318 }
319 },
320 new CharacterTest() {
321 boolean test(char ch) {
322 return !Character.isLetterOrDigit(ch) && !Character.isWhitespace(ch)
323 && !Character.isISOControl(ch);
324 }
325 },
326 new CharacterTest() {
327 boolean test(char ch) {
328 return Character.isWhitespace(ch);
329 }
330 },
331 new CharacterTest() {
332 boolean test(char ch) {
333 return Character.isDigit(ch);
334 }
335 },
336 new CharacterTest() {
337 boolean test(char ch) {
338 return "0123456789ABCDEF".indexOf(ch) > 0;
339 }
340 },
341 new CharacterTest() {
342 boolean test(char ch) {
343
344 return Character.isLetter(ch) && !String.valueOf(ch).matches("[A-Za-z]");
345 }
346 },
347 new CharacterTest() {
348 boolean test(char ch) {
349 log.warning("Character directionality not supported.");
350 return true;
351 }
352 },
353 new CharacterTest() {
354 boolean test(char ch) {
355 log.warning("Character directionality not supported.");
356 return false;
357 }
358 }
359 };
360
361 public final BitSet getCharacterPropertyTable(int territoryNumber, int property) {
362 if (territoryNumber == -1) {
363 territoryNumber = getDefaultTerritoryNumber();
364 }
365 final CharBuffer allChars = getWholeCharset(territoryNumber);
366 final BitSet result = new BitSet(allChars.length());
367 for (int position = 0; allChars.hasRemaining(); position++) {
368 final char c = allChars.get();
369 result.set(position, CHARACTER_TESTS[property].test(c));
370 }
371 return result;
372 }
373
374 /***
375 * Return an array of characters which are the lower-case representations of
376 * the first 256 characters in the default character set for the given territory.
377 * @param territoryNumber or -1 for the current territory
378 * @return array of lower-case characters
379 */
380 public final char[] getLowerCaseTable(int territoryNumber) {
381 if (territoryNumber == -1) {
382 territoryNumber = getDefaultTerritoryNumber();
383 }
384 String allChars = getWholeCharset(territoryNumber).toString();
385
386 return allChars.toLowerCase().toCharArray();
387 }
388
389 /***
390 * Return an array of characters which are the upper-case representations of
391 * the first 256 characters in the default character set for the given territory.
392 * @param territoryNumber or -1 for the current territory
393 * @return array of upper-case characters
394 */
395 public final char[] getUpperCaseTable(int territoryNumber) {
396 if (territoryNumber == -1) {
397 territoryNumber = getDefaultTerritoryNumber();
398 }
399 String allChars = getWholeCharset(territoryNumber).toString();
400
401 return allChars.toUpperCase().toCharArray();
402 }
403
404 int getDefaultTerritoryNumber() {
405 log.warning("Default territory is always set to UK");
406 return 1;
407 }
408
409 private CharBuffer getWholeCharset(int territoryNumber) {
410
411 final Charset charset = getCharsetForTerritory(territoryNumber);
412 final ByteBuffer encodedChars = ByteBuffer.allocate(256);
413 for (int i = 0; i < 256; i++) {
414 encodedChars.put(i, (byte) i);
415 }
416 return charset.decode(encodedChars);
417 }
418
419 public static void main(String[] args) {
420 Environment environment = new Environment(0x8000, new byte[0]);
421 Territory territory = getInstance();
422
423 environment.getCpu().R[0] = 1;
424 territory.UpperCaseTable(environment);
425 byte[] upperBytes =
426 ((RawDataBlock) environment.getMemoryMap().getDataBlock(environment.getCpu().R[0])).getBytes();
427 char[] upperChars = territory.getUpperCaseTable(1);
428 BitSet upperBits = territory.getCharacterPropertyTable(1, Territory.UPPERCASE);
429 for (int i = 0; i < upperChars.length; i++) {
430 final int value = ((int) upperBytes[i]) & 0xff;
431 System.out.println(i + " " + upperChars[i] + " " + value + (upperBits.get(i)? "*" : ""));
432 }
433
434 }
435
436 }