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.io.IOUtils;
035import org.apache.commons.lang.StringUtils;
036
037import org.ametys.plugins.core.ui.help.HelpSerializer;
038
039import com.fasterxml.jackson.core.JsonEncoding;
040import com.fasterxml.jackson.core.JsonFactory;
041import com.fasterxml.jackson.core.JsonGenerator;
042import com.fasterxml.jackson.core.JsonParser;
043import com.fasterxml.jackson.core.Version;
044import com.fasterxml.jackson.databind.ObjectMapper;
045import com.fasterxml.jackson.databind.module.SimpleModule;
046
047/**
048 * JSON helper
049 */
050public class JSONUtils implements Component, ThreadSafe, Serviceable, Initializable
051{
052    /** The avalon role */
053    public static final String ROLE = JSONUtils.class.getName();
054    
055    private JsonFactory _jsonFactory = new JsonFactory();
056    private ObjectMapper _objectMapper = new ObjectMapper();
057
058    private I18nizableTextSerializer _i18nizableTextSerializer;
059    private HelpSerializer _helpSerializer;
060    private ZonedDateTimeSerializer _zoneDateTimeSerializer;
061    private LocalDateSerializer _localDateSerializer;
062    
063    @Override
064    public void service(ServiceManager manager) throws ServiceException
065    {
066        _i18nizableTextSerializer = (I18nizableTextSerializer) manager.lookup(I18nizableTextSerializer.ROLE);
067        _zoneDateTimeSerializer = (ZonedDateTimeSerializer) manager.lookup(ZonedDateTimeSerializer.ROLE);
068        _localDateSerializer = (LocalDateSerializer) manager.lookup(LocalDateSerializer.ROLE);
069        _helpSerializer = (HelpSerializer) manager.lookup(HelpSerializer.ROLE);
070    }
071    
072    @Override
073    public void initialize() throws Exception
074    {
075        // Register new serializer for I18nizableText
076        SimpleModule i18nModule = new SimpleModule("AmetysI18nModule", new Version(1, 0, 0, null, null, null));
077        i18nModule.addSerializer(_i18nizableTextSerializer);
078        _objectMapper.registerModule(i18nModule);
079
080        // Register new serializer for help url
081        SimpleModule helpModule = new SimpleModule("AmetysHelpModule", new Version(1, 0, 0, null, null, null));
082        helpModule.addSerializer(_helpSerializer);
083        _objectMapper.registerModule(helpModule);
084        
085        // Register new serializer for ZonedDateTime
086        SimpleModule zoneDateTimeModule = new SimpleModule("AmetysZonedDateTimeModule", new Version(1, 0, 0, null, null, null));
087        zoneDateTimeModule.addSerializer(_zoneDateTimeSerializer);
088        _objectMapper.registerModule(zoneDateTimeModule);
089        
090        // Register new serializer for LocalDate
091        SimpleModule localDateModule = new SimpleModule("AmetysLocalDateModule", new Version(1, 0, 0, null, null, null));
092        localDateModule.addSerializer(_localDateSerializer);
093        _objectMapper.registerModule(localDateModule);
094    }
095    
096    /**
097     * Parse a JSON string to a {@link Map} object
098     * @param jsonString the string to parse
099     * @return object the infos as a Map.
100     */
101    public Map<String, Object> convertJsonToMap (String jsonString)
102    {
103        try 
104        {
105            if (StringUtils.isNotBlank(jsonString)) 
106            {
107                JsonParser jParser = _jsonFactory.createParser(new StringReader(jsonString));
108                Map<String, Object> map = _objectMapper.readValue(jParser, LinkedHashMap.class);
109                return map;
110            } 
111            else 
112            {
113                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
114            }
115        } 
116        catch (Exception e) 
117        {
118            throw new IllegalArgumentException("The json string " + jsonString + " can not be parsed as a Map.", e);
119        }
120    }
121    
122    /**
123     * Parse a JSON string to a {@link List} object.
124     * @param jsonString the string to parse.
125     * @return the infos as a List.
126     */
127    public List<Object> convertJsonToList(String jsonString)
128    {
129        try 
130        {
131            if (StringUtils.isNotBlank(jsonString)) 
132            {
133                JsonParser jParser = _jsonFactory.createParser(new StringReader(jsonString));
134                List<Object> list = _objectMapper.readValue(jParser, ArrayList.class);
135                return list;
136            }
137            else
138            {
139                return new ArrayList<>();
140            }
141        } 
142        catch (Exception e) 
143        {
144            throw new IllegalArgumentException("The json string " + jsonString + " can not be parsed as a List.", e);
145        }
146    }
147    
148    /**
149     * Parse a JSON string to an Object array.
150     * @param jsonString the JSON string to parse.
151     * @return the converted Object array.
152     */
153    public Object[] convertJsonToArray(String jsonString)
154    {
155        try 
156        {
157            if (StringUtils.isNotBlank(jsonString)) 
158            {
159                JsonParser jParser = _jsonFactory.createParser(new StringReader(jsonString));
160                Object[] array = _objectMapper.readValue(jParser, Object[].class);
161                return array;
162            }
163            else
164            {
165                return new Object[0];
166            }
167        } 
168        catch (Exception e) 
169        {
170            throw new IllegalArgumentException("The json string " + jsonString + " can not be parsed as an array.", e);
171        }
172    }
173    
174    /**
175     * Parse a JSON string to a String array.
176     * @param jsonString the JSON string to parse.
177     * @return the converted String array.
178     */
179    public String[] convertJsonToStringArray(String jsonString)
180    {
181        try 
182        {
183            if (StringUtils.isNotBlank(jsonString)) 
184            {
185                JsonParser jParser = _jsonFactory.createParser(new StringReader(jsonString));
186                String[] array = _objectMapper.readValue(jParser, String[].class);
187                return array;
188            }
189            else
190            {
191                return new String[0];
192            }
193        } 
194        catch (Exception e) 
195        {
196            throw new IllegalArgumentException("The json string " + jsonString + " can not be parsed as a String array.", e);
197        }
198    }
199    
200    /**
201     * Convert an object to JSON string using specified output stream.
202     * The out stream is closed after processing.
203     * @param out The output stream
204     * @param parameters The object to convert
205     */
206    public void convertObjectToJson (OutputStream out, Object parameters)
207    {
208        try
209        {
210            JsonGenerator jsonGenerator = _jsonFactory.createGenerator(out, JsonEncoding.UTF8);
211            _objectMapper.writeValue(jsonGenerator, parameters);
212            
213            IOUtils.closeQuietly(out);
214        }
215        catch (IOException e)
216        {
217            throw new IllegalArgumentException("The object can not be converted to json string", e);
218        }
219    }
220    
221    /**
222     * Convert an object to a JSON string
223     * @param parameters The object to convert (List, Map ..)
224     * @return The JSON string
225     */
226    public String convertObjectToJson (Object parameters)
227    {
228        try
229        {
230            StringWriter writer = new StringWriter();
231            
232            JsonGenerator jsonGenerator = _jsonFactory.createGenerator(writer);
233            _objectMapper.writeValue(jsonGenerator, parameters);
234            
235            return writer.toString();
236        }
237        catch (IOException e)
238        {
239            throw new IllegalArgumentException("The object can not be converted to json string", e);
240        }
241    }
242}