/*
 * 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 org.apache.jackrabbit.oak.commons;

import java.lang.reflect.Array;
import java.util.HashSet;
import java.util.Set;

/**
 * Array utility methods.
 */
public class ArrayUtils {

    public static final String[] EMPTY_STRING_ARRAY = new String[0];
    public static final long[] EMPTY_LONG_ARRAY = new long[0];
    public static final int[] EMPTY_INTEGER_ARRAY = new int[0];

    private ArrayUtils() {
    }

    /**
     * Replace an element in a clone of the array at the given position.
     *
     * @param values the values
     * @param index the index
     * @param x the value to add
     * @return the new array
     */
    public static <T> T[] arrayReplace(T[] values, int index, T x) {
        int size = values.length;
        @SuppressWarnings("unchecked")
        T[] v2 = (T[]) Array.newInstance(values.getClass().getComponentType(), size);
        System.arraycopy(values, 0, v2, 0, size);
        v2[index] = x;
        return v2;
    }

    /**
     * Insert an element into a clone of the array at the given position.
     *
     * @param values the values
     * @param index the index
     * @param x the value to add
     * @return the new array
     */
    public static int[] arrayInsert(int[] values, int index, int x) {
        int size = values.length;
        int[] v2 = new int[size + 1];
        v2[index] = x;
        copyArrayAdd(values, v2, size, index);
        return v2;
    }

    /**
     * Insert an element into a clone of the array at the given position.
     *
     * @param values the values
     * @param index the index
     * @param x the value to add
     * @return the new array
     */
    public static long[] arrayInsert(long[] values, int index, long x) {
        int size = values.length;
        long[] v2 = new long[size + 1];
        v2[index] = x;
        copyArrayAdd(values, v2, size, index);
        return v2;
    }

    /**
     * Insert an element into a clone of the array at the given position.
     *
     * @param values the values
     * @param index the index
     * @param x the value to add
     * @return the new array
     */
    public static <T> T[] arrayInsert(T[] values, int index, T x) {
        int size = values.length;
        @SuppressWarnings("unchecked")
        T[] v2 = (T[]) Array.newInstance(values.getClass().getComponentType(), size + 1);
        v2[index] = x;
        copyArrayAdd(values, v2, size, index);
        return v2;
    }

    /**
     * Insert an element into a clone of the array at the given position.
     *
     * @param values the values
     * @param index the index
     * @param x the value to add
     * @return the new array
     */
    public static String[] arrayInsert(String[] values, int index, String x) {
        int size = values.length;
        String[] v2 = new String[size + 1];
        v2[index] = x;
        copyArrayAdd(values, v2, size, index);
        return v2;
    }

    /**
     * Remove an element from a clone of the array at the given position.
     *
     * @param values the values
     * @param index the index
     * @return the new array
     */
    public static int[] arrayRemove(int[] values, int index) {
        int size = values.length;
        if (size == 1) {
            return EMPTY_INTEGER_ARRAY;
        }
        int[] v2 = new int[size - 1];
        copyArrayRemove(values, v2, size, index);
        return v2;
    }

    /**
     * Remove an element from a clone of the array at the given position.
     *
     * @param values the values
     * @param index the index
     * @return the new array
     */
    public static <T> T[] arrayRemove(T[] values, int index) {
        int size = values.length;
        @SuppressWarnings("unchecked")
        T[] v2 = (T[]) Array.newInstance(values.getClass().getComponentType(), size - 1);
        copyArrayRemove(values, v2, size, index);
        return v2;
    }

    /**
     * Remove an element from a clone of the array at the given position.
     *
     * @param values the values
     * @param index the index
     * @return the new array
     */
    public static long[] arrayRemove(long[] values, int index) {
        int size = values.length;
        if (size == 1) {
            return EMPTY_LONG_ARRAY;
        }
        long[] v2 = new long[size - 1];
        copyArrayRemove(values, v2, size, index);
        return v2;
    }


    /**
     * Remove an element from a clone of the array at the given position.
     *
     * @param values the values
     * @param index the index
     * @return the new array
     */
    public static String[] arrayRemove(String[] values, int index) {
        int size = values.length;
        if (size == 1) {
            return EMPTY_STRING_ARRAY;
        }
        String[] v2 = new String[size - 1];
        copyArrayRemove(values, v2, size, index);
        return v2;
    }

    private static void copyArrayAdd(Object src, Object dst, int size, int index) {
        if (index > 0) {
            System.arraycopy(src, 0, dst, 0, index);
        }
        if (index < size) {
            System.arraycopy(src, index, dst, index + 1, size - index);
        }
    }

    private static void copyArrayRemove(Object src, Object dst, int size, int index) {
        if (index > 0 && size > 0) {
            System.arraycopy(src, 0, dst, 0, index);
        }
        if (index < size) {
            System.arraycopy(src, index + 1, dst, index, size - index - 1);
        }
    }

        /**
     * Check whether an array contains a given element
     * @param array
     * @param element
     * @param <T>
     * @return {@code true} iff {@code array} contains {@code element}.
     */
    public static <T> boolean contains(T[] array, T element) {
        for (T t : array) {
            if (element == null && t == null || element != null && element.equals(t)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Convert an array to a set.
     * @param elements
     * @param <T>
     * @return
     */
    public static <T> Set<T> toSet(T... elements) {
        return new HashSet<T>(java.util.Arrays.asList(elements));
    }

    // TODO duplicate of copyArrayAdd?
    /**
     * Create a new array of the same type with an additional element added.
     * @param array
     * @param value
     * @param <T>
     * @return array of {@code array.length + 1} with {@code value} as its last element.
     */
    public static <T> T[] add(T[] array, T value) {
        T[] copy = java.util.Arrays.copyOf(array, array.length + 1);
        copy[array.length] = value;
        return copy;
    }
}

@@ -6929,469 +6929,6 @@
  %7D%0A%0A
-    // TODO duplicate of copyArrayAdd?%0A    /**%0A     * Create a new array of the same type with an additional element added.%0A     * @param array%0A     * @param value%0A     * @param %3CT%3E%0A     * @return array of %7B@code array.length + 1%7D with %7B@code value%7D as its last element.%0A     */%0A    public static %3CT%3E T%5B%5D add(T%5B%5D array, T value) %7B%0A        T%5B%5D copy = java.util.Arrays.copyOf(array, array.length + 1);%0A        copy%5Barray.length%5D = value;%0A        return copy;%0A    %7D%0A
 %7D%0A

remove unused/redundant method