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

com.cinchapi.common.collect.MergeStrategies Maven / Gradle / Ivy

Go to download

Accent4J is a suite of libraries, helpers and data structures that make Java programming idioms more fluent.

There is a newer version: 1.13.1
Show newest version
/*
 * Copyright (c) 2013-2018 Cinchapi Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * 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 com.cinchapi.common.collect;

import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.Nullable;

import com.google.common.collect.Lists;
import com.google.common.collect.Sets;

/**
 * A collection of strategies for
 * {@link Association#merge(java.util.Map, java.util.function.BiFunction)} and
 * {@link java.util.Map#merge(Object, Object, java.util.function.BiFunction)}.
 *
 * @author Jeff Nelson
 */
public final class MergeStrategies {

    private MergeStrategies() {/* no-init */}

    /**
     * Return a merge strategy that uses the incoming value if there is a
     * conflict with an existing value in the into {@link Map}.
     * 
     * @return the result of applying the strategy
     */
    public static Object theirs(Object ours, Object theirs) {
        return theirs;
    }

    /**
     * Return a merge strategy that uses the existing value if there is a
     * conflict with an incoming value in the from {@link Map}.
     * 
     * @return the result of applying the strategy
     */
    public static Object ours(Object ours, Object theirs) {
        return ours;
    }

    /**
     * Return a merge strategy that will concatenate the stored and incoming
     * values if there is a conflict between the into and from
     * {@link Map maps}.
     * 

* This strategy will map the conflicting key to a collection that contains * the value from the into map (or values if the value is a * collection) followed by the value (or values) from the from map. *

* * @return the result of applying the strategy */ @SuppressWarnings("unchecked") public static Object concat(Object ours, Object theirs) { if(ours instanceof Collection && theirs instanceof Collection) { ((Collection) ours).addAll((Collection) theirs); return ours; } else if(ours instanceof Collection) { ((Collection) ours).add(theirs); return ours; } else if(theirs instanceof Collection) { List merged = Lists.newArrayList(ours); merged.addAll((Collection) theirs); return merged; } else { return Lists.newArrayList(ours, theirs); } } /** * "Upsert" from {@code ours} into {@code theirs}. *

* The nature of the implementation changes depending on the types of the * two objects. In general, this method attempts to "merge" objects * (depending upon a definition of merging that makes sense for the object * type) in a manner that prefers {@code theirs} if there's ever a * conflict with {@code ours} data. The rules of upsertion within this * method are that *

*
    *
  • If the two objects don't have the same type, then {@code #theirs} is * returned if it is not null; otherwise {@code ours} is returned.
  • *
  • If both objects are {@link Collection collections}, the items from * {@code theirs} are preferred if there is a corresponding item in the same * position in {@code ours}. If the item at a position in {@code theirs} is * {@code null}, but there is a non-null item in the corresponding position * in {@code ours}, the item in {@code ours} is preferred.
  • *
  • If both objects are {@link Map maps}, this function will perform a * {@link Map#merge(String, Object, BiFunction} of the data in * {@code theirs} * to {@code ours} with a merge function that recursively calls this * method.
  • *
* * @param ours * @param theirs * * * @return the object after the upset or {@code null} if both {@code theirs} * and {@code ours} are null */ @SuppressWarnings("unchecked") @Nullable public static Object upsert(Object ours, Object theirs) { if(theirs instanceof Collection && ours instanceof Collection) { Iterator tit = ((Collection) theirs).iterator(); Iterator oit = ((Collection) ours).iterator(); Collection collection = theirs instanceof Set ? Sets.newLinkedHashSet() : Lists.newArrayList(); while (tit.hasNext()) { if(oit.hasNext()) { Object f = tit.next(); Object i = oit.next(); collection.add(upsert(i, f)); } else { break; } } while (oit.hasNext()) { collection.add(oit.next()); } while (tit.hasNext()) { collection.add(tit.next()); } return collection; } else if(theirs instanceof Map && ours instanceof Map) { // Note the #upsert #into the destination map, happens in-place and // the same is returned. ((Map) theirs).forEach((key, value) -> { if(value == null) { // The merge function prevents upserting a null value, so // perform an explicit put as a workaround. ((Map) ours).put(key, value); } else { ((Map) ours).merge(key, value, MergeStrategies::upsert); } }); return ours; } else { return theirs != null ? theirs : ours; } } }