1. /*
  2. * @(#)MetaData.java 1.31 03/01/23
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package java.beans;
  8. import java.lang.reflect.Array;
  9. import java.lang.reflect.Field;
  10. import java.lang.reflect.Method;
  11. import java.util.Vector;
  12. import java.util.Hashtable;
  13. import java.util.Iterator;
  14. import java.util.Enumeration;
  15. /*
  16. * Like the <code>Intropector</code>, the <code>MetaData</code> class
  17. * contains <em>meta</em> objects that describe the way
  18. * classes should express their state in terms of their
  19. * own public APIs.
  20. *
  21. * @see java.beans.Intropector
  22. *
  23. * @version 1.31 01/23/03
  24. * @author Philip Milne
  25. * @author Steve Langley
  26. */
  27. class NullPersistenceDelegate extends PersistenceDelegate {
  28. // Note this will be called by all classes when they reach the
  29. // top of their superclass chain.
  30. protected void initialize(Class type, Object oldInstance, Object newInstance, Encoder out) {
  31. }
  32. protected Expression instantiate(Object oldInstance, Encoder out) { return null; }
  33. public void writeObject(Object oldInstance, Encoder out) {
  34. // System.out.println("NullPersistenceDelegate:writeObject " + oldInstance);
  35. }
  36. }
  37. class PrimitivePersistenceDelegate extends PersistenceDelegate {
  38. protected boolean mutatesTo(Object oldInstance, Object newInstance) {
  39. return oldInstance.equals(newInstance);
  40. }
  41. protected Expression instantiate(Object oldInstance, Encoder out) {
  42. return new Expression(oldInstance, oldInstance.getClass(),
  43. "new", new Object[]{oldInstance.toString()});
  44. }
  45. }
  46. class ArrayPersistenceDelegate extends PersistenceDelegate {
  47. protected boolean mutatesTo(Object oldInstance, Object newInstance) {
  48. return (newInstance != null &&
  49. oldInstance.getClass() == newInstance.getClass() && // Also ensures the subtype is correct.
  50. Array.getLength(oldInstance) == Array.getLength(newInstance));
  51. }
  52. protected Expression instantiate(Object oldInstance, Encoder out) {
  53. // System.out.println("instantiate: " + type + " " + oldInstance);
  54. Class oldClass = oldInstance.getClass();
  55. return new Expression(oldInstance, Array.class, "newInstance",
  56. new Object[]{oldClass.getComponentType(),
  57. new Integer(Array.getLength(oldInstance))});
  58. }
  59. protected void initialize(Class type, Object oldInstance, Object newInstance, Encoder out) {
  60. int n = Array.getLength(oldInstance);
  61. for (int i = 0; i < n; i++) {
  62. Object index = new Integer(i);
  63. // Expression oldGetExp = new Expression(Array.class, "get", new Object[]{oldInstance, index});
  64. // Expression newGetExp = new Expression(Array.class, "get", new Object[]{newInstance, index});
  65. Expression oldGetExp = new Expression(oldInstance, "get", new Object[]{index});
  66. Expression newGetExp = new Expression(newInstance, "get", new Object[]{index});
  67. try {
  68. Object oldValue = oldGetExp.getValue();
  69. Object newValue = newGetExp.getValue();
  70. out.writeExpression(oldGetExp);
  71. if (!MetaData.equals(newValue, out.get(oldValue))) {
  72. // System.out.println("Not equal: " + newGetExp + " != " + actualGetExp);
  73. // invokeStatement(Array.class, "set", new Object[]{oldInstance, index, oldValue}, out);
  74. DefaultPersistenceDelegate.invokeStatement(oldInstance, "set", new Object[]{index, oldValue}, out);
  75. }
  76. }
  77. catch (Exception e) {
  78. // System.err.println("Warning:: failed to write: " + oldGetExp);
  79. out.getExceptionListener().exceptionThrown(e);
  80. }
  81. }
  82. }
  83. }
  84. class ProxyPersistenceDelegate extends PersistenceDelegate {
  85. protected Expression instantiate(Object oldInstance, Encoder out) {
  86. Class type = oldInstance.getClass();
  87. java.lang.reflect.Proxy p = (java.lang.reflect.Proxy)oldInstance;
  88. // This unappealing hack is not required but makes the
  89. // representation of EventHandlers much more concise.
  90. java.lang.reflect.InvocationHandler ih = java.lang.reflect.Proxy.getInvocationHandler(p);
  91. if (ih instanceof EventHandler) {
  92. EventHandler eh = (EventHandler)ih;
  93. Vector args = new Vector();
  94. args.add(type.getInterfaces()[0]);
  95. args.add(eh.getTarget());
  96. args.add(eh.getAction());
  97. if (eh.getEventPropertyName() != null) {
  98. args.add(eh.getEventPropertyName());
  99. }
  100. if (eh.getListenerMethodName() != null) {
  101. args.setSize(4);
  102. args.add(eh.getListenerMethodName());
  103. }
  104. return new Expression(oldInstance,
  105. EventHandler.class,
  106. "create",
  107. args.toArray());
  108. }
  109. return new Expression(oldInstance,
  110. java.lang.reflect.Proxy.class,
  111. "newProxyInstance",
  112. new Object[]{type.getClassLoader(),
  113. type.getInterfaces(),
  114. ih});
  115. }
  116. }
  117. // Strings
  118. class java_lang_String_PersistenceDelegate extends PersistenceDelegate {
  119. protected Expression instantiate(Object oldInstance, Encoder out) { return null; }
  120. public void writeObject(Object oldInstance, Encoder out) {
  121. // System.out.println("NullPersistenceDelegate:writeObject " + oldInstance);
  122. }
  123. }
  124. // Classes
  125. class java_lang_Class_PersistenceDelegate extends PersistenceDelegate {
  126. protected Expression instantiate(Object oldInstance, Encoder out) {
  127. Class c = (Class)oldInstance;
  128. // As of 1.3 it is not possible to call Class.forName("int"),
  129. // so we have to generate different code for primitive types.
  130. // This is needed for arrays whose subtype may be primitive.
  131. if (c.isPrimitive()) {
  132. Field field = MetaData.typeToField(c);
  133. if (field == null) {
  134. System.err.println("Unknown primitive type: " + c);
  135. }
  136. return new Expression(oldInstance, field, "get", new Object[]{null});
  137. }
  138. else if (oldInstance == String.class) {
  139. return new Expression(oldInstance, "", "getClass", new Object[]{});
  140. }
  141. else if (oldInstance == Class.class) {
  142. return new Expression(oldInstance, String.class, "getClass", new Object[]{});
  143. }
  144. else {
  145. return new Expression(oldInstance, Class.class, "forName", new Object[]{c.getName()});
  146. }
  147. }
  148. }
  149. // Fields
  150. class java_lang_reflect_Field_PersistenceDelegate extends PersistenceDelegate {
  151. protected Expression instantiate(Object oldInstance, Encoder out) {
  152. Field f = (Field)oldInstance;
  153. return new Expression(oldInstance,
  154. f.getDeclaringClass(),
  155. "getField",
  156. new Object[]{f.getName()});
  157. }
  158. }
  159. // Methods
  160. class java_lang_reflect_Method_PersistenceDelegate extends PersistenceDelegate {
  161. protected Expression instantiate(Object oldInstance, Encoder out) {
  162. Method m = (Method)oldInstance;
  163. return new Expression(oldInstance,
  164. m.getDeclaringClass(),
  165. "getMethod",
  166. new Object[]{m.getName(), m.getParameterTypes()});
  167. }
  168. }
  169. // Collections
  170. /*
  171. The Hashtable and AbstractMap classes have no common ancestor yet may
  172. be handled with a single persistence delegate: one which uses the methods
  173. of the Map insterface exclusively. Attatching the persistence delegates
  174. to the interfaces themselves is fraught however since, in the case of
  175. the Map, both the AbstractMap and HashMap classes are declared to
  176. implement the Map interface, leaving the obvious implementation prone
  177. to repeating their initialization. These issues and questions around
  178. the ordering of delegates attached to interfaces have lead us to
  179. ignore any delegates attached to interfaces and force all persistence
  180. delegates to be registered with concrete classes.
  181. */
  182. // Collection
  183. class java_util_Collection_PersistenceDelegate extends DefaultPersistenceDelegate {
  184. protected void initialize(Class type, Object oldInstance, Object newInstance, Encoder out) {
  185. java.util.Collection oldO = (java.util.Collection)oldInstance;
  186. java.util.Collection newO = (java.util.Collection)newInstance;
  187. if (newO.size() != 0) {
  188. invokeStatement(oldInstance, "clear", new Object[]{}, out);
  189. }
  190. for (Iterator i = oldO.iterator(); i.hasNext();) {
  191. invokeStatement(oldInstance, "add", new Object[]{i.next()}, out);
  192. }
  193. }
  194. }
  195. // List
  196. class java_util_List_PersistenceDelegate extends DefaultPersistenceDelegate {
  197. protected void initialize(Class type, Object oldInstance, Object newInstance, Encoder out) {
  198. java.util.List oldO = (java.util.List)oldInstance;
  199. java.util.List newO = (java.util.List)newInstance;
  200. int oldSize = oldO.size();
  201. int newSize = (newO == null) ? 0 : newO.size();
  202. if (oldSize < newSize) {
  203. invokeStatement(oldInstance, "clear", new Object[]{}, out);
  204. newSize = 0;
  205. }
  206. for (int i = 0; i < newSize; i++) {
  207. Object index = new Integer(i);
  208. Expression oldGetExp = new Expression(oldInstance, "get", new Object[]{index});
  209. Expression newGetExp = new Expression(newInstance, "get", new Object[]{index});
  210. try {
  211. Object oldValue = oldGetExp.getValue();
  212. Object newValue = newGetExp.getValue();
  213. out.writeExpression(oldGetExp);
  214. if (!MetaData.equals(newValue, out.get(oldValue))) {
  215. invokeStatement(oldInstance, "set", new Object[]{index, oldValue}, out);
  216. }
  217. }
  218. catch (Exception e) {
  219. out.getExceptionListener().exceptionThrown(e);
  220. }
  221. }
  222. for (int i = newSize; i < oldSize; i++) {
  223. invokeStatement(oldInstance, "add", new Object[]{oldO.get(i)}, out);
  224. }
  225. }
  226. }
  227. // Map
  228. class java_util_Map_PersistenceDelegate extends DefaultPersistenceDelegate {
  229. protected void initialize(Class type, Object oldInstance, Object newInstance, Encoder out) {
  230. // System.out.println("Initializing: " + newInstance);
  231. java.util.Map oldMap = (java.util.Map)oldInstance;
  232. java.util.Map newMap = (java.util.Map)newInstance;
  233. // Remove the new elements.
  234. // Do this first otherwise we undo the adding work.
  235. if (newMap != null) {
  236. java.util.Iterator newKeys = newMap.keySet().iterator();
  237. while(newKeys.hasNext()) {
  238. Object newKey = newKeys.next();
  239. // PENDING: This "key" is not in the right environment.
  240. if (!oldMap.containsKey(newKey)) {
  241. invokeStatement(oldInstance, "remove", new Object[]{newKey}, out);
  242. }
  243. }
  244. }
  245. // Add the new elements.
  246. java.util.Iterator oldKeys = oldMap.keySet().iterator();
  247. while(oldKeys.hasNext()) {
  248. Object oldKey = oldKeys.next();
  249. Expression oldGetExp = new Expression(oldInstance, "get", new Object[]{oldKey});
  250. // Pending: should use newKey.
  251. Expression newGetExp = new Expression(newInstance, "get", new Object[]{oldKey});
  252. try {
  253. Object oldValue = oldGetExp.getValue();
  254. Object newValue = newGetExp.getValue();
  255. out.writeExpression(oldGetExp);
  256. if (!MetaData.equals(newValue, out.get(oldValue))) {
  257. invokeStatement(oldInstance, "put", new Object[]{oldKey, oldValue}, out);
  258. }
  259. }
  260. catch (Exception e) {
  261. out.getExceptionListener().exceptionThrown(e);
  262. }
  263. }
  264. }
  265. }
  266. class java_util_AbstractCollection_PersistenceDelegate extends java_util_Collection_PersistenceDelegate {}
  267. class java_util_AbstractList_PersistenceDelegate extends java_util_List_PersistenceDelegate {}
  268. class java_util_AbstractMap_PersistenceDelegate extends java_util_Map_PersistenceDelegate {}
  269. class java_util_Hashtable_PersistenceDelegate extends java_util_Map_PersistenceDelegate {}
  270. // Beans
  271. class java_beans_beancontext_BeanContextSupport_PersistenceDelegate extends java_util_Collection_PersistenceDelegate {}
  272. // AWT
  273. class StaticFieldsPersistenceDelegate extends PersistenceDelegate {
  274. protected void installFields(Encoder out, Class cls) {
  275. Field fields[] = cls.getFields();
  276. for(int i = 0; i < fields.length; i++) {
  277. Field field = fields[i];
  278. // Don't install primitives, their identity will not be preserved
  279. // by wrapping.
  280. if (Object.class.isAssignableFrom(field.getType())) {
  281. out.writeExpression(new Expression(field, "get", new Object[]{null}));
  282. }
  283. }
  284. }
  285. protected Expression instantiate(Object oldInstance, Encoder out) {
  286. throw new RuntimeException("Unrecognized instance: " + oldInstance);
  287. }
  288. public void writeObject(Object oldInstance, Encoder out) {
  289. if (out.getAttribute(this) == null) {
  290. out.setAttribute(this, Boolean.TRUE);
  291. installFields(out, oldInstance.getClass());
  292. }
  293. super.writeObject(oldInstance, out);
  294. }
  295. }
  296. // SystemColor
  297. class java_awt_SystemColor_PersistenceDelegate extends StaticFieldsPersistenceDelegate {}
  298. // TextAttribute
  299. class java_awt_font_TextAttribute_PersistenceDelegate extends StaticFieldsPersistenceDelegate {}
  300. // MenuShortcut
  301. class java_awt_MenuShortcut_PersistenceDelegate extends PersistenceDelegate {
  302. protected Expression instantiate(Object oldInstance, Encoder out) {
  303. java.awt.MenuShortcut m = (java.awt.MenuShortcut)oldInstance;
  304. return new Expression(oldInstance, m.getClass(), "new",
  305. new Object[]{new Integer(m.getKey()), Boolean.valueOf(m.usesShiftModifier())});
  306. }
  307. }
  308. // Component
  309. class java_awt_Component_PersistenceDelegate extends DefaultPersistenceDelegate {
  310. protected void initialize(Class type, Object oldInstance, Object newInstance, Encoder out) {
  311. super.initialize(type, oldInstance, newInstance, out);
  312. java.awt.Component c = (java.awt.Component)oldInstance;
  313. java.awt.Component c2 = (java.awt.Component)newInstance;
  314. // The "background", "foreground" and "font" properties.
  315. // The foreground and font properties of Windows change from
  316. // null to defined values after the Windows are made visible -
  317. // special case them for now.
  318. if (!(oldInstance instanceof java.awt.Window)) {
  319. String[] fieldNames = new String[]{"background", "foreground", "font"};
  320. for(int i = 0; i < fieldNames.length; i++) {
  321. String name = fieldNames[i];
  322. Object oldValue = MetaData.getPrivateField(oldInstance, java.awt.Component.class, name, out.getExceptionListener());
  323. Object newValue = (newInstance == null) ? null : MetaData.getPrivateField(newInstance, java.awt.Component.class, name, out.getExceptionListener());
  324. if (oldValue != null && !oldValue.equals(newValue)) {
  325. invokeStatement(oldInstance, "set" + MetaData.capitalize(name), new Object[]{oldValue}, out);
  326. }
  327. }
  328. }
  329. // Bounds
  330. java.awt.Container p = c.getParent();
  331. if (p == null || p.getLayout() == null && !(p instanceof javax.swing.JLayeredPane)) {
  332. // Use the most concise construct.
  333. boolean locationCorrect = c.getLocation().equals(c2.getLocation());
  334. boolean sizeCorrect = c.getSize().equals(c2.getSize());
  335. if (!locationCorrect && !sizeCorrect) {
  336. invokeStatement(oldInstance, "setBounds", new Object[]{c.getBounds()}, out);
  337. }
  338. else if (!locationCorrect) {
  339. invokeStatement(oldInstance, "setLocation", new Object[]{c.getLocation()}, out);
  340. }
  341. else if (!sizeCorrect) {
  342. invokeStatement(oldInstance, "setSize", new Object[]{c.getSize()}, out);
  343. }
  344. }
  345. }
  346. }
  347. // Container
  348. class java_awt_Container_PersistenceDelegate extends DefaultPersistenceDelegate {
  349. protected void initialize(Class type, Object oldInstance, Object newInstance, Encoder out) {
  350. super.initialize(type, oldInstance, newInstance, out);
  351. // Ignore the children of a JScrollPane.
  352. // Pending(milne) find a better way to do this.
  353. if (oldInstance instanceof javax.swing.JScrollPane) {
  354. return;
  355. }
  356. java.awt.Container oldC = (java.awt.Container)oldInstance;
  357. java.awt.Component[] oldChildren = oldC.getComponents();
  358. java.awt.Container newC = (java.awt.Container)newInstance;
  359. java.awt.Component[] newChildren = (newC == null) ? new java.awt.Component[0] : newC.getComponents();
  360. // Pending. Assume all the new children are unaltered.
  361. for(int i = newChildren.length; i < oldChildren.length; i++) {
  362. invokeStatement(oldInstance, "add", new Object[]{oldChildren[i]}, out);
  363. }
  364. }
  365. }
  366. // Choice
  367. class java_awt_Choice_PersistenceDelegate extends DefaultPersistenceDelegate {
  368. protected void initialize(Class type, Object oldInstance, Object newInstance, Encoder out) {
  369. super.initialize(type, oldInstance, newInstance, out);
  370. java.awt.Choice m = (java.awt.Choice)oldInstance;
  371. java.awt.Choice n = (java.awt.Choice)newInstance;
  372. for (int i = n.getItemCount(); i < m.getItemCount(); i++) {
  373. invokeStatement(oldInstance, "add", new Object[]{m.getItem(i)}, out);
  374. }
  375. }
  376. }
  377. // Menu
  378. class java_awt_Menu_PersistenceDelegate extends DefaultPersistenceDelegate {
  379. protected void initialize(Class type, Object oldInstance, Object newInstance, Encoder out) {
  380. super.initialize(type, oldInstance, newInstance, out);
  381. java.awt.Menu m = (java.awt.Menu)oldInstance;
  382. java.awt.Menu n = (java.awt.Menu)newInstance;
  383. for (int i = n.getItemCount(); i < m.getItemCount(); i++) {
  384. invokeStatement(oldInstance, "add", new Object[]{m.getItem(i)}, out);
  385. }
  386. }
  387. }
  388. // MenuBar
  389. class java_awt_MenuBar_PersistenceDelegate extends DefaultPersistenceDelegate {
  390. protected void initialize(Class type, Object oldInstance, Object newInstance, Encoder out) {
  391. super.initialize(type, oldInstance, newInstance, out);
  392. java.awt.MenuBar m = (java.awt.MenuBar)oldInstance;
  393. java.awt.MenuBar n = (java.awt.MenuBar)newInstance;
  394. for (int i = n.getMenuCount(); i < m.getMenuCount(); i++) {
  395. invokeStatement(oldInstance, "add", new Object[]{m.getMenu(i)}, out);
  396. }
  397. }
  398. }
  399. // List
  400. class java_awt_List_PersistenceDelegate extends DefaultPersistenceDelegate {
  401. protected void initialize(Class type, Object oldInstance, Object newInstance, Encoder out) {
  402. super.initialize(type, oldInstance, newInstance, out);
  403. java.awt.List m = (java.awt.List)oldInstance;
  404. java.awt.List n = (java.awt.List)newInstance;
  405. for (int i = n.getItemCount(); i < m.getItemCount(); i++) {
  406. invokeStatement(oldInstance, "add", new Object[]{m.getItem(i)}, out);
  407. }
  408. }
  409. }
  410. // LayoutManagers
  411. // BorderLayout
  412. class java_awt_BorderLayout_PersistenceDelegate extends DefaultPersistenceDelegate {
  413. protected void initialize(Class type, Object oldInstance, Object newInstance, Encoder out) {
  414. super.initialize(type, oldInstance, newInstance, out);
  415. String[] locations = {"north", "south", "east", "west", "center"};
  416. String[] names = {java.awt.BorderLayout.NORTH, java.awt.BorderLayout.SOUTH, java.awt.BorderLayout.EAST, java.awt.BorderLayout.WEST, java.awt.BorderLayout.CENTER};
  417. for(int i = 0; i < locations.length; i++) {
  418. Object oldC = MetaData.getPrivateField(oldInstance, java.awt.BorderLayout.class, locations[i], out.getExceptionListener());
  419. Object newC = MetaData.getPrivateField(newInstance, java.awt.BorderLayout.class, locations[i], out.getExceptionListener());
  420. // Pending, assume any existing elements are OK.
  421. if (oldC != null && newC == null) {
  422. invokeStatement(oldInstance, "addLayoutComponent", new Object[]{oldC, names[i]}, out);
  423. }
  424. }
  425. }
  426. }
  427. // CardLayout
  428. class java_awt_CardLayout_PersistenceDelegate extends DefaultPersistenceDelegate {
  429. protected void initialize(Class type, Object oldInstance, Object newInstance, Encoder out) {
  430. super.initialize(type, oldInstance, newInstance, out);
  431. Hashtable tab = (Hashtable)MetaData.getPrivateField(oldInstance, java.awt.CardLayout.class, "tab", out.getExceptionListener());
  432. if (tab != null) {
  433. for(Enumeration e = tab.keys(); e.hasMoreElements();) {
  434. Object child = e.nextElement();
  435. invokeStatement(oldInstance, "addLayoutComponent",
  436. new Object[]{child, (String)tab.get(child)}, out);
  437. }
  438. }
  439. }
  440. }
  441. // GridBagLayout
  442. class java_awt_GridBagLayout_PersistenceDelegate extends DefaultPersistenceDelegate {
  443. protected void initialize(Class type, Object oldInstance, Object newInstance, Encoder out) {
  444. super.initialize(type, oldInstance, newInstance, out);
  445. Hashtable comptable = (Hashtable)MetaData.getPrivateField(oldInstance, java.awt.GridBagLayout.class, "comptable", out.getExceptionListener());
  446. if (comptable != null) {
  447. for(Enumeration e = comptable.keys(); e.hasMoreElements();) {
  448. Object child = e.nextElement();
  449. invokeStatement(oldInstance, "addLayoutComponent",
  450. new Object[]{child, comptable.get(child)}, out);
  451. }
  452. }
  453. }
  454. }
  455. // Swing
  456. // JFrame (If we do this for Window instead of JFrame, the setVisible call
  457. // will be issued before we have added all the children to the JFrame and
  458. // will appear blank).
  459. class javax_swing_JFrame_PersistenceDelegate extends DefaultPersistenceDelegate {
  460. protected void initialize(Class type, Object oldInstance, Object newInstance, Encoder out) {
  461. super.initialize(type, oldInstance, newInstance, out);
  462. java.awt.Window oldC = (java.awt.Window)oldInstance;
  463. java.awt.Window newC = (java.awt.Window)newInstance;
  464. boolean oldV = oldC.isVisible();
  465. boolean newV = newC.isVisible();
  466. if (newV != oldV) {
  467. // false means: don't execute this statement at write time.
  468. boolean executeStatements = out.executeStatements;
  469. out.executeStatements = false;
  470. invokeStatement(oldInstance, "setVisible", new Object[]{Boolean.valueOf(oldV)}, out);
  471. out.executeStatements = executeStatements;
  472. }
  473. }
  474. }
  475. // Models
  476. // DefaultListModel
  477. class javax_swing_DefaultListModel_PersistenceDelegate extends DefaultPersistenceDelegate {
  478. protected void initialize(Class type, Object oldInstance, Object newInstance, Encoder out) {
  479. // Note, the "size" property will be set here.
  480. super.initialize(type, oldInstance, newInstance, out);
  481. javax.swing.DefaultListModel m = (javax.swing.DefaultListModel)oldInstance;
  482. javax.swing.DefaultListModel n = (javax.swing.DefaultListModel)newInstance;
  483. for (int i = n.getSize(); i < m.getSize(); i++) {
  484. invokeStatement(oldInstance, "add", // Can also use "addElement".
  485. new Object[]{m.getElementAt(i)}, out);
  486. }
  487. }
  488. }
  489. // DefaultComboBoxModel
  490. class javax_swing_DefaultComboBoxModel_PersistenceDelegate extends DefaultPersistenceDelegate {
  491. protected void initialize(Class type, Object oldInstance, Object newInstance, Encoder out) {
  492. super.initialize(type, oldInstance, newInstance, out);
  493. javax.swing.DefaultComboBoxModel m = (javax.swing.DefaultComboBoxModel)oldInstance;
  494. for (int i = 0; i < m.getSize(); i++) {
  495. invokeStatement(oldInstance, "addElement", new Object[]{m.getElementAt(i)}, out);
  496. }
  497. }
  498. }
  499. // DefaultMutableTreeNode
  500. class javax_swing_tree_DefaultMutableTreeNode_PersistenceDelegate extends DefaultPersistenceDelegate {
  501. protected void initialize(Class type, Object oldInstance, Object
  502. newInstance, Encoder out) {
  503. super.initialize(type, oldInstance, newInstance, out);
  504. javax.swing.tree.DefaultMutableTreeNode m =
  505. (javax.swing.tree.DefaultMutableTreeNode)oldInstance;
  506. javax.swing.tree.DefaultMutableTreeNode n =
  507. (javax.swing.tree.DefaultMutableTreeNode)newInstance;
  508. for (int i = n.getChildCount(); i < m.getChildCount(); i++) {
  509. invokeStatement(oldInstance, "add", new
  510. Object[]{m.getChildAt(i)}, out);
  511. }
  512. }
  513. }
  514. // ToolTipManager
  515. class javax_swing_ToolTipManager_PersistenceDelegate extends PersistenceDelegate {
  516. protected Expression instantiate(Object oldInstance, Encoder out) {
  517. return new Expression(oldInstance, javax.swing.ToolTipManager.class,
  518. "sharedInstance", new Object[]{});
  519. }
  520. }
  521. // JComponents
  522. // JComponent (minimumSize, preferredSize & maximumSize).
  523. // Note the "size" methods in JComponent calculate default values
  524. // when their values are null. In Kestrel the new "isPreferredSizeSet"
  525. // family of methods can be used to disambiguate this situation.
  526. // We use the private fields here so that the code will work with
  527. // Kestrel beta.
  528. class javax_swing_JComponent_PersistenceDelegate extends DefaultPersistenceDelegate {
  529. protected void initialize(Class type, Object oldInstance, Object newInstance, Encoder out) {
  530. super.initialize(type, oldInstance, newInstance, out);
  531. int statementCount = 0;
  532. javax.swing.JComponent c = (javax.swing.JComponent)oldInstance;
  533. String[] fieldNames = new String[]{"minimumSize", "preferredSize", "maximumSize"};
  534. for(int i = 0; i < fieldNames.length; i++) {
  535. String name = fieldNames[i];
  536. Object value = MetaData.getPrivateField(c, javax.swing.JComponent.class, name, out.getExceptionListener());
  537. if (value != null) {
  538. // System.out.println("Setting " + name);
  539. invokeStatement(oldInstance, "set" + MetaData.capitalize(name), new Object[]{value}, out);
  540. }
  541. }
  542. }
  543. }
  544. // JTabbedPane
  545. class javax_swing_JTabbedPane_PersistenceDelegate extends DefaultPersistenceDelegate {
  546. protected void initialize(Class type, Object oldInstance, Object newInstance, Encoder out) {
  547. super.initialize(type, oldInstance, newInstance, out);
  548. javax.swing.JTabbedPane p = (javax.swing.JTabbedPane)oldInstance;
  549. for (int i = 0; i < p.getTabCount(); i++) {
  550. invokeStatement(oldInstance, "addTab",
  551. new Object[]{
  552. p.getTitleAt(i),
  553. p.getIconAt(i),
  554. p.getComponentAt(i)}, out);
  555. }
  556. }
  557. }
  558. // JMenu
  559. // Note that we do not need to state the initialiser for
  560. // JMenuItems since the getComponents() method defined in
  561. // Container will return all of the sub menu items that
  562. // need to be added to the menu item.
  563. // Not so for JMenu apparently.
  564. class javax_swing_JMenu_PersistenceDelegate extends DefaultPersistenceDelegate {
  565. protected void initialize(Class type, Object oldInstance, Object newInstance, Encoder out) {
  566. super.initialize(type, oldInstance, newInstance, out);
  567. javax.swing.JMenu m = (javax.swing.JMenu)oldInstance;
  568. java.awt.Component[] c = m.getMenuComponents();
  569. for (int i = 0; i < c.length; i++) {
  570. invokeStatement(oldInstance, "add", new Object[]{c[i]}, out);
  571. }
  572. }
  573. }
  574. /* XXX - doens't seem to work. Debug later.
  575. class javax_swing_JMenu_PersistenceDelegate extends DefaultPersistenceDelegate {
  576. protected void initialize(Class type, Object oldInstance, Object newInstance, Encoder out) {
  577. super.initialize(type, oldInstance, newInstance, out);
  578. javax.swing.JMenu m = (javax.swing.JMenu)oldInstance;
  579. javax.swing.JMenu n = (javax.swing.JMenu)newInstance;
  580. for (int i = n.getItemCount(); i < m.getItemCount(); i++) {
  581. invokeStatement(oldInstance, "add", new Object[]{m.getItem(i)}, out);
  582. }
  583. }
  584. }
  585. */
  586. class MetaData {
  587. private static Hashtable internalPersistenceDelegates = new Hashtable();
  588. private static Hashtable transientProperties = new Hashtable();
  589. private static PersistenceDelegate nullPersistenceDelegate = new NullPersistenceDelegate();
  590. private static PersistenceDelegate primitivePersistenceDelegate = new PrimitivePersistenceDelegate();
  591. private static PersistenceDelegate defaultPersistenceDelegate = new DefaultPersistenceDelegate();
  592. private static PersistenceDelegate arrayPersistenceDelegate;
  593. private static PersistenceDelegate proxyPersistenceDelegate;
  594. static {
  595. // Constructors.
  596. // util
  597. registerConstructor("java.util.Date", new String[]{"time"});
  598. // beans
  599. registerConstructor("java.beans.Statement", new String[]{"target", "methodName", "arguments"});
  600. registerConstructor("java.beans.Expression", new String[]{"target", "methodName", "arguments"});
  601. registerConstructor("java.beans.EventHandler", new String[]{"target", "action", "eventPropertyName", "listenerMethodName"});
  602. // awt
  603. registerConstructor("java.awt.Point", new String[]{"x", "y"});
  604. registerConstructor("java.awt.Dimension", new String[]{"width", "height"});
  605. registerConstructor("java.awt.Rectangle", new String[]{"x", "y", "width", "height"});
  606. registerConstructor("java.awt.Insets", new String[]{"top", "left", "bottom", "right"});
  607. registerConstructor("java.awt.Color", new String[]{"red", "green", "blue", "alpha"});
  608. registerConstructor("java.awt.Font", new String[]{"name", "style", "size"});
  609. registerConstructor("java.awt.Cursor", new String[]{"type"});
  610. registerConstructor("java.awt.GridBagConstraints",
  611. new String[]{"gridx", "gridy", "gridwidth", "gridheight",
  612. "weightx", "weighty",
  613. "anchor", "fill", "insets",
  614. "ipadx", "ipady"});
  615. registerConstructor("java.awt.ScrollPane", new String[]{"scrollbarDisplayPolicy"});
  616. // swing
  617. registerConstructor("javax.swing.plaf.FontUIResource", new String[]{"name", "style", "size"});
  618. registerConstructor("javax.swing.plaf.ColorUIResource", new String[]{"red", "green", "blue"});
  619. // registerConstructor(javax.swing.tree.DefaultTreeModel", new String[]{"root"});
  620. registerConstructor("javax.swing.tree.TreePath", new String[]{"path"});
  621. registerConstructor("javax.swing.OverlayLayout", new String[]{"target"});
  622. registerConstructor("javax.swing.BoxLayout", new String[]{"target", "axis"});
  623. registerConstructor("javax.swing.DefaultCellEditor", new String[]{"component"});
  624. /*
  625. This is required because the JSplitPane reveals a private layout class
  626. called BasicSplitPaneUI$BasicVerticalLayoutManager which changes with
  627. the orientation. To avoid the necessity for instantiating it we cause
  628. the orientation attribute to get set before the layout manager - that
  629. way the layout manager will be changed as a side effect. Unfortunately,
  630. the layout property belongs to the superclass and therefore precedes
  631. the orientation property. PENDING - we need to allow this kind of
  632. modification. For now, put the property in the constructor.
  633. */
  634. registerConstructor("javax.swing.JSplitPane", new String[]{"orientation"});
  635. // Try to synthesize the ImageIcon from its description.
  636. registerConstructor("javax.swing.ImageIcon", new String[]{"description"});
  637. // JButton's "label" and "actionCommand" properties are related,
  638. // use the label as a constructor argument to ensure that it is set first.
  639. // This remove the benign, but unnecessary, manipulation of actionCommand
  640. // property in the common case.
  641. registerConstructor("javax.swing.JButton", new String[]{"label"});
  642. // borders
  643. registerConstructor("javax.swing.border.BevelBorder", new String[]{"bevelType", "highlightOuter", "highlightInner", "shadowOuter", "shadowInner"});
  644. registerConstructor("javax.swing.plaf.BorderUIResource$BevelBorderUIResource", new String[]{"bevelType", "highlightOuter", "highlightInner", "shadowOuter", "shadowInner"});
  645. registerConstructor("javax.swing.border.CompoundBorder", new String[]{"outsideBorder", "insideBorder"});
  646. registerConstructor("javax.swing.plaf.BorderUIResource$CompoundBorderUIResource", new String[]{"outsideBorder", "insideBorder"});
  647. registerConstructor("javax.swing.border.EmptyBorder", new String[]{"top", "left", "bottom", "right"});
  648. registerConstructor("javax.swing.plaf.BorderUIResource$EmptyBorderUIResource", new String[]{"top", "left", "bottom", "right"});
  649. registerConstructor("javax.swing.border.EtchedBorder", new String[]{"etchType", "highlight", "shadow"});
  650. registerConstructor("javax.swing.plaf.BorderUIResource$EtchedBorderUIResource", new String[]{"etchType", "highlight", "shadow"});
  651. registerConstructor("javax.swing.border.LineBorder", new String[]{"lineColor", "thickness"});
  652. registerConstructor("javax.swing.plaf.BorderUIResource$LineBorderUIResource", new String[]{"lineColor", "thickness"});
  653. // Note this should check to see which of "color" and "tileIcon" is non-null.
  654. registerConstructor("javax.swing.border.MatteBorder", new String[]{"top", "left", "bottom", "right", "tileIcon"});
  655. registerConstructor("javax.swing.plaf.BorderUIResource$MatteBorderUIResource", new String[]{"top", "left", "bottom", "right", "tileIcon"});
  656. registerConstructor("javax.swing.border.SoftBevelBorder", new String[]{"bevelType", "highlightOuter", "highlightInner", "shadowOuter", "shadowInner"});
  657. // registerConstructorWithBadEqual("javax.swing.plaf.BorderUIResource$SoftBevelBorderUIResource", new String[]{"bevelType", "highlightOuter", "highlightInner", "shadowOuter", "shadowInner"});
  658. registerConstructor("javax.swing.border.TitledBorder", new String[]{"border", "title", "titleJustification", "titlePosition", "titleFont", "titleColor"});
  659. registerConstructor("javax.swing.plaf.BorderUIResource$TitledBorderUIResource", new String[]{"border", "title", "titleJustification", "titlePosition", "titleFont", "titleColor"});
  660. // Transient properties
  661. // awt
  662. // Infinite graphs.
  663. removeProperty("java.awt.geom.RectangularShape", "frame");
  664. // removeProperty("java.awt.Rectangle2D", "frame");
  665. // removeProperty("java.awt.Rectangle", "frame");
  666. removeProperty("java.awt.Rectangle", "bounds");
  667. removeProperty("java.awt.Dimension", "size");
  668. removeProperty("java.awt.Point", "location");
  669. // The color and font properties in Component need special treatment, see above.
  670. removeProperty("java.awt.Component", "foreground");
  671. removeProperty("java.awt.Component", "background");
  672. removeProperty("java.awt.Component", "font");
  673. // The visible property of Component needs special treatment because of Windows.
  674. removeProperty("java.awt.Component", "visible");
  675. // This property throws an exception if accessed when there is no child.
  676. removeProperty("java.awt.ScrollPane", "scrollPosition");
  677. // swing
  678. // The size properties in JComponent need special treatment, see above.
  679. removeProperty("javax.swing.JComponent", "minimumSize");
  680. removeProperty("javax.swing.JComponent", "preferredSize");
  681. removeProperty("javax.swing.JComponent", "maximumSize");
  682. // These properties have platform specific implementations
  683. // and should not appear in archives.
  684. removeProperty("javax.swing.ImageIcon", "image");
  685. removeProperty("javax.swing.ImageIcon", "imageObserver");
  686. // This property throws an exception when set in JMenu.
  687. // PENDING: Note we must delete the property from
  688. // the superclass even though the superclass's
  689. // implementation does not throw an error.
  690. // This needs some more thought.
  691. removeProperty("javax.swing.JMenu", "accelerator");
  692. removeProperty("javax.swing.JMenuItem", "accelerator");
  693. // This property unconditionally throws a "not implemented" exception.
  694. removeProperty("javax.swing.JMenuBar", "helpMenu");
  695. // The scrollBars in a JScrollPane are dynamic and should not
  696. // be archived. The row and columns headers are changed by
  697. // components like JTable on "addNotify".
  698. removeProperty("javax.swing.JScrollPane", "verticalScrollBar");
  699. removeProperty("javax.swing.JScrollPane", "horizontalScrollBar");
  700. removeProperty("javax.swing.JScrollPane", "rowHeader");
  701. removeProperty("javax.swing.JScrollPane", "columnHeader");
  702. removeProperty("javax.swing.JViewport", "extentSize");
  703. // Renderers need special treatment, since their properties
  704. // change during rendering.
  705. removeProperty("javax.swing.table.JTableHeader", "defaultRenderer");
  706. removeProperty("javax.swing.JList", "cellRenderer");
  707. removeProperty("javax.swing.JList", "selectedIndices");
  708. // The lead and anchor selection indexes are best ignored.
  709. // Selection is rarely something that should persist from
  710. // development to deployment.
  711. removeProperty("javax.swing.DefaultListSelectionModel", "leadSelectionIndex");
  712. removeProperty("javax.swing.DefaultListSelectionModel", "anchorSelectionIndex");
  713. // The selection must come after the text itself.
  714. removeProperty("javax.swing.JComboBox", "selectedIndex");
  715. // All selection information should come after the JTabbedPane is built
  716. removeProperty("javax.swing.JTabbedPane", "selectedIndex");
  717. removeProperty("javax.swing.JTabbedPane", "selectedComponent");
  718. // PENDING: The "disabledIcon" property is often computed from the icon property.
  719. removeProperty("javax.swing.AbstractButton", "disabledIcon");
  720. removeProperty("javax.swing.JLabel", "disabledIcon");
  721. // The caret property throws errors when it it set beyond
  722. // the extent of the text. We could just set it after the
  723. // text, but this is probably not something we want to archive anyway.
  724. removeProperty("javax.swing.text.JTextComponent", "caret");
  725. removeProperty("javax.swing.text.JTextComponent", "caretPosition");
  726. // The selectionStart must come after the text itself.
  727. removeProperty("javax.swing.text.JTextComponent", "selectionStart");
  728. removeProperty("javax.swing.text.JTextComponent", "selectionEnd");
  729. }
  730. /*pp*/ static boolean equals(Object o1, Object o2) {
  731. return (o1 == null) ? (o2 == null) : o1.equals(o2);
  732. }
  733. /*pp*/ static Field typeToField(Class type) {
  734. try {
  735. return Statement.typeToClass(type).getDeclaredField("TYPE");
  736. }
  737. catch (NoSuchFieldException e) {
  738. return null;
  739. }
  740. }
  741. /*pp*/ static Object getPrivateField(Object instance, Class declaringClass, String name, ExceptionListener el) {
  742. try {
  743. Field f = declaringClass.getDeclaredField(name);
  744. f.setAccessible(true);
  745. return f.get(instance);
  746. }
  747. catch (Exception e) {
  748. el.exceptionThrown(e);
  749. }
  750. return null;
  751. }
  752. private static boolean isPrimitive(Class type) {
  753. // return type == Boolean.class || type == Character.class || Number.class.isAssignableFrom(type);
  754. return Statement.primitiveTypeFor(type) != null;
  755. }
  756. // Package private entry points for Encoder.
  757. /*pp*/ static void setPersistenceDelegate(Class type, PersistenceDelegate persistenceDelegate) {
  758. setBeanAttribute(type, "persistenceDelegate", persistenceDelegate);
  759. }
  760. /*pp*/ static PersistenceDelegate getPersistenceDelegate(Class type) {
  761. if (type == null) {
  762. return nullPersistenceDelegate;
  763. }
  764. if (isPrimitive(type)) {
  765. return primitivePersistenceDelegate;
  766. }
  767. // The persistence delegate for arrays is non-trivial; instantiate it lazily.
  768. if (type.isArray()) {
  769. if (arrayPersistenceDelegate == null) {
  770. arrayPersistenceDelegate = new ArrayPersistenceDelegate();
  771. }
  772. return arrayPersistenceDelegate;
  773. }
  774. // Handle proxies lazily for backward compatibility with 1.2.
  775. try {
  776. if (java.lang.reflect.Proxy.isProxyClass(type)) {
  777. if (proxyPersistenceDelegate == null) {
  778. proxyPersistenceDelegate = new ProxyPersistenceDelegate();
  779. }
  780. return proxyPersistenceDelegate;
  781. }
  782. }
  783. catch(Exception e) {}
  784. // else if (type.getDeclaringClass() != null) {
  785. // return new DefaultPersistenceDelegate(new String[]{"this$0"});
  786. // }
  787. String typeName = type.getName();
  788. // Check to see if there are properties that have been lazily registered for removal.
  789. if (getBeanAttribute(type, "transient_init") == null) {
  790. Vector tp = (Vector)transientProperties.get(typeName);
  791. if (tp != null) {
  792. for(int i = 0; i < tp.size(); i++) {
  793. setPropertyAttribute(type, (String)tp.get(i), "transient", Boolean.TRUE);
  794. }
  795. }
  796. setBeanAttribute(type, "transient_init", Boolean.TRUE);
  797. }
  798. PersistenceDelegate pd = (PersistenceDelegate)getBeanAttribute(type, "persistenceDelegate");
  799. if (pd == null) {
  800. pd = getInternalPersistenceDelegate(typeName);
  801. if (pd != null) {
  802. return pd;
  803. }
  804. setInternalPersistenceDelegate(typeName, defaultPersistenceDelegate);
  805. try {
  806. String name = type.getName();
  807. // String unqualifiedClassName = name.substring(name.lastIndexOf('.')+1);
  808. Class c = Class.forName("java.beans." + name.replace('.', '_') + "_PersistenceDelegate");
  809. pd = (PersistenceDelegate)c.newInstance();
  810. setInternalPersistenceDelegate(typeName, pd);
  811. }
  812. catch (ClassNotFoundException e) {}
  813. catch (Exception e) {
  814. System.err.println("Internal error: " + e);
  815. }
  816. }
  817. return (pd != null) ? pd : defaultPersistenceDelegate;
  818. }
  819. /*pp*/ static String capitalize(String propertyName) {
  820. return propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
  821. }
  822. //
  823. // Wrapper for Introspector.getBeanInfo to handle exception handling.
  824. // Note: this relys on new 1.4 Introspector semantics which cache the BeanInfos
  825. /*pp*/ static BeanInfo getBeanInfo(Class type) {
  826. BeanInfo info = null;
  827. try {
  828. info = Introspector.getBeanInfo(type);
  829. } catch (Throwable e) {
  830. e.printStackTrace();
  831. }
  832. return info;
  833. }
  834. private static PropertyDescriptor getPropertyDescriptor(Class type, String propertyName) {
  835. BeanInfo info = getBeanInfo(type);
  836. PropertyDescriptor[] propertyDescriptors = info.getPropertyDescriptors();
  837. // System.out.println("Searching for: " + propertyName + " in " + type);
  838. for(int i = 0; i < propertyDescriptors.length; i++) {
  839. PropertyDescriptor pd = propertyDescriptors[i];
  840. if (propertyName.equals(pd.getName())) {
  841. return pd;
  842. }
  843. }
  844. return null;
  845. }
  846. private static void setPropertyAttribute(Class type, String property, String attribute, Object value) {
  847. PropertyDescriptor pd = getPropertyDescriptor(type, property);
  848. if (pd == null) {
  849. System.err.println("Warning: property " + property + " is not defined on " + type);
  850. return;
  851. }
  852. pd.setValue(attribute, value);
  853. }
  854. private static void setBeanAttribute(Class type, String attribute, Object value) {
  855. getBeanInfo(type).getBeanDescriptor().setValue(attribute, value);
  856. }
  857. private static Object getBeanAttribute(Class type, String attribute) {
  858. return getBeanInfo(type).getBeanDescriptor().getValue(attribute);
  859. }
  860. // MetaData registration
  861. private static void setInternalPersistenceDelegate(String typeName, PersistenceDelegate persistenceDelegate) {
  862. internalPersistenceDelegates.put(typeName, persistenceDelegate);
  863. }
  864. private static void registerConstructor(String typeName, String[] constructor) {
  865. setInternalPersistenceDelegate(typeName, new DefaultPersistenceDelegate(constructor));
  866. }
  867. private static PersistenceDelegate getInternalPersistenceDelegate(String typeName) {
  868. return (PersistenceDelegate)internalPersistenceDelegates.get(typeName);
  869. }
  870. private static void removeProperty(String typeName, String property) {
  871. Vector tp = (Vector)transientProperties.get(typeName);
  872. if (tp == null) {
  873. tp = new Vector();
  874. transientProperties.put(typeName, tp);
  875. }
  876. tp.add(property);
  877. }
  878. }