Fwd: How to copy properties of a domain class?

Next Topic
 
classic Classic list List threaded Threaded
6 messages Options
Reply | Threaded
Open this post in threaded view
|

Fwd: How to copy properties of a domain class?

Daniel Henrique Alves Lima
BootStrap.groovy in the attached project. For string properties, it seems work.

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email

test_props.zip (238K) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: Fwd: How to copy properties of a domain class?

mschneider
Hi Daniel,

thank you very much for your help. Works very well - perfect!
The solution was so easy & clear, that I couldn't see it

Best Regards,

-markus

Reply | Threaded
Open this post in threaded view
|

Re: Fwd: How to copy properties of a domain class?

john robens
Only works for basic stuff. I found this and started hacking it.  Trying to clone as part of a test. Clone, delete, compare etc. 

My test still has a few problems - Probably going to wrap in transactions. Either way the code below does pass all but one of the tests I wrote.

null
java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.nextEntry(HashMap.java:793)
at java.util.HashMap$KeyIterator.next(HashMap.java:828)
at org.hibernate.collection.AbstractPersistentCollection$IteratorProxy.next(AbstractPersistentCollection.java:577)
at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:1220)
at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:1196)
at org.codehaus.groovy.runtime.dgm$110.invoke(Unknown Source)
at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoMetaMethodSiteNoUnwrapNoCoerce.invoke(PojoMetaMethodSite.java:270)
at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:52)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:124)
at au.com.interlated.emissionscalculator.EmissionsParentImpl$_deepClone_closure1.doCall(EmissionsParentImpl.groovy:37)
at sun.reflect.GeneratedMethodAccessor943.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:90)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:233)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1058)
at groovy.lang.ExpandoMetaClass.invokeMethod(ExpandoMetaClass.java:1070)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:886)
at groovy.lang.Closure.call(Closure.java:282)
at groovy.lang.Closure.call(Closure.java:295)



public abstract class EmissionsParentImpl implements EmissionsParent {
    /*
 * Clones a domain object and recursively clones children, clearing ids and
 * attaching children to their new parents. Ownership relationships (indicated
 * by GORM belongsTo keyword) cause "copy as new" (a recursive deep clone),
 * but associations without ownership are shallow copied by reference.
 */

    public Object deepClone(domainInstanceToClone) {

        //Our target instance for the instance we want to clone
        // recursion
        def newDomainInstance = domainInstanceToClone.getClass().newInstance()

        //Returns a DefaultGrailsDomainClass (as interface GrailsDomainClass) for inspecting properties
        def domainClass = ApplicationHolder.application.getDomainClass(newDomainInstance.getClass().name)

        domainClass?.persistentProperties?.each {prop ->
            if (prop.association) {
                if (prop.owningSide) {
                    //we have to deep clone owned associations
                    if (prop.oneToOne) {
                        def newAssociationInstance = deepClone(domainInstanceToClone?."${prop.name}")
                        newDomainInstance."${prop.name}" = newAssociationInstance
                    }
                    else {
                        domainInstanceToClone."${prop.name}".each { associationInstance ->
                            def newAssociationInstance = deepClone(associationInstance)
                            newDomainInstance."addTo${StringUtils.capitalize(prop.name)}"(newAssociationInstance)
                        }
                    }
                }
                else {
                    if (!prop.bidirectional) {
                        //If the association isn't owned or the owner, then we can just do a  shallow copy of the reference.
                        newDomainInstance."${prop.name}" = domainInstanceToClone."${prop.name}"
                    }
                    // @@JR
                    // Yes bidirectional and not owning. E.g. clone Report, belongsTo Organisation which hasMany
                    // manyToOne. Just add to the owning objects collection.
                    else {
                       if (prop.manyToOne) {
                           newDomainInstance."${prop.name}" = domainInstanceToClone."${prop.name}"
                           def owningInstance = domainInstanceToClone."${prop.name}"
                           // Need to find the collection.
                           String otherSide = StringUtils.capitalize(prop.otherSide.name)
                           owningInstance."addTo${otherSide}"(newDomainInstance)
                       }
                    }
                }
            }
            else {
                //If the property isn't an association then simply copy the value
                newDomainInstance."${prop.name}" = domainInstanceToClone."${prop.name}"
            }
        }

        return newDomainInstance
    }

    /**
     * Copy the object for testing purposes.
     * @return
     */
}


On Sat, Apr 9, 2011 at 5:51 AM, mschneider <[hidden email]> wrote:
Hi Daniel,

thank you very much for your help. Works very well - perfect!
The solution was so easy & clear, that I couldn't see it

Best Regards,

-markus



--
View this message in context: http://grails.1312388.n4.nabble.com/Fwd-How-to-copy-properties-of-a-domain-class-tp3436759p3437304.html
Sent from the Grails - user mailing list archive at Nabble.com.

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

   http://xircles.codehaus.org/manage_email





--
0434 996 607
Websites with Intelligence

Reply | Threaded
Open this post in threaded view
|

Re: Fwd: How to copy properties of a domain class?

Daniel Henrique Alves Lima
    Hi, John.

    I can't see the line numbers in the provided snippet, so it's hard
to tell you exactly what is triggering the error, but this exception is
related to concurrent modification of a collection (List, Set, Map, ...).
    For example:


    def x = [a: 1, b: 2]
    for (key in x.keySet()) {
         x.c = 10
   }


    Will trigger java.util.ConcurrentModificationException.

    Best regards,

          Daniel.


John Robens wrote:

> Only works for basic stuff. I found this and started hacking it.
>  Trying to clone as part of a test. Clone, delete, compare etc.
>
> My test still has a few problems - Probably going to wrap in
> transactions. Either way the code below does pass all but one of the
> tests I wrote.
>
> null
> java.util.ConcurrentModificationException
> at java.util.HashMap$HashIterator.nextEntry(HashMap.java:793)
> at java.util.HashMap$KeyIterator.next(HashMap.java:828)
> at
> org.hibernate.collection.AbstractPersistentCollection$IteratorProxy.next(AbstractPersistentCollection.java:577)
> at
> org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:1220)
> at
> org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:1196)
> at org.codehaus.groovy.runtime.dgm$110.invoke(Unknown Source)
> at
> org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoMetaMethodSiteNoUnwrapNoCoerce.invoke(PojoMetaMethodSite.java:270)
> at
> org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:52)
> at
> org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:124)
> at
> au.com.interlated.emissionscalculator.EmissionsParentImpl$_deepClone_closure1.doCall(EmissionsParentImpl.groovy:37)
> at sun.reflect.GeneratedMethodAccessor943.invoke(Unknown Source)
> at
> sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
> at java.lang.reflect.Method.invoke(Method.java:597)
> at
> org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:90)
> at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:233)
> at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1058)
> at groovy.lang.ExpandoMetaClass.invokeMethod(ExpandoMetaClass.java:1070)
> at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:886)
> at groovy.lang.Closure.call(Closure.java:282)
> at groovy.lang.Closure.call(Closure.java:295)
>
>
>
> public abstract class EmissionsParentImpl implements EmissionsParent {
>     // http://studio945.com/djdaugherty/?p=386
>     /*
>  * Clones a domain object and recursively clones children, clearing
> ids and
>  * attaching children to their new parents. Ownership relationships
> (indicated
>  * by GORM belongsTo keyword) cause "copy as new" (a recursive deep
> clone),
>  * but associations without ownership are shallow copied by reference.
>  */
>
>     public Object deepClone(domainInstanceToClone) {
>
>         //Our target instance for the instance we want to clone
>         // recursion
>         def newDomainInstance =
> domainInstanceToClone.getClass().newInstance()
>
>         //Returns a DefaultGrailsDomainClass (as interface
> GrailsDomainClass) for inspecting properties
>         def domainClass =
> ApplicationHolder.application.getDomainClass(newDomainInstance.getClass().name)
>
>         domainClass?.persistentProperties?.each {prop ->
>             if (prop.association) {
>                 if (prop.owningSide) {
>                     //we have to deep clone owned associations
>                     if (prop.oneToOne) {
>                         def newAssociationInstance =
> deepClone(domainInstanceToClone?."${prop.name <http://prop.name>}")
>                         newDomainInstance."${prop.name
> <http://prop.name>}" = newAssociationInstance
>                     }
>                     else {
>                         domainInstanceToClone."${prop.name
> <http://prop.name>}".each { associationInstance ->
>                             def newAssociationInstance =
> deepClone(associationInstance)
>                            
>  newDomainInstance."addTo${StringUtils.capitalize(prop.name
> <http://prop.name>)}"(newAssociationInstance)
>                         }
>                     }
>                 }
>                 else {
>                     if (!prop.bidirectional) {
>                         //If the association isn't owned or the owner,
> then we can just do a  shallow copy of the reference.
>                         newDomainInstance."${prop.name
> <http://prop.name>}" = domainInstanceToClone."${prop.name
> <http://prop.name>}"
>                     }
>                     // @@JR
>                     // Yes bidirectional and not owning. E.g. clone
> Report, belongsTo Organisation which hasMany
>                     // manyToOne. Just add to the owning objects
> collection.
>                     else {
>                        if (prop.manyToOne) {
>                            newDomainInstance."${prop.name
> <http://prop.name>}" = domainInstanceToClone."${prop.name
> <http://prop.name>}"
>                            def owningInstance =
> domainInstanceToClone."${prop.name <http://prop.name>}"
>                            // Need to find the collection.
>                            String otherSide =
> StringUtils.capitalize(prop.otherSide.name <http://prop.otherSide.name>)
>                            
> owningInstance."addTo${otherSide}"(newDomainInstance)
>                        }
>                     }
>                 }
>             }
>             else {
>                 //If the property isn't an association then simply
> copy the value
>                 newDomainInstance."${prop.name <http://prop.name>}" =
> domainInstanceToClone."${prop.name <http://prop.name>}"
>             }
>         }
>
>         return newDomainInstance
>     }
>
>     /**
>      * Copy the object for testing purposes.
>      * @return
>      */
> }
>

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email


