Ranides Atterwim 10 лет назад
Родитель
Сommit
68d0ae01f1

+ 180 - 181
assira1/src/main/java/net/ranides/assira/annotations/ProcessorDef.java

@@ -1,181 +1,180 @@
-/*
- * @author Ranides Atterwim <ranides@gmail.com>
- * @copyright Ranides Atterwim
- * @license WTFPL
- * @url http://ranides.net/projects/assira
- */
-package net.ranides.assira.annotations;
-
-import java.io.BufferedReader;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
-import java.util.Collection;
-import java.util.Map;
-import java.util.Set;
-import javax.annotation.processing.AbstractProcessor;
-import javax.annotation.processing.Filer;
-import javax.annotation.processing.RoundEnvironment;
-import javax.annotation.processing.SupportedAnnotationTypes;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.MirroredTypeException;
-import javax.lang.model.type.TypeKind;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.Elements;
-import javax.tools.Diagnostic.Kind;
-import javax.tools.FileObject;
-import javax.tools.StandardLocation;
-import net.ranides.assira.collection.map.MultiHashMap;
-import net.ranides.assira.collection.map.MultiMap;
-import net.ranides.assira.io.FileHelper;
-
-/**
- * 
- * Based on "org.kohsuke.metainf-services"
- * http://weblogs.java.net/blog/kohsuke/archive/2009/03/my_project_of_t.html 
- * (C) by Kohsuke Kawaguchi(kk@kohsuke.org) under MIT license
- * 
- * @author ranides
- * @todo (migration) assira
- */
-@SupportedAnnotationTypes(
-    "net.ranides.assira.annotations.Meta.JavaService"
-)
-public class ProcessorDef extends AbstractProcessor {
-    
-    @Override
-    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
-        if ( !roundEnv.processingOver() ) {
-            writeMeta( readMeta( getServices(roundEnv) ) );
-        }
-        return false;
-    }
-    
-    private MultiMap<String, String> getServices(RoundEnvironment roundEnv) {
-        
-        MultiMap<String, String> services = new MultiHashMap<>();
-        Elements elements = processingEnv.getElementUtils();
-        
-        // discover services from the current compilation sources
-        for (Element element : roundEnv.getElementsAnnotatedWith(Meta.JavaService.class)) {
-            Meta.JavaService info = element.getAnnotation(Meta.JavaService.class);
-            if(info==null) {
-                continue; // shouldn't happen - ignore
-            }
-            if ( !element.getKind().isClass() && !element.getKind().isInterface()) {
-                continue; // shouldn't happen - ignore
-            }
-            
-            TypeElement type = (TypeElement)element;
-            TypeElement contract = getContract(type, info);
-            if(contract==null) {
-                continue; // error should have already been reported
-            } 
-
-            String cname = elements.getBinaryName(contract).toString();
-            String tname = elements.getBinaryName(type).toString();
-            services.putItem(cname, tname);
-        }
-        return services;
-    }
-
-    private MultiMap<String, String> readMeta(MultiMap<String, String> services) {
-        Filer filer = processingEnv.getFiler();
-        
-        for (Map.Entry<String, Collection<String>> entry : services.entrySet()) {
-            BufferedReader reader = null;
-            try {
-                String contract = entry.getKey();
-                Collection<String> target = entry.getValue();
-                
-                FileObject file = filer.getResource(StandardLocation.CLASS_OUTPUT, "", "META-INF/services/" +contract);
-                reader = new BufferedReader(new InputStreamReader(file.openInputStream(), "UTF-8"));
-                String line;
-                while( null!=(line=reader.readLine()) ) { target.add(line); }
-                
-            } catch (FileNotFoundException _e) { // NOPMD
-                // ignore, if doesn't exist
-            } catch (IOException cause) {
-                processingEnv.getMessager().printMessage(Kind.ERROR,"Failed to load existing service definition files: "+cause);
-            } finally {
-                FileHelper.close(reader);
-            }
-        }
-        return services;
-    }
-
-    private MultiMap<String, String> writeMeta(MultiMap<String, String> services) {
-        Filer filer = processingEnv.getFiler();
-
-        for (Map.Entry<String,Collection<String>> entry : services.entrySet()) {
-            try {
-                String contract = entry.getKey();
-                Collection<String> classes = entry.getValue();
-                processingEnv.getMessager().printMessage(Kind.NOTE,"Writing META-INF/services/"+contract);
-                
-                FileObject file = filer.createResource(StandardLocation.CLASS_OUTPUT, "", "META-INF/services/" +contract);
-                PrintWriter writer = new PrintWriter(new OutputStreamWriter(file.openOutputStream(), "UTF-8"));
-                for (String value : classes) {
-                    writer.println(value);
-                }
-                writer.close();
-            } catch (IOException x) {
-                processingEnv.getMessager().printMessage(Kind.ERROR,"Failed to write service definition files: "+x);
-            }
-        }
-        return services;
-    }
-    
-    private TypeElement getContract(TypeElement type, Meta.JavaService annotation) {
-        TypeMirror mirror = getExportValue(annotation);
-        if (mirror.getKind()== TypeKind.VOID) {
-            // contract inferred from the signature
-            boolean hasBaseClass = type.getSuperclass().getKind()!=TypeKind.NONE && !isObject(type.getSuperclass());
-            boolean hasInterfaces = !type.getInterfaces().isEmpty();
-            if(hasBaseClass^hasInterfaces) {
-                if(hasBaseClass) {
-                    return (TypeElement)((DeclaredType)type.getSuperclass()).asElement();
-                } else {
-                    return (TypeElement)((DeclaredType)type.getInterfaces().get(0)).asElement();
-                }
-            }
-
-            error(type, "Contract type was not specified, but it couldn't be inferred.");
-            return null;
-        }
-
-        if (mirror instanceof DeclaredType) {
-            return (TypeElement)((DeclaredType)mirror).asElement();
-        } else {
-            error(type, "Invalid type specified as the contract");
-            return null;
-        }
-    }
-    
-    private TypeMirror getExportValue(Meta.JavaService annotation) {
-        try {
-            annotation.value();
-            throw new AssertionError();
-        } catch (MirroredTypeException exception) {
-            return exception.getTypeMirror();
-        }
-    }
-
-    private boolean isObject(TypeMirror mirror) {
-        if (mirror instanceof DeclaredType) {
-            DeclaredType dtype = (DeclaredType)mirror;
-            TypeElement element = (TypeElement)dtype.asElement();
-            return element.getQualifiedName().toString().equals("java.lang.Object");
-        }
-        return false;
-    }
-
-    private void error(Element source, String msg) {
-        processingEnv.getMessager().printMessage(Kind.ERROR,msg,source);
-    }
-    
-}
+/*
+ * @author Ranides Atterwim <ranides@gmail.com>
+ * @copyright Ranides Atterwim
+ * @license WTFPL
+ * @url http://ranides.net/projects/assira
+ */
+package net.ranides.assira.annotations;
+
+import java.io.BufferedReader;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.Filer;
+import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedAnnotationTypes;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.MirroredTypeException;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.Elements;
+import javax.tools.Diagnostic.Kind;
+import javax.tools.FileObject;
+import javax.tools.StandardLocation;
+import net.ranides.assira.collection.map.MultiHashMap;
+import net.ranides.assira.collection.map.MultiMap;
+import net.ranides.assira.io.FileHelper;
+
+/**
+ * 
+ * Based on "org.kohsuke.metainf-services"
+ * http://weblogs.java.net/blog/kohsuke/archive/2009/03/my_project_of_t.html 
+ * (C) by Kohsuke Kawaguchi(kk@kohsuke.org) under MIT license
+ * 
+ * @author ranides
+ */
+@SupportedAnnotationTypes(
+    "net.ranides.assira.annotations.Meta.JavaService"
+)
+public class ProcessorDef extends AbstractProcessor {
+    
+    @Override
+    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+        if ( !roundEnv.processingOver() ) {
+            writeMeta( readMeta( getServices(roundEnv) ) );
+        }
+        return false;
+    }
+    
+    private MultiMap<String, String> getServices(RoundEnvironment roundEnv) {
+        
+        MultiMap<String, String> services = new MultiHashMap<>();
+        Elements elements = processingEnv.getElementUtils();
+        
+        // discover services from the current compilation sources
+        for (Element element : roundEnv.getElementsAnnotatedWith(Meta.JavaService.class)) {
+            Meta.JavaService info = element.getAnnotation(Meta.JavaService.class);
+            if(info==null) {
+                continue; // shouldn't happen - ignore
+            }
+            if ( !element.getKind().isClass() && !element.getKind().isInterface()) {
+                continue; // shouldn't happen - ignore
+            }
+            
+            TypeElement type = (TypeElement)element;
+            TypeElement contract = getContract(type, info);
+            if(contract==null) {
+                continue; // error should have already been reported
+            } 
+
+            String cname = elements.getBinaryName(contract).toString();
+            String tname = elements.getBinaryName(type).toString();
+            services.putItem(cname, tname);
+        }
+        return services;
+    }
+
+    private MultiMap<String, String> readMeta(MultiMap<String, String> services) {
+        Filer filer = processingEnv.getFiler();
+        
+        for (Map.Entry<String, Collection<String>> entry : services.entrySet()) {
+            BufferedReader reader = null;
+            try {
+                String contract = entry.getKey();
+                Collection<String> target = entry.getValue();
+                
+                FileObject file = filer.getResource(StandardLocation.CLASS_OUTPUT, "", "META-INF/services/" +contract);
+                reader = new BufferedReader(new InputStreamReader(file.openInputStream(), "UTF-8"));
+                String line;
+                while( null!=(line=reader.readLine()) ) { target.add(line); }
+                
+            } catch (FileNotFoundException _e) { // NOPMD
+                // ignore, if doesn't exist
+            } catch (IOException cause) {
+                processingEnv.getMessager().printMessage(Kind.ERROR,"Failed to load existing service definition files: "+cause);
+            } finally {
+                FileHelper.close(reader);
+            }
+        }
+        return services;
+    }
+
+    private MultiMap<String, String> writeMeta(MultiMap<String, String> services) {
+        Filer filer = processingEnv.getFiler();
+
+        for (Map.Entry<String,Collection<String>> entry : services.entrySet()) {
+            try {
+                String contract = entry.getKey();
+                Collection<String> classes = entry.getValue();
+                processingEnv.getMessager().printMessage(Kind.NOTE,"Writing META-INF/services/"+contract);
+                
+                FileObject file = filer.createResource(StandardLocation.CLASS_OUTPUT, "", "META-INF/services/" +contract);
+                PrintWriter writer = new PrintWriter(new OutputStreamWriter(file.openOutputStream(), "UTF-8"));
+                for (String value : classes) {
+                    writer.println(value);
+                }
+                writer.close();
+            } catch (IOException x) {
+                processingEnv.getMessager().printMessage(Kind.ERROR,"Failed to write service definition files: "+x);
+            }
+        }
+        return services;
+    }
+    
+    private TypeElement getContract(TypeElement type, Meta.JavaService annotation) {
+        TypeMirror mirror = getExportValue(annotation);
+        if (mirror.getKind()== TypeKind.VOID) {
+            // contract inferred from the signature
+            boolean hasBaseClass = type.getSuperclass().getKind()!=TypeKind.NONE && !isObject(type.getSuperclass());
+            boolean hasInterfaces = !type.getInterfaces().isEmpty();
+            if(hasBaseClass^hasInterfaces) {
+                if(hasBaseClass) {
+                    return (TypeElement)((DeclaredType)type.getSuperclass()).asElement();
+                } else {
+                    return (TypeElement)((DeclaredType)type.getInterfaces().get(0)).asElement();
+                }
+            }
+
+            error(type, "Contract type was not specified, but it couldn't be inferred.");
+            return null;
+        }
+
+        if (mirror instanceof DeclaredType) {
+            return (TypeElement)((DeclaredType)mirror).asElement();
+        } else {
+            error(type, "Invalid type specified as the contract");
+            return null;
+        }
+    }
+    
+    private TypeMirror getExportValue(Meta.JavaService annotation) {
+        try {
+            annotation.value();
+            throw new AssertionError();
+        } catch (MirroredTypeException exception) {
+            return exception.getTypeMirror();
+        }
+    }
+
+    private boolean isObject(TypeMirror mirror) {
+        if (mirror instanceof DeclaredType) {
+            DeclaredType dtype = (DeclaredType)mirror;
+            TypeElement element = (TypeElement)dtype.asElement();
+            return element.getQualifiedName().toString().equals("java.lang.Object");
+        }
+        return false;
+    }
+
+    private void error(Element source, String msg) {
+        processingEnv.getMessager().printMessage(Kind.ERROR,msg,source);
+    }
+    
+}

