public class

BSONSerializerFactory

extends Object

/**
 *      Copyright (C) 2012 10gen Inc.
 *  
 *   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 com.mongodb.util;

import com.mongodb.*;
import java.lang.reflect.Array;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Map.Entry;
import java.util.regex.Pattern;
import org.bson.types.*;

public class BSONSerializerFactory {

    public BSONSerializerFactory() {
    }

    public static BSONObjectSerializer buildLegacyBSONSerializer() {
        
        BSONObjectSerializer serializer = new BSONObjectSerializer();
        
        serializer.addObjectSerializer(byte[].class,
                new BSONObjectSerializer.ObjectSerializer() {

            @Override
            public void serialize(Object obj,
                    BSONObjectSerializer serializer, StringBuilder buf) {
                buf.append("<Binary Data>");
            }

        });
        serializer.addObjectSerializer(Object[].class,
                new BSONObjectSerializer.ObjectSerializer() {

            @Override
            public void serialize(Object obj,
                    BSONObjectSerializer serializer, StringBuilder buf) {
                buf.append("[ ");
                for (int i = 0; i < Array.getLength(obj); i++) {
                    if (i > 0)
                        buf.append(" , ");
                    serializer.serialize(Array.get(obj, i), buf);
                }

                buf.append("]");
            }

        });

        serializer.addObjectSerializer(Binary.class,
                new BSONObjectSerializer.ObjectSerializer() {

            @Override
            public void serialize(Object obj,
                    BSONObjectSerializer serializer, StringBuilder buf) {
                buf.append("<Binary Data>");
            }

        });

        serializer.addObjectSerializer(Boolean.class,
                new BSONObjectSerializer.ObjectSerializer() {

            @Override
            public void serialize(Object obj,
                    BSONObjectSerializer serializer, StringBuilder buf) {
                buf.append(obj.toString());
            }

        });

        serializer.addObjectSerializer(BSONTimestamp.class,
                new BSONObjectSerializer.ObjectSerializer() {

            @Override
            public void serialize(Object obj,
                    BSONObjectSerializer serializer, StringBuilder buf) {
                BSONTimestamp t = (BSONTimestamp) obj;
                BasicDBObject temp = new BasicDBObject();
                temp.put("$ts", Integer.valueOf(t.getTime()));
                temp.put("$inc", Integer.valueOf(t.getInc()));
                serializer.serialize(temp, buf);
            }

        });

        serializer.addObjectSerializer(byte[].class,
                new BSONObjectSerializer.ObjectSerializer() {

            @Override
            public void serialize(Object obj,
                    BSONObjectSerializer serializer, StringBuilder buf) {
                buf.append("<Binary Data>");
            }

        });

        serializer.addObjectSerializer(Code.class,
                new BSONObjectSerializer.ObjectSerializer() {

            @Override
            public void serialize(Object obj,
                    BSONObjectSerializer serializer, StringBuilder buf) {
                Code c = (Code) obj;
                BasicDBObject temp = new BasicDBObject();
                temp.put("$code", c.getCode());
                serializer.serialize(temp, buf);
            }

        });

        serializer.addObjectSerializer(CodeWScope.class,
                new BSONObjectSerializer.ObjectSerializer() {

            @Override
            public void serialize(Object obj,
                    BSONObjectSerializer serializer, StringBuilder buf) {
                CodeWScope c = (CodeWScope) obj;
                BasicDBObject temp = new BasicDBObject();
                temp.put("$code", c.getCode());
                temp.put("$scope", c.getScope());
                serializer.serialize(temp, buf);
            }

        });

        serializer.addObjectSerializer(Date.class,
                new BSONObjectSerializer.ObjectSerializer() {

            @Override
            public void serialize(Object obj,
                    BSONObjectSerializer serializer, StringBuilder buf) {
                Date d = (Date) obj;
                SimpleDateFormat format = new SimpleDateFormat(
                        "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
                format.setCalendar(new GregorianCalendar(
                        new SimpleTimeZone(0, "GMT")));
                serializer.serialize(
                        new BasicDBObject("$date", format.format(d)),
                        buf);
            }

        });

        serializer.addObjectSerializer(DBObject.class,
                new BSONObjectSerializer.ObjectSerializer() {

            @Override
            public void serialize(Object obj,
                    BSONObjectSerializer serializer, StringBuilder buf) {
                boolean first = true;
                buf.append("{ ");
                DBObject dbo = (DBObject) obj;
                String name;
                
                Iterator<String> it = dbo.keySet().iterator();
                while (it.hasNext() ){
                    name = it.next();
                    
                    if (first)
                        first = false;
                    else
                        buf.append(" , ");
                    
                    JSON.string(buf, name);
                    buf.append(" : ");
                    serializer.serialize(dbo.get(name), buf);
                }

                buf.append("}");
            }

        });

        serializer.addObjectSerializer(DBRefBase.class,
                new BSONObjectSerializer.ObjectSerializer() {

            @Override
            public void serialize(Object obj,
                    BSONObjectSerializer serializer, StringBuilder buf) {
                DBRefBase ref = (DBRefBase) obj;
                BasicDBObject temp = new BasicDBObject();
                temp.put("$ref", ref.getRef());
                temp.put("$id", ref.getId());
                serializer.serialize(temp, buf);
            }

        });

        serializer.addObjectSerializer(Iterable.class,
                new BSONObjectSerializer.ObjectSerializer() {

            @Override
            public void serialize(Object obj,
                    BSONObjectSerializer serializer, StringBuilder buf) {
                boolean first = true;
                buf.append("[ ");
                
                Iterator it = ((Iterable) obj).iterator();
                while ( it.hasNext() ) {
                    if (first)
                        first = false;
                    else
                        buf.append(" , ");
                    
                    serializer.serialize(it.next(), buf);
                }
                buf.append("]");
            }
        });

        serializer.addObjectSerializer(Map.class,
                new BSONObjectSerializer.ObjectSerializer() {

            @Override
            public void serialize(Object obj,
                    BSONObjectSerializer serializer, StringBuilder buf) {
                boolean first = true;
                buf.append("{ ");
                Map m = (Map) obj;
                Entry entry;
                Iterator it = m.entrySet().iterator();
                
                while ( it.hasNext()) {
                    entry = (Entry) it.next();
                    if (first)
                        first = false;
                    else
                        buf.append(" , ");
                    JSON.string(buf, entry.getKey().toString());
                    buf.append(" : ");
                    serializer.serialize(entry.getValue(), buf);
                }

                buf.append("}");
            }

        });

        serializer.addObjectSerializer(MaxKey.class,
                new BSONObjectSerializer.ObjectSerializer() {

            @Override
            public void serialize(Object obj,
                    BSONObjectSerializer serializer, StringBuilder buf) {
                serializer.serialize(new BasicDBObject("$maxKey",
                        Integer.valueOf(1)), buf);
            }

        });
        serializer.addObjectSerializer(MinKey.class,
                new BSONObjectSerializer.ObjectSerializer() {

            @Override
            public void serialize(Object obj,
                    BSONObjectSerializer serializer, StringBuilder buf) {
                serializer.serialize(new BasicDBObject("$minKey",
                        Integer.valueOf(1)), buf);
            }

        });

        serializer.addObjectSerializer(Number.class,
                new BSONObjectSerializer.ObjectSerializer() {

            @Override
            public void serialize(Object obj,
                    BSONObjectSerializer serializer, StringBuilder buf) {
                buf.append(obj.toString());
            }

        });

        serializer.addObjectSerializer(ObjectId.class,
                new BSONObjectSerializer.ObjectSerializer() {

            @Override
            public void serialize(Object obj,
                    BSONObjectSerializer serializer, StringBuilder buf) {
                serializer.serialize(
                        new BasicDBObject("$oid", obj.toString()), buf);
            }
        });
        
        serializer.addObjectSerializer(Pattern.class,
                new BSONObjectSerializer.ObjectSerializer() {

            @Override
            public void serialize(Object obj,
                    BSONObjectSerializer serializer, StringBuilder buf) {
                DBObject externalForm = new BasicDBObject();
                externalForm.put("$regex", obj.toString());
                if (((Pattern) obj).flags() != 0)
                    externalForm.put("$options",
                            Bytes.regexFlags(((Pattern) obj).flags()));
                serializer.serialize(externalForm, buf);
            }
        });

        serializer.addObjectSerializer(String.class,
                new BSONObjectSerializer.ObjectSerializer() {

            @Override
            public void serialize(Object obj,
                    BSONObjectSerializer serializer, StringBuilder buf) {
                JSON.string(buf, (String) obj);
            }
        });

        serializer.addObjectSerializer(UUID.class,
                new BSONObjectSerializer.ObjectSerializer() {

            @Override
            public void serialize(Object obj,
                    BSONObjectSerializer serializer, StringBuilder buf) {
                UUID uuid = (UUID) obj;
                BasicDBObject temp = new BasicDBObject();
                temp.put("$uuid", uuid.toString());
                serializer.serialize(temp, buf);
            }
        });
        return serializer;
    }

    public static BSONObjectSerializer buildStrictBSONSerializer() {
        
        BSONObjectSerializer serializer = buildLegacyBSONSerializer();
        
        serializer.addObjectSerializer(Binary.class,
                new BSONObjectSerializer.ObjectSerializer() {

            @Override
            public void serialize(Object obj,
                    BSONObjectSerializer serializer, StringBuilder buf) {
                Binary bin = (Binary) obj;
                DBObject temp = new BasicDBObject();
                temp.put("$binary",
                        (new Base64Codec()).encode(bin.getData()));
                temp.put("$type", Byte.valueOf(bin.getType()));
                serializer.serialize(temp, buf);
            }

        });

        serializer.addObjectSerializer(BSONTimestamp.class,
                new BSONObjectSerializer.ObjectSerializer() {

            @Override
            public void serialize(Object obj,
                    BSONObjectSerializer serializer, StringBuilder buf) {
                BSONTimestamp t = (BSONTimestamp) obj;
                BasicDBObject temp = new BasicDBObject();
                temp.put("$t", Integer.valueOf(t.getTime()));
                temp.put("$i", Integer.valueOf(t.getInc()));
                BasicDBObject timestampObj = new BasicDBObject();
                timestampObj.put("$timestamp", temp);
                serializer.serialize(timestampObj, buf);
            }

        });

        serializer.addObjectSerializer(byte[].class,
                new BSONObjectSerializer.ObjectSerializer() {

            @Override
            public void serialize(Object obj,
                    BSONObjectSerializer serializer, StringBuilder buf) {
                DBObject temp = new BasicDBObject();
                temp.put("$binary", (new Base64Codec()).encode((byte[]) obj));
                temp.put("$type", Byte.valueOf((byte) 0));
                serializer.serialize(temp, buf);
            }
        });

        serializer.addObjectSerializer(Date.class,
                new BSONObjectSerializer.ObjectSerializer() {

            @Override
            public void serialize(Object obj,
                    BSONObjectSerializer serializer, StringBuilder buf) {
                Date d = (Date) obj;
                serializer.serialize(
                        new BasicDBObject("$date", Long.valueOf(d.getTime() / 1000L)), buf);
            }

        });

        return serializer;
    }
}