Corner Cases

In the Walk-through page we documented the most common scenarios where you would want to use PODAM. These include:

  • Normal one-level POJO
  • (Complex) graph of POJOs
  • Immutable-like POJOs

In this page I will show how PODAM handles some corner cases, such as:

  • Inheritance (e.g. when a POJO extends a class, either abstract or non-abstract)
  • Singleton-like classes (e.g. when a public, no-arg constructor is not present but a series of static factory methods exist (such as getInstance())

Inheritance

PODAM supports inheritance. It will fill parent attributes if an accessible setter is available to the POJOs which you are filling with data.

Example

The following example illustrates a parent class, uk.co.jemos.podam.test.dto.AbstractOneDimensionalPojo which is extended by another POJO, uk.co.jemos.podam.test.dto.OneDimensionalChildPojo.

/**
 * 
 */
package uk.co.jemos.podam.test.dto;

import java.util.Calendar;

import uk.co.jemos.podam.annotations.PodamIntValue;

/**
 * Abstract POJO to test inheritance.
 * 
 * @author mtedone
 * 
 */
public abstract class AbstractOneDimensionalPojo {

        @PodamIntValue(maxValue = 10)
        private int parentIntField;
        
        private Calendar parentCalendarField;
        
        public int getParentIntField() {
                return parentIntField;
        }

        protected void setParentIntField(int parentIntField) {
                this.parentIntField = parentIntField;
        }
        
        public Calendar getParentCalendarField() {
                return parentCalendarField;
        }
        
        protected void setParentCalendarField(Calendar parentCalendarField) {
                this.parentCalendarField = parentCalendarField;
        }

        @Override
        public String toString() {
                final String TAB = "    ";

                StringBuilder retValue = new StringBuilder();

                retValue.append("AbstractOneDimensionalPojo ( ")
                                .append("parentIntField = ").append(parentIntField).append(TAB)
                                .append("parentCalendarField = ").append(parentCalendarField.getTime())
                                .append(TAB).append(" )");

                return retValue.toString();
        }
}

The class above defines two private attributes, parentIntField and parentCalendarField. It also provides two protected setters which are available to sub-classes. The presence of accessible setter methods is a pre-requisite for this inheritance to be handled properly by PODAM.

This is the child class:

/**
 * 
 */
package uk.co.jemos.podam.test.dto;

/**
 * A child class to test inheritance
 * @author mtedone
 * 
 */
public class OneDimensionalChildPojo extends AbstractOneDimensionalPojo {
        
        private int intField;
        
        private String strField;        

        public OneDimensionalChildPojo() {
                super();
        }

        public int getIntField() {
                return intField;
        }

        public void setIntField(int intField) {
                this.intField = intField;
        }

        public String getStrField() {
                return strField;
        }

        public void setStrField(String strField) {
                this.strField = strField;
        }

        /**
         * Constructs a <code>String</code> with all attributes in name = value
         * format.
         * 
         * @return a <code>String</code> representation of this object.
         */
        @Override
        public String toString() {
                final String TAB = "    ";

                StringBuilder retValue = new StringBuilder();
                retValue.append(super.toString()).append(TAB);

                retValue.append("OneDimensionalChildPojo ( ").append("intField = ")
                                .append(intField).append(TAB).append("strField = ")
                                .append(strField).append(TAB).append(" )");

                return retValue.toString();
        }       

}

Running PODAM on the OneDimensionalChildPojo class will produce the following toString() result:

AbstractOneDimensionalPojo ( parentIntField = 3    parentCalendarField = Sat Apr 30 13:49:49 BST 2011     )    
  OneDimensionalChildPojo ( intField = -516418302    strField = ﶖ藾冋荬馫콈愁㵼藙酬     )

As you can see Podam has set both the parent attributes values.

Singleton-like classes

A Singleton is a class which provides only an instance of itself. One of the implementation details of a Singleton is that, unless one used an Enum, the class should forbid clients from creating new instances. This is typically achieved by declaring the constructor private and by providing a public static method (e.g. getInstance()) which returns a singleton instance of the class.

PODAM handles Singleton-like classes, as the following example demonstrates:

Example

/**
 * 
 */
package uk.co.jemos.podam.test.dto;

import java.io.Serializable;
import java.util.Calendar;
import java.util.List;
import java.util.Map;

/**
 * A POJO to test a Singleton-like scenario where the public static Singleton
 * method contains parameters.
 * 
 * @author mtedone
 * 
 */
public class SingletonWithParametersInStaticFactoryPojo implements Serializable {       

        private static final long serialVersionUID = 1L;        

        private final Calendar createDate;

        private final List<OneDimensionalTestPojo> pojoList;

        private final Map<String, OneDimensionalTestPojo> pojoMap;

        private final String firstName;

        private static SingletonWithParametersInStaticFactoryPojo singleton;

        // ------------------->> Constructors

        /**
         * A private constructor to enforce the Singleton pattern
         * 
         * @param createDate
         *            The creation date
         * @param pojoList
         *            A list
         * @param pojoMap
         *            A map
         * @param firstName
         *            The first name
         */
        private SingletonWithParametersInStaticFactoryPojo(Calendar createDate,
                        List<OneDimensionalTestPojo> pojoList,
                        Map<String, OneDimensionalTestPojo> pojoMap, String firstName) {
                super();
                this.createDate = createDate;
                this.pojoList = pojoList;
                this.pojoMap = pojoMap;
                this.firstName = firstName;
        }
        

        /**
         * Singleton method
         * 
         * @param createDate
         *            The creation date
         * @param pojoList
         *            A list
         * @param pojoMap
         *            A map
         * @param firstName
         *            The first name
         * @return a singleton instance of this class
         */
        public static SingletonWithParametersInStaticFactoryPojo getInstance(
                        Calendar createDate, List<OneDimensionalTestPojo> pojoList,
                        Map<String, OneDimensionalTestPojo> pojoMap, String firstName) {
                if (null == singleton) {
                        singleton = new SingletonWithParametersInStaticFactoryPojo(
                                        createDate, pojoList, pojoMap, firstName);
                }

                return singleton;
        }
        
        public Calendar getCreateDate() {
                return createDate;
        }

        public List<OneDimensionalTestPojo> getPojoList() {
                return pojoList;
        }

        public Map<String, OneDimensionalTestPojo> getPojoMap() {
                return pojoMap;
        }

        public String getFirstName() {
                return firstName;
        }

}

The class above shows an interesting scenario: a Singleton-like class with the Singleton method which accepts parameters. To make the example even more palatable, some arguments are container-like (collections, maps).

Running the test on the class above produces the following result:

SingletonWithParametersInStaticFactoryPojo ( 
        createDate = Sat Apr 30 14:05:21 BST 2011    
        pojoList = [
                OneDimensionalTestPojo ( booleanField = true    booleanObjectField = true    byteField = 116    
                                                                 byteObjectField = 10    shortField = 77    shortObjectField = 4    charField = 㐘    
                                                                 charObjectField = ꪁ    intField = 1339494839    intObjectField = 980291811    
                                                                 longField = -3119222069530856219    longObjectField = 4260197822662475631    
                                                                 floatField = 0.24369287    floatObjectField = 0.7073253    doubleField = 0.7151146071422815    
                                                                 doubleObjectField = 0.8253745751884359    stringField = ౠ᮷᳃┸㯪쭵Ὼాꎐᆫ    
                                                                 objectField = java.lang.Object@57a7ddcf    calendarField = Sat Apr 30 14:05:21 BST 2011    
                                                                 dateField = Sat Apr 30 14:05:21 BST 2011    randomArray = [Ljava.util.Random;@4dd36dfe     )]    
        pojoMap = {
                研䷢쫗㛗á룸ꁧ╝楫=OneDimensionalTestPojo ( booleanField = false    booleanObjectField = true    byteField = 8    
                                                                                                byteObjectField = 39    shortField = 17    shortObjectField = 24    
                                                                                                charField = 끰    charObjectField = 뿩    intField = 1412793167    
                                                                                                intObjectField = -1521479114    longField = -5054977360881301041    
                                                                                                longObjectField = 8252315121779248953    floatField = 0.13922673    
                                                                                                floatObjectField = 0.6835188    doubleField = 0.7345653869074095    
                                                                                                doubleObjectField = 0.7172732709165366    
                                                                                                stringField = 英訆ฤ뭧㜲펮憷ᮻ焜懪    objectField = java.lang.Object@73da669c    
                                                                                                calendarField = Sat Apr 30 14:05:21 BST 2011    
                                                                                                dateField = Sat Apr 30 14:05:21 BST 2011    
                                                                                                randomArray = [Ljava.util.Random;@786c730     )}    
        firstName = 㪜됰찄狇브穊厣텾ꧠ     )

All three arguments to the Singleton method have been filled with data.

Generic classes

PODAM support classes with generic attributes by solving its currently generic types by the owner setter declaration.

Example

The following example illustrates a generic class, uk.co.jemos.podam.test.dto.pdm45.GenericPojo<F, S>, which is used several times as attributes of another POJO, uk.co.jemos.podam.test.dto.pdm45.GenericAttributePojo.

package uk.co.jemos.podam.test.dto.pdm45;

import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * Generic Pojo.
 * 
 * @author marciocarmona
 * 
 * @param <F>
 *            the first generic type
 * @param <S>
 *            the second generic type
 */
public class GenericPojo<F, S> {

        private F firstValue;
        private S secondValue;
        @PodamCollection(nbrElements = 2)
        private List<F> firstList;
        @PodamCollection(nbrElements = 2)
        private S[] secondArray;
        @PodamCollection(nbrElements = 2)
        private Map<F, S> firstSecondMap;

        public F getFirstValue() {
                return firstValue;
        }

        public void setFirstValue(F firstValue) {
                this.firstValue = firstValue;
        }

        public S getSecondValue() {
                return secondValue;
        }

        public void setSecondValue(S secondValue) {
                this.secondValue = secondValue;
        }

        public List<F> getFirstList() {
                return firstList;
        }

        public void setFirstList(List<F> firstList) {
                this.firstList = firstList;
        }

        public S[] getSecondArray() {
                return secondArray;
        }

        public void setSecondArray(S[] secondArray) {
                this.secondArray = secondArray;
        }

        public Map<F, S> getFirstSecondMap() {
                return firstSecondMap;
        }

        public void setFirstSecondMap(Map<F, S> firstSecondMap) {
                this.firstSecondMap = firstSecondMap;
        }

        @Override
        public String toString() {
                return "GenericPojo [firstValue=" + firstValue + ", secondValue="
                                + secondValue + ", firstList=" + firstList + ", secondArray="
                                + Arrays.toString(secondArray) + ", firstSecondMap="
                                + firstSecondMap + "]";
        }
}

Notice all the generic types (F and S) used by the class attributes, for each instance of this class we may use different types. It is very important to use the generic notation on the setters because this is where the current types are solved to create the instance that will be supplied for the setter.

Now this is a POJO using the generic class as an attribute, defining F and S as String and Long respectively.

package uk.co.jemos.podam.test.dto.pdm45;

/**
 * Pojo with generic attribute.
 * 
 * @author marciocarmona
 *
 */
public class GenericAttributePojo {

        private GenericPojo<String, Long> genericPojo;

        public GenericPojo<String, Long> getGenericPojo() {
                return genericPojo;
        }

        public void setGenericPojo(GenericPojo<String, Long> genericPojo) {
                this.genericPojo = genericPojo;
        }

        @Override
        public String toString() {
                return "GenericAttributePojo [genericPojo=" + genericPojo + "]";
        }
}

As before, it is necessary to specify the types to be used on the setter, because these will be the types used during the generic class instantiation.

Running the test on the class above produces the following result:

GenericAttributePojo [
    genericPojo=GenericPojo [
        firstValue=mJdAWTgzkv,
        secondValue=1340162143705,
        firstList=[EMZeLCj8qf, VJZbWl8Xyo],
        secondArray=[1340162143705, 1340162143705],
        firstSecondMap={sSb8TCcOZB=1340162143700, EqVTVO78RR=1340162143700}
    ]
]

As you can notice all the generic types have been solved properly as String or Long according to the types defined on GenericAttributePojo class. Also the collections, the array and the map have 2 items as specified by the @PodamCollection annotation.

If you want to manufacture a generic class directly, i.e., that is not wrapped by a non-generic class like on the example above, please refer to "Manufacturing generic types" section in the Introduction page.

External factory

There are some classes for which PODAM is not able to instantiate an object. Typically these classes are not POJOs, but require sophisticate instantiation. PODAM will leave values for such classes as null, but you still can manufacture them with second level factory registered to the PODAM factory.

Example

Define your own external factory implementing PodamFactory interface.

package uk.co.jemos.podam.example;

/**
 * External factory
 */
public class MyExternalFactory implements PodamFactory {

        private static final Logger LOG = LoggerFactory
                        .getLogger(MyExternalFactory.class);

        private static final Type[] NO_TYPES = new Type[0];

        @Override
        public <T> T manufacturePojo(Class<T> pojoClass) {
                return this.manufacturePojo(pojoClass, NO_TYPES);
        }

        @Override
        public <T> T manufacturePojo(Class<T> pojoClass, Type... genericTypeArgs) {
                if (pojoClass.equals(InputStream.class)) {
                        byte[] buffer = new byte[256];
                        @SuppressWarnings("unchecked")
                        T result = (T) new ByteArrayInputStream(buffer);
                        return result;
                } else {
                        LOG.warn("Cannot instantiate {} with arguments {}. Returning null.",
                                        pojoClass, Arrays.toString(genericTypeArgs));
                        return null;
                }
        }

        @Override
        public DataProviderStrategy getStrategy() {
                return null;
        }
}

Set the external factory, when instantiation PODAM factory.

PodamFactory factory = new PodamFactoryImpl(externalFactory);
InputStream pojo = factory.manufacturePojo(InputStream.class);

InputStream class was given just for an example.