/* * Copyright 2016 Red Hat, Inc. and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.kie.server.router.proxy.aggragate; import static org.kie.server.router.utils.Helper.readProperties; import java.lang.reflect.Field; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Properties; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; public class JSONResponseAggregator implements ResponseAggregator { private static final String JSON_TYPE = "application/json"; private static final Properties sortByMapping = readProperties(JSONResponseAggregator.class.getResourceAsStream("/sort-json.mapping")); public String aggregate(List<String> data) { return aggregate(data, null, true, 0, 10); } @Override public String aggregate(List<String> data, String sortBy, boolean ascending, Integer page, Integer pageSize) { try { JSONObject json = data.stream().map(s -> { return newJson(s); }).reduce((source, target) -> { deepMerge(source, target); return target; }).get(); String response = sort(sortBy, ascending, page, pageSize, json); return response; } catch (IllegalArgumentException e) { // try with sorting array JSONArray jsonArray = data.stream().map(s -> { return newJsonArray(s); }).reduce((source, target) -> { deepMergeArray(source, target); return target; }).get(); String response = sortArray(sortBy, ascending, page, pageSize, jsonArray); return response; } } protected String sort(String fieldName, boolean ascending, Integer page, Integer pageSize, JSONObject source) { try { for (String key: JSONObject.getNames(source)) { Object value = source.get(key); if (value instanceof JSONArray) { JSONArray array = (JSONArray) value; // apply sorting sortList(fieldName, array, ascending, page, pageSize); } } return source.toString(2); } catch (Exception e) { throw new RuntimeException("Error while sorting and paging of json", e); } } protected JSONObject deepMerge(JSONObject source, JSONObject target) { try { for (String key: JSONObject.getNames(source)) { Object value = source.get(key); if (!target.has(key)) { // new value for "key": target.put(key, value); } else { // existing value for "key" - recursively deep merge: if (value instanceof JSONObject) { JSONObject valueJson = (JSONObject)value; deepMerge(valueJson, target.getJSONObject(key)); } // insert each JSONArray's JSONObject in place else if (value instanceof JSONArray) { JSONArray jsonArray = ((JSONArray) value); for (int i = 0, size = jsonArray.length(); i < size; i++) { JSONObject objectInArray = jsonArray.getJSONObject(i); ((JSONArray) target.get(key)).put(objectInArray); } } else { target.put(key, value); } } } return target; } catch (JSONException e) { return null; } } protected JSONArray deepMergeArray(JSONArray source, JSONArray target) { try { for (int i = 0, size = source.length(); i < size; i++) { JSONArray objectInArray = source.getJSONArray(i); target.put(objectInArray); } return target; } catch (JSONException e) { return null; } } protected String sortArray(String fieldName, boolean ascending, Integer page, Integer pageSize, JSONArray source) { try { // apply sorting sortList(fieldName, source, ascending, page, pageSize); return source.toString(2); } catch (Exception e) { throw new RuntimeException("Error while sorting and paging of json", e); } } protected JSONObject newJson(String data) { try { return new JSONObject(data); } catch (JSONException e) { throw new IllegalArgumentException(e.getMessage(), e); } } protected JSONArray newJsonArray(String data) { try { return new JSONArray(data); } catch (JSONException e) { throw new IllegalArgumentException(e.getMessage(), e); } } @Override public boolean supports(Object... acceptTypes) { for (Object acceptType : acceptTypes ) { if (acceptType == null) { continue; } boolean found = acceptType.toString().toLowerCase().contains(JSON_TYPE); if (found) { return true; } } return false; } protected void sortList(String fieldName, JSONArray array, boolean ascending, int page, int pageSize) throws Exception{ Field f = array.getClass().getDeclaredField("myArrayList"); f.setAccessible(true); List<?> jsonList = (List<?>) f.get(array); if (fieldName != null && !fieldName.isEmpty()) { String sortBy = sortByMapping.getProperty(fieldName, fieldName); Collections.sort(jsonList, new Comparator<Object>() { @SuppressWarnings({"rawtypes", "unchecked"}) @Override public int compare(Object o1, Object o2) { if (o1 instanceof JSONObject && o2 instanceof JSONObject) { try { Comparable v1 = (Comparable<?>)((JSONObject) o1).get(sortBy); Comparable v2 = (Comparable<?>)((JSONObject) o2).get(sortBy); if (ascending) { return v1.compareTo(v2); } else { return v2.compareTo(v1); } } catch (Exception e) { } } return 0; } }); } // calculate paging int start = page * pageSize; int end = start + pageSize; // apply paging if (jsonList.size() < start) { // no elements in given range, return empty jsonList.clear(); } else if (jsonList.size() >= end) { List<?> tmp = jsonList.subList(start, end); jsonList.retainAll(tmp); } else if (jsonList.size() < end) { List<?> tmp = jsonList.subList(start, jsonList.size()); jsonList.retainAll(tmp); } } }