Index: src/main/java/java/beans/UtilCollectionPersistenceDelegate.java
===================================================================
--- src/main/java/java/beans/UtilCollectionPersistenceDelegate.java (revision 0)
+++ src/main/java/java/beans/UtilCollectionPersistenceDelegate.java (revision 0)
@@ -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);
+ }
+ }
+ }
+}
Index: src/main/java/org/apache/harmony/beans/PrimitiveWrapperPersistenceDelegate.java
===================================================================
--- src/main/java/org/apache/harmony/beans/PrimitiveWrapperPersistenceDelegate.java (revision 0)
+++ src/main/java/org/apache/harmony/beans/PrimitiveWrapperPersistenceDelegate.java (revision 0)
@@ -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 PersistenceDelegate 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);
+ }
+
+}
+
Index: src/test/java/org/apache/harmony/beans/tests/java/beans/PersistenceDelegateTest.java
===================================================================
--- src/test/java/org/apache/harmony/beans/tests/java/beans/PersistenceDelegateTest.java (revision 556788)
+++ src/test/java/org/apache/harmony/beans/tests/java/beans/PersistenceDelegateTest.java (working copy)
@@ -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 list = new LinkedList();
+ 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 l = (LinkedList) decoder.readObject();
+ assertEquals(list, l);
+ assertEquals(2, l.size());
+ assertEquals(new Integer(10), l.get(1));
+
}
// <--
Index: src/main/java/java/beans/Encoder.java
===================================================================
--- src/main/java/java/beans/Encoder.java (revision 556788)
+++ src/main/java/java/beans/Encoder.java (working copy)
@@ -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 Encoder, together with PersistenceDelegate
+ * 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.
+ *
+ * The Encoder and PersistenceDelegate 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.
+ *
+ *
+ */
+@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, PersistenceDelegate> persistenceDelegates = new HashMap, PersistenceDelegate>();
-
- Vector
+ *
+ * @param type
+ * a class type
+ * @return a PersistenceDelegate 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 PersistenceDelegate 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.
+ *
+ * The implementation first check the return value of the expression. If
+ * there exists a new version of the object, simply return.
+ *
+ *
+ * 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,
+ * writeObject() is called to create the new version.
+ *
+ *
+ * The new expression is then executed to obtained a new copy of the old
+ * return value.
+ *
+ *
+ * Call writeObject() 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.
+ *
+ *
+ * @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.
+ *
+ * The implementation simply finds the PersistenceDelegate
+ * responsible for the object's class, and delegate the call to it.
+ *
+ *
+ * @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.
+ *
+ * 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,
+ * writeObject() is called to create one.
+ *
+ *
+ * The new statement is then executed to change the state of the new object.
+ *
+ *
+ * @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;
- }
}
+
Index: src/main/java/java/beans/XMLEncoder.java
===================================================================
--- src/main/java/java/beans/XMLEncoder.java (revision 556788)
+++ src/main/java/java/beans/XMLEncoder.java (working copy)
@@ -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;
+/**
+ * XMLEncoder exnteds Encoder to write out the
+ * encoded statements and expressions in xml format. The xml can be read by
+ * XMLDecoder later to restore objects and their states.
+ *
+ * The API is similar to ObjectOutputStream.
+ *
+ *
+ */
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 printed = new Vector();
+ // 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 XMLEncoder.
+ *
+ * @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 flush() first, then write out xml footer and close
+ * the underlying output stream.
+ */
+ public void close() {
+ flush();
+ out.println("");
+ 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.
+ *
+ * The implementation write the xml header first if it has not been
+ * writtern. Then all pending objects since last flush are writtern.
+ *
+ */
+ public void flush() {
+ synchronized (this) {
+ // write xml header
+ if (!hasXmlHeader) {
+ out.println("");
+ out.println("");
+ 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("");
+ } 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("");
+ flushString((String) obj);
+ out.println("");
+ } else if (obj instanceof Class) {
+ out.print("");
+ out.print(((Class) obj).getName());
+ out.println("");
+ } else if (obj instanceof Boolean) {
+ out.print("");
+ out.print(obj);
+ out.println("");
+ } else if (obj instanceof Byte) {
+ out.print("");
+ out.print(obj);
+ out.println("");
+ } else if (obj instanceof Character) {
+ out.print("");
+ out.print(obj);
+ out.println("");
+ } else if (obj instanceof Double) {
+ out.print("");
+ out.print(obj);
+ out.println("");
+ } else if (obj instanceof Float) {
+ out.print("");
+ out.print(obj);
+ out.println("");
+ } else if (obj instanceof Integer) {
+ out.print("");
+ out.print(obj);
+ out.println("");
+ } else if (obj instanceof Long) {
+ out.print("");
+ out.print(obj);
+ out.println("");
+ } else if (obj instanceof Short) {
+ out.print("");
+ out.print(obj);
+ out.println("");
+ } 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("");
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 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, ""); //$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("");
+ 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("");
+ }
- 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 it = node.statements();
-
- while (it.hasNext()) {
- Statement s = it.next();
-
- printVoidTag(++tabCount, s);
- --tabCount;
- }
- } else { // if object
- Iterator i1;
- Iterator 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 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("");
+ }
+ }
- 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("<");
+ } else if (c == '>') {
+ out.print(">");
+ } else if (c == '&') {
+ out.print("&");
+ } else if (c == '\'') {
+ out.print("'");
+ } else if (c == '"') {
+ out.print(""");
+ } 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, ""); //$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("&"); //$NON-NLS-1$
- break;
- case '<':
- sb.append("<"); //$NON-NLS-1$
- break;
- case '>':
- sb.append(">"); //$NON-NLS-1$
- break;
- case '\'':
- sb.append("'"); //$NON-NLS-1$
- break;
- case '"':
- sb.append("""); //$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 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();
- this.characters = null;
- }
+ // count reference
+ rec.refCount++;
- public Tag(String name, String characters) {
- this.name = name;
- this.attrs = new LinkedHashMap();
- 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 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 numOfExemplars = new HashMap();
+ 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;
- }
- }
}
+
+