4.2 Annotation Inspectors

Beyond the base issue of inspecting an object and its properties, a number of inspectors are focussed on third-party annotations. These annotation inspectors all extend BaseObjectInspector, and so inherit its features, but in addition they inspect the following frameworks.

4.2.1 BeanValidationInspector

BeanValidationInspector inspects Bean Validation (JSR 303) annotations. It returns the following attributes for the following business properties:

Metawidget Attribute Bean Validation Annotation
maximum-fractional-digits @Digits(fraction=...)
maximum-integer-digits @Digits(integer=...)
maximum-length @Size(max=...)
maximum-value @Max
minimum-length @Size(min=...)
minimum-value @Min
required @NotNull

4.2.2 FacesInspector

FacesInspector inspects Java Server Faces-specific annotations. It returns the following attributes for the following business properties:

Metawidget Attribute Annotation
(any) @UiFacesAttributes and @UiFacesAttribute -annotates an arbitrary Metawidget attribute, based on a Java Server Faces EL expression.

Unlike @UiFacesLookup, which fits into a well-defined place within the JSF framework (ie. f:selectItems), the @UiFacesAttribute expression is evaluated by the Inspector, not by the Metawidget. This means the Inspector must be able access to FacesContext. In practice this usually happens automatically, but in some cases it may be necessary to 'combine remote inspections' (see Section 8.3, “Combine Remote Inspections”).

currency-code, currency-symbol, number-uses-grouping-separators, minimum-integer-digits, maximum-integer-digits, minimum-fractional-digits, maximum-fractional-digits, locale, number-pattern and number-type @UiFacesNumberConverter - annotates the field should use the standard Faces NumberConverter.

Note: the NumberConverter property integerOnly is not specified using this annotation, as it can be inferred from the property's type

date-style, locale, datetime-pattern, time-style, time-zone and datetime-type @UiFacesDateTimeConverter - annotates the field should use the standard Faces DateTimeConverter
faces-component @UiFacesComponent - annotates the field should be rendered using the given Faces UIComponent in the UI.

Use of this annotation does not bind the business class to the UI quite as tightly as it may appear, because JSF has a loosely coupled relationship between <component-name> and <component-class>, and a further loose coupling between <component> and <render-kit> - as defined in faces-config.xml

faces-converter-id UiFacesConverter - annotates the field should use the given Faces converter in the UI.

This annotation uses the converter Id, not the class. Whilst it is possible to specify the class through the XML (see FacesInspectionResultConstants.FACES_CONVERTER_CLASS), this does not work well for the annotations because they are applied to domain objects, whereas converters are UI-object. Using an annotation value of type Class would introduce dependencies in the wrong direction. Using a value of type String that contains a fully-qualified classname would work, but is brittle to refactoring

faces-lookup @UiFacesLookup - annotates the value in the field should belong to the set returned by the given EL expression
faces-suggest @UiFacesSuggest - annotates the value in the field should be 'suggested' (ie. using a pop-up box) using the set returned by the given EL expression

4.2.3 HibernateValidatorInspector

HibernateValidatorInspector inspects Hibernate Validator annotations. It returns the following attributes for the following business properties:

Metawidget Attribute Hibernate Validator Annotation
maximum-fractional-digits @Digits(fractionalDigits=...)
maximum-integer-digits @Digits(integerDigits=...)
maximum-length @Length(max=...)
maximum-value @Max
minimum-length @Length(min=...)
minimum-value @Min
required @NotNull or @NotEmpty

4.2.4 JexlInspector

JexlInspector inspects @UiJexlAttribute annotations and sets arbitrary attributes based on the result of evaluating an Apache Commons JEXL expression. It can be used to introduce declarative UI scripting into environments that lack their own expression language (ie. JSP has an EL, Swing does not). For example:

import org.metawidget.inspector.commons.jexl.*;

public class Person {
	public boolean retired;
	@UiJexlAttribute( name = "hidden", value = "!this.retired" )
	public BigDecimal pension;
}

