Basic Customization > User Interface Customization > Customizing HTML Clients Using the Windchill JSP Framework > Adding Validation Logic for Actions and Properties
  
Adding Validation Logic for Actions and Properties
This section provides developers with the information required for implementing a validation delegate (aka “validator”) to perform validation on a specific action or UI component.
Objective
The UI Component Validation Service was created to give Windchill clients a central service to call to perform validation for actions and other components appearing in the Windchill UI. Calls to the service and interpretation of the results should be managed by many of the common components developed in release 9.0. The main responsibility for an application developer would be development of Validator classes that are called by the validation service to validate a specific action or UI component. This documentation outlines the process and best practices for authoring Validator classes.
Applicability
This documentation should be used by a developer who is responsible for authoring one or more Validators to determine whether or not an action or UI component is valid on a given page/context/etc. The documentation should walk through an example of each type of validation operation that a Validator could be called upon to perform.
Structure
All of the classes in the UI Validation Service (except where noted) are defined in the com.ptc.core.ui.validation package.
A Validator developer will not need to interact with all of the classes in this diagram, but many of them are applicable. The various classes will be discussed throughout this document, but to begin with, a developer writing a Validator should always define their Validator class to extend DefaultUIComponentValidator.
* 
It is also important to note that as requirements evolve, these classes may be updated. To get the latest set of methods and attributes defined in each of the classes see the Windchill Javadoc.
Participants
The readers of this section should have a general understanding of the Java programming language, and also some familiarity with the Windchill solution suite.
Collaborations
Validator developers need to collaborate with common component developers and other callers of the Validation Service. This collaboration is necessary to ensure that a caller of the service is passing all of the data to the service that a given Validator will need to perform validation. It is strongly recommended that Validator developers include a list of the data required in a given validation method in that method’s Javadoc. It is also helpful to include a list of Validation keys (action names) that the method is designed to account for. For example:
public class DefaultWIPValidator extends DefaultUIComponentValidator
{

/**
* This implementation of performLimitedPreValidation will check the checkout
* state of all the Workable objects in the ValidationCriteria's targetObjects
* WTCollection, and base its validation results on whether an object in the
* given state can have the specified action performed on it. (e.g., an object
* in the checked-in state can not have an undo checkout action performed on it)
*
* At a minimum, a caller of this method should provide the targetObjects
* WTCollection in the validationCriteria argument.
*
* The expected validationKey arguments for this method are:
* checkin
* checkout
* undocheckout
*
* <BR><BR><B>Supported API: </B>false
*
* @param validationKey The String identifying the action or component being validated.
* @param validationCriteria Object holding information required to perform validation tasks.
* @param locale The user's Locale. If a <i>null</i> value is passed in, the session locale will be used.
* @return UIValidationResultSet
**/
public UIValidationResultSet performLimitedPreValidation (String validationKey,
UIValidationCriteria validationCriteria, Locale locale)
throws WTException
{

}

}
Consequences
By using this documentation as a reference, developers should create consistent, performant Validators. A caller of the validation service should be confident that whatever action or UI component they are validating will be validated in a consistent manner.
Implementation
Overview
It is probably helpful to begin with a definition of the term validation. For the purposes of this discussion, the term validation refers to activities performed to determine what a user can see or do. For example:
Should we display the “Create Part” action?
Should we allow the checkout of this object?
Is everything the user entered in this create wizard OK?
For the purposes of our discussion, validation can be broken down into three broad categories:
Pre-Validation
Attempts to answer the questions: “Should something appear to the user in the UI? And, if so, should it be editable/selectable?”
For example, Should we display and enable the “Create Part” action for user A in container B?
Pre-Validation can be performed for actions or other UI components (status glyphs, attributes, tables, etc.)
Post-Select Validation
Attempts to answer the question: “Should the operation that was just selected in the UI be allowed to proceed?”
For example, Can we allow the checkout of parts A, B, and C?
Post-Submit Validation
Attempts to answer the question: “is the data the user just entered valid?”
For example, When the user clicks ‘Next’ in the “Create Part” wizard, are we going to let them go to the next step, or do they need to modify some data (e.g., name, number) in the current step?
The UI Component (Action) Validation Service exposes one or more APIs for each of the types of validation listed above.
From a high level, a common component or some other client invokes a validation API on the Validation Service, passing one or more validation keys (which can be thought of as an action name, like create, for instance) and a UIValidationCriteria bean, which contains data known by the client that is required to perform validation. The Validation Service uses the validation key(s) to perform a lookup and identify the Validator class(es) that should be called to carry out the validation activity. The service then passes the key and UIValidationCriteria on to the identified Validator(s) and awaits a (set of) result(s). When the service has the result(s) from the Validator(s), it (creates a bundle of results and) returns it to the client.
This documentation will concentrate on the authoring of Validator classes and methods.
Packaging/Modularization
All of the classes related to the UI Component Validation Service are packaged in com.ptc.core.ui.validation. Their source is located in the module \Windchill\src\com\ptc\core\ui\validation. It is strongly recommended that any time a developer is doing Validator development, they update all of the files in this directory and compile the latest versions of the Java classes.
Developers writing Validators should put their Validator classes in a package or module that is meaningful for the action(s)/UI component(s) that the Validator validates.
Authoring a Validator Class
Action and UI Component owners will be responsible for writing the Validators that validate those actions and UI components. To create a Validator, you’ll need to create a subclass of com.ptc.core.ui.validation.DefaultUIComponentValidator.java. There are currently five public methods defined in DefaultUIComponentValidator.java. Your subclass can override one to all of them:
performFullPreValidation()
performLimitedPreValidation()
validateFormSubmission()
validateSelectedAction()
validateSelectedMultiSelectAction()
For those methods which you do not override, the default behavior (always enable/permit) will be inherited from the DefaultUIComponentValidator class.
You will also need to create a properties entry to associate your Validator class with a particular validation key (action). The validation service uses these entries to find the right Validator for a given validation key (action). The entry will go in service.properties, or your application team’s service properties file (ask your group lead where you should put your entry), and should have this format:
wt.services/rsc/default/com.ptc.core.ui.UIComponentValidator/<validationKey>/
null/0=com.ptc.my.validators.MyValidator
Where <validationKey> is the validation key for your action/component and the right-side value is the fully-qualified class name of your Validator.
There are three types of checks you should never have to perform in your Validator implementations. These checks are performed by the validation service before the Validators are called. They include:
Role-based checking (visibility of actions based on input into the RBUI system, which is not to be confused with access control checking, which needs to be done in the Validators.)
Install-based checking (should an action or UI component be available given the set of solutions installed on a given system?)
Client-based checking (should an action or UI component be available in a given client, like DTI or PSB?)
Sample Code
Example Validator – DefaultWIPValidator.java
* 
This source code can be found at the following location: <Windchill>\src\com\ptc\windchill\enterprise\ wip\DefaultWIPValidator.java
/* bcwti
*
* Copyright (c) 2004 Parametric Technology Corporation (PTC). All Rights
* Reserved.
*
* This software is the confidential and proprietary information of PTC.
* You shall not disclose such confidential information and shall use it
* only in accordance with the terms of the license agreement.
*
* ecwti
*/

