The Source for Java Technology Collaboration
User: Password:



Evan Summers

Evan Summers's Blog

Mmmm... Java

Posted by evanx on October 17, 2006 at 11:07 AM | Comments (6)

<!--title-->

What We Might Get

I understand a bit more now about closures thanks to great comments in a previous blog entry.

For instance, return, break and continue inside the closure, might be used to "transfer control outside the body of the closure" (as said here) ie. in back in the enclosing method. Since such functionality could be mimicked by throwing exceptions, what about using the throw keyword as follows.

   void refreshCalendarItems() {
      try {
         while (retry-- > 0) {
            List<CalendarItem> calendarItems = BlockingWorker() {
               try {
                  calendarItems = connection.getCalendarItems(today,
                      ProgressDisplay(int percent) {
                         publishProgress(percent);
                      });
                  return calendarItems; 
                      // from closure, not from enclosing refreshCalendarItems()
               } catch (CancellationException e) {
                  publishMessage("OK, so you wanna cancel, fine!");
                  throw return; // from refreshCalendarItems()
               } catch (IOException e) {
                  publishMessage("Comms error occurred");
                  throw continue; // retry 
               } catch (RuntimeException e) {
                  publishMessage("Unrecoverable fatal exception");
                  throw e; // propogate out of closure
               } finally {
                  ...
               }
            }
         }
         ... // update GUI with calendarItems
      } finally {
         ...
      }
   }

where our BlockingWorker is self-starting, and would pop up a modal progress dialog to block while the worker is running. I'll try write such a worker in a later blog article, which will probably use a message bus.

What C# Has Got That We Want

C# has delegates. I like those, eg. for event handler registration.

My crystal ball tells me that Java will get popular features of other popular languages. This is a bug and a feature of software, that competing products spur each other on to more "convenience" aka complexity. And languages, toolkits and tools are very competitive, eg. Java vs .NET, RoR, et al. Each release will try to provide more feature candy for developers eg. inspired by popular features in competing languages. "We got closures and delegates too, haa-Haa!"

What I Really Want

What i keep banging on about is toolable references to properties and methods.

What about a convention like MyClass#updateGui and object#updateGui, representing a class artifact by name, ie. a Field, Method or PropertyDescriptor. Actually first and foremost properties, then methods eg. event handlers, then maybe fields, or maybe not even fields.

The important thing is that MyClass#updateGui is understood by IDEs, and so auto-completable, verifiable, refactorable, and all that.

The effect could be that the compiler sees MyClass#updateGui as just the String "updateGui", or maybe better, as new ClassArtifact(MyClass.class, "updateGui").

The result is that we can do cool stuff like refactorable, toolable, reflection-safe beans binding, method delegation and what-not as follows.

public class MoviePresentationModel {
    String title;
    Integer year;
    ...
    public String getTitle() {
       return title;
    }

    @StringValidator(empty = false)
    public void setTitle(String title) {
       this.title = title;
    }
    ...
}

public class MoviePresentation {
   GuiHelper helper = new GuiHelper(this);
   ...
   JTextField movieTitle = new JTextField();
   ...
   MoviePresentationModel presentationModel = new MoviePresentationModel()