This code returns a hidden attribute based on evaluating the JEXL expression !this.retired (where this refers to the runtime instance of the Person being inspected). It could be used to show/hide the pension field in response to the retired checkbox being checked.

The JEXL expression language also supports branching statements. For example:

import org.metawidget.inspector.commons.jexl.*;

public class PersonController {

	@UiJexlAttribute( name = "label", value = "if ( this.readOnly ) 'Back'" )
	public void cancel() { ... }
}

This code overrides the label of an action to be either 'Back' or 'Cancel', depending on whether the Person was being edited. It is taken from the Swing Address Book sample (see Section 1.2.1, “Desktop Address Book”).

4.2.5 JpaInspector

JpaInspector inspects Java Persistence Architecture annotations. It returns the following attributes for the following business properties:

Metawidget Attribute JPA Annotation
hidden @Id, unless JpaInspectorConfig.setHideIds is false
large @Lob
maximum-length @Column(length=...)
required @Column(nullable=false) or @ManyToOne(optional=false)

4.2.6 MetawidgetAnnotationInspector

As much as possible, Metawidget tries to inspect metadata from existing sources, without introducing new concepts. Where that is not sufficient, MetawidgetAnnotationInspector adds a handful of annotations:

Metawidget Attribute Metawidget Annotation
(any) @UiAttributes and @UiAttribute - a 'catch all' for denoting arbitrary UI metadata
(order of fields) @UiComesAfter
action @UiAction
dont-expand @UiDontExpand - denotes a value should not be inspected and expanded into sub-widgets. This can be useful if, say, you have a read-only field and just want to display its top-level toString() rather than all its child properties
hidden @UiHidden - denotes a value should be hidden in the UI. The value may still be rendered on the client, depending on the Metawidget (ie. for Web clients, may use a HTML hidden field)
label @UiLabel - denotes the label to used in the UI. Can be a resource key if the UI is using resource bundles, or an EL expression if the UI has an expression language (ie. JSF)
large @UiLarge - denotes the field should be 'large' in the UI (ie. a multi-line textbox)
lookup @UiLookup - denotes the value in the field should belong to the given set of Strings
masked @UiMasked - denotes a value should be masked in the UI (eg. a password field)
read-only @UiReadOnly - denotes a value should be read-only in the UI
read-only @UiReadOnly - denotes a value should be read-only in the UI
section @UiSection - denotes the start of a logical grouping in the UI
wide @UiWide - denotes the field should be 'wide' in the UI, spanning all columns in a multi-column layout. 'Wide' is different to 'large', because 'large' implies a data size (ie. BLOB or CLOB) whereas 'wide' refers purely to spanning columns. Generally all 'large' fields are implicitly 'wide', but not all 'wide' fields are 'large'. For example, you may want a normal text field (not a text area) to span all columns.

4.2.7 OvalInspector

OvalInspector inspects OVal annotations. It returns the following attributes for the following business properties:

Metawidget Attribute OVal Annotation
maximum-length @Length(max=...) or @MaxLength
maximum-value @Max or @Range(max=...)
minimum-length @Length(min=...) or @MinLength
minimum-value @Min or @Range(min=...)
required @NotNull or @NotEmpty or @NotBlank

4.2.8 Troubleshooting

4.2.8.1 I get "java.lang.TypeNotPresentException"

If you are using Sun's implementation of Java, Metawidget's annotation support requires Java 5.0u6 or later, which includes a fix for this bug (Bug Parade ID: 6322301).

4.2.8.2 My inspector is not finding my annotations

Annotations are designed to 'silently fall away' in environments that do not support them: they never throw ClassDefNotFoundError. For example, if a JPA-annotated class is transferred to an application tier without ejb3-persistence.jar (or equivalent) in its classpath, the JPA annotations will disappear.

If this is the cause, either add the appropriate JAR to the tier, or consider implementing a remote inspector (see Section 8.1, “Order Fields”).