20 Feb 2011

ADF BC. Multiple LOVs for VO's attribute.

Everybody knows how to define LOV for view object's attribute. But what if depending on application's logic it's needed to get values for LOV from different sources or with completely different conditions. For suer, it's possible to define complex SQL query for LOV's VO and play with it's parameters. But most likely we'll get performance issue using this approach. And what we should do if it's required to use different display values for different use cases?

ADF BC allows us to define more than one LOV per attribute.
Let's say I have VO representing some form to input address information. It has attribute for country and attribute for region or state. If country is USA, user should select one of the US states, if country is India, user should select Indian state, in other cases user doesn't need any LOV and should input region manually.

I defined two LOVs for Regionstate attribute. Each of them retrieves values from its own data source  (US states and Indian states). In order to switch LOVs I defined new transient attribute StateLovSwitcher. The value of this attribute should contain proper LOV's name.



StateLovSwitcher's value is Groovy expression derived and defined as:


I'm going to provide user by two input controls for Regionstate field. For US and India user needs SelectOneChoice in other cases he needs InputText. I'm using af:switcher showing SelectOneChoice if  StateLovSwitcher has value and InputText if it doesn't.

  
    
      ...
    
  
    
        ...
    



And finally in order to get it working fine we need to clean region's value if country changes.

public void countryListener(ValueChangeEvent valueChangeEvent) {
   if (valueChangeEvent.getOldValue()!=valueChangeEvent.getNewValue())
          regionLOV.setValue(null);  
 }

You can download Jdev11.1.1.2.0 sample application for this post.

ADF BC. Adding validation rules to Entity object.

If you need to add validation rule to programmatically created Entity object or you just have to add some rules to the entity's attribute "on the fly",  you should use implementing classes of JboValidatorInterface. ADF Model's Java API has appropriate implementing class for each validation type. If you need something "special", you can create your own implementation of JboValidationInterface.
In this post I'm going to show a couple of examples of using JboValidator classes.

The following method creates validator to control the maximum length of some entity's attribute:
private JboLengthValidator getLengthValidator(Integer maxLength) {
        //Create validator    
        JboLengthValidator jlv = new JboLengthValidator();
        //Set operation type
        jlv.setOperType(jlv.LESSTHANEQUALTO);
        //Set the value to compare with
        jlv.setRhsValue(maxLength);
           
        //Refer to error message in resource bundle 
        jlv.setErrorMsgId(MAX_LENGTH_ERROR);
        //Populate parameter value for error message
        HashMap errvaluesMap = new HashMap();
        errvaluesMap.put("0", maxLength.toString());         
        jlv.setErrorMsgExpressions(errvaluesMap);
    
        return jlv;
    }

    //The key in your resource bundle properties file.
    // It contains error message like this:
    // maxlength_err=The maximum length is {0} characters 
    private static final String MAX_LENGTH_ERROR="maxlength_err"; 


The next validator is responsible for checking minimum value of some attribute:
private JboCompareValidator getMinValueValidator(Integer minValue) {
        JboCompareValidator jcv = 
            new JboCompareValidator(false,
                                    JboCompareValidator.GREATERTHANEQUALTO, 
                                    minValue);
        jcv.setErrorMsgId(MIN_VALUE_ERROR);
        HashMap errvaluesMap = new HashMap();
        errvaluesMap.put("0", minValue.toString());         
        jcv.setErrorMsgExpressions(errvaluesMap);        
        return jcv;
    }
    
    //minvalue_err=The minimum value is {0}
    private static final String MIN_VALUE_ERROR="minvalue_err";    

And at last the piece of code to add the validators to some attribute:
private void addValidators(AttributeDefImpl at, 
                               Integer maxLength, 
                               Integer minValue) {
        if (maxLength!=0) 
            at.addValidator(getLengthValidator(maxLength));  

        if (minValue!=0) 
            at.addValidator(getMinValueValidator(minValue));  
    }

That's it!

12 Feb 2011

ADF BC. Working with Oracle collection data types

Introduction
ADF BC allows us to use Oracle object types in quite easy way. If your table contains attribute of Oracle object type, JDeveloper creates corresponding domain class automatically. Or you can create it manually using Create Domain Wizard. After that you can work with the attribute as if it had regular type. But!  If you need to work with collection type (like ".. as table of ..."), it works in a little bit different way.


Let's do it
I've got in my database the following sql type definition:
create or replace type varchar2_array_type as table of varchar2(200)

And the following table:
create table testarray (
 SomeField Number,
 ArrValue VARCHAR2_ARRAY_TYPE)

nested table ArrValue store as arrvalue_tab return as value;

In JDeveloper I created entity object on this table and payed some attention to the definition of ArrValue attribute.  In JDev 11.1.1.2.0 you will get this:


Have a look at the type of ArrValue attribute. It is String!!! I don't know why.
In JDev 11.1.1.4.0 you will get this:



JDev 11.1.1.4.0 seems to be a bit smarter as for working with collection types. But never mind if you're using JDev 11.1.1.2.0 or earlier.  Magic button "Browse" will help. You can manually choose needed type - oracle.jbo.domain.Array.


It works. Trust me.
After creating default view object and dropping it from the data control palette to page as an ADF Form, you will get this:

The default DomainArrayConverter represents our array as a comma separated string. If you don't like the default behavior, it is possible to show array values in a table. Have a look at the following jspx source code:


   
      
   


And you will get this:


You can also implement your custom JSF converter for Array type. For example the following converter represents  Array as multi-row string:

public class StringArrayConverter implements Converter{
 public StringArrayConverter() {
     super();
 }
    
 private Array getArray(String value) {
     String[] splittArray = value.split("\n");
     Array arr = new Array(splittArray);
     return arr;
 }

 public Object getAsObject(FacesContext context, UIComponent component,
                           String newValue) throws ConverterException {
    if ((newValue == null) || "".equals(newValue))
         return null;
     return getArray(newValue);
  }


 public String getAsString(FacesContext context, UIComponent component,
                           Object value) throws ConverterException {
     if (value == null)  return null;
 
     Array array = (Array)value;
     if (array.getSize() <= 0) return null;
     Object[] data = array.getArray();        
     StringBuffer result = new StringBuffer();
        if (data.length > 0) {
            result.append(data[0]);
            for (int i=1; i < data.length; i++) {
                result.append("\n");
                result.append(data[i]);
            }
        }
    return result.toString();
 }
Create inputText using this converter

And you will get this:
For sure using oracle.jbo.domain.Array is not limited by arrays of varchar2 or number. It allows us to work with really complex data type structures.