001/* 
002 * JKNIV, whinstone one contract to access your database.
003 * 
004 * Copyright (C) 2017, the original author or authors.
005 *
006 * This library is free software; you can redistribute it and/or
007 * modify it under the terms of the GNU Lesser General Public
008 * License as published by the Free Software Foundation; either
009 * version 2.1 of the License.
010 * 
011 * This library is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014 * Lesser General Public License for more details.
015 * 
016 * You should have received a copy of the GNU Lesser General Public
017 * License along with this library; if not, write to the Free Software Foundation, Inc., 
018 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
019 */
020package net.sf.jkniv.whinstone.couchdb.commands;
021
022import java.io.IOException;
023import java.io.UnsupportedEncodingException;
024import java.util.HashMap;
025import java.util.Map;
026
027import org.slf4j.Logger;
028import org.slf4j.LoggerFactory;
029
030import com.fasterxml.jackson.core.JsonParseException;
031import com.fasterxml.jackson.core.type.TypeReference;
032import com.fasterxml.jackson.databind.DeserializationFeature;
033import com.fasterxml.jackson.databind.JsonMappingException;
034import com.fasterxml.jackson.databind.Module;
035import com.fasterxml.jackson.databind.ObjectMapper;
036import com.fasterxml.jackson.databind.SerializationFeature;
037import com.fasterxml.jackson.databind.module.SimpleModule;
038
039import net.sf.jkniv.exception.HandlerException;
040import net.sf.jkniv.reflect.beans.ObjectProxy;
041import net.sf.jkniv.reflect.beans.ObjectProxyFactory;
042import net.sf.jkniv.sqlegance.RepositoryException;
043import net.sf.jkniv.whinstone.QueryFactory;
044import net.sf.jkniv.whinstone.Queryable;
045
046public class JsonMapper
047{
048    private static final Logger LOG = LoggerFactory.getLogger(JsonMapper.class);
049    private static HandlerException handlerException;
050    static final ObjectMapper MAPPER = new ObjectMapper();
051    private static final Map<String, String> JACKSON_MODULES = new HashMap<String, String>();
052    private static final ThreadLocal<Queryable> CURRENT_QUERY = new ThreadLocal<Queryable>();
053    
054    static
055    {
056        handlerException = new HandlerException(RepositoryException.class, "Cannot set parameter [%s] value [%s]");
057        // JsonParseException | JsonMappingException | IOException
058        handlerException.config(JsonParseException.class, "Error to parser json non-well-formed content [%s]");
059        handlerException.config(JsonMappingException.class, "Error to deserialization content [%s]");
060        handlerException.config(UnsupportedEncodingException.class, "Error at json content encoding unsupported [%s]");
061        handlerException.config(IOException.class, "Error from I/O json content [%s]");
062        
063        JACKSON_MODULES.put("ParameterNamesModule", "com.fasterxml.jackson.module.paramnames.ParameterNamesModule");
064        JACKSON_MODULES.put("JavaTimeModule",       "com.fasterxml.jackson.datatype.jsr310.JavaTimeModule"); 
065        JACKSON_MODULES.put("Jdk8Module",           "com.fasterxml.jackson.datatype.jdk8.Jdk8Module"); 
066        JACKSON_MODULES.put("JSR310TimeModule",     "com.fasterxml.jackson.datatype.jsr310.JSR310TimeModule"); 
067        JACKSON_MODULES.put("ThreeTenModule",       "com.fasterxml.jackson.datatype.threetenbp.ThreeTenModule");
068        
069        SimpleModule simpleModule = new SimpleModule();
070        simpleModule.addDeserializer(CouchDbResultImpl.class, new CouchDbJsonDeserialization());
071        MAPPER.registerModule(simpleModule);
072        // pretty print
073        //String prettyStaff1 = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(staff2);
074        //MAPPER.writerWithDefaultPrettyPrinter();
075
076    }
077    
078    private JsonMapper()
079    {
080    }
081    
082    static void setCurrentQuery(Queryable queryable)
083    {
084        CURRENT_QUERY.set(queryable);
085    }
086
087    static Queryable getCurrentQuery()
088    {
089        Queryable queryable = CURRENT_QUERY.get();
090        CURRENT_QUERY.remove();
091        if(queryable == null)
092        {
093            queryable = QueryFactory.of("dummy", Map.class);
094        }
095        return queryable;
096    }
097    
098    public static void config(SerializationFeature feature, boolean state)
099    {
100        MAPPER.configure(feature, state);
101    }
102
103    public static void config(DeserializationFeature feature, boolean state)
104    {
105        MAPPER.configure(feature, state);
106    }
107
108    public static void register(String moduleName, boolean state)
109    {
110        if (!state)
111            return;
112        
113        Module module = null;
114        ObjectProxy<Module> proxy = null;
115        String classNameOfModule = JACKSON_MODULES.get(moduleName);
116        if (classNameOfModule !=  null)
117        {
118            proxy = ObjectProxyFactory.of(classNameOfModule);
119            module = proxy.newInstance();
120            if (module != null)
121            {
122                MAPPER.registerModule(module);
123                LOG.info("Jackson module {} was registered with success by class {} ", moduleName , classNameOfModule);
124            }
125        }
126        if (module == null)
127            LOG.error("Cannot register the module {}! {} ", moduleName, (classNameOfModule == null ?  "ParameterNamesModule, JavaTimeModule and Jdk8Module are supported to register" : "the class "+classNameOfModule +" was not found at classpath"));
128
129    }
130
131//    public static ObjectMapper newMapper()
132//    {
133//        //ObjectMapper mapper = new ObjectMapper();
134//        // FIXME design jackson json properties config
135//        //mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
136//        return MAPPER;
137//    }
138    
139    public static <T> T mapper(String content, Class<T> valueType)
140    {
141        try
142        {
143            return MAPPER.readValue(content, valueType);
144        }
145        catch (Exception e)
146        {
147            // JsonParseException | JsonMappingException | IOException
148            handlerException.handle(e);
149        }
150        return null;
151    }
152
153    @SuppressWarnings("rawtypes")
154    public static <T> T mapper(String content, TypeReference valueType)
155    {
156        try
157        {
158            return MAPPER.readValue(content, valueType);
159        }
160        catch (Exception e)
161        {
162            // JsonParseException | JsonMappingException | IOException
163            handlerException.handle(e);
164        }
165        return null;
166    }
167    
168    public static <T> T mapper(Map<String, Object> content, Class<T> valueType)
169    {
170        try
171        {
172            //final ObjectMapper mapper = new ObjectMapper(); // jackson's objectmapper
173            return MAPPER.convertValue(content, valueType);
174            //return newMapper().readValue(content, valueType);
175        }
176        catch (Exception e)
177        {
178            // JsonParseException | JsonMappingException | IOException
179            handlerException.handle(e);
180        }
181        return null;
182    }
183    
184    public static String mapper(Object o)
185    {
186        try
187        {
188            return MAPPER.writeValueAsString(o);
189        }
190        catch (Exception e)
191        {
192            // JsonParseException | JsonMappingException | IOException
193            handlerException.handle(e);
194        }
195        return null;
196    }
197}