About Me

Mein Bild
Freelancing software developer / architect, mobile geek. Topics: Software technologies (java, groovy, agile), politics, web, science, green, mobile, android.
Homepage: www.tutaona.com

"The absence of limitations is the enemy of art." - Orson Welles

Mittwoch, 11. Mai 2011

Cloning Objects in Groovy

Cloning objects is very often useful. groovy offers excellent tools for doing this by its meta programming features.

MOP the properties 1
class User {
def String name
def Boolean id
// the list carry on .. 
def User createNewInstance(){
User.metaClass.getProperties().findAll(){it.getSetter()!=null}.inject(new User()) { user,metaProp->
metaProp.setProperty(user,metaProp.getProperty(this))
user
}
}
}

Copying JSONObject to a bean
/**
 * Copies the properties of a JSONObject to a bean.
 *
 * @param source the JSONObject
 * @param target the target bean
 * @param ignoreProperties a list of property names to be ignored
 * @return the fully qualified class name of the originally marshalled class
 */
def copyJSONObjectPropertiesToBean(JSONObject source, target, ignoreProperties) {
ignoreProperties.add "class"
target.metaClass.properties.each {
if (!ignoreProperties.contains(it.name) && source.containsKey(it.name)) {
it.setProperty(target, source[it.name])
}
}
source.class
}
Grails simple copy

/**
 * Creates a new instance of class based on it's fully qualified class name and sets it's properties.
 * The instantiation works only when instantiating a grails domain class
 *
 * @param clazzName the fully qualified class name
 * @param properties an object with the properties to copy
 * @return an instance of the specified class
 */
def newDomainClassInstance(properties) {
def clazzName = properties.remove("class")
if(!clazzName) {
throw new IllegalArgumentException("attribute 'class' is missing")
}
def clazz = grailsApplication.getDomainClass(clazzName).getClazz()
def instance = clazz.newInstance(properties)
return instance
}


/*
 * 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
    }


Keine Kommentare:

Kommentar veröffentlichen