com.abra.j2xb.annotations
Annotation Type MOConstructionDescriptor


@Target(value={METHOD,TYPE})
@Retention(value=RUNTIME)
public @interface MOConstructionDescriptor

Defines the methods to construct a bean when reading an XML document. The MOConstructionDescriptor allows to define what contructor to use or factory method to use, and what parameters to pass to the constructor or factory method.

The annotation can appear on a top level bean (annotation with MOPersistentBean) or on a property which contains a bean or a collection of beans. If the annotation appears both on the bean and on a property, the definition on the property takes presedence.

Example 1: The Trivial Case

Consider a mapped class with a simple constructor. We define the constructor descriptor to indicate that the property 'name' is to be supported via the constructor.

note that there is no setter method for the 'name' property - it is not required anymore because the framework sets the property via the constructor.

 @MOPersistentBean(xmlName = "HelloWorld")
 @MOXmlNamespace(value = "http://example.abra.com/helloWorld", preferedPrefix = "ex1")
 @MOConstructionDescriptor(constructorArgs =
     { @MOConstructorArg(sourceProperty = "name", sourcePropertyOf = SourcePropertyOf.constructedInstance)})
 public class HelloWorld {

   private String name;

   public HelloWorld(String name) {
     this.name = name;
   }

   @MOProperty()
   public String getName() {
     return name;
   }

 }
 

Example 2: Source Property - Parent

In a parent child scenario, we may require the same attribute at both the parent and child level because of different applicative reasons (for instance, the Order class identity may require the customerName attribute of the owner customer).

when mapping those classes to XML, because the order XML element is written as a child of the customer XML element, there is no need to write the customerName again under the order XML element. Instead, one creates a constructor for the Order class which accepts the customerName as a parameter and maps it to a parent attribute using the annotation MOConstructorArg(sourceProperty = "customerName", sourcePropertyOf = SourcePropertyOf.parent)


 @MOPersistentBean(xmlName = "order")
 @MOXmlNamespaceRef(NodeTypeWithFactory.class)
 public class Order {
   private String customerNameOfOrder;
   private String orderDescription;
   private int orderID;

   public Order(String customerNameOfOrder, int orderID) {
     this.customerNameOfOrder = customerNameOfOrder;
     this.orderID = orderID;
   }

   order() {}

   public String getCustomerNameOfOrder() {
     return customerNameOfOrder;
   }

   @MOProperty
   public String getOrderDescription() {
     return orderDescription;
   }
   public void setOrderDescription(String orderDescription) {
     this.orderDescription = orderDescription;
   }

   @MOProperty
   public int getOrderID() {
     return orderID;
   }
 }

 @MOPersistentBean(xmlName = "customer")
 @MOXmlNamespaceRef(NodeTypeWithFactory.class)
 public class Customer {

   private String customerName;
   private int id;
   private List<Order> orders = new ArrayList<Order>();

   @MOProperty
   public String getCustomerName() {
     return customerName;
   }
   public void setCustomerName(String customerName) {
     this.customerName = customerName;
   }

   @MOProperty
   public int getId() {
     return id;
   }
   public void setId(int id) {
     this.id = id;
   }

   @MOProperty
   @MOConstructionDescriptor(constructorArgs = {
           @MOConstructorArg(sourceProperty = "customerName", sourcePropertyOf = SourcePropertyOf.parent),
           @MOConstructorArg(sourceProperty = "orderID", sourcePropertyOf = SourcePropertyOf.constructedInstance)
                   })
   public List<Order> getOrders() {
     return orders;
   }
 }

 

Example 3: Static Factory

In this example we add a new factory class with a static method that creates the Order instances. The factory method again requires two parameters (the customerNameOfOrder and orderID parameters).

The factory class can be

 public class OrdersFactory {

   public static Order createOrder(String customerNameOfOrder, int orderID) {
     Order order = new Order();
                 order.setCustomerNameOfOrder(customerNameOfOrder);
     order.setOrderID(orderID);
     return order;
         }

 }
 
And the mapping of the list in the Customer class changes to
 @MOProperty
 @MOConstructionDescriptor(
         factoryClass = OrdersFactory.class,
         factoryMethod = "createOrder",
         constructorArgs = {
         @MOConstructorArg(sourceProperty = "customerName", sourcePropertyOf = SourcePropertyOf.parent),
         @MOConstructorArg(sourceProperty = "orderID", sourcePropertyOf = SourcePropertyOf.constructedInstance)
                })
 public List<Order> getOrders() {
         return orders;
 }
 

Example 4: Instance Factory Method

In this example we use a method of a one bean (the parent bean) to create instances of child beans that are stored in properties (single or collection) of the parent bean. This method of using MOConstructionDescriptor is only allowed when used on a property, it is not allowed when used for a bean.

