harmony-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From le...@apache.org
Subject svn commit: r556804 - in /harmony/enhanced/classlib/trunk/modules/beans: META-INF/ src/main/java/java/beans/ src/main/java/org/apache/harmony/beans/ src/test/java/org/apache/harmony/beans/tests/java/beans/
Date Tue, 17 Jul 2007 03:49:40 GMT
Author: leoli
Date: Mon Jul 16 20:49:39 2007
New Revision: 556804

URL: http://svn.apache.org/viewvc?view=rev&rev=556804
Log:
Apply patch for HARMONY-4327([classlib][beans] Current bean implementation does not persist Collection and its subclasses correctly).

Added:
    harmony/enhanced/classlib/trunk/modules/beans/src/main/java/java/beans/UtilCollectionPersistenceDelegate.java   (with props)
    harmony/enhanced/classlib/trunk/modules/beans/src/main/java/org/apache/harmony/beans/PrimitiveWrapperPersistenceDelegate.java   (with props)
Modified:
    harmony/enhanced/classlib/trunk/modules/beans/META-INF/MANIFEST.MF
    harmony/enhanced/classlib/trunk/modules/beans/src/main/java/java/beans/Encoder.java
    harmony/enhanced/classlib/trunk/modules/beans/src/main/java/java/beans/XMLEncoder.java
    harmony/enhanced/classlib/trunk/modules/beans/src/test/java/org/apache/harmony/beans/tests/java/beans/PersistenceDelegateTest.java

Modified: harmony/enhanced/classlib/trunk/modules/beans/META-INF/MANIFEST.MF
URL: http://svn.apache.org/viewvc/harmony/enhanced/classlib/trunk/modules/beans/META-INF/MANIFEST.MF?view=diff&rev=556804&r1=556803&r2=556804
==============================================================================
--- harmony/enhanced/classlib/trunk/modules/beans/META-INF/MANIFEST.MF (original)
+++ harmony/enhanced/classlib/trunk/modules/beans/META-INF/MANIFEST.MF Mon Jul 16 20:49:39 2007
@@ -14,6 +14,7 @@
 Import-Package: java.applet,
  java.awt,
  java.awt.event;resolution:=optional,
+ java.awt.font,
  java.io,
  java.lang,
  java.lang.reflect,

Modified: harmony/enhanced/classlib/trunk/modules/beans/src/main/java/java/beans/Encoder.java
URL: http://svn.apache.org/viewvc/harmony/enhanced/classlib/trunk/modules/beans/src/main/java/java/beans/Encoder.java?view=diff&rev=556804&r1=556803&r2=556804
==============================================================================
--- harmony/enhanced/classlib/trunk/modules/beans/src/main/java/java/beans/Encoder.java (original)
+++ harmony/enhanced/classlib/trunk/modules/beans/src/main/java/java/beans/Encoder.java Mon Jul 16 20:49:39 2007
@@ -17,287 +17,364 @@
 
 package java.beans;
 
-import java.util.HashMap;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.Collection;
+import java.util.Hashtable;
 import java.util.IdentityHashMap;
-import java.util.Vector;
 
-import org.apache.harmony.beans.DefaultPersistenceDelegatesFactory;
-import org.apache.harmony.beans.NullPersistenceDelegate;
-import org.apache.harmony.beans.ObjectNode;
+import org.apache.harmony.beans.*;
 
+/**
+ * The <code>Encoder</code>, together with <code>PersistenceDelegate</code>
+ * s, can encode an object into a series of java statements. By executing these
+ * statements, a new object can be created and it will has the same state as the
+ * original object which has been passed to the encoder. Here "has the same
+ * state" means the two objects are indistinguishable from their public API.
+ * <p>
+ * The <code>Encoder</code> and <code>PersistenceDelegate</code> s do this
+ * by creating copies of the input object and all objects it references. The
+ * copy process continues recursively util every object in the object graph has
+ * its new copy and the new version has the same state as the old version. All
+ * statements used to create those new objects and executed on them during the
+ * process form the result of encoding.
+ * </p>
+ * 
+ */
+@SuppressWarnings("unchecked")
 public class Encoder {
 
-    private ExceptionListener exceptionListener = defaultExListener;
+	private static final Hashtable delegates = new Hashtable();
 
-    private static final ExceptionListener defaultExListener = new DefaultExceptionListener();
+	private static final DefaultPersistenceDelegate defaultPD = new DefaultPersistenceDelegate();
 
-    private static class DefaultExceptionListener implements ExceptionListener {
+	private static final ArrayPersistenceDelegate arrayPD = new ArrayPersistenceDelegate();
 
-        public void exceptionThrown(Exception exception) {
-            System.err.println("Exception during encoding:" + exception); //$NON-NLS-1$
-            System.err.println("Continue...");
-        }
-
-    }
-
-    private static final HashMap<Class<?>, PersistenceDelegate> persistenceDelegates = new HashMap<Class<?>, PersistenceDelegate>();
-
-    Vector<Object> roots = new Vector<Object>();
-
-    IdentityHashMap<Object, ObjectNode> nodes = new IdentityHashMap<Object, ObjectNode>();
-
-    private IdentityHashMap oldNewMap = new IdentityHashMap();
-
-    public Encoder() {
-        super();
-    }
-
-    public Object get(Object oldInstance) {
-        if (oldInstance == null || oldInstance instanceof String
-                || oldInstance == String.class) {
-            return oldInstance;
-        }
-
-        return oldNewMap.get(oldInstance);
-    }
-
-    public Object remove(Object oldInstance) {
-        // TODO - notify references on node deletion
-        if (oldInstance == null) {
-            return null;
-        }
-
-        getValue(nodes.remove(oldInstance));
-        return oldNewMap.remove(oldInstance);
-    }
-
-    public PersistenceDelegate getPersistenceDelegate(Class<?> type) {
-        PersistenceDelegate result = persistenceDelegates.get(type);
-
-        if (result == null) {
-            result = DefaultPersistenceDelegatesFactory
-                    .getPersistenceDelegate(type);
-        }
-
-        return result;
-    }
-
-    public void setPersistenceDelegate(Class<?> type,
-            PersistenceDelegate persistenceDelegate) {
-        if (type == null || persistenceDelegate == null) {
-            throw new NullPointerException();
-        }
-        persistenceDelegates.put(type, persistenceDelegate);
-    }
-
-    protected void writeObject(Object object) {
-        roots.add(object);
-        if (object == null) {
-            return;
-        }
-        doWriteObject(object);
-    }
-
-    void doWriteObject(Object object) {
-        PersistenceDelegate pd = (object != null) ? getPersistenceDelegate(object
-                .getClass())
-                : new NullPersistenceDelegate();
-
-        if (pd == null) {
-            pd = new DefaultPersistenceDelegate();
-        }
-
-        pd.writeObject(object, this);
-        if (isString(object.getClass())) {
-            nodes.put(object, new ObjectNode(pd.instantiate(object, this)));
-        }
-    }
-
-    private Object forceNew(Object old) {
-        if (old == null) {
-            return null;
-        }
-        Object nu = get(old);
-        if (nu != null) {
-            return nu;
-        }
-        writeObject(old);
-        return get(old);
-    }
-
-    private Object[] forceNewArray(Object oldArray[]) {
-        if (oldArray == null) {
-            return null;
-        }
-        Object newArray[] = new Object[oldArray.length];
-        for (int i = 0; i < oldArray.length; i++) {
-            newArray[i] = forceNew(oldArray[i]);
-        }
-        return newArray;
-    }
-
-    public void writeStatement(Statement oldStm) {
-        if (oldStm == null) {
-            throw new NullPointerException();
-        }
-        try {
-            // FIXME add target processing here
-            Object newTarget = forceNew(oldStm.getTarget());
-            Object newArgs[] = forceNewArray(oldStm.getArguments());
-            Statement statement = new Statement(newTarget, oldStm
-                    .getMethodName(), newArgs);
-            statement.execute();
-        } catch (Exception e) {
-            getExceptionListener().exceptionThrown(e);
-        }
-    }
+	private static final java_lang_reflect_ProxyPersistenceDelegate proxyPD = new java_lang_reflect_ProxyPersistenceDelegate();
+    
+    private static final NullPersistenceDelegate nullPD = new NullPersistenceDelegate();
+
+	private static final ExceptionListener defaultExListener = new DefaultExceptionListener();
+    
+	private static class DefaultExceptionListener implements ExceptionListener {
+
+		public void exceptionThrown(Exception exception) {
+			System.err.println("Exception during encoding:" + exception); //$NON-NLS-1$
+			System.err.println("Continue...");
+		}
+
+	}
+
+	static {
+		PersistenceDelegate ppd = new PrimitiveWrapperPersistenceDelegate();
+		delegates.put(Boolean.class, ppd);
+		delegates.put(Byte.class, ppd);
+		delegates.put(Character.class, ppd);
+		delegates.put(Double.class, ppd);
+		delegates.put(Float.class, ppd);
+		delegates.put(Integer.class, ppd);
+		delegates.put(Long.class, ppd);
+		delegates.put(Short.class, ppd);
+
+		delegates.put(Class.class, new java_lang_ClassPersistenceDelegate());
+		delegates.put(Field.class, new java_lang_reflect_FieldPersistenceDelegate());
+		delegates.put(Method.class, new java_lang_reflect_MethodPersistenceDelegate());
+		delegates.put(String.class, new java_lang_StringPersistenceDelegate());
+		delegates.put(Proxy.class, new java_lang_reflect_ProxyPersistenceDelegate());
+	}
+
+	private ExceptionListener listener = defaultExListener;
+
+	private IdentityHashMap oldNewMap = new IdentityHashMap();
+
+	/**
+	 * Construct a new encoder.
+	 */
+	public Encoder() {
+		super();
+	}
+
+	/**
+	 * Clear all the new objects have been created.
+	 */
+	void clear() {
+		oldNewMap.clear();
+	}
+
+	/**
+	 * Gets the new copy of the given old object.
+	 * <p>
+	 * Strings are special objects which have their new copy by default, so if
+	 * the old object is a string, it is returned directly.
+	 * </p>
+	 * 
+	 * @param old
+	 *            an old object
+	 * @return the new copy of the given old object, or null if there is not
+	 *         one.
+	 */
+	public Object get(Object old) {
+		if (old == null || old instanceof String) {
+			return old;
+		}
+		return oldNewMap.get(old);
+	}
+
+	/**
+	 * Returns the exception listener of this encoder.
+	 * <p>
+	 * An encoder always have a non-null exception listener. A default exception
+	 * listener is used when the encoder is created.
+	 * </p>
+	 * 
+	 * @return the exception listener of this encoder
+	 */
+	public ExceptionListener getExceptionListener() {
+		return listener;
+	}
+
+	/**
+	 * Returns a <code>PersistenceDelegate</code> for the given class type.
+	 * <p>
+	 * The <code>PersistenceDelegate</code> is determined as following:
+	 * <ol>
+	 * <li>If a <code>PersistenceDelegate</code> has been registered by
+	 * calling <code>setPersistenceDelegate</code> for the given type, it is
+	 * returned.</li>
+	 * <li>If the given type is an array class, a special
+	 * <code>PersistenceDelegate</code> for array types is returned.</li>
+	 * <li>If the given type is a proxy class, a special
+	 * <code>PersistenceDelegate</code> for proxy classes is returned.</li>
+	 * <li><code>Introspector</code> is used to check the bean descriptor
+	 * value "persistenceDelegate". If one is set, it is returned.</li>
+	 * <li>If none of the above applies, the
+	 * <code>DefaultPersistenceDelegate</code> is returned.</li>
+	 * </ol>
+	 * </p>
+	 * 
+	 * @param type
+	 *            a class type
+	 * @return a <code>PersistenceDelegate</code> for the given class type
+	 */
+	public PersistenceDelegate getPersistenceDelegate(Class<?> type) {
+		if (type == null) {
+			return nullPD; // may be return a special PD?
+		}
+
+        // registered delegate
+		PersistenceDelegate registeredPD = (PersistenceDelegate) delegates
+				.get(type);
+		if (registeredPD != null) {
+			return registeredPD;
+		}
+		
+        if (Collection.class.isAssignableFrom(type)) {
+            return new UtilCollectionPersistenceDelegate();
+        }
+        
+		if (type.isArray()) {
+			return arrayPD;
+		}
+		if (Proxy.isProxyClass(type)) {
+			return proxyPD;
+		}
+
+		// check "persistenceDelegate" property
+		try {
+			BeanInfo binfo = Introspector.getBeanInfo(type);
+			if (binfo != null) {
+				PersistenceDelegate pd = (PersistenceDelegate) binfo
+						.getBeanDescriptor().getValue("persistenceDelegate"); //$NON-NLS-1$
+				if (pd != null) {
+					return pd;
+				}
+			}
+		} catch (Exception e) {
+			// ignore
+		}
+
+		// default persistence delegate
+		return defaultPD;
+	}
+
+	private void put(Object old, Object nu) {
+		oldNewMap.put(old, nu);
+	}
+
+	/**
+	 * Remvoe the existing new copy of the given old object.
+	 * 
+	 * @param old
+	 *            an old object
+	 * @return the removed new version of the old object, or null if there is
+	 *         not one
+	 */
+	public Object remove(Object old) {
+		return oldNewMap.remove(old);
+	}
+
+	/**
+	 * Sets the exception listener of this encoder.
+	 * 
+	 * @param listener
+	 *            the exception listener to set
+	 */
+	public void setExceptionListener(ExceptionListener listener) {
+		if (listener == null) {
+			listener = defaultExListener;
+		}
+		this.listener = listener;
+	}
+
+	/**
+	 * Register the <code>PersistenceDelegate</code> of the specified type.
+	 * 
+	 * @param type
+	 * @param delegate
+	 */
+	public void setPersistenceDelegate(Class<?> type, PersistenceDelegate delegate) {
+		if (type == null || delegate == null) {
+			throw new NullPointerException();
+		}
+		delegates.put(type, delegate);
+	}
+
+	private Object forceNew(Object old) {
+		if (old == null) {
+			return null;
+		}
+		Object nu = get(old);
+		if (nu != null) {
+			return nu;
+		}
+		writeObject(old);
+		return get(old);
+	}
+
+	private Object[] forceNewArray(Object oldArray[]) {
+		if (oldArray == null) {
+			return null;
+		}
+		Object newArray[] = new Object[oldArray.length];
+		for (int i = 0; i < oldArray.length; i++) {
+			newArray[i] = forceNew(oldArray[i]);
+		}
+		return newArray;
+	}
+
+	/**
+	 * Write an expression of old objects.
+	 * <p>
+	 * The implementation first check the return value of the expression. If
+	 * there exists a new version of the object, simply return.
+	 * </p>
+	 * <p>
+	 * A new expression is created using the new versions of the target and the
+	 * arguments. If any of the old objects do not have its new version yet,
+	 * <code>writeObject()</code> is called to create the new version.
+	 * </p>
+	 * <p>
+	 * The new expression is then executed to obtained a new copy of the old
+	 * return value.
+	 * </p>
+	 * <p>
+	 * Call <code>writeObject()</code> with the old return value, so that more
+	 * statements will be executed on its new version to change it into the same
+	 * state as the old value.
+	 * </p>
+	 * 
+	 * @param oldExp
+	 *            the expression to write. The target, arguments, and return
+	 *            value of the expression are all old objects.
+	 */
+	public void writeExpression(Expression oldExp) {
+		if (oldExp == null) {
+			throw new NullPointerException();
+		}
+		try {
+			// if oldValue exists, noop
+			Object oldValue = oldExp.getValue();
+			if (oldValue == null || get(oldValue) != null) {
+				return;
+			}
+
+			// copy to newExp
+			Object newTarget = forceNew(oldExp.getTarget());
+			Object newArgs[] = forceNewArray(oldExp.getArguments());
+			Expression newExp = new Expression(newTarget, oldExp
+					.getMethodName(), newArgs);
+
+			// execute newExp
+			Object newValue = null;
+			try {
+				newValue = newExp.getValue();
+			} catch (IndexOutOfBoundsException ex) {
+				// Current Container does not have any component, newVal set
+				// to null
+			}
+
+			// relate oldValue to newValue
+			put(oldValue, newValue);
+
+			// force same state
+			writeObject(oldValue);
+		} catch (Exception e) {
+			listener.exceptionThrown(new Exception(
+					"failed to write expression: " + oldExp, e)); //$NON-NLS-1$
+		}
+	}
+
+	/**
+	 * Encode the given object into a series of statements and expressions.
+	 * <p>
+	 * The implementation simply finds the <code>PersistenceDelegate</code>
+	 * responsible for the object's class, and delegate the call to it.
+	 * </p>
+	 * 
+	 * @param o
+	 *            the object to encode
+	 */
+	protected void writeObject(Object o) {
+		if (o == null) {
+			return;
+		}
+		Class type = o.getClass();
+		getPersistenceDelegate(type).writeObject(o, this);
+	}
+
+	/**
+	 * Write a statement of old objects.
+	 * <p>
+	 * A new statement is created by using the new versions of the target and
+	 * arguments. If any of the objects do not have its new copy yet,
+	 * <code>writeObject()</code> is called to create one.
+	 * </p>
+	 * <p>
+	 * The new statement is then executed to change the state of the new object.
+	 * </p>
+	 * 
+	 * @param oldStat
+	 *            a statement of old objects
+	 */
+	public void writeStatement(Statement oldStat) {
+		if (oldStat == null) {
+			throw new NullPointerException();
+		}
+		try {
+			// copy to newStat
+			Object newTarget = forceNew(oldStat.getTarget());
+			Object newArgs[] = forceNewArray(oldStat.getArguments());
+			Statement newStat = new Statement(newTarget, oldStat
+					.getMethodName(), newArgs);
+
+			// execute newStat
+			newStat.execute();
+		} catch (Exception e) {
+			listener.exceptionThrown(new Exception(
+					"failed to write statement: " + oldStat, e)); //$NON-NLS-1$
+		}
+	}
 
-    private void put(Object old, Object nu) {
-        oldNewMap.put(old, nu);
-    }
-
-    public void writeExpression(Expression oldExp) {
-        if (oldExp == null) {
-            throw new NullPointerException();
-        }
-        try {
-            Object oldValue = oldExp.getValue();
-            if (oldValue == null || get(oldValue) != null) {
-                return;
-            }
-
-            // copy to newExp
-            Object newTarget = forceNew(oldExp.getTarget());
-            Object newArgs[] = forceNewArray(oldExp.getArguments());
-            Expression newExp = new Expression(newTarget, oldExp
-                    .getMethodName(), newArgs);
-
-            // execute newExp
-            Object newValue = null;
-            try {
-                newValue = newExp.getValue();
-            } catch (IndexOutOfBoundsException ex) {
-                // Current Container does not have any component, newVal set
-                // to null
-            }
-
-            // relate oldValue to newValue
-            put(oldValue, newValue);
-
-            // force same state
-            writeObject(oldValue);
-        } catch (Exception e) {
-            // TODO - remove written args
-            getExceptionListener().exceptionThrown(e);
-        }
-    }
-
-    public void setExceptionListener(ExceptionListener exceptionListener) {
-        if (exceptionListener == null) {
-            exceptionListener = defaultExListener;
-        }
-        this.exceptionListener = exceptionListener;
-    }
-
-    public ExceptionListener getExceptionListener() {
-        return exceptionListener;
-    }
-
-    private Object write(Object oldInstance) throws Exception {
-        if (oldInstance == null) {
-            return null;
-        }
-
-        ObjectNode node = nodes.get(oldInstance);
-
-        if (node == null) {
-            doWriteObject(oldInstance);
-            node = nodes.get(oldInstance);
-        } else {
-            node.addReference();
-        }
-
-        return node.getObjectValue();
-    }
-
-    Object[] write(Object[] oldInstances) throws Exception {
-        if (oldInstances != null) {
-            Object[] newInstances = new Object[oldInstances.length];
-
-            for (int i = 0; i < oldInstances.length; ++i) {
-                newInstances[i] = write(oldInstances[i]);
-            }
-            return newInstances;
-        }
-        return null;
-    }
-
-    /*
-     * @param node node to return the value for @return tentative object value
-     * for given node
-     */
-    private Object getValue(ObjectNode node) {
-        if (node != null) {
-            try {
-                Object result = node.getObjectValue();
-
-                return result;
-            } catch (Exception e) {
-                getExceptionListener().exceptionThrown(e);
-            }
-        }
-        return null;
-    }
-
-    static boolean isNull(Class<?> type) {
-        return type == null;
-    }
-
-    static boolean isPrimitive(Class<?> type) {
-        return type == Boolean.class || type == Byte.class
-                || type == Character.class || type == Double.class
-                || type == Float.class || type == Integer.class
-                || type == Long.class || type == Short.class;
-    }
-
-    static boolean isString(Class<?> type) {
-        return type == String.class;
-
-    }
-
-    static boolean isClass(Class<?> type) {
-        return type == Class.class;
-    }
-
-    static boolean isArray(Class<?> type) {
-        return type.isArray();
-    }
-
-    static String getPrimitiveName(Class<?> type) {
-        String result = null;
-
-        if (type == Boolean.class) {
-            result = "boolean"; //$NON-NLS-1$
-        } else if (type == Byte.class) {
-            result = "byte"; //$NON-NLS-1$
-        } else if (type == Character.class) {
-            result = "char"; //$NON-NLS-1$
-        } else if (type == Double.class) {
-            result = "double"; //$NON-NLS-1$
-        } else if (type == Float.class) {
-            result = "float"; //$NON-NLS-1$
-        } else if (type == Integer.class) {
-            result = "int"; //$NON-NLS-1$
-        } else if (type == Long.class) {
-            result = "long"; //$NON-NLS-1$
-        } else if (type == Short.class) {
-            result = "short"; //$NON-NLS-1$
-        } else if (type == String.class) {
-            result = "string"; //$NON-NLS-1$
-        } else if (type == Class.class) {
-            result = "class"; //$NON-NLS-1$
-        }
-
-        return result;
-    }
 }
