Tuesday 19 June 2007

I rest my case: Converting ADF BC EO/VO attributes to upper and lower case with custom properties

Oracle Forms at the item level has a property for ensuring string data entered is forced to uppercase or lowercase, or leaving the case unchanged. At this time in JDev 10.1.3.x there is no out of the box facility to do this. However there are a number of methods that can be employed to implement this feature. In this post we'll specifically look at using custom properties on the ADF Business Component (ADF BC) Entity Object (EO) attributes to flag to a custom method to convert the data to upper or lower case as required, or leaving the case in its original state if not.

The goal of this post is 3 fold: firstly a technique to solve the problem at hand, but secondly to show why custom properties are useful, and thirdly what a custom ADF BC framework can allow you to do.

What is a custom property?

JDeveloper's ADF BC Entity Objects and View Objects are comprised of 1 or more attributes representing the columns of data stored within the objects. For each attribute under the associated editor, on selecting a specific attribute, 3 tabs show: Entity Attributes, Custom Properties, and Control Hints.

The Entity Attributes tab shows a number of predefined properties against the attribute that the developer may set and change at design time that are used at runtime to determine the behaviour of the attributes. However sometimes we may wish to extend the default properties for the attributes to store other information and create custom behaviour. For example we could create a custom property for something as simple as a description of the attribute, or alternatively a custom property that your own code can make use of at runtime to flag forcing the data to upper or lowercase.

The JDev team has therefore included custom properties to allow extensibility in the ADF BC framework.

(For the record, custom properties in addition to being defined at the EO/VO attribute level, can also be defined at the EO and VO parent level, as well as for Application Modules (AM), allowing extensibility across the full range of ADF BC object types.)

Defining a custom property

Returning to our problem, within an EO we may have a certain attribute, that is a String, that we want to force to uppercase or lowercase regardless of what case the user supplies the data in. The first step in solving this problem is we create a custom property on an EO attribute in our ADF BC stack, called for example CASE, with a value such as U for Uppercase or L for Lowercase.

For example in the following picture, it demonstrates for the Description attribute of the Events EO, I have defined a custom property CASE with value L implying the Description attribute should be converted to lowercase once entered by the user.


Creating a custom ADF BC framework

The next step in the solution is to write some code that will capture an insert/update of any String attribute in our ADF BC EOs. On capturing this event our code will check if the custom property CASE exists for the attribute, and will convert the submitted String value to uppercase or lowercase dependent on the custom property's value (U or L).

In order to capture the insert/update of all attributes in ADF BC, we need to override the oracle.jbo.server.EntityImpl.setInternalAttribute() method. Section "25.2 Creating a Layer of Framework Extensions" of the Oracle Application Development Framework Developer's Guide for Forms 4GL/ Developers shows how to create a custom ADF BC framework that overrides all classes. Implementing the ADF BC custom framework as described in section 25.2 is considered good practice, though in this specific blog post we're only interested in providing a custom EntityImpl implementation where we can override the setInternalAttribute() method.

The end result is you will end up with a custom EntityImpl class that looks something like this:

public class EntityImpl extends oracle.jbo.server.EntityImpl {
  protected void setAttributeInternal(int i, Object object) {
    super.setAttributeInternal(i, object);

  }
}


Note I've added the setAttributeInternal() method by selecting the Source menu and Override Methods sub-option with the above code showing, then selecting the setAttributeInternal(int i, Object object) method out of the list.


If you've already created a number of ADF BC objects and associated classes, you'll need to change the existing classes to extend your new custom classes rather than the default JDev ones. eg:

public class EventsImpl extends model.common.EntityImpl


Overriding the setAttributeInternal method

The final part of our solution is to override the setAttributeInternal() method as follows:

protected void setAttributeInternal(int i, Object object) {

  AttributeDef attrDef = getEntityDef().getAttributeDef(i);

  if (attrDef != null &&
      attrDef.getJavaType().getName().equals("java.lang.String")) {
    String caseProperty =
      getEntityDef().getAttributeDef(i).getProperty("CASE").toString();

    if (caseProperty != null && !caseProperty.equals("")) {
      String value = (String)object;
      if (caseProperty.equals("U")) {
        value = value.toUpperCase();
      } else if (caseProperty.equals("L")) {
        value = value.toLowerCase();
      } else {
        // Do nothing; unknown value
      }
      object = value;
    }
  }
  super.setAttributeInternal(i, object);
}

The new method:
  1. Retrieves the definition for the current attribute which we need to retrieve the attribute datatype and custom property
  2. Checks if the attribute is based on java.lang.String
  3. Retrieves the CASE property from the attribute definition if it exists
  4. If the CASE value is U converts the settable value via a call to toUpperCase()
  5. If the CASE value is L converts the settable value via a call to toLowerCase()
  6. If the custom property CASE isn't present, the attribute isn't a String, or the CASE value isn't U or L, does not modify the settable value
  7. Calls the super setAttributeInternal() method to do the rest of the work.
Note how the custom ADF BC framework only requires us to define this code once, overriding and extended the default ADF BC facilities, rather than coding this facility across all our EOs.

Alternative methods for case conversion

As indicated in the header of this post, this is not the only method for achieving this. If you're using ADF Faces for your UI, you can utilise CSS styles or Javascript to force the data to uppercase. Of course you could also achieve this in the database by converting values to upper/lowercase via db triggers.

As normal in computing, there are a number of different ways to skin a cat, and which is best for you depends on your circumstances.

3 comments:

Anonymous said...

Excellent article. This was almost exactly what I was looking to do. The only difference I've added is a value of 'M' to force mixed case, since in my project attributes are set to upper case automatically.

Athanassios Bakalidis said...

An excellent article indeed. This technique is going to be very useful in case when we interface ADF with SAP, where many attributes like material and customer numbers come right padded with zeros and also have to stored in the DB the same way.

Thank you Chris

HusainD said...

Great Article !
It has opened up a whole new set of possibilities.
Thanks !!