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

groovy.lang.ListWithDefault Maven / Gradle / Ivy

There is a newer version: 5.0.0-alpha-11
Show newest version
/*
 *  Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you 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 groovy.lang;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

/**
 * A wrapper for {@link List} which automatically grows the list when either {@link #get(int)} or
 * {@link #getAt(int)} is called with an index greater than or equal to {@code size()}.
 *
 * @since 1.8.7
 */
public final class ListWithDefault implements List {

    private final List delegate;
    private final boolean lazyDefaultValues;

    private final Closure initClosure;

    private ListWithDefault(List items, boolean lazyDefaultValues, Closure initClosure) {
        this.delegate = items;
        this.lazyDefaultValues = lazyDefaultValues;
        this.initClosure = initClosure;
    }

    public List getDelegate() {
        return delegate != null ? new ArrayList(delegate) : null;
    }

    public boolean isLazyDefaultValues() {
        return lazyDefaultValues;
    }

    public Closure getInitClosure() {
        return initClosure != null ? (Closure) initClosure.clone() : null;
    }

    public static  ListWithDefault newInstance(List items, boolean lazyDefaultValues, Closure initClosure) {
        if (items == null)
            throw new IllegalArgumentException("Parameter \"items\" must not be null");
        if (initClosure == null)
            throw new IllegalArgumentException("Parameter \"initClosure\" must not be null");

        return new ListWithDefault(new ArrayList(items), lazyDefaultValues, (Closure) initClosure.clone());
    }

    @Override
    public int size() {
        return delegate.size();
    }

    @Override
    public boolean isEmpty() {
        return delegate.isEmpty();
    }

    @Override
    public boolean contains(Object o) {
        return delegate.contains(o);
    }

    @Override
    public Iterator iterator() {
        return delegate.iterator();
    }

    @Override
    public Object[] toArray() {
        return delegate.toArray();
    }

    @Override
    public  U[] toArray(U[] ts) {
        return delegate.toArray(ts);
    }

    @Override
    public boolean add(T t) {
        return delegate.add(t);
    }

    @Override
    public boolean remove(Object o) {
        return delegate.remove(o);
    }

    @Override
    public boolean containsAll(Collection objects) {
        return delegate.containsAll(objects);
    }

    @Override
    public boolean addAll(Collection ts) {
        return delegate.addAll(ts);
    }

    @Override
    public boolean addAll(int i, Collection ts) {
        return delegate.addAll(i, ts);
    }

    @Override
    public boolean removeAll(Collection objects) {
        return delegate.removeAll(objects);
    }

    @Override
    public boolean retainAll(Collection objects) {
        return delegate.retainAll(objects);
    }

    @Override
    public void clear() {
        delegate.clear();
    }

    /**
     * Overwrites subscript operator handling by redirecting to {@link #get(int)}.
     *
     * @param index an index (might be greater or equal to {@code size()}, or smaller than 0)
     * @return the value at the given {@code index} or the default value
     */
    public T getAt(int index) {
        return get(index);
    }

    /**
     * Returns the element at the given index but grows the list if needed. If the requested {@code index} is
     * greater than or equal to {@code size()}, the list will grow to the new size and a default value calculated
     * using the initClosure will be used to populate the missing value and returned.
     * 

* If lazyDefaultValues is true any gaps when growing the list are filled * with nulls. Subsequent attempts to retrieve items from the list from those gap index values * will, upon finding null, call the initClosure to populate the list for the * given list value. Hence, when in this mode, nulls cannot be stored in this list. * If lazyDefaultValues is false any gaps when growing the list are filled * eagerly by calling the initClosure for all gap indexes during list growth. * No calls to initClosure are made except during list growth and it is ok to * store null values in the list when in this mode. *

* This implementation breaks * the contract of {@link java.util.List#get(int)} as it a) possibly modifies the underlying list and b) does * NOT throw an {@link IndexOutOfBoundsException} when {@code index < 0 || index >= size()}. * * @param index an index (might be greater or equal to {@code size()}, or smaller than 0) * @return the value at the given {@code index} or the default value */ @Override public T get(int index) { final int size = size(); int normalisedIndex = normaliseIndex(index, size); if (normalisedIndex < 0) { throw new IndexOutOfBoundsException("Negative index [" + normalisedIndex + "] too large for list size " + size); } // either index >= size or the normalised index is negative if (normalisedIndex >= size) { // find out the number of gaps to fill with null/the default value final int gapCount = normalisedIndex - size; // fill all gaps for (int i = 0; i < gapCount; i++) { final int idx = size(); // if we lazily create default values, use 'null' as placeholder if (lazyDefaultValues) delegate.add(idx, null); else delegate.add(idx, getDefaultValue(idx)); } // add the first/last element being always the default value final int idx = normalisedIndex; delegate.add(idx, getDefaultValue(idx)); // normalise index again to get positive index normalisedIndex = normaliseIndex(index, size()); } T item = delegate.get(normalisedIndex); if (item == null && lazyDefaultValues) { item = getDefaultValue(normalisedIndex); delegate.set(normalisedIndex, item); } return item; } @SuppressWarnings("unchecked") private T getDefaultValue(int idx) { return (T) initClosure.call(new Object[]{idx}); } private static int normaliseIndex(int index, int size) { if (index < 0) { index += size; } return index; } @Override public T set(int i, T t) { return delegate.set(i, t); } @Override public void add(int i, T t) { delegate.add(i, t); } @Override public T remove(int i) { return delegate.remove(i); } @Override public int indexOf(Object o) { return delegate.indexOf(o); } @Override public int lastIndexOf(Object o) { return delegate.lastIndexOf(o); } @Override public ListIterator listIterator() { return delegate.listIterator(); } @Override public ListIterator listIterator(int i) { return delegate.listIterator(i); } @Override public boolean equals(Object obj) { return delegate.equals(obj); } @Override public int hashCode() { return delegate.hashCode(); } /** * Returns a view of a portion of this list. This method returns a list with the same * lazy list settings as the original list. * * @param fromIndex low endpoint of the subList (inclusive) * @param toIndex upper endpoint of the subList (exclusive) * @return a view of a specified range within this list, keeping all lazy list settings */ @Override public ListWithDefault subList(int fromIndex, int toIndex) { return new ListWithDefault(delegate.subList(fromIndex, toIndex), lazyDefaultValues, (Closure) initClosure.clone()); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy