/*
 * Copyright (C) 2011 Benoit GUEROUT <bguerout at gmail dot com> and Yves AMSELLEM <amsellem dot yves at gmail dot com>
 *
 * Licensed 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.jongo;

import com.mongodb.*;
import org.bson.LazyBSONCallback;
import org.bson.types.ObjectId;
import org.jongo.bson.BsonDocument;
import org.jongo.marshall.Marshaller;

import java.util.HashSet;
import java.util.Set;

class Save {

    private final Marshaller marshaller;
    private final DBCollection collection;
    private final ObjectIdUpdater objectIdUpdater;
    private final Object pojo;
    private WriteConcern concern;

    Save(DBCollection collection, Marshaller marshaller, ObjectIdUpdater objectIdUpdater, Object pojo) {
        this.marshaller = marshaller;
        this.collection = collection;
        this.objectIdUpdater = objectIdUpdater;
        this.pojo = pojo;
    }

    public Save concern(WriteConcern concern) {
        this.concern = concern;
        return this;
    }

    public WriteResult execute() {
        DBObject dbObject;
        if (objectIdUpdater.canSetObjectId(pojo)) {
            dbObject = createDBObjectToInsert();
        } else {
            dbObject = createDBObjectToUpdate();
        }

        return collection.save(dbObject, determineWriteConcern());
    }

    private DBObject createDBObjectToUpdate() {
        BsonDocument document = marshallDocument();
        return new UncheckableDBObject(document.toByteArray());
    }

    private DBObject createDBObjectToInsert() {
        ObjectId id = ObjectId.get();
        objectIdUpdater.setDocumentGeneratedId(pojo, id);

        BsonDocument document = marshallDocument();
        DBObject dbo = new UncheckableDBObject(document.toByteArray());
        dbo.put("_id", id);

        return dbo;
    }

    private BsonDocument marshallDocument() {
        try {
            return marshaller.marshall(pojo);
        } catch (Exception e) {
            String message = String.format("Unable to save object %s due to a marshalling error", pojo);
            throw new IllegalArgumentException(message, e);
        }
    }

    private WriteConcern determineWriteConcern() {
        return concern == null ? collection.getWriteConcern() : concern;
    }

    private static class UncheckableDBObject extends LazyWriteableDBObject {

        private final HashSet<String> keys;

        private UncheckableDBObject(byte[] data) {
            super(data, new LazyBSONCallback());
            this.keys = new HashSet<String>();
        }

        @Override
        public Set<String> keySet() {
            return keys;
        }
    }
}

@@ -1925,35 +1925,38 @@
  return new 
-Uncheckable
+AlreadyChecked
 DBObject(doc
@@ -2208,27 +2208,30 @@
 o = new 
-Uncheckable
+AlreadyChecked
 DBObject
@@ -2788,27 +2788,30 @@
 c class 
-Uncheckable
+AlreadyChecked
 DBObject
@@ -2909,19 +2909,22 @@
 ate 
-Uncheckable
+AlreadyChecked
 DBOb

Rename internal fake DBObject