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

org.dbflute.util.DfCollectionUtil Maven / Gradle / Ivy

There is a newer version: 1.2.8
Show newest version
/*
 * Copyright 2014-2023 the original author or authors.
 *
 * 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 org.dbflute.util;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author jflute
 * @since 0.9.4 (2009/03/20 Friday)
 */
public class DfCollectionUtil {

    // ===================================================================================
    //                                                                          Definition
    //                                                                          ==========
    // Collections's empty collections can be called clear()
    // (unmodifiable used before 1.1)
    private static final List EMPTY_LIST = Collections.emptyList();
    private static final Map EMPTY_MAP = Collections.emptyMap();
    private static final Set EMPTY_SET = Collections.emptySet();

    // ===================================================================================
    //                                                                          Collection
    //                                                                          ==========
    public static Class findFirstElementType(Collection collection) {
        for (Object object : collection) {
            if (object != null) {
                return object.getClass();
            }
        }
        return null;
    }

    public static boolean hasValidElement(Collection collection) {
        for (Object object : collection) {
            if (object != null) {
                return true;
            }
        }
        return false;
    }

    // ===================================================================================
    //                                                                                List
    //                                                                                ====
    public static  ArrayList newArrayList() {
        return new ArrayList();
    }

    public static  ArrayList newArrayList(Collection elements) {
        final ArrayList list = newArrayListSized(elements.size());
        list.addAll(elements);
        return list;
    }

    @SafeVarargs
    public static  ArrayList newArrayList(ELEMENT... elements) {
        final ArrayList list = newArrayListSized(elements.length);
        for (ELEMENT element : elements) {
            list.add(element);
        }
        return list;
    }

    public static  ArrayList newArrayListSized(int size) {
        return new ArrayList(size);
    }

    @SuppressWarnings("unchecked")
    public static  List emptyList() {
        return (List) EMPTY_LIST;
    }

    // -----------------------------------------------------
    //                                               Utility
    //                                               -------
    public static  List> splitByLimit(List elementList, int limit) {
        final List> valueList = newArrayList();
        final int valueSize = elementList.size();
        int index = 0;
        int remainderSize = valueSize;
        do {
            final int beginIndex = limit * index;
            final int endPoint = beginIndex + limit;
            final int endIndex = limit <= remainderSize ? endPoint : valueSize;
            final List splitList = newArrayList();
            splitList.addAll(elementList.subList(beginIndex, endIndex));
            valueList.add(splitList);
            remainderSize = valueSize - endIndex;
            ++index;
        } while (remainderSize > 0);
        return valueList;
    }

    // -----------------------------------------------------
    //                                               Advance
    //                                               -------
    public static  OrderDiff analyzeOrderDiff(List beforeUniqueList, List afterUniqueList) {
        final LinkedHashSet linkedBeforeSet = newLinkedHashSet(beforeUniqueList);
        if (beforeUniqueList.size() != linkedBeforeSet.size()) {
            String msg = "The argument 'beforeList' should be unique: " + beforeUniqueList;
            throw new IllegalArgumentException(msg);
        }
        final LinkedHashSet linkedAfterSet = newLinkedHashSet(afterUniqueList);
        if (afterUniqueList.size() != linkedAfterSet.size()) {
            String msg = "The argument 'afterList' should be unique: " + afterUniqueList;
            throw new IllegalArgumentException(msg);
        }
        final ElementDiff elementDiff = analyzeElementDiff(linkedBeforeSet, linkedAfterSet);
        final Set addedSet = elementDiff.getAddedSet();
        final Set deletedSet = elementDiff.getDeletedSet();
        final List beforeRemainingList = newArrayList(beforeUniqueList);
        beforeRemainingList.removeAll(deletedSet);
        final List afterRemainingList = newArrayList(afterUniqueList);
        afterRemainingList.removeAll(addedSet);
        if (beforeRemainingList.size() != afterRemainingList.size()) {
            String msg = "The beforeRemainingList.size() should be the same as the afterRemainingList's:";
            msg = msg + " beforeRemainingList.size()=" + beforeRemainingList.size();
            msg = msg + " afterRemainingList.size()=" + afterRemainingList.size();
            throw new IllegalStateException(msg);
        }
        final Map> movedMap = newLinkedHashMap();
        doAnalyzeOrderChange(beforeRemainingList, afterRemainingList, movedMap);
        for (Entry> entry : movedMap.entrySet()) {
            final ELEMENT movedElement = entry.getKey();
            final ELEMENT previousElement = entry.getValue().getPreviousElement();
            final int movedIndex = afterUniqueList.indexOf(movedElement);
            final ELEMENT realPrevious = afterUniqueList.get(movedIndex - 1);
            if (!previousElement.equals(realPrevious)) {
                entry.getValue().setPreviousElement(realPrevious);
            }
        }
        final OrderDiff orderDiff = new OrderDiff();
        orderDiff.setMovedMap(movedMap);
        return orderDiff;
    }

