# Creating Attribute Editors and Validators

You can write components that provide a user interface for editing your control's property attributes, and for validating new attribute values. This topic provides an introduction to building components that expose property attribute values for editing and that validate the new values. For a sample control that implements these features, see the CustomerData control. You'll find this sample in the propEditor package of the ControlFeatures project in the ControlDevKit sample application.

This topic assumes some familiarity with properties and their definitions in an annotation XML file. For an introduction to control properties, see Control Properties Overview.

## Validating Properties

You can provide validation at both the property annotation level and for individual attributes for which you have defined a custom type. Providing a validator can be handy when a property requires a specific combination of attribute values. It's also useful when an attribute's value is complex, perhaps even having its own syntax (as with an SQL expression for a Database control method).

WebLogic Workshop provides validation interfaces that you can implement for validating properties and their attributes.

- You implement the ValidateAttribute interface to provide validation for a custom attribute type. Your implementation is used to validate each attribute defined to use the custom type.

  By default, WebLogic Workshop provides validation for all of its predefined attribute types. For example, a control's user may not enter "true" as the value for an attribute whose type is "decimal". For attribute types you define, you implement ValidateAttribute to provide logic for validation.

- You implement the ValidateTag interface to validate a property annotation as a whole. Having an implementation scoped to the annotation is useful when the property exposes multiple attributes and the property isn't valid unless attributes are valid together. For example, you might have a property with "user-name", "password" and "server-name" attributes. If your control's code requires all three to authenticate someone, then the property might be invalid if one of the three attributes is missing.

### Implementing a Validation Interface

The two interfaces are very similar. They both define two methods whose names are the same, but whose parameters differ for obvious reasons; a ValidateTag implementation is designed to receive a complete tag, with potentially many attributes; a ValidateAttribute implementation receives a single attribute. In general, your implementation of either method should validate the incoming values, then return null if they're valid. If they're not valid, your implementation should return an array of objects that implement the Issue interface. Your implementation of Issue provides messages that the IDE can present to the user.

The two methods defined in these interfaces reflect the fact that the IDE is designed to validate at two stages: compile-time and edit-time.

- validateDuringCompile — Compile-time validation isn't restricted to validation when the control's user chooses to build code that includes the property. Compilation for the purpose of validation actually occurs quite frequently. It's more useful to think of compile-time validation as "Source View" validation — the kind of validation that results in red squiggles for invalid values.
- validateDuringEdit — Edit-time validation is performed when the user edits a property through the Property Editor.

While it's undoubtedly more convenient to implement both compile-time and edit-time validation in the same class, it's not necessary. A class for compile-time validation and one for edit-time validation are invoked at in different ways by the IDE, as described below.

### Connecting a Validator to the IDE

How you make your validator class available to the IDE differs depending on the circumstances under which validation is being done.

- You've written a custom attribute editor user interface and you want to validate the new value the user is attempting to set. In this case you call a method of your validator class yourself from within your attribute editor implementation. See later in this topic for more details.
- You want to validate the new value when the user edits the attribute's value in the Property Editor directly. You implement an editor support class so that return to the IDE your validator class for the EditorSupport.BEHAVIOR_ATTRIBUTE_VALIDATOR behavior. For more information on implementing editor support, see Controlling Appearance and Handling Actions in Design View. Note that an editor support class is only used for custom attribute types; others are validated by the IDE.
- You want to validate the property annotation code in Source View, or at build time. You specify your validator class in annotation XML file that defines the properties and their attributes. You can specify a class that implements ValidateTag for validating an entire property tag; the class name should be the value for validator-class, an attribute of the control-tag or method-tag element. You can also specify a class that implements ValidateAttribute for validating a custom attribute type; the class name should be the value for class-name, an attribute of the custom element. (Note that the IDE itself validates attributes that aren't custom types.)

## Building a Custom Editor

You can build a user interface that is designed specifically to edit the value of a particular property attribute. This can be useful when an attribute's value is potentially long (requiring more space than the Property Editor can provide), or requires some "builder" style editor to compose it. In fact, there are many reasons why you might want to create your own editor.

The Java control API provides two interfaces that define the methods required by a custom editor class. Your custom editor implements one of these to provide a means for the IDE to interact with your user interface, get the value set the by user, and so on.

Choosing an interface to implement is a matter of simplicity versus customization. AttributeEditorSimple, the simpler alternative, is the one to use when your user interface will consist of a single panel. If your user interface will be made up of multiple panels or dialogs (as with a wizard), you should implement AttributeEditorWizard.

#### Implementing AttributeEditorSimple

The AttributeEditorSimple interface is the basic approach, and will probably suit most needs. When you implement this interface, your user interface will be fit into a dialog owned by the IDE. Your user interface need not provide an OK or Cancel button because these are in the dialog into which your user interface is placed.

Note: The dialog created will be made as big as needed, up to the size of the IDE.

Here are the methods it exposes, roughly in the order they'll be called by the IDE.

- getFormatter — The IDE calls this method to retrieve your class extending AbstractFormatter; this is used to format the attribute value in the Property Editor. You can return null to indicate that no formatting is needed. The IDE's call to this method is not connected with your custom attribute editor (although, of course, you call it yourself).
- getEditorComponent — The IDE calls this to get the user interface component for the attribute editor. Extending the JPanel class is a good choice here, given that the component you return will be used in a dialog.
- onFinish — Called by the IDE when the user has clicked the OK button on the dialog containing your user interface. Your implementation of this method can validate the value (by calling code in a validator class if you have created one). Return null if its all right to update the value. Otherwise, return an array of classes that implement the Issue interface; these will bear messages that the IDE will present to the user.
- getNewAttributeValue — Called to retrieve the attribute's new value so that it can be written into the property annotation in source code, and so that it can be displayed in the Property Editor. The value you return from this method should be retrieved from your user interface code.

### Implementing AttributeEditorWizard

Implement the AttributeEditorWizard interface when you want to manage the dialog (or dialogs) containing your attribute editor user interface. This interface defines one method, getDialog, which is called by the IDE to get the Dialog class that defines your user interface. (Note that the getEditorComponent method is not called.) You should use the Frame object received as a parameter of getDialog when constructing your Dialog. This helps to ensure that your dialog is displayed properly with respect to the IDE. When the user clicks OK in your dialog, the IDE calls your implementation of the AttributeEditorSimple.onFinish() method.

### Connecting an Editor to the IDE

Once you have written the user interface for your attribute editor, you connect it to the IDE using your implementation of an editor support class. As described in Controlling Appearance and Handling Actions in Design View, an editor support class handles user actions such as double-clicking a method arrow in Design View or clicking the ellipses next to an attribute value in the Property Editor.

An editor support class provides specifics for several "behaviors" defined by the IDE, one of which corresponds to a user's request to edit an attribute value. The constant representing this behavior is EditorSupport.BEHAVIOR_ATTRIBUTE_EDITOR.

The following example describes how you might use an editor support class to connect your custom editor to the IDE.

## Putting It All Together

The code in the following example is based on the CustomerData sample control available with the Control Developers Kit. For the complete working version, see the ControlDevKit sample application; there, look for the propEditor folder of the ControlFeatures project.

### Validation Class Example

The basic set of tasks for a validation class is simple: receive the values to validate, validate them, and return messages to the user if validation fails (or null if it succeeds). The following code shows a simple attribute validation class.

    // Implement the ValidateAttribute interface.
    public class CustIdValidator implements ValidateAttribute
    {
        // Define legal values for the attribute.
        int legalValues[] = { 987654, 987655, 987658, 987659 };

        /*
         * Implement the Issue interface as a way to send messages back
         * to the IDE when the value is invalid. The IDE will extract
         * information from this implementation and present it to the user.
         */
        static class CustIdIssue implements Issue
        {
            String _message;
            private CustIdIssue(String message) { _message = message; }
            public boolean isError() { return true; }
            public String getDescription() { return _message; }
            public String getPrescription()
            {
                return "Provide one of the following values: " +
                 "987654, 987655, 987658, 987659. that's all";
            }
        }

        // Called by the attribute editor.
        public Issue[] validateId(String value)
        {
             return validateDuringCompile(null, value, null);
        }

        /*
         * Receive the attribute type name as defined in the annotation XML file. Also
         * receive the value that the user is trying to set for this attribute.
         * The context variable, unused in this example, would be used to store
         * context information that could accumulate and be inspected at validation time.
         */
        public Issue[] validateDuringCompile(String attributeType, String value, Map context)
        {
            // Initialize the Issue array for return to the IDE.
            Issue[] issues = new Issue[1];

            int val;
            try
            {
                val = Integer.parseInt(value);
            }
            catch(Exception e)
            {
                issues[0] = new CustIdIssue(e.getMessage());
                return issues;
            }

            // Find out if the value received is one of the allowed values.
            for(int i = 0; i < legalValues.length; i++)
            {
                // If the value is okay, return null: no issues here.
                if(legalValues[i] == val)
                {
                    return null;
                }
            }

            /* Otherwise, the value must be invalid. Put in instance of the
             * Issue interface implementation into the array and send it to
             * the IDE.
             */
            issues[0] = new CustIdIssue("Unsupported value error");
            return issues;
        }

        public Issue[] validateDuringEdit(String attributeType, String value)
        {
            return null;
        }
    }

### Custom Editor Example

The purpose of a custom editor is to provide a user interface for editing an attribute's value, and to provide a way for the IDE to get the UI and the edited value. This example defines both the user interface and the IDE hooks in the same class, but those could easily have been in separate classes.

    package propEditor.ide;

    import java.util.StringTokenizer;
    import java.awt.BorderLayout;
    import java.awt.Component;
    import javax.swing.JTextField;
    import javax.swing.JLabel;
    import javax.swing.JFormattedTextField;
    import javax.swing.JPanel;
    import com.bea.ide.control.EditorSupport;
    import javax.swing.JTextPane;
    import com.bea.ide.control.AttributeEditorSimple;
    import com.bea.control.Issue;
    import com.bea.control.DefaultIssue;

    /*
     * Represents an attribute editing/validation dialog for the
     * customer-id attribute.
     */
    public class CustomerIdEditorSimple extends JPanel implements AttributeEditorSimple
    {
        private JTextField m_field;

        /*
         * Constructs a dialog using the existing value for
         * the customer-id attribute. Note that this constructor
         * is called by the getEditorComponent method in this class.
         */
        public CustomerIdEditorSimple(String origValue)
        {
            super(new BorderLayout());
            m_field = new JTextField(origValue);
            String messageText = "This sample supports the following values: \n" +
                                 "987654    987655 \n" +
                                 "987658    987659 \n"
                                 +"that's all \n";
            JTextPane valueMessage = new JTextPane();
            valueMessage.setText(messageText);
            valueMessage.setBackground(null);
            valueMessage.setEditable(false);
            this.add(m_field, BorderLayout.SOUTH);
            this.add(valueMessage, BorderLayout.NORTH);

        }

        // No special formatting is needed for this value.
        public JFormattedTextField.AbstractFormatter getFormatter()
        {
            return null;
        }

        /*
         * Returns the component to use for the customer-id
         * editing dialog. The IDE calls this method to display
         * the dialog. This class's constructor is used to
         * contain the user interface code.
         *
         * Note that an implementation of the AttributeEditorWizard
         * (as a more customizable alternative) would implement the
         * getDialog method, rather than this one, to return user interface.
         */
        public Component getEditorComponent()
        {
            return this;
        }

        /*
         * Provides a way for the IDE to retrieve the newly
         * entered attribute value. The getText method below is
         * exposed by the JTextField class. The user puts the value in,
         * this code gets it out so the IDE can retrieve it.
         */
        public String getNewAttributeValue()
        {
            return m_field.getText();
        }

        /*
         * Called by the IDE when the user clicks OK. This
         * method calls the validator code above to
         * validate the attribute value, ensuring that it is
         * one of the allowable values.
         */
        public Issue[] onFinish()
        {
            CustIdValidator cIdV = new CustIdValidator();
            return cIdV.validateId(getNewAttributeValue());
        }
    }

### Editor Support Example

The editor support class acts as the signpost, directing the IDE to specific code for each behavior. This implementation handles two behaviors (although one of them is for illustration only): a request for a custom editor and a request for a validator.

    package propEditor.ide;

    import com.bea.ide.control.ControlBehaviorContext;
    import com.bea.ide.control.EditorSupport;
    import com.bea.ide.control.ControlAttribute;
    import com.bea.ide.control.DefaultEditorSupport;

    /*
     * Represents support for actions in the IDE. In particular, this
     * class provides code that executes when the user clicks the ... in the
     * Property Editor to edit the customer-id attribute.
     */
    public class CustomerDataEditorSupport extends DefaultEditorSupport
    {
        public Object getBehavior(String behavior, ControlBehaviorContext ctx)
        {
            /*
             * When the user clicks the ellipses in the Property Editor, the IDE
             * sends a request for specifics on the BEHAVIOR_ATTRIBUTE_EDITOR
             * behavior. This code handles that request, return an object that
             * implements AttributeEditorSimple and provides UI for the custom editor.
             */
            if (behavior.equals(EditorSupport.BEHAVIOR_ATTRIBUTE_EDITOR))
            {
                if (ctx instanceof ControlAttribute &&
                    ((ControlAttribute)ctx).getName().equals("customer-id"))
                {
                    return new CustomerIdEditorSimple(((ControlAttribute)ctx).getValue());
                }
            }

            /*
             * This behavior request won't be received because validation will
             * occur in the context of the custom editor referred to above.
             * But the code is included here for illustration purposes. It would
             * be called if a custom editor were not provided and the user attempted
             * to set a new value in the Property Editor. The CustIdValidator class
             * implements ValidateAttribute.
             */
            if (behavior.equals(EditorSupport.BEHAVIOR_ATTRIBUTE_VALIDATOR))
            {
                if (ctx instanceof ControlAttribute &&
                    ((ControlAttribute)ctx).getName().equals("customer-id"))
                {
                    return new CustIdValidator("customer-id",
                        ((ControlAttribute)ctx).getValue());
                }
            }
            // Return the default implementation for other behaviors.
            return super.getBehavior(behavior, ctx);
        }
    }

Related Topics

None.