It is with great pleasure that I announce availability of Sapphire 0.4 release. This release includes major enhancements, such as API improvements in services and XML binding, more powerful Sapphire EL and ability to use it in more places, content proposals in text fields, split form, regular multi-section form editor page for cases where master-details presentation isn't appropriate, and much more.
Thursday, December 1, 2011
Saturday, November 19, 2011
Java Language Puzzle 3
Given the following source listing, explain why the compiler is producing a warning at invocation to the list method and give a solution for removing the warning without resorting to @SuppressWarnings annotation.
public class JavaLanguagePuzzle3
{
public static void main( String[] args )
{
list( "1", 2, new BigDecimal( "3.5" ) );
}
private static <T> List<T> list( T... items )
{
return Arrays.asList( items );
}
}
Warning:
Type safety: A generic array of Object&Serializable&Comparable<?> is created for a varargs parameter
Friday, October 7, 2011
Announcing Sapphire 0.3.1 Release
It is with great pleasure that I announce availability of Sapphire 0.3.1 release. This maintenance release includes 64 bug fixes and enhancements. Adopters of 0.3 release can expect full backwards compatibility. No migration guide is being published.
Wednesday, August 24, 2011
Using Sapphire to Work with Java Types
This article covers an intermediate topic related to Sapphire. Those unfamiliar with Sapphire should first read the introduction.
When implementing Eclipse tooling that works with Java projects, a frequent requirement is to deliver models and UI that reference Java types (classes, interfaces, enumerations and annotations). Implementing this from scratch, even using the excellent API provided by JDT is a challenge. Fortunately, Sapphire's JDT integration makes this quite easy.
The first step is the model.
@Type( base = JavaTypeName.class )
@Reference( target = JavaType.class )
@Label( standard = "filter" )
@JavaTypeConstraint( kind = { JavaTypeKind.CLASS, JavaTypeKind.INTERFACE }, type = "java.io.FileFilter" )
@MustExist
@Required
ValueProperty PROP_FILTER = new ValueProperty( TYPE, "Filter" );
ReferenceValue<JavaTypeName,JavaType> getFilter();
void setFilter( String value );
void setFilter( JavaTypeName value );
Here we utilize reference value construct which states that the property holds a JavaTypeName which is resolvable to a JavaType. The resolution is provided by the framework and works as long as the model is loaded in the context of a Java project.
The @JavaTypeConstraint annotation specifies that the referenced type must be a class or an interface and that it must derive from java.io.FileFilter type.
The @MustExist annotation specifies that the named type must be present in the project.
The @Required annotation specifies that the property must have a value (null is not ok).
The next step is the UI definition. Here we create an editor section with a single property editor, but the property editor is, of course, not limited to editor sections. It can be used in any form context.
<section>
<content>
<property-editor>Filter</property-editor>
</content>
</section>
That is all that is necessary to define a model property that references a Java type and to present that property in the UI. Once this example is executed, you will see a property editor that is composed of a label, a text box, two action buttons and a validation feedback marker.
Clicking on the validation feedback marker shows the problem message along with wealth of semantic information about the property.
The browse button provides the means to select from among existing Java types using JDT's type selection dialog. The framework automatically constraints the contents of the dialog based on @JavaTypeConstraint annotation.
The create button provides the means to define a new Java type if the specified type name cannot be resolved.
Since @JavaTypeConstraint annotation in this example specifies that the property can reference either a class or an interface, the user is presented with a choice after clicking on the create button.
Once the appropriate option is selected, the new type is created and opened in the Java editor. The created type derives from the type specified in @JavaTypeConstraint annotation and is formatted according to user's format preferences.
Note that this article covers features in 0.3.1 and 0.4 releases of Sapphire, which at the time of this writing are still in development. A subset of the described features is available in version 0.3, which is the latest released version. Please direct all questions to the forum.
Monday, August 1, 2011
From XML to Form to Diagram with Sapphire
Many of the systems that developers and administrators interact with on the daily basis are configured via a menagerie of XML files. Even armed with a schema and a good XML editor, users have a hard time editing these files by hand. A good tooling strategy to address this difficulty is to create a multi-page editor where a user can flip back-n-forth between XML source view and a higher level form-based view. Going a notch further, certain relationships can be presented with one or more diagrams on separate pages.
Unfortunately, building such multi-page editor using basic Eclipse platform facilities is a daunting challenge. It can take many months to create something half-decent for even smaller schemas. Sapphire can make this a much less daunting task by focusing developer attention on modeling semantics of the data rather than wiring individual widgets.
I have proposed a talk for EclipseCon Europe 2011 to present Sapphire as a whole and the new diagram editing features in Sapphire 0.3 (Indigo) release. You can comment and vote on the proposal if you are interested.
Proposed Talk: From XML to Form to Diagram with Sapphire
Sapphire is a relatively new project at Eclipse started with an initial contribution from Oracle. It is a UI building framework that allows developers to specify UI in terms of higher level constructs like property editors instead of widgets and layouts. This paradigm results in several orders of magnitude improvement in developer productivity while simultaneously delivering better quality UI that is easier to maintain.
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.
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.
Monday, June 27, 2011
Java Language Puzzle 2
Given the following source listing, explain why InstantiationException is thrown when running (via the main method entry point) and how the code should be fixed.
package puzzle;
public class Entity
{
private Entity()
{
}
public class Factory
{
public Entity create()
{
return new Entity();
}
}
public static void main( String[] args ) throws Exception
{
Factory.class.newInstance();
}
}
Exception in thread "main" java.lang.InstantiationException: puzzle.Entity$Factory
at java.lang.Class.newInstance0(Unknown Source)
at java.lang.Class.newInstance(Unknown Source)
at puzzle.Entity.main(Entity.java:19)
ANSWER: It looks like everyone who responded correctly identified the fact that Factory class is not defined as static as the cause of this exception. The solution is to add the static keyword. I would imagine that vast majority of Java developers are pretty comfortable with static/non-static inner classes semantics. What raises this to a puzzle level is a rather unhelpful exception.