com.zenesis.qx.remote.ProxyObjectMapper.java Source code

Java tutorial

Introduction

Here is the source code for com.zenesis.qx.remote.ProxyObjectMapper.java

Source

/**
 * ************************************************************************
 * 
 *    server-objects - a contrib to the Qooxdoo project that makes server 
 *    and client objects operate seamlessly; like Qooxdoo, server objects 
 *    have properties, events, and methods all of which can be access from
 *    either server or client, regardless of where the original object was
 *    created.
 * 
 *    http://qooxdoo.org
 * 
 *    Copyright:
 *      2010 Zenesis Limited, http://www.zenesis.com
 * 
 *    License:
 *      LGPL: http://www.gnu.org/licenses/lgpl.html
 *      EPL: http://www.eclipse.org/org/documents/epl-v10.php
 *      
 *      This software is provided under the same licensing terms as Qooxdoo,
 *      please see the LICENSE file in the Qooxdoo project's top-level directory 
 *      for details.
 * 
 *    Authors:
 *      * John Spackman (john.spackman@zenesis.com)
 * 
 * ************************************************************************
 */
package com.zenesis.qx.remote;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;

import org.apache.log4j.Logger;

import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.core.io.CharTypes;
import com.fasterxml.jackson.core.json.JsonWriteContext;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;

/**
 * Simple wrapper for Jackson ObjectMapper that uses our custom de/serialisation factories and adds a few helper
 * methods.
 * 
 * @author <a href="mailto:john.spackman@zenesis.com">John Spackman</a>
 */
@SuppressWarnings("serial")
public class ProxyObjectMapper extends ObjectMapper {

    @SuppressWarnings("unused")
    private static final Logger log = Logger.getLogger(ProxyObjectMapper.class);

    private static final SimpleDateFormat DF = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");

    /*
     * Serialises a Date as the JS equivelant
     */
    private static final class DateSerializer extends JsonSerializer<Date> {

        /* (non-Javadoc)
         * @see com.fasterxml.jackson.databind.JsonSerializer#serialize(java.lang.Object, com.fasterxml.jackson.core.JsonGenerator, com.fasterxml.jackson.databind.SerializerProvider)
         */
        @Override
        public void serialize(Date value, JsonGenerator gen, SerializerProvider sp)
                throws IOException, JsonProcessingException {
            if (value == null)
                gen.writeNull();
            else
                gen.writeRawValue("new Date(\"" + DF.format(value) + "\")");
        }
    }

    /*
     * Serialises a string, encoding unicode characters
     */
    private static final class StringSerializer extends JsonSerializer<String> {

        final char[] HEX_CHARS = "0123456789ABCDEF".toCharArray();
        final int[] ESCAPE_CODES = CharTypes.get7BitOutputEscapes();

        /* (non-Javadoc)
         * @see com.fasterxml.jackson.databind.JsonSerializer#serialize(java.lang.Object, com.fasterxml.jackson.core.JsonGenerator, com.fasterxml.jackson.databind.SerializerProvider)
         */
        @Override
        public void serialize(String value, JsonGenerator gen, SerializerProvider sp)
                throws IOException, JsonProcessingException {
            int status = ((JsonWriteContext) gen.getOutputContext()).writeValue();
            switch (status) {
            case JsonWriteContext.STATUS_OK_AFTER_COLON:
                gen.writeRaw(':');
                break;

            case JsonWriteContext.STATUS_OK_AFTER_COMMA:
                gen.writeRaw(',');
                break;

            case JsonWriteContext.STATUS_EXPECT_NAME:
                throw new JsonGenerationException("Can not write string value here");
            }
            gen.writeRaw('"');
            for (char c : value.toCharArray()) {
                if (c >= 0x80)
                    writeUnicodeEscape(gen, c); // use generic escaping for all
                // non US-ASCII characters
                else {
                    // use escape table for first 128 characters
                    int code = (c < ESCAPE_CODES.length ? ESCAPE_CODES[c] : 0);
                    if (code == 0)
                        gen.writeRaw(c); // no escaping
                    else if (code < 0)
                        writeUnicodeEscape(gen, (char) (-code - 1)); // generic escaping
                    else
                        writeShortEscape(gen, (char) code); // short escaping (\n \t ...)
                }
            }
            gen.writeRaw('"');
        }

        private void writeUnicodeEscape(JsonGenerator gen, char c) throws IOException {
            gen.writeRaw('\\');
            gen.writeRaw('u');
            gen.writeRaw(HEX_CHARS[(c >> 12) & 0xF]);
            gen.writeRaw(HEX_CHARS[(c >> 8) & 0xF]);
            gen.writeRaw(HEX_CHARS[(c >> 4) & 0xF]);
            gen.writeRaw(HEX_CHARS[c & 0xF]);
        }

        private void writeShortEscape(JsonGenerator gen, char c) throws IOException {
            gen.writeRaw('\\');
            gen.writeRaw(c);
        }
    }

    /*
     * Serialises enums in camelCase
     */
    private static final class EnumSerializer extends JsonSerializer<Enum> {

        /* (non-Javadoc)
         * @see org.codehaus.jackson.map.JsonSerializer#serialize(java.lang.Object, org.codehaus.jackson.JsonGenerator, org.codehaus.jackson.map.SerializerProvider)
         */
        @Override
        public void serialize(Enum value, JsonGenerator jgen, SerializerProvider provider)
                throws IOException, JsonProcessingException {
            if (value == null)
                jgen.writeNull();
            else
                jgen.writeString(Helpers.enumToCamelCase(value));
        }

    };

