Document encodeUpdate()

in grails-data-mongodb/core/src/main/groovy/org/grails/datastore/mapping/mongo/engine/codecs/PersistentEntityCodec.groovy [202:359]


    Document encodeUpdate(Object value, EntityAccess access = createEntityAccess(value), EncoderContext encoderContext = DEFAULT_ENCODER_CONTEXT, boolean embedded = false) {
        Document update = new Document()
        def entity = access.persistentEntity

        def proxyFactory = mappingContext.proxyFactory
        if( proxyFactory.isProxy(value) ) {
            value = proxyFactory.unwrap(value)
        }
        if(value instanceof DirtyCheckable) {
            def sets = new BsonDocument()
            def unsets = new Document()
            BsonWriter writer = new BsonDocumentWriter(sets)
            writer.writeStartDocument()
            DirtyCheckable dirty = (DirtyCheckable)value
            Set<String> processed = []

            def dirtyProperties = new ArrayList<String>(dirty.listDirtyPropertyNames())
            boolean isNew = dirtyProperties.isEmpty() && dirty.hasChanged()
            def isVersioned = entity.isVersioned()
            if(isNew) {
                // if it is new it can only be an embedded entity that has now been updated
                // so we get all properties
                dirtyProperties = entity.persistentPropertyNames
                if(!entity.isRoot()) {
                    sets.put(MongoConstants.MONGO_CLASS_FIELD, new BsonString(entity.discriminator))
                }

                if(isVersioned) {
                    EntityPersister.incrementEntityVersion(access)
                }

            }
            else {
                // schedule lastUpdated if necessary
                if( entity.getPropertyByName(GormProperties.LAST_UPDATED) != null) {
                    dirtyProperties.add(GormProperties.LAST_UPDATED)
                }
            }


            for(propertyName in dirtyProperties) {
                def prop = entity.getPropertyByName(propertyName)
                if(prop != null) {

                    processed << propertyName
                    Object v = access.getProperty(prop.name)
                    if (v != null) {
                        if(prop instanceof Embedded) {
                            encodeEmbeddedUpdate(sets,unsets, (Association)prop, v)
                        }
                        else if(prop instanceof EmbeddedCollection) {
                            encodeEmbeddedCollectionUpdate(access, sets, unsets, (Association)prop, v)
                        }
                        else {
                            def propKind = prop.getClass().superclass
                            PropertyEncoder<? extends PersistentProperty> propertyEncoder = getPropertyEncoder((Class<? extends PersistentProperty>)propKind)
                            propertyEncoder?.encode(writer, prop, v, access, encoderContext, codecRegistry)
                        }

                    }
                    else if(embedded || !isNew) {
                        unsets[prop.name] = BLANK_STRING
                    }
                }
            }

            if(value instanceof DynamicAttributes) {
                Map<String, Object> attributes = ((DynamicAttributes) value).attributes()
                for(attr in attributes.keySet()) {
                    Object v = attributes.get(attr)
                    if(v == null) {
                        unsets.put(attr,BLANK_STRING)
                    }
                    else {
                        writer.writeName(attr)
                        Codec<Object> codec = (Codec<Object>)codecRegistry.get(v.getClass())
                        codec.encode(writer, v, encoderContext)
                    }
                }
            }
            else {

                GormEnhancer.findStaticApi(entity.javaClass).withSession { Session mongoSession ->
                    if(mongoSession != null) {
                        Document schemaless = (Document)mongoSession.getAttribute(value, SCHEMALESS_ATTRIBUTES)
                        if(schemaless != null) {
                            for(name in schemaless.keySet()) {
                                def v = schemaless.get(name)
                                if(v == null) {
                                    unsets.put(name,BLANK_STRING)
                                }
                                else {
                                    writer.writeName(name)
                                    Codec<Object> codec = (Codec<Object>)codecRegistry.get(v.getClass())
                                    codec.encode(writer, v, encoderContext)
                                }
                            }
                        }
                    }
                }

            }

            for(association in entity.associations) {
                if(processed.contains( association.name )) continue
                if(association instanceof OneToMany) {
                    def v = access.getProperty(association.name)
                    if (v != null) {
                        // TODO: handle unprocessed association
                    }
                }
                else if(association instanceof ToOne) {
                    def v = access.getProperty(association.name)
                    if( v instanceof DirtyCheckable ) {
                        if(((DirtyCheckable)v).hasChanged()) {
                            if(association instanceof Embedded) {
                                encodeEmbeddedUpdate(sets, unsets, association, v)
                            }
                        }
                    }
                }
                else if(association instanceof EmbeddedCollection) {
                    def v = access.getProperty(association.name)
                    if( v instanceof DirtyCheckableCollection ) {
                        if(((DirtyCheckableCollection)v).hasChanged()) {
                            encodeEmbeddedCollectionUpdate(access, sets, unsets, association, v)
                        }
                    }
                }
            }


            boolean hasSets = !sets.isEmpty()
            boolean hasUnsets = !unsets.isEmpty()

            if(hasSets && isVersioned) {
                def version = entity.version
                def propKind = version.getClass().superclass
                MongoCodecEntityPersister.incrementEntityVersion(access)
                def v = access.getProperty(version.name)
                getPropertyEncoder((Class<? extends PersistentProperty>) propKind)?.encode(writer, version, v, access, encoderContext, codecRegistry)
            }

            writer.writeEndDocument()

            if(hasSets) {
                update.put(MONGO_SET_OPERATOR, sets)
            }
            if(hasUnsets) {
                update.put(MONGO_UNSET_OPERATOR,unsets)
            }
        }
        else {
            // TODO: Support non-dirty checkable objects?
        }

        return update
    }