Overview Package Class Use Source Tree Index Deprecated About
GNU Classpath (0.95)
Frames | No Frames

Source for java.beans.Encoder

 1:  /* Encoder.java
 2:  Copyright (C) 2005, 2006 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.DefaultExceptionListener;
 42:  import gnu.java.beans.encoder.ArrayPersistenceDelegate;
 43:  import gnu.java.beans.encoder.ClassPersistenceDelegate;
 44:  import gnu.java.beans.encoder.CollectionPersistenceDelegate;
 45:  import gnu.java.beans.encoder.MapPersistenceDelegate;
 46:  import gnu.java.beans.encoder.PrimitivePersistenceDelegate;
 47: 
 48:  import java.util.AbstractCollection;
 49:  import java.util.HashMap;
 50:  import java.util.IdentityHashMap;
 51: 
 52:  /**
 53:  * @author Robert Schuster (robertschuster@fsfe.org)
 54:  * @since 1.4
 55:  */
 56:  public class Encoder
 57: {
 58: 
 59:  /**
 60:  * An internal DefaultPersistenceDelegate instance that is used for every
 61:  * class that does not a have a special special PersistenceDelegate.
 62:  */
 63:  private static PersistenceDelegate defaultPersistenceDelegate;
 64: 
 65:  private static PersistenceDelegate fakePersistenceDelegate;
 66: 
 67:  /**
 68:  * Stores the relation Class->PersistenceDelegate.
 69:  */
 70:  private static HashMap delegates = new HashMap();
 71: 
 72:  /**
 73:  * Stores the relation oldInstance->newInstance
 74:  */
 75:  private IdentityHashMap candidates = new IdentityHashMap();
 76: 
 77:  private ExceptionListener exceptionListener;
 78: 
 79:  /**
 80:  * A simple number that is used to restrict the access to writeExpression and
 81:  * writeStatement. The rule is that both methods should only be used when an
 82:  * object is written to the stream (= writeObject). Therefore accessCounter is
 83:  * incremented just before the call to writeObject and decremented afterwards.
 84:  * Then writeStatement and writeExpression allow execution only if
 85:  * accessCounter is bigger than zero.
 86:  */
 87:  private int accessCounter = 0;
 88: 
 89:  public Encoder()
 90:  {
 91:  setupDefaultPersistenceDelegates();
 92: 
 93:  setExceptionListener(null);
 94:  }
 95: 
 96:  /**
 97:  * Sets up a bunch of {@link PersistenceDelegate} instances which are needed
 98:  * for the basic working of a {@link Encoder}s.
 99:  */
 100:  private static void setupDefaultPersistenceDelegates()
 101:  {
 102:  synchronized (delegates)
 103:  {
 104:  if (defaultPersistenceDelegate != null)
 105:  return;
 106: 
 107:  delegates.put(Class.class, new ClassPersistenceDelegate());
 108: 
 109:  PersistenceDelegate pd = new PrimitivePersistenceDelegate();
 110:  delegates.put(Boolean.class, pd);
 111:  delegates.put(Byte.class, pd);
 112:  delegates.put(Short.class, pd);
 113:  delegates.put(Integer.class, pd);
 114:  delegates.put(Long.class, pd);
 115:  delegates.put(Float.class, pd);
 116:  delegates.put(Double.class, pd);
 117: 
 118:  delegates.put(Object[].class, new ArrayPersistenceDelegate());
 119: 
 120:  pd = new CollectionPersistenceDelegate();
 121:  delegates.put(AbstractCollection.class, pd);
 122:  
 123:  pd = new MapPersistenceDelegate();
 124:  delegates.put(java.util.AbstractMap.class, pd);
 125:  delegates.put(java.util.Hashtable.class, pd);
 126:  
 127:  defaultPersistenceDelegate = new DefaultPersistenceDelegate();
 128:  delegates.put(Object.class, defaultPersistenceDelegate);
 129: 
 130:  // Creates a PersistenceDelegate implementation which is
 131:  // returned for 'null'. In practice this instance is
 132:  // not used in any way and is just here to be compatible
 133:  // with the reference implementation which returns a
 134:  // similar instance when calling getPersistenceDelegate(null) .
 135:  fakePersistenceDelegate = new PersistenceDelegate()
 136:  {
 137:  protected Expression instantiate(Object o, Encoder e)
 138:  {
 139:  return null;
 140:  }
 141:  };
 142: 
 143:  }
 144:  }
 145: 
 146:  protected void writeObject(Object o)
 147:  {
 148:  // 'null' has no PersistenceDelegate and will not
 149:  // create an Expression which has to be cloned.
 150:  // However subclasses should be aware that writeObject
 151:  // may be called with a 'null' argument and should
 152:  // write the proper representation of it.
 153:  if (o == null)
 154:  return;
 155: 
 156:  PersistenceDelegate pd = getPersistenceDelegate(o.getClass());
 157: 
 158:  accessCounter++;
 159:  pd.writeObject(o, this);
 160:  accessCounter--;
 161:  
 162:  }
 163: 
 164:  /**
 165:  * Sets the {@link ExceptionListener} instance to be used for reporting
 166:  * recorable exceptions in the instantiation and initialization sequence. If
 167:  * the argument is <code>null</code> a default instance will be used that
 168:  * prints the thrown exception to <code>System.err</code>.
 169:  */
 170:  public void setExceptionListener(ExceptionListener listener)
 171:  {
 172:  exceptionListener = (listener != null) 
 173:  ? listener : DefaultExceptionListener.INSTANCE;
 174:  }
 175: 
 176:  /**
 177:  * Returns the currently active {@link ExceptionListener} instance.
 178:  */
 179:  public ExceptionListener getExceptionListener()
 180:  {
 181:  return exceptionListener;
 182:  }
 183: 
 184:  public PersistenceDelegate getPersistenceDelegate(Class<?> type)
 185:  {
 186:  // This is not specified but the JDK behaves like this.
 187:  if (type == null)
 188:  return fakePersistenceDelegate;
 189: 
 190:  // Treats all array classes in the same way and assigns
 191:  // them a shared PersistenceDelegate implementation tailored
 192:  // for array instantation and initialization.
 193:  if (type.isArray())
 194:  return (PersistenceDelegate) delegates.get(Object[].class);
 195: 
 196:  PersistenceDelegate pd = (PersistenceDelegate) delegates.get(type);
 197: 
 198:  return (pd != null) ? pd : (PersistenceDelegate) defaultPersistenceDelegate;
 199:  }
 200: 
 201:  /**
 202:  * Sets the {@link PersistenceDelegate} instance for the given class.
 203:  * <p>
 204:  * Note: Throws a <code>NullPointerException</code> if the argument is
 205:  * <code>null</code>.
 206:  * </p>
 207:  * <p>
 208:  * Note: Silently ignores PersistenceDelegates for Array types and primitive
 209:  * wrapper classes.
 210:  * </p>
 211:  * <p>
 212:  * Note: Although this method is not declared <code>static</code> changes to
 213:  * the {@link PersistenceDelegate}s affect <strong>all</strong>
 214:  * {@link Encoder} instances. <strong>In this implementation</strong> the
 215:  * access is thread safe.
 216:  * </p>
 217:  */
 218:  public void setPersistenceDelegate(Class<?> type,
 219:  PersistenceDelegate delegate)
 220:  {
 221:  // If the argument is null this will cause a NullPointerException
 222:  // which is expected behavior.
 223: 
 224:  // This makes custom PDs for array, primitive types and their wrappers
 225:  // impossible but this is how the JDK behaves.
 226:  if (type.isArray() || type.isPrimitive() || type == Boolean.class
 227:  || type == Byte.class || type == Short.class || type == Integer.class
 228:  || type == Long.class || type == Float.class || type == Double.class)
 229:  return;
 230: 
 231:  synchronized (delegates)
 232:  {
 233:  delegates.put(type, delegate);
 234:  }
 235: 
 236:  }
 237: 
 238:  public Object remove(Object oldInstance)
 239:  {
 240:  return candidates.remove(oldInstance);
 241:  }
 242: 
 243:  /**
 244:  * Returns the replacement object which has been created by the encoder during
 245:  * the instantiation sequence or <code>null</code> if the object has not
 246:  * been processed yet.
 247:  * <p>
 248:  * Note: The <code>String</code> class acts as an endpoint for the
 249:  * inherently recursive algorithm of the {@link Encoder}. Therefore instances
 250:  * of <code>String</code> will always be returned by this method. In other
 251:  * words the assertion: <code>
 252:  * assert (anyEncoder.get(anyString) == anyString)
 253:  * </code<
 254:  * will always hold.</p>
 255:  *
 256:  * <p>Note: If <code>null</code> is requested, the result will
 257:  * always be <code>null</code>.</p>
 258:  */
 259:  public Object get(Object oldInstance)
 260:  {
 261:  // String instances are handled in a special way.
 262:  // No one knows why this is not officially specified
 263:  // because this is a rather important design decision.
 264:  return (oldInstance == null) ? null : 
 265:  (oldInstance.getClass() == String.class) ?
 266:  oldInstance : candidates.get(oldInstance);
 267:  }
 268: 
 269:  /**
 270:  * <p>
 271:  * Note: If you call this method not from within an object instantiation and
 272:  * initialization sequence it will be silently ignored.
 273:  * </p>
 274:  */
 275:  public void writeStatement(Statement stmt)
 276:  {
 277:  // Silently ignore out of bounds calls.
 278:  if (accessCounter <= 0)
 279:  return;
 280: 
 281:  Object target = stmt.getTarget();
 282: 
 283:  Object newTarget = get(target);
 284:  if (newTarget == null)
 285:  {
 286:  writeObject(target);
 287:  newTarget = get(target);
 288:  }
 289: 
 290:  Object[] args = stmt.getArguments();
 291:  Object[] newArgs = new Object[args.length];
 292: 
 293:  for (int i = 0; i < args.length; i++)
 294:  {
 295:  newArgs[i] = get(args[i]);
 296:  if (newArgs[i] == null || isImmutableType(args[i].getClass()))
 297:  {
 298:  writeObject(args[i]);
 299:  newArgs[i] = get(args[i]);
 300:  }
 301:  }
 302: 
 303:  Statement newStmt = new Statement(newTarget, stmt.getMethodName(), newArgs);
 304: 
 305:  try
 306:  {
 307:  newStmt.execute();
 308:  }
 309:  catch (Exception e)
 310:  {
 311:  exceptionListener.exceptionThrown(e);
 312:  }
 313: 
 314:  }
 315: 
 316:  /**
 317:  * <p>
 318:  * Note: If you call this method not from within an object instantiation and
 319:  * initialization sequence it will be silently ignored.
 320:  * </p>
 321:  */
 322:  public void writeExpression(Expression expr)
 323:  {
 324:  // Silently ignore out of bounds calls.
 325:  if (accessCounter <= 0)
 326:  return;
 327: 
 328:  Object target = expr.getTarget();
 329:  Object value = null;
 330:  Object newValue = null;
 331: 
 332:  try
 333:  {
 334:  value = expr.getValue();
 335:  }
 336:  catch (Exception e)
 337:  {
 338:  exceptionListener.exceptionThrown(e);
 339:  return;
 340:  }
 341:  
 342:  
 343:  newValue = get(value);
 344: 
 345:  if (newValue == null)
 346:  {
 347:  Object newTarget = get(target);
 348:  if (newTarget == null)
 349:  {
 350:  writeObject(target);
 351:  newTarget = get(target);
 352: 
 353:  // May happen if exception was thrown.
 354:  if (newTarget == null)
 355:  {
 356:  return;
 357:  }
 358:  }
 359: 
 360:  Object[] args = expr.getArguments();
 361:  Object[] newArgs = new Object[args.length];
 362: 
 363:  for (int i = 0; i < args.length; i++)
 364:  {
 365:  newArgs[i] = get(args[i]);
 366:  if (newArgs[i] == null || isImmutableType(args[i].getClass()))
 367:  {
 368:  writeObject(args[i]);
 369:  newArgs[i] = get(args[i]);
 370:  }
 371:  }
 372:  
 373:  Expression newExpr = new Expression(newTarget, expr.getMethodName(),
 374:  newArgs);
 375:  
 376:  // Fakes the result of Class.forName(<primitiveType>) to make it possible
 377:  // to hand such a type to the encoding process.
 378:  if (value instanceof Class && ((Class) value).isPrimitive())
 379:  newExpr.setValue(value);
 380:  
 381:  // Instantiates the new object.
 382:  try
 383:  {
 384:  newValue = newExpr.getValue();
 385: 
 386:  candidates.put(value, newValue);
 387:  }
 388:  catch (Exception e)
 389:  {
 390:  exceptionListener.exceptionThrown(e);
 391:  
 392:  return;
 393:  }
 394:  
 395:  writeObject(value);
 396: 
 397:  }
 398:  else if(value.getClass() == String.class || value.getClass() == Class.class)
 399:  {
 400:  writeObject(value);
 401:  }
 402: 
 403:  }
 404: 
 405:  /** Returns whether the given class is an immutable
 406:  * type which has to be handled differently when serializing it.
 407:  * 
 408:  * <p>Immutable objects always have to be instantiated instead of
 409:  * modifying an existing instance.</p>
 410:  * 
 411:  * @param type The class to test.
 412:  * @return Whether the first argument is an immutable type.
 413:  */
 414:  boolean isImmutableType(Class type)
 415:  {
 416:  return type == String.class || type == Class.class
 417:  || type == Integer.class || type == Boolean.class
 418:  || type == Byte.class || type == Short.class
 419:  || type == Long.class || type == Float.class
 420:  || type == Double.class;
 421:  }
 422:  
 423:  /** Sets the stream candidate for a given object.
 424:  * 
 425:  * @param oldObject The object given to the encoder.
 426:  * @param newObject The object the encoder generated.
 427:  */
 428:  void putCandidate(Object oldObject, Object newObject)
 429:  {
 430:  candidates.put(oldObject, newObject);
 431:  }
 432:  
 433: }
Overview Package Class Use Source Tree Index Deprecated About
GNU Classpath (0.95)

AltStyle によって変換されたページ (->オリジナル) /