Explorar el Código

testy
przebudowane ArrayUtils dla typów prymitywnych
UnsafeArrayAccess bardziej tolerancyjny na podawany typ: array/component
UnsafeArrayAccess # create
UnsafeArrayAccess # length

Ranides Atterwim hace 13 años
padre
commit
d9029ac1bc

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 188 - 0
hs_err_pid3152.log


+ 126 - 514
src/main/java/net/ranides/assira/collection/ArrayUtils.java

@@ -20,6 +20,8 @@ import net.ranides.assira.generic.Function;
 import net.ranides.assira.generic.ValueUtils;
 import net.ranides.assira.math.HashHelper;
 import net.ranides.assira.reflection.ClassInspector;
+import net.ranides.assira.reflection.UnsafeArrayAccess;
+import net.ranides.assira.trace.LoggerUtils;
 
 /**
  * Operacje na tablicach.
@@ -69,7 +71,7 @@ public final class ArrayUtils {
      * @param clazz szukana klasa
      * @return
      */
-    public static <T> T first(Object[] values, Class<T> clazz) {
+    public static <K, T extends K> T first(K[] values, Class<T> clazz) {
         if(null == values) { return null; }
         for (int i=0, n=values.length; i<n; i++) {
             if( clazz.isInstance(values[i]) ) { return clazz.cast(values[i]); }
@@ -85,8 +87,10 @@ public final class ArrayUtils {
      * @param values tablica, w której obiekt klasy jest szukany
      * @return
      */
-    public static <T> T first(T[] values) { // @test
-        if(null == values || 0==values.length) { return null; }
+    public static <T> T first(T[] values) {
+        if(null == values || 0==values.length) { 
+            return null; 
+        }
         return values[0];
     }
 
@@ -99,8 +103,10 @@ public final class ArrayUtils {
      * @param clazz szukana klasa
      * @return
      */
-    public static <T> T last(Object[] values, Class<T> clazz) { // @test
-        if(null == values) { return null; }
+    public static <T> T last(Object[] values, Class<T> clazz) {
+        if(null == values) { 
+            return null; 
+        }
         for (int i=values.length-1; i >= 0; i--) {
             if( clazz.isInstance(values[i]) ) { return clazz.cast(values[i]); }
         }
@@ -115,8 +121,10 @@ public final class ArrayUtils {
      * @param values tablica, w której obiekt klasy jest szukany
      * @return
      */
-    public static <T> T last(T[] values) { // @test
-        if(null == values || 0==values.length) { return null; }
+    public static <T> T last(T[] values) {
+        if(null == values || 0==values.length) { 
+            return null; 
+        }
         return values[values.length - 1];
     }
 
@@ -128,15 +136,15 @@ public final class ArrayUtils {
      * @param values
      * @return
      */
-    public static <T> int size(T[] values) { // @test
+    public static <T> int size(T[] values) {
         return null == values ? 0 : values.length;
     }
     
-    public static int size(Object values) { // @test
+    public static int size(Object values) { 
         if(null == values) {
             return 0;
         }
-        if(values.getClass().isArray()) {
+        if(!values.getClass().isArray()) {
             throw new IllegalArgumentException("type is not an array: " + values.getClass());
         }
         return Array.getLength(values);
@@ -150,7 +158,7 @@ public final class ArrayUtils {
      * @param values
      * @return
      */
-    public static <T> boolean isEmpty(T[] values) { // @test
+    public static <T> boolean isEmpty(T[] values) {
         return 0 == size(values);
     }
     
@@ -162,59 +170,33 @@ public final class ArrayUtils {
      * @param values
      * @return
      */
-    public static <T> boolean isEmpty(Object values) { // @test
+    public static <T> boolean isEmpty(Object values) {
         return 0 == size(values);
     }
 
-    /**
-     * Porównuje rozmiary tablic.
-     * 
-     * <table style="margin: 10px 0 0 0; background: #c0F0c0; border: solid 1px #006000;" cellpadding="0"><tr><td>{@code null} safe method</td></tr></table>
-     * @param <T1>
-     * @param <T2>
-     * @param a
-     * @param b
-     * @return
-     * <table>
-     *  <tr><td>ujemne</td><td> - jeśli a &lt; b</td></tr>
-     *  <tr><td>dodatnie</td><td> - jeśli a &gt; b </td></tr>
-     *  <tr><td>dodatnie</td><td> - jeśli a == b </td></tr>
-     * </table>
-     */
-    public static <T1,T2> int compareSize(T1[] a, T2[] b) { // @test
-        return size(a) - size(b);
-    }
-
-    /**
-     * Sprawdza, czy tablice mają ten sam rozmiar.
-     * Tablice, które są równe {@code null} traktowane są jako puste (rozmiar 0).
-     * 
-     * <table style="margin: 10px 0 0 0; background: #c0F0c0; border: solid 1px #006000;" cellpadding="0"><tr><td>{@code null} safe method</td></tr></table>
-     * @param <T1>
-     * @param <T2>
-     * @param a
-     * @param b
-     * @return
-     */
-    public static <T1,T2> boolean equalSize(T1[] a, T2[] b) { // @test
-        return size(a) == size(b);
-    }
-
-
     /**
      * Przycina tablicę, jeśli tablica ma długość większą od podanej granicy.
      * @param <T>
-     * @param list
+     * @param array
      * @param size maksymalna długość tablicy
      * @return {@code list} albo nowa tablica, jeśli konieczne było obcięcie
      */
+    public static <T> T[] clip(T[] array, int size) {
+        if( array.length <= size ) {
+            return array; 
+        }
+        T result[] = make(array.getClass(), size);
+        System.arraycopy(array, 0, result, 0, size);
+        return result;
+    }
+    
     @SuppressWarnings("unchecked")
-    public static <T> T[] clip(T[] list, int size) { // @test
-        if( list.length <= size ) {
-            return list; 
+    public static <ArrayT> ArrayT clip(ArrayT array, int size) {
+        if( size(array) <= size ) {
+            return array; 
         }
-        T result[] = (T[])Array.newInstance(list.getClass().getComponentType(), size);
-        System.arraycopy(list, 0, result, 0, size);
+        ArrayT result= make(array.getClass(), size);
+        System.arraycopy(array, 0, result, 0, size);
         return result;
     }
 
@@ -308,75 +290,19 @@ public final class ArrayUtils {
      * odpowiednich rzutowań i unbox'owań, jeśli są konieczne. Zwrócona tablica jest
      * niezależna od listy.
      * @param <T> 
-     * @param arrayClazz
+     * @param array
      * @param source
      * @return
      * @throws ClassCastException jeśli konwersja elementów jest niemożliwa do przeprowadzenia
      */
     @SuppressWarnings({"PMD", "unchecked"})
-    public static <T> T toArray(Class<T> arrayClazz, List<?> source) throws ClassCastException {
-        int count = source.size();
-        if ( boolean[].class.equals(arrayClazz) ) {
-            boolean[] result = new boolean[count];
-            for(int i=0; i<count; i++) { result[i] = (Boolean)source.get(i); }
-            return (T)result;
-        }
-        if ( char[].class.equals(arrayClazz) ) {
-            char[] result = new char[count];
-            for(int i=0; i<count; i++) { result[i] = (Character)source.get(i); }
-            return (T)result;
-        }
-        if ( byte[].class.equals(arrayClazz) ) {
-            byte[] result = new byte[count];
-            for(int i=0; i<count; i++) { result[i] = (Byte)source.get(i); }
-            return (T)result;
-        }
-        if ( short[].class.equals(arrayClazz) ) {
-            short[] result = new short[count];
-            for(int i=0; i<count; i++) { result[i] = (Short)source.get(i); }
-            return (T)result;
-        }
-        if ( int[].class.equals(arrayClazz) ) {
-            int[] result = new int[count];
-            for(int i=0; i<count; i++) { result[i] = (Integer)source.get(i); }
-            return (T)result;
-        }
-        if ( long[].class.equals(arrayClazz) ) {
-            long[] result = new long[count];
-            for(int i=0; i<count; i++) { result[i] = (Long)source.get(i); }
-            return (T)result;
-        }
-        if ( float[].class.equals(arrayClazz) ) {
-            float[] result = new float[count];
-            for(int i=0; i<count; i++) { result[i] = (Float)source.get(i); }
-            return (T)result;
-        }
-        if ( double[].class.equals(arrayClazz) ) {
-            double[] result = new double[count];
-            for(int i=0; i<count; i++) { result[i] = (Double)source.get(i); }
-            return (T)result;
-        }
-        try {
-            Object[] result = (Object[])Array.newInstance(arrayClazz.getComponentType(), count);
-            for(int i=0; i<count; i++) { result[i] = source.get(i); }
-            return (T)result;
-        } catch(ArrayStoreException cause) {
-            throw new ClassCastException("cannot cast " + cause.getMessage() + " to " + arrayClazz.getComponentType());
-        }
-    }
-
-    /**
-     * Tworzy nową tablicę o podanym rozmiarze, która może przechowywać obiekty
-     * podanego typu. Metoda nie nadaje się do tworzenia tablic typu prostego.
-     * Do utworzenia takich tablic użyj jednej z funkcji {@link #nCopies}.
-     * @param <T>
-     * @param componentClass
-     * @param count
-     * @return
-     */
-    @SuppressWarnings("unchecked")
-    public static <T extends Object> T[] make(Class<T> componentClass, int count) {
-        return (T[])Array.newInstance(componentClass, count);
+    public static <Auto> Auto toArray(Class<?> type, List<?> source) throws ClassCastException {
+        UnsafeArrayAccess access = UnsafeArrayAccess.forType(type);
+        int size = source.size();
+        Object target = access.create(size);
+        int index = 0;
+        for(Object item : source) { access.put(target, index++, item);  }
+        return (Auto)target;
     }
 
     /**
@@ -390,7 +316,9 @@ public final class ArrayUtils {
      */
     public static <K extends Comparable<K>, V> void sort(V[] values, K[] keys) {
         final Pair[] table = new Pair[keys.length];
-        for(int i=0; i<table.length; i++) { table[i] = new Pair<K>(keys[i], values[i]); }
+        for(int i=0; i<table.length; i++) { 
+            table[i] = new Pair<K>(keys[i], values[i]); 
+        }
         java.util.Arrays.sort(table);
 
         
@@ -423,7 +351,7 @@ public final class ArrayUtils {
 
 /* ************************************************************************** */
     
-    private static class Pair<K extends Comparable<K>> implements Comparable<Pair<K>> {
+    static class Pair<K extends Comparable<K>> implements Comparable<Pair<K>> {
         public final K key;
         public final Object value;
 
@@ -483,186 +411,82 @@ public final class ArrayUtils {
     
 /* ************************************************************************** */
     
-    /**
-     * Zobacz: {@link #nCopies(int, java.lang.Object) nCopies}
-     * @param size
-     * @param value
-     * @return
-     */
-    public static char[] nCopies(int size, char value) { // @test
-        char[] content = new char[size];
-        if(value!=0) { for(int i=0; i<size; i++) { content[i] = value; } }
-        return content;
-    }
-    
-    /**
-     * Zobacz: {@link #nCopies(int, java.lang.Object) nCopies}
-     * @param size
-     * @param value
-     * @return
-     */
-    public static short[] nCopies(int size, short value) { // @test
-        short[] content = new short[size];
-        if(value!=0) { for(int i=0; i<size; i++) { content[i] = value; } }
-        return content;
-    }
-    
-    /**
-     * Zobacz: {@link #nCopies(int, java.lang.Object) nCopies}
-     * @param size
-     * @param value
-     * @return
-     */
-    public static int[] nCopies(int size, int value) { // @test
-        int[] content = new int[size];
-        if(value!=0) { for(int i=0; i<size; i++) { content[i] = value; } }
-        return content;
+    @SuppressWarnings("unchecked")
+    public static <Auto> Auto make(Class<?> type, int size) {
+        return (Auto)UnsafeArrayAccess.forType(type).create(size);
     }
     
-    /**
-     * Zobacz: {@link #nCopies(int, java.lang.Object) nCopies}
-     * @param size
-     * @param value
-     * @return
-     */
-    public static long[] nCopies(int size, long value) { // @test
-        long[] content = new long[size];
-        if(value!=0) { for(int i=0; i<size; i++) { content[i] = value; } }
-        return content;
+    @SuppressWarnings("unchecked")
+    public static <Auto> Auto make(Class<?> type, int size, Object value) {
+        UnsafeArrayAccess access = UnsafeArrayAccess.forType(type);
+        Object content = access.create(size);
+        if(size != 0 && value!=null) { 
+            for(int i=0; i<size; i++) { access.put(content, i, value); } 
+        }
+        return (Auto)content;
     }
     
-    /**
-     * Zobacz: {@link #nCopies(int, java.lang.Object) nCopies}
-     * @param size
-     * @param value
-     * @return
-     */
-    public static float[] nCopies(int size, float value) { // @test
-        float[] content = new float[size];
-        if(value!=0) { for(int i=0; i<size; i++) { content[i] = value; } }
-        return content;
+    @SuppressWarnings("unchecked")
+    public static <Auto> Auto make(int size, Object value) {
+        assert value != null : "value must be specified";
+        
+        Class<?> type = ClassInspector.unbox(value.getClass());
+        UnsafeArrayAccess access = UnsafeArrayAccess.forType(type);
+        Object content = access.create(size);
+        for(int i=0; i<size; i++) { 
+            access.put(content, i, value); 
+        } 
+        return (Auto)content;
     }
     
     /**
-     * Zobacz: {@link #nCopies(int, java.lang.Object) nCopies}
+     * Tworzy nową tablicę o podanym rozmiarze, która może przechowywać obiekty
+     * podanego typu. Metoda nie nadaje się do tworzenia tablic typu prostego.
+     * Do utworzenia takich tablic użyj jednej z funkcji {@link #nCopies}.
+     * @param <T>
+     * @param component
      * @param size
-     * @param value
      * @return
      */
-    public static double[] nCopies(int size, double value) { // @test
-        double[] content = new double[size];
-        if(value!=0.0) { for(int i=0; i<size; i++) { content[i] = value; } }
-        return content;
+    @SuppressWarnings("unchecked")
+    public static <T extends Object> T[] makeT(Class<T> component, int size) {
+        return (T[])Array.newInstance(component, size);
     }
     
-    /**
-     * Tworzy tablicę o podanym rozmiarze, w całości zainicjalizowaną podaną wartością.
-     * @param <T> 
-     * @param size rozmiar tablicy
-     * @param value wartość, która zostanie przypisana do każdej komórki w tablicy
-     * @return
-     */
-    public static <T> T[] nCopies(int size, T value) {
-        @SuppressWarnings("unchecked")
-        T[] content = (T[])make(value.getClass(), size);
+    @SuppressWarnings("unchecked")
+    public static <T extends Object> T[] makeT(Class<T> component, int size, T value) {
+        T[] content = makeT(component, size);
         for(int i=0; i<size; i++) { content[i] = value; }
         return content;
     }
     
-    /**
-     * Zobacz: {@link #shift(Object[]) shift}
-     * @param values
-     * @return
-     */
-    public static byte shift(byte[] values) {
-        if(values == null || values.length==0) { throw new EmptyCollectionException(); }
-        byte value = values[0];
-        for(int i=1,n=values.length; i<n; i++) { values[i-1] = values[i]; }
-        return value;
-    }
-    
-    /**
-     * Zobacz: {@link #shift(Object[]) shift}
-     * @param values
-     * @return
-     */
-    public static char shift(char[] values) {
-        if(values == null || values.length==0) { throw new EmptyCollectionException(); }
-        char value = values[0];
-        for(int i=1,n=values.length; i<n; i++) { values[i-1] = values[i]; }
-        return value;
-    }
-    
-    /**
-     * Zobacz: {@link #shift(Object[]) shift}
-     * @param values
-     * @return
-     */
-    public static short shift(short[] values) {
-        if(values == null || values.length==0) { throw new EmptyCollectionException(); }
-        short value = values[0];
-        for(int i=1,n=values.length; i<n; i++) { values[i-1] = values[i]; }
-        return value;
-    }
-    
-    /**
-     * Zobacz: {@link #shift(Object[]) shift}
-     * @param values
-     * @return
-     */
-    public static int shift(int[] values) {
-        if(values == null || values.length==0) { throw new EmptyCollectionException(); }
-        int value = values[0];
-        for(int i=1,n=values.length; i<n; i++) { values[i-1] = values[i]; }
-        return value;
-    }
-    
-    /**
-     * Zobacz: {@link #shift(Object[]) shift}
-     * @param values
-     * @return
-     */
-    public static long shift(long[] values) {
-        if(values == null || values.length==0) { throw new EmptyCollectionException(); }
-        long value = values[0];
-        for(int i=1,n=values.length; i<n; i++) { values[i-1] = values[i]; }
-        return value;
-    }
-    
-    /**
-     * Zobacz: {@link #shift(Object[]) shift}
-     * @param values
-     * @return
-     */
-    public static float shift(float[] values) {
-        if(values == null || values.length==0) { throw new EmptyCollectionException(); }
-        float value = values[0];
-        for(int i=1,n=values.length; i<n; i++) { values[i-1] = values[i]; }
-        return value;
+    @SuppressWarnings("unchecked")
+    public static <T> T[] makeT(int size, T value) {
+        return makeT((Class<T>)value.getClass(), size, value);
     }
     
-    /**
-     * Zobacz: {@link #shift(Object[]) shift}
-     * @param values
-     * @return
-     */
-    public static double shift(double[] values) {
-        if(values == null || values.length==0) { throw new EmptyCollectionException(); }
-        double value = values[0];
-        for(int i=1,n=values.length; i<n; i++) { values[i-1] = values[i]; }
-        return value;
-    }
     
+
     /**
      * Zobacz: {@link #shift(Object[]) shift}
      * @param values
      * @return
      */
-    public static boolean shift(boolean[] values) {
-        if(values == null || values.length==0) { throw new EmptyCollectionException(); }
-        boolean value = values[0];
-        for(int i=1,n=values.length; i<n; i++) { values[i-1] = values[i]; }
-        return value;
+    @SuppressWarnings("unchecked")
+    public static <Auto> Auto shift(Object values) {
+        if(values == null) {  // @test
+            throw new EmptyCollectionException(); 
+        }
+        UnsafeArrayAccess access = UnsafeArrayAccess.forObject(values);
+        int n = access.length(values);
+        if(n == 0) { // @test
+            throw new EmptyCollectionException(); 
+        }
+        Object value = access.get(values, 0);
+        for(int i=1; i<n; i++) { 
+            access.put(values, i-1, access.get(values, i));
+        }
+        return (Auto)value;
     }
     
     /**
@@ -684,95 +508,20 @@ public final class ArrayUtils {
      * @param values
      * @return
      */
-    public static byte unshift(byte[] values) {
-        if(values == null || values.length==0) { throw new EmptyCollectionException(); }
-        byte value = values[values.length-1];
-        for(int i=values.length-1; i>0; i--) { values[i] = values[i-1]; }
-        return value;
-    }
-    
-    /**
-     * Zobacz: {@link #unshift(Object[]) unshift}
-     * @param values
-     * @return
-     */
-    public static short unshift(short[] values) {
-        if(values == null || values.length==0) { throw new EmptyCollectionException(); }
-        short value = values[values.length-1];
-        for(int i=values.length-1; i>0; i--) { values[i] = values[i-1]; }
-        return value;
-    }
-    
-    /**
-     * Zobacz: {@link #unshift(Object[]) unshift}
-     * @param values
-     * @return
-     */
-    public static int unshift(int[] values) {
-        if(values == null || values.length==0) { throw new EmptyCollectionException(); }
-        int value = values[values.length-1];
-        for(int i=values.length-1; i>0; i--) { values[i] = values[i-1]; }
-        return value;
-    }
-    
-    /**
-     * Zobacz: {@link #unshift(Object[]) unshift}
-     * @param values
-     * @return
-     */
-    public static long unshift(long[] values) {
-        if(values == null || values.length==0) { throw new EmptyCollectionException(); }
-        long value = values[values.length-1];
-        for(int i=values.length-1; i>0; i--) { values[i] = values[i-1]; }
-        return value;
-    }
-    
-    /**
-     * Zobacz: {@link #unshift(Object[]) unshift}
-     * @param values
-     * @return
-     */
-    public static float unshift(float[] values) {
-        if(values == null || values.length==0) { throw new EmptyCollectionException(); }
-        float value = values[values.length-1];
-        for(int i=values.length-1; i>0; i--) { values[i] = values[i-1]; }
-        return value;
-    }
-    
-    /**
-     * Zobacz: {@link #unshift(Object[]) unshift}
-     * @param values
-     * @return
-     */
-    public static double unshift(double[] values) {
-        if(values == null || values.length==0) { throw new EmptyCollectionException(); }
-        double value = values[values.length-1];
-        for(int i=values.length-1; i>0; i--) { values[i] = values[i-1]; }
-        return value;
-    }
-    
-    /**
-     * Zobacz: {@link #unshift(Object[]) unshift}
-     * @param values
-     * @return
-     */
-    public static char unshift(char[] values) {
-        if(values == null || values.length==0) { throw new EmptyCollectionException(); }
-        char value = values[values.length-1];
-        for(int i=values.length-1; i>0; i--) { values[i] = values[i-1]; }
-        return value;
-    }
-    
-    /**
-     * Zobacz: {@link #unshift(Object[]) unshift}
-     * @param values
-     * @return
-     */
-    public static boolean unshift(boolean[] values) {
-        if(values == null || values.length==0) { throw new EmptyCollectionException(); }
-        boolean value = values[values.length-1];
-        for(int i=values.length-1; i>0; i--) { values[i] = values[i-1]; }
-        return value;
+    public static <Auto> Auto unshift(Object values) {
+        if(values == null) { // @test
+            throw new EmptyCollectionException(); 
+        }
+        UnsafeArrayAccess access = UnsafeArrayAccess.forObject(values);
+        int n = access.length(values);
+        if(n == 0) { // @test
+            throw new EmptyCollectionException(); 
+        }
+        Object value = access.get(values, n-1);
+        for(int i=n-1; i>0; i--) { 
+            access.put(values, i, access.get(values, i-1));
+        }
+        return (Auto)value;
     }
     
     /**
@@ -795,102 +544,14 @@ public final class ArrayUtils {
      * @param second
      * @return
      */
-    public static byte[] concat(byte[] first, byte[] second) {
-        byte[] result = new byte[ first.length + second.length ];
-        System.arraycopy(first, 0, result, 0, first.length);
-        System.arraycopy(second, 0, result, first.length, second.length);
-        return result;
-    }
-    
-    /**
-     * Zobacz: {@link #concat(Object[], Object[]) concat}
-     * @param first
-     * @param second
-     * @return
-     */
-    public static short[] concat(short[] first, short[] second) {
-        short[] result = new short[ first.length + second.length ];
-        System.arraycopy(first, 0, result, 0, first.length);
-        System.arraycopy(second, 0, result, first.length, second.length);
-        return result;
-    }
-    
-    /**
-     * Zobacz: {@link #concat(Object[], Object[]) concat}
-     * @param first
-     * @param second
-     * @return
-     */
-    public static int[] concat(int[] first, int[] second) {
-        int[] result = new int[ first.length + second.length ];
-        System.arraycopy(first, 0, result, 0, first.length);
-        System.arraycopy(second, 0, result, first.length, second.length);
-        return result;
-    }
-    
-    /**
-     * Zobacz: {@link #concat(Object[], Object[]) concat}
-     * @param first
-     * @param second
-     * @return
-     */
-    public static long[] concat(long[] first, long[] second) {
-        long[] result = new long[ first.length + second.length ];
-        System.arraycopy(first, 0, result, 0, first.length);
-        System.arraycopy(second, 0, result, first.length, second.length);
-        return result;
-    }
-    
-    /**
-     * Zobacz: {@link #concat(Object[], Object[]) concat}
-     * @param first
-     * @param second
-     * @return
-     */
-    public static float[] concat(float[] first, float[] second) {
-        float[] result = new float[ first.length + second.length ];
-        System.arraycopy(first, 0, result, 0, first.length);
-        System.arraycopy(second, 0, result, first.length, second.length);
-        return result;
-    }
-    
-    /**
-     * Zobacz: {@link #concat(Object[], Object[]) concat}
-     * @param first
-     * @param second
-     * @return
-     */
-    public static double[] concat(double[] first, double[] second) {
-        double[] result = new double[ first.length + second.length ];
-        System.arraycopy(first, 0, result, 0, first.length);
-        System.arraycopy(second, 0, result, first.length, second.length);
-        return result;
-    }
-    
-    /**
-     * Zobacz: {@link #concat(Object[], Object[]) concat}
-     * @param first
-     * @param second
-     * @return
-     */
-    public static char[] concat(char[] first, char[] second) {
-        char[] result = new char[ first.length + second.length ];
-        System.arraycopy(first, 0, result, 0, first.length);
-        System.arraycopy(second, 0, result, first.length, second.length);
-        return result;
-    }
-    
-    /**
-     * Zobacz: {@link #concat(Object[], Object[]) concat}
-     * @param first
-     * @param second
-     * @return
-     */
-    public static boolean[] concat(boolean[] first, boolean[] second) {
-        boolean[] result = new boolean[ first.length + second.length ];
-        System.arraycopy(first, 0, result, 0, first.length);
-        System.arraycopy(second, 0, result, first.length, second.length);
-        return result;
+    @SuppressWarnings("SuspiciousSystemArraycopy")
+    public static <ArrayT> ArrayT concat(ArrayT first, ArrayT second) {
+        int length1 = size(first);
+        int length2 = size(second);
+        Object result = make(first.getClass(), length1 + length2);
+        System.arraycopy(first, 0, result, 0, length1);
+        System.arraycopy(second, 0, result, length1, length2);
+        return (ArrayT)result;
     }
     
     /**
@@ -972,7 +633,7 @@ public final class ArrayUtils {
     @SuppressWarnings("unchecked")
     public static <T> T[] arraycopy(T[] target, Object[] source) { // @test
         Class<T> component = (Class<T>)target.getClass().getComponentType();
-        T[] result = target.length >= source.length ? target : make(component, source.length)    ;
+        T[] result = target.length >= source.length ? target : makeT(component, source.length);
         
         for(int i=0, n=source.length; i<n; i++) {
             result[i] = component.cast(source[i]);
@@ -1000,11 +661,11 @@ public final class ArrayUtils {
      * @param source
      * @return 
      */
-    public static Object arraycopy(Object source) { // @test
-        int size = Array.getLength(source);
-        Object target = Array.newInstance(source.getClass().getComponentType());
+    public static <ArrayT> ArrayT arraycopy(ArrayT source) { // @test
+        int size = size(source);
+        Object target = make(source.getClass(), size);
         System.arraycopy(source, 0, target, 0, size);
-        return target;
+        return (ArrayT)target;
     }
 
     /**
@@ -1037,55 +698,6 @@ public final class ArrayUtils {
         return indexOf(array, value) >= 0;
     }
     
-    /**
-     * Porównuje zawartość tablic, i zwraca {@code true}, jeśli są równe.
-     * @param array1
-     * @param array2
-     * @return
-     */
-    public static boolean isEqual(byte[] array1, byte[] array2) { // @test
-        if (array1==array2) {
-            return true;
-        }
-        if (array1==null || array2==null) {
-            return false;
-        }
-        int n = array1.length;
-        if (array2.length != n) {
-            return false;
-        }
-
-        for (int i=0; i<n; i++) {
-            if (array1[i] != array2[i]) { return false; }
-        }
-        return true;
-    }
-    
-    /**
-     * Porównuje pierwsze {@code size} elementów w tablicach, i zwraca {@code true},
-     * jeśli są równe.
-     * @param size
-     * @param array1
-     * @param array2
-     * @return
-     */
-    public static boolean isEqual(int size, byte[] array1, byte[] array2) { // @test
-        if (array1==array2) {
-            return true;
-        }
-        if (array1==null || array2==null) {
-            return false;
-        }
-        if(array1.length != array2.length && (array1.length < size || array2.length < size) ) {
-            return false;
-        }
-        
-        for (int i=0, n=Math.min(array1.length, size); i<n; i++) {
-            if (array1[i] != array2[i]) { return false; }
-        }
-        return true;
-    }
-    
     public static <S> Object[] apply(S[] values, Function<?,S> function) { // @test
         final int n = values.length;
         Object[] target = new Object[values.length];

+ 89 - 0
src/main/java/net/ranides/assira/collection/ByteArrays.java

@@ -0,0 +1,89 @@
+/*
+ * @author Ranides Atterwim <ranides@gmail.com>
+ * @copyright Ranides Atterwim
+ * @license WTFPL
+ * @url http://ranides.net/projects/assira
+ */
+package net.ranides.assira.collection;
+
+/**
+ *
+ * @author ranides
+ */
+public class ByteArrays {
+    
+    public static byte[] make(int size, byte value) {
+        byte[] content = new byte[size];
+        if(value!=0) { for(int i=0; i<size; i++) { content[i] = value; } }
+        return content;
+    }
+    
+    public static byte shift(byte[] values) {
+        if(values == null || values.length==0) { throw new EmptyCollectionException(); }
+        byte value = values[0];
+        for(int i=1,n=values.length; i<n; i++) { values[i-1] = values[i]; }
+        return value;
+    }
+    
+    public static byte unshift(byte[] values) {
+        if(values == null || values.length==0) { throw new EmptyCollectionException(); }
+        byte value = values[values.length-1];
+        for(int i=values.length-1; i>0; i--) { values[i] = values[i-1]; }
+        return value;
+    }
+    
+    public static byte[] concat(byte[] first, byte[] second) {
+        byte[] result = new byte[ first.length + second.length ];
+        System.arraycopy(first, 0, result, 0, first.length);
+        System.arraycopy(second, 0, result, first.length, second.length);
+        return result;
+    }
+    
+    /**
+     * Porównuje zawartość tablic, i zwraca {@code true}, jeśli są równe.
+     * @param array1
+     * @param array2
+     * @return
+     */
+    public static boolean isEqual(byte[] array1, byte[] array2) { // @test
+        if (array1==array2) {
+            return true;
+        }
+        if (array1==null || array2==null) {
+            return false;
+        }
+        int n = array1.length;
+        if (array2.length != n) {
+            return false;
+        }
+        for (int i=0; i<n; i++) {
+            if (array1[i] != array2[i]) { return false; }
+        }
+        return true;
+    }
+    
+    /**
+     * Porównuje pierwsze {@code size} elementów w tablicach, i zwraca {@code true},
+     * jeśli są równe.
+     * @param size
+     * @param array1
+     * @param array2
+     * @return
+     */
+    public static boolean isEqual(int size, byte[] array1, byte[] array2) { // @test
+        if (array1==array2) {
+            return true;
+        }
+        if (array1==null || array2==null) {
+            return false;
+        }
+        if(array1.length != array2.length && (array1.length < size || array2.length < size) ) {
+            return false;
+        }
+        for (int i=0, n=Math.min(array1.length, size); i<n; i++) {
+            if (array1[i] != array2[i]) { return false; }
+        }
+        return true;
+    }
+    
+}

+ 2 - 2
src/main/java/net/ranides/assira/collection/ListUtils.java

@@ -226,8 +226,8 @@ public final class ListUtils {
      * @return
      * @throws ClassCastException jeśli konwersja elementów jest niemożliwa do przeprowadzenia
      */
-    public static <T> T toArray(Class<T> arrayClazz, List<?> source) {
-        return ArrayUtils.toArray(arrayClazz, source);
+    public static <Auto> Auto toArray(Class<?> type, List<?> source) {
+        return (Auto)ArrayUtils.toArray(type, source);
     }
     
     /**

+ 1 - 1
src/main/java/net/ranides/assira/collection/SingleCollection.java

@@ -71,7 +71,7 @@ public class SingleCollection<T> implements List<T>, Set<T>  {
             result = target;
             Arrays.fill(target, 1, target.length, null);
         } else {
-            result = ArrayUtils.make(clazz, 1);
+            result = ArrayUtils.makeT(clazz, 1);
         }
         result[0] = clazz.cast(value);
         return result;

+ 2 - 2
src/main/java/net/ranides/assira/collection/list/NativeArrayList.java

@@ -68,7 +68,7 @@ public class NativeArrayList<T> extends AbstractList<T> implements RandomAccess
     public NativeArrayList(Object array) {
         checkType(array.getClass());
         this.array = array;
-        this.accessor = UnsafeArrayAccess.getInstance(array.getClass());
+        this.accessor = UnsafeArrayAccess.forType(array.getClass());
         this.capacity = Array.getLength(array);
         this.size = capacity;
     }
@@ -110,7 +110,7 @@ public class NativeArrayList<T> extends AbstractList<T> implements RandomAccess
         checkType(clazz);
         checkType(array.getClass());
         this.array = array;
-        this.accessor = UnsafeArrayAccess.getInstance(clazz, array.getClass());
+        this.accessor = UnsafeArrayAccess.forType(clazz, array.getClass());
         int ascale = UnsafeAccess.API.arrayIndexScale(array.getClass());
         int cscale = UnsafeAccess.API.arrayIndexScale(clazz);
         this.capacity = (Array.getLength(array) * ascale) / cscale;

+ 1 - 2
src/main/java/net/ranides/assira/math/Randomizer.java

@@ -10,7 +10,6 @@ package net.ranides.assira.math;
 import java.awt.Color;
 import java.util.ArrayList;
 import java.util.List;
-import net.ranides.assira.collection.ArrayUtils;
 import net.ranides.assira.reflection.ClassInspector;
 
 /**
@@ -187,7 +186,7 @@ public final class Randomizer {
         return chars.charAt( number(chars.length()) );
     }
 
-    @SuppressWarnings("unchecked")
+    @SuppressWarnings({"unchecked", "PMD"})
     public static <T> T object(Class<T> type) {
         Class<?> boxed = ClassInspector.box(type);
         if(Character.class.equals(boxed)) {

+ 1 - 1
src/main/java/net/ranides/assira/reflection/GenericFactory.java

@@ -122,7 +122,7 @@ public final class GenericFactory {
             if(type.getTypeParameters().length == 0) {
                 this.params = EMPTY_CLASSES;
             } else {
-                this.params = ArrayUtils.nCopies(type.getTypeParameters().length, UNKNOWN_CLASS);
+                this.params = ArrayUtils.make(type.getTypeParameters().length, UNKNOWN_CLASS);
             }
         }
         @Override

+ 187 - 86
src/main/java/net/ranides/assira/reflection/UnsafeArrayAccess.java

@@ -7,6 +7,8 @@
 
 package net.ranides.assira.reflection;
 
+import java.lang.reflect.Array;
+
 /**
  * Klasa udostępniająca bezpośredni dostęp do tablic dowolnego typu znaczniej 
  * wydajniej i JIT-friendly, niż reflection API. Samodzielnie zarządza 
@@ -26,73 +28,113 @@ package net.ranides.assira.reflection;
  * @author ranides
  */
 public abstract class UnsafeArrayAccess {
+    
+    public static UnsafeArrayAccess forObject(Object array) {
+        return forType(array.getClass());
+    }
+    
+    public static UnsafeArrayAccess forType(Class<?> type) {
+        return forType(type, type);
+    }
 
     /**
      * Tworzy obiekt {@code UnsafeArrayAccess} odczytujący danez tablicy o klasie
      * {@code sourceType} i prezentujący jako tablicę typu {@code targetType}.
      * <p>Uwaga! Argumenty mają definiować klasę tablicy, a nie jej komponentu</p>
-     * @param targetType typ, na który tablica będzie rzutowana
-     * @param sourceType realny typ tablicy przechowującej dane
+     * @param target typ, na który tablica będzie rzutowana
+     * @param source realny typ tablicy przechowującej dane
      * @return
      */
-    @SuppressWarnings("PMD.CyclomaticComplexity")
-    static public UnsafeArrayAccess getInstance(Class<?> targetType, Class<?> sourceType) {
-        if( !targetType.isArray() ) {
-            throw new IllegalArgumentException(targetType + " is not an array");
-        }
-        if(byte[].class.equals(targetType)) {
-            return ByteArrayNativeAccessor.getInstance(sourceType);
-        } else if(short[].class.equals(targetType)) {
-            return ShortArrayNativeAccessor.getInstance(sourceType);
-        } else if(int[].class.equals(targetType)) {
-            return IntArrayNativeAccessor.getInstance(sourceType);
-        } else if(long[].class.equals(targetType)) {
-            return LongArrayNativeAccessor.getInstance(sourceType);
-        } else if(char[].class.equals(targetType)) {
-            return CharArrayNativeAccessor.getInstance(sourceType);
-        } else if(float[].class.equals(targetType)) {
-            return FloatArrayNativeAccessor.getInstance(sourceType);
-        } else if(double[].class.equals(targetType)) {
-            return DoubleArrayNativeAccessor.getInstance(sourceType);
-        } else if(boolean[].class.equals(targetType)) {
-            return BooleanArrayNativeAccessor.getInstance(sourceType);
-        } else {
-            return ObjectArrayNativeAccessor.getInstance(sourceType);
-        }
+    @SuppressWarnings("PMD")
+    public static UnsafeArrayAccess forType(Class<?> target, Class<?> source) {
+        Class<?> ctarget = asComponentType(target);
+        Class<?> csource = asComponentType(source);
+        Class<?> asource = asArrayType(source);
+        
+        if(byte.class.equals(ctarget)) {
+            return ByteArrayNativeAccessor.getInstance(asource);
+        } 
+        if(short.class.equals(ctarget)) {
+            return ShortArrayNativeAccessor.getInstance(asource);
+        } 
+        if(int.class.equals(ctarget)) {
+            return IntArrayNativeAccessor.getInstance(asource);
+        } 
+        if(long.class.equals(ctarget)) {
+            return LongArrayNativeAccessor.getInstance(asource);
+        } 
+        if(char.class.equals(ctarget)) {
+            return CharArrayNativeAccessor.getInstance(asource);
+        } 
+        if(float.class.equals(ctarget)) {
+            return FloatArrayNativeAccessor.getInstance(asource);
+        } 
+        if(double.class.equals(ctarget)) {
+            return DoubleArrayNativeAccessor.getInstance(asource);
+        } 
+        if(boolean.class.equals(ctarget)) {
+            return BooleanArrayNativeAccessor.getInstance(asource);
+        } 
+        return ObjectArrayNativeAccessor.forComponent(csource);
     }
-
-    /**
-     * Tworzy obiekt {@code UnsafeArrayAccess} odczytujący dane z tablicy o klasie
-     * {@code arrayClazz}
-     * <p>Uwaga! {@code arrayClazz} ma definiować klasę tablicy, a nie jej komponentu</p>
-     * @param arrayClazz
-     * @return
-     */
-    @SuppressWarnings("PMD.CyclomaticComplexity")
-    static public UnsafeArrayAccess getInstance(Class<?> arrayClazz) {
-        if( !arrayClazz.isArray() ) {
-            throw new IllegalArgumentException(arrayClazz + " is not an array");
-        }
-        if(byte[].class.equals(arrayClazz)) {
-            return ByteArrayNativeAccessor.THIS;
-        } else if(short[].class.equals(arrayClazz)) {
-            return ShortArrayNativeAccessor.THIS;
-        } else if(int[].class.equals(arrayClazz)) {
-            return IntArrayNativeAccessor.THIS;
-        } else if(long[].class.equals(arrayClazz)) {
-            return LongArrayNativeAccessor.THIS;
-        } else if(char[].class.equals(arrayClazz)) {
-            return CharArrayNativeAccessor.THIS;
-        } else if(float[].class.equals(arrayClazz)) {
-            return FloatArrayNativeAccessor.THIS;
-        } else if(double[].class.equals(arrayClazz)) {
-            return DoubleArrayNativeAccessor.THIS;
-        } else if(boolean[].class.equals(arrayClazz)) {
-            return BooleanArrayNativeAccessor.THIS;
+    
+    @SuppressWarnings("PMD")
+    public static Class<?> asComponentType(Class<?> type) {
+        if( !type.isArray() ) {
+            return type;
         } else {
-            return ObjectArrayNativeAccessor.getInstance(arrayClazz);
+            return type.getComponentType();
         }
     }
+    
+    @SuppressWarnings("PMD")
+    public static Class<?> asArrayType(Class<?> type) {
+        return asArrayType(type, true, 1);
+    }
+    
+    @SuppressWarnings("PMD")
+    public static Class<?> asArrayType(Class<?> type, int dimensions) {
+        return asArrayType(type, false, dimensions);
+    }
+    
+    @SuppressWarnings("PMD")
+    private static Class<?> asArrayType(Class<?> type, boolean raw, int dimensions) {
+        Class<?> ctype = type;
+        for(int i=1; i<dimensions && ctype.isArray(); i++) {
+            ctype = ctype.getComponentType();
+        }
+        if( ctype.isArray() ) {
+            return ctype;
+        }
+        if(byte.class.equals(ctype)) {
+            return byte[].class;
+        } 
+        if(short.class.equals(ctype)) {
+            return short[].class;
+        } 
+        if(int.class.equals(ctype)) {
+            return int[].class;
+        } 
+        if(long.class.equals(ctype)) {
+            return long[].class;
+        } 
+        if(char.class.equals(ctype)) {
+            return char[].class;
+        } 
+        if(float.class.equals(ctype)) {
+            return float[].class;
+        } 
+        if(double.class.equals(ctype)) {
+            return double[].class;
+        } 
+        if(boolean.class.equals(ctype)) {
+            return boolean[].class;
+        }
+        if(raw) {
+            return Object[].class;
+        }
+        return Array.newInstance(ctype, 0).getClass();
+    }
 
 /* ************************************************************************** */
 
@@ -102,36 +144,16 @@ public abstract class UnsafeArrayAccess {
         this.base = UnsafeAccess.API.arrayBaseOffset(clazz);
     }
 
-    /**
-     * 
-     * @param object
-     * @param index
-     * @return
-     */
     public abstract Object get(Object object, int index);
 
-    /**
-     * 
-     * @param object
-     * @param index
-     * @param value
-     */
     public abstract void put(Object object, int index, Object value);
 
-    /**
-     * 
-     * @param object
-     * @param index
-     */
-    public abstract void putNull(Object object, int index);
+    public abstract void putNull(Object array, int index);
+    
+    public abstract Object create(int size);
+    
+    public abstract int length(Object array);
 
-    /**
-     * 
-     * @param object
-     * @param index
-     * @param value
-     * @return
-     */
     public Object change(Object object, int index, Object value) {
         Object prev = get(object, index);
         put(object, index, value);
@@ -164,6 +186,14 @@ public abstract class UnsafeArrayAccess {
         public void putNull(Object object, int index) {
             UnsafeAccess.API.putByte(object, base+index, (byte)0 );
         }
+        @Override
+        public Object create(int size) {
+            return new byte[size];
+        }
+        @Override
+        public int length(Object array) {
+            return ((byte[])array).length;
+        }
     }
 
     static class ShortArrayNativeAccessor extends UnsafeArrayAccess {
@@ -190,6 +220,14 @@ public abstract class UnsafeArrayAccess {
         public void putNull(Object object, int index) {
             UnsafeAccess.API.putShort(object, base+index, (short)0 );
         }
+        @Override
+        public Object create(int size) {
+            return new short[size];
+        }
+        @Override
+        public int length(Object array) {
+            return ((short[])array).length;
+        }
     }
 
     static class IntArrayNativeAccessor extends UnsafeArrayAccess {
@@ -216,6 +254,14 @@ public abstract class UnsafeArrayAccess {
         public void putNull(Object object, int index) {
             UnsafeAccess.API.putInt(object, base+index, 0 );
         }
+        @Override
+        public Object create(int size) {
+            return new int[size];
+        }
+        @Override
+        public int length(Object array) {
+            return ((int[])array).length;
+        }
     }
 
     static class LongArrayNativeAccessor extends UnsafeArrayAccess {
@@ -242,6 +288,14 @@ public abstract class UnsafeArrayAccess {
         public void putNull(Object object, int index) {
             UnsafeAccess.API.putLong(object, base+index, 0L );
         }
+        @Override
+        public Object create(int size) {
+            return new long[size];
+        }
+        @Override
+        public int length(Object array) {
+            return ((long[])array).length;
+        }
     }
 
     static class CharArrayNativeAccessor extends UnsafeArrayAccess {
@@ -268,6 +322,14 @@ public abstract class UnsafeArrayAccess {
         public void putNull(Object object, int index) {
             UnsafeAccess.API.putChar(object, base+index, '\000' );
         }
+        @Override
+        public Object create(int size) {
+            return new char[size];
+        }
+        @Override
+        public int length(Object array) {
+            return ((char[])array).length;
+        }
     }
 
     static class FloatArrayNativeAccessor extends UnsafeArrayAccess {
@@ -294,6 +356,14 @@ public abstract class UnsafeArrayAccess {
         public void putNull(Object object, int index) {
             UnsafeAccess.API.putFloat(object, base+index, .0f );
         }
+        @Override
+        public Object create(int size) {
+            return new float[size];
+        }
+        @Override
+        public int length(Object array) {
+            return ((float[])array).length;
+        }
     }
 
     static class DoubleArrayNativeAccessor extends UnsafeArrayAccess {
@@ -320,6 +390,14 @@ public abstract class UnsafeArrayAccess {
         public void putNull(Object object, int index) {
             UnsafeAccess.API.putDouble(object, base+index, .0 );
         }
+        @Override
+        public Object create(int size) {
+            return new double[size];
+        }
+        @Override
+        public int length(Object array) {
+            return ((double[])array).length;
+        }
     }
 
     static class BooleanArrayNativeAccessor extends UnsafeArrayAccess {
@@ -347,18 +425,29 @@ public abstract class UnsafeArrayAccess {
         public void putNull(Object object, int index) {
             UnsafeAccess.API.putBoolean(object, base+index, false );
         }
+        @Override
+        public Object create(int size) {
+            return new boolean[size];
+        }
+        @Override
+        public int length(Object array) {
+            return ((boolean[])array).length;
+        }
     }
 
     static class ObjectArrayNativeAccessor extends UnsafeArrayAccess {
 
         private static final long SCALE = UnsafeAccess.API.arrayIndexScale(Object[].class);
+        
+        private final Class<?> component;
 
-        ObjectArrayNativeAccessor(Class<?> clazz) {
-            super(clazz);
+        ObjectArrayNativeAccessor(Class<?> component) {
+            super(Object[].class);
+            this.component = component;
         }
 
-        public static UnsafeArrayAccess getInstance(Class<?> clazz) {
-            return new ObjectArrayNativeAccessor(clazz);
+        public static UnsafeArrayAccess forComponent(Class<?> component) {
+            return new ObjectArrayNativeAccessor(component);
         }
 
         @Override
@@ -367,12 +456,24 @@ public abstract class UnsafeArrayAccess {
         }
         @Override
         public void put(Object object, int index, Object value) {
-            UnsafeAccess.API.putObject(object, base+index*SCALE, value);
+            try {
+                UnsafeAccess.API.putObject(object, base+index*SCALE, component.cast(value));
+            } catch(ClassCastException _) {
+                throw new ClassCastException("cannot cast " + value.getClass().getName() + " to " + component);
+            }
         }
         @Override
         public void putNull(Object object, int index) {
             UnsafeAccess.API.putObject(object, base+index, null );
         }
+        @Override
+        public Object create(int size) {
+            return Array.newInstance(component, size);
+        }
+        @Override
+        public int length(Object array) {
+            return ((Object[])array).length;
+        }
     }
 
 }

+ 1 - 1
src/main/java/net/ranides/assira/text/format/AnsiString.java

@@ -50,7 +50,7 @@ public class AnsiString implements AnsiCharSequence, Serializable {
     }
     
     public AnsiString(AnsiColorScheme scheme, int size, char c) {
-        this(Init.DIRECT, scheme, ArrayUtils.nCopies(size, c));
+        this(Init.DIRECT, scheme, (char[])ArrayUtils.make(size, c));
     }
     
     public AnsiString(AnsiCharSequence source, int begin, int end) {

+ 180 - 2
src/test/java/net/ranides/assira/collection/ArrayUtilsTest.java

@@ -51,6 +51,11 @@ public class ArrayUtilsTest {
         assertArrayEquals(rawArray, new int[]{1,2,77,78,5,6});
     }
     
+    @Test
+    public void testWrapCast() {
+        // @test ArrayUtils.wrap(clazz, this)
+    }
+    
     @Test
     public void testAsList() {
         Integer[] boxArray = new Integer[]{1,2,3,4,5,6};
@@ -339,10 +344,10 @@ public class ArrayUtilsTest {
         assertArrayEquals(new long[]{10L, 20L, 30L, 40L, 50L}, primtvArray);
         
         try {
-            String[] stringArray = ArrayUtils.toArray(String[].class, longList);
+            String[] result = ArrayUtils.toArray(String[].class, longList);
             fail("cast exception expected");
         } catch(ClassCastException cause) {
-            assertEquals(cause.getMessage(), "cannot cast java.lang.Long to class java.lang.String");
+            assertEquals("cannot cast java.lang.Long to class java.lang.String", cause.getMessage());
         }
     }
 
@@ -387,6 +392,7 @@ public class ArrayUtilsTest {
         );
     }
     
+    @SuppressWarnings("PMD")
     @Test
     public void testConcatPrimitive() {
         new ConcatTest<byte[]>(0, 10, byte[].class){ @Override void run() {
@@ -542,4 +548,176 @@ public class ArrayUtilsTest {
             }
         }
     }
+
+    @Test
+    public void testFirst() {
+        assertNull( ArrayUtils.first(null) );
+        assertNull( ArrayUtils.first(new Integer[]{}) );
+        assertEquals( (Integer)7, ArrayUtils.first(new Integer[]{7}) );
+        assertEquals( (Integer)7, ArrayUtils.first(new Integer[]{7,2,3}) );
+        
+        assertNull( ArrayUtils.first(null, String.class) );
+        assertNull( ArrayUtils.first(new Number[]{}, Float.class) );
+        assertNull( ArrayUtils.first( new Number[]{7.0f}, Double.class) );
+        assertEquals( 7.0, ArrayUtils.first( new Number[]{1, 2.0f, 7.0, 8.0f, 9.0 }, Double.class), 0.1 );
+        assertEquals( 7.0, ArrayUtils.first( new Number[]{1, 2.0f, 7.0, 8.0f, 9.0 }, Double.class), 0.1 );
+    }
+    
+    @Test
+    public void testLast() {
+        assertNull( ArrayUtils.last(null) );
+        assertNull( ArrayUtils.last(new Integer[]{}) );
+        assertEquals( (Integer)7, ArrayUtils.last(new Integer[]{7}) );
+        assertEquals( (Integer)3, ArrayUtils.last(new Integer[]{7,2,3}) );
+        
+        assertNull( ArrayUtils.last(null, String.class) );
+        assertNull( ArrayUtils.last(new Number[]{}, Float.class) );
+        assertNull( ArrayUtils.last( new Number[]{7.0f}, Double.class) );
+        assertEquals( 9.0, ArrayUtils.last( new Number[]{1, 2.0f, 7.0, 8.0f, 9.0 }, Double.class), 0.1 );
+        assertEquals( 9.0, ArrayUtils.last( new Number[]{1, 2.0f, 7.0, 8.0f, 9.0 }, Double.class), 0.1 );
+    }
+    
+    @Test
+    public void testSize() {
+        assertEquals(0, ArrayUtils.size(null) );
+        assertEquals(0, ArrayUtils.size(new int[0]) );
+        assertEquals(0, ArrayUtils.size(new Integer[0]) );
+        
+        assertEquals(4, ArrayUtils.size(new int[4]) );
+        assertEquals(4, ArrayUtils.size(new Integer[4]) );
+        
+        try {
+            ArrayUtils.size("");
+        } catch(IllegalArgumentException cause) {
+            assertTrue( cause.getMessage().contains("type is not an array"));
+        }
+        
+        
+    }
+    
+    @Test
+    public void testIsEmpty() {
+        assertTrue( ArrayUtils.isEmpty(null) );
+        assertTrue( ArrayUtils.isEmpty(new int[0]) );
+        assertTrue( ArrayUtils.isEmpty(new Integer[0]) );
+        assertFalse( ArrayUtils.isEmpty(new int[3]) );
+        assertFalse( ArrayUtils.isEmpty(new Integer[3]) );
+        
+        try {
+            ArrayUtils.isEmpty("");
+        } catch(IllegalArgumentException cause) {
+            assertTrue( cause.getMessage().contains("type is not an array"));
+        }
+    }
+    
+    @Test
+    public void testClip() {
+        int[] int0 = new int[]{};
+        int[] int3 = new int[]{1, 2, 3};
+        int[] int5 = new int[]{1, 2, 3, 4, 5};
+        
+        Integer[] integer0 = new Integer[]{};
+        Integer[] integer3 = new Integer[]{1, 2, 3};
+        Integer[] integer5 = new Integer[]{1, 2, 3, 4, 5};
+        
+        assertArrayEquals(int0, ArrayUtils.clip(int0, 0));
+        assertArrayEquals(int0, ArrayUtils.clip(int3, 0));
+        assertArrayEquals(int0, ArrayUtils.clip(int0, 2));
+        
+        assertArrayEquals(int3, ArrayUtils.clip(int3, 3));
+        assertArrayEquals(int3, ArrayUtils.clip(int3, 5));
+        assertArrayEquals(int3, ArrayUtils.clip(int5, 3));
+        
+        assertArrayEquals(integer0, ArrayUtils.clip(integer0, 0));
+        assertArrayEquals(integer0, ArrayUtils.clip(integer3, 0));
+        assertArrayEquals(integer0, ArrayUtils.clip(integer0, 2));
+        
+        assertArrayEquals(integer3, ArrayUtils.clip(integer3, 3));
+        assertArrayEquals(integer3, ArrayUtils.clip(integer3, 5));
+        assertArrayEquals(integer3, ArrayUtils.clip(integer5, 3));
+
+    }
+    
+    @Test
+    public void testMake() {
+        int[] int4zeroA = ArrayUtils.make(int.class, 4);
+        int[] int4fiveA = ArrayUtils.make(int.class, 4, 5);
+        Integer[] int4A = ArrayUtils.make(Integer.class, 4, 9);
+        
+        int[] int4zeroB = ArrayUtils.make(int[].class, 4);
+        int[] int4fiveB = ArrayUtils.make(int[].class, 4, 5);
+        Integer[] int4B = ArrayUtils.make(Integer[].class, 4, 9);
+        
+        int[] int4seven = ArrayUtils.make(4, 7);
+        String[] text5a = ArrayUtils.make(5, "a");
+        
+        Number[] object1 = ArrayUtils.makeT(Integer.class, 4);
+        
+        Integer[] object2 = ArrayUtils.makeT(Integer.class, 4, 0);
+        Number[]  object3 = ArrayUtils.makeT(Float.class, 4, 0f);
+        
+        Number[] object4 = ArrayUtils.makeT(4, 7L);
+        Number[] object5 = ArrayUtils.makeT(4, 7f);
+        
+        assertArrayEquals(new int[]{0,0,0,0}, int4zeroA);
+        assertArrayEquals(new int[]{5,5,5,5}, int4fiveA);
+        assertArrayEquals(new Integer[]{9,9,9,9}, int4A);
+        
+        assertArrayEquals(new int[]{0,0,0,0}, int4zeroB);
+        assertArrayEquals(new int[]{5,5,5,5}, int4fiveB);
+        assertArrayEquals(new Integer[]{9,9,9,9}, int4B);
+        
+        assertArrayEquals(new int[]{7,7,7,7}, int4seven);
+        
+        assertArrayEquals(new String[]{"a","a","a","a","a"}, text5a);
+        
+        assertArrayEquals(new Integer[]{null,null,null,null}, object1);
+        assertArrayEquals(new Integer[]{0,0,0,0}, object2);
+        assertArrayEquals(new Float[]{0f,0f,0f,0f}, object3);
+        assertArrayEquals(new Long[]{7L,7L,7L,7L}, object4);
+        assertArrayEquals(new Float[]{7f,7f,7f,7f}, object5);
+        
+        try {
+            int[] int4cast = ArrayUtils.make(int[].class, 4, (short)5);
+            fail("exception expected");
+        } catch(ClassCastException cause) {
+            assertTrue(cause.getMessage().matches(".*Short.*Integer.*"));
+        }
+        try {
+            String[] int4cast = ArrayUtils.make(int[].class, 4, 5);
+            fail("exception expected");
+        } catch(ClassCastException cause) {
+            assertTrue(cause.getMessage().matches(".*\\[I.*String;*"));
+        }
+        try {
+            int[] long4cast = ArrayUtils.make(4, 7L);
+            fail("exception expected");
+        } catch(ClassCastException cause) {
+            assertTrue(cause.getMessage().matches(".*\\[J.*\\[I*"));
+        }
+        try {
+            ArrayUtils.make(4, null);
+        } catch(AssertionError cause) {
+            assertTrue(true); // dla trybu z włączonymi asercjami
+        } catch(NullPointerException cause) {
+            assertTrue(true); // dla trybu release 
+        }
+        try {
+            Integer[] object1cast = ArrayUtils.makeT(int.class, 7);
+        } catch(ClassCastException cause) {
+            assertTrue(cause.getMessage().matches(".*\\[I.*Object.*"));
+        } 
+    }
+    
+    @Test
+    public void testPair() {
+        // ArrayUtils.Pair
+        // ArrayUtils.IntPair
+    }
+    
+    @Test
+    public void testIterator() {
+        
+    }
+
 }