Reply | Threaded
Open this post in threaded view
|

Re: Fwd: How to copy properties of a domain class?

Daniel Henrique Alves Lima
    In this context, i mean: You're changing the collection inside an
iterator loop.

Daniel Henrique Alves Lima wrote:

>    Hi, John.
>
>    I can't see the line numbers in the provided snippet, so it's hard
> to tell you exactly what is triggering the error, but this exception
> is related to concurrent modification of a collection (List, Set, Map,
> ...).
>    For example:
>
>
>    def x = [a: 1, b: 2]
>    for (key in x.keySet()) {
>         x.c = 10
>   }
>
>
>    Will trigger java.util.ConcurrentModificationException.
>
>    Best regards,
>
>          Daniel.
>
>
> John Robens wrote:
>> Only works for basic stuff. I found this and started hacking it.
>>  Trying to clone as part of a test. Clone, delete, compare etc.
>> My test still has a few problems - Probably going to wrap in
>> transactions. Either way the code below does pass all but one of the
>> tests I wrote.
>>
>> null
>> java.util.ConcurrentModificationException
>> at java.util.HashMap$HashIterator.nextEntry(HashMap.java:793)
>> at java.util.HashMap$KeyIterator.next(HashMap.java:828)
>

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email


Reply | Threaded
Open this post in threaded view
|

Re: Fwd: How to copy properties of a domain class?

mmckenzie
In reply to this post by john robens
You can fix the ConcurrentModificationException by adding ".collect()".

Instead of this:

domainInstanceToClone."${prop.name}".each { associationInstance ->

Use this:  

domainInstanceToClone."${ prop.name }".collect().each { associationInstance ->

Let me know if that works.