package com.ptc.windchill.enterprise.wip;

import com.ptc.core.ui.validation.*;

import java.lang.ClassNotFoundException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Locale;

import org.apache.log4j.Logger;

import wt.access.AccessPermission;
import wt.access.AccessControlHelper;
import wt.epm.workspaces.EPMWorkspaceHelper;
import wt.fc.Persistable;
import wt.fc.ReferenceFactory;
import wt.fc.WTReference;
import wt.fc.collections.WTArrayList;
import wt.fc.collections.WTCollection;
import wt.folder.CabinetBased;
import wt.folder.CabinetMember;
import wt.folder.Foldered;
import wt.folder.FolderHelper;
import wt.inf.container.WTContained;
import wt.inf.container.WTContainerHelper;
import wt.log4j.LogR;
import wt.org.WTUser;
import wt.sandbox.SandboxHelper;
import wt.session.SessionHelper;
import wt.util.WTException;
import wt.vc.Iterated;
import wt.vc.VersionControlHelper;
import wt.vc.wip.Workable;
import wt.vc.wip.WorkInProgressHelper;
import wt.vc.wip.WorkInProgressState;

public class DefaultWIPValidator extends DefaultUIComponentValidator
{
private Logger
logger = LogR.getLogger("wt.method.server.httpgw");
private ReferenceFactory refFactory = null;

private static final String FULL = "full";
private static final String LIMITED = "limited";
private static final String SELECTED = "selected";

/**
* This implementation of performLimitedPreValidation will
* check the checkout state of all the Workable objects
* in the ValidationCriteria's targetObjects
* WTCollection, and base its validation results on whether
* an object in the given state can have the specified
* action performed on it. (e.g., an object
* in the checked-in state can not have an undo checkout
* action performed on it)
*
* At a minimum, a caller of this method should provide the
* targetObjects
* WTCollection in the validationCriteria argument.
*
* The expected validationKey arguments for this method are:
* checkin
* checkout
* undocheckout
*
* <BR><BR><B>Supported API: </B>false
*
* @param validationKey The String identifying the action
* or component being validated.
* @param validationCriteria Object holding information required
* to perform validation tasks.
* @param locale The user's Locale. If a null value is passed in,
* the session locale will be used.
* @return UIValidationResultSet
**/
public UIValidationResultSet performLimitedPreValidation (String valida
tionKey,
UIValidationCriteria validationCriteria, Locale loc
ale)
throws WTException
{
logger.debug("ENTERING DefaultWIPValidator.performLimitedPreValidati
on");
logger.trace(" validtionKey -> " + validationKey);
logger.trace(" validationCriteria -> " + validationCriteria.toStrin
g());

UIValidationResultSet resultSet = performWIPValidation(validationKey,
validationCriteria, locale, LIMITED);

logger.trace("RETURNING " + resultSet.toString());
logger.debug("EXITING DefaultWIPValidator.performLimitedPreValidat
ion");
return resultSet;
}


public UIValidationResultSet performFullPreValidation (String validation
Key,
UIValidationCriteria validationCriteria, Locale loca
le)
throws WTException
{
logger.debug("ENTERING DefaultWIPValidator.performFullPreValidation");
logger.trace(" validtionKey -> " + validationKey);
logger.trace(" validationCriteria -> " + validationCriteria.toString(
));

UIValidationResultSet resultSet = performWIPValidation(validationKey,
validationCriteria, locale, FULL);

logger.trace("RETURNING " + resultSet.toString());
logger.debug("EXITING DefaultWIPValidator.performFullPreValidation");
return resultSet;
}

public UIValidationResult validateSelectedAction (String validation
Key,
UIValidationCriteria validationCriteria, Locale locale)
throws WTException
{
logger.debug("ENTERING DefaultWIPValidator.validateSelectedAction");
logger.trace(" validtionKey -> " + validationKey);
logger.trace(" validationCriteria -> " + validationCriteria.toString(
));

UIValidationResult result = null;
WTReference wtRef = validationCriteria.getContextObject();
Persistable persistable = wtRef.getObject();

if (!(persistable instanceof Workable)){
return new UIValidationResult(validationKey, wtRef, UIValidat
ion
Status.DENIED, null);
}

Workable workable = (Workable)persistable;

if (validationKey.equalsIgnoreCase("checkin") || validationKey.equals
IgnoreCase("undocheckout")){
result = performCheckinValidation(validationKey, workable, SELE
CTED,
(WTUser)(validationCriteria.getUser().getPrincipal()));
}
else if (validationKey.equalsIgnoreCase("checkout")){
result = performCheckoutValidation(validationKey, workable, SELE
CTED);
}

logger.trace("RETURNING " + result.toString());
logger.debug("EXITING DefaultWIPValidator.validateSelectedAction");
return result;
}


public UIValidationResultSet validateSelectedMultiSelectAction
(String validationKey,
UIValidationCriteria validationCriteria, Locale locale)
throws WTException
{
logger.debug("ENTERING DefaultWIPValidator.
validateSelectedMultiSelectAction");
logger.trace(" validtionKey ->
" + validationKey);
logger.trace(" validationCriteria ->
" + validationCriteria.toString());

UIValidationResultSet resultSet =
performWIPValidation(validationKey,
validationCriteria, locale, SELECTED);

logger.trace("RETURNING " + resultSet.toString());
logger.debug("EXITING DefaultWIPValidator.validateSelectedMultiSelect
Action");
return resultSet;
}

// ***NOTE:
// There is no post-submit validation for the WIP actions
// (checkin, checkout, undocheckout), since there is
// no wizard launched when one of the actions
// is performed. Therefore, there is no need to define a
// validateFormSubmission method in this class.
//
// public UIValidationResult validateFormSubmission (String validationKey,
// UIValidaitonCriteria validationCriteria, Locale locale)


private UIValidationResultSet performWIPValidation(String validationKey,
UIValidationCriteria validationCriteria, Locale locale, String
validationType)
throws WTException
{
UIValidationResultSet resultSet = new UIValidationResultSet();
WTCollection targetObjects = new WTArrayList(validationCriteria.get
TargetObjects());
Iterator workableIter = getWorkableIterator(targetObjects);
Workable workable = null;

while (workableIter.hasNext()){
workable = (Workable)workableIter.next();
if (validationKey.equalsIgnoreCase("checkin") || validationKey.equal
sIgnoreCase("undocheckout")){
resultSet.addResult(performCheckinValidation(validationKey, worka
ble, validationType,
(WTUser)(validationCriteria.getUser().getPrincipal())));
}
else if (validationKey.equalsIgnoreCase("checkout")){
resultSet.addResult(performCheckoutValidation(validationKey, worka
ble, validationType));
}
}
resultSet.appendResults(processNonWorkables(targetObjects, validationKey,
validationType));
return resultSet;
}


private UIValidationResult performCheckinValidation(String validationKey,
Workable workable,
String validationType, WTUser user)
throws WTException
{
WTReference wtRef = getWTReference(workable);

if (validationType.equals(LIMITED)){
WorkInProgressState state = workable.getCheckoutInfo().getState();
if (state.equals(WorkInProgressState.CHECKED_OUT) || state.equals(
WorkInProgressState.
CHECKED_OUT_TO_SANDBOX)){
return new UIValidationResult(validationKey, wtRef, UIValidation
Status.ENABLED, null);
}
else{
return new UIValidationResult(validationKey, wtRef, UIValidation
Status.DISABLED, null);
}
}

else if (validationType.equals(FULL) || validationType.equals(SELECTED)){
UIValidationStatus goodStatus = null;
UIValidationStatus badStatus = null;

if (validationType.equals(FULL)){
goodStatus = UIValidationStatus.ENABLED;
badStatus = UIValidationStatus.DISABLED;
}
else{
goodStatus = UIValidationStatus.PERMITTED;
badStatus = UIValidationStatus.DENIED;
}

if (workable instanceof CabinetBased){
CabinetBased orig;
if (WorkInProgressHelper.isWorkingCopy(workable)) {
orig = (CabinetBased)WorkInProgressHelper.service.originalCopyOf
(workable);
}
else {
orig = (CabinetBased)workable;
}

if (isNewInWorkspace(orig)){
return new UIValidationResult(validationKey, wtRef, badStatus,
null);
}
}
if (WorkInProgressHelper.isCheckedOut(workable, user) ||
(WorkInProgressHelper.isCheckedOut(workable) &&
WTContainerHelper.service.isAdministrator(((WTContained)workable).
getContainerReference(), user))){
return new UIValidationResult(validationKey, wtRef, goodStatus,
null);
}
else{
return new UIValidationResult(validationKey, wtRef, badStatus,
null);
}
}
return new UIValidationResult(validationKey, wtRef, UIValidationStatus.
ENABLED, null);
}



private UIValidationResult performCheckoutValidation(String validationKey,
Workable workable,
String validationType)
throws WTException
{
WTReference wtRef = getWTReference(workable);

if (validationType.equals(LIMITED)){
WorkInProgressState state = workable.getCheckoutInfo().getState();
if (state.equals(WorkInProgressState.CHECKED_OUT) || state.equals(
WorkInProgress
State.CHECKED_OUT_TO_SANDBOX)){
return new UIValidationResult(validationKey, wtRef, UIValidation
Status.DISABLED,null);
}
else{
return new UIValidationResult(validationKey, wtRef, UIValidation
Status.ENABLED, null);
}
}

else if (validationType.equals(FULL) || validationType.equals(SELECTED)){
UIValidationStatus goodStatus = null;
UIValidationStatus badStatus = null;

if (validationType.equals(FULL)){
goodStatus = UIValidationStatus.ENABLED;
badStatus = UIValidationStatus.DISABLED;
}
else{
goodStatus = UIValidationStatus.PERMITTED;
badStatus = UIValidationStatus.DENIED;
}

if (isNewInWorkspace(workable)){
return new UIValidationResult(validationKey, wtRef, badStatus,
null);
}

if ((AccessControlHelper.manager.hasAccess(workable, AccessPermis
sion.MODIFY)) &&
(!WorkInProgressHelper.isCheckedOut(workable) &&
(VersionControlHelper.isLatestIteration((Iterated)workable)) &&
(!SandboxHelper.isCheckedOutToSandbox(workable)))){
return new UIValidationResult(validationKey, wtRef, goodStatus,
null);
}
else{
return new UIValidationResult(validationKey, wtRef, badStatus,
null);
}
}
return new UIValidationResult(validationKey, wtRef, UIValidationStat
us.ENABLED, null);
}


private Iterator getWorkableIterator(WTCollection targetObjects)
{
try{
return targetObjects.persistableIterator(Class.forName("wt.vc.wip.
Workable"), true);
}
catch(Exception e){
return (new ArrayList(0)).iterator();
}
}


private WTReference getWTReference(Workable workable)
{
if (refFactory == null){
refFactory = new ReferenceFactory();
}

try{
return refFactory.getReference(workable);
}
catch(WTException wte){
return null;
}
}


private static boolean isNewInWorkspace(Object object)
throws WTException
{
if(object instanceof Foldered || object instanceof CabinetMember) {
WTArrayList objArray = new WTArrayList();
objArray.add(object);
if(FolderHelper.service.getObjsInPersonalCabinets(objArray).size()
> 0) {
if(EPMWorkspaceHelper.manager.getNewObjects(objArray).size() >
0) {
return true;
}
}
}
return false;
}


private UIValidationResultSet processNonWorkables(WTCollection
targetObjects, String validationKey, String validationType)
{
UIValidationResultSet resultSet = new UIValidationResultSet();
UIValidationStatus status = null;

if (validationType.equals(SELECTED))
status = UIValidationStatus.DENIED;
else
status = UIValidationStatus.HIDDEN;

try{
targetObjects.removeAll(Class.forName("wt.vc.wip.Workable"), true);
}
catch(ClassNotFoundException cnfe){
// do nothing
}
Iterator nonWorkableIter = targetObjects.referenceIterator();
WTReference wtRef = null;
while(nonWorkableIter.hasNext()){
wtRef = (WTReference)nonWorkableIter.next();
resultSet.addResult(new UIValidationResult(validationKey, wtRef, status,
null));
}

return resultSet;
}
}
Known Uses
This documentation should be used by any developer responsible for writing a Validator for an action or UI component.