    protected static  void doAnalyzeOrderChange(List beforeRemainingList, List afterRemainingList,
            Map> movedMap) {
        ELEMENT movedElement = null;
        ELEMENT previousElement = null;
        for (int i = 0; i < beforeRemainingList.size(); i++) {
            final ELEMENT beforeElement = beforeRemainingList.get(i);
            final int afterIndex = afterRemainingList.indexOf(beforeElement);
            if (i == afterIndex) { // no change
                continue;
            }
            // changed
            movedElement = beforeElement;
            previousElement = afterRemainingList.get(afterIndex - 1);
            break;
        }
        if (movedElement != null) {
            final OrderDiffDetail diffResult = new OrderDiffDetail();
            diffResult.setMovedElement(movedElement);
            diffResult.setPreviousElement(previousElement);
            movedMap.put(movedElement, diffResult);
            final List movedList = moveElementToIndex(beforeRemainingList, movedElement, previousElement);
            doAnalyzeOrderChange(movedList, afterRemainingList, movedMap); // recursive call
        }
    }

    public static class OrderDiff {
        protected Map> _movedMap;

        public Map> getMovedMap() {
            return _movedMap;
        }

        public void setMovedMap(Map> movedMap) {
            this._movedMap = movedMap;
        }
    }

    public static class OrderDiffDetail {
        protected ELEMENT _movedElement;
        protected ELEMENT _previousElement;

        public ELEMENT getMovedElement() {
            return _movedElement;
        }

        public void setMovedElement(ELEMENT movedElement) {
            this._movedElement = movedElement;
        }

        public ELEMENT getPreviousElement() {
            return _previousElement;
        }

        public void setPreviousElement(ELEMENT previousElement) {
            this._previousElement = previousElement;
        }
    }

    public static  List moveElementToIndex(List list, ELEMENT fromElement, ELEMENT toElement) {
        assertObjectNotNull("list", list);
        final int fromIndex = list.indexOf(fromElement);
        final int toIndex = list.indexOf(toElement);
        return moveElementToIndex(list, fromIndex, toIndex);
    }

    public static  List moveElementToIndex(List list, int fromIndex, int toIndex) {
        assertObjectNotNull("list", list);
        if (fromIndex == toIndex) {
            String msg = "The argument 'fromIndex' and 'toIndex' should not be same:";
            msg = msg + " fromIndex=" + fromIndex + " toIndex" + toIndex;
            throw new IllegalArgumentException(msg);
        }
        if (fromIndex < 0 || toIndex < 0) {
            String msg = "The argument 'fromIndex' and 'toIndex' should not be minus:";
            msg = msg + " fromIndex=" + fromIndex + " toIndex" + toIndex;
            throw new IllegalArgumentException(msg);
        }
        final boolean fromLess = fromIndex < toIndex;
        final List movedList = new ArrayList();
        final int firstIndex = fromLess ? fromIndex : toIndex;
        final int secondIndex = !fromLess ? fromIndex : toIndex;
        final List first = list.subList(0, firstIndex);
        final ELEMENT element = list.get(fromIndex);
        final int adjustmentIndex = fromLess ? 1 : 0;
        final List middle = list.subList(firstIndex + adjustmentIndex, secondIndex + adjustmentIndex);
        final List last = list.subList(secondIndex + 1, list.size());
        movedList.addAll(first);
        if (!fromLess) {
            movedList.add(element);
        }
        movedList.addAll(middle);
        if (fromLess) {
            movedList.add(element);
        }
        movedList.addAll(last);
        return movedList;
    }

    // -----------------------------------------------------
    //                                                 Array
    //                                                 -----
    public static  List toListFromArray(Object ary) {
        final int len = Array.getLength(ary);
        final List list = new ArrayList(len);
        for (int i = 0; i < len; i++) {
            @SuppressWarnings("unchecked")
            final ELEMENT element = (ELEMENT) Array.get(ary, i);
            list.add(element);
        }
        return list;
    }

    // ===================================================================================
    //                                                                                 Map
    //                                                                                 ===
    public static  HashMap newHashMap() {
        return new HashMap();
    }