The Parent Bean in this case is the Orders class and the child bean is the Order bean. The @MOConstructionDescriptor defines to use the method createOrder to create order instances, where the method is an instance method of the parent bean instance.

 public class Orders {

   @MOProperty
   @MOConstructionDescriptor(
         factoryMethod = "createOrder",
         constructorArgs = {
         @MOConstructorArg(sourceProperty = "orderID", sourcePropertyOf = SourcePropertyOf.constructedInstance)
                })
   public List<Order> getOrders() {
     return orders;
   }

   public Order createOrder(int orderID) {
     Order order = new Order();
     order.setCustomerNameOfOrder(this.getCustomerNameOfOrder);
     order.setOrderID(orderID);
     return order;
         }

 }
 

Example 5: Post Creation Initialization Properties

* Assume we modify again the the Order class. We change the constructor to accept only one parameter, the orderID. The parameter customerNameOfOrder we now want to initialize as a property, but we do not want to map it to XML at the Order class (because the Customer class already maps this value). This is done using the MOInitializerProperty annotation that maps a property from the parent class (Customer in the example) to a property of the child class (Order in the example). We change the Order class to be

 @MOPersistentBean(xmlName = "order")
 @MOXmlNamespaceRef(NodeTypeWithFactory.class)
 public class Order {
   private String customerNameOfOrder;
   private String orderDescription;
   private int orderID;

   public Order(int orderID) {
     this.orderID = orderID;
   }

   public void setCustomerNameOfOrder(String customerNameOfOrder) {
     this.customerNameOfOrder = customerNameOfOrder;
   }

   public String getCustomerNameOfOrder() {
     return customerNameOfOrder;
   }
   ...
 }
 
the mapping of the Customer class changes to be
 @MOProperty
 @MOConstructionDescriptor(
         constructorArgs = {
             @MOConstructorArg(sourceProperty = "orderID", sourcePropertyOf = SourcePropertyOf.constructedInstance)},
         InitializerProperties = {
             @MOInitializerProperty(sourceProperty = "customerName", targetProperty = "customerNameOfOrder")}
 )
 

Example 6: Using Factory to Create Inherited Beans

Assume we a Bean Hierarchy where we have the Shape bean, and two subclasses Circle and Square.

We map all three classes, and define another class named Drawing with a property which is a collection of Shape instances or a single property of type Shape. Substitution groups kick into play (for more details about mapping substitution groups see MOXmlGlobalBeanRef).

If we want to create the instances of the Shape subclasses using a factory, we face an interesting question - how does the factory know which subclass to instantiate? the obviuos answer is by the parameters passed to the factory method. The parameters can be read from the XML document, from the instantiated bean or the parent bean of the instantiated one (see the previous examples). However, the substitution groups mechanism includes one additional piece of information - the name of the child bean XML element, which is automatically mapped to the class of the bean expected to be instantiated.

To define that the factory method will accept this parameter - a parameter of type class which is the class of the instantiated bean, one has to be reminded that all beans in Java have a special read-only property named class. By defining a parameter named class, the factory will recieve the expected class to be instantiated.

The constructor descriptor is then

 @MOProperty
 @MOConstructionDescriptor(
         factoryClass = ShapesFactory.class,
         factoryMethod = "createShape",
         constructorArgs = {
         @MOConstructorArg(sourceProperty = "class", sourcePropertyOf = SourcePropertyOf.constructedInstance),
         @MOConstructorArg(sourceProperty = "size", sourcePropertyOf = SourcePropertyOf.constructedInstance)
                })
 public List<Shape> getShapes() {
         return shapes;
 }
 

Since:
JDK1.5
Version:
1.0, May 1, 2008
Author:
Yoav Abrahami
See Also:
MOConstructorArg, MOInitializerProperty, SourcePropertyOf

Optional Element Summary
 MOConstructorArg[] constructorArgs
          defines what parameter values to use when calling the constructor / factory.
 java.lang.Class factoryClass
          the factory class. if not specified, the annotated class is assumed to be the factory class
 java.lang.String factoryMethod
          the factory method name.
 MOInitializerProperty[] InitializerProperties
          defines what properties of the bean to initialize and from what source right after creating an instance of the bean.
 

constructorArgs

public abstract MOConstructorArg[] constructorArgs
defines what parameter values to use when calling the constructor / factory.

Default:
{}

InitializerProperties

public abstract MOInitializerProperty[] InitializerProperties
defines what properties of the bean to initialize and from what source right after creating an instance of the bean.

Default:
{}

factoryClass

public abstract java.lang.Class factoryClass
the factory class. if not specified, the annotated class is assumed to be the factory class

Default:
java.lang.Object.class

factoryMethod

public abstract java.lang.String factoryMethod
the factory method name. This method should be a static method, returning an instance of the annotated class (or a derived class). The factory method parameter values are defined using the constructorArgs() member.

Default:
""


Copyright © 2008. All Rights Reserved.