Author: aadamchik
Date: Thu Sep 17 14:13:19 2009
New Revision: 816205

URL: http://svn.apache.org/viewvc?rev=816205&view=rev
Log:
prototyping (de)serializer based on XStream

callbacks support

Added:
    
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/DeserializationCallback.java
      - copied, changed from r816198, 
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/xstream/SerializerStack.java
    
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/SerializationCallback.java
      - copied, changed from r816198, 
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/xstream/SerializerStack.java
    
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/xstream/PersistentDeserializeConverter.java
      - copied, changed from r816198, 
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/xstream/PersistentCloneUnmarshalConverter.java
    
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/xstream/PersistentSerializeConverter.java
      - copied, changed from r816198, 
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/xstream/PersistentMarshalConverter.java
Removed:
    
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/xstream/PersistentCloneUnmarshalConverter.java
    
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/xstream/PersistentMarshalConverter.java
Modified:
    
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/BaseSerializer.java
    
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/Subgraph.java
    
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/SubgraphNode.java
    
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/xstream/DeserializerStack.java
    
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/xstream/SerializerStack.java
    
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/xstream/XStreamDeserializer.java
    
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/xstream/XStreamSerializer.java
    
cayenne/sandbox/cayenne-serialization/src/test/java/org/apache/cayenne/serialization/SubgraphTest.java
    
cayenne/sandbox/cayenne-serialization/src/test/java/org/apache/cayenne/serialization/xstream/XStreamDeserializerTest.java
    
cayenne/sandbox/cayenne-serialization/src/test/java/org/apache/cayenne/serialization/xstream/XStreamSerializerTest.java

Modified: 
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/BaseSerializer.java
URL: 
http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/BaseSerializer.java?rev=816205&r1=816204&r2=816205&view=diff
==============================================================================
--- 
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/BaseSerializer.java
 (original)
+++ 
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/BaseSerializer.java
 Thu Sep 17 14:13:19 2009
@@ -1,3 +1,21 @@
+/*****************************************************************
+ *   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.cayenne.serialization;
 
 import java.io.OutputStream;

Copied: 
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/DeserializationCallback.java
 (from r816198, 
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/xstream/SerializerStack.java)
URL: 
http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/DeserializationCallback.java?p2=cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/DeserializationCallback.java&p1=cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/xstream/SerializerStack.java&r1=816198&r2=816205&rev=816205&view=diff
==============================================================================
--- 
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/xstream/SerializerStack.java
 (original)
+++ 
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/DeserializationCallback.java
 Thu Sep 17 14:13:19 2009
@@ -16,34 +16,9 @@
  *  specific language governing permissions and limitations
  *  under the License.
  ****************************************************************/
-package org.apache.cayenne.serialization.xstream;
+package org.apache.cayenne.serialization;
 
-import org.apache.cayenne.serialization.Subgraph;
-import org.apache.cayenne.serialization.SubgraphNode;
+public interface DeserializationCallback {
 
-import com.thoughtworks.xstream.core.util.FastStack;
-
-/**
- * A single-threaded stack for {...@link Subgraph} traversal.
- */
-class SerializerStack {
-
-       private FastStack stack;
-
-       public SerializerStack(SubgraphNode graphDescriptorRoot) {
-               stack = new FastStack(graphDescriptorRoot.getMaxDepth());
-               stack.push(graphDescriptorRoot);
-       }
-
-       public void pushNode(SubgraphNode node) {
-               stack.push(node);
-       }
-
-       public void popNode() {
-               stack.popSilently();
-       }
-
-       public SubgraphNode peekNode() {
-               return (SubgraphNode) stack.peek();
-       }
+       void postDeserialize(SubgraphNode node, Object object);
 }

Copied: 
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/SerializationCallback.java
 (from r816198, 
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/xstream/SerializerStack.java)
URL: 
http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/SerializationCallback.java?p2=cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/SerializationCallback.java&p1=cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/xstream/SerializerStack.java&r1=816198&r2=816205&rev=816205&view=diff
==============================================================================
--- 
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/xstream/SerializerStack.java
 (original)
+++ 
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/SerializationCallback.java
 Thu Sep 17 14:13:19 2009
@@ -16,34 +16,11 @@
  *  specific language governing permissions and limitations
  *  under the License.
  ****************************************************************/
-package org.apache.cayenne.serialization.xstream;
+package org.apache.cayenne.serialization;
 
-import org.apache.cayenne.serialization.Subgraph;
-import org.apache.cayenne.serialization.SubgraphNode;
+import org.apache.cayenne.query.Query;
 
-import com.thoughtworks.xstream.core.util.FastStack;
+public interface SerializationCallback {
 
-/**
- * A single-threaded stack for {...@link Subgraph} traversal.
- */
-class SerializerStack {
-
-       private FastStack stack;
-
-       public SerializerStack(SubgraphNode graphDescriptorRoot) {
-               stack = new FastStack(graphDescriptorRoot.getMaxDepth());
-               stack.push(graphDescriptorRoot);
-       }
-
-       public void pushNode(SubgraphNode node) {
-               stack.push(node);
-       }
-
-       public void popNode() {
-               stack.popSilently();
-       }
-
-       public SubgraphNode peekNode() {
-               return (SubgraphNode) stack.peek();
-       }
+       Query relationshipQuery(SubgraphNode node, Object sourceObject);
 }

Modified: 
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/Subgraph.java
URL: 
http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/Subgraph.java?rev=816205&r1=816204&r2=816205&view=diff
==============================================================================
--- 
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/Subgraph.java
 (original)
+++ 
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/Subgraph.java
 Thu Sep 17 14:13:19 2009
@@ -69,7 +69,7 @@
                        String token = tokens.nextToken();
 
                        if (tokens.hasMoreTokens()) {
-                               node = node.getOrAppendChild(token);
+                               node = node.getChild(token, false);
                        } else {
                                // last token is the attribute name
                                node.excludeAttribute(token);
@@ -86,7 +86,7 @@
 
                SubgraphNode node = rootNode;
                while (tokens.hasMoreTokens()) {
-                       node = node.getOrAppendChild(tokens.nextToken());
+                       node = node.getChild(tokens.nextToken(), true);
                }
        }
 
@@ -101,8 +101,36 @@
                SubgraphNode node = rootNode;
                while (tokens.hasMoreTokens()) {
                        String token = tokens.nextToken();
-                       node = node.getOrAppendChild(token);
+                       node = node.getChild(token, true);
                        node.setSerializedByReference(!tokens.hasMoreTokens());
                }
        }
+
+       public void addDeserializationCallback(String path,
+                       DeserializationCallback callback) {
+
+               StringTokenizer tokens = new StringTokenizer(path, ".");
+
+               SubgraphNode node = rootNode;
+               while (tokens.hasMoreTokens()) {
+                       String token = tokens.nextToken();
+                       node = node.getChild(token, false);
+               }
+
+               node.addDeserializationCallback(callback);
+       }
+
+       public void addSerializationCallback(String path,
+                       SerializationCallback callback) {
+
+               StringTokenizer tokens = new StringTokenizer(path, ".");
+
+               SubgraphNode node = rootNode;
+               while (tokens.hasMoreTokens()) {
+                       String token = tokens.nextToken();
+                       node = node.getChild(token, false);
+               }
+
+               node.addSerializationCallback(callback);
+       }
 }

Modified: 
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/SubgraphNode.java
URL: 
http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/SubgraphNode.java?rev=816205&r1=816204&r2=816205&view=diff
==============================================================================
--- 
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/SubgraphNode.java
 (original)
+++ 
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/SubgraphNode.java
 Thu Sep 17 14:13:19 2009
@@ -44,11 +44,17 @@
        private Map<String, SubgraphNode> children;
        private boolean serializedByReference;
        private List<AttributeProperty> attributeProperties;
+       private List<SerializationCallback> serializationCallbacks;
+       private List<DeserializationCallback> deserializationCallbacks;
 
        /**
         * Creates a root subgraph node.
         */
        SubgraphNode(ClassDescriptor classDescriptor) {
+
+               this.serializationCallbacks = new 
ArrayList<SerializationCallback>(3);
+               this.deserializationCallbacks = new 
ArrayList<DeserializationCallback>(
+                               3);
                this.classDescriptor = classDescriptor;
 
                // cache properties
@@ -74,21 +80,20 @@
                this(incomingProperty.getTargetDescriptor());
                this.incomingProperty = incomingProperty;
        }
-       
+
        public int getMaxDepth() {
-               if(children != null) {
+               if (children != null) {
                        int depth = 0;
-                       for(SubgraphNode child : children.values()) {
+                       for (SubgraphNode child : children.values()) {
                                int childDepth = child.getMaxDepth();
-                               
-                               if(depth < childDepth) {
+
+                               if (depth < childDepth) {
                                        depth = childDepth;
                                }
                        }
-                       
+
                        return depth + 1;
-               }
-               else {
+               } else {
                        return 1;
                }
        }
@@ -104,38 +109,51 @@
        void setSerializedByReference(boolean reference) {
                this.serializedByReference = reference;
        }
-       
+
        void excludeAttribute(String attributeName) {
                Iterator<AttributeProperty> it = attributeProperties.iterator();
-               while(it.hasNext()) {
+               while (it.hasNext()) {
                        AttributeProperty property = it.next();
-                       if(property.getName().equals(attributeName)) {
+                       if (property.getName().equals(attributeName)) {
                                it.remove();
-                               break;
+                               return;
                        }
                }
+
+               throw new IllegalArgumentException(
+                               "Attribute is either unmapped or was exlcuded 
before: "
+                                               + attributeName);
+       }
+
+       public SubgraphNode getChild(String name) {
+               return children != null ? children.get(name) : null;
        }
 
-       SubgraphNode getOrAppendChild(String path) {
+       SubgraphNode getChild(String name, boolean create) {
 
                SubgraphNode child = null;
                if (children != null) {
-                       child = children.get(path);
+                       child = children.get(name);
                } else {
                        children = new LinkedHashMap<String, SubgraphNode>();
                }
 
                if (child == null) {
 
-                       Property relationship = 
classDescriptor.getProperty(path);
+                       if (!create) {
+                               throw new IllegalArgumentException("Invalid 
child path: "
+                                               + name);
+                       }
+
+                       Property relationship = 
classDescriptor.getProperty(name);
 
                        if (relationship == null) {
-                               throw new IllegalArgumentException("Path '" + 
path
+                               throw new IllegalArgumentException("Path '" + 
name
                                                + "' does not denote a mapped 
class property");
                        }
 
                        if (!(relationship instanceof ArcProperty)) {
-                               throw new IllegalArgumentException("Path '" + 
path
+                               throw new IllegalArgumentException("Path '" + 
name
                                                + "' does not denote a mapped 
relationship property. "
                                                + "Is this an attribute?");
                        }
@@ -143,12 +161,20 @@
                        ArcProperty arc = (ArcProperty) relationship;
 
                        child = new SubgraphNode(arc);
-                       children.put(path, child);
+                       children.put(name, child);
                }
 
                return child;
        }
 
+       void addSerializationCallback(SerializationCallback callback) {
+               serializationCallbacks.add(callback);
+       }
+
+       void addDeserializationCallback(DeserializationCallback callback) {
+               deserializationCallbacks.add(callback);
+       }
+
        public ClassDescriptor getClassDescriptor() {
                return classDescriptor;
        }
@@ -169,4 +195,12 @@
                return children != null ? children.values() : Collections
                                .<SubgraphNode> emptyList();
        }
+
+       public List<SerializationCallback> getSerializationCallbacks() {
+               return serializationCallbacks;
+       }
+
+       public List<DeserializationCallback> getDeserializationCallbacks() {
+               return deserializationCallbacks;
+       }
 }
\ No newline at end of file

Modified: 
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/xstream/DeserializerStack.java
URL: 
http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/xstream/DeserializerStack.java?rev=816205&r1=816204&r2=816205&view=diff
==============================================================================
--- 
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/xstream/DeserializerStack.java
 (original)
+++ 
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/xstream/DeserializerStack.java
 Thu Sep 17 14:13:19 2009
@@ -18,49 +18,76 @@
  ****************************************************************/
 package org.apache.cayenne.serialization.xstream;
 
+import org.apache.cayenne.PersistenceState;
+import org.apache.cayenne.Persistent;
 import org.apache.cayenne.reflect.ArcProperty;
 import org.apache.cayenne.reflect.ToManyProperty;
+import org.apache.cayenne.serialization.DeserializationCallback;
+import org.apache.cayenne.serialization.SubgraphNode;
 
 import com.thoughtworks.xstream.core.util.FastStack;
 
 class DeserializerStack {
 
-       private FastStack stack;
+       private FastStack subgraphStack;
+       private FastStack objectStack;
        private int counter;
 
-       DeserializerStack() {
-               stack = new FastStack(10);
+       DeserializerStack(SubgraphNode root) {
+               int maxDepth = root.getMaxDepth();
+               objectStack = new FastStack(maxDepth);
+               subgraphStack = new FastStack(maxDepth);
+               subgraphStack.push(root);
        }
 
-       int incrementCounter() {
-               return ++counter;
-       }
+       void pushObject(Object object) {
 
-       void push(Object object) {
+               SubgraphNode node = (SubgraphNode) subgraphStack.peek();
 
                // connect to parent
-               Object[] peek = (Object[]) stack.peek();
-               if (peek != null) {
+               ArcProperty incoming = node.getIncomingProperty();
+               if (incoming != null) {
+                       Object peek = objectStack.peek();
 
-                       if (peek[1] instanceof ToManyProperty) {
-                               ((ToManyProperty) peek[1]).addTarget(peek[0], 
object, true);
+                       if (incoming instanceof ToManyProperty) {
+                               ((ToManyProperty) incoming).addTarget(peek, 
object, true);
                        } else {
-                               ((ArcProperty) peek[1]).writeProperty(peek[0], 
null, object);
+                               incoming.writeProperty(peek, null, object);
                        }
                }
 
-               Object[] entry = new Object[2];
-               entry[0] = object;
-               stack.push(entry);
+               // apply callbacks
+               for (DeserializationCallback callback : node
+                               .getDeserializationCallbacks()) {
+                       callback.postDeserialize(node, object);
+               }
+
+               objectStack.push(object);
        }
 
-       void pop() {
-               stack.popSilently();
+       int popObject() {
+               Persistent object = (Persistent) objectStack.pop();
+               if (object != null
+                               && object.getPersistenceState() == 
PersistenceState.NEW) {
+                       counter++;
+               }
+               
+               return counter;
        }
 
-       void startRelationship(ArcProperty property) {
-               Object[] peek = (Object[]) stack.peek();
-               peek[1] = property;
+       boolean pushPath(String name) {
+
+               SubgraphNode child = ((SubgraphNode) subgraphStack.peek())
+                               .getChild(name);
+               if (child == null) {
+                       return false;
+               }
+
+               subgraphStack.push(child);
+               return true;
        }
 
+       void popPath() {
+               subgraphStack.popSilently();
+       }
 }

Copied: 
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/xstream/PersistentDeserializeConverter.java
 (from r816198, 
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/xstream/PersistentCloneUnmarshalConverter.java)
URL: 
http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/xstream/PersistentDeserializeConverter.java?p2=cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/xstream/PersistentDeserializeConverter.java&p1=cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/xstream/PersistentCloneUnmarshalConverter.java&r1=816198&r2=816205&rev=816205&view=diff
==============================================================================
--- 
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/xstream/PersistentCloneUnmarshalConverter.java
 (original)
+++ 
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/xstream/PersistentDeserializeConverter.java
 Thu Sep 17 14:13:19 2009
@@ -27,6 +27,7 @@
 import org.apache.cayenne.reflect.ClassDescriptor;
 import org.apache.cayenne.reflect.Property;
 import org.apache.cayenne.reflect.ToManyProperty;
+import org.apache.cayenne.serialization.SubgraphNode;
 
 import com.thoughtworks.xstream.converters.Converter;
 import com.thoughtworks.xstream.converters.MarshallingContext;
@@ -34,22 +35,20 @@
 import com.thoughtworks.xstream.io.HierarchicalStreamReader;
 import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
 
-/**
- * "Clone" in {...@link PersistentCloneUnmarshalConverter} means that ids of 
the
- * Persistent objects will be ignored, and new objects will be created.
- */
-class PersistentCloneUnmarshalConverter implements Converter {
+class PersistentDeserializeConverter implements Converter {
 
-       private static final String TRAVERSAL_CONTEXT_KEY = 
PersistentCloneUnmarshalConverter.class
+       private static final String STACK_KEY = 
PersistentDeserializeConverter.class
                        .getName()
-                       + "_TRAVERSAL_CONTEXT";
+                       + "_STACK";
 
        private ObjectContext objectContext;
        private int commitCountThreshold;
+       private SubgraphNode rootNode;
 
-       public PersistentCloneUnmarshalConverter(ObjectContext objectContext,
-                       int commitCountThreshold) {
+       public PersistentDeserializeConverter(SubgraphNode rootNode,
+                       ObjectContext objectContext, int commitCountThreshold) {
 
+               this.rootNode = rootNode;
                this.objectContext = objectContext;
                this.commitCountThreshold = commitCountThreshold;
        }
@@ -83,9 +82,9 @@
                // TODO: handle deleted objects that no longer exist...
 
                if (object != null) {
-                       DeserializerStack stack = 
getDeserialierCounter(context);
-                       stack.push(object);
-                       stack.pop();
+                       DeserializerStack stack = getStack(context);
+                       stack.pushObject(object);
+                       stack.popObject();
                }
 
                return object;
@@ -98,12 +97,12 @@
                ClassDescriptor descriptor = objectContext.getEntityResolver()
                                .getClassDescriptor(entityName);
 
-               DeserializerStack stack = getDeserialierCounter(context);
+               DeserializerStack stack = getStack(context);
 
                Object object = descriptor.createObject();
                objectContext.registerNewObject(object);
 
-               stack.push(object);
+               stack.pushObject(object);
 
                while (reader.hasMoreChildren()) {
                        reader.moveDown();
@@ -113,27 +112,31 @@
                                deserializeAttribute(reader, context, object,
                                                (AttributeProperty) property);
                        } else {
-                               ArcProperty arc = (ArcProperty) property;
 
-                               stack.startRelationship(arc);
+                               if (stack.pushPath(property.getName())) {
 
-                               if (arc.getRelationship().isToMany()) {
-                                       deserializeToManyRelationship(reader, 
context, object,
-                                                       (ToManyProperty) arc);
-                               } else {
-                                       deserializeToOneRelationship(reader, 
context, object, arc);
+                                       ArcProperty arc = (ArcProperty) 
property;
+                                       if (arc.getRelationship().isToMany()) {
+                                               
deserializeToManyRelationship(reader, context, object,
+                                                               
(ToManyProperty) arc);
+                                       } else {
+                                               
deserializeToOneRelationship(reader, context, object,
+                                                               arc);
+                                       }
+
+                                       stack.popPath();
                                }
                        }
 
                        reader.moveUp();
                }
 
-               if (commitCountThreshold > 0
-                               && stack.incrementCounter() % 
commitCountThreshold == 0) {
+               int count = stack.popObject();
+
+               if (commitCountThreshold > 0 && count % commitCountThreshold == 
0) {
                        objectContext.commitChanges();
                }
 
-               stack.pop();
                return object;
        }
 
@@ -152,9 +155,12 @@
 
                Class<?> javaType = 
property.getTargetDescriptor().getObjectClass();
 
-               reader.moveDown();
-               context.convertAnother(parentObject, javaType);
-               reader.moveUp();
+               // check for children to handle optional to-one
+               if (reader.hasMoreChildren()) {
+                       reader.moveDown();
+                       context.convertAnother(parentObject, javaType);
+                       reader.moveUp();
+               }
        }
 
        private void deserializeToManyRelationship(HierarchicalStreamReader 
reader,
@@ -170,15 +176,14 @@
                }
        }
 
-       private DeserializerStack getDeserialierCounter(UnmarshallingContext 
context) {
-               DeserializerStack deserializerCounter = (DeserializerStack) 
context
-                               .get(TRAVERSAL_CONTEXT_KEY);
+       private DeserializerStack getStack(UnmarshallingContext context) {
+               DeserializerStack stack = (DeserializerStack) 
context.get(STACK_KEY);
 
-               if (deserializerCounter == null) {
-                       deserializerCounter = new DeserializerStack();
-                       context.put(TRAVERSAL_CONTEXT_KEY, deserializerCounter);
+               if (stack == null) {
+                       stack = new DeserializerStack(rootNode);
+                       context.put(STACK_KEY, stack);
                }
 
-               return deserializerCounter;
+               return stack;
        }
 }

Copied: 
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/xstream/PersistentSerializeConverter.java
 (from r816198, 
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/xstream/PersistentMarshalConverter.java)
URL: 
http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/xstream/PersistentSerializeConverter.java?p2=cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/xstream/PersistentSerializeConverter.java&p1=cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/xstream/PersistentMarshalConverter.java&r1=816198&r2=816205&rev=816205&view=diff
==============================================================================
--- 
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/xstream/PersistentMarshalConverter.java
 (original)
+++ 
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/xstream/PersistentSerializeConverter.java
 Thu Sep 17 14:13:19 2009
@@ -21,14 +21,18 @@
 import org.apache.cayenne.CayenneException;
 import org.apache.cayenne.CayenneRuntimeException;
 import org.apache.cayenne.DataObject;
+import org.apache.cayenne.DataObjectUtils;
 import org.apache.cayenne.DataRow;
+import org.apache.cayenne.ObjectContext;
 import org.apache.cayenne.Persistent;
 import org.apache.cayenne.access.DataContext;
 import org.apache.cayenne.access.ResultIterator;
+import org.apache.cayenne.query.Query;
 import org.apache.cayenne.query.RelationshipQuery;
 import org.apache.cayenne.reflect.ArcProperty;
 import org.apache.cayenne.reflect.AttributeProperty;
 import org.apache.cayenne.reflect.Property;
+import org.apache.cayenne.serialization.SerializationCallback;
 import org.apache.cayenne.serialization.Subgraph;
 import org.apache.cayenne.serialization.SubgraphNode;
 
@@ -38,16 +42,16 @@
 import com.thoughtworks.xstream.io.HierarchicalStreamReader;
 import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
 
-class PersistentMarshalConverter<T> implements Converter {
+class PersistentSerializeConverter implements Converter {
 
-       private static final String TRAVERSAL_CONTEXT_KEY = 
PersistentMarshalConverter.class
+       private static final String STACK_KEY = 
PersistentSerializeConverter.class
                        .getName()
-                       + "_TRAVERSAL_CONTEXT";
+                       + "_STACK";
 
-       private Subgraph<T> subgraph;
+       private Subgraph<?> subgraph;
        private int statementFetchSize;
 
-       public PersistentMarshalConverter(Subgraph<T> subgraph,
+       public PersistentSerializeConverter(Subgraph<?> subgraph,
                        int statementFetchSize) {
                this.subgraph = subgraph;
                this.statementFetchSize = statementFetchSize;
@@ -56,7 +60,7 @@
        public void marshal(Object object, HierarchicalStreamWriter writer,
                        MarshallingContext context) {
 
-               SerializerStack serializerContext = getSerializerStack(context);
+               SerializerStack serializerContext = getStack(context);
 
                SubgraphNode node = serializerContext.peekNode();
 
@@ -76,13 +80,24 @@
 
                        serializerContext.pushNode(child);
 
+                       Query query = null;
+                       for (SerializationCallback callback : child
+                                       .getSerializationCallbacks()) {
+                               query = callback.relationshipQuery(child, 
object);
+                               if (query != null) {
+                                       break;
+                               }
+                       }
+
                        ArcProperty incoming = child.getIncomingProperty();
                        boolean byReference = child.isSerializedByReference();
 
                        if (incoming.getRelationship().isToMany()) {
-                               marshalToMany(object, incoming, writer, 
context, byReference);
+                               marshalToMany(object, incoming, writer, 
context, byReference,
+                                               query);
                        } else {
-                               marshalToOne(object, incoming, writer, context, 
byReference);
+                               marshalToOne(object, incoming, writer, context, 
byReference,
+                                               query);
                        }
 
                        serializerContext.popNode();
@@ -107,31 +122,47 @@
 
        private void marshalToOne(Object object, ArcProperty arc,
                        HierarchicalStreamWriter writer, MarshallingContext 
context,
-                       boolean byReference) {
+                       boolean byReference, Query query) {
 
-               writer.startNode(arc.getName());
+               Persistent value = null;
 
-               Persistent value = (Persistent) arc.readProperty(object);
+               if (query != null) {
+                       ObjectContext objectContext = ((Persistent) object)
+                                       .getObjectContext();
+                       value = (Persistent) 
DataObjectUtils.objectForQuery(objectContext,
+                                       query);
+               } else {
+                       value = (Persistent) arc.readProperty(object);
+               }
+
+               // note that we don't even write an empty tag for NULL to-one. 
This may
+               // be a problem only if we allow non-reference nodes to be 
attached to
+               // reference-serialized nodes
                if (value != null) {
+                       writer.startNode(arc.getName());
                        context.convertAnother(byReference ? 
value.getObjectId() : value);
+                       writer.endNode();
                }
 
-               writer.endNode();
        }
 
        private void marshalToMany(Object object, ArcProperty arc,
                        HierarchicalStreamWriter writer, MarshallingContext 
context,
-                       boolean byReference) {
+                       boolean byReference, Query query) {
 
                writer.startNode(arc.getName());
 
                Persistent persistent = (Persistent) object;
-               RelationshipQuery query = new RelationshipQuery(persistent
-                               .getObjectId(), arc.getName());
 
-               // "fetchSize" is absolutely critical to avoid storing the 
entire
-               // ResultSet in memory.
-               query.setStatementFetchSize(statementFetchSize);
+               if (query == null) {
+                       RelationshipQuery relationshipQuery = new 
RelationshipQuery(
+                                       persistent.getObjectId(), 
arc.getName());
+
+                       // "fetchSize" is absolutely critical to avoid storing 
the entire
+                       // ResultSet in memory.
+                       
relationshipQuery.setStatementFetchSize(statementFetchSize);
+                       query = relationshipQuery;
+               }
 
                DataContext dataContext = (DataContext) 
persistent.getObjectContext();
 
@@ -162,16 +193,15 @@
                writer.endNode();
        }
 
-       private SerializerStack getSerializerStack(MarshallingContext context) {
-               SerializerStack travsersalContext = (SerializerStack) context
-                               .get(TRAVERSAL_CONTEXT_KEY);
+       private SerializerStack getStack(MarshallingContext context) {
+               SerializerStack stack = (SerializerStack) 
context.get(STACK_KEY);
 
-               if (travsersalContext == null) {
-                       travsersalContext = new 
SerializerStack(subgraph.getRootNode());
-                       context.put(TRAVERSAL_CONTEXT_KEY, travsersalContext);
+               if (stack == null) {
+                       stack = new SerializerStack(subgraph.getRootNode());
+                       context.put(STACK_KEY, stack);
                }
 
-               return travsersalContext;
+               return stack;
        }
 
        public Object unmarshal(HierarchicalStreamReader reader,

Modified: 
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/xstream/SerializerStack.java
URL: 
http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/xstream/SerializerStack.java?rev=816205&r1=816204&r2=816205&view=diff
==============================================================================
--- 
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/xstream/SerializerStack.java
 (original)
+++ 
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/xstream/SerializerStack.java
 Thu Sep 17 14:13:19 2009
@@ -30,9 +30,9 @@
 
        private FastStack stack;
 
-       public SerializerStack(SubgraphNode graphDescriptorRoot) {
-               stack = new FastStack(graphDescriptorRoot.getMaxDepth());
-               stack.push(graphDescriptorRoot);
+       public SerializerStack(SubgraphNode root) {
+               stack = new FastStack(root.getMaxDepth());
+               stack.push(root);
        }
 
        public void pushNode(SubgraphNode node) {

Modified: 
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/xstream/XStreamDeserializer.java
URL: 
http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/xstream/XStreamDeserializer.java?rev=816205&r1=816204&r2=816205&view=diff
==============================================================================
--- 
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/xstream/XStreamDeserializer.java
 (original)
+++ 
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/xstream/XStreamDeserializer.java
 Thu Sep 17 14:13:19 2009
@@ -42,8 +42,8 @@
                int commitCountThreshold = isCommitting() ? 
getCommitCountThreshold()
                                : 0;
 
-               xstream.registerConverter(new PersistentCloneUnmarshalConverter(
-                               context, commitCountThreshold));
+               xstream.registerConverter(new PersistentDeserializeConverter(
+                               subgraph.getRootNode(), context, 
commitCountThreshold));
                xstream.registerConverter(new ObjectIdConverter(context
                                .getEntityResolver()));
 

Modified: 
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/xstream/XStreamSerializer.java
URL: 
http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/xstream/XStreamSerializer.java?rev=816205&r1=816204&r2=816205&view=diff
==============================================================================
--- 
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/xstream/XStreamSerializer.java
 (original)
+++ 
cayenne/sandbox/cayenne-serialization/src/main/java/org/apache/cayenne/serialization/xstream/XStreamSerializer.java
 Thu Sep 17 14:13:19 2009
@@ -40,7 +40,7 @@
                XStream xstream = createXStream(subgraph.getRootNode()
                                .getClassDescriptor());
 
-               xstream.registerConverter(new 
PersistentMarshalConverter<T>(subgraph,
+               xstream.registerConverter(new 
PersistentSerializeConverter(subgraph,
                                statementFetchSize));
                xstream.registerConverter(new ObjectIdConverter(null));
 

Modified: 
cayenne/sandbox/cayenne-serialization/src/test/java/org/apache/cayenne/serialization/SubgraphTest.java
URL: 
http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-serialization/src/test/java/org/apache/cayenne/serialization/SubgraphTest.java?rev=816205&r1=816204&r2=816205&view=diff
==============================================================================
--- 
cayenne/sandbox/cayenne-serialization/src/test/java/org/apache/cayenne/serialization/SubgraphTest.java
 (original)
+++ 
cayenne/sandbox/cayenne-serialization/src/test/java/org/apache/cayenne/serialization/SubgraphTest.java
 Thu Sep 17 14:13:19 2009
@@ -18,6 +18,7 @@
  ****************************************************************/
 package org.apache.cayenne.serialization;
 
+import org.apache.cayenne.query.Query;
 import org.apache.cayenne.serialization.persistent.Table1;
 import org.apache.cayenne.serialization.persistent.Table2;
 import org.apache.cayenne.serialization.unit.SerializationCase;
@@ -32,6 +33,53 @@
                assertEquals(0, subgraph.getRootNode().getChildren().size());
        }
 
+       public void testAddDerserializationCallback() {
+               Subgraph<Table2> subgraph = new Subgraph<Table2>(Table2.class,
+                               newContext().getEntityResolver());
+               subgraph.addSerializeByReferencePath(Table2.TABLE1_PROPERTY);
+
+               SubgraphNode node = subgraph.getRootNode().getChild(
+                               Table2.TABLE1_PROPERTY, false);
+
+               assertEquals(0, node.getSerializationCallbacks().size());
+               assertEquals(0, node.getDeserializationCallbacks().size());
+
+               subgraph.addDeserializationCallback(Table2.TABLE1_PROPERTY,
+                               new DeserializationCallback() {
+                                       public void 
postDeserialize(SubgraphNode node, Object object) {
+                                               // noop
+                                       }
+                               });
+
+               assertEquals(0, node.getSerializationCallbacks().size());
+               assertEquals(1, node.getDeserializationCallbacks().size());
+       }
+
+       public void testAddSerializationCallback() {
+               Subgraph<Table2> subgraph = new Subgraph<Table2>(Table2.class,
+                               newContext().getEntityResolver());
+               subgraph.addSerializeByReferencePath(Table2.TABLE1_PROPERTY);
+
+               SubgraphNode node = subgraph.getRootNode().getChild(
+                               Table2.TABLE1_PROPERTY, false);
+
+               assertEquals(0, node.getSerializationCallbacks().size());
+               assertEquals(0, node.getDeserializationCallbacks().size());
+
+               subgraph.addSerializationCallback(Table2.TABLE1_PROPERTY,
+                               new SerializationCallback() {
+
+                                       public Query 
relationshipQuery(SubgraphNode node,
+                                                       Object sourceObject) {
+                                               // TODO Auto-generated method 
stub
+                                               return null;
+                                       }
+                               });
+
+               assertEquals(1, node.getSerializationCallbacks().size());
+               assertEquals(0, node.getDeserializationCallbacks().size());
+       }
+
        public void testAddSerializeByReferencePathToOne() {
                Subgraph<Table2> subgraph = new Subgraph<Table2>(Table2.class,
                                newContext().getEntityResolver());

Modified: 
cayenne/sandbox/cayenne-serialization/src/test/java/org/apache/cayenne/serialization/xstream/XStreamDeserializerTest.java
URL: 
http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-serialization/src/test/java/org/apache/cayenne/serialization/xstream/XStreamDeserializerTest.java?rev=816205&r1=816204&r2=816205&view=diff
==============================================================================
--- 
cayenne/sandbox/cayenne-serialization/src/test/java/org/apache/cayenne/serialization/xstream/XStreamDeserializerTest.java
 (original)
+++ 
cayenne/sandbox/cayenne-serialization/src/test/java/org/apache/cayenne/serialization/xstream/XStreamDeserializerTest.java
 Thu Sep 17 14:13:19 2009
@@ -27,7 +27,9 @@
 import org.apache.cayenne.DataObjectUtils;
 import org.apache.cayenne.ObjectContext;
 import org.apache.cayenne.PersistenceState;
+import org.apache.cayenne.serialization.DeserializationCallback;
 import org.apache.cayenne.serialization.Subgraph;
+import org.apache.cayenne.serialization.SubgraphNode;
 import org.apache.cayenne.serialization.persistent.Table1;
 import org.apache.cayenne.serialization.persistent.Table2;
 import org.apache.cayenne.serialization.unit.SerializationCase;
@@ -182,4 +184,88 @@
                assertTrue(names.contains("t22"));
        }
 
+       public void testCallbackByReference() throws IOException {
+
+               ObjectContext context = newContext();
+               Table1 t11 = context.newObject(Table1.class);
+               t11.setName("t11");
+
+               Table2 t21 = context.newObject(Table2.class);
+               t21.setName("t21");
+               t21.setTable1(t11);
+
+               context.commitChanges();
+
+               final boolean[] callbackInvoked = new boolean[1];
+
+               Subgraph<Table2> subgraph = new Subgraph<Table2>(Table2.class, 
context
+                               .getEntityResolver());
+               subgraph.addSerializeByReferencePath(Table2.TABLE1_PROPERTY);
+               subgraph.addDeserializationCallback(Table2.TABLE1_PROPERTY,
+                               new DeserializationCallback() {
+                                       public void 
postDeserialize(SubgraphNode node, Object object) {
+                                               assertNotNull(node);
+                                               
assertEquals(Table2.TABLE1_PROPERTY, node
+                                                               
.getIncomingProperty().getName());
+                                               assertNotNull(object);
+                                               assertTrue(object instanceof 
Table1);
+
+                                               callbackInvoked[0] = true;
+                                       }
+                               });
+
+               XStreamDeserializer deserializer = new XStreamDeserializer();
+
+               int id = DataObjectUtils.intPKForObject(t11);
+               String xml = "<Table2><name>t21</name><table1><Table1 
ref=\"true\"><PK>"
+                               + id + "</PK></Table1></table1></Table2>";
+
+               InputStream in = new ByteArrayInputStream(xml.getBytes());
+               try {
+                       deserializer.deserialize(context, subgraph, in);
+               } finally {
+                       in.close();
+               }
+
+               assertTrue(callbackInvoked[0]);
+       }
+
+       public void testCallbackByValue() throws IOException {
+
+               ObjectContext context = newContext();
+
+               final boolean[] callbackInvoked = new boolean[1];
+
+               Subgraph<Table1> subgraph = new Subgraph<Table1>(Table1.class, 
context
+                               .getEntityResolver());
+               subgraph.addSerializeByValuePath(Table1.TABLE2S_PROPERTY);
+               subgraph.addDeserializationCallback(Table1.TABLE2S_PROPERTY,
+                               new DeserializationCallback() {
+                                       public void 
postDeserialize(SubgraphNode node, Object object) {
+                                               assertNotNull(node);
+                                               
assertEquals(Table1.TABLE2S_PROPERTY, node
+                                                               
.getIncomingProperty().getName());
+                                               assertNotNull(object);
+                                               assertTrue(object instanceof 
Table2);
+
+                                               callbackInvoked[0] = true;
+                                       }
+                               });
+
+               XStreamDeserializer deserializer = new XStreamDeserializer();
+
+               String xml = "<Table1><name>t11</name><table2s>"
+                               + "<Table2><name>t21</name></Table2>"
+                               + 
"<Table2><name>t22</name></Table2></table2s></Table1>";
+
+               InputStream in = new ByteArrayInputStream(xml.getBytes());
+               try {
+                       deserializer.deserialize(context, subgraph, in);
+               } finally {
+                       in.close();
+               }
+
+               assertTrue(callbackInvoked[0]);
+       }
+
 }

Modified: 
cayenne/sandbox/cayenne-serialization/src/test/java/org/apache/cayenne/serialization/xstream/XStreamSerializerTest.java
URL: 
http://svn.apache.org/viewvc/cayenne/sandbox/cayenne-serialization/src/test/java/org/apache/cayenne/serialization/xstream/XStreamSerializerTest.java?rev=816205&r1=816204&r2=816205&view=diff
==============================================================================
--- 
cayenne/sandbox/cayenne-serialization/src/test/java/org/apache/cayenne/serialization/xstream/XStreamSerializerTest.java
 (original)
+++ 
cayenne/sandbox/cayenne-serialization/src/test/java/org/apache/cayenne/serialization/xstream/XStreamSerializerTest.java
 Thu Sep 17 14:13:19 2009
@@ -18,13 +18,18 @@
  ****************************************************************/
 package org.apache.cayenne.serialization.xstream;
 
+import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.OutputStream;
 
 import org.apache.cayenne.DataObjectUtils;
 import org.apache.cayenne.ObjectContext;
+import org.apache.cayenne.query.Query;
+import org.apache.cayenne.serialization.SerializationCallback;
 import org.apache.cayenne.serialization.Subgraph;
+import org.apache.cayenne.serialization.SubgraphNode;
 import org.apache.cayenne.serialization.persistent.Table1;
 import org.apache.cayenne.serialization.persistent.Table2;
 import org.apache.cayenne.serialization.unit.SerializationCase;
@@ -175,9 +180,9 @@
 
                assertEquals("<Table1><name>t11</name></Table1>", 
Util.stringFromFile(
                                f1).trim());
-               
+
                subgraph.excludeAttribute(Table1.NAME_PROPERTY);
-               
+
                File f2 = tempFile(".xml");
 
                FileOutputStream out2 = new FileOutputStream(f2);
@@ -190,8 +195,48 @@
                assertTrue(f2.isFile());
                assertTrue(f2.length() > 0);
 
-               assertEquals("<Table1/>", Util.stringFromFile(
-                               f2).trim());
-               
+               assertEquals("<Table1/>", Util.stringFromFile(f2).trim());
+
        }
+
+       public void testCallbackByValueToMany() throws IOException {
+
+               ObjectContext context = newContext();
+               Table1 t11 = context.newObject(Table1.class);
+               t11.setName("t11");
+
+               Table2 t21 = context.newObject(Table2.class);
+               t21.setName("t21");
+               t21.setTable1(t11);
+
+               context.commitChanges();
+
+               final boolean[] callbackInvoked = new boolean[1];
+
+               Subgraph<Table1> subgraph = new Subgraph<Table1>(Table1.class, 
context
+                               .getEntityResolver());
+               subgraph.addSerializeByValuePath(Table1.TABLE2S_PROPERTY);
+               subgraph.addSerializationCallback(Table1.TABLE2S_PROPERTY,
+                               new SerializationCallback() {
+                                       public Query 
relationshipQuery(SubgraphNode node,
+                                                       Object sourceObject) {
+
+                                               callbackInvoked[0] = true;
+                                               return null;
+                                       }
+                               });
+
+               XStreamSerializer serializer = new XStreamSerializer();
+               serializer.setCreatingCompactXML(true);
+
+               OutputStream out = new ByteArrayOutputStream();
+               try {
+                       serializer.serialize(t11, subgraph, out);
+               } finally {
+                       out.close();
+               }
+
+               assertTrue(callbackInvoked[0]);
+       }
+
 }


Reply via email to