    public static  HashMap newHashMap(Map map) {
        return new HashMap(map);
    }

    public static  HashMap newHashMap(KEY key, VALUE value) {
        final HashMap map = newHashMapSized(1);
        map.put(key, value);
        return map;
    }

    public static  HashMap newHashMap(KEY key1, VALUE value1, KEY key2, VALUE value2) {
        final HashMap map = newHashMapSized(2);
        map.put(key1, value1);
        map.put(key2, value2);
        return map;
    }

    public static  HashMap newHashMapSized(int size) {
        return new HashMap(size);
    }

    public static  LinkedHashMap newLinkedHashMap() {
        return new LinkedHashMap();
    }

    public static  LinkedHashMap newLinkedHashMap(Map map) {
        return new LinkedHashMap(map);
    }

    public static  LinkedHashMap newLinkedHashMap(KEY key, VALUE value) {
        final LinkedHashMap map = newLinkedHashMapSized(1);
        map.put(key, value);
        return map;
    }

    public static  LinkedHashMap newLinkedHashMap(KEY key1, VALUE value1, KEY key2, VALUE value2) {
        final LinkedHashMap map = newLinkedHashMapSized(2);
        map.put(key1, value1);
        map.put(key2, value2);
        return map;
    }

    public static  LinkedHashMap newLinkedHashMapSized(int size) {
        return new LinkedHashMap(size);
    }

    public static  ConcurrentHashMap newConcurrentHashMap() {
        return new ConcurrentHashMap();
    }

    public static  ConcurrentHashMap newConcurrentHashMap(Map map) {
        return new ConcurrentHashMap(map);
    }

    public static  ConcurrentHashMap newConcurrentHashMap(KEY key, VALUE value) {
        final ConcurrentHashMap map = newConcurrentHashMapSized(1);
        map.put(key, value);
        return map;
    }

    public static  ConcurrentHashMap newConcurrentHashMap(KEY key1, VALUE value1, KEY key2, VALUE value2) {
        final ConcurrentHashMap map = newConcurrentHashMapSized(2);
        map.put(key1, value1);
        map.put(key2, value2);
        return map;
    }

    public static  ConcurrentHashMap newConcurrentHashMapSized(int size) {
        return new ConcurrentHashMap(size);
    }

    @SuppressWarnings("unchecked")
    public static  Map emptyMap() {
        return (Map) EMPTY_MAP;
    }

    // ===================================================================================
    //                                                                                 Set
    //                                                                                 ===
    public static  HashSet newHashSet() {
        return new HashSet();
    }

    public static  HashSet newHashSet(Collection elements) {
        final HashSet set = newHashSetSized(elements.size());
        set.addAll(elements);
        return set;
    }

    @SafeVarargs
    public static  HashSet newHashSet(ELEMENT... elements) {
        final HashSet set = newHashSetSized(elements.length);
        for (ELEMENT element : elements) {
            set.add(element);
        }
        return set;
    }

    public static  HashSet newHashSetSized(int size) {
        return new HashSet(size);
    }

    public static  LinkedHashSet newLinkedHashSet() {
        return new LinkedHashSet();
    }

    public static  LinkedHashSet newLinkedHashSet(Collection elements) {
        final LinkedHashSet set = newLinkedHashSetSized(elements.size());
        set.addAll(elements);
        return set;
    }

    @SafeVarargs
    public static  LinkedHashSet newLinkedHashSet(ELEMENT... elements) {
        final LinkedHashSet set = newLinkedHashSetSized(elements.length);
        for (ELEMENT element : elements) {
            set.add(element);
        }
        return set;
    }

    public static  LinkedHashSet newLinkedHashSetSized(int size) {
        return new LinkedHashSet(size);
    }

    @SuppressWarnings("unchecked")
    public static  Set emptySet() {
        return (Set) EMPTY_SET;
    }

    // -----------------------------------------------------
    //                                               Advance
    //                                               -------
    public static  ElementDiff analyzeElementDiff(Set beforeCol, Set afterCol) {
        final Set addedSet = newLinkedHashSet();
        final Set deletedSet = newLinkedHashSet();
        final Set remainingSet = newLinkedHashSet();
        for (ELEMENT beforeElement : beforeCol) {
            if (afterCol.contains(beforeElement)) {
                remainingSet.add(beforeElement);
            } else {
                deletedSet.add(beforeElement);
            }
        }
        for (ELEMENT afterElement : afterCol) {
            if (!beforeCol.contains(afterElement)) {
                addedSet.add(afterElement);
            }
        }
        final ElementDiff elementDiff = new ElementDiff();
        elementDiff.setAddedSet(addedSet);
        elementDiff.setDeletedSet(deletedSet);
        elementDiff.setRemainingSet(remainingSet);
        return elementDiff;
    }

