org.cgiar.ccafs.marlo.utils.HistoryComparator.java Source code

Java tutorial

Introduction

Here is the source code for org.cgiar.ccafs.marlo.utils.HistoryComparator.java

Source

/*****************************************************************
 * This file is part of Managing Agricultural Research for Learning &
 * Outcomes Platform (MARLO).
 * MARLO is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * at your option) any later version.
 * MARLO is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with MARLO. If not, see <http://www.gnu.org/licenses/>.
 *****************************************************************/

package org.cgiar.ccafs.marlo.utils;

import org.cgiar.ccafs.marlo.data.IAuditLog;
import org.cgiar.ccafs.marlo.data.manager.AuditLogManager;
import org.cgiar.ccafs.marlo.data.model.Auditlog;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

import javax.inject.Inject;
import javax.inject.Named;

import com.google.common.collect.MapDifference;
import com.google.common.collect.MapDifference.ValueDifference;
import com.google.common.collect.Maps;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.ibm.icu.text.SimpleDateFormat;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Named
public class HistoryComparator {

    private static final Logger LOG = LoggerFactory.getLogger(HistoryComparator.class);

    private final AuditLogManager auditlogManager;

    private Class<?> c;

    @Inject
    public HistoryComparator(AuditLogManager auditlogManager) {
        this.auditlogManager = auditlogManager;

    }

    private void addRelationField(Set<HistoryDifference> differencesUniques, Auditlog actual, Auditlog before,
            Auditlog principal, Map<String, String> specialList) throws ClassNotFoundException {
        Class<?> classRelation = Class.forName(actual.getEntityName().replace("class ", ""));
        String listName = this.getListName(classRelation);
        if (listName != null) {
            differencesUniques.add(new HistoryDifference(UUID.randomUUID().toString(), listName, true, "", ""));
        } else {
            if (actual != null && actual.getRelationName() != null) {
                String relationName = actual.getRelationName().replace(":" + principal.getEntityId(), "");
                if (specialList.containsKey(relationName)) {
                    differencesUniques.add(new HistoryDifference(UUID.randomUUID().toString(),
                            specialList.get(relationName), true, "", ""));
                }
            }

        }
    }

    private List<HistoryDifference> compareHistory(String jsonNew, String jsonOlder, String subFix) {
        List<HistoryDifference> myDifferences = new ArrayList<>();
        Gson g = new Gson();
        Type mapType = new TypeToken<Map<String, Object>>() {
        }.getType();

        JSONObject jsonObjNew = new JSONObject(jsonNew);

        JSONObject jsonObjOld = new JSONObject(jsonOlder);

        this.removeJSONField(jsonObjNew, "activeSince");
        this.removeJSONField(jsonObjOld, "activeSince");
        this.removeJSONField(jsonObjNew, "modifiedBy");
        this.removeJSONField(jsonObjOld, "modifiedBy");
        this.removeJSONField(jsonObjNew, "modificationJustification");
        this.removeJSONField(jsonObjOld, "modificationJustification");
        String subFixStr[] = subFix.split("\\.");
        for (String string : subFixStr) {
            this.removeJSONField(jsonObjNew, string);
            this.removeJSONField(jsonObjOld, string);
        }

        Map<String, Object> firstMap = g.fromJson(jsonObjNew.toString(), mapType);
        Map<String, Object> secondMap = g.fromJson(jsonObjOld.toString(), mapType);
        MapDifference<String, Object> comparable = Maps.difference(firstMap, secondMap);
        Map<String, ValueDifference<Object>> diferences = comparable.entriesDiffering();
        Map<String, Object> diferencesLeft = comparable.entriesOnlyOnLeft();
        diferences.forEach((k, v) -> myDifferences.add(new HistoryDifference(UUID.randomUUID().toString(), k, false,
                v.rightValue().toString(), v.leftValue().toString())));
        diferencesLeft.forEach((k, v) -> new HistoryDifference(UUID.randomUUID().toString(), k, true, "", ""));
        return myDifferences;
    }

    public List<HistoryDifference> getDifferences(String transactionID, Map<String, String> specialList,
            String subFix) throws ClassNotFoundException, NoSuchFieldException, SecurityException {

        Auditlog principal = auditlogManager.getAuditlog(transactionID);

        c = Class.forName(principal.getEntityName().replace("class ", ""));
        Set<HistoryDifference> differencesUniques = new HashSet<>();
        List<HistoryDifference> differences = new ArrayList<>();
        List<Auditlog> actualHistory = auditlogManager.getCompleteHistory(transactionID);
        List<Auditlog> beforeHistory = auditlogManager.getHistoryBefore(transactionID);

        if (!beforeHistory.isEmpty()) {
            for (Auditlog actual : actualHistory) {
                Auditlog before = this.getSimiliar(actual, beforeHistory);
                if (before == null) {
                    this.addRelationField(differencesUniques, actual, before, principal, specialList);
                } else {
                    switch (actual.getMain().intValue()) {
                    case 1:
                        differencesUniques.addAll(
                                this.compareHistory(actual.getEntityJson(), before.getEntityJson(), subFix));
                        break;

                    case 3:

                        List<HistoryDifference> diList = this.compareHistory(actual.getEntityJson(),
                                before.getEntityJson(), subFix);
                        if (!diList.isEmpty()) {
                            this.addRelationField(differencesUniques, actual, before, principal, specialList);
                        }

                        break;

                    }
                }
            }

        }

        for (HistoryDifference str : differencesUniques) {

            try {

                Field field = this.getField(str.getDifference());
                if (IAuditLog.class.isAssignableFrom(field.getType())) {
                    str.setDifference(subFix + "." + str.getDifference() + ".id");
                    str.setAdded(true);

                    differences.add(str);
                } else {

                    if (Date.class.isAssignableFrom(field.getType())) {
                        SimpleDateFormat formatter = new SimpleDateFormat("MMM dd, yyyy HH:mm:ss a");
                        SimpleDateFormat formatterSystem = new SimpleDateFormat("yyyy-MM-dd");
                        str.setNewValue(formatterSystem.format(formatter.parse(str.getNewValue())));
                        str.setOldValue(formatterSystem.format(formatter.parse(str.getOldValue())));

                        str.setDifference(subFix + "." + str.getDifference());

                    } else {
                        str.setDifference(subFix + "." + str.getDifference());
                    }

                    differences.add(str);

                }
            } catch (Exception e) {
                LOG.error("another thing went wrong???", e);
                str.setDifference(subFix + "." + str.getDifference());
                differences.add(str);
            }

        }
        return differences;

    }

    public List<HistoryDifference> getDifferencesList(IAuditLog iaAuditLog, String transactionID,
            Map<String, String> specialList, String subFix, String subFixDelete, int levels)
            throws ClassNotFoundException, NoSuchFieldException, SecurityException {
        List<HistoryDifference> differences = new ArrayList<>();
        // This should be refactored so that the auditLog is retrieved from the Hibernate 1st level cache. (Assuming we have
        // the id).
        Auditlog principal = auditlogManager.getAuditlog(transactionID, iaAuditLog);
        if (principal != null) {
            c = Class.forName(principal.getEntityName().replace("class ", ""));
            Set<HistoryDifference> differencesUniques = new HashSet<>();

            // This should be refactored so that the auditLog is retrieved from the Hibernate 1st level cache. (Assuming we
            // have the id).
            List<Auditlog> beforeHistory = auditlogManager.getHistoryBeforeList(transactionID,
                    iaAuditLog.getClass().toString(), iaAuditLog.getId().toString());

            if (!beforeHistory.isEmpty()) {
                Auditlog actual = principal;
                {
                    Auditlog before = this.getSimiliar(actual, beforeHistory);
                    if (before == null) {
                        differencesUniques
                                .add(new HistoryDifference(UUID.randomUUID().toString(), "id", true, "", ""));
                    } else {

                        List<HistoryDifference> diffrencesFields = this.compareHistory(actual.getEntityJson(),
                                before.getEntityJson(), subFixDelete);

                        if (!diffrencesFields.isEmpty()) {
                            differencesUniques.addAll(diffrencesFields);
                            differencesUniques
                                    .add(new HistoryDifference(UUID.randomUUID().toString(), "id", true, "", ""));
                        }

                    }
                }

            } else {
                differencesUniques.add(new HistoryDifference(UUID.randomUUID().toString(), "id", true, "", ""));
            }

            String parent = "";
            try {
                String subFixSplit[] = subFix.split("\\.");

                for (int i = 0; i < levels && levels > 1; i++) {
                    if (parent.isEmpty()) {
                        parent = subFixSplit[i];
                    } else {
                        parent = parent + "." + subFixSplit[i];
                    }
                }
            } catch (Exception e) {
                LOG.error("unable to split a string I guess?", e);
                /**
                 * Original code swallows the exception and didn't even log it. Now we at least log it,
                 * but we need to revisit to see if we should continue processing or re-throw the exception.
                 */
            }
            for (HistoryDifference str : differencesUniques) {
                try {
                    Field field = this.getField(str.getDifference());
                    if (str.getDifference().equals("id")) {
                        differences.add(
                                new HistoryDifference(UUID.randomUUID().toString(), parent + ".id", true, "", ""));
                    }
                    if (IAuditLog.class.isAssignableFrom(field.getType())) {
                        str.setDifference(subFix + "." + str.getDifference() + ".id");
                        str.setAdded(true);

                    } else {
                        if (Date.class.isAssignableFrom(field.getType())) {
                            SimpleDateFormat formatter = new SimpleDateFormat("MMM dd, yyyy HH:mm:ss a");
                            SimpleDateFormat formatterSystem = new SimpleDateFormat("yyyy-MM-dd");
                            str.setNewValue(formatterSystem.format(formatter.parse(str.getNewValue())));
                            str.setOldValue(formatterSystem.format(formatter.parse(str.getOldValue())));

                            str.setDifference(subFix + "." + str.getDifference());

                        } else {
                            str.setDifference(subFix + "." + str.getDifference());
                        }

                    }
                } catch (Exception e) {
                    LOG.error("not sure but something went wrong", e);
                    str.setDifference(subFix + "." + str.getDifference());
                }
                differences.add(str);

            }

        }

        return differences;
    }

    public Field getField(String str) {
        Field[] fields = c.getDeclaredFields();

        for (Field field : fields) {
            if (field.getName().equals(str)) {
                return field;
            }
        }
        return null;
    }

    private String getListName(Class<?> objectClass) {

        Field[] fields = c.getDeclaredFields();

        for (Field field : fields) {

            try {
                ParameterizedType integerListType = (ParameterizedType) field.getGenericType();
                Class<?> integerListClass = (Class<?>) integerListType.getActualTypeArguments()[0];
                if (integerListClass.equals(objectClass) && field.getType().getSimpleName().equals("List")) {
                    return field.getName();
                }

            } catch (Exception e) {
                LOG.error("unable to return matching field: " + field, e);
                /**
                 * Original code swallows the exception and didn't even log it. Now we at least log it,
                 * but we need to revisit to see if we should continue processing or re-throw the exception.
                 */
            }
        }
        return null;
    }

    private Auditlog getSimiliar(Auditlog actual, List<Auditlog> beforeHistory) {
        for (Auditlog before : beforeHistory) {
            switch (actual.getMain().intValue()) {
            case 1:
                if (before.getMain().intValue() == 1) {
                    return before;
                }
                break;

            case 3:
                if (before.getMain().intValue() == 3) {
                    if (before.getRelationName().equals(actual.getRelationName())
                            && before.getEntityId().equals(actual.getEntityId())) {
                        return before;
                    }
                }
                break;
            }

        }
        return null;

    }

    private void removeJSONField(JSONObject obj, String attribute) throws JSONException {
        obj.remove(attribute);

        Iterator<String> it = obj.keys();
        while (it.hasNext()) {
            String key = it.next();
            Object childObj = obj.get(key);
            if (childObj instanceof JSONArray) {
                JSONArray arrayChildObjs = ((JSONArray) childObj);
                int size = arrayChildObjs.length();
                for (int i = 0; i < size; i++) {
                    this.removeJSONField(arrayChildObjs.getJSONObject(i), attribute);
                }
            }
            if (childObj instanceof JSONObject) {
                this.removeJSONField(((JSONObject) childObj), attribute);

            }

        }
    }
}