Переглянути джерело

* javadoc

 * ArrayUtils # make(size, value) - akceptuje null

 * asm.AsmLabeler - przerobiono na klasę abstrakcyjną dając domyślne implementacje kilku metod
 * asm.AsmPrinter i asm.AsmBuffer - konstruktory tworzące domyślny AsmFormat
Ranides Atterwim 12 роки тому
батько
коміт
1d3a0bee51

+ 1 - 1
pom.xml

@@ -4,7 +4,7 @@
 
     <groupId>net.ranides</groupId>
     <artifactId>assira</artifactId>
-    <version>0.62.0</version>
+    <version>0.63.1</version>
     <packaging>jar</packaging>
 
     <name>assira</name>

+ 47 - 41
src/main/java/net/ranides/assira/asm/AsmBuffer.java

@@ -18,11 +18,18 @@ import net.ranides.asm.util.ASMifiable;
 import net.ranides.asm.util.Printer;
 
 /**
- *
+ * <p>Klasa do generowania String'ów używanych przez AsmPrinter. Zawiera metodę
+ * "append", która jest odpowiednikiem klasycznej funkcji "printf", ale obsługuje
+ * zupełnie inne rodzaje formatowania, tzn jest specializowana z myślą o konstruowaniu
+ * wstawek kodu ASM.
+ * </p><p>
+ * Klasa raczej pomocnicza, pozwala np sformatowanie liczby 0x3002D do tekstu "V1_1"
+ * za pomocą: {@code new AsmBuffer().append("%v", 0x3002D).toString() }
+ * </p>
  * @author ranides
  */
 public class AsmBuffer {
-    
+
     private final AsmLabeler labeler = new Labeler();
     private final Map<Label, String> labels = new HashMap<Label, String>();
     private final Map<Label, String> labelsView = Collections.unmodifiableMap(labels);
@@ -30,6 +37,16 @@ public class AsmBuffer {
     private final AsmFormat format;
     private final String visitor;
 
+    public AsmBuffer() {
+        this.builder = new StringBuilder();
+        this.format = AsmFormat.getDefault();
+        this.visitor = this.format.getClassWriter();
+    }
+
+    public AsmBuffer(AsmFormat format) {
+        this(format, format.getClassWriter());
+    }
+
     public AsmBuffer(AsmFormat format, String visitor) {
         this.builder = new StringBuilder();
         this.format = format;
@@ -40,14 +57,19 @@ public class AsmBuffer {
     public String toString() {
         return builder.toString();
     }
-    
+
+    /**
+     * Czyści zawartość bufora.
+     * @return this
+     */
     public AsmBuffer reset() {
         builder.setLength(0);
         return this;
     }
-    
+
     /**
-     * Format specifiers:
+     * Method with printf-like behaviour, but with ASM-specific format specifiers.
+     * Formats passed {@code pattern} and appends result to buffer.
      * <table>
      *  <tr><th>symbol</th> <th>type</th> <th>description</th> <th>reference</th></tr>
      *  <tr><td>{@code %%}</td>  <td>-</td>  <td>percent sign</td><td>-</td></tr>
@@ -78,7 +100,7 @@ public class AsmBuffer {
      * </table>
      * @param pattern
      * @param arguments
-     * @return 
+     * @return
      */
     @SuppressWarnings("PMD")
     public AsmBuffer append(String pattern, Object... arguments) {
@@ -86,7 +108,7 @@ public class AsmBuffer {
         for(int i=0, n=pattern.length(); i<n; i++) {
             if( '#' == pattern.charAt(i) && i+1<n ) {
                 switch(pattern.charAt(++i)) {
-                    case '#': 
+                    case '#':
                         builder.append('#');
                         break;
                     case 'a':
@@ -118,7 +140,7 @@ public class AsmBuffer {
             }
             if( '%' == pattern.charAt(i) && i+1<n ) {
                 switch(pattern.charAt(++i)) {
-                    case '%': 
+                    case '%':
                         builder.append('%');
                         break;
                     case 'i':
@@ -181,13 +203,13 @@ public class AsmBuffer {
                         break;
                 }
                 continue;
-            } 
+            }
 
             builder.append(pattern.charAt(i));
         }
         return this;
     }
-    
+
     private AsmBuffer appendRaws(Object value) {
         List<Object> list = ArrayUtils.wrap(value);
         for(int i=0, n=list.size(); i<n; i++) {
@@ -196,7 +218,7 @@ public class AsmBuffer {
         }
         return this;
     }
-    
+
     @SuppressWarnings("PMD")
     private AsmBuffer appendStr(Object value) {
         final String text = String.valueOf(value);
@@ -228,7 +250,7 @@ public class AsmBuffer {
         builder.append('\"');
         return this;
     }
-    
+
     private AsmBuffer appendStrs(Object value) {
         List<Object> list = ArrayUtils.wrap(value);
         for(int i=0, n=list.size(); i<n; i++) {
@@ -237,7 +259,7 @@ public class AsmBuffer {
         }
         return this;
     }
-    
+
     private AsmBuffer appendConsts(Object value) {
         List<Object> list = ArrayUtils.wrap(value);
         for(int i=0, n=list.size(); i<n; i++) {
@@ -246,7 +268,7 @@ public class AsmBuffer {
         }
         return this;
     }
-    
+
     @SuppressWarnings("PMD")
     private void appendConst(Object value) {
         if (value == null) {
@@ -339,31 +361,31 @@ public class AsmBuffer {
             builder.append('}');
         }
     }
-    
+
     private void appendFrameTypes(final int size, final Object[] frames) {
         for (int i = 0; i < size; ++i) {
             if(i>0) { builder.append(", "); }
             appendFrameType(frames[i]);
         }
     }
-    
+
     private void appendFrameType(Object value) {
         if (value instanceof String) {
             appendConst(value);
-        } 
+        }
         else if (value instanceof Integer) {
             builder.append( Asmifier.fromFrameType((Integer)value) );
-        } 
+        }
         else {
             appendLabel(value);
         }
     }
-    
+
     private AsmBuffer appendLabel(Object value) {
         builder.append(labels.get( (Label)value ));
         return this;
     }
-    
+
     private AsmBuffer appendLabels(Object value) {
         Label[] values = (Label[])value;
         for(int i=0, n=values.length; i<n; i++) {
@@ -372,12 +394,12 @@ public class AsmBuffer {
         }
         return this;
     }
-    
+
     public AsmLabeler labels() {
         return labeler;
     }
-    
-    private final class Labeler implements AsmLabeler {
+
+    private final class Labeler extends AsmLabeler {
 
         @Override
         public AsmLabeler declare(Label value) {
@@ -389,27 +411,11 @@ public class AsmBuffer {
             return this;
         }
 
-        @Override
-        public AsmLabeler declare(Label[] values) {
-            for(Label value : values) {
-                declare(value);
-            }
-            return this;
-        }
-
-        @Override
-        public AsmLabeler declare(int size, Object[] labels) {
-            for (int i = 0; i < size; ++i) {
-                if (labels[i] instanceof Label) { declare( (Label)labels[i] ); }
-            }
-            return this;
-        }
-
         @Override
         public Map<Label, String> map() {
             return labelsView;
         }
-        
+
     }
-    
+
 }

+ 50 - 10
src/main/java/net/ranides/assira/asm/AsmLabeler.java

@@ -10,17 +10,57 @@ import java.util.Map;
 import net.ranides.asm.Label;
 
 /**
- *
+ * Klasa abstrakcyjna służąca do zarządzania etykietami ASM. Klasa
+ * ma pilnować, aby każda etykieta była deklarowana w kodzie tylko raz oraz żeby
+ * każde odwołanie do tej samej instancji {@link Label} było konwertowane do tego
+ * samego tekstu.
  * @author ranides
  */
-public interface AsmLabeler {
-        
-    AsmLabeler declare(Label value);
-    
-    AsmLabeler declare(Label[] values);
-    
-    AsmLabeler declare(final int size, final Object[] values);
+public abstract class AsmLabeler {
+
+    /**
+     * Zapamiętuje podaną etykietę wykrytą przez ASM podczas analizy. Jeśli
+     * etykiety wcześniej nie było, to rezerwuje dla niej nazwę zmiennej, przez
+     * którą będzie reprezentowana w generowanym kodzie.
+     * @param value
+     * @return this
+     */
+    public abstract AsmLabeler declare(Label value);
+
+    /**
+     * Zapamiętuje podane etykiety wykryte przez ASM podczas analizy.
+     * Dla każdej etykiety rezerwuje nazwę zmiennej, która będzie ją reprezentować
+     * w generowanym kodzie - o ile wcześniej etykieta nie została już zadeklarowana.
+     * @param values
+     * @return this
+     */
+    public final AsmLabeler declare(Label[] values) {
+        for(Label value : values) {
+            declare(value);
+        }
+        return this;
+    }
+
+    /**
+     * Przegląda pierwsze {@code size} elementów z podanej tablicy, jeśli element
+     * jest etykietą (instancją {@link Label}), to ją deklaruje.
+     * @param size
+     * @param values
+     * @return
+     */
+    public final AsmLabeler declare(final int size, final Object[] values) {
+        for(int i = 0; i < size; ++i) {
+            if (values[i] instanceof Label) {
+                declare( (Label)values[i] );
+            }
+        }
+        return this;
+    }
+
+    /**
+     * Zwraca mapę tylko do odczytu, która zawiera wszystkie zadeklarowane etykiety.
+     * @return
+     */
+    public abstract Map<Label, String> map();
 
-    Map<Label, String> map();
-    
 }

+ 14 - 10
src/main/java/net/ranides/assira/asm/AsmPrinter.java

@@ -19,17 +19,21 @@ import net.ranides.asm.util.Printer;
  */
 @SuppressWarnings("unchecked")
 public class AsmPrinter extends Printer {
-    
+
     private static final int CLOSE = -1;
-    
+
     private static final int OPEN  = +1;
-    
+
     private final AsmFormat format;
-    
+
     private final int id;
-    
+
     private final AsmBuffer buffer;
 
+    public AsmPrinter() {
+        this(AsmFormat.getDefault());
+    }
+
     public AsmPrinter(AsmFormat format) {
         this(format, format.getClassWriter(), 0);
     }
@@ -47,7 +51,7 @@ public class AsmPrinter extends Printer {
         if(null != format.getPackageName(name)) {
             buffer.append("package #p;\n", name);
         }
-        
+
         buffer.append("import net.ranides.asm.*;\n\n");
         buffer.append("@SuppressWarnings(\"PMD\")\n");
         buffer.append("public class #n implements Opcodes {\n\n", name);
@@ -172,7 +176,7 @@ public class AsmPrinter extends Printer {
         return visitAnnotation(desc, visible);
     }
 
-    
+
     @Override
     public AsmPrinter visitParameterAnnotation(int parameter, String desc, boolean visible) {
         indent(OPEN);
@@ -357,14 +361,14 @@ public class AsmPrinter extends Printer {
         if( format.isCommentAnnotations() ) {
             addLine("// ATTRIBUTE %i", attr.type);
         }
-        
+
         if (attr instanceof ASMifiable) {
             indent(OPEN);
             addLine("%m#v.visitAttribute(attr);", attr);
             indent(CLOSE);
         }
     }
-    
+
     protected void indent(int indent) {
         for(int i=0; i<indent; i++) {
             text.add("{\n");
@@ -389,5 +393,5 @@ public class AsmPrinter extends Printer {
         buffer.append("\n");
         text.add(buffer.toString());
     }
-    
+
 }

+ 129 - 5
src/main/java/net/ranides/assira/collection/ArrayUtils.java

@@ -65,6 +65,7 @@ public final class ArrayUtils {
      * Wyszukuje i zwraca pierwszy obiekt o klasie T, lub null, jeśli takiej wartości brak
      *
      * <div class="message-note">{@code null} safe method</div>
+     * @param <K>
      * @param <T>
      * @param values tablica, w której obiekt klasy jest szukany
      * @param clazz szukana klasa
@@ -139,6 +140,12 @@ public final class ArrayUtils {
         return null == values ? 0 : values.length;
     }
 
+    /**
+     * Zwraca rozmiar podanej tablicy. Jeśli przekazany obiekt nie jest tablicą
+     * (np int[]) to rzuca wyjątek.
+     * @param values
+     * @return
+     */
     public static int size(Object values) {
         if(null == values) {
             return 0;
@@ -189,6 +196,17 @@ public final class ArrayUtils {
         return result;
     }
 
+    /**
+     * Przycina tablicę do podanego rozmiaru. To znaczy, jeśli tablica ma rozmiar
+     * większy od {@code size}, to tworzona jest nowa tablica identycznego typu
+     * ale o rozmiarze {@code size}, zawartość jest przepisywana, i ta nowa kopia
+     * jest zwracana jako wynik. Jeśli tablica ma rozmiar mniejszy od {@code size},
+     * to nic nie jest robione - funkcja zwraca {@code array}.
+     * @param <ArrayT>
+     * @param array
+     * @param size
+     * @return
+     */
     @SuppressWarnings({"unchecked", "SuspiciousSystemArraycopy"})
     public static <ArrayT> ArrayT clip(ArrayT array, int size) {
         if( size(array) <= size ) {
@@ -288,8 +306,8 @@ public final class ArrayUtils {
      * Konwertuje listę elementów na tablicę o podanym typie, dokonując po drodze
      * odpowiednich rzutowań i unbox'owań, jeśli są konieczne. Zwrócona tablica jest
      * niezależna od listy.
-     * @param <T>
-     * @param array
+     * @param <Auto>
+     * @param type
      * @param source
      * @return
      * @throws ClassCastException jeśli konwersja elementów jest niemożliwa do przeprowadzenia
@@ -412,11 +430,51 @@ public final class ArrayUtils {
 
 /* ************************************************************************** */
 
+    /**
+     * Tworzy tablicę o podanym typie oraz rozmiarze. Może tworzyć tablice typów
+     * prostych. Podając jako typ {@code int.class} dostaniemy jako wynik tablicę
+     * {@code int[]}. Dla obiektów tworzy tablice o dokładnym typie, a nie tablice
+     * {@code Object[]}, to znaczy, że podając {@code String.class} otrzymamy
+     * jako wynik {@code String[]}.
+     * <p>
+     * Metoda jest o tyle wygodna i jednocześnie niebezpieczna, że zwraca typ,
+     * który musi być inferred przez kompilator. Czyli możemy napisać bez rzutowania:
+     * <p>
+     * <pre>
+     * {@code int[] v1 = make(int.class, 6); // ok }
+     * {@code int[] v2 = make(float.class, 6); // undetected error }
+     * </pre>
+     * @param <Auto>
+     * @param type
+     * @param size
+     * @return
+     */
     @SuppressWarnings("unchecked")
     public static <Auto> Auto make(Class<?> type, int size) {
         return (Auto)UnsafeArrayAccess.forType(type).create(size);
     }
 
+    /**
+     * Tworzy tablicę o podanym typie oraz rozmiarze inicjalizując elementy
+     * wartością {@code value}. Może tworzyć tablice typów
+     * prostych. Podając jako typ {@code int.class} dostaniemy jako wynik tablicę
+     * {@code int[]}. Dla obiektów tworzy tablice o dokładnym typie, a nie tablice
+     * {@code Object[]}, to znaczy, że podając {@code String.class} otrzymamy
+     * jako wynik {@code String[]}.
+     * <p>
+     * Metoda jest o tyle wygodna i jednocześnie niebezpieczna, że zwraca typ,
+     * który musi być inferred przez kompilator. Czyli możemy napisać bez rzutowania:
+     * <p>
+     * <pre>
+     * {@code int[] v1 = make(int.class, 6); // ok }
+     * {@code int[] v2 = make(float.class, 6); // undetected error }
+     * </pre>
+     * @param <Auto>
+     * @param type
+     * @param size
+     * @param value
+     * @return
+     */
     @SuppressWarnings("unchecked")
     public static <Auto> Auto make(Class<?> type, int size, Object value) {
         UnsafeArrayAccess access = UnsafeArrayAccess.forType(type);
@@ -427,10 +485,31 @@ public final class ArrayUtils {
         return (Auto)content;
     }
 
+    /**
+     * Tworzy tablicę o podanym rozmiarze inicjalizując elementy
+     * wartością {@code value}. Typ tablicy jest dedukowany na podstawie typu
+     * wartości.
+     * <p>
+     * Metoda preferuje tworzenie tablic typu wbudowanego, co oznacza,
+     * że przekazując jako {@code value} zmienną typu {@code Integer} lub {@code int}
+     * otrzymamy w obu przypadkach jako wynik {@code int[]}
+     * </p><p>
+     * Jeśli jako wartość zostanie przekazany {@code null}, to zwrócona zostanie
+     * tablica typu {@code Object[]}, oczywiście o podanym rozmiarze.
+     * </p><p>
+     * Metoda jest o tyle wygodna i jednocześnie niebezpieczna, że zwraca typ,
+     * który musi być inferred przez kompilator. Czyli możemy napisać bez rzutowania:
+     * <p>
+     * @param <Auto>
+     * @param size
+     * @param value
+     * @return
+     */
     @SuppressWarnings("unchecked")
     public static <Auto> Auto make(int size, Object value) {
-        assert value != null : "value must be specified";
-
+        if( value == null ) {
+            return (Auto)new Object[size];
+        }
         Class<?> type = ClassInspector.unbox(value.getClass());
         UnsafeArrayAccess access = UnsafeArrayAccess.forType(type);
         Object content = access.create(size);
@@ -443,7 +522,7 @@ public final class ArrayUtils {
     /**
      * 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}.
+     * Do utworzenia takich tablic użyj jednej z funkcji {@link #make}.
      * @param <T>
      * @param component
      * @param size
@@ -454,6 +533,17 @@ public final class ArrayUtils {
         return (T[])Array.newInstance(component, size);
     }
 
+    /**
+     * Tworzy nową tablicę o podanym rozmiarze, która może przechowywać obiekty
+     * podanego typu, inicjalizując elementy podaną wartością. Metoda nie nadaje
+     * się do tworzenia tablic typu prostego. Do utworzenia takich tablic użyj
+     * jednej z funkcji {@link #make}.
+     * @param <T>
+     * @param component
+     * @param size
+     * @param value
+     * @return
+     */
     @SuppressWarnings("unchecked")
     public static <T extends Object> T[] makeT(Class<T> component, int size, T value) {
         T[] content = makeT(component, size);
@@ -461,6 +551,20 @@ public final class ArrayUtils {
         return content;
     }
 
+    /**
+     * Tworzy nową tablicę o podanym rozmiarze, inicjalizując elementy podaną
+     * wartością. Typ tablicy jest dedukowany z typu argumentu. Metoda nie nadaje
+     * się do tworzenia tablic typu prostego. Do utworzenia takich tablic użyj
+     * jednej z funkcji {@link #make}.
+     * <p>
+     * Uwaga! Jeśli {@code value} jest tak naprawdę klasą C (pochodną {@code T}),
+     * to zostanie utworzona tablica typu {@code C[]}, a nie {@code T[]}.
+     * </p>
+     * @param <T>
+     * @param size
+     * @param value
+     * @return
+     */
     @SuppressWarnings("unchecked")
     public static <T> T[] makeT(int size, T value) {
         return makeT((Class<T>)value.getClass(), size, value);
@@ -470,6 +574,7 @@ public final class ArrayUtils {
 
     /**
      * Zobacz: {@link #shift(Object[]) shift}
+     * @param <Auto>
      * @param values
      * @return
      */
@@ -506,6 +611,7 @@ public final class ArrayUtils {
 
     /**
      * Zobacz: {@link #unshift(Object[]) unshift}
+     * @param <Auto>
      * @param values
      * @return
      */
@@ -542,6 +648,7 @@ public final class ArrayUtils {
 
     /**
      * Zobacz: {@link #concat(Object[], Object[]) concat}
+     * @param <ArrayT>
      * @param first
      * @param second
      * @return
@@ -660,6 +767,7 @@ public final class ArrayUtils {
      * Kopiuje zawartość podanej tablicy. Metoda posiada przeładowane wersje
      * przeznaczone do obsługi tablic obiektów, jak również tablic typów prostych
      * (w takim wypadku tracona jest informacja o typie).
+     * @param <ArrayT>
      * @param source
      * @return
      */
@@ -701,6 +809,14 @@ public final class ArrayUtils {
         return indexOf(array, value) >= 0;
     }
 
+    /**
+     * Konwertuje wszystkie elementy tablicy za pomocą przekazanej funkcji,
+     * umieszczając wynik w nowej tablicy typu {@code Object[]}
+     * @param <S>
+     * @param values
+     * @param function
+     * @return
+     */
     public static <S> Object[] apply(S[] values, Function<?,S> function) {
         if(values == null) {
             return values;
@@ -713,6 +829,14 @@ public final class ArrayUtils {
         return target;
     }
 
+    /**
+     * Konwertuje wszystkie elementy tablicy za pomocą przekazanej funkcji,
+     * umieszczając wynik w nowej tablicy typu {@code Object[]}
+     * @param <T>
+     * @param values
+     * @param function
+     * @return
+     */
     public static <T> Object[] inverse(T[] values, CoFunction<T,?> function) {
         if(values == null) {
             return values;

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

@@ -16,12 +16,27 @@ public final class ByteArrays {
         // utility class
     }
 
+    /**
+     * Tworzy nową tablicę o podanym rozmiarze, inicjalizując wszystkie elementy
+     * podaną wartością.
+     * @param size
+     * @param value
+     * @return
+     */
     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;
     }
 
+    /**
+     * Usuwa z tablicy pierwszy element, przesuwając wszystkie pozostałe o 1
+     * pozycję w lewo. Jako wynik zwraca wartość usuniętego elementu. Uwaga:
+     * rozmiar tablicy nie jest zmieniany, wartość ostatniego elementu też - co
+     * oznacza, że wartość ostatniego elementu jest duplikowana.
+     * @param values
+     * @return
+     */
     public static byte shift(byte[] values) {
         if(values == null || values.length==0) { throw new EmptyCollectionException(); }
         byte value = values[0];
@@ -29,6 +44,14 @@ public final class ByteArrays {
         return value;
     }
 
+    /**
+     * Usuwa z tablicy ostatni element, przesuwając wszystkie pozostałe o 1
+     * pozycję w prawo. Jako wynik zwraca wartość usuniętego elementu. Uwaga:
+     * rozmiar tablicy nie jest zmieniany, wartość pierwszego elementu też - co
+     * oznacza, że wartość pierwszego elementu jest duplikowana.
+     * @param values
+     * @return
+     */
     public static byte unshift(byte[] values) {
         if(values == null || values.length==0) { throw new EmptyCollectionException(); }
         byte value = values[values.length-1];
@@ -36,6 +59,12 @@ public final class ByteArrays {
         return value;
     }
 
+    /**
+     * Tworzy nową tablicę i kopiuje do niej zawartość obu tablic.
+     * @param first
+     * @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);

+ 377 - 377
src/main/java/net/ranides/assira/collection/CollectionUtils.java

@@ -1,377 +1,377 @@
-/*
- *  @author Ranides Atterwim <ranides@gmail.com>
- *  @copyright Ranides Atterwim
- *  @license WTFPL
- *  @url http://ranides.net/projects/assira
- */
-
-package net.ranides.assira.collection;
-
-import java.util.AbstractCollection;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Iterator;
-import net.ranides.assira.generic.Function;
-
-/**
- * Generyczne operacje na kolekcjach. Jeśli to możliwe, korzystać z innych klas
- * dedykowanych dla konkretnego typu kolekcji, ponieważ poniższe metody opierają
- * się głównie na iteratorach i często mają bardzo kiepską złożoność rzędu O(N)
- * @author ranides
- */
-@SuppressWarnings({"PMD.ShortVar"})
-public final class CollectionUtils {
-
-    private CollectionUtils() { }
-
-
-    /**
-     * Pobiera index-ty element z kolekcji, lub zwraca wartość domyślną, jeśli
-     * kolekcja ma mniejszy rozmiar niż konieczne.
-     *
-     * <div class="message-note">{@code null} safe method</div>
-     * @param <T>
-     * @param values
-     * @param index
-     * @param ddefault
-     * @return
-     */
-    public static <T> T get(Collection<T> values, int index, T ddefault) {
-        if(null == values) { return ddefault; }
-        if(index >= values.size() ) { return ddefault; }
-        Iterator<T> iterator = values.iterator();
-        // pomijamy (index-1) elementów
-        for(int i=1; i<index; i++) {
-            iterator.next();
-        }
-        return iterator.next();
-    }
-
-
-    /**
-     * Pobiera index-ty element z kolekcji, lub zwraca null, jeśli
-     * kolekcja ma mniejszy rozmiar niż konieczne.
-     *
-     * <div class="message-note">{@code null} safe method</div>
-     * @param <T>
-     * @param values
-     * @param index
-     * @return
-     */
-    public static <T> T get(Collection<T> values, int index) {
-        return get(values, index, null);
-    }
-
-
-    /**
-     * Wyszukuje i zwraca pierwszy obiekt o klasie T, lub null, jeśli takiej wartości brak
-     *
-     * <div class="message-note">{@code null} safe method</div>
-     * @param <T>
-     * @param values kolekcja, w której obiekt klasy jest szukany
-     * @param clazz szukana klasa
-     * @return
-     */
-    public static <T> T first(Collection<?> values, Class<T> clazz) {
-        if(null == values) { return null; }
-        Iterator<?> i = values.iterator();
-        while(i.hasNext()) {
-            Object value = i.next();
-            if(clazz.isInstance(value)) { return clazz.cast(value); }
-        }
-        return null;
-    }
-
-
-    /**
-     * Wyszukuje i zwraca pierwszy obiekt w kolekcji, lub null, jeśli kolekcja pusta.
-     *
-     * <div class="message-note">{@code null} safe method</div>
-     * @param <T>
-     * @param values kolekcja, z której obiekt jest zwracany
-     * @return
-     */
-    public static <T> T first(Collection<T> values) {
-        if(null == values) { return null; }
-        return values.isEmpty() ? null : values.iterator().next();
-    }
-
-
-    /**
-     * Jeśli obiekt jest kolekcją lub iteratorem zwraca pierwszy element.
-     * Jeśli innym obiektem, zwraca po prostu ten obiekt.
-     * Jeśli kolekcja jest pusta - zwraca null.
-     *
-     * <div class="message-note">{@code null} safe method</div>
-     * @param values kolekcja, iterator lub zwykły obiekt
-     * @return psuedo-pierwszy element
-     */
-    public static Object first(Object values) {
-        if(values instanceof Iterable) {
-            Iterator<?> iterator =((Iterable)values).iterator();
-            return iterator.hasNext() ? iterator.next() : null;
-        }
-        if(values instanceof Iterator) {
-            Iterator<?> iterator =(Iterator)values;
-            return iterator.hasNext() ? iterator.next() : null;
-        }
-        return values;
-    }
-
-
-    /**
-     * Wyszukuje i zwraca ostatni obiekt o klasie T, lub null, jeśli takiej wartości brak.
-     *
-     * <div class="message-note">{@code null} safe method</div>
-     * @param <T>
-     * @param values kolekcja, w której obiekt klasy jest szukany
-     * @param clazz szukana klasa
-     * @return
-     */
-    public static <T> T last(Collection<?> values, Class<T> clazz) {
-        if(null == values) { return null; }
-        Iterator<?> i = values.iterator();
-        T result = null;
-        while(i.hasNext()) {
-            Object value = i.next();
-            if(clazz.isInstance(value)) { result = clazz.cast(value); }
-        }
-        return result;
-    }
-
-    /**
-     * Wyszukuje i zwraca pierwszy obiekt w kolekcji, lub null, jeśli kolekcja pusta
-     *
-     * <div class="message-note">{@code null} safe method</div>
-     * @param <T>
-     * @param values kolekcja, z której obiekt jest zwracany
-     * @return
-     */
-    public static <T> T last(Collection<T> values) {
-        if(null == values) { return null; }
-        Iterator<T> i = values.iterator();
-        T result = null;
-        while(i.hasNext()) { result = i.next(); }
-        return result;
-    }
-
-    /**
-     * Jeśli obiekt jest kolekcją lub iteratorem zwraca pierwszy element.
-     * Jeśli innym obiektem, zwraca po prostu ten obiekt.
-     * Jeśli kolekcja jest pusta - zwraca null.
-     *
-     * <div class="message-note">{@code null} safe method</div>
-     * @param values kolekcja, iterator lub zwykły obiekt
-     * @return psuedo-pierwszy element
-     */
-    public static Object last(Object values) {
-        Object result = null;
-        if(values instanceof Iterable) {
-            Iterator<?> iterator = ((Iterable)values).iterator();
-            while(iterator.hasNext()) { result = iterator.next(); }
-            return result;
-        }
-        if(values instanceof Iterator) {
-            Iterator<?> iterator = (Iterator)values;
-            while(iterator.hasNext()) { result = iterator.next(); }
-            return result;
-        }
-        return null;
-    }
-
-    /**
-     * Zwraca rozmiar kolekcji.
-     * <div class="message-note">{@code null} safe method</div>
-     * @param <T>
-     * @param values
-     * @return
-     */
-    public static <T> int size(Collection<T> values) {
-        return null == values ? 0 : values.size();
-    }
-
-    /**
-     * Sprawdza czy kolekcja jest pusta.
-     * <div class="message-note">{@code null} safe method</div>
-     * @param <T>
-     * @param values
-     * @return
-     */
-    public static <T> boolean isEmpty(Collection<T> values) {
-        return 0 == size(values);
-    }
-
-    /**
-     * Sprawdza, czy kolekcje mają ten sam rozmiar.
-     * Kolekcje, które są równe {@code null} traktowane są jako puste (rozmiar 0).
-     *
-     * <div class="message-note">{@code null} safe method</div>
-     * @param <T1>
-     * @param <T2>
-     * @param a
-     * @param b
-     * @return
-     */
-    public static <T1,T2> boolean equalSize(Collection<T1> a, Collection<T2> b) {
-        return size(a) == size(b);
-    }
-
-    /**
-     * Sprawdza, czy kolekcje zawierają te same elementy ignorując kolejność.
-     * Uwaga: bardzo kosztowna metoda, ponieważ kopiuje a następnie sortuje obie
-     * kolekcje.
-     * @param <T>
-     * @param a
-     * @param b
-     * @return
-     */
-    public static <T> boolean equalContent(Collection<T> a, Collection<T> b) {
-        if( a.size() != b.size() ){
-            return false;
-        }
-        // najpierw spróbujmy tak sprawdzić - takie porównanie powinno być względnie tanie
-        // (złożoność O(N)) w porówaniu do następnej metody
-        if( a.equals(b) || b.equals(a) ) {
-            return true;
-        }
-        // dużo kopiowania, na dodatek mamy sortowanie, czyli złożoność
-        // na pamięć O(N) oraz operacje O(N*log(N)) + O(N)
-        Object[] asort = a.toArray();
-        Object[] bsort = b.toArray();
-        Arrays.sort(asort);
-        Arrays.sort(bsort);
-        return Arrays.equals(asort, bsort);
-    }
-
-    /**
-     * Przycina kolekcję jeśli kolekcja ma rozmiar większy od podanej granicy.
-     * @param <T>
-     * @param values
-     * @param size maksymalny rozmiar kolekcji
-     * @return
-     * "view" kolekcji podanej jako argument. Modyfikacja obciętej kolekcji
-     * ma wpływ na kolekcję źródłową.
-     */
-    public static <T> Collection<T> clip(Collection<T> values, int size) {
-        if(values.size() <= size) { return values; }
-        return new ClipCollection<T>(values, size);
-    }
-
-/* ************************************************************************** */
-
-    private static final class ClipIterator<T> implements Iterator<T> {
-
-        private final Iterator<T> iterator;
-        private final int size;
-        private int index;
-
-        public ClipIterator(Collection<T> values, int max) {
-            this.iterator = values.iterator();
-            this.size = max;
-            this.index = 0;
-        }
-        @Override
-        public boolean hasNext() {
-            return index < size;
-        }
-        @Override
-        public T next() {
-            index++;
-            return iterator.next();
-        }
-        @Override
-        public void remove() {
-            iterator.remove();
-        }
-
-    }
-
-    private static final class ClipCollection<T> extends AbstractCollection<T> {
-
-        private final Collection<T> values;
-        private final int size;
-
-        public ClipCollection(Collection<T> values, int size) {
-            this.values = values;
-            this.size = size;
-        }
-
-        @Override
-        public Iterator<T> iterator() {
-            return new ClipIterator<T>(values, size);
-        }
-
-        @Override
-        public int size() {
-            return size;
-        }
-
-    }
-
-    /**
-     * Konwertuje podany obiekt iterable na kolekcję o rozmiarze równym {@code size}.
-     * Ponieważ na podstawie iteratora nie można wydedukować rozmiaru zbioru,
-     * podana wartość {@code size} musi być odpowiednia, tzn nie może być
-     * większa niż ilość elementów zwróconych przez iterator.
-     *
-     * @param <T>
-     * @param size
-     * @param iterable
-     * @return
-     */
-    public static <T> Collection<T> asCollection(final int size, final Iterable<T> iterable) {
-        return new AbstractCollection<T>() {
-            @Override
-            public Iterator<T> iterator() { return iterable.iterator(); }
-            @Override
-            public int size() { return size; }
-        };
-    }
-
-    /**
-     * Zwraca iterator transformujący leniwie elementy za pomocą podanej funkcji odwzorującej.
-     * @param <T>
-     * @param <S>
-     * @param iterator
-     * @param function
-     * @return
-     */
-    public static <T,S> Iterator<T> adapt(final Iterator<S> iterator, final Function<T,S> function) {
-        return new Iterator<T>() {
-            @Override
-            public boolean hasNext() {
-                return iterator.hasNext();
-            }
-            @Override
-            public T next() {
-                return function.apply(iterator.next());
-            }
-            @Override
-            public void remove() {
-                iterator.remove();
-            }
-        };
-    }
-
-    /**
-     * Zwraca kolekcję transformującą leniwie elementy za pomocą podanej funkcji odwzorującej.
-     * @param <T>
-     * @param <S>
-     * @param collection
-     * @param function
-     * @return
-     */
-    public static <T,S> Collection<T> adapt(final Collection<S> collection, final Function<T,S> function) {
-        return new AbstractCollection<T>() {
-            @Override
-            public Iterator<T> iterator() {
-                return adapt(collection.iterator(), function);
-            }
-            @Override
-            public int size() {
-                return collection.size();
-            }
-        };
-    }
-
-}
+/*
+ *  @author Ranides Atterwim <ranides@gmail.com>
+ *  @copyright Ranides Atterwim
+ *  @license WTFPL
+ *  @url http://ranides.net/projects/assira
+ */
+
+package net.ranides.assira.collection;
+
+import java.util.AbstractCollection;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import net.ranides.assira.generic.Function;
+
+/**
+ * Generyczne operacje na kolekcjach. Jeśli to możliwe, korzystać z innych klas
+ * dedykowanych dla konkretnego typu kolekcji, ponieważ poniższe metody opierają
+ * się głównie na iteratorach i często mają bardzo kiepską złożoność rzędu O(N)
+ * @author ranides
+ */
+@SuppressWarnings({"PMD.ShortVar"})
+public final class CollectionUtils {
+
+    private CollectionUtils() { }
+
+
+    /**
+     * Pobiera index-ty element z kolekcji, lub zwraca wartość domyślną, jeśli
+     * kolekcja ma mniejszy rozmiar niż konieczne.
+     *
+     * <div class="message-note">{@code null} safe method</div>
+     * @param <T>
+     * @param values
+     * @param index
+     * @param ddefault
+     * @return
+     */
+    public static <T> T get(Collection<T> values, int index, T ddefault) {
+        if(null == values) { return ddefault; }
+        if(index >= values.size() ) { return ddefault; }
+        Iterator<T> iterator = values.iterator();
+        // pomijamy (index-1) elementów
+        for(int i=1; i<index; i++) {
+            iterator.next();
+        }
+        return iterator.next();
+    }
+
+
+    /**
+     * Pobiera index-ty element z kolekcji, lub zwraca null, jeśli
+     * kolekcja ma mniejszy rozmiar niż konieczne.
+     *
+     * <div class="message-note">{@code null} safe method</div>
+     * @param <T>
+     * @param values
+     * @param index
+     * @return
+     */
+    public static <T> T get(Collection<T> values, int index) {
+        return get(values, index, null);
+    }
+
+
+    /**
+     * Wyszukuje i zwraca pierwszy obiekt o klasie T, lub null, jeśli takiej wartości brak
+     *
+     * <div class="message-note">{@code null} safe method</div>
+     * @param <T>
+     * @param values kolekcja, w której obiekt klasy jest szukany
+     * @param clazz szukana klasa
+     * @return
+     */
+    public static <T> T first(Collection<?> values, Class<T> clazz) {
+        if(null == values) { return null; }
+        Iterator<?> i = values.iterator();
+        while(i.hasNext()) {
+            Object value = i.next();
+            if(clazz.isInstance(value)) { return clazz.cast(value); }
+        }
+        return null;
+    }
+
+
+    /**
+     * Wyszukuje i zwraca pierwszy obiekt w kolekcji, lub null, jeśli kolekcja pusta.
+     *
+     * <div class="message-note">{@code null} safe method</div>
+     * @param <T>
+     * @param values kolekcja, z której obiekt jest zwracany
+     * @return
+     */
+    public static <T> T first(Collection<T> values) {
+        if(null == values) { return null; }
+        return values.isEmpty() ? null : values.iterator().next();
+    }
+
+
+    /**
+     * Jeśli obiekt jest kolekcją lub iteratorem zwraca pierwszy element.
+     * Jeśli innym obiektem, zwraca po prostu ten obiekt.
+     * Jeśli kolekcja jest pusta - zwraca null.
+     *
+     * <div class="message-note">{@code null} safe method</div>
+     * @param values kolekcja, iterator lub zwykły obiekt
+     * @return psuedo-pierwszy element
+     */
+    public static Object first(Object values) {
+        if(values instanceof Iterable) {
+            Iterator<?> iterator =((Iterable)values).iterator();
+            return iterator.hasNext() ? iterator.next() : null;
+        }
+        if(values instanceof Iterator) {
+            Iterator<?> iterator =(Iterator)values;
+            return iterator.hasNext() ? iterator.next() : null;
+        }
+        return values;
+    }
+
+
+    /**
+     * Wyszukuje i zwraca ostatni obiekt o klasie T, lub null, jeśli takiej wartości brak.
+     *
+     * <div class="message-note">{@code null} safe method</div>
+     * @param <T>
+     * @param values kolekcja, w której obiekt klasy jest szukany
+     * @param clazz szukana klasa
+     * @return
+     */
+    public static <T> T last(Collection<?> values, Class<T> clazz) {
+        if(null == values) { return null; }
+        Iterator<?> i = values.iterator();
+        T result = null;
+        while(i.hasNext()) {
+            Object value = i.next();
+            if(clazz.isInstance(value)) { result = clazz.cast(value); }
+        }
+        return result;
+    }
+
+    /**
+     * Wyszukuje i zwraca pierwszy obiekt w kolekcji, lub null, jeśli kolekcja pusta
+     *
+     * <div class="message-note">{@code null} safe method</div>
+     * @param <T>
+     * @param values kolekcja, z której obiekt jest zwracany
+     * @return
+     */
+    public static <T> T last(Collection<T> values) {
+        if(null == values) { return null; }
+        Iterator<T> i = values.iterator();
+        T result = null;
+        while(i.hasNext()) { result = i.next(); }
+        return result;
+    }
+
+    /**
+     * Jeśli obiekt jest kolekcją lub iteratorem zwraca pierwszy element.
+     * Jeśli innym obiektem, zwraca po prostu ten obiekt.
+     * Jeśli kolekcja jest pusta - zwraca null.
+     *
+     * <div class="message-note">{@code null} safe method</div>
+     * @param values kolekcja, iterator lub zwykły obiekt
+     * @return psuedo-pierwszy element
+     */
+    public static Object last(Object values) {
+        Object result = null;
+        if(values instanceof Iterable) {
+            Iterator<?> iterator = ((Iterable)values).iterator();
+            while(iterator.hasNext()) { result = iterator.next(); }
+            return result;
+        }
+        if(values instanceof Iterator) {
+            Iterator<?> iterator = (Iterator)values;
+            while(iterator.hasNext()) { result = iterator.next(); }
+            return result;
+        }
+        return null;
+    }
+
+    /**
+     * Zwraca rozmiar kolekcji.
+     * <div class="message-note">{@code null} safe method</div>
+     * @param <T>
+     * @param values
+     * @return
+     */
+    public static <T> int size(Collection<T> values) {
+        return null == values ? 0 : values.size();
+    }
+
+    /**
+     * Sprawdza czy kolekcja jest pusta.
+     * <div class="message-note">{@code null} safe method</div>
+     * @param <T>
+     * @param values
+     * @return
+     */
+    public static <T> boolean isEmpty(Collection<T> values) {
+        return 0 == size(values);
+    }
+
+    /**
+     * Sprawdza, czy kolekcje mają ten sam rozmiar.
+     * Kolekcje, które są równe {@code null} traktowane są jako puste (rozmiar 0).
+     *
+     * <div class="message-note">{@code null} safe method</div>
+     * @param <T1>
+     * @param <T2>
+     * @param a
+     * @param b
+     * @return
+     */
+    public static <T1,T2> boolean equalSize(Collection<T1> a, Collection<T2> b) {
+        return size(a) == size(b);
+    }
+
+    /**
+     * Sprawdza, czy kolekcje zawierają te same elementy ignorując kolejność.
+     * Uwaga: bardzo kosztowna metoda, ponieważ kopiuje a następnie sortuje obie
+     * kolekcje.
+     * @param <T>
+     * @param a
+     * @param b
+     * @return
+     */
+    public static <T> boolean equalContent(Collection<T> a, Collection<T> b) {
+        if( a.size() != b.size() ){
+            return false;
+        }
+        // najpierw spróbujmy tak sprawdzić - takie porównanie powinno być względnie tanie
+        // (złożoność O(N)) w porówaniu do następnej metody
+        if( a.equals(b) || b.equals(a) ) {
+            return true;
+        }
+        // dużo kopiowania, na dodatek mamy sortowanie, czyli złożoność
+        // na pamięć O(N) oraz operacje O(N*log(N)) + O(N)
+        Object[] asort = a.toArray();
+        Object[] bsort = b.toArray();
+        Arrays.sort(asort);
+        Arrays.sort(bsort);
+        return Arrays.equals(asort, bsort);
+    }
+
+    /**
+     * Przycina kolekcję jeśli kolekcja ma rozmiar większy od podanej granicy.
+     * @param <T>
+     * @param values
+     * @param size maksymalny rozmiar kolekcji
+     * @return
+     * "view" kolekcji podanej jako argument. Modyfikacja obciętej kolekcji
+     * ma wpływ na kolekcję źródłową.
+     */
+    public static <T> Collection<T> clip(Collection<T> values, int size) {
+        if(values.size() <= size) { return values; }
+        return new ClipCollection<T>(values, size);
+    }
+
+/* ************************************************************************** */
+
+    private static final class ClipIterator<T> implements Iterator<T> {
+
+        private final Iterator<T> iterator;
+        private final int size;
+        private int index;
+
+        public ClipIterator(Collection<T> values, int max) {
+            this.iterator = values.iterator();
+            this.size = max;
+            this.index = 0;
+        }
+        @Override
+        public boolean hasNext() {
+            return index < size;
+        }
+        @Override
+        public T next() {
+            index++;
+            return iterator.next();
+        }
+        @Override
+        public void remove() {
+            iterator.remove();
+        }
+
+    }
+
+    private static final class ClipCollection<T> extends AbstractCollection<T> {
+
+        private final Collection<T> values;
+        private final int size;
+
+        public ClipCollection(Collection<T> values, int size) {
+            this.values = values;
+            this.size = size;
+        }
+
+        @Override
+        public Iterator<T> iterator() {
+            return new ClipIterator<T>(values, size);
+        }
+
+        @Override
+        public int size() {
+            return size;
+        }
+
+    }
+
+    /**
+     * Konwertuje podany obiekt iterable na kolekcję o rozmiarze równym {@code size}.
+     * Ponieważ na podstawie iteratora nie można wydedukować rozmiaru zbioru,
+     * podana wartość {@code size} musi być odpowiednia, tzn nie może być
+     * większa niż ilość elementów zwróconych przez iterator.
+     *
+     * @param <T>
+     * @param size
+     * @param iterable 
+     * @return
+     */
+    public static <T> Collection<T> asCollection(final int size, final Iterable<T> iterable) {
+        return new AbstractCollection<T>() {
+            @Override
+            public Iterator<T> iterator() { return iterable.iterator(); }
+            @Override
+            public int size() { return size; }
+        };
+    }
+
+    /**
+     * Zwraca iterator transformujący leniwie elementy za pomocą podanej funkcji odwzorującej.
+     * @param <T>
+     * @param <S>
+     * @param iterator
+     * @param function
+     * @return
+     */
+    public static <T,S> Iterator<T> adapt(final Iterator<S> iterator, final Function<T,S> function) {
+        return new Iterator<T>() {
+            @Override
+            public boolean hasNext() {
+                return iterator.hasNext();
+            }
+            @Override
+            public T next() {
+                return function.apply(iterator.next());
+            }
+            @Override
+            public void remove() {
+                iterator.remove();
+            }
+        };
+    }
+
+    /**
+     * Zwraca kolekcję transformującą leniwie elementy za pomocą podanej funkcji odwzorującej.
+     * @param <T>
+     * @param <S>
+     * @param collection
+     * @param function
+     * @return
+     */
+    public static <T,S> Collection<T> adapt(final Collection<S> collection, final Function<T,S> function) {
+        return new AbstractCollection<T>() {
+            @Override
+            public Iterator<T> iterator() {
+                return adapt(collection.iterator(), function);
+            }
+            @Override
+            public int size() {
+                return collection.size();
+            }
+        };
+    }
+
+}

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

@@ -10,18 +10,18 @@ package net.ranides.assira.collection;
 import java.util.*;
 
 /**
- * Operacje na listach. 
+ * Operacje na listach.
  * @author ranides
  */
 @SuppressWarnings({"PMD.ShortVar"})
 public final class ListUtils {
-    
+
     private ListUtils() { }
-    
+
     /**
      * Pobiera index-ty element z listy, lub zwraca wartość domyślną, jeśli
      * lista ma mniejszy rozmiar niż konieczne
-     * 
+     *
      * <div class="message-note">{@code null} safe method</div>
      * @param <T>
      * @param values
@@ -38,7 +38,7 @@ public final class ListUtils {
     /**
      * Pobiera index-ty element z listy, lub zwraca null, jeśli
      * lista ma mniejszy rozmiar niż konieczne
-     * 
+     *
      * <div class="message-note">{@code null} safe method</div>
      * @param <T>
      * @param values
@@ -52,9 +52,9 @@ public final class ListUtils {
 
     /**
      * Wyszukuje i zwraca pierwszy obiekt o klasie T, lub null, jeśli takiej wartości brak
-     * 
+     *
      * <div class="message-note">{@code null} safe method</div>
-     * @param <T> 
+     * @param <T>
      * @param values lista, w której obiekt klasy jest szukany
      * @param clazz szukana klasa
      * @return
@@ -70,9 +70,9 @@ public final class ListUtils {
 
     /**
      * Wyszukuje i zwraca pierwszy obiekt z listy, lub null, jeśli lista pusta.
-     * 
+     *
      * <div class="message-note">{@code null} safe method</div>
-     * @param <T> 
+     * @param <T>
      * @param values
      * @return
      */
@@ -80,10 +80,10 @@ public final class ListUtils {
         if(null == values) { return null; }
         return values.isEmpty() ? null : values.get(0);
     }
-    
+
     /**
      * Wyszukuje i zwraca pierwszy obiekt z listy, lub null, jeśli obiekt nie jest listą.
-     * 
+     *
      * <div class="message-note">{@code null} safe method</div>
      * @param values
      * @return
@@ -100,9 +100,9 @@ public final class ListUtils {
 
     /**
      * Wyszukuje i zwraca ostatni obiekt o klasie T, lub null, jeśli takiej wartości brak.
-     * 
+     *
      * <div class="message-note">{@code null} safe method</div>
-     * @param <T> 
+     * @param <T>
      * @param values lista, w której obiekt klasy jest szukany
      * @param clazz szukana klasa
      * @return
@@ -117,9 +117,9 @@ public final class ListUtils {
 
     /**
      * Zwraca ostatni obiekt z listy, lub null, jeśli lista jest pusta.
-     * 
+     *
      * <div class="message-note">{@code null} safe method</div>
-     * @param <T> 
+     * @param <T>
      * @param values lista, w której obiekt klasy jest szukany
      * @return
      */
@@ -127,10 +127,10 @@ public final class ListUtils {
         if(null == values || values.isEmpty()) { return null; }
         return values.get(values.size()-1);
     }
-    
+
     /**
      * Zwraca ostatni obiekt z listy, lub null, jeśli obiekt nie jest listą.
-     * 
+     *
      * <div class="message-note">{@code null} safe method</div>
      * @param values
      * @return
@@ -143,8 +143,8 @@ public final class ListUtils {
             return null;
         }
     }
-    
-        
+
+
 
     /**
      * Zwraca rozmiar listy.
@@ -156,7 +156,7 @@ public final class ListUtils {
     public static <T> int size(List<T> values) {
         return null == values ? 0 : values.size();
     }
-    
+
     /**
      * Sprawdza czy lista jest pusta.
      * <div class="message-note">{@code null} safe method</div>
@@ -189,7 +189,7 @@ public final class ListUtils {
     /**
      * Sprawdza, czy listy mają ten sam rozmiar.
      * Listy, które są równe {@code null} traktowane są jako puste (rozmiar 0).
-     * 
+     *
      * <div class="message-note">{@code null} safe method</div>
      * @param <T1>
      * @param <T2>
@@ -220,8 +220,8 @@ public final class ListUtils {
      * Konwertuje listę elementów na tablicę o podanym typie, dokonując po drodze
      * odpowiednich rzutowań i unbox'owań, jeśli są konieczne. Zwrócona tablica jest
      * niezależna od listy.
-     * @param <T> 
-     * @param arrayClazz
+     * @param <Auto>
+     * @param type
      * @param source
      * @return
      * @throws ClassCastException jeśli konwersja elementów jest niemożliwa do przeprowadzenia
@@ -229,7 +229,7 @@ public final class ListUtils {
     public static <Auto> Auto toArray(Class<?> type, List<?> source) {
         return (Auto)ArrayUtils.toArray(type, source);
     }
-    
+
     /**
      * Usuwa pierwszy element z listy i przesuwa wszystkie pozostałe wartości w lewo.
      * Uwaga! Ostatni element po operacji {@code shift} jest zduplikowany!
@@ -254,7 +254,7 @@ public final class ListUtils {
             return result;
         }
     }
-    
+
     /**
      * Usuwa ostatni element z tablicy i przesuwa wszystkie pozostałe wartości w prawo.
      * Uwaga! Pierwszy element po operacji {@code unshift} jest zduplikowany!
@@ -279,7 +279,7 @@ public final class ListUtils {
             return result;
         }
     }
-    
+
     /**
      * Tworzy nową listę i kopiuje do niej elementy znajdujące się w obiekcie {@code iterable}
      * @param <T>
@@ -289,7 +289,7 @@ public final class ListUtils {
     public static <T> List<T> asList(Iterable<T> iterable) {
         return asList(iterable.iterator());
     }
-    
+
     /**
      * Tworzy nową listę i kopiuje do niej elementy zwrócone przez podany iterator.
      * @param <T>
@@ -301,19 +301,31 @@ public final class ListUtils {
         while(iterator.hasNext()) { list.add(iterator.next()); }
         return list;
     }
-    
+
+    /**
+     * Tworzy listę, której nie można modyfikować, kopiując do niej podane wartości.
+     * @param <T>
+     * @param values
+     * @return
+     */
     public static <T> List<T> constCopy(T[] values) {
         return constCopy(Arrays.asList(values));
     }
-    
+
+    /**
+     * Tworzy listę, której nie można modyfikować, kopiując do niej podane wartości.
+     * @param <T>
+     * @param values
+     * @return
+     */
     public static <T> List<T> constCopy(Collection<? extends T> values) {
         return new ConstList<T>(values);
     }
-    
-    public static class ConstList<T> extends AbstractList<T> {
+
+    private static class ConstList<T> extends AbstractList<T> {
 
         private final Object[] array;
-        
+
         public ConstList(T[] values) {
             this.array = ArrayUtils.arraycopy(values);
         }
@@ -334,7 +346,7 @@ public final class ListUtils {
         }
 
     }
-            
-            
+
+
 
 }

+ 89 - 12
src/main/java/net/ranides/assira/collection/MapUtils.java

@@ -22,11 +22,21 @@ import net.ranides.assira.text.Strings;
  * @author ranides
  */
 public final class MapUtils {
-    
+
+    /**
+     * Pusta mapa, której nie można modyfikować.
+     */
     public static final MultiMap EMPTY_MULTIMAP = new EmptyMultiMap();
 
     private MapUtils() { }
-    
+
+    /**
+     * Zwraca pustę mapę, której nie można modyfikować. Metoda nie tworzy nowego
+     * obiektu lecz reuse only one internal immutable object.
+     * @param <K>
+     * @param <V>
+     * @return
+     */
     @SuppressWarnings("unchecked")
     public static <K, V> MultiMap<K, V> emptyMap() {
         return EMPTY_MULTIMAP;
@@ -81,23 +91,90 @@ public final class MapUtils {
         }
         return target;
     }
-    
+
+    /**
+     * Tworzy adapter, który podaną funkcję prezentuje jako interfejs {@code Map}.
+     * Uwaga: zwrócona mapa nie obsługuje większości metod:
+     * <ul>
+     *   <li>nie można modyfikować</li>
+     *   <li>nie można iterować po wartościach</li>
+     *   <li>nie można sprawdzić rozmiaru</li>
+     *   <li>można sprawdzić istnienie klucza</li>
+     *   <li>można odczytać wartość przypisaną do klucza</li>
+     * </ul>
+     *
+     * @param <K>
+     * @param <V>
+     * @param generator
+     * @return
+     */
     public static <K,V> Map<K,V> produce(Function<V,K> generator) {
         return new VirtualMap.Apply<K,V>(generator);
     }
-    
+
+    /**
+     * Tworzy adapter, który podaną funkcję prezentuje jako interfejs {@code Map}.
+     * Mapa obsługuje większość metod, jednak nie można modyfikować wartości
+     * przypisanych do kluczy ani dodawać nowych. Jeśli przekazany argument
+     * {@code domain} pozwala na usuwanie elementów, to można z mapy przypisania usuwać.
+     *
+     * @param <K>
+     * @param <V>
+     * @param domain
+     * @param generator
+     * @return
+     */
     public static <K,V> Map<K,V> produce(Set<K> domain, BiFunction<V,K> generator) {
         return new VirtualMap.DomApply<K, V>(domain, generator);
     }
-    
+
+    /**
+     * Spłaszcza mapę, która zawiera wartości oraz zagnieżdżone mapy w ten sposób,
+     * że w wyniku znajdują się wartości wyciągnięte z wszystkich zagnieżdżonych map,
+     * ale nie same mapy. Klucz dla każdej wartości jest generowany tak, że klucze
+     * z kolejno zagnieżdżonych map są konwertowane na {@code String} a następnie
+     * sklejane razem. Na przykład:
+     * <pre>
+     * {@code source.get(17).get("a").get(".value") ==> target.get("17a.value") }
+     * </pre>
+     * @param source
+     * @return
+     */
     public static Map<String,Object> flat(Map<?,?> source) {
         return flat(new HashMap<String, Object>(), source, "");
     }
-    
+
+    /**
+     * Spłaszcza mapę, która zawiera wartości oraz zagnieżdżone mapy w ten sposób,
+     * że w wyniku znajdują się wartości wyciągnięte z wszystkich zagnieżdżonych map,
+     * ale nie same mapy. Klucz dla każdej wartości jest generowany tak, że klucze
+     * z zagnieżdżonych map są sklejane przez funkcję {@code joiner}.
+     *
+     * <p>
+     * Funkcja sklejająca jest wywoływana z dwoma argumentami: kluczem-prefixem [P]
+     * oraz kluczem najbardziej zagnieżdżonym [K]. Tak obliczony klucz [KP] może
+     * zostać w późniejszym czasie przekazany jako prefix [P] do funkcji sklejającej,
+     * jeśli pod kluczem (K) znajdowała się kolejna zagnieżdżona mapa z wartościami.
+     * </p><p>
+     * Klucze najwyższego poziomu są generowane jako sklejenie {@code seed}'u
+     * oraz klucza, któremu przypisano wartość.
+     * </p><p>
+     * Uwaga: funkcja może założyć, że jako pierwszy argument zawsze dostaje wartość
+     * typu K, ale drugi argument może być całkowicie dowolny - zależy to tylko
+     * od zawartości map. Funkcja powinna być przygotowana na konwersje {@code Object -> K}
+     * lub bardziej szczegółowe, jeśli istnieją jakieś gwarancje co do kluczy w
+     * mapie źródłowej.
+     * </p>
+     * @param <K>
+     * @param source
+     * @param seed
+     * @param joiner
+     * @return
+     */
     public static <K> Map<K,Object> flat(Map<?,?> source, K seed, AnyFunction<K> joiner) {
         return flat(new HashMap<K, Object>(), source, seed, joiner);
     }
-    
+
     private static Map<String,Object> flat(Map<String,Object> target, Map<?,?> source, String prefix) {
         return flat(target, source, prefix, new AnyFunction<String>() {
             @Override
@@ -106,7 +183,7 @@ public final class MapUtils {
             }
         });
     }
-    
+
     @SuppressWarnings("unchecked")
     private static <K> Map<K,Object> flat(Map<K,Object> target, Map<?,?> source, K seed, AnyFunction<K> joiner) {
         for(Map.Entry<?,?> entry : source.entrySet()) {
@@ -146,7 +223,7 @@ public final class MapUtils {
         private Object readResolve() {
             return EMPTY_MULTIMAP;
         }
-        
+
         @Override public boolean containsItem(V value) { return false; }
 
         @Override public V getFirst(K key) { return null; }
@@ -156,7 +233,7 @@ public final class MapUtils {
         @Override public void putItem(K key, V value) { throw new UnsupportedOperationException("Immutable map"); }
 
         @Override public V removeFirst(K key) { return null; }
-        
+
         @Override public V removeLast(K key) { return null; }
 
         @Override public boolean removeItem(K key, V value) { return false; }
@@ -168,7 +245,7 @@ public final class MapUtils {
         @Override public List<V> items() { return Collections.emptyList(); }
 
         @Override public int multiSize() { return 0; }
-        
+
     }
-    
+
 }

+ 6 - 0
src/main/java/net/ranides/assira/collection/map/SwitchMap.java

@@ -48,6 +48,12 @@ public final class SwitchMap<K extends Comparable<K>,V> implements Map<K,V> {
         ArrayUtils.sort(this.values, this.keys);
     }
 
+    /**
+     * Tworzy nową mapę wypełniając wskazanymi kluczami i przyporządkowując im
+     * wskazane wartości.
+     * @param keys
+     * @param values
+     */
     public SwitchMap(Entry<K,V>[] entries) {
         this(entries.length, entries.length);
         int index = 0;

+ 7 - 7
src/main/java/net/ranides/assira/reflection/MethodPointer.java

@@ -14,7 +14,7 @@ import net.ranides.assira.generic.AnyFunction;
  * @author ranides
  */
 public final class MethodPointer<T> extends AnyFunction<T> {
-    
+
     private final Object object;
     private final Method pointer;
 
@@ -28,7 +28,7 @@ public final class MethodPointer<T> extends AnyFunction<T> {
     public T apply(Object[] arguments) {
         return (T)Invoker.invoke(object, pointer, arguments);
     }
-    
+
     @Override
     public MethodPointer<T> bind(Object that) {
         return new MethodPointer<T>(object, pointer);
@@ -38,7 +38,7 @@ public final class MethodPointer<T> extends AnyFunction<T> {
     public boolean unbinded() {
         return false;
     }
-    
+
     @Override
     public boolean matches(Object... arguments) {
         return ClassInspector.isInstance(pointer.getParameterTypes(), arguments);
@@ -48,11 +48,11 @@ public final class MethodPointer<T> extends AnyFunction<T> {
     public String toString() {
         return "MethodPointer: " + ReflectUtils.sig(pointer) + " @ " + object;
     }
-    
+
     public static MethodPointer<Object> make(Object object, String method) {
         return make(object, ObjectInspector.getMethod(object, method));
     }
-    
+
     public static <T> MethodPointer<T> make(Object object, Class<T> rtype, String method) {
         return make(object, rtype, ObjectInspector.getMethod(object, method));
     }
@@ -60,12 +60,12 @@ public final class MethodPointer<T> extends AnyFunction<T> {
     public static MethodPointer<Object> make(Object object, Method method) {
         return new MethodPointer<Object>(object, method);
     }
-    
+
     public static <T> MethodPointer<T> make(Object object, Class<T> rtype, Method method) {
         if( !rtype.isAssignableFrom(method.getReturnType()) ) {
             throw new InspectException("Method returns type: "+method.getReturnType()+" instead of "+rtype);
         }
         return new MethodPointer<T>(object, method);
     }
-    
+
 }