    public static class ElementDiff {
        protected Set _addedSet;
        protected Set _deletedSet;
        protected Set _remainingSet;

        public Set getAddedSet() {
            return _addedSet;
        }

        public void setAddedSet(Set addedSet) {
            this._addedSet = addedSet;
        }

        public Set getDeletedSet() {
            return _deletedSet;
        }

        public void setDeletedSet(Set deletedSet) {
            this._deletedSet = deletedSet;
        }

        public Set getRemainingSet() {
            return _remainingSet;
        }

        public void setRemainingSet(Set remainingSet) {
            this._remainingSet = remainingSet;
        }
    }

    // ===================================================================================
    //                                                                               Order
    //                                                                               =====
    /**
     * Order the unordered list according to specified resources.
     * @param  The type of element.
     * @param  The type of ID.
     * @param unorderedList The unordered list. (NotNull)
     * @param resource The resource of according-to-order. (NotNull)
     */
    public static  void orderAccordingTo(List unorderedList,
            AccordingToOrderResource resource) {
        assertObjectNotNull("unorderedList", unorderedList);
        if (unorderedList.isEmpty()) {
            return;
        }
        assertObjectNotNull("resource", resource);
        final List orderedUniqueIdList = resource.getOrderedUniqueIdList();
        assertObjectNotNull("resource.getOrderedUniqueIdList()", orderedUniqueIdList);
        if (orderedUniqueIdList.isEmpty()) {
            return;
        }
        final AccordingToOrderIdExtractor idExtractor = resource.getIdExtractor();
        assertObjectNotNull("resource.getIdExtractor()", idExtractor);

        final Map idIndexMap = new LinkedHashMap();
        int index = 0;
        for (ID_TYPE id : orderedUniqueIdList) {
            if (idIndexMap.containsKey(id)) {
                String msg = "The id was duplicated: id=" + id + " orderedUniqueIdList=" + orderedUniqueIdList;
                throw new IllegalStateException(msg);
            }
            idIndexMap.put(id, index);
            ++index;
        }
        final Comparator comp = new Comparator() {
            public int compare(ELEMENT_TYPE o1, ELEMENT_TYPE o2) {
                final ID_TYPE id1 = idExtractor.extractId(o1);
                final ID_TYPE id2 = idExtractor.extractId(o2);
                assertObjectNotNull("id1 of " + o1, id1);
                assertObjectNotNull("id2 of " + o2, id2);
                final Integer index1 = idIndexMap.get(id1);
                final Integer index2 = idIndexMap.get(id2);
                if (index1 != null && index2 != null) {
                    return index1.compareTo(index2);
                }
                if (index1 == null && index2 == null) {
                    return 0;
                }
                return index1 == null ? 1 : -1;
            }
        };
        Collections.sort(unorderedList, comp);
    }

    public static interface AccordingToOrderIdExtractor {

        /**
         * Extract ID from the element instance.
         * @param element Element instance. (NotNull)
         * @return Extracted ID. (NotNull)
         */
        ID_TYPE extractId(ELEMENT_TYPE element);
    }

    public static class AccordingToOrderResource {
        protected List _orderedUniqueIdList;
        protected AccordingToOrderIdExtractor _idExtractor;

        public AccordingToOrderResource setupResource(List orderedUniqueIdList,
                AccordingToOrderIdExtractor idExtractor) {
            setOrderedUniqueIdList(orderedUniqueIdList);
            setIdExtractor(idExtractor);
            return this;
        }

        public List getOrderedUniqueIdList() {
            return _orderedUniqueIdList;
        }

        public void setOrderedUniqueIdList(List orderedUniqueIdList) {
            _orderedUniqueIdList = orderedUniqueIdList;
        }

        public AccordingToOrderIdExtractor getIdExtractor() {
            return _idExtractor;
        }

        public void setIdExtractor(AccordingToOrderIdExtractor idExtractor) {
            _idExtractor = idExtractor;
        }
    }

    // ===================================================================================
    //                                                                       Assert Helper
    //                                                                       =============
    protected static void assertObjectNotNull(String variableName, Object value) {
        if (variableName == null) {
            String msg = "The value should not be null: variableName=null value=" + value;
            throw new IllegalArgumentException(msg);
        }
        if (value == null) {
            String msg = "The value should not be null: variableName=" + variableName;
            throw new IllegalArgumentException(msg);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy