Defining a new tax identification number data type

The following examples illustrates the steps involved in defining a new data type and using it. The example defines a new data type for Tax Identification Number objects, called TaxID. The data type has one required property, the name of the property on the context object. This property, countryProperty, identifies which country is in context for validating the data.

This example contains the following steps:

Step 1: Register the data type

About this task

To register a new data type, create a file named XXX.dti, with XXX as the name of the new data type. In this case, create a file named TaxID.dti. To do this:

Procedure

  1. In Guidewire Studio, in the Project tool window, navigate to configuration > config > datatypes.
  2. Right-click datatypes, and then click New > File.
  3. Enter TaxID.dti as the file name. This action creates an empty data type file and places it in the datatypes folder.
  4. Enter the following text in the file:
    <?xml version="1.0"?>
    <DataTypeDef xmlns="http://guidewire.com/datatype" type="gw.newdatatypes.TaxIDDataTypeDef"
            valueType="java.lang.String">
      <ParameterDef name="countryProperty" desc="The name of a property on the owning entity, 
              whose value contains the country with which to validate and format values."
              required="true" type="java.lang.String"/>
    </DataTypeDef>

    The root element of TaxID.dti is <DataTypeDef> and the namespace is http://guidewire.com/datatype.

    This example defines the following:

    data type name

    TaxID

    value type

    String

    parameter

    contactType

    implementation type

    gw.newdatatypes.TaxIDDataTypeDef

What to do next

After completing this task, complete Step 2: Implement the IDataTypeDef Interface.

See also

Step 2: Implement the IDataTypeDef Interface

Before you begin

Before beginning this task, complete Step 1: Register the data type.

About this task

The implementation class that you create to handle the TaxID data type must do the following:

  • It must implement the gw.datatype.def.IDataTypeDef interface.
  • It must have a no-argument constructor.
  • It must have a property for each of the data type parameters.

For example, suppose that you have a new data type that has a String parameter named someParameter. The implementation class (specified in the type attribute) must define a writable property named someParameter, so that the data type factory can pass the argument values to the implementation. The implementation can then use the parameters in the implementation of the various handlers, which are:

  • gw.datatype.handler.IDataTypeConstraintsHandler
  • gw.datatype.handler.IDataTypePersistenceHandler
  • gw.datatype.handler.IDataTypePresentationHandler

Class TaxIDDataTypeDef

For our example data type, the gw.newdatatypes.TaxIDDataTypeDef class looks similar to the following. To create this file, first create the package, then the class file, in the Studio Classes folder.

package gw.newdatatypes
 
uses gw.datatype.def.IDataTypeDef
uses gw.datatype.handler.IDataTypeConstraintsHandler
uses gw.datatype.handler.IDataTypePresentationHandler
uses gw.datatype.handler.IDataTypePersistenceHandler
uses gw.lang.reflect.IPropertyInfo
uses gw.datatype.handler.IDataTypeValueHandler
uses gw.datatype.def.IDataTypeDefValidationErrors
uses gw.datatype.impl.VarcharPersistenceHandler
uses gw.datatype.impl.SimpleValueHandler
 
class TaxIDDataTypeDef implements IDataTypeDef {
 
  private var _countryProperty : String as CountryProperty
 
  override property get ConstraintsHandler() : IDataTypeConstraintsHandler {
    return new TaxIDConstraintsHandler(CountryProperty)
  }
 
  override property get PersistenceHandler() : IDataTypePersistenceHandler {
    return new VarcharPersistenceHandler(/* encrypted */      false, 
                                         /* trimWhitespace */ true, 
                                         /* size */           30)
  }
 
  override property get PresentationHandler() : IDataTypePresentationHandler {
    return new TaxIDPresentationHandler(CountryProperty)
  }
 
  override property get ValueHandler() : IDataTypeValueHandler {
    return new SimpleValueHandler(String)
  }
 
  override function validate(prop : IPropertyInfo, errors : IDataTypeDefValidationErrors) {

    // Check that the CountryProperty names an actual property on the owning type, and that
    // the type of the property is typekey.Country.
    var countryProp = prop.OwnersType.TypeInfo.getProperty(CountryProperty)

    if (countryProp == null) {

      errors.addError("Property \"" + CountryProperty + "\" does not exist on type " +
             prop.OwnersType)

    } else if (not typekey.Country.Type.isAssignableFrom(countryProp.Type)) {

      errors.addError("Property " + countryProp + " does not resolve to a " + typekey.Country)

    }
  }
}

Note that the class defines a property named CountryProperty, which the system calls to pass the countryProperty parameter. Also notice how the implementation reads the value of CountryProperty as its constructs its constraints and presentation handlers. Guidewire guarantees to fill the implementation parameters before calling the handlers.

In the example code, the class refers to constraints and presentation handlers created specifically for this data type. However, it also reuses a Guidewire-provided persistence handler, the VarcharPersistenceHandler. You do not usually need to create your own persistence handler, as Guidewire defines persistence handlers for all the basic database column types.

What to do next

After completing this task, complete Step 3: Implement the data type aspect handlers.

Step 3: Implement the data type aspect handlers

Before you begin

Before beginning this task, complete Step 2: Implement the IDataTypeDef Interface.

About this task

As you define a new data type, it is possible (actually likely) that you need to define one or more handlers for the data type. These handler interfaces are different than the Data Type API interfaces. For example, clients that use the Data Type API use the following:

gw.datatype.IConstrainedDataType

However, if you define a new data type, you must implement the following:

gw.datatype.handler.IDataTypeConstraintsHandler

This separation of interfaces allows the definition of a caller-friendly interface for data type clients and a implementation-friendly interface for data type designers.

The example data type defines a handler for both constraints and presentation.

Class TaxIDConstraintsHandler

This class looks similar to the following:

package gw.newdatatypes
 
uses gw.datatype.handler.IStringConstraintsHandler
uses gw.lang.reflect.IPropertyInfo
uses java.lang.Iterable
uses java.lang.Integer
uses java.lang.CharSequence
uses gw.datatype.DataTypeException
 
class TaxIDConstraintsHandler implements IStringConstraintsHandler {
 
  var _countryProperty : String
 
  construct(countryProperty : String) {
    _countryProperty = countryProperty
  }
 
  override function validateValue(ctx : Object, prop : IPropertyInfo, value : Object) {
    var country = getCountry(ctx)

    switch (country) {
      case "US": validateUSTaxID(ctx, prop, value as java.lang.String)
                 break
      // other countries ... 
    }

  }
 
  override function validateUserInput(ctx : Object, prop : IPropertyInfo, strValue : String) {
    validateValue(ctx, prop, strValue)
  }
 
  override function getConsistencyCheckerPredicates(columnName : String) : Iterable<CharSequence> {
    return {}
  }
 
  override function getLoaderValidationPredicates(columnName : String) : Iterable<CharSequence> {
    return {}
  }
 
  override function getLength(ctx : Object, prop : IPropertyInfo) : Integer {
    var country = getCountry(ctx)

    switch (country) {
      case "US": return ctx typeis Person ? 11 : 10
      // other countries ... 
    }

    return null

  }
 
  private function getCountry(ctx : Object) : Country {
    return ctx[_countryProperty] as Country
  }
 
  private function validateUSTaxID(ctx : Object, prop : IPropertyInfo, value : String) {
    var pattern = ctx typeis Person ? "\\d{3}-\\d{2}-\\d{4}" : "\\d{2}-\\d{7}"
    if (not value.matches(pattern)) {
      throw new DataTypeException("${value} does not match required pattern ${pattern}", prop,
              "Validation.TaxID", { value })
    }
  }

}

Class TaxIDPresentationHandler

This class looks similar to the following:

package gw.newdatatypes
 
uses gw.lang.reflect.IPropertyInfo
uses gw.datatype.handler.IStringPresentationHandler
 
class TaxIDPresentationHandler implements IStringPresentationHandler {
 
  private var _countryProperty : String
 
  construct(countryProperty : String) {
    _countryProperty = countryProperty
  }
 
  function getEditorValue(ctx : Object, prop : IPropertyInfo) : Object {
    return null
  }
 
  override function getDisplayFormat(ctx : Object, prop : IPropertyInfo ) : String {
    return null
  }
 
  override function getInputMask(ctx : Object, prop : IPropertyInfo) : String {

    switch (getCountry(ctx)) {
      case "US": return ctx typeis Person ? "###-##-####" : "##-#######"
      // other countries ...
    }

    return null

  }
 
  override function getPlaceholderChar(ctx : Object, prop : IPropertyInfo) : String {
    return null
  }
 
  private function getCountry(ctx : Object) : Country {
    return ctx[_countryProperty] as Country
  }

}

Notice how each of these handlers makes use of the context object in order to determine the type of input mask and validation string to use.