/** * 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.plugin.metacard.util; import static org.apache.commons.lang.Validate.notNull; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; import com.google.common.collect.ImmutableList; import ddf.catalog.data.Attribute; import ddf.catalog.data.AttributeDescriptor; import ddf.catalog.data.Metacard; import ddf.catalog.data.MetacardType; import ddf.catalog.data.impl.MetacardImpl; import ddf.catalog.data.impl.MetacardTypeImpl; /** * Support and bulk operations for {@link Metacard}s. Contains state relevant to the provided * services. */ public class MetacardServices { private final List<MetacardType> systemMetacardTypes; /** * Initializes the services with an empty list of system {@link MetacardType}s. */ public MetacardServices() { this.systemMetacardTypes = new ArrayList<>(); } /** * Initializes the services with the provided list of {@link MetacardType}s. * * @param systemMetacardTypes The list of metacard types to use. */ public MetacardServices(List<MetacardType> systemMetacardTypes) { this.systemMetacardTypes = systemMetacardTypes; } /** * Returns a new list of new {@link Metacard}s created from the {@param metacards} collection and * applies each attribute in the {@param attributeMap} only if the attribute is not already set on the * {@link Metacard}. That is, the attribute must be {@code null} or not in the {@link Metacard}'s * map. Appropriate {@link AttributeDescriptor}s are injected into a new {@link MetacardType} for * each new {@link Metacard} created. * * @param metacards The list of metacards to attempt to add the attributes to. Can be empty. * @param attributeMap The map of attributes to attempt to add. Attributes already set on any * given {@link Metacard} will not be changed. For multi-valued attributes, * the value string should separate different entities with commas. * @param attributeFactory The factory to use to create attributes. */ public List<Metacard> setAttributesIfAbsent(List<Metacard> metacards, Map<String, String> attributeMap, AttributeFactory attributeFactory) { notNull(metacards, "The list of metacards cannot be null"); notNull(attributeMap, "The map of new attributes cannot be null"); notNull(attributeFactory, "The attribute factory cannot be null"); if (metacards.isEmpty() || attributeMap.isEmpty()) { return metacards; } Map<String, AttributeDescriptor> systemAndMetacardDescriptors = getUniqueSystemAndMetacardDescriptors(metacards); return metacards.stream() .map(metacard -> { Set<AttributeDescriptor> relevantDescriptors = attributeMap.keySet() .stream() .filter(key -> metacard.getAttribute(key) == null) .map(systemAndMetacardDescriptors::get) .filter(Objects::nonNull) .collect(Collectors.toSet()); return createNewMetacardWithInjectedAttributes(metacard, relevantDescriptors.stream() .map(descriptor -> attributeFactory.createAttribute(descriptor, attributeMap.get(descriptor.getName()))) .collect(Collectors.toList()), relevantDescriptors); }) .collect(Collectors.toList()); } /** * Helper method to combine all system-recognized {@link AttributeDescriptor}s with any new * ones on the given list of {@link Metacard}s without repeats. * * @param metacards List of metacards whose attribute descriptors should be added to the map. * @return A map of all system-recognized descriptors plus any new ones introduced by the metacards. */ private Map<String, AttributeDescriptor> getUniqueSystemAndMetacardDescriptors( List<Metacard> metacards) { List<MetacardType> systemMetacardTypesCopy = ImmutableList.copyOf(systemMetacardTypes); return Stream.concat(systemMetacardTypesCopy.stream() .map(MetacardType::getAttributeDescriptors) .flatMap(Set::stream) .filter(Objects::nonNull), metacards.stream() .map(Metacard::getMetacardType) .map(MetacardType::getAttributeDescriptors) .flatMap(Set::stream) .filter(Objects::nonNull)) .collect(Collectors.toMap(AttributeDescriptor::getName, Function.identity(), (oldValue, newValue) -> oldValue)); } /** * Creates a new metacard from the original with all original attributes plus the new ones given * in the list. A new metacard type is created for this new metacard using the original type and * the given descriptors. */ private Metacard createNewMetacardWithInjectedAttributes(Metacard originalMetacard, List<Attribute> attributes, Set<AttributeDescriptor> descriptors) { MetacardImpl newMetacard; if (descriptors.isEmpty()) { newMetacard = new MetacardImpl(originalMetacard); } else { MetacardTypeImpl injectedAttributeType = new MetacardTypeImpl(originalMetacard.getMetacardType() .getName(), originalMetacard.getMetacardType(), descriptors); newMetacard = new MetacardImpl(originalMetacard, injectedAttributeType); } attributes.forEach(newMetacard::setAttribute); return newMetacard; } }