    /*   
       private static final class EnumDeserializer extends JsonDeserializer<Enum> {
        
          @Override
          public Enum deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
     String value = jp.getText();
     if (value == null || value.length() == 0)
        return null;
     return null;
          }
              
       }
    */

    /*
     * Serialises Maps
     */
    private static final class MapSerializer extends JsonSerializer<Map> {

        /* (non-Javadoc)
         * @see org.codehaus.jackson.map.JsonSerializer#serialize(java.lang.Object, org.codehaus.jackson.JsonGenerator, org.codehaus.jackson.map.SerializerProvider)
         */
        @Override
        public void serialize(Map map, JsonGenerator jgen, SerializerProvider provider)
                throws IOException, JsonProcessingException {
            if (map == null)
                jgen.writeNull();
            else {
                jgen.writeStartObject();
                for (Object key : map.keySet()) {
                    if (key == null)
                        continue;
                    if (key instanceof Enum)
                        jgen.writeFieldName(Helpers.enumToCamelCase((Enum) key));
                    else
                        jgen.writeFieldName(key.toString());
                    Object value = map.get(key);
                    if (value == null)
                        jgen.writeNull();
                    else
                        jgen.writeObject(value);
                }
                jgen.writeEndObject();
            }
        }

    };

    /*
     * Serialises a file, but only showing the part of the path relative to the "root" dir of
     * the web application
     */
    public static final class FileSerializer extends JsonSerializer<File> {

        private final String localPrefix;

        public FileSerializer(File localDir) {
            this.localPrefix = localDir == null ? null : localDir.getAbsolutePath() + File.separatorChar;
        }

        /* (non-Javadoc)
         * @see com.fasterxml.jackson.databind.JsonSerializer#serialize(java.lang.Object, com.fasterxml.jackson.core.JsonGenerator, com.fasterxml.jackson.databind.SerializerProvider)
         */
        @Override
        public void serialize(File value, JsonGenerator jgen, SerializerProvider provider)
                throws IOException, JsonProcessingException {
            if (value == null)
                jgen.writeNull();
            else {
                String str = value.getAbsolutePath();
                if (localPrefix == null)
                    jgen.writeString(str);
                else {
                    int len = localPrefix.length();
                    if (len < str.length() && str.substring(0, len).equalsIgnoreCase(localPrefix))
                        jgen.writeString(str.substring(len));
                    else
                        jgen.writeString(str);
                }
            }
        }
    }

    /*
     * Deserialises a file, keeping it relative to the root of the web application
     */
    public static final class FileDeserializer extends JsonDeserializer<File> {

        private final File localDir;

        public FileDeserializer(File localDir) {
            this.localDir = localDir;
        }

        /* (non-Javadoc)
         * @see com.fasterxml.jackson.databind.JsonDeserializer#deserialize(com.fasterxml.jackson.core.JsonParser, com.fasterxml.jackson.databind.DeserializationContext)
         */
        @Override
        public File deserialize(JsonParser jp, DeserializationContext ctxt)
                throws IOException, JsonProcessingException {
            String value = jp.getText();
            if (value == null || value.length() == 0)
                return null;
            if (localDir == null)
                return new File(value);
            if (File.separatorChar == '/' && value.charAt(0) == '/')
                return new File(value);
            if (File.separatorChar == '\\' && value.length() > 1 && value.charAt(1) == ':')
                return new File(value);
            File file = new File(localDir, value);
            return file;
        }

    }

    /** the tracker */
    private final ProxySessionTracker tracker;

    /**
     * Constructor
     * @param tracker
     */
    public ProxyObjectMapper(ProxySessionTracker tracker) {
        this(tracker, true, new File("."));
    }

    /**
     * Constructor
     * @param tracker
     * @param indent whether to indent JSON
     */
    public ProxyObjectMapper(ProxySessionTracker tracker, boolean indent) {
        this(tracker, indent, new File("."));
    }

    /**
     * Constructor
     * @param tracker
     * @param indent whether to indent JSON
     * @param rootDir root directory to serialise all File's as relative to
     */
    public ProxyObjectMapper(ProxySessionTracker tracker, boolean indent, File rootDir) {
        super();
        this.tracker = tracker;
        SimpleModule module = new SimpleModule("ProxyObjectMapper", Version.unknownVersion());
        module.addSerializer(Proxied.class, new ProxiedSerializer());
        module.addDeserializer(Proxied.class, new ProxiedDeserializer());
        module.addSerializer(Date.class, new DateSerializer());
        module.addSerializer(String.class, new StringSerializer());
        module.addSerializer(Enum.class, new EnumSerializer());
        module.addSerializer(File.class, new FileSerializer(rootDir));
        module.addDeserializer(File.class, new FileDeserializer(rootDir));
        module.addSerializer(Map.class, new MapSerializer());
        registerModule(module);
    }

    /**
     * @return the tracker
     */
    public ProxySessionTracker getTracker() {
        return tracker;
    }

    /**
     * Enables or disabled quoted field names
     * @param set
     */
    public void setQuoteFieldNames(boolean set) {
        if (set)
            getJsonFactory().enable(JsonGenerator.Feature.QUOTE_FIELD_NAMES);
        else
            getJsonFactory().disable(JsonGenerator.Feature.QUOTE_FIELD_NAMES);
    }

    /**
     * Whether field names will be quoted
     * @return
     */
    public boolean isQuoteFieldNames() {
        return getJsonFactory().isEnabled(JsonGenerator.Feature.QUOTE_FIELD_NAMES);
    }

}