View Javadoc
1   /*
2    * This file is part of ***  M y C o R e  ***
3    * See http://www.mycore.de/ for details.
4    *
5    * MyCoRe is free software: you can redistribute it and/or modify
6    * it under the terms of the GNU General Public License as published by
7    * the Free Software Foundation, either version 3 of the License, or
8    * (at your option) any later version.
9    *
10   * MyCoRe is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   * GNU General Public License for more details.
14   *
15   * You should have received a copy of the GNU General Public License
16   * along with MyCoRe.  If not, see <http://www.gnu.org/licenses/>.
17   */
18  
19  package org.mycore.restapi.v1.utils;
20  
21  import java.io.IOException;
22  import java.io.OutputStream;
23  import java.lang.annotation.Annotation;
24  import java.lang.reflect.Type;
25  import java.nio.charset.StandardCharsets;
26  import java.util.Map;
27  import java.util.Properties;
28  import java.util.stream.Collectors;
29  
30  import org.apache.logging.log4j.LogManager;
31  import org.apache.logging.log4j.Logger;
32  
33  import com.google.common.reflect.TypeToken;
34  import com.google.gson.GsonBuilder;
35  import com.google.gson.JsonElement;
36  import com.google.gson.JsonObject;
37  import com.google.gson.JsonSerializationContext;
38  import com.google.gson.JsonSerializer;
39  
40  import jakarta.ws.rs.Produces;
41  import jakarta.ws.rs.WebApplicationException;
42  import jakarta.ws.rs.core.HttpHeaders;
43  import jakarta.ws.rs.core.MediaType;
44  import jakarta.ws.rs.core.MultivaluedMap;
45  import jakarta.ws.rs.ext.MessageBodyWriter;
46  import jakarta.ws.rs.ext.Provider;
47  
48  @Produces(MediaType.APPLICATION_JSON)
49  @Provider
50  public class MCRPropertiesToJSONTransformer implements MessageBodyWriter<Properties> {
51  
52      @Override
53      public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
54          return Properties.class.isAssignableFrom(type) && mediaType.isCompatible(MediaType.APPLICATION_JSON_TYPE);
55      }
56  
57      @Override
58      public long getSize(Properties t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
59          return -1;
60      }
61  
62      @Override
63      public void writeTo(Properties t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType,
64          MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream)
65          throws IOException, WebApplicationException {
66          httpHeaders.putSingle(HttpHeaders.CONTENT_TYPE,
67              MediaType.APPLICATION_JSON_TYPE.withCharset(StandardCharsets.UTF_8.name()));
68          final Type mapType = new TypeToken<Map<String, String>>() {
69              private static final long serialVersionUID = 1L;
70          }.getType();
71          Map<String, String> writeMap = t.entrySet()
72              .stream()
73              .collect(Collectors.toMap(e -> e.getKey().toString(), e -> e.getValue().toString()));
74          String json = new GsonBuilder().registerTypeAdapter(mapType, new BundleMapSerializer())
75              .create()
76              .toJson(writeMap, mapType);
77          entityStream.write(json.getBytes(StandardCharsets.UTF_8));
78      }
79  
80      public static class BundleMapSerializer implements JsonSerializer<Map<String, String>> {
81  
82          private static final Logger LOGGER = LogManager.getLogger();
83  
84          @Override
85          public JsonElement serialize(final Map<String, String> bundleMap, final Type typeOfSrc,
86              final JsonSerializationContext context) {
87              final JsonObject resultJson = new JsonObject();
88  
89              for (final String key : bundleMap.keySet()) {
90                  try {
91                      createFromBundleKey(resultJson, key, bundleMap.get(key));
92                  } catch (final IOException e) {
93                      LOGGER.error("Bundle map serialization exception: ", e);
94                  }
95              }
96  
97              return resultJson;
98          }
99  
100         public static JsonObject createFromBundleKey(final JsonObject resultJson, final String key, final String value)
101             throws IOException {
102             if (!key.contains(".")) {
103                 resultJson.addProperty(key, value);
104 
105                 return resultJson;
106             }
107 
108             final String currentKey = firstKey(key);
109             if (currentKey != null) {
110                 final String subRightKey = key.substring(currentKey.length() + 1);
111                 final JsonObject childJson = getJsonIfExists(resultJson, currentKey);
112 
113                 resultJson.add(currentKey, createFromBundleKey(childJson, subRightKey, value));
114             }
115 
116             return resultJson;
117         }
118 
119         private static String firstKey(final String fullKey) {
120             final String[] splittedKey = fullKey.split("\\.");
121 
122             return (splittedKey.length != 0) ? splittedKey[0] : fullKey;
123         }
124 
125         private static JsonObject getJsonIfExists(final JsonObject parent, final String key) {
126             if (parent == null) {
127                 LOGGER.warn("Parent json parameter is null!");
128                 return null;
129             }
130 
131             if (parent.get(key) != null && !(parent.get(key) instanceof JsonObject)) {
132                 throw new IllegalArgumentException("Invalid key \'" + key + "\' for parent: " + parent
133                     + "\nKey can not be JSON object and property or array in one time");
134             }
135 
136             if (parent.getAsJsonObject(key) != null) {
137                 return parent.getAsJsonObject(key);
138             } else {
139                 return new JsonObject();
140             }
141         }
142     }
143 }