1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.microemu.device.impl;
21
22 import java.io.BufferedReader;
23 import java.io.File;
24 import java.io.FileWriter;
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.io.InputStreamReader;
28 import java.net.URL;
29 import java.util.Enumeration;
30 import java.util.HashMap;
31 import java.util.Hashtable;
32 import java.util.Locale;
33 import java.util.Map;
34 import java.util.Vector;
35
36 import javax.microedition.lcdui.Font;
37 import javax.microedition.lcdui.Image;
38
39 import nanoxml.XMLElement;
40 import nanoxml.XMLParseException;
41
42 import org.microemu.CommandManager;
43 import org.microemu.EmulatorContext;
44 import org.microemu.app.util.IOUtils;
45 import org.microemu.device.Device;
46 import org.microemu.device.DeviceDisplay;
47 import org.microemu.device.FontManager;
48 import org.microemu.device.InputMethod;
49
50 public abstract class DeviceImpl implements Device {
51
52 private String name;
53
54 private EmulatorContext context;
55
56 private Image normalImage;
57
58 private Image overImage;
59
60 private Image pressedImage;
61
62 private Vector buttons;
63
64 private Vector softButtons;
65
66 private boolean hasPointerEvents;
67
68 private boolean hasPointerMotionEvents;
69
70
71 private boolean hasRepeatEvents;
72
73 private Map systemProperties;
74
75 private int skinVersion;
76
77 public static final String DEFAULT_LOCATION = "org/microemu/device/default/device.xml";
78
79
80
81
82 private String descriptorLocation;
83
84 private static Map specialInheritanceAttributeSet;
85
86 public DeviceImpl() {
87
88 systemProperties = new HashMap();
89 buttons = new Vector();
90 softButtons = new Vector();
91 }
92
93 public static DeviceImpl create(EmulatorContext context, ClassLoader classLoader, String descriptorLocation,
94 Class defaultDeviceClass) throws IOException {
95 XMLElement doc = loadDeviceDescriptor(classLoader, descriptorLocation);
96
97 DeviceImpl device = null;
98 for (Enumeration e = doc.enumerateChildren(); e.hasMoreElements();) {
99 XMLElement tmp = (XMLElement) e.nextElement();
100 if (tmp.getName().equals("class-name")) {
101 try {
102 Class deviceClass = Class.forName(tmp.getContent(), true, classLoader);
103 device = (DeviceImpl) deviceClass.newInstance();
104 } catch (ClassNotFoundException ex) {
105 throw new IOException(ex.getMessage());
106 } catch (InstantiationException ex) {
107 throw new IOException(ex.getMessage());
108 } catch (IllegalAccessException ex) {
109 throw new IOException(ex.getMessage());
110 }
111 break;
112 }
113 }
114
115 if (device == null) {
116 try {
117 device = (DeviceImpl) defaultDeviceClass.newInstance();
118 } catch (InstantiationException ex) {
119 throw new IOException(ex.getMessage());
120 } catch (IllegalAccessException ex) {
121 throw new IOException(ex.getMessage());
122 }
123 }
124 device.context = context;
125 device.loadConfig(classLoader, besourceBase(descriptorLocation), doc);
126
127 return device;
128 }
129
130
131
132
133
134
135 public void init() {
136
137 }
138
139
140
141
142
143 public void init(EmulatorContext context) {
144 init(context, DEFAULT_LOCATION);
145 }
146
147
148
149
150
151 public void init(EmulatorContext context, String descriptorLocation) {
152 this.context = context;
153 if (descriptorLocation.startsWith("/")) {
154 this.descriptorLocation = descriptorLocation.substring(1);
155 } else {
156 this.descriptorLocation = descriptorLocation;
157 }
158
159 try {
160 String base = descriptorLocation.substring(0, descriptorLocation.lastIndexOf("/"));
161 XMLElement doc = loadDeviceDescriptor(getClass().getClassLoader(), descriptorLocation);
162 loadConfig(getClass().getClassLoader(), base, doc);
163 } catch (IOException ex) {
164 System.out.println("Cannot load config: " + ex);
165 }
166 }
167
168
169
170
171 public String getDescriptorLocation() {
172 return descriptorLocation;
173 }
174
175
176
177
178
179
180 public void destroy() {
181 }
182
183
184
185
186
187
188 public String getName() {
189 return name;
190 }
191
192 public EmulatorContext getEmulatorContext() {
193 return context;
194 }
195
196
197
198
199
200
201 public InputMethod getInputMethod() {
202 return context.getDeviceInputMethod();
203 }
204
205
206
207
208
209
210 public FontManager getFontManager() {
211 return context.getDeviceFontManager();
212 }
213
214
215
216
217
218
219 public DeviceDisplay getDeviceDisplay() {
220 return context.getDeviceDisplay();
221 }
222
223
224
225
226
227
228 public Image getNormalImage() {
229 return normalImage;
230 }
231
232
233
234
235
236
237 public Image getOverImage() {
238 return overImage;
239 }
240
241
242
243
244
245
246 public Image getPressedImage() {
247 return pressedImage;
248 }
249
250
251
252
253
254
255 public Vector getSoftButtons() {
256 return softButtons;
257 }
258
259
260
261
262
263
264 public Vector getButtons() {
265 return buttons;
266 }
267
268 protected void loadConfig(ClassLoader classLoader, String base, XMLElement doc) throws IOException {
269 String deviceName = doc.getStringAttribute("name");
270 if (deviceName != null) {
271 name = deviceName;
272 } else {
273 name = base;
274 }
275
276 loadSkinVersion(doc);
277
278 hasPointerEvents = false;
279 hasPointerMotionEvents = false;
280 hasRepeatEvents = false;
281
282 ((FontManagerImpl) getFontManager()).setAntialiasing(false);
283
284
285
286
287
288
289 parseDisplay(classLoader, base, doc.getChild("display"));
290
291 for (Enumeration e = doc.enumerateChildren(); e.hasMoreElements();) {
292 XMLElement tmp = (XMLElement) e.nextElement();
293 if (tmp.getName().equals("system-properties")) {
294 parseSystemProperties(tmp);
295 } else if (tmp.getName().equals("img") && !((DeviceDisplayImpl) getDeviceDisplay()).isResizable()) {
296 try {
297 if (tmp.getStringAttribute("name").equals("normal")) {
298 normalImage = loadImage(classLoader, base, tmp.getStringAttribute("src"));
299 } else if (tmp.getStringAttribute("name").equals("over")) {
300 overImage = loadImage(classLoader, base, tmp.getStringAttribute("src"));
301 } else if (tmp.getStringAttribute("name").equals("pressed")) {
302 pressedImage = loadImage(classLoader, base, tmp.getStringAttribute("src"));
303 }
304 } catch (IOException ex) {
305 System.out.println("Cannot load " + tmp.getStringAttribute("src"));
306 return;
307 }
308 } else if (tmp.getName().equals("fonts")) {
309 parseFonts(classLoader, base, tmp);
310 } else if (tmp.getName().equals("input") || tmp.getName().equals("keyboard")) {
311
312 parseInput(tmp);
313 }
314 }
315 }
316
317 private void loadSkinVersion(XMLElement doc) {
318 String xmlns = doc.getStringAttribute("xmlns");
319 if (xmlns == null) {
320 skinVersion = 20000;
321 } else {
322 if (xmlns.endsWith("/2.0.2/")) {
323 skinVersion = 20002;
324 } else {
325 skinVersion = 20000;
326 }
327 }
328 }
329
330 private void parseDisplay(ClassLoader classLoader, String base, XMLElement tmp) throws IOException {
331 DeviceDisplayImpl deviceDisplay = (DeviceDisplayImpl) getDeviceDisplay();
332
333 String resizable = tmp.getStringAttribute("resizable", "false");
334 if (resizable.equalsIgnoreCase("true")) {
335 deviceDisplay.setResizable(true);
336 } else {
337 deviceDisplay.setResizable(false);
338 }
339
340 for (Enumeration e_display = tmp.enumerateChildren(); e_display.hasMoreElements();) {
341 XMLElement tmp_display = (XMLElement) e_display.nextElement();
342 if (tmp_display.getName().equals("numcolors")) {
343 deviceDisplay.setNumColors(Integer.parseInt(tmp_display.getContent()));
344 } else if (tmp_display.getName().equals("iscolor")) {
345 deviceDisplay.setIsColor(parseBoolean(tmp_display.getContent()));
346 } else if (tmp_display.getName().equals("numalphalevels")) {
347 deviceDisplay.setNumAlphaLevels(Integer.parseInt(tmp_display.getContent()));
348 } else if (tmp_display.getName().equals("background")) {
349 deviceDisplay.setBackgroundColor(new Color(Integer.parseInt(tmp_display.getContent(), 16)));
350 } else if (tmp_display.getName().equals("foreground")) {
351 deviceDisplay.setForegroundColor(new Color(Integer.parseInt(tmp_display.getContent(), 16)));
352 } else if (tmp_display.getName().equals("rectangle")) {
353 Rectangle rect = getRectangle(tmp_display);
354 if (deviceDisplay.isResizable()) {
355 rect.x = 0;
356 rect.y = 0;
357 }
358 deviceDisplay.setDisplayRectangle(rect);
359 } else if (tmp_display.getName().equals("paintable")) {
360 deviceDisplay.setDisplayPaintable(getRectangle(tmp_display));
361 }
362 }
363 for (Enumeration e_display = tmp.enumerateChildren(); e_display.hasMoreElements();) {
364 XMLElement tmp_display = (XMLElement) e_display.nextElement();
365 if (tmp_display.getName().equals("img")) {
366 if (tmp_display.getStringAttribute("name").equals("up")
367 || tmp_display.getStringAttribute("name").equals("down")) {
368
369 SoftButton icon = deviceDisplay.createSoftButton(skinVersion, tmp_display
370 .getStringAttribute("name"), getRectangle(tmp_display.getChild("paintable")), loadImage(
371 classLoader, base, tmp_display.getStringAttribute("src")), loadImage(classLoader, base,
372 tmp_display.getStringAttribute("src")));
373 getSoftButtons().addElement(icon);
374 } else if (tmp_display.getStringAttribute("name").equals("mode")) {
375
376 if (tmp_display.getStringAttribute("type").equals("123")) {
377 deviceDisplay.setMode123Image(new PositionedImage(loadImage(classLoader, base, tmp_display
378 .getStringAttribute("src")), getRectangle(tmp_display.getChild("paintable"))));
379 } else if (tmp_display.getStringAttribute("type").equals("abc")) {
380 deviceDisplay.setModeAbcLowerImage(new PositionedImage(loadImage(classLoader, base, tmp_display
381 .getStringAttribute("src")), getRectangle(tmp_display.getChild("paintable"))));
382 } else if (tmp_display.getStringAttribute("type").equals("ABC")) {
383 deviceDisplay.setModeAbcUpperImage(new PositionedImage(loadImage(classLoader, base, tmp_display
384 .getStringAttribute("src")), getRectangle(tmp_display.getChild("paintable"))));
385 }
386 }
387 } else if (tmp_display.getName().equals("icon")) {
388 Image iconNormalImage = null;
389 Image iconPressedImage = null;
390 for (Enumeration e_icon = tmp_display.enumerateChildren(); e_icon.hasMoreElements();) {
391 XMLElement tmp_icon = (XMLElement) e_icon.nextElement();
392 if (tmp_icon.getName().equals("img")) {
393 if (tmp_icon.getStringAttribute("name").equals("normal")) {
394 iconNormalImage = loadImage(classLoader, base, tmp_icon.getStringAttribute("src"));
395 } else if (tmp_icon.getStringAttribute("name").equals("pressed")) {
396 iconPressedImage = loadImage(classLoader, base, tmp_icon.getStringAttribute("src"));
397 }
398 }
399 }
400 SoftButton icon = deviceDisplay.createSoftButton(skinVersion, tmp_display.getStringAttribute("name"),
401 getRectangle(tmp_display.getChild("paintable")), iconNormalImage, iconPressedImage);
402 if (icon.getName().equals("up")) {
403 icon.setCommand(CommandManager.CMD_SCREEN_UP);
404 } else if (icon.getName().equals("down")) {
405 icon.setCommand(CommandManager.CMD_SCREEN_DOWN);
406 }
407 getSoftButtons().addElement(icon);
408 } else if (tmp_display.getName().equals("status")) {
409 if (tmp_display.getStringAttribute("name").equals("input")) {
410 Rectangle paintable = getRectangle(tmp_display.getChild("paintable"));
411 for (Enumeration e_status = tmp_display.enumerateChildren(); e_status.hasMoreElements();) {
412 XMLElement tmp_status = (XMLElement) e_status.nextElement();
413 if (tmp_status.getName().equals("img")) {
414 if (tmp_status.getStringAttribute("name").equals("123")) {
415 deviceDisplay.setMode123Image(new PositionedImage(loadImage(classLoader, base,
416 tmp_status.getStringAttribute("src")), paintable));
417 } else if (tmp_status.getStringAttribute("name").equals("abc")) {
418 deviceDisplay.setModeAbcLowerImage(new PositionedImage(loadImage(classLoader, base,
419 tmp_status.getStringAttribute("src")), paintable));
420 } else if (tmp_status.getStringAttribute("name").equals("ABC")) {
421 deviceDisplay.setModeAbcUpperImage(new PositionedImage(loadImage(classLoader, base,
422 tmp_status.getStringAttribute("src")), paintable));
423 }
424 }
425 }
426 }
427 }
428 }
429 }
430
431 private void parseFonts(ClassLoader classLoader, String base, XMLElement tmp) throws IOException {
432 FontManagerImpl fontManager = (FontManagerImpl) getFontManager();
433
434 String hint = tmp.getStringAttribute("hint");
435 boolean antialiasing = false;
436 if (hint != null && hint.equals("antialiasing")) {
437 antialiasing = true;
438 }
439 fontManager.setAntialiasing(antialiasing);
440
441 for (Enumeration e_fonts = tmp.enumerateChildren(); e_fonts.hasMoreElements();) {
442 XMLElement tmp_font = (XMLElement) e_fonts.nextElement();
443 if (tmp_font.getName().equals("font")) {
444 String face = tmp_font.getStringAttribute("face").toLowerCase();
445 String style = tmp_font.getStringAttribute("style").toLowerCase();
446 String size = tmp_font.getStringAttribute("size").toLowerCase();
447
448 if (face.startsWith("face_")) {
449 face = face.substring("face_".length());
450 }
451 if (style.startsWith("style_")) {
452 style = style.substring("style_".length());
453 }
454 if (size.startsWith("size_")) {
455 size = size.substring("size_".length());
456 }
457
458 for (Enumeration e_defs = tmp_font.enumerateChildren(); e_defs.hasMoreElements();) {
459 XMLElement tmp_def = (XMLElement) e_defs.nextElement();
460 if (tmp_def.getName().equals("system")) {
461 String defName = tmp_def.getStringAttribute("name");
462 String defStyle = tmp_def.getStringAttribute("style");
463 int defSize = Integer.parseInt(tmp_def.getStringAttribute("size"));
464
465 fontManager.setFont(face, style, size, fontManager.createSystemFont(defName, defStyle, defSize,
466 antialiasing));
467 } else if (tmp_def.getName().equals("ttf")) {
468 String defSrc = tmp_def.getStringAttribute("src");
469 String defStyle = tmp_def.getStringAttribute("style");
470 int defSize = Integer.parseInt(tmp_def.getStringAttribute("size"));
471
472 fontManager.setFont(face, style, size, fontManager.createTrueTypeFont(getResourceUrl(
473 classLoader, base, defSrc), defStyle, defSize, antialiasing));
474 }
475 }
476 }
477 }
478 }
479
480 private void parseInput(XMLElement tmp) {
481 DeviceDisplayImpl deviceDisplay = (DeviceDisplayImpl) getDeviceDisplay();
482 boolean resizable = deviceDisplay.isResizable();
483
484 for (Enumeration e_keyboard = tmp.enumerateChildren(); e_keyboard.hasMoreElements();) {
485 XMLElement tmp_keyboard = (XMLElement) e_keyboard.nextElement();
486 if (tmp_keyboard.getName().equals("haspointerevents")) {
487 hasPointerEvents = parseBoolean(tmp_keyboard.getContent());
488 } else if (tmp_keyboard.getName().equals("haspointermotionevents")) {
489 hasPointerMotionEvents = parseBoolean(tmp_keyboard.getContent());
490 } else if (tmp_keyboard.getName().equals("hasrepeatevents")) {
491 hasRepeatEvents = parseBoolean(tmp_keyboard.getContent());
492 } else if (tmp_keyboard.getName().equals("button")) {
493 Shape shape = null;
494 Hashtable inputToChars = new Hashtable();
495 for (Enumeration e_button = tmp_keyboard.enumerateChildren(); e_button.hasMoreElements();) {
496 XMLElement tmp_button = (XMLElement) e_button.nextElement();
497 if (tmp_button.getName().equals("chars")) {
498 String input = tmp_button.getStringAttribute("input", "common");
499 Vector stringArray = new Vector();
500 for (Enumeration e_chars = tmp_button.enumerateChildren(); e_chars.hasMoreElements();) {
501 XMLElement tmp_chars = (XMLElement) e_chars.nextElement();
502 if (tmp_chars.getName().equals("char")) {
503 stringArray.addElement(tmp_chars.getContent());
504 }
505 }
506 char[] charArray = new char[stringArray.size()];
507 for (int i = 0; i < stringArray.size(); i++) {
508 String str = (String) stringArray.elementAt(i);
509 if (str.length() > 0) {
510 charArray[i] = str.charAt(0);
511 } else {
512 charArray[i] = ' ';
513 }
514 }
515 inputToChars.put(input, charArray);
516 } else if (tmp_button.getName().equals("rectangle") && !resizable) {
517 shape = getRectangle(tmp_button);
518 } else if (tmp_button.getName().equals("polygon") && !resizable) {
519 shape = getPolygon(tmp_button);
520 }
521 }
522 int keyCode = tmp_keyboard.getIntAttribute("keyCode", Integer.MIN_VALUE);
523 getButtons().addElement(
524 deviceDisplay.createButton(skinVersion, tmp_keyboard.getStringAttribute("name"), shape,
525 keyCode, tmp_keyboard.getStringAttribute("key"), tmp_keyboard
526 .getStringAttribute("keyboardChars"), inputToChars, tmp_keyboard
527 .getBooleanAttribute("modeChange", false)));
528 } else if (tmp_keyboard.getName().equals("softbutton")) {
529 Vector commands = new Vector();
530 Shape shape = null;
531 Rectangle paintable = null;
532 Font font = null;
533 for (Enumeration e_button = tmp_keyboard.enumerateChildren(); e_button.hasMoreElements();) {
534 XMLElement tmp_button = (XMLElement) e_button.nextElement();
535 if (tmp_button.getName().equals("rectangle") && !resizable) {
536 shape = getRectangle(tmp_button);
537 } else if (tmp_button.getName().equals("polygon") && !resizable) {
538 shape = getPolygon(tmp_button);
539 } else if (tmp_button.getName().equals("paintable")) {
540 paintable = getRectangle(tmp_button);
541 } else if (tmp_button.getName().equals("command")) {
542 commands.addElement(tmp_button.getContent());
543 } else if (tmp_button.getName().equals("font")) {
544 font = getFont(tmp_button.getStringAttribute("face"), tmp_button.getStringAttribute("style"),
545 tmp_button.getStringAttribute("size"));
546 }
547 }
548 int keyCode = tmp_keyboard.getIntAttribute("keyCode", Integer.MIN_VALUE);
549 SoftButton button = deviceDisplay.createSoftButton(skinVersion,
550 tmp_keyboard.getStringAttribute("name"), shape, keyCode,
551 tmp_keyboard.getStringAttribute("key"), paintable,
552 tmp_keyboard.getStringAttribute("alignment"), commands, font);
553 getButtons().addElement(button);
554 getSoftButtons().addElement(button);
555 }
556 }
557 }
558
559 private void parseSystemProperties(XMLElement tmp) {
560 for (Enumeration e_prop = tmp.enumerateChildren(); e_prop.hasMoreElements();) {
561 XMLElement tmp_prop = (XMLElement) e_prop.nextElement();
562 if (tmp_prop.getName().equals("system-property")) {
563 systemProperties.put(tmp_prop.getStringAttribute("name"), tmp_prop.getStringAttribute("value"));
564 }
565 }
566 }
567
568 private static Font getFont(String face, String style, String size) {
569 int meFace = 0;
570 if (face.equalsIgnoreCase("system")) {
571 meFace |= Font.FACE_SYSTEM;
572 } else if (face.equalsIgnoreCase("monospace")) {
573 meFace |= Font.FACE_MONOSPACE;
574 } else if (face.equalsIgnoreCase("proportional")) {
575 meFace |= Font.FACE_PROPORTIONAL;
576 }
577
578 int meStyle = 0;
579 String testStyle = style.toLowerCase();
580 if (testStyle.indexOf("plain") != -1) {
581 meStyle |= Font.STYLE_PLAIN;
582 }
583 if (testStyle.indexOf("bold") != -1) {
584 meStyle |= Font.STYLE_BOLD;
585 }
586 if (testStyle.indexOf("italic") != -1) {
587 meStyle |= Font.STYLE_ITALIC;
588 }
589 if (testStyle.indexOf("underlined") != -1) {
590 meStyle |= Font.STYLE_UNDERLINED;
591 }
592
593 int meSize = 0;
594 if (size.equalsIgnoreCase("small")) {
595 meSize |= Font.SIZE_SMALL;
596 } else if (size.equalsIgnoreCase("medium")) {
597 meSize |= Font.SIZE_MEDIUM;
598 } else if (size.equalsIgnoreCase("large")) {
599 meSize |= Font.SIZE_LARGE;
600 }
601
602 return Font.getFont(meFace, meStyle, meSize);
603 }
604
605 private Rectangle getRectangle(XMLElement source) {
606 Rectangle rect = new Rectangle();
607
608 for (Enumeration e_rectangle = source.enumerateChildren(); e_rectangle.hasMoreElements();) {
609 XMLElement tmp_rectangle = (XMLElement) e_rectangle.nextElement();
610 if (tmp_rectangle.getName().equals("x")) {
611 rect.x = Integer.parseInt(tmp_rectangle.getContent());
612 } else if (tmp_rectangle.getName().equals("y")) {
613 rect.y = Integer.parseInt(tmp_rectangle.getContent());
614 } else if (tmp_rectangle.getName().equals("width")) {
615 rect.width = Integer.parseInt(tmp_rectangle.getContent());
616 } else if (tmp_rectangle.getName().equals("height")) {
617 rect.height = Integer.parseInt(tmp_rectangle.getContent());
618 }
619 }
620
621 return rect;
622 }
623
624 private Polygon getPolygon(XMLElement source) {
625 Polygon poly = new Polygon();
626
627 for (Enumeration e_poly = source.enumerateChildren(); e_poly.hasMoreElements();) {
628 XMLElement tmp_point = (XMLElement) e_poly.nextElement();
629 if (tmp_point.getName().equals("point")) {
630 poly.addPoint(Integer.parseInt(tmp_point.getStringAttribute("x")), Integer.parseInt(tmp_point
631 .getStringAttribute("y")));
632 }
633 }
634
635 return poly;
636 }
637
638 private boolean parseBoolean(String value) {
639 if (value.toLowerCase().equals(new String("true").toLowerCase())) {
640 return true;
641 } else {
642 return false;
643 }
644 }
645
646
647
648
649
650
651 public boolean hasPointerEvents() {
652 return hasPointerEvents;
653 }
654
655
656
657
658
659
660 public boolean hasPointerMotionEvents() {
661 return hasPointerMotionEvents;
662 }
663
664
665
666
667
668
669 public boolean hasRepeatEvents() {
670 return hasRepeatEvents;
671 }
672
673
674
675
676
677
678 public boolean vibrate(int duration) {
679 return false;
680 }
681
682
683
684
685
686
687 public Map getSystemProperties() {
688 return this.systemProperties;
689 }
690
691 private static void saveDevice(XMLElement doc) {
692 File configFile = new File(".", "device-tmp.xml");
693 FileWriter fw = null;
694 try {
695 fw = new FileWriter(configFile);
696 doc.write(fw);
697 fw.close();
698 } catch (IOException ex) {
699 System.out.println(ex);
700 } finally {
701 IOUtils.closeQuietly(fw);
702 }
703 }
704
705 private static XMLElement loadDeviceDescriptor(ClassLoader classLoader, String descriptorLocation)
706 throws IOException {
707 InputStream descriptor = classLoader.getResourceAsStream(descriptorLocation);
708 if (descriptor == null) {
709 throw new IOException("Cannot find descriptor at: " + descriptorLocation);
710 }
711 XMLElement doc;
712 try {
713 doc = loadXmlDocument(descriptor);
714 } finally {
715 IOUtils.closeQuietly(descriptor);
716 }
717
718 String parent = doc.getStringAttribute("extends");
719 if (parent != null) {
720 return inheritXML(loadDeviceDescriptor(classLoader, expandResourcePath(besourceBase(descriptorLocation),
721 parent)), doc, "/");
722 }
723 return doc;
724 }
725
726 private static void inheritanceConstInit() {
727 if (specialInheritanceAttributeSet == null) {
728 specialInheritanceAttributeSet = new Hashtable();
729 specialInheritanceAttributeSet.put("//FONTS/FONT", new String[] { "face", "style", "size" });
730 }
731 }
732
733
734
735
736 static XMLElement inheritXML(XMLElement parent, XMLElement child, String parentName) {
737 inheritanceConstInit();
738 if (parent == null) {
739 return child;
740 }
741 parent.setContent(child.getContent());
742 for (Enumeration ena = child.enumerateAttributeNames(); ena.hasMoreElements();) {
743 String key = (String) ena.nextElement();
744 parent.setAttribute(key, child.getAttribute(key));
745 }
746 for (Enumeration enc = child.enumerateChildren(); enc.hasMoreElements();) {
747 XMLElement c = (XMLElement) enc.nextElement();
748 String fullName = (parentName + "/" + c.getName()).toUpperCase(Locale.ENGLISH);
749
750 boolean inheritWithName = false;
751 if (c.getStringAttribute("name") != null) {
752 inheritWithName = true;
753 } else {
754
755 inheritWithName = ((child.getChildCount(c.getName()) > 1) || (parent.getChildCount(c.getName()) > 1));
756 }
757 XMLElement p;
758 if (inheritWithName) {
759 String[] equalAttributes = (String[]) specialInheritanceAttributeSet.get(fullName);
760 if (equalAttributes != null) {
761 p = parent.getChild(c.getName(), c.getStringAttributes(equalAttributes));
762 } else {
763 p = parent.getChild(c.getName(), c.getStringAttribute("name"));
764 }
765 } else {
766 p = parent.getChild(c.getName());
767 }
768
769 if (p == null) {
770 parent.addChild(c);
771 } else {
772 inheritXML(p, c, fullName);
773 }
774 }
775 return parent;
776 }
777
778 private static XMLElement loadXmlDocument(InputStream descriptor) throws IOException {
779 BufferedReader dis = new BufferedReader(new InputStreamReader(descriptor));
780 XMLElement doc = new XMLElement();
781 try {
782 doc.parseFromReader(dis, 1);
783 } catch (XMLParseException ex) {
784 throw new IOException(ex.toString());
785 } finally {
786 dis.close();
787 }
788 return doc;
789 }
790
791 private static String besourceBase(String descriptorLocation) {
792 return descriptorLocation.substring(0, descriptorLocation.lastIndexOf("/"));
793 }
794
795 private static String expandResourcePath(String base, String src) throws IOException {
796 String expandedSource;
797 if (src.startsWith("/")) {
798 expandedSource = src;
799 } else {
800 expandedSource = base + "/" + src;
801 }
802 if (expandedSource.startsWith("/")) {
803 expandedSource = expandedSource.substring(1);
804 }
805 return expandedSource;
806 }
807
808 private URL getResourceUrl(ClassLoader classLoader, String base, String src) throws IOException {
809 String expandedSource = expandResourcePath(base, src);
810
811 URL result = classLoader.getResource(expandedSource);
812
813 if (result == null) {
814 throw new IOException("Cannot find resource: " + expandedSource);
815 }
816
817 return result;
818 }
819
820 private Image loadImage(ClassLoader classLoader, String base, String src) throws IOException {
821 URL url = getResourceUrl(classLoader, base, src);
822
823 return ((DeviceDisplayImpl) getDeviceDisplay()).createSystemImage(url);
824 }
825
826 }