+ 372 - 373
assira1/src/main/java/net/ranides/assira/collection/CollectionUtils.java

@@ -1,373 +1,372 @@
-/*
- *  @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.AbstractSet;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Iterator;
-import net.ranides.assira.generic.Function;
-import net.ranides.assira.generic.Predicate;
-
-/**
- * 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
- * @todo (migration) assira
- */
-@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<0 || index >= values.size() ) { return ddefault; }
-        Iterator<T> iterator = values.iterator();
-        // pomijamy (index-1) elementów
-        for(int i=0; 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(Iterable<?> 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(Iterable<T> values) {
-        return null == values ? null : first(values.iterator());
-    }
-
-
-    /**
-     * 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 <T> T first(Iterator<T> iterator) {
-        return iterator.hasNext() ? iterator.next() : null;
-    }
-
-
-    /**
-     * 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(Iterable<?> 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(Iterable<T> values) {
-        return null == values ? null : last(values.iterator());
-    }
-
-    /**
-     * 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 <T> T last(Iterator<T> iterator) {
-        T result = null;
-        while(iterator.hasNext()) { result = iterator.next(); }
-        return result;
-    }
-
-    /**
-     * 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 a
-     * @param b
-     * @return
-     */
-    public static boolean equalContent(Collection<?> a, Collection<?> 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<>(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 AbstractSet<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<>(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 AbstractSet<T>() {
-            @Override
-            public Iterator<T> iterator() {
-                return adapt(collection.iterator(), function);
-            }
-            @Override
-            public int size() {
-                return collection.size();
-            }
-        };
-    }
-    
-    public static <T> Iterator<T> filter(final Iterator<T> iterator, final Predicate<T> predicate) {
-        return new FilterIterator<T>(iterator) {
-            @Override
-            public boolean accept(T source) {
-                return predicate.apply(source);
-            }
-        };
-    }
-    
-    public static <T> Iterable<T> filter(final Iterable<T> iterable, final Predicate<T> predicate) {
-        return new Iterable<T>() {
-            @Override
-            public Iterator<T> iterator() {
-                return filter(iterable.iterator(), predicate);
-            }
-        };
-    }
-
-}
+/*
+ *  @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.AbstractSet;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import net.ranides.assira.generic.Function;
+import net.ranides.assira.generic.Predicate;
+
+/**
+ * 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<0 || index >= values.size() ) { return ddefault; }
+        Iterator<T> iterator = values.iterator();
+        // pomijamy (index-1) elementów
+        for(int i=0; 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(Iterable<?> 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(Iterable<T> values) {
+        return null == values ? null : first(values.iterator());
+    }
+
+
+    /**
+     * 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 <T> T first(Iterator<T> iterator) {
+        return iterator.hasNext() ? iterator.next() : null;
+    }
+
+
+    /**
+     * 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(Iterable<?> 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(Iterable<T> values) {
+        return null == values ? null : last(values.iterator());
+    }
+
+    /**
+     * 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 <T> T last(Iterator<T> iterator) {
+        T result = null;
+        while(iterator.hasNext()) { result = iterator.next(); }
+        return result;
+    }
+
+    /**
+     * 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 a
+     * @param b
+     * @return
+     */
+    public static boolean equalContent(Collection<?> a, Collection<?> 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<>(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 AbstractSet<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<>(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 AbstractSet<T>() {
+            @Override
+            public Iterator<T> iterator() {
+                return adapt(collection.iterator(), function);
+            }
+            @Override
+            public int size() {
+                return collection.size();
+            }
+        };
+    }
+    
+    public static <T> Iterator<T> filter(final Iterator<T> iterator, final Predicate<T> predicate) {
+        return new FilterIterator<T>(iterator) {
+            @Override
+            public boolean accept(T source) {
+                return predicate.apply(source);
+            }
+        };
+    }
+    
+    public static <T> Iterable<T> filter(final Iterable<T> iterable, final Predicate<T> predicate) {
+        return new Iterable<T>() {
+            @Override
+            public Iterator<T> iterator() {
+                return filter(iterable.iterator(), predicate);
+            }
+        };
+    }
+
+}