org.codice.ddf.catalog.ui.metacard.associations.Associated.java Source code

Java tutorial

Introduction

Here is the source code for org.codice.ddf.catalog.ui.metacard.associations.Associated.java

Source

/**
 * Copyright (c) Codice Foundation
 *
 * <p>This is free software: you can redistribute it and/or modify it under the terms of the GNU
 * Lesser General Public License as published by the Free Software Foundation, either version 3 of
 * the License, or any later version.
 *
 * <p>This program 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 Lesser General Public License for more details. A copy of the GNU Lesser General Public
 * License is distributed along with this program and can be found at
 * <http://www.gnu.org/licenses/lgpl.html>.
 */
package org.codice.ddf.catalog.ui.metacard.associations;

import static ddf.catalog.util.impl.ResultIterable.resultIterable;

import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import ddf.catalog.CatalogFramework;
import ddf.catalog.core.versioning.DeletedMetacard;
import ddf.catalog.core.versioning.MetacardVersion;
import ddf.catalog.data.Attribute;
import ddf.catalog.data.Metacard;
import ddf.catalog.data.Result;
import ddf.catalog.data.impl.AttributeImpl;
import ddf.catalog.federation.FederationException;
import ddf.catalog.operation.impl.QueryImpl;
import ddf.catalog.operation.impl.QueryRequestImpl;
import ddf.catalog.operation.impl.UpdateRequestImpl;
import ddf.catalog.source.IngestException;
import ddf.catalog.source.SourceUnavailableException;
import ddf.catalog.source.UnsupportedQueryException;
import ddf.catalog.util.impl.ResultIterable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.codice.ddf.catalog.ui.util.EndpointUtil;
import org.opengis.filter.Filter;
import org.opengis.filter.sort.SortBy;

public class Associated {

    private static final Set<String> ASSOCIATION_TYPES = ImmutableSet.of(Metacard.DERIVED, Metacard.RELATED);

    private final EndpointUtil util;

    private final CatalogFramework catalogFramework;

    private static int pageSize = 250;

    public Associated(EndpointUtil util, CatalogFramework catalogFramework) {
        this.util = util;
        this.catalogFramework = catalogFramework;
    }

    public Collection<Edge> getAssociations(String metacardId)
            throws UnsupportedQueryException, SourceUnavailableException, FederationException {
        Map<String, Metacard> metacardMap = query(withNonrestrictedTags(forRootAndParents(metacardId)));
        if (metacardMap.isEmpty()) {
            return Collections.emptyList();
        }
        Metacard root = metacardMap.get(metacardId);
        Collection<Metacard> parents = metacardMap.values().stream().filter(m -> !m.getId().equals(metacardId))
                .collect(Collectors.toList());

        Map<String, Metacard> childMetacardMap = query(withNonrestrictedTags(forChildAssociations(root)));

        Collection<Edge> parentEdges = createParentEdges(parents, root);
        Collection<Edge> childrenEdges = createChildEdges(childMetacardMap.values(), root);

        Collection<Edge> edges = Stream.of(parentEdges, childrenEdges).flatMap(Collection::stream)
                .collect(Collectors.toList());
        return edges;
    }

