1 19 package org.netbeans.modules.java.source.engine; 20 21 import java.awt.*; 22 import java.awt.event.*; 23 import java.beans.*; 24 import java.io.*; 25 import java.lang.reflect.*; 26 import java.util.*; 27 import java.util.prefs.Preferences ; 28 import javax.swing.*; 29 import org.openide.ErrorManager; 30 import org.openide.util.NbBundle; 31 32 46 public class PropertySheetInfo { 47 48 private final Class base; 49 private final ArrayList<Item> items = new ArrayList<Item>(); 50 private String title = null; 51 private boolean hasTabs; 52 private Preferences prefs; 53 54 public static final String REMOVE_PANEL_ACTION = "remove PropertySheetInfo panel"; private static final ErrorManager logger = ErrorManager.getDefault(); 56 57 private static final int DEFAULT_PAD = 12; 58 59 public static PropertySheetInfo find(Class c) { 60 return new PropertySheetInfo(c); 61 } 62 public static JComponent findButtonedPanel(final Object obj, ActionListener apply, ActionListener cancel) { 63 return find(obj.getClass()).buildButtonedPanel(obj, apply, cancel); 64 } 65 public static JComponent findPanel(final Object obj, Runnable incHandler) { 66 return find(obj.getClass()).buildPanel(obj,incHandler); 67 } 68 69 70 private PropertySheetInfo(Class base) { 71 this.base = base; 72 String panelName = base.getName().replace('.','/'); 73 prefs = Preferences.userRoot().node(panelName); 74 Method[] methods = base.getDeclaredMethods(); 75 Field[] fields = base.getDeclaredFields(); 76 for(int i = fields.length; --i>=0; ) { 77 Field f = fields[i]; 78 if((f.getModifiers()&Modifier.PUBLIC)==0) continue; 79 if((f.getModifiers()&Modifier.STATIC)!=0) continue; 80 String nm = f.getName(); 81 Class t = f.getType(); 82 PropertyEditor bi = PropertyEditorManager.findEditor(t); 83 if(bi==null && t!=PickOne.class) continue; 84 items.add(new FieldItem(nm,f,t,bi)); 85 } 86 for(int i = methods.length; --i>=0; ) { 87 Method m = methods[i]; 88 String nm = m.getName(); 89 String pnm; 90 boolean getter; 91 Class rt = m.getReturnType(); 92 Class [] pt = m.getParameterTypes(); 93 Class propType; 94 if(rt!=void.class) { 95 if(pt.length!=0) continue; 96 if((pnm=pattern("get",nm))==null && (pnm=pattern("is",nm))==null) continue; 99 getter = true; 100 propType = rt; 101 } 102 else { 103 if(pt.length!=1 || (pnm=pattern("set",nm)) == null) continue; 105 getter = false; 106 propType = pt[0]; 107 } 108 PropertyEditor bi = PropertyEditorManager.findEditor(propType); 109 if(bi==null && propType!=PickOne.class) continue; 110 Item item0 = null; 111 for(int j = items.size(); --j>=0; ) { 112 Item item = items.get(j); 113 if(item.name.equals(pnm)) { 114 item0 = item; 115 break; 116 } 117 } 118 MethodItem item; 119 if(item0==null) { 120 item = new MethodItem(pnm,propType,bi); 121 items.add(item); 122 } else if(!(item0 instanceof MethodItem)) continue; 123 else { 124 item = (MethodItem) item0; 125 if(propType != item.propType) { 126 logger.log(ErrorManager.USER, "set/get type mismatch "+item); 127 continue; 128 } 129 } 130 if(getter) item.getter = m; 131 else item.setter = m; 132 } 133 pname = null; 134 if (base.getPackage() == null) { 135 validate(); 137 return; 138 } 139 String cname = base.getSimpleName(); 140 String rnm = cname+"-"+Locale.getDefault().getLanguage()+".panel"; InputStream cfg = null; 142 if((cfg = base.getResourceAsStream(rnm))==null) { 143 if((cfg = base.getResourceAsStream(rnm=cname+".panel"))==null) { validate(); 145 return; 146 } 147 } 148 Reader in = new InputStreamReader(new BufferedInputStream(cfg)); 149 char[] buf = new char[2]; 150 int slot = 0; 151 try { 152 int c; 153 int pos = 0; 154 String tag = null; 155 while((c = in.read())>=0) 156 if(c!='\n' && c!='\r') 157 if(c==':' && tag==null) { 158 tag = new String (buf,0,pos).trim(); 159 pos=0; 160 } else { 161 if(pos>=buf.length) { 162 char[] nb = new char[pos*2]; 163 System.arraycopy(buf,0,nb,0,pos); 164 buf = nb; 165 } 166 buf[pos++] = (char)c; 167 } 168 else { 169 if(pos>0 && tag!=null && tag.length()>0) { 170 String body = new String (buf,0,pos).trim(); 171 if(tag.equals("*title")) { 172 title = body; 173 tag = null; 174 pos = 0; 175 continue; 176 } else if(tag.equals("*tab")) { 177 items.add(slot++,new TabItem(body)); 178 hasTabs = true; 179 } else if(!tag.startsWith("#")) { 180 for(int i = items.size(); --i>=slot; ) { 181 Item item = items.get(i); 182 if(item.name.equals(tag)) { 183 item.setLabel(body); 184 items.remove(item); 185 items.add(slot++,item); 186 body = null; 187 break; 188 } 189 } 190 if(body != null) 191 logger.log(ErrorManager.USER, "Panel field "+tag+" missing from object"); 192 } 193 } 194 pos = 0; 195 tag = null; 196 } 197 in.close(); 198 } catch(IOException ioe) { 199 ioe.printStackTrace(); 200 } 201 validate(); 202 } 203 private void validate() { 204 for(int i = items.size(); --i>=0; ) { 205 Item item = items.get(i); 206 if(!item.valid()) items.remove(i); 207 } 208 } 209 private String pattern(String pfx, String name) { 210 if(!name.startsWith(pfx)) return null; 211 int nml = name.length(); 212 int pfl = pfx.length(); 213 if(pfl==nml) return null; 214 if(pname.length<nml) pname = new char[nml]; 215 name.getChars(0,nml,pname,0); 216 if(Character.isUpperCase(pname[pfl])) 217 pname[pfl] = Character.toLowerCase(pname[pfl]); 218 return new String (pname,pfl,nml-pfl); 219 } 220 public String getTitle() { 221 if(title==null) { 222 title = base.getName(); 223 int dot = title.lastIndexOf('.'); 224 if(dot>0) title = title.substring(dot+1); 225 } 226 return title; 227 } 228 public boolean nonEmpty() { return items.size()>0; } 229 public void print(PrintStream out) { 230 for(int i = 0; i<items.size(); i++) { 231 Item item = items.get(i); 232 out.print(item.toString()); 233 if(item.propertyEditor!=null) 234 out.print(item.propertyEditor+"\n isP=" 235 +item.propertyEditor.isPaintable()+" sce=" 236 +item.propertyEditor.supportsCustomEditor()); 237 out.println(); 238 } 239 } 240 public void loadValues(Object dst) { 242 for (Item item : items) 243 item.set(dst, item.get(dst)); 244 } 245 public void saveValues(Object src) throws InstantiationException , IllegalAccessException { 246 Object orig = src.getClass().newInstance(); 247 for (Item item : items) { 248 Object value = item.get(src); 249 item.set(orig, item.get(orig)); item.set(src, value); 251 } 252 } 253 public int showDialog(Object obj, ActionListener apply) { 254 if(apply==null && obj instanceof ActionListener) apply = (ActionListener) obj; 255 JFrame frame = new JFrame(getTitle()); 256 frame.getContentPane().add(buildButtonedPanel(obj, apply)); 257 frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE); 258 frame.pack(); 259 frame.setVisible(true); 260 return 0; 261 } 262 public JComponent buildButtonedPanel(Object obj, ActionListener apply) { 263 return buildButtonedPanel(obj, apply, null); 264 } 265 public JComponent buildButtonedPanel(final Object obj, ActionListener apply, ActionListener cancel) { 266 if(apply==null && obj instanceof ActionListener) 267 apply = (ActionListener) obj; 268 final Box panel = Box.createVerticalBox(); 269 Box buttons = Box.createHorizontalBox(); 270 panel.add(buildPanel(obj)); 271 panel.add(Box.createVerticalGlue()); 272 panel.add(Box.createVerticalStrut(DEFAULT_PAD)); 273 panel.add(buttons); 274 buttons.add(Box.createHorizontalGlue()); 275 final RemovablePane rpane = new RemovablePane(panel); 276 if(apply != null) { 277 JButton apBut = new JButton(NbBundle.getMessage(PropertySheetInfo.class, "LBL_Apply")); 278 apBut.addActionListener(apply); 279 buttons.add(apBut); 280 if (cancel != null) 281 buttons.add(Box.createHorizontalStrut(DEFAULT_PAD / 2)); 282 } 283 if(cancel != null) { 284 final JButton canBut = new JButton(NbBundle.getMessage(PropertySheetInfo.class, "LBL_Cancel")); 285 if (cancel != null) { 286 rpane.addActionListener(cancel); 287 canBut.addActionListener(rpane); 288 } 289 buttons.add(canBut); 290 } 291 return rpane; 292 } 293 294 private static class RemovablePane extends JScrollPane 295 implements ActionListener { 296 RemovablePane(Component view) { 297 super(view); 298 } 299 public void actionPerformed(ActionEvent e) { 300 if (listener != null) 301 listener.actionPerformed(new ActionEvent(this, 0, 302 REMOVE_PANEL_ACTION)); 303 } 304 public void addActionListener(ActionListener l) { 305 listener = l; 306 } 307 private ActionListener listener = null; 308 } 309 private Component wrapLabel(String l) { 310 int length = l.length(); 311 int split; 312 if(length<50 || (split = l.indexOf(' ', l.length()>>1))<=0) 313 return new JLabel(l); 314 Box ret = Box.createVerticalBox(); 315 ret.add(new JLabel(l.substring(0,split).trim())); 316 ret.add(new JLabel(l.substring(split+1).trim())); 317 return ret; 318 } 319 public JComponent buildPanel() { 320 try { 321 Object obj = base.newInstance(); 322 loadValues(obj); 323 return buildPanel(obj); 324 } catch (Exception e) { 325 e.printStackTrace(); 326 return null; 327 } 328 } 329 public JComponent buildPanel(Object obj) { 330 return buildPanel(obj, null); 331 } 332 public JComponent buildPanel(final Object obj, Runnable incrementalHandler) { 333 try { 334 BeanDescriptor beanDesc = Introspector.getBeanInfo(base).getBeanDescriptor(); 335 Class customizerClass = beanDesc.getCustomizerClass(); 336 if (customizerClass != null) { 337 final Customizer customizer = (Customizer)customizerClass.newInstance(); 338 customizer.setObject(this); 339 if (customizer instanceof JComponent) 340 return (JComponent)customizer; 341 else { 342 JPanel wrapper = new JPanel(); 343 wrapper.add((Component)customizer); 344 return wrapper; 345 } 346 } 347 } catch (Exception e) { 348 e.printStackTrace(); 349 } 351 JPanel panel = null; 352 JTabbedPane tabs; 353 if(hasTabs) 354 tabs = new JTabbedPane(); 355 else 356 tabs=null; 357 String tabTitle = NbBundle.getMessage(PropertySheetInfo.class, "LBL_Properties_Tab"); 358 GridBagLayout gbl = new GridBagLayout(); 359 if(!base.isInstance(obj)) 360 throw new IllegalArgumentException ("Type mismatch"); 361 GridBagConstraints gbc = new GridBagConstraints(); 362 gbc.gridx = 0; 363 gbc.gridy = 0; 364 gbc.weightx = 1; 365 gbc.weighty = 1; 366 gbc.gridwidth = 1; 367 gbc.gridheight = 1; 368 gbc.fill = gbc.HORIZONTAL; 369 gbc.anchor = gbc.NORTHWEST; 370 int last = items.size()-1; 371 for(int i = 0; i<=last; i++) { 372 final Item item = items.get(i); 373 if(!item.valid()) continue; 374 gbc.weighty = i==last || items.get(i+1) instanceof TabItem ? 1000 : 1; 375 if(item instanceof TabItem) { 376 tabTitle = item.getLabel(); 377 panel = null; 378 continue; 379 } 380 if(panel==null) { 381 panel = new JPanel(); 382 panel.setLayout(gbl); 383 gbc.gridy = 0; 384 if(tabs!=null) { 385 panel.setBorder(BorderFactory.createEmptyBorder(12, 12, 12, 12)); 386 tabs.addTab(tabTitle, panel); 387 } 388 } 389 String label = item.getLabel(); 390 if(label!=null && label.length()>0) { 391 gbc.gridx = 0; 392 gbc.weightx = 0; 393 gbc.insets.right = 12; 394 panel.add(wrapLabel(label), gbc); 395 } 396 gbc.gridx = 1; 397 gbc.weightx = 1; 398 gbc.insets.right = 0; 399 Component comp = item.getInteractor(obj,incrementalHandler); 400 if(comp instanceof JScrollPane) 401 gbc.fill = gbc.BOTH; 402 panel.add(comp, gbc); 403 gbc.gridy++; 404 } 405 return tabs!=null ? (JComponent) tabs : panel!=null ? (JComponent) panel : new JLabel(NbBundle.getMessage(PropertySheetInfo.class, "MSG_No_items_to_edit")); 406 } 407 408 private static class PropertySheet { 409 private Component sheet; 410 private Box buttons; 411 private void addButton(JComponent b) { 412 if(buttons==null) { 413 buttons = Box.createHorizontalBox(); 414 Box nSheet = Box.createVerticalBox(); 415 nSheet.add(sheet); 416 nSheet.add(Box.createVerticalGlue()); 417 nSheet.add(buttons); 418 sheet = nSheet; 419 buttons.add(Box.createHorizontalGlue()); 420 } else buttons.add(Box.createHorizontalStrut(10)); 421 buttons.add(b); 422 } 423 public PropertySheet addButton(String label, final ActionListener al) { 424 JButton b = new JButton(label); 425 b.addActionListener(al); 426 addButton(b); 427 return this; 428 } 429 public Component getComponent() { return sheet; } 430 } 431 432 char[] pname = new char[2]; 433 private static String ns(Object s) { return s==null ? "" : s.toString(); } 434 public abstract class Item { 435 protected abstract Object get0(Object src); 436 protected abstract void set0(Object dst, Object value); 437 public String name; 438 boolean textarea = false; 439 private String label; 440 private Object oldValue; 441 protected boolean trnsient = false; 442 public final Object get(Object src) { 443 Object fromobj = get0(src); 444 if(oldValue==null && name!=null && fromobj!=null) { 445 if(fromobj instanceof Boolean ) 446 fromobj = Boolean.valueOf(prefs.getBoolean(name,((Boolean )fromobj).booleanValue())); 447 else if(fromobj instanceof String ) 448 fromobj = prefs.get(name,((String )fromobj)); 449 else if(fromobj instanceof Integer ) 450 fromobj = new Integer (prefs.getInt(name,((Integer )fromobj).intValue())); 451 else if(fromobj instanceof PickOne) 452 ((PickOne) fromobj).value = prefs.getInt(name,((PickOne)fromobj).value); 453 else logger.log(ErrorManager.USER, "Missing type "+fromobj.getClass().getName()+" for "+name); 454 set0(src,fromobj); 455 } 456 oldValue = fromobj; 457 return fromobj; 458 } 459 public final void set(Object dst, Object value) { 460 if(!trnsient && value!=null && !value.equals(oldValue)) { 461 if(value instanceof Boolean ) 462 prefs.putBoolean(name,((Boolean )value).booleanValue()); 463 else if(value instanceof Integer ) 464 prefs.putInt(name,((Integer )value).intValue()); 465 else if(value instanceof String ) 466 prefs.put(name,(String )value); 467 flush(); 468 } 469 set0(dst,value); 470 } 471 void flush() { 472 try { 473 prefs.flush(); 474 } catch (java.util.prefs.BackingStoreException e) { 475 e.printStackTrace(); 476 } 477 } 478 public void setLabel(String l) { 479 if(l.startsWith("textarea")) { 480 l = l.substring(8).trim(); 481 textarea = true; 482 } 483 label = l; 484 } 485 public String getLabel() { 486 if(label==null) { 487 String key = base.getSimpleName() + '.' + name; 490 try { 491 label = NbBundle.getBundle(base).getString(key); 492 } catch (MissingResourceException e) { 493 ErrorManager.getDefault().log(ErrorManager.WARNING, key + " property not localized"); 494 } 496 } 497 if(label==null) { 498 StringBuffer sb = new StringBuffer (); 500 char lastc = 0; 501 int limit = name.length(); 502 for(int i = 0; i<limit; i++) { 503 char c = name.charAt(i); 504 char nc; 505 if(c=='_') nc = ' '; 506 else if(Character.isUpperCase(c)) { 507 nc = Character.toLowerCase(c); 508 sb.append(' '); 509 } else nc = c; 510 if(lastc==0 && Character.isLowerCase(nc)) nc = Character.toUpperCase(nc); 511 sb.append(nc); 512 lastc = c; 513 } 514 label = sb.toString(); 515 } 516 return label; 517 } 518 public Class propType; 519 public PropertyEditor propertyEditor; 520 public Component getInteractor(final Object obj, final Runnable incrementalHandler) { 521 try { 522 final PropertyEditor pe = propertyEditor; 523 if(propType == String .class) { 525 final javax.swing.text.JTextComponent string; 526 if(textarea) { 527 JTextArea ta = new JTextArea(); 528 string = ta; 529 } else string = new JTextField(); 530 string.setText(ns(get(obj))); 531 string.addFocusListener(new java.awt.event.FocusAdapter () { 532 public void focusLost(FocusEvent e) { 533 Object newValue = string.getText(); 534 if(newValue.equals(get(obj))) return; 535 if(incrementalHandler!=null) EventQueue.invokeLater(incrementalHandler); 536 set(obj,newValue); 537 } 538 }); 539 if (textarea) { 540 Component c = new JScrollPane(string); 541 c.setPreferredSize(new Dimension(320,200)); 542 return c; 543 } 544 else 545 return string; 546 } else if(pe!=null && pe.supportsCustomEditor()) { 547 pe.setValue(get(obj)); 548 pe.addPropertyChangeListener(new PropertyChangeListener() { 549 public void propertyChange(PropertyChangeEvent evt) { 550 if(incrementalHandler!=null) EventQueue.invokeLater(incrementalHandler); 551 } 552 }); 553 return pe.getCustomEditor(); 554 } else if(propType == boolean.class) { 555 final JCheckBox cb = new JCheckBox(null,null,((Boolean )get(obj)).booleanValue()); 556 cb.addChangeListener(new javax.swing.event.ChangeListener () { 557 public void stateChanged(javax.swing.event.ChangeEvent ce) { 558 Object newValue = cb.isSelected() ? Boolean.TRUE : Boolean.FALSE; 559 if(newValue.equals(get(obj))) return; 560 if(incrementalHandler!=null) EventQueue.invokeLater(incrementalHandler); 561 set(obj,newValue); 562 } 563 }); 564 return cb; 565 } else if(propType == int.class) { 566 final JSpinner spinner = new JSpinner(); 567 spinner.setValue(get(obj)); 568 spinner.addChangeListener(new javax.swing.event.ChangeListener () { 569 public void stateChanged(javax.swing.event.ChangeEvent ce) { 570 Object newValue = spinner.getValue(); 571 if(newValue.equals(get(obj))) return; 572 if(incrementalHandler!=null) EventQueue.invokeLater(incrementalHandler); 573 set(obj,newValue); 574 } 575 }); 576 return spinner; 577 } else if(propType == PickOne.class) { 578 final PickOne pickone = (PickOne) get(obj); 579 if(pickone==null) return new JLabel(NbBundle.getMessage(PropertySheetInfo.class, "MSG_Null_PickOne")); 580 final JComboBox combo = new JComboBox(pickone.keys); 581 combo.setSelectedIndex(pickone.value); 582 combo.addItemListener(new ItemListener() { 583 public void itemStateChanged(ItemEvent e) { 584 if(e.getStateChange()==e.SELECTED) { 585 int nValue = combo.getSelectedIndex(); 586 if(nValue == pickone.value) return; 587 pickone.value = nValue; 588 prefs.putInt(name, nValue); 589 flush(); 590 if(incrementalHandler!=null) 591 EventQueue.invokeLater(incrementalHandler); 592 } 593 } 594 }); 595 return combo; 596 } else { 597 Object [] tags = pe.getTags(); 598 if(tags!=null && tags.length>0) { 599 JComboBox jcb = new JComboBox(tags); 600 jcb.setEditable(false); 601 jcb.setSelectedItem(pe.getAsText()); 602 return jcb; 603 } else { 604 return new JTextField(pe.getAsText()); 605 } 606 } 607 } catch(Exception e) { 608 e.printStackTrace(); 609 return new JLabel(e.toString()); 610 } 611 } 612 public boolean valid() { return true; } 613 614 protected final Object defaultItem(Class type) { 615 if (type == String .class) 616 return ""; 617 if (type == Boolean .class) 618 return false; 619 if (type == Integer .class) 620 return 0; 621 return null; 622 } 623 } 624 private class TabItem extends Item { 625 TabItem(String label) { 626 setLabel(label); 627 propType = String .class; 628 } 629 public Object get0(Object src) { return NbBundle.getMessage(PropertySheetInfo.class, "MSG_Invalid_TabItem"); } 630 public void set0(Object dst, Object value) { } 631 } 632 private class MethodItem extends Item { 633 Method setter, getter; 634 MethodItem(String nm, Class pt, PropertyEditor bi) { 635 propertyEditor = bi; 636 propType = pt; 637 name = nm; 638 } 639 public boolean valid() { return getter!=null && setter!=null; } 640 public Object get0(Object src) { 641 try { 642 Object ret = getter.invoke(src); 643 if (ret == null) 644 ret = defaultItem(getter.getReturnType()); 645 return ret; 646 } 647 catch(IllegalAccessException ice) { return null; } 648 catch(InvocationTargetException ice) { return null; } 649 } 650 public void set0(Object dst, Object value) { 651 try { 652 setter.invoke(dst,new Object []{value}); 653 } 654 catch(IllegalAccessException ice) { } 655 catch(InvocationTargetException ice) { } 656 } 657 public String toString() { return name+"/"+getter+"/"+setter; } 658 } 659 private class FieldItem extends Item { 660 Field field; 661 FieldItem(String nm, Field f, Class t, PropertyEditor bi) { 662 field = f; 663 name = nm; 664 propType = t; 665 propertyEditor = bi; 666 trnsient = Modifier.isTransient(f.getModifiers()); 667 } 668 public Object get0(Object src) { 669 try { 670 Object ret = field.get(src); 671 if (ret == null) 672 ret = defaultItem(field.getType()); 673 return ret; 674 } catch(IllegalAccessException ice) { 675 return null; 676 } 677 } 678 public void set0(Object dst, Object value) { 679 try { 680 field.set(dst,value); 681 } catch(IllegalAccessException ice) { 682 logger.log(ErrorManager.USER, "bad set, value=" + value); 683 } 684 } 685 public String toString() { return name+" "+propType; } 686 } 687 } 688 | Popular Tags |