Fix a bug that broke -freorder-functions
[official-gcc.git] / libjava / classpath / java / beans / Encoder.java
blobb3d232a312ac2a2ce22d5e281a74ab2c3a827153
1 /* Encoder.java
2 Copyright (C) 2005, 2006 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
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.
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.
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.
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.
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. */
39 package java.beans;
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;
48 import java.util.AbstractCollection;
49 import java.util.HashMap;
50 import java.util.IdentityHashMap;
52 /**
53 * @author Robert Schuster (robertschuster@fsfe.org)
54 * @since 1.4
56 public class Encoder
59 /**
60 * An internal DefaultPersistenceDelegate instance that is used for every
61 * class that does not a have a special special PersistenceDelegate.
63 private static PersistenceDelegate defaultPersistenceDelegate;
65 private static PersistenceDelegate fakePersistenceDelegate;
67 /**
68 * Stores the relation Class->PersistenceDelegate.
70 private static HashMap delegates = new HashMap();
72 /**
73 * Stores the relation oldInstance->newInstance
75 private IdentityHashMap candidates = new IdentityHashMap();
77 private ExceptionListener exceptionListener;
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.
87 private int accessCounter = 0;
89 public Encoder()
91 setupDefaultPersistenceDelegates();
93 setExceptionListener(null);
96 /**
97 * Sets up a bunch of {@link PersistenceDelegate} instances which are needed
98 * for the basic working of a {@link Encoder}s.
100 private static void setupDefaultPersistenceDelegates()
102 synchronized (delegates)
104 if (defaultPersistenceDelegate != null)
105 return;
107 delegates.put(Class.class, new ClassPersistenceDelegate());
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);
118 delegates.put(Object[].class, new ArrayPersistenceDelegate());
120 pd = new CollectionPersistenceDelegate();
121 delegates.put(AbstractCollection.class, pd);
123 pd = new MapPersistenceDelegate();
124 delegates.put(java.util.AbstractMap.class, pd);
125 delegates.put(java.util.Hashtable.class, pd);
127 defaultPersistenceDelegate = new DefaultPersistenceDelegate();
128 delegates.put(Object.class, defaultPersistenceDelegate);
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()
137 protected Expression instantiate(Object o, Encoder e)
139 return null;
146 protected void writeObject(Object o)
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;
156 PersistenceDelegate pd = getPersistenceDelegate(o.getClass());
158 accessCounter++;
159 pd.writeObject(o, this);
160 accessCounter--;
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>.
170 public void setExceptionListener(ExceptionListener listener)
172 exceptionListener = (listener != null)
173 ? listener : DefaultExceptionListener.INSTANCE;
177 * Returns the currently active {@link ExceptionListener} instance.
179 public ExceptionListener getExceptionListener()
181 return exceptionListener;
184 public PersistenceDelegate getPersistenceDelegate(Class<?> type)
186 // This is not specified but the JDK behaves like this.
187 if (type == null)
188 return fakePersistenceDelegate;
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);
196 PersistenceDelegate pd = (PersistenceDelegate) delegates.get(type);
198 return (pd != null) ? pd : defaultPersistenceDelegate;
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>
218 public void setPersistenceDelegate(Class<?> type,
219 PersistenceDelegate delegate)
221 // If the argument is null this will cause a NullPointerException
222 // which is expected behavior.
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;
231 synchronized (delegates)
233 delegates.put(type, delegate);
238 public Object remove(Object oldInstance)
240 return candidates.remove(oldInstance);
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>
256 * <p>Note: If <code>null</code> is requested, the result will
257 * always be <code>null</code>.</p>
259 public Object get(Object oldInstance)
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);
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>
275 public void writeStatement(Statement stmt)
277 // Silently ignore out of bounds calls.
278 if (accessCounter <= 0)
279 return;
281 Object target = stmt.getTarget();
283 Object newTarget = get(target);
284 if (newTarget == null)
286 writeObject(target);
287 newTarget = get(target);
290 Object[] args = stmt.getArguments();
291 Object[] newArgs = new Object[args.length];
293 for (int i = 0; i < args.length; i++)
295 newArgs[i] = get(args[i]);
296 if (newArgs[i] == null || isImmutableType(args[i].getClass()))
298 writeObject(args[i]);
299 newArgs[i] = get(args[i]);
303 Statement newStmt = new Statement(newTarget, stmt.getMethodName(), newArgs);
307 newStmt.execute();
309 catch (Exception e)
311 exceptionListener.exceptionThrown(e);
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>
322 public void writeExpression(Expression expr)
324 // Silently ignore out of bounds calls.
325 if (accessCounter <= 0)
326 return;
328 Object target = expr.getTarget();
329 Object value = null;
330 Object newValue = null;
334 value = expr.getValue();
336 catch (Exception e)
338 exceptionListener.exceptionThrown(e);
339 return;
343 newValue = get(value);
345 if (newValue == null)
347 Object newTarget = get(target);
348 if (newTarget == null)
350 writeObject(target);
351 newTarget = get(target);
353 // May happen if exception was thrown.
354 if (newTarget == null)
356 return;
360 Object[] args = expr.getArguments();
361 Object[] newArgs = new Object[args.length];
363 for (int i = 0; i < args.length; i++)
365 newArgs[i] = get(args[i]);
366 if (newArgs[i] == null || isImmutableType(args[i].getClass()))
368 writeObject(args[i]);
369 newArgs[i] = get(args[i]);
373 Expression newExpr = new Expression(newTarget, expr.getMethodName(),
374 newArgs);
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);
381 // Instantiates the new object.
384 newValue = newExpr.getValue();
386 candidates.put(value, newValue);
388 catch (Exception e)
390 exceptionListener.exceptionThrown(e);
392 return;
395 writeObject(value);
398 else if(value.getClass() == String.class || value.getClass() == Class.class)
400 writeObject(value);
405 /** Returns whether the given class is an immutable
406 * type which has to be handled differently when serializing it.
408 * <p>Immutable objects always have to be instantiated instead of
409 * modifying an existing instance.</p>
411 * @param type The class to test.
412 * @return Whether the first argument is an immutable type.
414 boolean isImmutableType(Class type)
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;
423 /** Sets the stream candidate for a given object.
425 * @param oldObject The object given to the encoder.
426 * @param newObject The object the encoder generated.
428 void putCandidate(Object oldObject, Object newObject)
430 candidates.put(oldObject, newObject);