Friday, July 1, 2011

Sapphire 0.4 : FactsService

This is the first in what I hope to be a series of blog posts highlighting new features in the upcoming Sapphire 0.4 release.

When a property is described to a user in documentation one does it with a series of short statements that define its semantics, such as "must be specified" or "maximum value is 100". When a property is described to Sapphire one does it with a series of annotations, such as @Required or @NumericRange. This duplicate specification is a maintenance problem.

A FactsService provides a means to dynamically derive statements about property's semantics based on property's metadata. The derived facts can then be presented to the user as part of documentation, property editor information popup and in other relevant places.

A single facts service can produce multiple facts and multiple facts services can be active concurrently for a given property. FactsAggregationServices provides an easier way to consume all facts.

Sapphire includes a number of FactsService implementations.

ID Description
Sapphire.FactsService.JavaTypeConstraint Creates fact statements about Java type property's constraints by using semantical information specified by @JavaTypeConstraints annotation.
Sapphire.FactsService.Static Creates fact statements about property by using static content specified in @Fact and @Facts annotations.
Sapphire.FactsService.DefaultValue Creates fact statements about property's default value by using semantical information specified by DefaultValueService and @DefaultValue annotation.
Sapphire.FactsService.NumericRange Creates fact statements about numeric value property's range by using semantical information specified by @NumericRange annotation.
Sapphire.FactsService.Required Creates fact statements about property's optionality by using semantical information specified by @Required annotation.
Sapphire.FactsService.ReadOnly Creates fact statements about property's read-only state by using semantical information specified by @ReadOnly annotation.
Sapphire.FactsService.CountConstraint Creates fact statements about list property's count constraint by using semantical information specified by @CountConstraint annotation.
Sapphire.FactsService.AbsolutePath Creates fact statements about property's absolute path requirement by using semantical information specified by @AbsolutePath annotation.
Sapphire.FactsService.MustExist Creates fact statements about existence requirement on the entity referenced by property's value by using semantical information specified by @MustExist annotation.
Sapphire.FactsService.NoDuplicates Creates fact statements about value property's uniqueness constraint by using semantical information specified by @NoDuplicates annotation.
Sapphire.FactsService.ValidFileExtensions Creates fact statements about valid file extensions for property's value by using semantical information specified by @ValidFileExtensions annotation.
Sapphire.FactsService.ValidFileSystemResourceType Creates fact statements about valid file system resource type (file or folder) for property's value by using semantical information specified by @ValidFileSystemResourceType annotation.
Sapphire.FactsService.ProjectRelativePath Creates fact statements about property's relative to the project path requirement by using semantical information specified by @ProjectRelativePath annotation.
Sapphire.FactsService.WorkspaceRelativePath Creates fact statements about property's relative to the workspace path requirement by using semantical information specified by @WorkspaceRelativePath annotation.

Example

This screen capture shows user experience with some of the provided FactsService implementation. See if you can match facts in the screen capture to service implementations above.

FactsService

Adopters can provide custom FactService implementations either globally using Sapphire extension system or at the property level using @Service annotation.

Example

A simple global FactsService implementation that is triggered by a hypothetical @Since property annotation.

public class SinceVersionFactsService extends FactsService
{
    @Override
    protected void facts( List facts )
    {
        Since since = property().getAnnotation( Since.class );
        facts.add( "Since version " + since.version() + "." );
    }

    public static class Factory extends ModelPropertyServiceFactory
    {
        @Override
        public boolean applicable( IModelElement element,
                                   ModelProperty property,
                                   Class<? extends ModelPropertyService> service )
        {
            return property.hasAnnotation( Since.class );
        }
    
        @Override
        public ModelPropertyService create( IModelElement element,
                                            ModelProperty property,
                                            Class<? extends ModelPropertyService> service )
        {
            return new SinceVersionFactsService();
        }
    }
}

The service implementation is registered in META-INF/sapphire-extension.xml file.

<extension xmlns="http://www.eclipse.org/sapphire/xmlns/extension">
    <model-property-service>
        <id>Example.SinceVersionFactsService</id>
        <type>org.eclipse.sapphire.services.FactsService</type>
        <factory>example.SinceVersionFactsService$Factory</factory>
    </model-property-service>
</extension>

Facts can also be statically specified for a given property by using @Fact annotation. Use @Facts annotation to specify multiple facts. The facts contained in these annotations are surfaced by an included FactsService implementation (id:Sapphire.FactsService.Static).

Example

// *** ExampleOne ***
    
@Fact( statement = "Important fact.")
    
ValueProperty PROP_EXAMPLE_ONE = new ValueProperty( TYPE, "ExampleOne" );
    
Value getExampleOne();
void setExampleOne( String value );

// *** ExampleMultiple ***
    
@Facts( { @Fact( statement = "First important fact." ), @Fact( statement = "Second important fact." ) } )
    
ValueProperty PROP_EXAMPLE_MULTIPLE = new ValueProperty( TYPE, "ExampleMultiple" );
    
Value getExampleMultiple();
void setExampleMultiple( String value );

Please direct your comments and questions to Sapphire adopter forum.