+

Added: harmony/enhanced/classlib/trunk/modules/beans/src/main/java/java/beans/UtilCollectionPersistenceDelegate.java
URL: http://svn.apache.org/viewvc/harmony/enhanced/classlib/trunk/modules/beans/src/main/java/java/beans/UtilCollectionPersistenceDelegate.java?view=auto&rev=556804
==============================================================================
--- harmony/enhanced/classlib/trunk/modules/beans/src/main/java/java/beans/UtilCollectionPersistenceDelegate.java (added)
+++ harmony/enhanced/classlib/trunk/modules/beans/src/main/java/java/beans/UtilCollectionPersistenceDelegate.java Mon Jul 16 20:49:39 2007
@@ -0,0 +1,69 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package java.beans;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+class UtilCollectionPersistenceDelegate extends
+        DefaultPersistenceDelegate {
+    @Override
+    protected void initialize(Class<?> type, Object oldInstance,
+            Object newInstance, Encoder enc) {
+
+        Collection oldList = (Collection) oldInstance, newList = (Collection)newInstance;
+        Iterator oldIterator = oldList.iterator(), newIterator = newList.iterator();
+        for (; oldIterator.hasNext();) {
+            Expression getterExp = new Expression(oldIterator, "next", null);
+            try {
+                // Calculate the old value of the property
+                Object oldVal = getterExp.getValue();
+
+                Object newVal = null;
+                try {
+                    newVal = new Expression(newIterator, "next", null).getValue();
+                } catch (ArrayIndexOutOfBoundsException ex) {
+                    // The newInstance has no elements, so current property
+                    // value remains null
+                }
+                /*
+                 * Make the target value and current property value equivalent
+                 * in the new environment
+                 */
+                if (null == oldVal) {
+                    if (null != newVal) {
+                        // Set to null
+                        Statement setterStm = new Statement(oldInstance, "add",
+                                new Object[] { null });
+                        enc.writeStatement(setterStm);
+                    }
+                } else {
+                    PersistenceDelegate pd = enc
+                            .getPersistenceDelegate(oldVal.getClass());
+                    if (!pd.mutatesTo(oldVal, newVal)) {
+                        Statement setterStm = new Statement(oldInstance, "add",
+                                new Object[] { oldVal });
+                        enc.writeStatement(setterStm);
+                    }
+                }
+            } catch (Exception ex) {
+                enc.getExceptionListener().exceptionThrown(ex);
+            }
+        }
+    }
+}

Propchange: harmony/enhanced/classlib/trunk/modules/beans/src/main/java/java/beans/UtilCollectionPersistenceDelegate.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: harmony/enhanced/classlib/trunk/modules/beans/src/main/java/java/beans/XMLEncoder.java
URL: http://svn.apache.org/viewvc/harmony/enhanced/classlib/trunk/modules/beans/src/main/java/java/beans/XMLEncoder.java?view=diff&rev=556804&r1=556803&r2=556804
==============================================================================
--- harmony/enhanced/classlib/trunk/modules/beans/src/main/java/java/beans/XMLEncoder.java (original)
+++ harmony/enhanced/classlib/trunk/modules/beans/src/main/java/java/beans/XMLEncoder.java Mon Jul 16 20:49:39 2007
@@ -15,603 +15,888 @@
  *  limitations under the License.
  */
 
+
 package java.beans;
 
-import java.io.IOException;
+import java.awt.SystemColor;
+import java.awt.font.TextAttribute;
 import java.io.OutputStream;
-import java.util.Enumeration;
-import java.util.HashMap;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.lang.reflect.Array;
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.IdentityHashMap;
 import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.Vector;
-
-import org.apache.harmony.beans.ObjectNode;
-import org.apache.harmony.beans.internal.nls.Messages;
+import java.util.List;
 
