Ranides Atterwim 4 سال پیش
والد
کامیت
a9f11960ab

+ 12 - 1
assira.core/src/main/java/net/ranides/assira/collection/maps/BlockMap.java

@@ -9,11 +9,22 @@ package net.ranides.assira.collection.maps;
 import java.util.Map;
 
 /**
+ * This interface represents map, which internally stores data inside continuous block of memory.
+ * It allows you to examine size of consumed memory and try to compact collection.
  *
  * @author Ranides Atterwim {@literal <ranides@gmail.com>}
  */
 public interface BlockMap<K,V> extends Map<K,V> {
-    
+
+    /**
+     * Maximum number of entries which can be stored inside map before reallocation.
+     * Please note, that it is not strictly the same value as maximum number of mappings.
+     *
+     * Different maps have different strategies for memory allocation,
+     * for example hash maps use load factor to allocate internal array of bucket efficiently.
+     *
+     * @return int
+     */
     int capacity();
 
     /**

+ 242 - 2
assira.core/src/main/java/net/ranides/assira/functional/Branch.java

@@ -21,8 +21,7 @@ import java.util.function.Supplier;
  * Class contains functional implementation of switch-case construct.
  * Useful if we want to switch over values, which are not constant at compile-time.
  *
- * @author mwx1031516 [mariusz.czarnowski@huawei.com]
- * @since 2021-03-14
+ * @author Ranides Atterwim {@literal <ranides@gmail.com>}
  */
 @UtilityClass
 public class Branch {
@@ -45,63 +44,304 @@ public class Branch {
         .withParam(new TypeToken<Optional<?>>() {})
         .function();
 
+    /**
+     * Creates new selector, which will return first non-null value.
+     *
+     * @return new selector
+     */
     public static BranchSelector<Object> first() {
         return new BranchChain<>(true, null);
     }
 
+    /**
+     * Creates new selector, which stores specified input value, and examines all conditions declared
+     * in chain, to return result matching specified input.
+     *
+     * Effectively, it is more flexible, functional version of switch-case construct.
+     *
+     * @param value input
+     * @return new selector
+     */
     public static BranchSelector<Object> first(Object value) {
         return new BranchChain<>(true, value);
     }
 
+    /**
+     * Creates new selector, which will return first non-null value.
+     * It checks if there is exactly one unambiguous non-null result
+     *
+     * @return new selector
+     */
     public static BranchSelector<Object> single() {
         return new BranchChain<>(false, null);
     }
 
+    /**
+     * Creates new selector, which stores specified input value, and examines all conditions declared
+     * in chain, to return result matching specified input.
+     * It checks if there is exactly one unambiguous non-null result
+     *
+     * Effectively, it is more flexible, non-ambiguous, functional version of switch-case construct.
+     *
+     * @param value input
+     * @return new selector
+     */
     public static BranchSelector<Object> single(Object value) {
         return new BranchChain<>(false, value);
     }
 
+    /**
+     * Branch selector, which allows you to declare switch-case construct by chaining conditions one by one.
+     * Every branch selector is initially constructed by one of factory methods, which defines basic contract.
+     * Then, you can add specific clauses into it, and finally query for result.
+     *
+     * Please note, that every clause can declare narrowed result type, although if you shouldn't abuse
+     * this functionality, because class cast exception will be thrown at the end. Ideally, you should use
+     * this functionality to define result type in first clause and then do not narrow it in next clauses.
+     *
+     * @param <F> type of result
+     */
     public interface BranchSelector<F> {
+        /**
+         * Returns computed result as Optional, possibly empty if none condition is met.
+         *
+         * @return optional result
+         */
         Optional<F> optional();
 
+        /**
+         * Returns computed result.
+         * If there is no result, it throws exception.
+         *
+         * @return result
+         * @throws NoSuchElementException if there is no result
+         */
         F get();
 
+        /**
+         * Returns computed result.
+         * Returns default value, if there is no result.
+         *
+         * @param other default result
+         * @return result
+         */
         F orElse(F other);
 
+        /**
+         * BRANCH CLAUSE: return const value if input has specified type.
+         *
+         * @param type type of input
+         * @param result result
+         * @param <T> type of input
+         * @param <R> type of result, every clause can narrow it
+         * @return selector
+         */
         <T, R extends F> BranchSelector<R> forType(Class<T> type, R result);
 
+        /**
+         * BRANCH CLAUSE: return const value if input has specified type.
+         *
+         * @param type type of input
+         * @param result result
+         * @param <T> type of input
+         * @param <R> type of result, every clause can narrow it
+         * @return selector
+         */
         <T, R extends F> BranchSelector<R> forType(IClass<T> type, R result);
 
+        /**
+         * BRANCH CLAUSE: return const value if input has specified value.
+         *
+         * @param condition expected value
+         * @param result result
+         * @param <T> type of input
+         * @param <R> type of result, every clause can narrow it
+         * @return selector
+         */
         <T, R extends F> BranchSelector<R> forValue(T condition, R result);
 
+        /**
+         * BRANCH CLAUSE: return const value if input has specified value generated by supplier.
+         * Expected value is computed lazily.
+         *
+         * @param condition expected value
+         * @param result result
+         * @param <T> type of input
+         * @param <R> type of result, every clause can narrow it
+         * @return selector
+         */
         <T, R extends F> BranchSelector<R> forExpression(Supplier<T> condition, R result);
 
+        /**
+         * BRANCH CLAUSE: return const value if input has specified value.
+         *
+         * @param condition expected value
+         * @param result result
+         * @param <T> type of input
+         * @param <R> type of result, every clause can narrow it
+         * @return selector
+         */
         <T, R extends F> BranchSelector<R> forOptional(Optional<T> condition, R result);
 
+        /**
+         * BRANCH CLAUSE: return generated value if input has value generated by specified supplier.
+         * Result is computed lazily.
+         *
+         * @param condition expected value
+         * @param supplier result
+         * @param <T> type of input
+         * @param <R> type of result, every clause can narrow it
+         * @return selector
+         */
         <T, R extends F> BranchSelector<R> forOptional(Supplier<Optional<T>> condition, R supplier);
 
+        /**
+         * BRANCH CLAUSE: return generated value if input has specified type.
+         * Expected value is computed lazily.
+         * Result is computed lazily.
+         *
+         * @param type type
+         * @param supplier supplier
+         * @param <T> type of input
+         * @param <R> type of result, every clause can narrow it
+         * @return selector
+         */
         <T, R extends F> BranchSelector<R> computeForType(Class<T> type, Supplier<R> supplier);
 
+        /**
+         * BRANCH CLAUSE: return generated value if input has specified type.
+         * Result is computed lazily.
+         *
+         * @param type type
+         * @param supplier supplier
+         * @param <T> type of input
+         * @param <R> type of result, every clause can narrow it
+         * @return selector
+         */
         <T, R extends F> BranchSelector<R> computeForType(IClass<T> type, Supplier<R> supplier);
 
+        /**
+         * BRANCH CLAUSE: return generated value if input has specified value.
+         * Result is computed lazily.
+         *
+         * @param condition condition
+         * @param supplier supplier
+         * @param <T> type of input
+         * @param <R> type of result, every clause can narrow it
+         * @return selector
+         */
         <T, R extends F> BranchSelector<R> computeForValue(T condition, Supplier<R> supplier);
 
+        /**
+         * BRANCH CLAUSE: return generated value if input has the same value as generated by specified supplier.
+         * Expected value is computed lazily.
+         * Result is computed lazily.
+         *
+         * @param condition condition
+         * @param supplier supplier
+         * @param <T> type of input
+         * @param <R> type of result, every clause can narrow it
+         * @return selector
+         */
         <T, R extends F> BranchSelector<R> computeForExpression(Supplier<T> condition, Supplier<R> supplier);
 
+        /**
+         * BRANCH CLAUSE: return generated value if input has specified value.
+         * Result is computed lazily.
+         *
+         * @param condition condition
+         * @param supplier supplier
+         * @param <T> type of input
+         * @param <R> type of result, every clause can narrow it
+         * @return selector
+         */
         <T, R extends F> BranchSelector<R> computeForOptional(Optional<T> condition, Supplier<R> supplier);
 
+        /**
+         * BRANCH CLAUSE: return generated value if input has the same value as generated by specified supplier.
+         * Expected value is computed lazily.
+         * Result is computed lazily.
+         *
+         * @param condition condition
+         * @param supplier supplier
+         * @param <T> type of input
+         * @param <R> type of result, every clause can narrow it
+         * @return selector
+         */
         <T, R extends F> BranchSelector<R> computeForOptional(Supplier<Optional<T>> condition, Supplier<R> supplier);
 
+        /**
+         * BRANCH CLAUSE: map input into result if input has specified type
+         * Result is computed lazily.
+         *
+         * @param type type
+         * @param function function
+         * @param <T> type of input
+         * @param <R> type of result, every clause can narrow it
+         * @return selector
+         */
         <T, R extends F> BranchSelector<R> mapType(Class<T> type, Function<T, R> function);
 
+        /**
+         * BRANCH CLAUSE: map input into result if input has specified type
+         * Result is computed lazily.
+         *
+         * @param type type
+         * @param function function
+         * @param <T> type of input
+         * @param <R> type of result, every clause can narrow it
+         * @return selector
+         */
         <T, R extends F> BranchSelector<R> mapType(IClass<T> type, Function<T, R> function);
 
+        /**
+         * BRANCH CLAUSE: map input into result if input has specified value
+         * Result is computed lazily.
+         *
+         * @param condition condition
+         * @param function function
+         * @param <T> type of input
+         * @param <R> type of result, every clause can narrow it
+         * @return selector
+         */
         <T, R extends F> BranchSelector<R> mapValue(T condition, Function<T, R> function);
 
+        /**
+         * BRANCH CLAUSE: map input into result if input has the same value as generated by specified supplier.
+         * Expected value is computed lazily.
+         * Result is computed lazily.
+         *
+         * @param condition condition
+         * @param function function
+         * @param <T> type of input
+         * @param <R> type of result, every clause can narrow it
+         * @return selector
+         */
         <T, R extends F> BranchSelector<R> mapExpression(Supplier<T> condition, Function<T, R> function);
 
+        /**
+         * BRANCH CLAUSE: map input into result if input has specified value
+         * Result is computed lazily.
+         *
+         * @param condition condition
+         * @param function function
+         * @param <T> type of input
+         * @param <R> type of result, every clause can narrow it
+         * @return selector
+         */
         <T, R extends F> BranchSelector<R> mapOptional(Optional<T> condition, Function<T, R> function);
 
+        /**
+         * BRANCH CLAUSE: map input into result if input has the same value as generated by specified supplier.
+         * Expected value is computed lazily.
+         * Result is computed lazily.
+         *
+         * @param condition condition
+         * @param function function
+         * @param <T> type of input
+         * @param <R> type of result, every clause can narrow it
+         * @return selector
+         */
         <T, R extends F> BranchSelector<R> mapOptional(Supplier<Optional<T>> condition, Function<T, R> function);
     }
 

+ 88 - 18
assira.core/src/main/java/net/ranides/assira/io/BufferAdapter.java

@@ -6,6 +6,8 @@
  */
 package net.ranides.assira.io;
 
+import lombok.experimental.UtilityClass;
+
 import java.io.EOFException;
 import java.io.IOException;
 import java.io.InputStream;
@@ -25,60 +27,121 @@ import net.ranides.assira.generic.ValueUtils;
 import net.ranides.assira.math.MathUtils;
 
 /**
- * 
+ * Various I/O conversions allowing you to use ByteBuffer as data storage in situations when required type
+ * is ByteChannel, Readers and Stream or other I/O class.
+ *
+ * This utility class creates under the hood rather trivial adapter objects.
+ *
  * @author Ranides Atterwim {@literal <ranides@gmail.com>}
  */
+@UtilityClass
 public final class BufferAdapter {
     
     private static final ByteBuffer EMPTY = ByteBuffer.wrap(new byte[0]);
-    
-    private BufferAdapter() {
-        // utility class
-    }
 
+    /**
+     * Creates ByteChannel accessing data in ByteBuffer
+     *
+     * @param buffer buffer
+     * @return channel
+     */
     public static SeekableByteChannel asChannel(ByteBuffer buffer) {
         return new BChannel(buffer);
     }
-    
+
+    /**
+     * Creates ObjectInputStream reading data from ByteBuffer
+     *
+     * @param buffer buffer
+     * @return stream
+     * @throws IOException if buffer can't be used as ObjectInputStream
+     */
     public static ObjectInputStream asObjectInput(ByteBuffer buffer) throws IOException {
         return new ObjectInputStream(asInputStream(buffer));
     }
-    
+
+    /**
+     * Creates ObjectOutputStream writing data into ByteBuffer
+     *
+     * @param buffer buffer
+     * @return stream
+     * @throws IOException if buffer can't be used as ObjectOutputStream
+     */
     public static ObjectOutputStream asObjectOutput(ByteBuffer buffer) throws IOException {
         return new ObjectOutputStream(asOutputStream(buffer));
     }
-    
+
+    /**
+     * Creates InputStream reading data from ByteBuffer
+     *
+     * @param buffer buffer
+     * @return stream
+     */
     public static InputStream asInputStream(ByteBuffer buffer) {
         return new BIStream(buffer);
     }
-    
+
+    /**
+     * Creates OutputStream writing data into ByteBuffer
+     *
+     * @param buffer buffer
+     * @return stream
+     */
     public static OutputStream asOutputStream(ByteBuffer buffer) {
         return new BOStream(buffer);
     }
-    
+
+    /**
+     * Creates Reader reading data from ByteBuffer
+     *
+     * @param buffer buffer
+     * @param charset data
+     * @return reader
+     */
     public static Reader asReader(ByteBuffer buffer, Charset charset) {
         return asReader(charset.decode(buffer));
     }
-    
+
+    /**
+     * Creates Reader reading data from CharBuffer
+     *
+     * @param buffer buffer
+     * @return reader
+     */
     public static Reader asReader(CharBuffer buffer) {
         return new CReader(buffer);
     }
     
     /**
-     * Returns unbuffered Writer which use charset encoder on the fly.
+     * Creates Writer writing data into ByteBuffer.
+     * This writer is unbuffered and it uses charset encoder on the fly.
      * It's probably ineffective if you want to write char by char.
-     * @param buffer
-     * @param charset
-     * @return 
+     *
+     * @param buffer buffer
+     * @param charset charset
+     * @return writer
      */
     public static Writer asWriter(ByteBuffer buffer, Charset charset) {
         return new BWriter(buffer, charset);
     }
-    
+
+    /**
+     * Creates Writer writing data into CharBuffer
+     *
+     * @param buffer buffer
+     * @return writer
+     */
     public static Writer asWriter(CharBuffer buffer) {
         return new CWriter(buffer);
     }
-    
+
+    /**
+     * Reads all remaining data from buffer and returns it.
+     * It does not change state of buffer (especially does not change position).
+     *
+     * @param buffer buffer
+     * @return data
+     */
     public static byte[] toArray(ByteBuffer buffer) {
         int n = buffer.remaining();
         int p = buffer.position();
@@ -87,7 +150,14 @@ public final class BufferAdapter {
         buffer.position(p);
         return array;
     }
-    
+
+    /**
+     * Reads all remaining data from buffer and returns it.
+     * It does not change state of buffer (especially does not change position).
+     *
+     * @param buffer buffer
+     * @return data
+     */
     public static char[] toArray(CharBuffer buffer) {
         int n = buffer.remaining();
         int p = buffer.position();

+ 88 - 10
assira.core/src/main/java/net/ranides/assira/reflection/BeanMethod.java

@@ -10,37 +10,115 @@ package net.ranides.assira.reflection;
 import net.ranides.assira.functional.special.AnyFunction;
 
 /**
+ * Reflective model for method defined in JavaBean.
+ * Instances of BeanMethod can be obtained from BeanModel.
  *
  * @author Ranides Atterwim {@literal <ranides@gmail.com>}
  */
 public interface BeanMethod {
-    
+
+    /**
+     * Name of method
+     *
+     * @return string
+     */
     String name();
-        
+
+    /**
+     * Creates new function with "this" variable binded to specified object.
+     *
+     * @param that that
+     * @return new function
+     */
     AnyFunction<Object> bind(Object that);
-    
+
+    /**
+     * Invokes method with specified arguments. First argument is used as "this".
+     * Behaviour of invoke is identical as {@link IMethod#invoke(Object that)}
+     *
+     * @param that that
+     * @return object
+     */
     Object invoke(Object that);
-    
+
+    /**
+     * Invokes method with specified arguments. First argument is used as "this".
+     * Behaviour of invoke is identical as {@link IMethod#invoke(Object that, Object... arguments)}
+     *
+     * @param that that
+     * @param arguments arguments
+     * @return object
+     */
     Object invoke(Object that, Object... arguments);
-    
+
+    /**
+     * Invokes method with specified arguments. First argument is used as "this".
+     * Behaviour of invoke is identical as {@link IMethod#call(Object... arguments)}
+     *
+     * @param arguments arguments
+     * @return object
+     */
     Object call(Object... arguments);
 
+    /**
+     * Invokes method with specified arguments. First argument is used as "this".
+     * Behaviour of invoke is identical as {@link IMethod#apply(Object that, Object[] arguments)}
+     *
+     * @param that that
+     * @param arguments arguments
+     * @return object
+     */
     Object apply(Object that, Object[] arguments);
-    
+
+    /**
+     * Returns true if this method can be invoked with arguments of specified types.
+     * Behaviour of invoke is identical as {@link IMethod#matches(IClasses arguments)}
+     *
+     * @param arguments arguments
+     * @return bool
+     */
     boolean matches(IClasses arguments);
-    
+
+    /**
+     * Returns true if this method can be invoked with arguments of specified types.
+     * Behaviour of invoke is identical as {@link IMethod#matches(IClasses arguments)}
+     *
+     * @param arguments arguments
+     * @return bool
+     */
     default boolean matches(IClass<?>... arguments) {
         return matches(IClasses.typeinfo(arguments));
     }
-    
+
+    /**
+     * Returns true if this method can be invoked with arguments of specified types.
+     * Behaviour of invoke is identical as {@link IMethod#matches(Class... arguments)}
+     *
+     * @param arguments arguments
+     * @return bool
+     */
     default boolean matches(Class<?>... arguments) {
         return matches(IClasses.typeinfo(arguments));
     }
-    
+
+    /**
+     * Returns true if this method can be invoked without arguments.
+     * Effectively it means that checks if method is static.
+     * Behaviour of invoke is identical as {@link IMethod#matches()}
+     *
+     * @return bool
+     */
     default boolean matches() {
         return matches(IClasses.EMPTY);
     }
-    
+
+    /**
+     * Returns true if this method can be invoked with specified argument values.
+     * Behaviour of invoke is identical as {@link IMethod#accepts(Object... arguments)}
+     *
+     * @param arguments arguments
+     * @return bool
+     */
     boolean accepts(Object... arguments);
         
 }

+ 157 - 18
assira.core/src/main/java/net/ranides/assira/reflection/BeanModel.java

@@ -14,57 +14,196 @@ import java.util.Optional;
 import java.util.Set;
 
 /**
- * 
+ * This interface is designed for reflectin inspection of JavaBeans.
+ * Standard {@link java.beans.Introspector} is very old and it does not support more sophisticated cases:
+ *  - overloaded methods
+ *  - overloaded setters (in fact, we do not support them too)
+ *  - fluent getters and setters (without get/set prefixes)
+ *  - indexed properties ({@code Type[]} and {@code List<Type>})
+ *  - generic types (very important!)
+ *
+ *
+ * It is compatible with IClass interfaces.
+ *
+ * Overloaded method can't be accessed directly, but are grouped inside BeanMethod object.
+ * BeanMethod will select appropriate overloaded version at call time.
+ *
+ * There is support for "fluent convention" - you can create fluent map, which will allow you to
+ * read all JavaBean properties AND use no-argument methods as computed-property. For example,
+ * if your class contains method with name "length()" then you can read fluent property "length" despite
+ * the fact, that there is no "getLength" accessor.
+ *
+ * This functionality is accessible only through FluentMap returned by "fluent" method.
+ * You can't call this type of method through "property" or "properties".
+ *
+ * Note about indexed properties:
+ * Some properties defined more than ordinary getters and setters for lists or arrays.
+ * In such a case, BeanModel does not return value of property directly, but wraps it by specialized
+ * virtual list.
+ *
+ * This list allows you to modify list elements one-by-one without rewriting whole property over and
+ * over again. Please note, that you can introduce structural changes into list, but they won't be
+ * visible in JavaBean. In such a case you have to invoke appropriate setter method.
+ *
  * @author Ranides Atterwim {@literal <ranides@gmail.com>}
  */
 public interface BeanModel {
-    
+
+    /**
+     * Creates model for specified type
+     *
+     * @param type type
+     * @return model
+     */
     static BeanModel typeinfo(Class<?> type) {
         return typeinfo(IClass.typeinfo(type));
     }
-    
+
+    /**
+     * Creates model for specified type
+     *
+     * @param type type
+     * @return model
+     */
     static BeanModel typeinfo(IClass<?> type) {
         return FBeanModel.resolve(type);
     }
-    
+
+    /**
+     * Creates model for specified object
+     *
+     * @param object object
+     * @return model
+     */
     static BeanModel typefor(Object object) {
         return typeinfo(IClass.typefor(object));
     }
 
-    // java.beans.Introspector jest w praktyce totalnie przestarzałe - nie wypuszczamy tego wcale
-    // MOŻE wewnętrznie używajmy... ale tylko MOŻE. Ręczne zaimplementowanie wchodzi w grę, bo i tak
-    // musimy porobić hacki na Introspektora, np żeby obsługiwać:
-    //      - overloaded methods
-    //      - overloaded setters (NIE obsługujemy obecnie!)
-    //      - indexed properties: Type[] oraz List<Type>
-    //      - generic types
-    
+    /**
+     * Returns type of introspected bean
+     *
+     * @return type
+     */
     IClass<?> type();
-    
+
+    /**
+     * Returns method with specified name.
+     * If model has overloaded version, returned BeanMethod represents multi-dispatch.
+     *
+     * @param name name
+     * @return empty if not found
+     */
     Optional<BeanMethod> method(String name);
-    
+
+    /**
+     * Returns all methods inside JavaBean, mapped by name.
+     * Overloaded methods are joined and represented as one multi-dispatch BeanMethod
+     *
+     * @return map
+     */
     Map<String, BeanMethod> methods();
 
+    /**
+     * Returns property with specified name.
+     * Method supports only JavaBean properties (not fluent)
+     *
+     * @param name name
+     * @return empty if not found
+     */
     Optional<BeanProperty> property(String name);
-    
+
+    /**
+     * Returns map of properties.
+     * Method supports only JavaBean properties (not fluent)
+     *
+     * @return map
+     */
     Map<String, BeanProperty> properties();
-    
+
+    /**
+     * Returns map which is view of specified JavaBean.
+     * Any JavaBean property is visible in map.
+     * Modification of map will modify JavaBean.
+     *
+     * @param that object
+     * @return map view
+     */
     Map<String, Object> properties(Object that);
 
+    /**
+     * Returns all names which can be used as keys inside FluentMap
+     * Fluent keys are names of all properties and no-argument methods which return non-void value.
+     *
+     * @return keys
+     */
     Set<String> fluent();
 
+    /**
+     * Returns map which is view of specified JavaBean.
+     *
+     * Map contains keys for all properties and and no-argument methods which return non-void value.
+     *
+     * Access to map content will cause invocation of getter or method with specified name.
+     * Modification of properties is possible.
+     * Modification of computed value (fluent method) will cause exception.
+     *
+     * @param that that
+     * @return map
+     */
     FluentMap fluent(Object that);
 
+    /**
+     * Creates new instance of JavaBean described by this model.
+     * Class of constructed object is identical as returned by {@link #type()} method.
+     *
+     * @return new object
+     */
     Object construct();
-    
+
+    /**
+     * Creates new instance of JavaBean described by this model.
+     * Class of constructed object is identical as returned by {@link #type()} method.
+     *
+     * Assigns JavaBeans properties to values specified by map passed as argument.
+     *
+     * @param values property values
+     * @return new object
+     */
     Object construct(Map<String, Object> values);
-    
+
+    /**
+     * Creates new object and copies all properties from source object to created instance
+     *
+     * @param that source object
+     * @return new object
+     */
     Object clone(Object that);
 
+    /**
+     * This is specialization of map interface representing view of JavaBean
+     * Map contains entries for every property and no-argument method (fluent method).
+     *
+     * Writable properties can be modified by put methods.
+     * Read-only properties or computed values can't be modified.
+     * Map does not support adding or removing keys.
+     *
+     * Illegal operation will throw UnsupportedOperationException,
+     * with more detailed exception as a cause.
+     */
     interface FluentMap extends Map<String, Object> {
 
+        /**
+         * Returns JavaBean instance which is represented by this map view.
+         *
+         * @return object
+         */
         Object unwrap();
 
+        /**
+         * Returns BeanModel for this specific map.
+         *
+         * @return model
+         */
         BeanModel model();
 
     }

+ 80 - 10
assira.core/src/main/java/net/ranides/assira/reflection/BeanProperty.java

@@ -10,28 +10,98 @@ package net.ranides.assira.reflection;
 import net.ranides.assira.generic.Wrapper;
 
 /**
+ * Model for JavaBean property.
  *
  * @author Ranides Atterwim {@literal <ranides@gmail.com>}
  */
 public interface BeanProperty {
-    
+
+    /**
+     * Property name, derived from getter & setter name, using various JavaBean conventions.
+     * @return string
+     */
     String name();
-    
+
+    /**
+     * Property type, derived from getter & setter signature.
+     * @return type
+     */
     IClass<?> type();
-        
+
+    /**
+     * Creates new wrapper object, which allows you to modify described property in specified object.
+     *
+     * @param that object
+     * @return wrapper
+     */
     Wrapper<Object> bind(Object that);
-    
+
+    /**
+     * Creates new wrapper object, which allows you to modify described property in specified object.
+     *
+     * Please note, that this version infers type of property in unsafe way.
+     * It is responsibility of the caller, to store result in correct variable.
+     *
+     * @param that object
+     * @param <T> inferred type of property
+     * @return wrapper
+     */
     <T> Wrapper<T> $bind(Object that);
-    
+
+    /**
+     * Changes property inside specified object. Is uses setter method.
+     *
+     * @param that object
+     * @param value value
+     * @throws UnsupportedOperationException for read-only properties
+     */
     void set(Object that, Object value);
-    
+
+    /**
+     * Reads property from specified object. Is uses getter method.
+     *
+     * @param that object
+     * @return value
+     * @throws UnsupportedOperationException for write-only properties
+     */
     Object get(Object that);
-    
+
+    /**
+     * Reads property from specified object. Is uses getter method.
+     *
+     * Please note, that this version infers type of property in unsafe way.
+     * It is responsibility of the caller, to store result in correct variable.
+     *
+     * @param that object
+     * @param <T> inferred type
+     * @return value
+     * @throws UnsupportedOperationException for write-only properties
+     */
     <T> T $get(Object that);
-    
+
+    /**
+     * Changes property inside specified object. Is uses setter method.
+     * Returns old value of property. It uses getter method.
+     * If property is write-only, it returns null as previous value.
+     *
+     * @param that object
+     * @param value value
+     * @return previous value or null
+     * @throws UnsupportedOperationException for read-only properties
+     */
     Object replace(Object that, Object value);
-    
+
+    /**
+     * Returns true if property is readable
+     *
+     * @return bool
+     */
     boolean isReadable();
-    
+
+    /**
+     * Returns true if property is writable
+     *
+     * @return bool
+     */
     boolean isWritable();
 }

+ 6 - 6
assira.core/src/main/java/net/ranides/assira/reflection/impl/bean/FBeanModel.java

@@ -11,12 +11,7 @@ import lombok.experimental.UtilityClass;
 
 import java.util.Collection;
 import net.ranides.assira.collection.maps.CacheMap;
-import net.ranides.assira.reflection.BeanMethod;
-import net.ranides.assira.reflection.BeanModel;
-import net.ranides.assira.reflection.BeanProperty;
-import net.ranides.assira.reflection.IAttribute;
-import net.ranides.assira.reflection.IClass;
-import net.ranides.assira.reflection.IMethod;
+import net.ranides.assira.reflection.*;
 
 /**
  *
@@ -50,4 +45,9 @@ public final class FBeanModel {
         return new RPField(info);
     }
 
+    public static UnsupportedOperationException newUnsupportedOperationException(String message) {
+        return new UnsupportedOperationException(new IReflectiveException(message));
+    }
+
+
 }

+ 5 - 6
assira.core/src/main/java/net/ranides/assira/reflection/impl/bean/RBeanModel.java

@@ -183,8 +183,7 @@ public class RBeanModel implements BeanModel, Serializable {
         if(!method.arguments().equals(INT_ARGS)) {
             return false;
         }
-        IAttributes ra = method.returns().attributes();
-        if(ra.contains(IAttribute.VOID)) {
+        if(method.returns().isVoid()) {
             return false;
         }
         
@@ -194,7 +193,7 @@ public class RBeanModel implements BeanModel, Serializable {
             return true;
         }
         // akceptacja gettera logicznego "isValue"
-        if(name.startsWith("is") && name.length()>2 && Character.isUpperCase(name.charAt(2)) && ra.contains(IAttribute.BOOLEAN) ) {
+        if(name.startsWith("is") && name.length()>2 && Character.isUpperCase(name.charAt(2)) && method.returns().isBoolean()) {
             return true;
         }
         return false;
@@ -312,9 +311,9 @@ public class RBeanModel implements BeanModel, Serializable {
             }
             BeanMethod m = fluent.get(key);
             if(m != null) {
-                throw new IReflectiveException("Computed fluent property " + key + " is read-only");
+                throw FBeanModel.newUnsupportedOperationException("Computed fluent property " + key + " is read-only");
             }
-            throw new IReflectiveException("No property with name " + key);
+            throw FBeanModel.newUnsupportedOperationException("No property with name " + key);
         }
 
         @Override
@@ -419,7 +418,7 @@ public class RBeanModel implements BeanModel, Serializable {
 
         @Override
         public Object setValue(Object value) {
-            throw new IReflectiveException("Computed fluent property " + p.name() + " is read-only");
+            throw FBeanModel.newUnsupportedOperationException("Computed fluent property " + p.name() + " is read-only");
         }
     }
 

+ 2 - 2
assira.core/src/main/java/net/ranides/assira/reflection/impl/bean/RPArray.java

@@ -103,13 +103,13 @@ public class RPArray extends ABeanProperty implements Serializable {
     
     private void checkRA() {
         if(!isReadable()) {
-            throw new IReflectiveException("property " + name() + " is read-only");
+            throw FBeanModel.newUnsupportedOperationException("property " + name() + " is read-only");
         }
     }
     
     private void checkWA() {
         if(!isWritable()) {
-            throw new IReflectiveException("property " + name() + " is write-only");
+            throw FBeanModel.newUnsupportedOperationException("property " + name() + " is write-only");
         }
     }
     

+ 2 - 2
assira.core/src/main/java/net/ranides/assira/reflection/impl/bean/RPField.java

@@ -31,7 +31,7 @@ public class RPField extends ABeanProperty implements Serializable {
     @Override
     public void set(Object that, Object value) {
         if(null == setter) {
-            throw new IReflectiveException("Property " + name() + " is read-only");
+            throw FBeanModel.newUnsupportedOperationException("Property " + name() + " is read-only");
         }
         setter.invoke(that, value);
     }
@@ -39,7 +39,7 @@ public class RPField extends ABeanProperty implements Serializable {
     @Override
     public Object get(Object that) {
         if(null == getter) {
-            throw new IReflectiveException("Property " + name() + " is write-only");
+            throw FBeanModel.newUnsupportedOperationException("Property " + name() + " is write-only");
         }
         return getter.invoke(that);
     }

+ 2 - 2
assira.core/src/main/java/net/ranides/assira/reflection/impl/bean/RPIndexed.java

@@ -89,13 +89,13 @@ public class RPIndexed extends ABeanProperty implements Serializable {
     
     private void checkRA() {
         if(!isReadable()) {
-            throw new IReflectiveException("property " + name() + " is read-only");
+            throw FBeanModel.newUnsupportedOperationException("property " + name() + " is read-only");
         }
     }
     
     private void checkWA() {
         if(!isWritable()) {
-            throw new IReflectiveException("property " + name() + " is write-only");
+            throw FBeanModel.newUnsupportedOperationException("property " + name() + " is write-only");
         }
     }
     

+ 85 - 6
assira.core/src/main/java/net/ranides/assira/reflection/util/BeanUtils.java

@@ -22,19 +22,31 @@ import java.util.List;
 import java.util.function.BiConsumer;
 
 /**
- *
+ * Helper method for JavaBeans
  * @author Ranides Atterwim {@literal <ranides@gmail.com>}
  */
 @UtilityClass
 public final class BeanUtils {
 
+    /**
+     * Converts XML into JavaBean. It uses XMLDecoder.
+     *
+     * @param xml xml
+     * @return new instance of JavaBean
+     */
     public static Object fromXML(String xml) {
         XMLDecoder dec = new XMLDecoder(new StringInput(xml));
         Object object = dec.readObject();
         dec.close();
         return object;
     }
-    
+
+    /**
+     * Converts JavaBean into XML. It uses XMLEncoder.
+     *
+     * @param object object
+     * @return xml
+     */
     public static String toXML(Object object) {
         StringOutput output = new StringOutput();
         XMLEncoder encoder = new XMLEncoder(output);
@@ -42,15 +54,50 @@ public final class BeanUtils {
         encoder.close();
         return output.toString();
     }
-    
+
+    /**
+     * It creates deep copy of JavaBean by serialization to and from XML format.
+     *
+     * @param object object
+     * @return new deep copy of an object
+     */
     public static Object copy(Object object) {
         return fromXML( toXML(object) );
     }
 
+    /**
+     * It creates BeanMapper which converts JavaBeans of source type into JavaBean of target type.
+     * Please read {@link #map} description to know more about conversion.
+     *
+     * @param target type
+     * @param source type
+     * @param <T> type
+     * @param <S> type
+     * @return mapper
+     */
     public static <T,S> BeanMapper<T,S> mapper(IClass<T> target, IClass<S> source) {
         return new BeanMapper<>(target, source);
     }
 
+    /**
+     * It copies properties from source and puts them into target.
+     * It tries to copy only properties if following conditions are met:
+     *  - properties have identical name
+     *  - properties have equivalent types
+     *  - property is readable from source
+     *  - property is writable in target
+     *
+     * If some property does not meet any of requirements, it is silently ignored.
+     * For example, method does not throw exception if it detects properties with the same name but different type.
+     *
+     * This method does not create new instance of target object. It modifies the passed one.
+     * Modified target object is returned as a result.
+     *
+     * @param target object
+     * @param source object
+     * @param <T> type of target
+     * @return target
+     */
     public static <T> T map(T target, Object source) {
         match(BeanModel.typefor(target), BeanModel.typefor(source), (pt, ps)-> { pt.set(target, ps.get(source)); });
         return target;
@@ -67,20 +114,52 @@ public final class BeanUtils {
         }
     }
 
+    /**
+     * BeanMapper can be used to copy properties from one JavaBean to another one.
+     * Please read description of method {@link BeanUtils#map(Object, Object)} to know more details about
+     * copy strategy.
+     *
+     * BeanMapper is preferred to "map" method when you have to map a lot of objects of known types.
+     * In this case BeanMapper has better performance, because it creates list of matched properties only once
+     * and reuses it.
+     *
+     * @param <T> target type
+     * @param <S> source type
+     */
     public static class BeanMapper<T,S> {
 
+        private final IClass<T> type;
         private final List<BeanPropertyMapper> matched = new ArrayList<>();
 
         private BeanMapper(IClass<T> target, IClass<S> source) {
+            type = target;
             match(BeanModel.typeinfo(target), BeanModel.typeinfo(source), (pt,ps) -> {
                 matched.add(new BeanPropertyMapper(pt, ps));
             });
         }
 
-        public void map(T target, S source) {
+        /**
+         * Copies properties from source into target.
+         *
+         * @param target target
+         * @param source source
+         * @return target
+         */
+        public T map(T target, S source) {
             for(BeanPropertyMapper bpm : matched) {
-                bpm.move(target, source);
+                bpm.copy(target, source);
             }
+            return target;
+        }
+
+        /**
+         * Creates new instance of target object and copies into it properties from source.
+         *
+         * @param source source
+         * @return new instance of object
+         */
+        public T map(S source) {
+            return map(type.construct(), source);
         }
     }
 
@@ -95,7 +174,7 @@ public final class BeanUtils {
             this.sp = source;
         }
 
-        public void move(Object target, Object source) {
+        public void copy(Object target, Object source) {
             tp.set(target, sp.get(source));
         }
     }