View Javadoc

1   /**
2    *  MicroEmulator
3    *  Copyright (C) 2002-2005 Bartek Teodorczyk <barteo@barteo.net>
4    *
5    *  This library is free software; you can redistribute it and/or
6    *  modify it under the terms of the GNU Lesser General Public
7    *  License as published by the Free Software Foundation; either
8    *  version 2.1 of the License, or (at your option) any later version.
9    *
10   *  This library 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 GNU
13   *  Lesser General Public License for more details.
14   *
15   *  You should have received a copy of the GNU Lesser General Public
16   *  License along with this library; if not, write to the Free Software
17   *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
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  	// TODO not implemented yet
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  	 * @deprecated
81  	 */
82  	private String descriptorLocation;
83  
84  	private static Map specialInheritanceAttributeSet;
85  
86  	public DeviceImpl() {
87  		// Permits null values.
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  		// saveDevice(doc);
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 	 * (non-Javadoc)
132 	 * 
133 	 * @see org.microemu.device.DeviceA#init()
134 	 */
135 	public void init() {
136 
137 	}
138 
139 	/**
140 	 * @deprecated use Device.create(EmulatorContext context, ClassLoader
141 	 *             classLoader, String descriptorLocation);
142 	 */
143 	public void init(EmulatorContext context) {
144 		init(context, DEFAULT_LOCATION);
145 	}
146 
147 	/**
148 	 * @deprecated use Device.create(EmulatorContext context, ClassLoader
149 	 *             classLoader, String descriptorLocation);
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 	 * @deprecated
170 	 */
171 	public String getDescriptorLocation() {
172 		return descriptorLocation;
173 	}
174 
175 	/*
176 	 * (non-Javadoc)
177 	 * 
178 	 * @see org.microemu.device.DeviceA#destroy()
179 	 */
180 	public void destroy() {
181 	}
182 
183 	/*
184 	 * (non-Javadoc)
185 	 * 
186 	 * @see org.microemu.device.DeviceA#getName()
187 	 */
188 	public String getName() {
189 		return name;
190 	}
191 
192 	public EmulatorContext getEmulatorContext() {
193 		return context;
194 	}
195 
196 	/*
197 	 * (non-Javadoc)
198 	 * 
199 	 * @see org.microemu.device.DeviceA#getInputMethod()
200 	 */
201 	public InputMethod getInputMethod() {
202 		return context.getDeviceInputMethod();
203 	}
204 
205 	/*
206 	 * (non-Javadoc)
207 	 * 
208 	 * @see org.microemu.device.DeviceA#getFontManager()
209 	 */
210 	public FontManager getFontManager() {
211 		return context.getDeviceFontManager();
212 	}
213 
214 	/*
215 	 * (non-Javadoc)
216 	 * 
217 	 * @see org.microemu.device.DeviceA#getDeviceDisplay()
218 	 */
219 	public DeviceDisplay getDeviceDisplay() {
220 		return context.getDeviceDisplay();
221 	}
222 
223 	/*
224 	 * (non-Javadoc)
225 	 * 
226 	 * @see org.microemu.device.DeviceA#getNormalImage()
227 	 */
228 	public Image getNormalImage() {
229 		return normalImage;
230 	}
231 
232 	/*
233 	 * (non-Javadoc)
234 	 * 
235 	 * @see org.microemu.device.DeviceA#getOverImage()
236 	 */
237 	public Image getOverImage() {
238 		return overImage;
239 	}
240 
241 	/*
242 	 * (non-Javadoc)
243 	 * 
244 	 * @see org.microemu.device.DeviceA#getPressedImage()
245 	 */
246 	public Image getPressedImage() {
247 		return pressedImage;
248 	}
249 
250 	/*
251 	 * (non-Javadoc)
252 	 * 
253 	 * @see org.microemu.device.DeviceA#getSoftButtons()
254 	 */
255 	public Vector getSoftButtons() {
256 		return softButtons;
257 	}
258 
259 	/*
260 	 * (non-Javadoc)
261 	 * 
262 	 * @see org.microemu.device.DeviceA#getButtons()
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 		 * parseDisplay have to be performed first to check if device display
286 		 * has resizable flag set, parseInput skips rectangle or polygon element
287 		 * then, also normalImage, overImage and pressedImage aren't needed
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 				// "keyboard" is for backward compatibility
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 					// deprecated, moved to icon
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 					// deprecated, moved to status
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 	 * (non-Javadoc)
648 	 * 
649 	 * @see org.microemu.device.DeviceA#hasPointerEvents()
650 	 */
651 	public boolean hasPointerEvents() {
652 		return hasPointerEvents;
653 	}
654 
655 	/*
656 	 * (non-Javadoc)
657 	 * 
658 	 * @see org.microemu.device.DeviceA#hasPointerMotionEvents()
659 	 */
660 	public boolean hasPointerMotionEvents() {
661 		return hasPointerMotionEvents;
662 	}
663 
664 	/*
665 	 * (non-Javadoc)
666 	 * 
667 	 * @see org.microemu.device.DeviceA#hasRepeatEvents()
668 	 */
669 	public boolean hasRepeatEvents() {
670 		return hasRepeatEvents;
671 	}
672 
673 	/*
674 	 * (non-Javadoc)
675 	 * 
676 	 * @see org.microemu.device.DeviceA#hasRepeatEvents()
677 	 */
678 	public boolean vibrate(int duration) {
679 		return false;
680 	}
681 
682 	/*
683 	 * (non-Javadoc)
684 	 * 
685 	 * @see org.microemu.device.DeviceA#getSystemProperties()
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 	 * Very simple xml inheritance for devices.
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 			// System.out.println("processing [" + fullName + "]");
750 			boolean inheritWithName = false;
751 			if (c.getStringAttribute("name") != null) {
752 				inheritWithName = true;
753 			} else {
754 				// Find if element has siblings
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 			// System.out.println("inheritXML " + c.getName());
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 }