3.1. Beans

Fabio Piro edited this page May 15, 2016 · 12 revisions

Build Status Maven Central Dependency Status

High level reflection API for dealing with JavaBean properties and derivate (indexed\mapped). Inspired by Apache Commons BeanUtils and Spring BeanWrapper abstraction.

Classes: 1 Status: Stable. Test coverage: 100%. Works on Android: Yes.

Dependencies

Required

Optional

  • cglib If found in the classpath then the bytecode proxy generation will be automatically enabled to provide better performance.

Installation

  • Maven:

      <dependencies>
      	<dependency>
          	<groupId>org.minimalcode</groupId>
          	<artifactId>minimalcode-beans</artifactId>
          	<version>0.5.1</version><!-- or last version -->
        	</dependency>
        
        	<!-- Optional Cglib Dependency (avoid it on Android systems) -->
        	<dependency>
          	<groupId>cglib</groupId>
            	<artifactId>cglib</artifactId>
            	<version>3.1</version>
        	</dependency>
    	</dependencies>

ObjectWrapper

ObjectWrapper can wrap any type of object. In a not-concurrent context, it is also perfectly safe to change the wrapped object at any time, without side effects (avoiding instantiating a new wrapper). Options can also be changed at any time.

// New ObjectWrapper
Object obj = new Book();
ObjectWrapper wrapper = new ObjectWrapper(obj);
String bookTitle = (String) wrapper.getSimpleValue("title");// obj.getTitle();

// Switch
Object obj2 = new Author();
wrapper.setWrappedObject(obj2);
int authorAge = (int) wrapper.getSimpleValue("age");// obj2.getAge();

Reflect

ObjectWrapper is built upon the MinimalCode Reflect framework, which provides raw reflection and properties introspection (like annotations, type etc...). It is highly recommended to read also its documentation.

The Bean of the wrapped object is always available through ObjectWrapper::getBean. Note that while each of the following functions also works with a property-name lookup (by String), is always preferible to use the Property object directly, when available (for example in loops).

Bean<?> bean = wrapper.getBean();

for(Property property : bean.getProperties()) {
  if(property.isAnnotationPresent(MyAnnotationOne.class)) {
    return wrapper.getSimpleValue(property);
  }
}

Simple Properties

Simple, or scalar, properties have a single value that may be retrieved or modified. The underlying property type might be a primitive (such as int) or an object (such as a java.lang.String or java.util.LinkedList).

// Get Simple
String name = (String) wrapper.getSimpleValue("name");// obj.getName();
List<String> authorsList = (List<String>) wrapper.getSimpleValue("authorsList");// obj.getAuthorsList();

// Set Simple
wrapper.setSimpleValue("name", "new-name-value");// obj.setName("new-name-value");
wrapper.setSimpleValue("namesMap", new HashMap<Locale, String>());// obj.setNamesMap(new HashMap<Locale, String>());

Indexed Properties

An indexed property is a java.util.List or an array type which stores an ordered collection of objects (all of the same type) that can be individually accessed by an integer-valued, non-negative index. As an extension the wrapper considers, for retrieving only, any finite java.lang.Iterable (Collection, List, Set...) to be indexed as well.

// Get Indexed (List, Iterable, Collection, array...)
String author = (String) wrapper.getIndexedValue("authorsList", 1);// obj.getAuthorsList().get(1);
Chapter chapter = (Chapter) wrapper.getIndexedValue("chaptersArray", 3);// obj.getChaptersArray()[3];
Tag favorite = (Tag) wrapper.getIndexedValue("favoritesCollection", 1);// obj.getFavoritesCollection() --> iterate to 1

// Set Indexed (List or array)
wrapper.setIndexedValue("authorsList", 1, "new-author");// obj.getAuthorsList().set(1, "new-author");
wrapper.setIndexedValue("chaptersArray", 3, new Chapter());// obj.getChaptersArray()[3] = new Chapter();

Mapped Properties

A mapped property is a java.util.Map type whose elements can be individually accessed by an Object-valued key.

// Get Mapped (Map)
String nameInEnglish = (String) wrapper.getMappedValue("namesMap", Locale::ENGLISH);// obj.getNamesMap().get(Locale::ENGLISH);

// Set Mapped (Map)
wrapper.setMappedValue("namesMap", Locale::ENGLISH, "name-in-english");// obj.getNamesMap().put(Locale::ENGLISH, "name-in-english");

Nested Properties

It is possible to concatenate together the property names of the access path, using "." separators for simple properties, and using square brackets "[x]" for indexed and mapped properties. Any arbitrary level of nesting is allowed.

// Get Nested
String name = (String) wrapper.getValue("book.author.publishersMap[profile].websitesArray[1].owner.name");
// obj.getBook().getAuthor().getPublishersMap().get("profile").getWebsitesArray()[1].getOwner().getName();

// Set Nested
wrapper.setValue("site.projectsList[1].collaboratorsMap[ciaps].name", "Andrea");
// obj.getSite().getProjectsList().get(1).getCollaboratorsMap().get("ciaps").setName("Andrea");

Auto Instancing Option

Default: enabled. If enabled, automatically instances a new object (a no-args constructor is required) or a new List, Map or array, if the nested property has value null in the wrapped object.

Book obj = new Book();
obj.setAuthor(null);// profile and websitesArray are also null

ObjectWrapper wrapper = new ObjectWrapper(obj);
wrapper.setValue("author.profile.websitesArray[1]", "minimalcode.org");
// obj.{new Author()}.{new Profile()}.{new String[1]}[1] = "minimalcode.org";

// Disable it
wrapper.setAutoInstancing(false);

Auto Growing Option

Default: enabled. If enabled, automatically grows-up any out of bounds List or array to accomodate an element in an index position larger than their capacity.

Book obj = new Book();
obj.setChaptersArray(new Chapter[2]);

ObjectWrapper wrapper = new ObjectWrapper(obj);
wrapper.setIndexedValue("chaptersArray", 5, new Chapter());
// obj.getChaptersArray().{grows up to 5 elements}[5] = new Chapter();

// Disable it
wrapper.setAutoGrowing(false);

OutOfBounds Safety Option

Default: enabled. If enabled, always returns a null value instead of throwing an IndexOutOfBoundsException when trying to access to an item in an out of bounds position of a List, array or any type of finite Iterable, behaving like a Map#get(Object) implementation.

wrapper.getIndexedValue("chaptersArray", 10);
// returns 'null' with obj.getChaptersArray().lenght < 10

wrapper.getIndexedValue("authorsList", 9999999);
// returns 'null' with obj.getAuthorsList().size() < 9999999

// Disable it
wrapper.setOutOfBoundsSafety(false);
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.