GNU Classpath (0.20) | |
Frames | No Frames |
1: /* Encoder.java 2: Copyright (C) 2005 Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: 39: package java.beans; 40: 41: import gnu.java.beans.encoder.ArrayPersistenceDelegate; 42: import gnu.java.beans.encoder.ClassPersistenceDelegate; 43: import gnu.java.beans.encoder.CollectionPersistenceDelegate; 44: import gnu.java.beans.encoder.MapPersistenceDelegate; 45: import gnu.java.beans.encoder.PrimitivePersistenceDelegate; 46: 47: import java.util.ArrayList; 48: import java.util.HashMap; 49: import java.util.HashSet; 50: import java.util.IdentityHashMap; 51: import java.util.LinkedHashSet; 52: import java.util.LinkedList; 53: import java.util.TreeMap; 54: import java.util.TreeSet; 55: import java.util.Vector; 56: 57: /** 58: * @author Robert Schuster (robertschuster@fsfe.org) 59: * @since 1.4 60: */ 61: public class Encoder 62: { 63: 64: /** 65: * An internal DefaultPersistenceDelegate instance that is used for every 66: * class that does not a have a special special PersistenceDelegate. 67: */ 68: private static PersistenceDelegate defaultPersistenceDelegate; 69: 70: private static PersistenceDelegate fakePersistenceDelegate; 71: 72: /** 73: * Stores the relation Class->PersistenceDelegate. 74: */ 75: private static HashMap delegates = new HashMap(); 76: 77: /** 78: * Stores the relation oldInstance->newInstance 79: */ 80: private IdentityHashMap candidates = new IdentityHashMap(); 81: 82: private ExceptionListener exceptionListener; 83: 84: /** 85: * A simple number that is used to restrict the access to writeExpression and 86: * writeStatement. The rule is that both methods should only be used when an 87: * object is written to the stream (= writeObject). Therefore accessCounter is 88: * incremented just before the call to writeObject and decremented afterwards. 89: * Then writeStatement and writeExpression allow execution only if 90: * accessCounter is bigger than zero. 91: */ 92: private int accessCounter = 0; 93: 94: public Encoder() 95: { 96: setupDefaultPersistenceDelegates(); 97: 98: setExceptionListener(null); 99: } 100: 101: /** 102: * Sets up a bunch of {@link PersistenceDelegate} instances which are needed 103: * for the basic working of a {@link Encoder}s. 104: */ 105: private static void setupDefaultPersistenceDelegates() 106: { 107: synchronized (delegates) 108: { 109: if (defaultPersistenceDelegate != null) 110: return; 111: 112: delegates.put(Class.class, new ClassPersistenceDelegate()); 113: 114: PersistenceDelegate pd = new PrimitivePersistenceDelegate(); 115: delegates.put(Boolean.class, pd); 116: delegates.put(Byte.class, pd); 117: delegates.put(Short.class, pd); 118: delegates.put(Integer.class, pd); 119: delegates.put(Long.class, pd); 120: delegates.put(Float.class, pd); 121: delegates.put(Double.class, pd); 122: 123: delegates.put(Object[].class, new ArrayPersistenceDelegate()); 124: 125: pd = new CollectionPersistenceDelegate(); 126: delegates.put(ArrayList.class, pd); 127: delegates.put(LinkedList.class, pd); 128: delegates.put(Vector.class, pd); 129: delegates.put(HashSet.class, pd); 130: delegates.put(LinkedHashSet.class, pd); 131: delegates.put(TreeSet.class, pd); 132: 133: pd = new MapPersistenceDelegate(); 134: delegates.put(HashMap.class, pd); 135: delegates.put(TreeMap.class, pd); 136: delegates.put(java.util.Hashtable.class, pd); 137: delegates.put(java.util.IdentityHashMap.class, pd); 138: 139: delegates.put(java.util.LinkedHashMap.class, pd); 140: delegates.put(java.util.Properties.class, pd); 141: 142: delegates.put(java.awt.RenderingHints.class, pd); 143: delegates.put(java.util.WeakHashMap.class, pd); 144: delegates.put(javax.swing.UIDefaults.class, pd); 145: 146: // TODO: These classes need to be implemented first 147: //delegates.put(java.security.AuthProvider.class, pd); 148: //delegates.put(java.util.concurrent.ConcurrentHashMap.class, pd); 149: //delegates.put(java.util.EnumMap.class, pd); 150: //delegates.put(javax.management.openmbean.TabularDataSupport.class, pd); 151: 152: defaultPersistenceDelegate = new DefaultPersistenceDelegate(); 153: delegates.put(Object.class, defaultPersistenceDelegate); 154: 155: // Creates a PersistenceDelegate implementation which is 156: // returned for 'null'. In practice this instance is 157: // not used in any way and is just here to be compatible 158: // with the reference implementation which returns a 159: // similar instance when calling getPersistenceDelegate(null) . 160: fakePersistenceDelegate = new PersistenceDelegate() 161: { 162: protected Expression instantiate(Object o, Encoder e) 163: { 164: return null; 165: } 166: }; 167: 168: } 169: } 170: 171: protected void writeObject(Object o) 172: { 173: // 'null' has no PersistenceDelegate and will not 174: // create an Expression which has to be cloned. 175: // However subclasses should be aware that writeObject 176: // may be called with a 'null' argument and should 177: // write the proper representation of it. 178: if (o == null) 179: return; 180: 181: PersistenceDelegate pd = getPersistenceDelegate(o.getClass()); 182: 183: accessCounter++; 184: pd.writeObject(o, this); 185: accessCounter--; 186: 187: } 188: 189: /** 190: * Sets the {@link ExceptionListener} instance to be used for reporting 191: * recorable exceptions in the instantiation and initialization sequence. If 192: * the argument is <code>null</code> a default instance will be used that 193: * prints the thrown exception to <code>System.err</code>. 194: */ 195: public void setExceptionListener(ExceptionListener listener) 196: { 197: exceptionListener = (listener != null) ? listener : new ExceptionListener() 198: { 199: public void exceptionThrown(Exception e) 200: { 201: System.err.println("exception thrown: " + e); 202: e.printStackTrace(); 203: } 204: }; 205: } 206: 207: /** 208: * Returns the currently active {@link ExceptionListener} instance. 209: */ 210: public ExceptionListener getExceptionListener() 211: { 212: return exceptionListener; 213: } 214: 215: public PersistenceDelegate getPersistenceDelegate(Class type) 216: { 217: // This is not specified but the JDK behaves like this. 218: if (type == null) 219: return fakePersistenceDelegate; 220: 221: // Treats all array classes in the same way and assigns 222: // them a shared PersistenceDelegate implementation tailored 223: // for array instantation and initialization. 224: if (type.isArray()) 225: return (PersistenceDelegate) delegates.get(Object[].class); 226: 227: PersistenceDelegate pd = (PersistenceDelegate) delegates.get(type); 228: 229: return (pd != null) ? pd : (PersistenceDelegate) defaultPersistenceDelegate; 230: } 231: 232: /** 233: * Sets the {@link PersistenceDelegate} instance for the given class. 234: * <p> 235: * Note: Throws a <code>NullPointerException</code> if the argument is 236: * <code>null</code>. 237: * </p> 238: * <p> 239: * Note: Silently ignores PersistenceDelegates for Array types and primitive 240: * wrapper classes. 241: * </p> 242: * <p> 243: * Note: Although this method is not declared <code>static</code> changes to 244: * the {@link PersistenceDelegate}s affect <strong>all</strong> 245: * {@link Encoder} instances. <strong>In this implementation</strong> the 246: * access is thread safe. 247: * </p> 248: */ 249: public void setPersistenceDelegate(Class type, PersistenceDelegate delegate) 250: { 251: // If the argument is null this will cause a NullPointerException 252: // which is expected behavior. 253: 254: // This makes custom PDs for array, primitive types and their wrappers 255: // impossible but this is how the JDK behaves. 256: if (type.isArray() || type.isPrimitive() || type == Boolean.class 257: || type == Byte.class || type == Short.class || type == Integer.class 258: || type == Long.class || type == Float.class || type == Double.class) 259: return; 260: 261: synchronized (delegates) 262: { 263: delegates.put(type, delegate); 264: } 265: 266: } 267: 268: public Object remove(Object oldInstance) 269: { 270: return candidates.remove(oldInstance); 271: } 272: 273: /** 274: * Returns the replacement object which has been created by the encoder during 275: * the instantiation sequence or <code>null</code> if the object has not 276: * been processed yet. 277: * <p> 278: * Note: The <code>String</code> class acts as an endpoint for the 279: * inherently recursive algorithm of the {@link Encoder}. Therefore instances 280: * of <code>String</code> will always be returned by this method. In other 281: * words the assertion: <code> 282: * assert (anyEncoder.get(anyString) == anyString) 283: * </code< 284: * will always hold.</p> 285: * 286: * <p>Note: If <code>null</code> is requested, the result will 287: * always be <code>null</code>.</p> 288: */ 289: public Object get(Object oldInstance) 290: { 291: // String instances are handled in a special way. 292: // No one knows why this is not officially specified 293: // because this is a rather important design decision. 294: return (oldInstance == null) ? null : 295: (oldInstance.getClass() == String.class) ? 296: oldInstance : candidates.get(oldInstance); 297: } 298: 299: /** 300: * <p> 301: * Note: If you call this method not from within an object instantiation and 302: * initialization sequence it will be silently ignored. 303: * </p> 304: */ 305: public void writeStatement(Statement stmt) 306: { 307: // Silently ignore out of bounds calls. 308: if (accessCounter <= 0) 309: return; 310: 311: Object target = stmt.getTarget(); 312: 313: Object newTarget = get(target); 314: if (newTarget == null) 315: { 316: writeObject(target); 317: newTarget = get(target); 318: } 319: 320: Object[] args = stmt.getArguments(); 321: Object[] newArgs = new Object[args.length]; 322: 323: for (int i = 0; i < args.length; i++) 324: { 325: newArgs[i] = get(args[i]); 326: if (newArgs[i] == null || isImmutableType(args[i].getClass())) 327: { 328: writeObject(args[i]); 329: newArgs[i] = get(args[i]); 330: } 331: } 332: 333: Statement newStmt = new Statement(newTarget, stmt.getMethodName(), newArgs); 334: 335: try 336: { 337: newStmt.execute(); 338: } 339: catch (Exception e) 340: { 341: exceptionListener.exceptionThrown(e); 342: } 343: 344: } 345: 346: /** 347: * <p> 348: * Note: If you call this method not from within an object instantiation and 349: * initialization sequence it will be silently ignored. 350: * </p> 351: */ 352: public void writeExpression(Expression expr) 353: { 354: // Silently ignore out of bounds calls. 355: if (accessCounter <= 0) 356: return; 357: 358: Object target = expr.getTarget(); 359: Object value = null; 360: Object newValue = null; 361: 362: try 363: { 364: value = expr.getValue(); 365: } 366: catch (Exception e) 367: { 368: exceptionListener.exceptionThrown(e); 369: return; 370: } 371: 372: 373: newValue = get(value); 374: 375: if (newValue == null) 376: { 377: Object newTarget = get(target); 378: if (newTarget == null) 379: { 380: writeObject(target); 381: newTarget = get(target); 382: 383: // May happen if exception was thrown. 384: if (newTarget == null) 385: { 386: return; 387: } 388: } 389: 390: Object[] args = expr.getArguments(); 391: Object[] newArgs = new Object[args.length]; 392: 393: for (int i = 0; i < args.length; i++) 394: { 395: newArgs[i] = get(args[i]); 396: if (newArgs[i] == null || isImmutableType(args[i].getClass())) 397: { 398: writeObject(args[i]); 399: newArgs[i] = get(args[i]); 400: } 401: } 402: 403: Expression newExpr = new Expression(newTarget, expr.getMethodName(), 404: newArgs); 405: 406: // Fakes the result of Class.forName(<primitiveType>) to make it possible 407: // to hand such a type to the encoding process. 408: if (value instanceof Class && ((Class) value).isPrimitive()) 409: newExpr.setValue(value); 410: 411: // Instantiates the new object. 412: try 413: { 414: newValue = newExpr.getValue(); 415: 416: candidates.put(value, newValue); 417: } 418: catch (Exception e) 419: { 420: exceptionListener.exceptionThrown(e); 421: 422: return; 423: } 424: 425: writeObject(value); 426: 427: } 428: else if(value.getClass() == String.class || value.getClass() == Class.class) 429: { 430: writeObject(value); 431: } 432: 433: } 434: 435: /** Returns whether the given class is an immutable 436: * type which has to be handled differently when serializing it. 437: * 438: * <p>Immutable objects always have to be instantiated instead of 439: * modifying an existing instance.</p> 440: * 441: * @param type The class to test. 442: * @return Whether the first argument is an immutable type. 443: */ 444: boolean isImmutableType(Class type) 445: { 446: return type == String.class || type == Class.class 447: || type == Integer.class || type == Boolean.class 448: || type == Byte.class || type == Short.class 449: || type == Long.class || type == Float.class 450: || type == Double.class; 451: } 452: 453: /** Sets the stream candidate for a given object. 454: * 455: * @param oldObject The object given to the encoder. 456: * @param newObject The object the encoder generated. 457: */ 458: void putCandidate(Object oldObject, Object newObject) 459: { 460: candidates.put(oldObject, newObject); 461: } 462: 463: }
GNU Classpath (0.20) |