001/* 002 * jPOS Project [http://jpos.org] 003 * Copyright (C) 2000-2026 jPOS Software SRL 004 * 005 * This program is free software: you can redistribute it and/or modify 006 * it under the terms of the GNU Affero General Public License as 007 * published by the Free Software Foundation, either version 3 of the 008 * License, or (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 * GNU Affero General Public License for more details. 014 * 015 * You should have received a copy of the GNU Affero General Public License 016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 017 */ 018 019package org.jpos.ui; 020 021import org.jdom2.Element; 022import org.jdom2.JDOMException; 023import org.jpos.util.Log; 024 025import javax.swing.*; 026import java.awt.*; 027import java.awt.event.ActionListener; 028import java.net.MalformedURLException; 029import java.net.URL; 030import java.util.*; 031 032/** 033 * @author Alejandro Revilla 034 * 035 * <p>jPOS UI main class</p> 036 * 037 * @see UIFactory 038 * 039 * See src/examples/ui/* for usage details 040 */ 041@SuppressWarnings({"unchecked", "deprecation"}) 042public class UI implements UIFactory, UIObjectFactory { 043 JFrame mainFrame; 044 Map registrar, mapping; 045 Element config; 046 UIObjectFactory objFactory; 047 048 Log log; 049 boolean destroyed = false; 050 static final ResourceBundle classMapping; 051 052 static { 053 classMapping = ResourceBundle.getBundle(UI.class.getName()); 054 } 055 /** 056 * Create a new UI object 057 */ 058 public UI () { 059 super (); 060 registrar = new HashMap (); 061 mapping = new HashMap (); 062 setObjectFactory (this); 063 } 064 /** 065 * Creates a new UI object 066 * @param config configuration element 067 */ 068 public UI (Element config) { 069 this (); 070 setConfig(config); 071 } 072 /** 073 * Assigns an object factory use to create new object instances. 074 * If no object factory is asigned, UI uses the default classloader 075 * 076 * @param objFactory reference to an Object Factory 077 */ 078 public void setObjectFactory (UIObjectFactory objFactory) { 079 this.objFactory = objFactory; 080 } 081 /** 082 * @param config the Configuration element 083 */ 084 public void setConfig (Element config) { 085 this.config = config; 086 } 087 /** 088 * @param log an optional Log instance 089 * @see org.jpos.util.Log 090 */ 091 public void setLog (Log log) { 092 this.log = log; 093 } 094 public Log getLog () { 095 return log; 096 } 097 /** 098 * UI uses a map to hold references to its components 099 * ("id" attribute) 100 * 101 * @return UI component registrar 102 */ 103 public Map getRegistrar () { 104 return registrar; 105 } 106 /** 107 * @param id Component id ("id" configuration attribute) 108 * @return the Object or null 109 */ 110 public Object get (String id) { 111 return registrar.get (id); 112 } 113 /** 114 * UI is itself a UIFactory. 115 * This strategy is used to recursively instantiate components 116 * inside a container 117 * 118 * @param ui reference to this UI instance 119 * @param e free form configuration Element 120 * @return JComponent 121 */ 122 public JComponent create (UI ui, Element e) { 123 return create(e); 124 } 125 /** 126 * UIObjectFactory implementation. 127 * uses default classloader 128 * @param clazz the Clazzzz 129 * @return the Object 130 * @throws Exception if unable to instantiate 131 * @see #setLog 132 */ 133 public Object newInstance (String clazz) throws Exception { 134 ClassLoader cl = Thread.currentThread().getContextClassLoader (); 135 Class type = cl.loadClass (clazz); 136 return type.newInstance(); 137 } 138 /** 139 * configure this UI object 140 */ 141 public void configure () throws JDOMException { 142 configure (config); 143 } 144 /** 145 * reconfigure can be used in order to re-configure components 146 * inside a container (i.e. changing a panel in response to 147 * an event). 148 * @see org.jpos.ui.action.Redirect 149 * 150 * @param elementName the element name used as new configuration 151 * @param panelName panel ID (see "id" attribute) 152 */ 153 public void reconfigure (String elementName, String panelName) { 154 Container c = 155 panelName == null ? mainFrame.getContentPane() : (JComponent) get (panelName); 156 if (c != null) { 157 c.removeAll (); 158 c.add ( 159 createComponent (config.getChild (elementName)) 160 ); 161 if (c instanceof JComponent) { 162 c.revalidate(); 163 } 164 c.repaint (); 165 } 166 } 167 /** 168 * dispose this UI object 169 */ 170 public void dispose () { 171 /* This is the right code for the dispose, but it freezes in 172 JVM running under WinXP (in linux went fine.. I didn't 173 test it under other OS's) 174 (last version tested: JRE 1.5.0-beta2) 175 176 if (mainFrame != null) { 177 // dumpComponent (mainFrame); 178 mainFrame.dispose (); 179 */ 180 destroyed = true; 181 182 Iterator it = Arrays.asList(Frame.getFrames()).iterator(); 183 184 while (it.hasNext()) { 185 JFrame jf = (JFrame) it.next(); 186 removeComponent(jf); 187 } 188 } 189 /** 190 * @return true if this UI object has been disposed and is no longer valid 191 */ 192 public boolean isDestroyed () { 193 return destroyed; 194 } 195 196 protected void configure (Element ui) throws JDOMException { 197 setLookAndFeel (ui); 198 createMappings (ui); 199 createObjects (ui, "object"); 200 createObjects (ui, "action"); 201 if (!"ui".equals (ui.getName())) { 202 ui = ui.getChild ("ui"); 203 } 204 if (ui != null) { 205 JFrame frame = initFrame (ui); 206 Element mb = ui.getChild ("menubar"); 207 if (mb != null) 208 frame.setJMenuBar (buildMenuBar (mb)); 209 210 frame.setContentPane ( 211 createComponent (ui.getChild ("components")) 212 ); 213 if ("true".equals (ui.getAttributeValue ("full-screen"))) { 214 GraphicsDevice device = GraphicsEnvironment 215 .getLocalGraphicsEnvironment() 216 .getDefaultScreenDevice(); 217 frame.setUndecorated ( 218 "true".equals (ui.getAttributeValue ("undecorated")) 219 ); 220 device.setFullScreenWindow(frame); 221 } else { 222 frame.show (); 223 } 224 } 225 } 226 227 private void removeComponent (Component c) { 228 if (c instanceof Container) { 229 Container cont = (Container) c; 230 Component[] cc = cont.getComponents(); 231 232 for (Component aCc : cc) { 233 removeComponent(aCc); 234 } 235 cont.removeAll(); 236 } 237 } 238 239 // ##DEBUG## 240 private void dumpComponent (Component c) { 241 System.out.println (c.getClass().getName() + ":" + c.getBounds().getSize().toString()); 242 if (c instanceof Container) { 243 Component[] cc = ((Container) c).getComponents(); 244 for (Component aCc : cc) { 245 dumpComponent(aCc); 246 } 247 } 248 } 249 250 private JFrame initFrame (Element ui) { 251 Element caption = ui.getChild ("caption"); 252 mainFrame = caption == null ? 253 new JFrame () : 254 new JFrame (caption.getText()); 255 256 JOptionPane.setRootFrame (mainFrame); 257 258 mainFrame.getContentPane().setLayout(new BorderLayout()); 259 260 String close = ui.getAttributeValue ("close"); 261 262 if ("false".equals (close)) 263 mainFrame.setDefaultCloseOperation (JFrame.DO_NOTHING_ON_CLOSE); 264 else if ("exit".equals (close)) 265 mainFrame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE); 266 267 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); 268 mainFrame.setSize(getDimension (ui, screenSize)); 269 locateOnScreen(mainFrame); 270 return mainFrame; 271 } 272 273 private void locateOnScreen(Frame frame) { 274 Dimension paneSize = frame.getSize(); 275 Dimension screenSize = frame.getToolkit().getScreenSize(); 276 frame.setLocation( 277 (screenSize.width - paneSize.width) / 2, 278 (screenSize.height - paneSize.height) / 2); 279 } 280 private JMenuBar buildMenuBar (Element ui) { 281 JMenuBar mb = new JMenuBar (); 282 Iterator iter = ui.getChildren("menu").iterator(); 283 while (iter.hasNext()) 284 mb.add (menu ((Element) iter.next())); 285 286 return mb; 287 } 288 private JMenu menu (Element m) { 289 JMenu menu = new JMenu (m.getAttributeValue ("id")); 290 setItemAttributes (menu, m); 291 Iterator iter = m.getChildren ().iterator(); 292 while (iter.hasNext()) 293 addMenuItem(menu, (Element) iter.next()); 294 return menu; 295 } 296 private void addMenuItem (JMenu menu, Element m) { 297 String tag = m.getName (); 298 299 if ("menuitem".equals (tag)) { 300 JMenuItem item = new JMenuItem (m.getAttributeValue ("id")); 301 setItemAttributes (item, m); 302 menu.add (item); 303 } else if ("menuseparator".equals (tag)) { 304 menu.addSeparator (); 305 } else if ("button-group".equals (tag)) { 306 addButtonGroup (menu, m); 307 } else if ("check-box".equals (tag)) { 308 JCheckBoxMenuItem item = new JCheckBoxMenuItem ( 309 m.getAttributeValue ("id") 310 ); 311 setItemAttributes (item, m); 312 item.setState ( 313 "true".equals (m.getAttributeValue ("state")) 314 ); 315 menu.add (item); 316 } else if ("menu".equals (tag)) { 317 menu.add (menu (m)); 318 } 319 } 320 private void addButtonGroup (JMenu menu, Element m) { 321 ButtonGroup group = new ButtonGroup(); 322 Iterator iter = m.getChildren ("radio-button").iterator(); 323 while (iter.hasNext()) { 324 addRadioButton (menu, group, (Element) iter.next()); 325 } 326 } 327 private void addRadioButton (JMenu menu, ButtonGroup group, Element m) { 328 JRadioButtonMenuItem item = new JRadioButtonMenuItem 329 (m.getAttributeValue ("id")); 330 setItemAttributes (item, m); 331 item.setSelected ( 332 "true".equals (m.getAttributeValue ("selected")) 333 ); 334 group.add (item); 335 menu.add (item); 336 } 337 private Dimension getDimension (Element e, Dimension def) { 338 String w = e.getAttributeValue ("width"); 339 String h = e.getAttributeValue ("height"); 340 341 return new Dimension ( 342 w != null ? Integer.parseInt (w) : def.width, 343 h != null ? Integer.parseInt (h) : def.height 344 ); 345 } 346 private void setItemAttributes (AbstractButton b, Element e) 347 { 348 String s = e.getAttributeValue ("accesskey"); 349 if (s != null && s.length() == 1) 350 b.setMnemonic (s.charAt(0)); 351 352 String icon = e.getAttributeValue ("icon"); 353 if (icon != null) { 354 try { 355 b.setIcon (new ImageIcon (new URL (icon))); 356 } catch (MalformedURLException ex) { 357 ex.printStackTrace (); 358 } 359 } 360 b.setActionCommand (e.getAttributeValue ("command")); 361 String actionId = e.getAttributeValue ("action"); 362 if (actionId != null) { 363 b.addActionListener ((ActionListener) get (actionId)); 364 } 365 } 366 protected void setLookAndFeel (Element ui) { 367 String laf = ui.getAttributeValue ("look-and-feel"); 368 if (laf != null) { 369 try { 370 UIManager.setLookAndFeel (laf); 371 } catch (Exception e) { 372 warn (e); 373 } 374 } 375 } 376 private JComponent createComponent (Element e) { 377 if (e == null) 378 return new JPanel (); 379 380 JComponent component; 381 UIFactory factory = null; 382 String clazz = e.getAttributeValue ("class"); 383 if (clazz == null) 384 clazz = (String) mapping.get (e.getName()); 385 if (clazz == null) { 386 try { 387 clazz = classMapping.getString (e.getName()); 388 } catch (MissingResourceException ignored) { 389 // OK to happen on components handled by this factory 390 } 391 } 392 try { 393 if (clazz == null) 394 factory = this; 395 else 396 factory = (UIFactory) objFactory.newInstance (clazz.trim()); 397 398 component = factory.create (this, e); 399 setSize (component, e); 400 if (component instanceof AbstractButton) { 401 AbstractButton b = (AbstractButton) component; 402 b.setActionCommand (e.getAttributeValue ("command")); 403 String actionId = e.getAttributeValue ("action"); 404 if (actionId != null) { 405 b.addActionListener ((ActionListener) get (actionId)); 406 } 407 } 408 put (component, e); 409 410 Element script = e.getChild ("script"); 411 if (script != null) 412 component = doScript (component, script); 413 414 if ("true".equals (e.getAttributeValue ("scrollable"))) 415 component = new JScrollPane (component); 416 } catch (Exception ex) { 417 warn ("Error instantiating class " + clazz); 418 warn (ex); 419 component = new JLabel ("Error instantiating class " + clazz); 420 } 421 return component; 422 } 423 protected JComponent doScript (JComponent component, Element e) { 424 return component; 425 } 426 private void setSize (JComponent c, Element e) { 427 String w = e.getAttributeValue ("width"); 428 String h = e.getAttributeValue ("height"); 429 Dimension d = c.getPreferredSize (); 430 double dw = d.getWidth (); 431 double dh = d.getHeight (); 432 if (w != null) 433 dw = Double.parseDouble (w); 434 if (h != null) 435 dh = Double.parseDouble (h); 436 if (w != null || h != null) { 437 d.setSize (dw, dh); 438 c.setPreferredSize (d); 439 } 440 } 441 public JComponent create (Element e) { 442 JComponent component = null; 443 444 Iterator iter = e.getChildren().iterator(); 445 for (int i=0; iter.hasNext(); i++) { 446 JComponent c = createComponent((Element) iter.next ()); 447 if (i == 0) 448 component = c; 449 else if (i == 1) { 450 JPanel p = new JPanel (); 451 p.add (component); 452 p.add (c); 453 component = p; 454 put (component, e); 455 } else { 456 component.add (c); 457 } 458 } 459 return component; 460 } 461 public JFrame getMainFrame() { 462 return mainFrame; 463 } 464 465 private void createObjects (Element e, String name) { 466 Iterator iter = e.getChildren (name).iterator (); 467 while (iter.hasNext()) { 468 try { 469 Element ee = (Element) iter.next (); 470 String clazz = ee.getAttributeValue ("class"); 471 Object obj = objFactory.newInstance (clazz.trim()); 472 if (obj instanceof UIAware) { 473 ((UIAware) obj).setUI (this, ee); 474 } 475 put (obj, ee); 476 } catch (Exception ex) { 477 warn (ex); 478 } 479 } 480 } 481 private void createMappings (Element e) { 482 Iterator iter = e.getChildren ("mapping").iterator (); 483 while (iter.hasNext()) { 484 try { 485 Element ee = (Element) iter.next (); 486 String name = ee.getAttributeValue ("name"); 487 String clazz = ee.getAttributeValue("factory"); 488 mapping.put(name, clazz); 489 } catch (Exception ex) { 490 warn (ex); 491 } 492 } 493 } 494 protected void warn (Object obj) { 495 if (log != null) 496 log.warn (obj); 497 } 498 protected void warn (Object obj, Exception ex) { 499 if (log != null) 500 log.warn (obj, ex); 501 } 502 503 private void put (Object obj, Element e) { 504 String id = e.getAttributeValue ("id"); 505 if (id != null) { 506 registrar.put (id, obj); 507 } 508 } 509} 510