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.InputStream;
021import java.io.InputStreamReader;
022import java.io.OutputStream;
023import java.io.StringReader;
024import java.io.StringWriter;
025import java.nio.charset.Charset;
026import java.util.ArrayList;
027import java.util.LinkedHashMap;
028import java.util.List;
029import java.util.Map;
030
031import org.apache.avalon.framework.activity.Initializable;
032import org.apache.avalon.framework.component.Component;
033import org.apache.avalon.framework.service.ServiceException;
034import org.apache.avalon.framework.service.ServiceManager;
035import org.apache.avalon.framework.service.Serviceable;
036import org.apache.avalon.framework.thread.ThreadSafe;
037import org.apache.commons.lang3.StringUtils;
038
039import org.ametys.plugins.core.ui.help.HelpSerializer;
040
041import com.fasterxml.jackson.core.JsonEncoding;
042import com.fasterxml.jackson.core.JsonFactory;
043import com.fasterxml.jackson.core.JsonGenerator;
044import com.fasterxml.jackson.core.JsonParser;
045import com.fasterxml.jackson.core.Version;
046import com.fasterxml.jackson.databind.ObjectMapper;
047import com.fasterxml.jackson.databind.module.SimpleModule;
048
049/**
050 * JSON helper
051 */
052public class JSONUtils implements Component, ThreadSafe, Serviceable, Initializable
053{
054    /** The avalon role */
055    public static final String ROLE = JSONUtils.class.getName();
056    
057    private JsonFactory _jsonFactory = new JsonFactory();
058    private ObjectMapper _objectMapper = new ObjectMapper();
059
060    private I18nizableSerializer _i18nizableSerializer;
061    private HelpSerializer _helpSerializer;
062    private ZonedDateTimeSerializer _zoneDateTimeSerializer;
063    private LocalDateSerializer _localDateSerializer;
064    
065    @Override
066    public void service(ServiceManager manager) throws ServiceException
067    {
068        _i18nizableSerializer = (I18nizableSerializer) manager.lookup(I18nizableSerializer.ROLE);
069        _zoneDateTimeSerializer = (ZonedDateTimeSerializer) manager.lookup(ZonedDateTimeSerializer.ROLE);
070        _localDateSerializer = (LocalDateSerializer) manager.lookup(LocalDateSerializer.ROLE);
071        _helpSerializer = (HelpSerializer) manager.lookup(HelpSerializer.ROLE);
072    }
073    
074    @Override
075    public void initialize() throws Exception
076    {
077        // Register new serializer for I18nizableText
078        SimpleModule i18nModule = new SimpleModule("AmetysI18nModule", new Version(1, 0, 0, null, null, null));
079        i18nModule.addSerializer(_i18nizableSerializer);
080        _objectMapper.registerModule(i18nModule);
081
082        // Register new serializer for help url
083        SimpleModule helpModule = new SimpleModule("AmetysHelpModule", new Version(1, 0, 0, null, null, null));
084        helpModule.addSerializer(_helpSerializer);
085        _objectMapper.registerModule(helpModule);
086        
087        // Register new serializer for ZonedDateTime
088        SimpleModule zoneDateTimeModule = new SimpleModule("AmetysZonedDateTimeModule", new Version(1, 0, 0, null, null, null));
089        zoneDateTimeModule.addSerializer(_zoneDateTimeSerializer);
090        _objectMapper.registerModule(zoneDateTimeModule);
091        
092        // Register new serializer for LocalDate
093        SimpleModule localDateModule = new SimpleModule("AmetysLocalDateModule", new Version(1, 0, 0, null, null, null));
094        localDateModule.addSerializer(_localDateSerializer);
095        _objectMapper.registerModule(localDateModule);
096    }
097    
098    /**
099     * Parse a JSON string to a {@link Map} object
100     * @param jsonString the string to parse
101     * @return object the infos as a Map.
102     */
103    public Map<String, Object> convertJsonToMap (String jsonString)
104    {
105        try
106        {
107            if (StringUtils.isNotBlank(jsonString))
108            {
109                JsonParser jParser = _jsonFactory.createParser(new StringReader(jsonString));
110                Map<String, Object> map = _objectMapper.readValue(jParser, LinkedHashMap.class);
111                return map;
112            }
113            else
114            {
115                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
116            }
117        }
118        catch (Exception e)
119        {
120            throw new IllegalArgumentException("The json string " + jsonString + " can not be parsed as a Map.", e);
121        }
122    }
123    
124    /**
125     * Parse a JSON input stream to a {@link Map} object
126     * @param jsonInput the input stream to parse
127     * @param charset the charset of the stream
128     * @return object the infos as a Map.
129     */
130    public Map<String, Object> convertJsonToMap(InputStream jsonInput, Charset charset)
131    {
132        try (InputStreamReader reader = new InputStreamReader(jsonInput, charset);
133                JsonParser jParser = _jsonFactory.createParser(reader))
134        {
135            return _objectMapper.readValue(jParser, LinkedHashMap.class);
136        }
137        catch (Exception e)
138        {
139            throw new IllegalArgumentException("The json input can not be parsed as a Map.", e);
140        }
141    }
142    
143    /**
144     * Parse a JSON string to a {@link List} object.
145     * @param jsonString the string to parse.
146     * @return the infos as a List.
147     */
148    public List<Object> convertJsonToList(String jsonString)
149    {
150        try
151        {
152            if (StringUtils.isNotBlank(jsonString))
153            {
154                JsonParser jParser = _jsonFactory.createParser(new StringReader(jsonString));
155                List<Object> list = _objectMapper.readValue(jParser, ArrayList.class);
156                return list;
157            }
158            else
159            {
160                return new ArrayList<>();
161            }
162        }
163        catch (Exception e)
164        {
165            throw new IllegalArgumentException("The json string " + jsonString + " can not be parsed as a List.", e);
166        }
167    }
168    
169    /**
170     * Parse a JSON input stream to a {@link List} object.
171     * @param jsonInput the input stream to parse
172     * @param charset the charset of the stream
173     * @return the infos as a List.
174     */
175    public List<Object> convertJsonToList(InputStream jsonInput, Charset charset)
176    {
177        try (InputStreamReader reader = new InputStreamReader(jsonInput, charset);
178                JsonParser jParser = _jsonFactory.createParser(reader))
179        {
180            return _objectMapper.readValue(jParser, ArrayList.class);
181        }
182        catch (Exception e)
183        {
184            throw new IllegalArgumentException("The json input can not be parsed as a Map.", e);
185        }
186    }
187    
188    /**
189     * Parse a JSON string to an Object array.
190     * @param jsonString the JSON string to parse.
191     * @return the converted Object array.
192     */
193    public Object[] convertJsonToArray(String jsonString)
194    {
195        try
196        {
197            if (StringUtils.isNotBlank(jsonString))
198            {
199                JsonParser jParser = _jsonFactory.createParser(new StringReader(jsonString));
200                Object[] array = _objectMapper.readValue(jParser, Object[].class);
201                return array;
202            }
203            else
204            {
205                return new Object[0];
206            }
207        }
208        catch (Exception e)
209        {
210            throw new IllegalArgumentException("The json string " + jsonString + " can not be parsed as an array.", e);
211        }
212    }
213    
214    /**
215     * Parse a JSON input stream to an {@link Object} array.
216     * @param jsonInput the input stream to parse
217     * @param charset the charset of the stream
218     * @return the infos as a List.
219     */
220    public Object[] convertJsonToArray(InputStream jsonInput, Charset charset)
221    {
222        try (InputStreamReader reader = new InputStreamReader(jsonInput, charset);
223                JsonParser jParser = _jsonFactory.createParser(reader))
224        {
225            return _objectMapper.readValue(jParser, Object[].class);
226        }
227        catch (Exception e)
228        {
229            throw new IllegalArgumentException("The json input can not be parsed as a Map.", e);
230        }
231    }
232    
233    /**
234     * Parse a JSON string to a String array.
235     * @param jsonString the JSON string to parse.
236     * @return the converted String array.
237     */
238    public String[] convertJsonToStringArray(String jsonString)
239    {
240        try
241        {
242            if (StringUtils.isNotBlank(jsonString))
243            {
244                JsonParser jParser = _jsonFactory.createParser(new StringReader(jsonString));
245                String[] array = _objectMapper.readValue(jParser, String[].class);
246                return array;
247            }
248            else
249            {
250                return new String[0];
251            }
252        }
253        catch (Exception e)
254        {
255            throw new IllegalArgumentException("The json string " + jsonString + " can not be parsed as a String array.", e);
256        }
257    }
258    
259    /**
260     * Parse a JSON input stream to an {@link String} array.
261     * @param jsonInput the input stream to parse
262     * @param charset the charset of the stream
263     * @return the infos as a List.
264     */
265    public Object[] convertJsonToStringArray(InputStream jsonInput, Charset charset)
266    {
267        try (InputStreamReader reader = new InputStreamReader(jsonInput, charset);
268                JsonParser jParser = _jsonFactory.createParser(reader))
269        {
270            return _objectMapper.readValue(jParser, String[].class);
271        }
272        catch (Exception e)
273        {
274            throw new IllegalArgumentException("The json input can not be parsed as a Map.", e);
275        }
276    }
277    
278    /**
279     * Convert an object to JSON string using specified output stream.
280     * @param out The output stream
281     * @param parameters The object to convert
282     */
283    public void convertObjectToJson (OutputStream out, Object parameters)
284    {
285        try
286        {
287            JsonGenerator jsonGenerator = _jsonFactory.createGenerator(out, JsonEncoding.UTF8);
288            _objectMapper.writeValue(jsonGenerator, parameters);
289        }
290        catch (IOException e)
291        {
292            throw new IllegalArgumentException("The object can not be converted to json string", e);
293        }
294    }
295    
296    /**
297     * Convert an object to a JSON string
298     * @param parameters The object to convert (List, Map ..)
299     * @return The JSON string
300     */
301    public String convertObjectToJson (Object parameters)
302    {
303        try
304        {
305            StringWriter writer = new StringWriter();
306            
307            JsonGenerator jsonGenerator = _jsonFactory.createGenerator(writer);
308            _objectMapper.writeValue(jsonGenerator, parameters);
309            
310            return writer.toString();
311        }
312        catch (IOException e)
313        {
314            throw new IllegalArgumentException("The object can not be converted to json string", e);
315        }
316    }
317}