+/**
+ * <code>XMLEncoder</code> exnteds <code>Encoder</code> to write out the
+ * encoded statements and expressions in xml format. The xml can be read by
+ * <code>XMLDecoder</code> later to restore objects and their states.
+ * <p>
+ * The API is similar to <code>ObjectOutputStream</code>.
+ * </p>
+ * 
+ */
 public class XMLEncoder extends Encoder {
 
-    private OutputStream out;
+	/*
+	 * Every object written by the encoder has a record.
+	 */
+	private static class Record {
+		boolean born = false;
+
+		// The expression by which the object is created or obtained.
+		Expression exp = null;
 
-    private Object owner;
+		// Id of the object, if it is referenced more than once.
+		String id = null;
 
-    private final Vector<ObjectNode> printed = new Vector<ObjectNode>();
+		// Count of the references of the object.
+		int refCount = 0;
 
-    public XMLEncoder(OutputStream out) {
-        this.out = out;
-        this.owner = null;
-    }
-
-    @Override
-    public void writeObject(Object object) {
-        super.writeObject(object);
-    }
-
-    public void setOwner(Object owner) {
-        this.owner = owner;
-    }
-
-    public Object getOwner() {
-        return owner;
-    }
-    
-    @Override
-    public void writeExpression(Expression oldExp) {
+		// A list of statements that execute on the object.
+		ArrayList stats = new ArrayList();
+	}
 
-		Object oldValue = null;
-		try {
-			oldValue = oldExp.getValue();
-		} catch (Exception e) {
-			getExceptionListener()
-					.exceptionThrown(
-							new Exception("failed to execute expression: "
-									+ oldExp, e));
+	private static final int INDENT_UNIT = 1;
+
+	private static final boolean isStaticConstantsSupported = true;
+
+	// the main record of all root objects
+	private ArrayList flushPending = new ArrayList();
+
+	// the record of root objects with a void tag
+	private ArrayList flushPendingStat = new ArrayList();
+
+	// keep the pre-required objects for each root object
+	private ArrayList flushPrePending = new ArrayList();
+
+	private boolean hasXmlHeader = false;
+
+	private int idSerialNo = 0;
+
+	/*
+	 * if any expression or statement references owner, it is set true in method
+	 * recordStatement() or recordExpressions(), and, at the first time
+	 * flushObject() meets an owner object, it calls the flushOwner() method and
+	 * then set needOwner to false, so that all succeeding flushing of owner
+	 * will call flushExpression() or flushStatement() normally, which will get
+	 * a reference of the owner property.
+	 */
+	private boolean needOwner = false;
+
+	private PrintWriter out;
+
+	private Object owner = null;
+
+	private IdentityHashMap records = new IdentityHashMap();
+
+	private boolean writingObject = false;
+
+	/**
+	 * Construct a <code>XMLEncoder</code>.
+	 * 
+	 * @param out
+	 *            the output stream where xml is writtern to
+	 */
+	public XMLEncoder(OutputStream out) {
+		if (null != out) {
+            try {
+                this.out = new PrintWriter(
+                        new OutputStreamWriter(out, "UTF-8"), true);
+            } catch (UnsupportedEncodingException e) {
+                // never occur
+                e.printStackTrace();
+            }
+        }
+	}
+
+	/**
+	 * Call <code>flush()</code> first, then write out xml footer and close
+	 * the underlying output stream.
+	 */
+	public void close() {
+		flush();
+		out.println("</java>");
+		out.close();
+	}
+
+	private StringBuffer decapitalize(String s) {
+		StringBuffer buf = new StringBuffer(s);
+		buf.setCharAt(0, Character.toLowerCase(buf.charAt(0)));
+		return buf;
+	}
+
+	/**
+	 * Writes out all objects since last flush to the output stream.
+	 * <p>
+	 * The implementation write the xml header first if it has not been
+	 * writtern. Then all pending objects since last flush are writtern.
+	 * </p>
+	 */
+	public void flush() {
+		synchronized (this) {
+			// write xml header
+			if (!hasXmlHeader) {
+				out.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
+				out.println("<java version=\""
+						+ System.getProperty("java.version")
+						+ "\" class=\"java.beans.XMLDecoder\">");
+				hasXmlHeader = true;
+			}
+
+			// preprocess pending objects
+			for (Iterator iter = flushPending.iterator(); iter.hasNext();) {
+				Object o = iter.next();
+				Record rec = (Record) records.get(o);
+				if (rec != null) {
+					preprocess(o, rec);
+				}
+			}
+
+			// flush pending objects
+			for (Iterator iter = flushPending.iterator(); iter.hasNext();) {
+				Object o = iter.next();
+				flushObject(o, INDENT_UNIT);
+				// remove flushed obj
+				iter.remove();
+			}
+
+			// clear statement records
+			records.clear();
+			flushPendingStat.clear();
+
+			// remove all old->new mappings
+			super.clear();
+		}
+	}
+
+	private void flushBasicObject(Object obj, int indent) {
+		if( obj instanceof Proxy) {
 			return;
 		}
-		
-		if (get(oldValue) != null) {
+		flushIndent(indent);
+		if (obj == null) {
+			out.println("<null />");
+		} else if (obj instanceof String) {
+			Record rec = (Record) records.get(obj);
+			if( null != rec) {
+				if (flushPendingStat.contains(obj)) {
+					flushExpression(obj, rec, indent - 3, true);
+				} else {
+					flushExpression(obj, rec, indent - 3, false);
+				}
+				return;
+			}
+			out.print("<string>");
+			flushString((String) obj);
+			out.println("</string>");
+		} else if (obj instanceof Class) {
+			out.print("<class>");
+			out.print(((Class) obj).getName());
+			out.println("</class>");
+		} else if (obj instanceof Boolean) {
+			out.print("<boolean>");
+			out.print(obj);
+			out.println("</boolean>");
+		} else if (obj instanceof Byte) {
+			out.print("<byte>");
+			out.print(obj);
+			out.println("</byte>");
+		} else if (obj instanceof Character) {
+			out.print("<char>");
+			out.print(obj);
+			out.println("</char>");
+		} else if (obj instanceof Double) {
+			out.print("<double>");
+			out.print(obj);
+			out.println("</double>");
+		} else if (obj instanceof Float) {
+			out.print("<float>");
+			out.print(obj);
+			out.println("</float>");
+		} else if (obj instanceof Integer) {
+			out.print("<int>");
+			out.print(obj);
+			out.println("</int>");
+		} else if (obj instanceof Long) {
+			out.print("<long>");
+			out.print(obj);
+			out.println("</long>");
+		} else if (obj instanceof Short) {
+			out.print("<short>");
+			out.print(obj);
+			out.println("</short>");
+		} else {
+			getExceptionListener().exceptionThrown(
+					new Exception("Unknown basic object: " + obj));
+		}
+	}
+
+	private void flushExpression(Object obj, Record rec, int indent,
+			boolean asStatement) {
+		// not first time, use idref
+		if (rec.id != null) {
+			flushIndent(indent);
+			out.print("<object idref=\"");
+			out.print(rec.id);
+			out.println("\" />");
 			return;
 		}
-		
-		Object oldTarget = oldExp.getTarget();
 
-		ObjectNode valueNode = null;
-		Class<?> valueType = null;
+		// generate id, if necessary
+		if (rec.refCount > 1) {
+			rec.id = nameForClass(obj.getClass()) + idSerialNo;
+			idSerialNo++;
+		}
 
-		// write target
-		if (!Statement.isPDConstructor(oldExp)
-				&& !Statement.isStaticMethodCall(oldExp)) {
-			ObjectNode parent;
+		// flush
+		Statement stat = asStatement ? new Statement(rec.exp.getTarget(),
+				rec.exp.getMethodName(), rec.exp.getArguments()) : rec.exp;
+		flushStatement(stat, rec.id, rec.stats, indent);
+	}
 
-			// XXX investigate
-			// write(oldTarget);
-			parent = nodes.get(oldTarget);
-			if (parent != null) {
-				parent.addExpression(oldExp);
-			}
+	private void flushIndent(int indent) {
+		for (int i = 0; i < indent; i++) {
+			out.print(" ");
 		}
+	}
 
-		// write value
-		valueType = oldValue.getClass();
-		valueNode = nodes.get(oldValue);
+	private void flushObject(Object obj, int indent) {
+		Record rec = (Record) records.get(obj);
+		if (rec == null && !isBasicType(obj))
+			return;
 
-		if (valueNode == null) {
+		if (obj == owner && this.needOwner) {
+			flushOwner(obj, rec, indent);
+			this.needOwner = false;
+			return;
+		}
 
-			if (isNull(valueType) || isPrimitive(valueType)
-					|| isString(valueType)) {
-				valueNode = new ObjectNode(oldExp);
+		if (isBasicType(obj)) {
+			flushBasicObject(obj, indent);
+		} else {
+			if (flushPendingStat.contains(obj)) {
+				flushExpression(obj, rec, indent, true);
 			} else {
-				try {
-					write((Object[])oldExp.getArguments());
-				} catch (Exception e) {
-					getExceptionListener().exceptionThrown(e);
-				}
-				valueNode = new ObjectNode(oldExp, nodes);
+				flushExpression(obj, rec, indent, false);
 			}
-
-			nodes.put(oldValue, valueNode);
-		} else if (oldExp.getMethodName().equals("new")) { //$NON-NLS-1$
-			valueNode.addReference();
-		} else {
-			// XXX the information about referencedExpressions is not
-			// being used by anyone
-			// node.addReferencedExpression(oldExp);
 		}
-		super.writeExpression(oldExp);
 	}
 
-    @Override
-    public void writeStatement(Statement oldStm) {
-        try {
-            super.writeStatement(oldStm);
-        } catch (NullPointerException ignore) {
-            // ignore exception like RI does
-            ignore.printStackTrace();
-        }
-    }
+	private void flushOwner(Object obj, Record rec, int indent) {
+		if (rec.refCount > 1) {
+			rec.id = nameForClass(obj.getClass()) + idSerialNo;
+			idSerialNo++;
+		}
 
-    public void flush() {
-        writeAll();
-    }
-
-    public void close() {
-        try {
-            flush();
-            out.close();
-        } catch (Exception e) {
-            getExceptionListener().exceptionThrown(e);
-        }
-    }
+		flushIndent(indent);
+		String tagName = "void";
+		out.print("<");
+		out.print(tagName);
+
+		// id attribute
+		if (rec.id != null) {
+			out.print(" id=\"");
+			out.print(rec.id);
+			out.print("\"");
+		}
 
-    private void writeAll() {
-        Tag mainTag = new Tag("java"); //$NON-NLS-1$
-        Enumeration<Object> e;
+		out.print(" property=\"owner\"");
 
-        printed.clear();
-        NameMaker.clear();
+		// open tag, end
+		if (rec.exp.getArguments().length == 0 && rec.stats.isEmpty()) {
+			out.println("/>");
+			return;
+		}
+		out.println(">");
 
-        mainTag.addAttr("version", System.getProperty("java.version")); //$NON-NLS-1$ //$NON-NLS-2$
-        mainTag.addAttr("class", "java.beans.XMLDecoder"); //$NON-NLS-1$ //$NON-NLS-2$
+		// arguments
+		for (int i = 0; i < rec.exp.getArguments().length; i++) {
+			flushObject(rec.exp.getArguments()[i], indent + INDENT_UNIT);
+		}
 
-        printBytes(0, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); //$NON-NLS-1$
-        printBytes(0, mainTag.toStringOnOpen());
+		// sub statements
+		flushSubStatements(rec.stats, indent);
 
-        e = roots.elements();
-        while (e.hasMoreElements()) {
-            Object object = e.nextElement();
+		// close tag
+		flushIndent(indent);
+		out.print("</");
+		out.print(tagName);
+		out.println(">");
+	}
 
-            if (object != null) {
-                ObjectNode node = nodes.get(object);
+	private void flushStatArray(Statement stat, String id, List subStats,
+			int indent) {
+		// open tag, begin
+		flushIndent(indent);
+		out.print("<array");
+
+		// id attribute
+		if (id != null) {
+			out.print(" id=\"");
+			out.print(id);
+			out.print("\"");
+		}
 
-                printObjectTag(1, object, node);
-            } else {
-                printNullTag(1);
-            }
-        }
+		// class & length
+		out.print(" class=\"");
+		out.print(((Class) stat.getArguments()[0]).getName());
+		out.print("\" length=\"");
+		out.print(stat.getArguments()[1]);
+		out.print("\"");
+
+		// open tag, end
+		if (subStats.isEmpty()) {
+			out.println("/>");
+			return;
+		}
+		out.println(">");
 
-        printBytes(0, mainTag.toStringOnClose());
-    }
+		// sub statements
+		flushSubStatements(subStats, indent);
 
-    // FIXME processing of default constructor: investigate
-    private void printObjectTag(int tabCount, Object object, ObjectNode node) {
-        Class<?> nodeType = null;
-
-        try {
-            nodeType = node.getObjectType();
-        } catch (Exception e) {
-            Exception e2 = new Exception(Messages.getString(
-                    "beans.3B", node.getInitializer())); //$NON-NLS-1$
-
-            e2.initCause(e);
-            getExceptionListener().exceptionThrown(e2);
-            return;
-        }
+		// close tag
+		flushIndent(indent);
+		out.println("</array>");
+	}
 
-        if (isPrimitive(nodeType) || isString(nodeType) || isClass(nodeType)) {
-            String tagName = getPrimitiveName(nodeType);
-            Object arg = node.getObjectArguments()[0];
-            Tag tag = new Tag(tagName, arg.toString());
-
-            printBytes(tabCount, tag.toString());
-        } else { // if array or complex object
-            Tag tag = null;
-            Object[] arguments = node.getObjectArguments();
-            boolean objectPrinted = false;
-            boolean isReferenced = node.getReferencesNumber() > 0;
-
-            if (isArray(nodeType)) {
-                tag = new Tag("array"); //$NON-NLS-1$
-            } else {
-                tag = new Tag("object"); //$NON-NLS-1$
-            }
+	private void flushStatCommon(Statement stat, String id, List subStats,
+			int indent) {
+		// open tag, begin
+		flushIndent(indent);
+		String tagName = stat instanceof Expression ? "object" : "void";
+		out.print("<");
+		out.print(tagName);
+
+		// id attribute
+		if (id != null) {
+			out.print(" id=\"");
+			out.print(id);
+			out.print("\"");
+		}
 
-            // check if the object presents references
-            if (isReferenced) {
-                if (printed.contains(node)) {
-                    String nodeId = node.getId();
-
-                    if (nodeId != null) {
-                        tag.addAttr("idref", node.getId()); //$NON-NLS-1$
-                    }
-
-                    objectPrinted = true;
-                } else { // if(printed.contains(node) == false
-                    try {
-                        Class<?> type = node.getObjectType();
-
-                        if (type != null) {
-                            // check if it is necessary to assign
-                            // and display *id* attribute to the object
-                            String objectName = NameMaker.getInstanceName(type);
-
-                            node.setId(objectName);
-                            tag.addAttr("id", objectName); //$NON-NLS-1$
-                        }
-                    } catch (Exception e) {
-                        getExceptionListener().exceptionThrown(e);
-                    }
-                }
-            }
+		// special class attribute
+		if (stat.getTarget() instanceof Class) {
+			out.print(" class=\"");
+			out.print(((Class) stat.getTarget()).getName());
+			out.print("\"");
+		}
 
-            if (!objectPrinted) {
-                try {
-                    if (isArray(nodeType)) {
-                        tag.addAttr("class", ((Class) arguments[0]).getName()); //$NON-NLS-1$
-                        tag.addAttr("length", ((Integer) arguments[1]) //$NON-NLS-1$
-                                .toString());
-                    } else {
-                        tag.addAttr("class", ((Class)node.getInitializer().getTarget()).getName()); //$NON-NLS-1$
-                        tag.addAttr("method", node.getInitializer().getMethodName());
-                    }
-                } catch (Exception e) {
-                    getExceptionListener().exceptionThrown(e);
-                }
-            }
+		// method attribute
+		if (!"new".equals(stat.getMethodName())) {
+			out.print(" method=\"");
+			out.print(stat.getMethodName());
+			out.print("\"");
+		}
 
-            // preprocessing is done, print it!
-            if (objectPrinted) {
-                // if object has already been printed then only print the
-                // reference
-                printBytes(tabCount, tag.toStringShortForm());
-            } else if (isArray(nodeType) && !node.statements().hasNext()) {
-                // if we have an empty array
-                printBytes(tabCount, tag.toStringShortForm());
-            } else if (arguments.length == 0 && !node.statements().hasNext()
-                    && !node.expressions().hasNext()) {
-                // if given tag has no children print the short form of the tag
-                printBytes(tabCount, tag.toStringShortForm());
-            } else {
-                // the tag has not been printed and contains children,
-                // let's print them
-
-                printBytes(tabCount, tag.toStringOnOpen());
-
-                printed.add(node);
-
-                if (isArray(nodeType)) { // if array
-                    Iterator<Statement> it = node.statements();
-
-                    while (it.hasNext()) {
-                        Statement s = it.next();
-
-                        printVoidTag(++tabCount, s);
-                        --tabCount;
-                    }
-                } else { // if object
-                    Iterator<Expression> i1;
-                    Iterator<Statement> i2;
-
-                    for (Object element : arguments) {
-                        if (element != null) {
-                            ObjectNode succNode = nodes.get(element);
-
-                            printObjectTag(++tabCount, element, succNode);
-                        } else {
-                            printNullTag(++tabCount);
-                        }
-
-                        --tabCount;
-                    }
-
-                    i1 = node.expressions();
-                    while (i1.hasNext()) {
-                        Expression e = i1.next();
-
-                        printVoidTag(++tabCount, e);
-                        --tabCount;
-                    }
-
-                    i2 = node.statements();
-                    while (i2.hasNext()) {
-                        Statement s = i2.next();
-
-                        printVoidTag(++tabCount, s);
-                        --tabCount;
-                    }
-                } // if object
+		// open tag, end
+		if (stat.getArguments().length == 0 && subStats.isEmpty()) {
+			out.println("/>");
+			return;
+		}
+		out.println(">");
 
-                printBytes(tabCount, tag.toStringOnClose());
-            }
-        } // if node is of non-trivial type
-    }
+		// arguments
+		for (int i = 0; i < stat.getArguments().length; i++) {
+			flushObject(stat.getArguments()[i], indent + INDENT_UNIT);
+		}
 
-    private void printVoidTag(int tabCount, Expression expr) {
-        Object exprValue = null;
+		// sub statements
+		flushSubStatements(subStats, indent);
 
-        try {
-            Enumeration<ObjectNode> enumeration;
-            Tag tag;
-            String objectName;
-            String methodName;
-            ObjectNode node;
-            Object[] args;
-
-            exprValue = expr.getValue();
-
-            // find out, if this object is already printed
-            enumeration = printed.elements();
-            while (enumeration.hasMoreElements()) {
-                ObjectNode node2 = enumeration.nextElement();
-
-                if (node2.getObjectValue() == exprValue) {
-                    return;
-                }
-            }
+		// close tag
+		flushIndent(indent);
+		out.print("</");
+		out.print(tagName);
+		out.println(">");
+	}
 
-            node = nodes.get(exprValue);
+	private void flushStatement(Statement stat, String id, List subStats,
+			int indent) {
+		Object target = stat.getTarget();
+		String method = stat.getMethodName();
+		Object args[] = stat.getArguments();
+
+		// special case for array
+		if (Array.class == target && "newInstance".equals(method)) {
+			flushStatArray(stat, id, subStats, indent);
+			return;
+		}
+		// special case for get(int) and set(int, Object)
+		if (isGetArrayStat(target, method, args)
+				|| isSetArrayStat(target, method, args)) {
+			flushStatIndexed(stat, id, subStats, indent);
+			return;
+		}
+		// special case for getProperty() and setProperty(Object)
+		if (isGetPropertyStat(method, args) || isSetPropertyStat(method, args)) {
+			flushStatGetterSetter(stat, id, subStats, indent);
+			return;
+		}
 
-            // find out, if this object has no references to be printed
-            // System.out.println("---- node.getReferencesNumber() = " +
-            // node.getReferencesNumber());
-            // System.out.println("---- node.getReferencedExpressionsNumber() =
-            // " + node.getReferencedExpressionsNumber());
+		if (isStaticConstantsSupported
+				&& "getField".equals(stat.getMethodName())) {
+			flushStatField(stat, id, subStats, indent);
+			return;
+		}
 
-            if (node.getReferencesNumber() == 0) {
-                return;
-            }
+		// common case
+		flushStatCommon(stat, id, subStats, indent);
+	}
 
-            tag = new Tag("void"); //$NON-NLS-1$
-            objectName = NameMaker.getInstanceName(exprValue.getClass());
+	private void flushStatField(Statement stat, String id, List subStats,
+			int indent) {
+		// open tag, begin
+		flushIndent(indent);
+		String tagName = "object";
+		out.print("<");
+		out.print(tagName);
+
+		// id attribute
+		if (id != null) {
+			out.print(" id=\"");
+			out.print(id);
+			out.print("\"");
+		}
 
-            node.setId(objectName);
-            tag.addAttr("id", objectName); //$NON-NLS-1$
+		// special class attribute
+		if (stat.getTarget() instanceof Class) {
+			out.print(" class=\"");
+			out.print(((Class) stat.getTarget()).getName());
+			out.print("\"");
+		}
 
-            methodName = expr.getMethodName();
-            args = expr.getArguments();
+		Object target = stat.getTarget();
+		if(target == SystemColor.class || target == TextAttribute.class) {
+			out.print(" field=\"");
+			out.print(stat.getArguments()[0]);
+			out.print("\"");
+			out.println("/>");
 
-            if (methodName.startsWith("get") //$NON-NLS-1$
-                    && (args.length == 0 || args.length == 1
-                            && args[0].getClass() == Integer.class)
-                    || methodName.startsWith("set") //$NON-NLS-1$
-                    && (args.length == 1 || args.length == 2
-                            && args[0].getClass() == Integer.class)) {
-                String propertyName = methodName.substring(3);
-
-                if (propertyName.length() > 0) {
-                    tag.addAttr("property", Introspector //$NON-NLS-1$
-                            .decapitalize(propertyName));
-                }
-
-                if (methodName.startsWith("get") && args.length == 1 //$NON-NLS-1$
-                        || methodName.startsWith("set") && args.length == 2) { //$NON-NLS-1$
-                    tag.addAttr("index", args[0].toString()); //$NON-NLS-1$
-                }
-            } else {
-                tag.addAttr("method", expr.getMethodName()); //$NON-NLS-1$
-            }
+		}
+		else {
+			out.print(" method=\"");
+			out.print(stat.getMethodName());
+			out.print("\"");
+			out.println(">");
+			Object fieldName = (String) stat.getArguments()[0];
+			flushObject(fieldName, indent + INDENT_UNIT);
+			flushIndent(indent);
+			out.println("</object>");
+		}
+	}
 
-            printBytes(tabCount, tag.toStringOnOpen());
+	private void flushStatGetterSetter(Statement stat, String id,
+			List subStats, int indent) {
+		// open tag, begin
+		flushIndent(indent);
+		String tagName = stat instanceof Expression ? "object" : "void";
+		out.print("<");
+		out.print(tagName);
+
+		// id attribute
+		if (id != null) {
+			out.print(" id=\"");
+			out.print(id);
+			out.print("\"");
+		}
 
-            for (int i = tag.hasAttr("index") ? 1 : 0; i < args.length; ++i) { //$NON-NLS-1$
-                if (args[i] != null) {
-                    ObjectNode node2 = nodes.get(args[i]);
-
-                    printObjectTag(++tabCount, args[i], node2);
-                } else {
-                    printNullTag(++tabCount);
-                }
+		// special class attribute
+		if (stat.getTarget() instanceof Class) {
+			out.print(" class=\"");
+			out.print(((Class) stat.getTarget()).getName());
+			out.print("\"");
+		}
 
-                --tabCount;
-            }
+		// property attribute
+		out.print(" property=\"");
+		out.print(decapitalize(stat.getMethodName().substring(3)));
+		out.print("\"");
+
+		// open tag, end
+		if (stat.getArguments().length == 0 && subStats.isEmpty()) {
+			out.println("/>");
+			return;
+		}
+		out.println(">");
 
-            printBytes(tabCount, tag.toStringOnClose());
+		// arguments
+		for (int i = 0; i < stat.getArguments().length; i++) {
+			flushObject(stat.getArguments()[i], indent + INDENT_UNIT);
+		}
 
-            printed.add(node);
-        } catch (Exception e) {
-            // TODO - signal problem with expr.getValue()
-        }
+		// sub statements
+		flushSubStatements(subStats, indent);
 
-    }
+		// close tag
+		flushIndent(indent);
+		out.print("</");
+		out.print(tagName);
+		out.println(">");
+	}
 
-    private void printVoidTag(int tabCount, Statement stat) {
-        Tag tag = new Tag("void"); //$NON-NLS-1$
+	private void flushStatIndexed(Statement stat, String id, List subStats,
+			int indent) {
+		// open tag, begin
+		flushIndent(indent);
+		String tagName = stat instanceof Expression ? "object" : "void";
+		out.print("<");
+		out.print(tagName);
+
+		// id attribute
+		if (id != null) {
+			out.print(" id=\"");
+			out.print(id);
+			out.print("\"");
+		}
 
-        String methodName = stat.getMethodName();
-        Object[] args = stat.getArguments();
+		// special class attribute
+		if (stat.getTarget() instanceof Class) {
+			out.print(" class=\"");
+			out.print(((Class) stat.getTarget()).getName());
+			out.print("\"");
+		}
 
-        if (methodName.startsWith("get") //$NON-NLS-1$
-                && (args.length == 0 || args.length == 1
-                        && args[0].getClass() == Integer.class)
-                || methodName.startsWith("set") //$NON-NLS-1$
-                && (args.length == 1 || args.length == 2
-                        && args[0].getClass() == Integer.class)) {
-            String propertyName = methodName.substring(3);
-
-            if (propertyName.length() > 0) {
-                tag.addAttr("property", Introspector //$NON-NLS-1$
-                        .decapitalize(propertyName));
-            }
+		// index attribute
+		out.print(" index=\"");
+		out.print(stat.getArguments()[0]);
+		out.print("\"");
+
+		// open tag, end
+		if (stat.getArguments().length == 1 && subStats.isEmpty()) {
+			out.println("/>");
+			return;
+		}
+		out.println(">");
 
-            if (methodName.startsWith("get") && args.length == 1 //$NON-NLS-1$
-                    || methodName.startsWith("set") && args.length == 2) { //$NON-NLS-1$
-                tag.addAttr("index", args[0].toString()); //$NON-NLS-1$
-            }
-        } else {
-            tag.addAttr("method", stat.getMethodName()); //$NON-NLS-1$
-        }
+		// arguments
+		for (int i = 1; i < stat.getArguments().length; i++) {
+			flushObject(stat.getArguments()[i], indent + INDENT_UNIT);
+		}
 
-        printBytes(tabCount, tag.toStringOnOpen());
+		// sub statements
+		flushSubStatements(subStats, indent);
 
-        for (int i = tag.hasAttr("index") ? 1 : 0; i < args.length; ++i) { //$NON-NLS-1$
-            if (args[i] != null) {
-                ObjectNode node = nodes.get(args[i]);
-
-                printObjectTag(++tabCount, args[i], node);
-            } else {
-                printNullTag(++tabCount);
-            }
+		// close tag
+		flushIndent(indent);
+		out.print("</");
+		out.print(tagName);
+		out.println(">");
+	}
 
-            --tabCount;
-        }
+	private void flushString(String s) {
+		char c;
+		for (int i = 0; i < s.length(); i++) {
+			c = s.charAt(i);
+			if (c == '<') {
+				out.print("&lt;");
+			} else if (c == '>') {
+				out.print("&gt;");
+			} else if (c == '&') {
+				out.print("&amp;");
+			} else if (c == '\'') {
+				out.print("&apos;");
+			} else if (c == '"') {
+				out.print("&quot;");
+			} else {
+				out.print(c);
+			}
+		}
+	}
 
-        printBytes(tabCount, tag.toStringOnClose());
-    }
+	private void flushSubStatements(List subStats, int indent) {
+		for (int i = 0; i < subStats.size(); i++) {
+			Statement subStat = (Statement) subStats.get(i);
+			try {
+				if (subStat instanceof Expression) {
+					Expression subExp = (Expression) subStat;
+					Object obj = subExp.getValue();
+					Record rec = (Record) records.get(obj);
+					flushExpression(obj, rec, indent + INDENT_UNIT, true);
+				} else {
+					flushStatement(subStat, null, Collections.EMPTY_LIST,
+							indent + INDENT_UNIT);
+				}
+			} catch (Exception e) {
+				// should not happen
+				getExceptionListener().exceptionThrown(e);
+			}
+		}
+	}
 
-    private void printNullTag(int tabCount) {
-        printBytes(tabCount, "<null/>"); //$NON-NLS-1$
-    }
-
-    private void printBytes(int tabCount, String s) {
-        try {
-            String result = ""; //$NON-NLS-1$
+	/**
+	 * Returns the owner of this encoder.
+	 * 
+	 * @return the owner of this encoder
+	 */
+	public Object getOwner() {
+		return owner;
+	}
 
-            for (int i = 0; i < tabCount; ++i) {
-                result += ' ';
-            }
-            result = result + s + "\n"; //$NON-NLS-1$
-            out.write(result.getBytes("UTF-8")); //$NON-NLS-1$
-        } catch (IOException ioe) {
-            ExceptionListener listener = getExceptionListener();
+	private boolean isBasicType(Object value) {
+		return value == null || value instanceof Boolean
+				|| value instanceof Byte || value instanceof Character
+				|| value instanceof Class || value instanceof Double
+				|| value instanceof Float || value instanceof Integer
+				|| value instanceof Long || value instanceof Short
+				|| value instanceof String || value instanceof Proxy;
+	}
 
-            if (listener != null) {
-                listener.exceptionThrown(ioe);
-            }
-        }
-    }
+	private boolean isGetArrayStat(Object target, String method, Object[] args) {
+		return ("get".equals(method) && args.length == 1
+				&& args[0] instanceof Integer && target.getClass().isArray());
+	}
 
-    /**
-     * Escapes '&', '<', '>', '\'', '"' chars.
-     * 
-     * @param input
-     *            input string to be escaped
-     * @return string with escaped characters
-     */
-    static String escapeChars(String input) {
-        StringBuffer sb = new StringBuffer();
-
-        for (int i = 0; i < input.length(); i++) {
-            char c = input.charAt(i);
-
-            switch (c) {
-                case '&':
-                    sb.append("&amp;"); //$NON-NLS-1$
-                    break;
-                case '<':
-                    sb.append("&lt;"); //$NON-NLS-1$
-                    break;
-                case '>':
-                    sb.append("&gt;"); //$NON-NLS-1$
-                    break;
-                case '\'':
-                    sb.append("&apos;"); //$NON-NLS-1$
-                    break;
-                case '"':
-                    sb.append("&quot;"); //$NON-NLS-1$
-                    break;
-                default:
-                    sb.append(c);
-                    break;
-            }
-        }
-        return sb.toString();
-    }
+	private boolean isGetPropertyStat(String method, Object[] args) {
+		return (method.startsWith("get") && method.length() > 3 && args.length == 0);
+	}
 
-    /**
-     * This class is used by XMLEncoder to store XML tag information.
-     */
-    static class Tag {
+	private boolean isSetArrayStat(Object target, String method, Object[] args) {
+		return ("set".equals(method) && args.length == 2
+				&& args[0] instanceof Integer && target.getClass().isArray());
+	}
 
-        String name;
+	private boolean isSetPropertyStat(String method, Object[] args) {
+		return (method.startsWith("set") && method.length() > 3 && args.length == 1);
+	}
 
-        LinkedHashMap<String, String> attrs;
+	private String nameForClass(Class c) {
+		if (c.isArray()) {
+			return nameForClass(c.getComponentType()) + "Array";
+		} else {
+			String name = c.getName();
+			int i = name.lastIndexOf('.');
+			if (-1 == i) {
+				return name;
+			} else {
+				return name.substring(i + 1);
+			}
+		}
+	}
 
-        String characters;
+	/*
+	 * The preprocess removes unused statements and counts references of every
+	 * object
+	 */
+	private void preprocess(Object obj, Record rec) {
+		if (isBasicType(obj) && writingObject) {
+			return;
+		}
 
-        public Tag(String name) {
-            this.name = name;
-            this.attrs = new LinkedHashMap<String, String>();
-            this.characters = null;
-        }
+		// count reference
+		rec.refCount++;
 
-        public Tag(String name, String characters) {
-            this.name = name;
-            this.attrs = new LinkedHashMap<String, String>();
-            this.characters = characters;
-        }
+		// do things only one time for each record
+		if (rec.refCount > 1) {
+			return;
+		}
 
-        public boolean hasAttr(String attrName) {
-            return attrs.get(attrName) != null;
-        }
+		// deal with 'field' property
+		try {
+			if (isStaticConstantsSupported
+					&& "getField".equals(((Record) records.get(rec.exp
+							.getTarget())).exp.getMethodName())) {
+				records.remove(obj);
+			}
+		} catch (NullPointerException e) {
+			// do nothing, safely
+		}
 
-        public void addAttr(String attrName, String attrValue) {
-            attrs.put(attrName, attrValue);
-        }
+		// do it recursively
+		if (null != rec.exp) {
+			Object args[] = rec.exp.getArguments();
+			for (int i = 0; i < args.length; i++) {
+				Record argRec = (Record) records.get(args[i]);
+				if (argRec != null) {
+					preprocess(args[i], argRec);
+				}
+			}
+		}
 
-        public String toStringOnOpenUnfinished() {
-            String result = "<" + name; //$NON-NLS-1$
-            Iterator<String> i = attrs.keySet().iterator();
-
-            while (i.hasNext()) {
-                String attrName = i.next();
-                String attrValue = attrs.get(attrName);
+		for (Iterator iter = rec.stats.iterator(); iter.hasNext();) {
+			Statement subStat = (Statement) iter.next();
+			if (subStat instanceof Expression) {
+				try {
+					Expression subExp = (Expression) subStat;
+					Record subRec = (Record) records.get(subExp.getValue());
+					if (subRec == null || subRec.exp == null
+							|| subRec.exp != subExp) {
+						iter.remove();
+						continue;
+					}
+					preprocess(subExp.getValue(), subRec);
+					if (subRec.stats.isEmpty()) {
+						if (isGetArrayStat(subExp.getTarget(), subExp
+								.getMethodName(), subExp.getArguments())
+								|| isGetPropertyStat(subExp.getMethodName(),
+										subExp.getArguments())) {
+							iter.remove();
+							continue;
+						}
+					}
+				} catch (Exception e) {
+					getExceptionListener().exceptionThrown(e);
+					iter.remove();
+				}
+				continue;
+			}
 
-                result += " " + attrName + "=\"" + attrValue + "\""; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
-            }
-            return result;
-        }
+			Object subStatArgs[] = subStat.getArguments();
+			for (int i = 0; i < subStatArgs.length; i++) {
+				Record argRec = (Record) records.get(subStatArgs[i]);
+				if (argRec != null) {
+					preprocess(subStatArgs[i], argRec);
+				}
+			}
+		}
+	}
 
-        public String toStringOnOpen() {
-            return toStringOnOpenUnfinished() + ">"; //$NON-NLS-1$
-        }
+	private void recordExpression(Object value, Expression exp) {
+		// record how a new object is created or obtained
+		Record rec = (Record) records.get(value);
+		if (rec == null) {
+			rec = new Record();
+			records.put(value, rec);
+		}
 
-        public String toStringShortForm() {
-            return toStringOnOpenUnfinished() + "/>"; //$NON-NLS-1$
-        }
+		if (rec.exp == null) {
+			// it is generated by its sub stats
+			for (Iterator iter = rec.stats.iterator(); iter.hasNext();) {
+				Statement stat = (Statement) iter.next();
+				try {
+					if (stat instanceof Expression) {
+						Expression expr = (Expression) stat;
+						Object subObj = expr.getValue();
+						// if(expr.getTarget().getClass() ==
+						// Class.class.getClass())
+						flushPrePending.add(value);
+					}
+				} catch (Exception e) {
+					e.printStackTrace();
+				}
 
-        public String toStringOnClose() {
-            return "</" + name + ">"; //$NON-NLS-1$ //$NON-NLS-2$
-        }
+			}
+		}
 
-        public String toStringOnCharacters() {
-            return XMLEncoder.escapeChars(characters);
-        }
+		rec.exp = exp;
 
-        @Override
-        public String toString() {
-            return toStringOnOpen() + toStringOnCharacters()
-                    + toStringOnClose();
-        }
-    }
+		// deal with 'owner' property
+		if (value == owner && owner != null) {
+			needOwner = true;
+		}
 
-    static class NameMaker {
+		// also record as a statement
+		recordStatement(exp);
+	}
 
-        private static final HashMap<String, Integer> numOfExemplars = new HashMap<String, Integer>();
+	private void recordStatement(Statement stat) {
+		// deal with 'owner' property
+		if (stat.getTarget() == owner && owner != null) {
+			needOwner = true;
+		}
 
-        public static void clear() {
-            numOfExemplars.clear();
-        }
+		// record how a statement affects the target object
+		Record rec = (Record) records.get(stat.getTarget());
+		if (rec == null) {
+			rec = new Record();
+			records.put(stat.getTarget(), rec);
+		}
+		rec.stats.add(stat);
+	}
 
-        private static String getCompName(Class<?> clz) {
-            if (clz.isArray()) {
-                return getCompName(clz.getComponentType()) + "Array"; //$NON-NLS-1$
-            }
-            return clz.getName().substring(clz.getName().lastIndexOf(".") + 1); //$NON-NLS-1$
-        }
+	/**
+	 * Sets the owner of this encoder.
+	 * 
+	 * @param owner
+	 *            the owner to set
+	 */
+	public void setOwner(Object owner) {
+		this.owner = owner;
+	}
+
+	/**
+	 * Records the expression so that it can be writtern out later, then calls
+	 * super implementation.
+	 */
+	public void writeExpression(Expression oldExp) {
+	    boolean oldWritingObject = writingObject;
+	    writingObject = true;
+		// get expression value
+		Object oldValue = null;
+		try {
+			oldValue = oldExp.getValue();
+		} catch (Exception e) {
+			getExceptionListener()
+					.exceptionThrown(
+							new Exception("failed to execute expression: "
+									+ oldExp, e));
+			return;
+		}
+
+		// check existence
+		if (get(oldValue) != null && (!(oldValue instanceof String) || oldWritingObject)) {
+			return;
+		}
 
-        public static String getInstanceName(Class<?> type) {
-            String result = null;
+		// record how the object is obtained
+		if (!isBasicType(oldValue) || (oldValue instanceof String && !oldWritingObject)) {
+			recordExpression(oldValue, oldExp);
+		}
 
-            String fullName;
-            String shortName;
-            Integer iNum;
-
-            if (type.isArray()) {
-                fullName = getCompName(type);
-                shortName = fullName;
-            } else {
-                fullName = type.getName();
-                shortName = fullName.substring(fullName.lastIndexOf(".") + 1); //$NON-NLS-1$
-            }
-            iNum = numOfExemplars.get(shortName);
-            if (iNum == null) {
-                numOfExemplars.put(shortName, new Integer(0));
-                result = shortName + "0"; //$NON-NLS-1$
-            } else {
-                int newValue = iNum.intValue() + 1;
+		super.writeExpression(oldExp);
+		writingObject = oldWritingObject;
+	}
+
+	/**
+	 * Records the object so that it can be writtern out later, then calls super
+	 * implementation.
+	 */
+	public void writeObject(Object o) {
+		synchronized (this) {
+			boolean oldWritingObject = writingObject;
+			writingObject = true;
+			try {
+				super.writeObject(o);
+			} finally {
+				writingObject = oldWritingObject;
+			}
+
+			// root object?
+			if (!writingObject) {
+				// add to pending
+				flushPending.addAll(flushPrePending);
+				flushPendingStat.addAll(flushPrePending);
+				flushPrePending.clear();
+				if (flushPending.contains(o)) {
+					flushPrePending.remove(o);
+					flushPendingStat.remove(o);
+				} else {
+					flushPending.add(o);
+				}
+				if (needOwner) {
+					this.flushPending.remove(owner);
+					this.flushPending.add(0, owner);
+				}
+			}
+		}
+	}
+
+	/**
+	 * Records the statement so that it can be writtern out later, then calls
+	 * super implementation.
+	 */
+	public void writeStatement(Statement oldStat) {
+		// record how the object is changed
+		recordStatement(oldStat);
+
+		super.writeStatement(oldStat);
+	}
 
-                result = shortName + Integer.toString(newValue);
-                numOfExemplars.put(shortName, new Integer(newValue));
-            }
-            return result;
-        }
-    }
 }
+
+

Added: harmony/enhanced/classlib/trunk/modules/beans/src/main/java/org/apache/harmony/beans/PrimitiveWrapperPersistenceDelegate.java
URL: http://svn.apache.org/viewvc/harmony/enhanced/classlib/trunk/modules/beans/src/main/java/org/apache/harmony/beans/PrimitiveWrapperPersistenceDelegate.java?view=auto&rev=556804
==============================================================================
--- harmony/enhanced/classlib/trunk/modules/beans/src/main/java/org/apache/harmony/beans/PrimitiveWrapperPersistenceDelegate.java (added)
+++ harmony/enhanced/classlib/trunk/modules/beans/src/main/java/org/apache/harmony/beans/PrimitiveWrapperPersistenceDelegate.java Mon Jul 16 20:49:39 2007
@@ -0,0 +1,69 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+
+package org.apache.harmony.beans;
+
+import java.beans.Encoder;
+import java.beans.Expression;
+import java.beans.PersistenceDelegate;
+
+/**
+ * A special internal <code>PersistenceDelegate</code> for wrapper classes of
+ * primitive types like int.
+ * 
+ */
+public class PrimitiveWrapperPersistenceDelegate extends PersistenceDelegate {
+
+    /*
+     * It's unnecessary to do anything for initialization, because two mutatable
+     * wrapper objects are actually equivalent already.
+     */
+    @Override
+    protected void initialize(Class type, Object oldInstance,
+            Object newInstance, Encoder enc) {
+        // do nothing
+    }
+
+    /*
+     * Instantiates a wrapper object using the constructor taking one String
+     * parameter except for Character.
+     */
+    @Override
+    protected Expression instantiate(Object oldInstance, Encoder enc) {
+        if (oldInstance instanceof Character) {
+            return new Expression(oldInstance, oldInstance.toString(),
+                    "charAt", new Object[] { new Integer(0) }); //$NON-NLS-1$
+        }
+        return new Expression(oldInstance, oldInstance.getClass(),
+                "new", new Object[] { oldInstance //$NON-NLS-1$
+                        .toString() });
+    }
+
+    /*
+     * Two wrapper objects are regarded mutatable if they are equal.
+     */
+    @Override
+    protected boolean mutatesTo(Object o1, Object o2) {
+        if (null == o2) {
+            return false;
+        }
+        return o1.equals(o2);
+    }
+
+}
+

Propchange: harmony/enhanced/classlib/trunk/modules/beans/src/main/java/org/apache/harmony/beans/PrimitiveWrapperPersistenceDelegate.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: harmony/enhanced/classlib/trunk/modules/beans/src/test/java/org/apache/harmony/beans/tests/java/beans/PersistenceDelegateTest.java
URL: http://svn.apache.org/viewvc/harmony/enhanced/classlib/trunk/modules/beans/src/test/java/org/apache/harmony/beans/tests/java/beans/PersistenceDelegateTest.java?view=diff&rev=556804&r1=556803&r2=556804
==============================================================================
--- harmony/enhanced/classlib/trunk/modules/beans/src/test/java/org/apache/harmony/beans/tests/java/beans/PersistenceDelegateTest.java (original)
+++ harmony/enhanced/classlib/trunk/modules/beans/src/test/java/org/apache/harmony/beans/tests/java/beans/PersistenceDelegateTest.java Mon Jul 16 20:49:39 2007
@@ -31,6 +31,7 @@
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import java.util.EmptyStackException;
+import java.util.LinkedList;
 import java.util.Stack;
 
 import junit.framework.TestCase;
@@ -266,6 +267,27 @@
         assertEquals(method, aMethod);
         assertEquals(method.getName(), aMethod.getName());
         assertEquals("barTalk", aMethod.getName());
+    }
+    
+    public void test_writeObject_java_util_Collection() {
+        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+        XMLEncoder encoder = new XMLEncoder(new BufferedOutputStream(
+            byteArrayOutputStream));
+        LinkedList<Integer> list = new LinkedList<Integer>();
+        list.add(10);
+        list.addFirst(2);
+        System.out.println(encoder.getPersistenceDelegate(LinkedList.class));
+
+        encoder.writeObject(list);
+        encoder.close();
+        DataInputStream stream = new DataInputStream(new ByteArrayInputStream(
+                byteArrayOutputStream.toByteArray()));
+        XMLDecoder decoder = new XMLDecoder(stream);
+        LinkedList<Integer> l = (LinkedList<Integer>) decoder.readObject();
+        assertEquals(list, l);
+        assertEquals(2, l.size());
+        assertEquals(new Integer(10), l.get(1));
+        
     }
 
     // <--



Mime
View raw message