Showing posts with label Usus. Show all posts
Showing posts with label Usus. Show all posts

Friday, July 30, 2010

Speaking at the Eclipse Testing Day

My colleage at andrena, and fellow blogger over at Crafting Code, Marc and I will give a talk at the Eclipse Testing Day in Darmstadt on Sep 8th. Here's the abstract:
In software projects with sizeable code bases maintaining the suite of all unit tests regularly becomes an issue: developers want to be able to execute the entire collection of tests with a single command, but an AllTests suite that lists the test cases explicitly must be kept manually, which is inefficient and error-prone.
The well-known ClasspathSuite solves this problem elegantly, but it has its weaknesses, too: it becomes slow on large workspaces and executes static code blocks in all classes when loading and analyzing them, which can cause unexpected effects and in some cases make the tests fail.
The Eclipse IDE allows to run all tests in a single Java project or source folder directly from the context menu, but doesn't extend this functionality to the entire workspace. (For good reasons, as we will explain in the talk.) There are approaches to mitigate this issue (e.g. this Plug-in), but they have their own shortcomings. Still, they clearly show the need for a solution in this area.
After discussing the current status of test suite management in Eclipse, we introduce a new approach to address these issues; we'll also demonstrate a prototypical implementation which we are currently developing at Project Usus.
 (If you want to join us at the Testing Day, here's the registration link :-)

Wednesday, May 5, 2010

Usus 0.4 preview build available

Just a brief update about what's going on at the Usus front: there is a new preview build of Usus available at projectusus.org — you can get it as usual from the update site (look here for install instructions). This one includes now improved ACD computation and packages in cycles as a new metric.

In other news, today Nicole and Andreas gave a talk about Usus at the 'Eclipse Tools Day' track at JAX 2010, the big Java Enterprise conference in Wiesbaden. If you were there, you've seen the new version first-hand :-)

Monday, February 15, 2010

A rose by any other name...

Some years ago when I wrote about Eclipse's APIs for supporting automated refactorings, it wasn't all too convenient to drive an existing JDT refactoring from your own plug-in. For instance, if you had an IField or an IMethod in your hands (one of JDT's Java language model objects), you couldn't simply call some API methods to rename that thing (field or method). You had to write quite a bit of code, and it was still difficult to avoid Eclipse presenting your refactoring in the standard wizard dialog. A few days ago I was playing around with a new fun feature in Usus (I'm not telling what it is :-) — and I found it much more convenient to do such a thing nowadays.

How you do it

The central object that represents a refactoring procedure is the Language Toolkit's Refactoring (unsurprisingly). If you want to use an existing automated refactoring, such as Rename field or Extract method from the JDT pool, your first step is to retrieve an object that conforms to the Refactoring contract.

You get one of these guys from the refactoring core plugin: first, add a plug-in dependency to org.eclipse.ltk.core.refactoring. You can then call the static factory method RefactoringCore.getRefactoringContribution( String id )in order to retrieve a refactoring contribution, i.e. an automated refactoring that was contributed from some plug-in via an extension in the plugin.xml. For this you need to know the ID of that refactoring contribution. In the case of the JDT refactorings, use the constants in IJavaRefactorings (add a dependency to org.eclipse.jdt.core.manipulation). You can now feed the refactoring contribution with the parameters it needs; in the case of a Rename Method refactoring, for instance, that would be the IMethod object that represents the method we want to rename, and the new name we to give to it. Having done that, we can ask the contribution to create the Refactoring object for us. In sum, you'd do something like this:


private Refactoring mkRefactoring( RefactoringStatus status, IMethod methodToRename, String newName ) throws CoreException {
RenameJavaElementDescriptor desc = loadDescriptor();
desc.setJavaElement( methodToRename );
desc.setNewName( newName );
return desc.createRefactoring( status );
}

private RenameJavaElementDescriptor loadDescriptor() {
String id = IJavaRefactorings.RENAME_METHOD;
RefactoringContribution contrib = RefactoringCore.getRefactoringContribution( id );
return (RenameJavaElementDescriptor)contrib.createDescriptor();
}



As you can see, I'm passing a RefactoringStatus object into the creation operation. That object will contain the result of the creation (which might be an OK or some error status).

Once you are in possession of a refactoring object, you might run in it the standard refactoring dialog (in order to be nice to the user and present them with a preview and the cancel option), or you could extract the Change objects from it and just ask them to perform their work. All in all, a quick test drive of a Rename Method refactoring might be something along these lines:


RefactoringStatus status = new RefactoringStatus();
Refactoring refactoring = mkRefactoring( status, methodToRename, newName );
if( status.isOK() ) {
if( refactoring.checkAllConditions( nullMonitor() ).isOK() ) {
Change change = refactoring.createChange( nullMonitor() );
change.perform( nullMonitor() );
}
}

// ...

private IProgressMonitor nullMonitor() {
return new NullProgressMonitor();
}



A word to the wise

Obviously, there are many aspects you want to consider if you try this in production code (are there editors open which you'll want to save? what about undo operations? do you want to support the local history? ...). You might want to look at the JDT code more in depth in order to find out how to handle these things. (That's something I didn't because I merely wanted to play around with them.) Have fun!

Monday, February 8, 2010

Elegantification is possible even in Java

Sometimes it's the small things that make you smile. (That's so. Sometimes at least ;-)

The unfavorable situation

In the Usus UI, we have several tables displaying code proportions information, and in order to simplify the code that configures such a table, we have a common TreeViewer that reads column information from an enum, where each of the enum's elements represents a table column and its meta data, such as the header string, the column weight and the text alignment in the column's cells. Thus, the table description for the Usus Cockpit view looked like this:
enum CockpitColumnDesc implements IColumnDesc {

INDICATOR( "Indicator", LEFT, 56, true ) {
public String getLabel( CodeProportion element ) {
return element.getMetric().getLabel();
}
},
SQI( "SQI", 10, false ) {
public String getLabel( CodeProportion element ) {
// ...
}
},
// ... more enum fields

private final String headLabel;
private final int weight;
private final boolean hasImage;

CockpitColumnDesc( String headLabel, ColumnAlignment align, int weight, boolean hasImage ) {
this.headLabel = headLabel;
this.align = align;
this.weight = weight;
this.hasImage = hasImage;
}

CockpitColumnDesc( String headLabel, int weight, boolean hasImage ) {
this( headLabel, RIGHT, weight, hasImage );
}

public int getWeight() {
return weight;
}

public String getHeadLabel() {
return headLabel;
}
// ...
}
Now, having about a dozen or so column description enums like this, it became a little unwieldy to add more column information (such as the alignment, i.e. LEFT, RIGHT or CENTER). Each of the enums needed another field that kept the alignment, a getter and another constructor if a sensible default value was to apply. In other words, in order to add alignment information, we still had to write a bunch of code lines into each of the enums (that is, a bunch of code lines that was practically identical for each of them). That looked unelegant to me. The elegant solution was to use a custom annotation type.

Making it look nicer

Annotations are normally used to attach meta data to language elements (classes, methods, fields), so that development tools can read them and do something sensible with the information. For instance, the well-known @SuppressWarnings annotation, when put above a method declaration, tells the Java compiler to shup up about some thing it might have complained about otherwise. (And gentle reader, you won't be surprised that this feature is misused every so often...)

On the other hand, you can make annotation information available at runtime, and read it via reflection mechanisms. Let's say you declare a new annotation type:
@Target( value = { ElementType.FIELD } )
@Retention( RetentionPolicy.RUNTIME )
public @interface UsusTreeColumn {
String header() default "";

// column weight (percentage of overall width in the table that this column takes)
int weight() default 5;

ColumnAlignment align() default ColumnAlignment.LEFT;

}
The bit about the RetentionPolicy tells the compiler to include the annotation information in the compiled code, so that it can be loaded at runtime. If someone uses your annotation like so:
@UsusTreeColumn( header = "SQI", align = RIGHT, weight = 10 )
SQI( false ) {
public String getLabel( CodeProportion element ) {
// ...
}
}
you can reach the info in the annotation this way:

        try {
Field field = loadField( "SQI" );
for( Annotation annotation : field.getAnnotations() ) {
if( annotation instanceof UsusTreeColumn ) {
UsusTreeColumn column = (UsusTreeColumn)annotation;
String header = column.header();
// ...
}
}
} catch( NoSuchFieldException nosufex ) {
// ...
}

// ...

private Field loadField() throws NoSuchFieldException {
Class enumClass = columnDescEnumValue.getClass();
if( enumClass.isAnonymousClass() ) {
enumClass = enumClass.getEnclosingClass();
}
return enumClass.getDeclaredField( columnDescEnumValue.toString() );
}

Basically, you have to find the language element (in this case a field from the enum type) that has the annotation attached to it, and then getAnnotations() from it. The way you find it is via reflection as usual. Once you have the annotation, you can cast it to the interface type you declared (in this case UsusTreeColumn) and simply use it like any other Java object. In essence, that is what our tree viewer now does. The enums, on the other hand, look compact and much more readable:

enum CockpitColumnDesc implements IColumnDesc {

@UsusTreeColumn( header = "Indicator", weight = 56 )
INDICATOR( true ) {
public String getLabel( CodeProportion element ) {
return element.getMetric().getLabel();
}
},
@UsusTreeColumn( header = "SQI", align = RIGHT, weight = 10 )
SQI( false ) {
public String getLabel( CodeProportion element ) {
// ...
}
}

// ...

private final boolean hasImage;

CockpitColumnDesc( boolean hasImage ) {
this.hasImage = hasImage;
}

public boolean hasImage() {
return hasImage;
}
}

Most of the information is compressed in the annotations, though it is still well compiler-checked; and sensible defaults can be used, so that we can leave out parameters in the annotations if we like. (The code for all the enums that describe columns in the Usus UI is now less than half that it was before. Not that code size reduction is everything that matters — but since the new code brings the same information in a much more compact notation, it's really more readable now.)

Friday, January 22, 2010

EclipseMagazin article about Usus

In November 2009 we presented Project Usus for the first time at the Eclipse DemoCamp in Frankfurt. EclipseMagazin now features a short summary article on each of the demos. Usus is described on page 89 by Nicole, Stefan and me. Go grab a copy!