    public void putAssociations(String id, Collection<Edge> edges)
            throws UnsupportedQueryException, SourceUnavailableException, FederationException, IngestException {
        Collection<Edge> oldEdges = getAssociations(id);

        List<String> ids = Stream.concat(oldEdges.stream(), edges.stream())
                .flatMap(e -> Stream.of(e.child, e.parent)).filter(Objects::nonNull).map(m -> m.get(Metacard.ID))
                .filter(Objects::nonNull).map(Object::toString).distinct().collect(Collectors.toList());

        Map<String, Metacard> metacards = util.getMetacardsWithTagById(ids, getNonrestrictedTagsFilter()).entrySet()
                .stream().collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().getMetacard()));

        Map<String, Metacard> changedMetacards = new HashMap<>();
        Set<Edge> oldEdgeSet = new HashSet<>(oldEdges);
        Set<Edge> newEdgeSet = new HashSet<>(edges);

        Set<Edge> oldDiff = Sets.difference(oldEdgeSet, newEdgeSet);
        Set<Edge> newDiff = Sets.difference(newEdgeSet, oldEdgeSet);

        for (Edge edge : oldDiff) {
            removeEdge(edge, metacards, changedMetacards);
        }
        for (Edge edge : newDiff) {
            addEdge(edge, metacards, changedMetacards);
        }

        if (changedMetacards.isEmpty()) {
            return;
        }

        catalogFramework.update(new UpdateRequestImpl(changedMetacards.keySet().toArray(new String[0]),
                new ArrayList<>(changedMetacards.values())));
    }

    private void removeEdge(Edge edge, Map<String, Metacard> metacards,
            /*Mutable*/ Map<String, Metacard> changedMetacards) {
        String id = edge.parent.get(Metacard.ID).toString();
        Metacard target = changedMetacards.getOrDefault(id, metacards.get(id));
        ArrayList<String> values = Optional.of(target).map(m -> m.getAttribute(edge.relation))
                .map(Attribute::getValues).map(util::getStringList).orElseGet(ArrayList::new);
        values.remove(edge.child.get(Metacard.ID).toString());
        target.setAttribute(new AttributeImpl(edge.relation, values));
        changedMetacards.put(id, target);
    }

    private void addEdge(Edge edge, Map<String, Metacard> metacards, Map<String, Metacard> changedMetacards) {
        String id = edge.parent.get(Metacard.ID).toString();
        Metacard target = changedMetacards.getOrDefault(id, metacards.get(id));
        ArrayList<String> values = Optional.of(target).map(m -> m.getAttribute(edge.relation))
                .map(Attribute::getValues).map(util::getStringList).orElseGet(ArrayList::new);
        values.add(edge.child.get(Metacard.ID).toString());
        target.setAttribute(new AttributeImpl(edge.relation, values));
        changedMetacards.put(id, target);
    }

    private Collection<Edge> createChildEdges(Collection<Metacard> children, Metacard root) {
        List<Edge> edges = new ArrayList<>();
        for (Metacard child : children) {
            List<String> relations = getRelationsToChild(root, child);
            edges.addAll(relations.stream().map(relation -> new Edge(root, child, relation))
                    .collect(Collectors.toList()));
        }
        return edges;
    }

    private Collection<Edge> createParentEdges(Collection<Metacard> parents, Metacard root) {
        List<Edge> edges = new ArrayList<>();
        for (Metacard parent : parents) {
            List<String> relations = getRelationsToChild(parent, root);
            edges.addAll(relations.stream().map(relation -> new Edge(parent, root, relation))
                    .collect(Collectors.toList()));
        }
        return edges;
    }

    private Filter forRootAndParents(String rootId) {
        Filter root = util.getFilterBuilder().attribute(Metacard.ID).is().equalTo().text(rootId);
        Filter related = util.getFilterBuilder().attribute(Metacard.RELATED).is().like().text(rootId);
        Filter derived = util.getFilterBuilder().attribute(Metacard.DERIVED).is().like().text(rootId);
        Filter parents = util.getFilterBuilder().anyOf(related, derived);

        return util.getFilterBuilder().anyOf(root, parents);
    }

    private Filter withNonrestrictedTags(Filter filter) {
        if (filter == null) {
            return null;
        }
        return util.getFilterBuilder().allOf(filter, getNonrestrictedTagsFilter());
    }

    private Filter getNonrestrictedTagsFilter() {
        return util.getFilterBuilder().not(util.getFilterBuilder().anyOf(
                util.getFilterBuilder().attribute(Metacard.TAGS).is().like().text(DeletedMetacard.DELETED_TAG),
                util.getFilterBuilder().attribute(Metacard.TAGS).is().like().text(MetacardVersion.VERSION_TAG)));
    }

    private Filter forChildAssociations(Metacard metacard) {
        Set<String> childIds = ASSOCIATION_TYPES.stream().map(metacard::getAttribute).filter(Objects::nonNull)
                .map(Attribute::getValues).filter(Objects::nonNull).flatMap(Collection::stream)
                .filter(String.class::isInstance).map(String.class::cast).collect(Collectors.toSet());

        if (childIds.isEmpty()) {
            return null;
        }
        return util.getFilterBuilder()
                .anyOf(childIds.stream()
                        .map(id -> util.getFilterBuilder().attribute(Metacard.ID).is().equalTo().text(id))
                        .collect(Collectors.toList()));
    }

    private Map<String, Metacard> query(Filter filter)
            throws UnsupportedQueryException, SourceUnavailableException, FederationException {
        if (filter == null) {
            return Collections.emptyMap();
        }

        ResultIterable resultIterable = resultIterable(catalogFramework, new QueryRequestImpl(
                new QueryImpl(filter, 1, pageSize, SortBy.NATURAL_ORDER, false, TimeUnit.SECONDS.toMillis(30)),
                false));
        return resultIterable.stream().map(Result::getMetacard)
                .collect(Collectors.toMap(Metacard::getId, Function.identity()));
    }

    private List<String> getRelationsToChild(Metacard parent, Metacard child) {
        List<String> relations = new ArrayList<>();
        for (String associationType : ASSOCIATION_TYPES) {
            if (Optional.of(parent).map(m -> m.getAttribute(associationType)).map(Attribute::getValues)
                    .map(util::getStringList).map(l -> l.contains(child.getId())).orElse(false)) {
                relations.add(associationType);
            }
        }

        return relations;
    }

    public class Edge {
        private Map<String, Object> parent;

        private Map<String, Object> child;

        private String relation;

        public Edge(Metacard parent, Metacard child, String relation) {
            this.parent = util.getMetacardMap(parent);
            this.child = util.getMetacardMap(child);
            this.relation = relation;
        }

        public int hashCode() {
            return new HashCodeBuilder().append(parent.get(Metacard.ID).toString())
                    .append(child.get(Metacard.ID).toString()).append(relation).build();
        }

        public boolean equals(Object o) {
            if (o == null) {
                return false;
            }
            if (o == this) {
                return true;
            }
            if (!(o instanceof Edge)) {
                return false;
            }
            Edge rhs = (Edge) o;
            return new EqualsBuilder()
                    .append(parent.get(Metacard.ID).toString(), rhs.parent.get(Metacard.ID).toString())
                    .append(child.get(Metacard.ID).toString(), rhs.child.get(Metacard.ID).toString())
                    .append(relation, rhs.relation).build();
        }

        @Override
        public String toString() {
            return String.format("%s [%s]-> %s", parent.get("id").toString(), relation, child.get("id").toString());
        }
    }
}