All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.activitystream.model.aspects.AbstractMapAspect Maven / Gradle / Ivy

package com.activitystream.model.aspects;

import com.activitystream.model.ASConstants;
import com.activitystream.model.core.AbstractMapElement;
import com.activitystream.model.interfaces.AspectInterface;
import com.activitystream.model.interfaces.BaseStreamElement;
import com.activitystream.model.entities.BusinessEntity;
import com.activitystream.model.entities.EntityChangeMap;
import com.activitystream.model.entities.EntityReference;
import com.activitystream.model.validation.InvalidPropertyContentError;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

@SuppressWarnings("unchecked")
public abstract class AbstractMapAspect extends AbstractMapElement implements AspectInterface {

    protected static final Logger logger = LoggerFactory.getLogger(AbstractMapAspect.class);

    boolean autoGenerated = false;

    public AbstractMapAspect() {
    }

    public AbstractMapAspect(Map values, BaseStreamElement root) {
        super(values, root);
    }

    @Override
    public void markAsAutoGenerated() {
        this.autoGenerated = true;
    }

    @Override
    public boolean isAutoGenerated() {
        return this.autoGenerated;
    }

    @Override
    public boolean isInherited() {
        return containsKey(ASConstants.FIELD_INHERITED_VIA);
    }

    @Override
    public void loadFromValue(Object value) {
        if (value instanceof Map) {
            setMapValues((Map) value);
        } else if (value != null) {
            validator().addProblem(new InvalidPropertyContentError(
                    "Can not create aspect '" + getAspectSignature() + "' from " + value.getClass() + " (" + value + ")"));
        }
    }

    private boolean conflicts(Map values) {
        for (Map.Entry entry : values.entrySet()) {
            Object current = get(entry.getKey());
            if (current != null && !current.equals(entry.getValue()))
                return true;
        }
        return false;
    }

    private void setInheritancePath(Set newKeys, List> newPath) {
        if (newKeys.containsAll(keySet()))
            setInheritedVia(newPath);
        else {
            List> prevPath =
                    (List>) getOrDefault(ASConstants.FIELD_INHERITED_VIA, Collections.emptyList());
            List> commonPath = commonPrefix(newPath, prevPath);
            setInheritedVia(commonPath);

            if (!newPath.equals(commonPath)) {
                setMergePath(newKeys, newPath);
            }
            if (!prevPath.equals(commonPath)) {
                setMergePath(keySet(), prevPath);
            }
        }
    }

    private void setMergePath(Set keys, List> path) {
        Map>> mergedFields =
                (Map>>) computeIfAbsent(ASConstants.FIELD_MERGED_FIELDS, i -> new HashMap<>());
        keys.stream()
                .filter(key -> !key.startsWith("_"))
                .forEach(key -> mergedFields.putIfAbsent(key, path));
    }

    private void setInheritedVia(List> path) {
        if (path.isEmpty())
            super.remove(ASConstants.FIELD_INHERITED_VIA);
        else
            super.put(ASConstants.FIELD_INHERITED_VIA, path);
    }

    private List> commonPrefix(List> path1, List> path2) {
        int max = Math.min(path1.size(), path2.size());
        int common = 0;
        while (common < max && path1.get(common).equals(path2.get(common))) {
            common++;
        }
        return path1.subList(0, common);
    }

    @Override
    public void mergeAspect(AspectInterface aspect) {
        if (shouldMerge())
            ((AbstractMapAspect) aspect).forEach(this::putIfAbsent);
    }

    private boolean shouldMerge() {
        if (getAspectType().getMergeStrategy() == null) {
            logger.warn("Merge strategy not defined for aspect {}", getAspectSignature());
            return false;
        }
        return getAspectType().getMergeStrategy() != AspectType.MergeStrategy.REPLACE;
    }

    protected void collectValuesToSave(Map values) {
        Map mergedFields = (Map) getOrDefault(ASConstants.FIELD_MERGED_FIELDS, Collections.emptyMap());

        for (Map.Entry entry : ((Map) this).entrySet()) {
            if (entry.getKey().startsWith("_"))
                continue;

            if (mergedFields.containsKey(entry.getKey()))
                continue;

            if (entry.getValue() instanceof String) {
                String valueString = (String) entry.getValue();
                if (valueString.equals("null") || valueString.equals("_del") || valueString.trim().isEmpty()) {
                    values.remove(entry.getKey());
                    continue;
                }
            }

            values.put(entry.getKey(), entry.getValue());
        }
    }

    protected void handleChanges(Map oldValues, Map newValues) {
    }

    void registerChanges(Map oldValues, Map newValues, EntityChangeMap.ACTION triggersReIndexing,
                         EntityChangeMap.ACTION triggersTimeSeriesUpdate) {
        if (getRoot() instanceof BusinessEntity) {
            Map mapChanges = getMapChanges(oldValues, newValues);
            if (mapChanges != null) {
                EntityChangeMap entityChangeMap = new EntityChangeMap(getAspectSignature(), triggersReIndexing, triggersTimeSeriesUpdate);
                entityChangeMap.putAll(mapChanges);
                ((BusinessEntity) getRoot()).registerChanges(entityChangeMap);
            }
        }
    }

    private Object convertFromDocumentValue(Object value) {
        if (value instanceof Date)
            return new DateTime(value);

        // Copy any collections so that updates to the loaded objects will not be written automatically to the graph via OTrackedMap and friends.
        if (value instanceof List) {
            List list = (List) value;
            List copy = new ArrayList<>(list.size());

            for (Object entry : list) {
                copy.add(convertFromDocumentValue(entry));
            }
            return copy;
        }

        if (value instanceof Map) {
            Map map = (Map) value;
            Map copy = new HashMap<>();

            for (Map.Entry entry : map.entrySet()) {
                copy.put(entry.getKey(), convertFromDocumentValue(entry.getValue()));
            }
            return copy;
        }

        return value;
    }

    private String getAspectSignature() {
        return getAspectType().getAspectSignature();
    }
}