001/*
002 *  Copyright 2012 Anyware Services
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016
017package org.ametys.core.util;
018
019import java.io.IOException;
020import java.io.OutputStream;
021import java.io.StringReader;
022import java.io.StringWriter;
023import java.util.ArrayList;
024import java.util.LinkedHashMap;
025import java.util.List;
026import java.util.Map;
027
028import org.apache.avalon.framework.activity.Initializable;
029import org.apache.avalon.framework.component.Component;
030import org.apache.avalon.framework.service.ServiceException;
031import org.apache.avalon.framework.service.ServiceManager;
032import org.apache.avalon.framework.service.Serviceable;
033import org.apache.avalon.framework.thread.ThreadSafe;
034import org.apache.commons.lang3.StringUtils;
035
036import org.ametys.plugins.core.ui.help.HelpSerializer;
037
038import com.fasterxml.jackson.core.JsonEncoding;
039import com.fasterxml.jackson.core.JsonFactory;
040import com.fasterxml.jackson.core.JsonGenerator;
041import com.fasterxml.jackson.core.JsonParser;
042import com.fasterxml.jackson.core.Version;
043import com.fasterxml.jackson.databind.ObjectMapper;
044import com.fasterxml.jackson.databind.module.SimpleModule;
045
046/**
047 * JSON helper
048 */
049public class JSONUtils implements Component, ThreadSafe, Serviceable, Initializable
050{
051    /** The avalon role */
052    public static final String ROLE = JSONUtils.class.getName();
053    
054    private JsonFactory _jsonFactory = new JsonFactory();
055    private ObjectMapper _objectMapper = new ObjectMapper();
056
057    private I18nizableSerializer _i18nizableSerializer;
058    private HelpSerializer _helpSerializer;
059    private ZonedDateTimeSerializer _zoneDateTimeSerializer;
060    private LocalDateSerializer _localDateSerializer;
061    
062    @Override
063    public void service(ServiceManager manager) throws ServiceException
064    {
065        _i18nizableSerializer = (I18nizableSerializer) manager.lookup(I18nizableSerializer.ROLE);
066        _zoneDateTimeSerializer = (ZonedDateTimeSerializer) manager.lookup(ZonedDateTimeSerializer.ROLE);
067        _localDateSerializer = (LocalDateSerializer) manager.lookup(LocalDateSerializer.ROLE);
068        _helpSerializer = (HelpSerializer) manager.lookup(HelpSerializer.ROLE);
069    }
070    
071    @Override
072    public void initialize() throws Exception
073    {
074        // Register new serializer for I18nizableText
075        SimpleModule i18nModule = new SimpleModule("AmetysI18nModule", new Version(1, 0, 0, null, null, null));
076        i18nModule.addSerializer(_i18nizableSerializer);
077        _objectMapper.registerModule(i18nModule);
078
079        // Register new serializer for help url
080        SimpleModule helpModule = new SimpleModule("AmetysHelpModule", new Version(1, 0, 0, null, null, null));
081        helpModule.addSerializer(_helpSerializer);
082        _objectMapper.registerModule(helpModule);
083        
084        // Register new serializer for ZonedDateTime
085        SimpleModule zoneDateTimeModule = new SimpleModule("AmetysZonedDateTimeModule", new Version(1, 0, 0, null, null, null));
086        zoneDateTimeModule.addSerializer(_zoneDateTimeSerializer);
087        _objectMapper.registerModule(zoneDateTimeModule);
088        
089        // Register new serializer for LocalDate
090        SimpleModule localDateModule = new SimpleModule("AmetysLocalDateModule", new Version(1, 0, 0, null, null, null));
091        localDateModule.addSerializer(_localDateSerializer);
092        _objectMapper.registerModule(localDateModule);
093    }
094    
095    /**
096     * Parse a JSON string to a {@link Map} object
097     * @param jsonString the string to parse
098     * @return object the infos as a Map.
099     */
100    public Map<String, Object> convertJsonToMap (String jsonString)
101    {
102        try 
103        {
104            if (StringUtils.isNotBlank(jsonString)) 
105            {
106                JsonParser jParser = _jsonFactory.createParser(new StringReader(jsonString));
107                Map<String, Object> map = _objectMapper.readValue(jParser, LinkedHashMap.class);
108                return map;
109            } 
110            else 
111            {
112                return new LinkedHashMap<>(); // We should not return a Collections.emptyMap() here, since we need to return a modifiable map that is always of the same type
113            }
114        } 
115        catch (Exception e) 
116        {
117            throw new IllegalArgumentException("The json string " + jsonString + " can not be parsed as a Map.", e);
118        }
119    }
120    
121    /**
122     * Parse a JSON string to a {@link List} object.
123     * @param jsonString the string to parse.
124     * @return the infos as a List.
125     */
126    public List<Object> convertJsonToList(String jsonString)
127    {
128        try 
129        {
130            if (StringUtils.isNotBlank(jsonString)) 
131            {
132                JsonParser jParser = _jsonFactory.createParser(new StringReader(jsonString));
133                List<Object> list = _objectMapper.readValue(jParser, ArrayList.class);
134                return list;
135            }
136            else
137            {
138                return new ArrayList<>();
139            }
140        } 
141        catch (Exception e) 
142        {
143            throw new IllegalArgumentException("The json string " + jsonString + " can not be parsed as a List.", e);
144        }
145    }
146    
147    /**
148     * Parse a JSON string to an Object array.
149     * @param jsonString the JSON string to parse.
150     * @return the converted Object array.
151     */
152    public Object[] convertJsonToArray(String jsonString)
153    {
154        try 
155        {
156            if (StringUtils.isNotBlank(jsonString)) 
157            {
158                JsonParser jParser = _jsonFactory.createParser(new StringReader(jsonString));
159                Object[] array = _objectMapper.readValue(jParser, Object[].class);
160                return array;
161            }
162            else
163            {
164                return new Object[0];
165            }
166        } 
167        catch (Exception e) 
168        {
169            throw new IllegalArgumentException("The json string " + jsonString + " can not be parsed as an array.", e);
170        }
171    }
172    
173    /**
174     * Parse a JSON string to a String array.
175     * @param jsonString the JSON string to parse.
176     * @return the converted String array.
177     */
178    public String[] convertJsonToStringArray(String jsonString)
179    {
180        try 
181        {
182            if (StringUtils.isNotBlank(jsonString)) 
183            {
184                JsonParser jParser = _jsonFactory.createParser(new StringReader(jsonString));
185                String[] array = _objectMapper.readValue(jParser, String[].class);
186                return array;
187            }
188            else
189            {
190                return new String[0];
191            }
192        } 
193        catch (Exception e) 
194        {
195            throw new IllegalArgumentException("The json string " + jsonString + " can not be parsed as a String array.", e);
196        }
197    }
198    
199    /**
200     * Convert an object to JSON string using specified output stream.
201     * @param out The output stream
202     * @param parameters The object to convert
203     */
204    public void convertObjectToJson (OutputStream out, Object parameters)
205    {
206        try
207        {
208            JsonGenerator jsonGenerator = _jsonFactory.createGenerator(out, JsonEncoding.UTF8);
209            _objectMapper.writeValue(jsonGenerator, parameters);
210        }
211        catch (IOException e)
212        {
213            throw new IllegalArgumentException("The object can not be converted to json string", e);
214        }
215    }
216    
217    /**
218     * Convert an object to a JSON string
219     * @param parameters The object to convert (List, Map ..)
220     * @return The JSON string
221     */
222    public String convertObjectToJson (Object parameters)
223    {
224        try
225        {
226            StringWriter writer = new StringWriter();
227            
228            JsonGenerator jsonGenerator = _jsonFactory.createGenerator(writer);
229            _objectMapper.writeValue(jsonGenerator, parameters);
230            
231            return writer.toString();
232        }
233        catch (IOException e)
234        {
235            throw new IllegalArgumentException("The object can not be converted to json string", e);
236        }
237    }
238}