   public MoviePresentation() {
      binder.bind(movieTitle, presentationModel, presentationModel#title);
      refreshButton.addActionListener(helper.createActionListener(this#refresh));
   }

   @Action(block = Action.WINDOW, worker = BlockingWorker.class)
   protected void refresh() {
      ...
      helper.invokeAndWait(this#updateGui);
   }

   @InEdt()
   protected void updateGui() {
      ... 
   }
}

I like this because i have a little problem with strings as references. Because they aint toolable, verifiable, factorable, refactorable, and worst of all, you gotta type them out letter by letter!

Enum references with no Strings attached

I guess we can implement this ourselves as follows.

@PropertyInfo(MoviePresentationModel.class)
public enum MoviePresentationModelProperty {
   title,
   year,
   ...
}

@MethodInfo(MoviePresentation.class)
public enum MoviePresentationMethod {
   refresh,
   updateGui,
   ...
}

We just gotta make sure that we include these in unit tests, so that when a refactoring operation breaks something, we find out sooner rather than later.

Our application might then be factored as follows.

public class MoviePresentation {
   GuiHelper helper = new GuiHelper(this);
   ...
   JTextField movieTitle = new JTextField();
   ...
   protected MoviePresentation() {
      helper.bind(movieTitle, presentationModel, MoviePresentationModelProperty.title);
      refreshButton.addActionListener(helper.createActionListener(MoviePresentationMethod.refresh));
   }

   @Action(block = Action.WINDOW, worker = BlockingWorker.class)
   protected void refresh() {
      ...
      helper.invokeAndWait(MoviePresentationMethod.updateGui);
   }
   ...
}

where our helper can use name() to get the name of the enum object, and use that to get the Method or PropertyDescriptor being referenced, since it was already passed a reference to this in its constructor.

The problem is that MoviePresentationMethod.updateGui is not navigable, eg. using Alt-G in Netbeans, and that's a showstopper for me, for methods anyway.

Testing Enum Property References

Let's say we gonna use enums for property references, rather than strings. To make it safe for refactoring, we need to test, which we can do as follows.

    @Test()
    public void test()  {
        try {
            testProperty(MoviePresentationModelProperty.class);
            ... // all our other such classes
        } catch (Exception e) {            
            assertTrue(e.getMessage(), false);
        }
    } 
    
     public void testProperty(Class propertyEnumType) throws Exception {
        PropertyInfo propertyInfo =
                (PropertyInfo) propertyEnumType.getAnnotation(PropertyInfo.class);
        if (propertyInfo == null) {
            throw new Exception(propertyEnumType.getSimpleName());
        }
        test(propertyInfo.value(), propertyEnumType);
    }
    
    public void test(Class beanClass, Class propertyEnumType) throws Exception {
        BeanInfo beanInfo = Introspector.getBeanInfo(beanClass);
        List<String> propertyNameList = new ArrayList();
        for (PropertyDescriptor descriptor: beanInfo.getPropertyDescriptors()) {
            propertyNameList.add(descriptor.getName());
        }
        for (Enum propertyReference: (Enum[]) propertyEnumType.getEnumConstants()) {
            String propertyName = propertyReference.name();
            if (!propertyNameList.contains(propertyName)) {
                throw new Exception(propertyName);
            }
        }
    }

Mmmm... refactoring.

Postscript

I wrote this blog using very minimal thingymajig which i wrote just yesterday, which i call quitehyper. I'll write a blog on it a week or so, once i've written a WebStart'able GUI tool to go with it. But here's a sneak preview of its code :)

Here's a snippet of the source text (for this article).

What Might We Get //section  

Now that i understand a bit more about 
:{closures}{http://weblogs.java.net/blog/evanx/archive/2006/10/better_than_goo.html}, 
i'm a happier and more fulfilled person.
No really. <i>Hopefully no one everytakes anything i say seriously!?</i>

For instance, #return, #break and #continue inside the closure, can refer
to the enclosing method. Since such functionality can only otherwise be mimicked
by throwing exceptions, i suggest that we use the #throw keyword? <i>Unless 
someone else already did?</i>

<pre class='java'>
   void refreshCalendarItems() {
      try {
         while (retry-- > 0) {
            List<CalendarItem> calendarItems = BlockingWorker() {
               try {
                  calendarItems = connection.getCalendarItems(today,
                      ProgressDisplay(int percent) {
                         publishProgress(percent);
                      });
                  return calendarItems; // from closure, not from refresh()
...

We automatically do some syntax highlighting (and HTML escaping) of the Java code in pre blocks, as below, as below. (That is not a typo.)

public class NHyperJavaProcessor {    
    static final String keywordStyle = "color: #000099; font-weight: bold";
    static final String stringLiteralStyle = "color: #99006b";
    static final String numericLiteralStyle = "color: #780000";
    static final String methodStyle = "font-weight: bold";
    
    static final String[] delimiters = new String[] {
        "\n", "... //", "//", "<", ">",
        " ", ".", "(", ")", "{", "}", "[", "]", "\\\"", "\"",
        "+", "-", "*", "/", "@", "%%", "%", "#",
        "package", "import",
        "static", "final", "abstract", "synchronized", "transient",
        "default",
        "class", "interface", "@interface", "enum",
        "extends", "implements",
        "public", "private", "protected",
        "true", "false", "null",
        "instanceof",
        "void", "boolean", "int", "char", "long",
        "throws", "throw", "try", "catch", "finally",
        "new", "this", "super",
        "if", "else", "for", "while",
        "continue", "break",
        "return"
    };
    
    enum NState {
        quoteState,
        spaceState,
        dotState,
        wordState,
        noState;
    };
    
    enum NType {
        wordType,
        leftRoundBracketType,
        punctuationType,
        keywordType,
        delimiterType,
        dotType,
        quoteType,
        spaceType,
        leftAngleBracketType,
        rightAngleBracketType,
        ampersandType,
        percentType,
        noType;
    };

    String sourceText;
    StringBuilder stringBuilder = new StringBuilder();
    StringBuilder subBuilder = new StringBuilder();
    NState state = NState.noState;
    
    public NHyperJavaProcessor(String sourceText) {
        this.sourceText = sourceText;
    }
    
    public static String processText(String sourceText) {
        return new NHyperJavaProcessor(sourceText).process();
    }
    
    protected String process() {
        for (String token : NTokeniser.tokeniseText(sourceText, delimiters)) {
            processToken(token);
        }
        stringBuilder.append(subBuilder);
        return stringBuilder.toString();
    }
    
    protected void processToken(String token) {
        NType type = getTokenType(token);
        token = escape(token, type);
        if (state == NState.wordState) {
            if (type == NType.leftRoundBracketType) {
                stringBuilder.append(spanStyle(methodStyle, subBuilder.toString()));
                stringBuilder.append(token);
                subBuilder.setLength(0);
                state = NState.noState;
                return;
            }
            state = NState.noState;
        } else if (state == NState.quoteState) {
            if (type == NType.quoteType) {
                subBuilder.append(token);
                stringBuilder.append(spanStyle(stringLiteralStyle, subBuilder.toString()));
                subBuilder.setLength(0);
                state = NState.noState;
                return;
            }
            subBuilder.append(token);
            return;
        } else if (state == NState.spaceState || state == NState.dotState) {
            if (type == NType.wordType) {
                stringBuilder.append(subBuilder);
                subBuilder.setLength(0);
                subBuilder.append(token);
                state = NState.wordState;
                return;
            }
            state = NState.noState;
        }
        state = NState.noState;
        if (type == NType.quoteType) {
            stringBuilder.append(subBuilder);
            subBuilder.setLength(0);
            subBuilder.append(token);
            state = NState.quoteState;
            return;
        } else if (type == NType.spaceType) {
            state = NState.spaceState;
        } else if (type == NType.dotType) {
            state = NState.dotState;
        } else if (type == NType.keywordType) {
            token = spanStyle(keywordStyle, token);
        }
        stringBuilder.append(subBuilder);
        stringBuilder.append(token);
        subBuilder.setLength(0);
    }
    
    protected NType getTokenType(String token) {
        if (token.equals("\"")) return NType.quoteType;
        if (token.equals("(")) return NType.leftRoundBracketType;
        if (token.equals(" ")) return NType.spaceType;
        if (token.equals("\t")) return NType.spaceType;
        if (token.equals("\n")) return NType.spaceType;
        if (token.equals(".")) return NType.dotType;
        if (token.equals("<")) return NType.leftAngleBracketType;
        if (token.equals(">")) return NType.rightAngleBracketType;
        if (token.equals("&")) return NType.ampersandType;
        if (token.equals("%")) return NType.percentType;
        if (stringHelper.startsWithLetter(token)) {
            if (stringHelper.equals(token, delimiters)) {
                return NType.keywordType;
            } else {
                return NType.wordType;
            }
        }
        return NType.noType;
    }
    
    protected String spanStyle(String style, String string) {
        return formatter.format("<span style='%s'>%s</span>", style, string);
    }
    
    protected String escape(String token, NType type) {
        if (type == NType.leftAngleBracketType) return "<";
        if (type == NType.rightAngleBracketType) return ">";
        if (type == NType.ampersandType) return "&";
        return token;
    }
}    

which is like a state machine or something?


Bookmark blog post: del.icio.us del.icio.us Digg Digg DZone DZone Furl Furl Reddit Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment

  • Be careful what you wish for, you may get it. My experience with delegates was in doing XMLSchema validation in C#, the reasonable approach there being to collect up all the errors and then display them. I had to add this collection to my main class instead of encapsulating it nicely in an inner class. Well, I did encapsulate it, but that wasn't what the paradigm invited me to do...

    Posted by: tobega on October 18, 2006 at 12:55 AM

  • These member references would be more usable if properties also were supported (obj#name instead of obj#getName), not only as a convention, but as a language construct.

    Posted by: ronaldtm on October 18, 2006 at 06:40 AM

  • What do I want that C# has? Operator overloading. Engineers and other technical computing folks everywhere would rejoice.

    Posted by: jeeky on October 18, 2006 at 07:22 PM

  • For that reason exists JCP ...just propose your best thoughts and become famous and happy .))

    Posted by: felipegaucho on October 18, 2006 at 11:23 PM

  • While some of the features might be useful .. i would regret those - i do not want to end up having to read/write java code which looks like C++ when writing Windows GUIs... i know beauty lies in the eye of the beholder but keeping things simple should be the main goal, not only for aethetic reasons...

    Posted by: jkleemann on October 19, 2006 at 12:46 AM

  • I believe in clarity rather than conciseness. The main goal though is toolability for property references et al, and also convenience for event handler registration and EDT programming using method references - because anonymous classes have their limitations. Anyway that might be addressed by Closures, in which case that just leaves property references. I'll be publishing an article soon (it's almost finished) which looks at EDT programming, inter alia using CGLIB rather than dynamic proxies - that solves the EDT programming problem for me, but i would prefer delegates aka method references. For properties, I'm relatively happy with using enums for this, as presented here.

    Posted by: evanx on October 19, 2006 at 01:13 AM





Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds