<?xml version="1.0" encoding="utf-8" ?>

<rss version="2.0" 
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
   xmlns:admin="http://webns.net/mvcb/"
   xmlns:dc="http://purl.org/dc/elements/1.1/"
   xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
   xmlns:wfw="http://wellformedweb.org/CommentAPI/"
   xmlns:content="http://purl.org/rss/1.0/modules/content/"
   >
<channel>
    <title>Hulles - NetBeans</title>
    <link>http://hulles.supersized.org/</link>
    <description>generating stack traces so you don't have to</description>
    <dc:language>en</dc:language>
    <generator>Serendipity 1.3-alpha1 - http://www.s9y.org/</generator>
    <pubDate>Wed, 14 Jan 2009 06:43:27 GMT</pubDate>

    <image>
        <url>http://hulles.supersized.org/templates/default/img/s9y_banner_small.png</url>
        <title>RSS: Hulles - NetBeans - generating stack traces so you don't have to</title>
        <link>http://hulles.supersized.org/</link>
        <width>100</width>
        <height>21</height>
    </image>

<item>
    <title>Tips on Using Lookup in NetBeans</title>
    <link>http://hulles.supersized.org/archives/23-Tips-on-Using-Lookup-in-NetBeans.html</link>
            <category>NetBeans</category>
    
    <comments>http://hulles.supersized.org/archives/23-Tips-on-Using-Lookup-in-NetBeans.html#comments</comments>
    <wfw:comment>http://hulles.supersized.org/wfwcomment.php?cid=23</wfw:comment>

    <slash:comments>4</slash:comments>
    <wfw:commentRss>http://hulles.supersized.org/rss.php?version=2.0&amp;type=comments&amp;cid=23</wfw:commentRss>
    

    <author>nospam@example.com (Hulles)</author>
    <content:encoded>
    &lt;p&gt;
Recently I decided I had better bite the bullet and share with you some of the results of my experimentation with the &lt;strong&gt;Lookup&lt;/strong&gt; functionality in NetBeans. I have always found the Lookup documentation confusing, and since the Lookup implementation itself seems to have evolved over the years it is difficult to separate out the currently-preferred Lookup syntax and usage from that of previous incarnations. So here are a couple tips that I discovered that might help you if you&#039;re in the same boat.
&lt;/p&gt;&lt;br /&gt;&lt;p&gt;
First, just a bit about what Lookup is and what it does: Lookup is designed to decouple providers and consumers of functionality within a NetBeans application. To borrow (I believe) Tim Boudreau&#039;s example, if you have a module &lt;em&gt;SpellCheck&lt;/em&gt; that needs to look up words in a set of dictionaries and determine if a given word is spelled correctly, you can use the Lookup function to determine &lt;em&gt;&lt;strong&gt;at run time&lt;/strong&gt;&lt;/em&gt; what dictionary modules are available to use. &lt;em&gt;SpellCheck&lt;/em&gt; does not need to know at compile time what providers will be available; it just defines an interface that can be implemented by anybody who cares to and at run time looks around to see who showed up.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;
If you&#039;ve done a lot of development, you understand the beauty of this: I develop a module &lt;em&gt;SpellCheck&lt;/em&gt;, I provide a dictionary specification in the form of a public interface, and you can write a module &lt;em&gt;BritishEnglishDict&lt;/em&gt; that implements the interface. I then go on to create a module suite called &lt;em&gt;FancyEditor&lt;/em&gt; that includes my spiffy little &lt;em&gt;SpellCheck&lt;/em&gt; module and your module in it as well if I think Brits might be using it and if they sent me a cheque. When &lt;em&gt;FancyEditor&lt;/em&gt; is run, within &lt;em&gt;SpellCheck&lt;/em&gt; there is a Lookup statement that grabs all available implementing modules of the dictionary interface, including yours, and gives them to &lt;em&gt;SpellCheck&lt;/em&gt; to use in looking up the spelling of a given word. Neither you nor I need to know anything about the other&#039;s module other than the contract provided by the interface. Both compile independently in NetBeans just fine without the presence of the other, thanks to Lookup.
&lt;/p&gt;&lt;br /&gt;&lt;p&gt;
So how do you implement Lookup? That&#039;s a harder question because there are a lot of ways to do it. I found at least three in my research:  cookies, META-INF/Services, and layer file. Of the three, you should use the &lt;strong&gt;layer file&lt;/strong&gt; approach unless you need a) backwards compatibility with previous NetBeans versions (cookies) or b) compatibility with JDK ServiceProvider syntax (META-INF/Services). Assuming that neither of these conditions is the case, use the layer file as it seems to be the preferred way to do things these days in NetBeans country. (And if I&#039;m wrong, please tell me, it&#039;s difficult to stay current if you&#039;re a rugged lone-wolf renegade like me.)
&lt;/p&gt;&lt;br /&gt;&lt;p&gt;
By the way, you might notice that I&#039;m trying to speak of &quot;providers&quot; and &quot;consumers&quot; without talking about services here. That&#039;s because it&#039;s easy to confuse JDK ServiceProvider services, NetBeans Lookup &quot;services&quot;, and the myriad of other services available out there. So forget about the word &quot;services&quot; and let us never mention it again. Tough to find a good synonym though, but we&#039;ll get through this.
&lt;/p&gt;&lt;br /&gt;&lt;p&gt;
In this article (which is already a lot longer than I intended!) I will only discuss the layer file approach. The layer file is the means by which you tell NetBeans Lookup that your module &lt;em&gt;BritishEnglishDict&lt;/em&gt; can provide dictionary functionality as specified in my interface.
All you need to do is this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a public interface in the consumer module (&lt;em&gt;SpellCheck&lt;/em&gt; in my example) that defines what is needed.&lt;/li&gt;
&lt;li&gt;Add a lookup statement to the consumer to call for modules that implement the interface.&lt;/li&gt;
&lt;li&gt;Create a provider module (&lt;em&gt;BritishEnglishDict&lt;/em&gt;) that implements the interface.&lt;/li&gt;
&lt;li&gt;Create a layer file in the provider that announces to NetBeans (and Lookup) that our provider module implements the interface.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;
If all goes well, your brand new module suite will function perfectly and you can write ten more dictionary provider modules for various dialects of English without changing a thing in the consumer module.
&lt;/p&gt;
&lt;br /&gt;&lt;h3&gt;How To Make All Go Well&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;
To illustrate with some concrete code, first let&#039;s specify the interface in a hypothetical &lt;em&gt;SpellCheck&lt;/em&gt; module that will be checking the spelling of some imaginary documents :
&lt;/p&gt;
&lt;strong&gt;&lt;pre&gt;
package org.hulles.spellcheck;

public interface SpellDictionary {

    public boolean isCorrect(String word);

}
&lt;/pre&gt;&lt;/strong&gt;
&lt;p&gt;
Not much to it, for our example purposes. We just want to query a set of dictionaries and find if our word is spelled correctly. Next, let&#039;s add our Lookup to a class in &lt;em&gt;SpellCheck&lt;/em&gt;:&lt;/p&gt;
&lt;strong&gt;&lt;pre&gt;
...
public List&amp;lt;SpellDictionary&amp;gt; getDictionaries() {
    Collection&amp;lt;? extends SpellDictionary&amp;gt; dicts;

    StatusDisplayer.getDefault().setStatusText(&quot;Loading dictionaries....&quot;);
    dicts = Lookup.getDefault().lookupAll(SpellDictionary.class);
    return new ArrayList&amp;lt;SpellDictionary&amp;gt;(dicts);
}
...
&lt;/pre&gt;&lt;/strong&gt;
&lt;p&gt;
In my example we&#039;re using the &quot;lookupAll&quot; method to get all implementations of our interface but there are variations that you can use as well to get, for example, either zero or one implementation. And also notice there is no &quot;Lookup.Result&quot; in there either. We&#039;ll stick with &quot;lookupAll&quot; to keep it simple. You might also note the status message; while not strictly relevant to what we&#039;re discussing it&#039;s such a damn good use of the NetBeans platform that I left it in anyway.
&lt;/p&gt;&lt;br /&gt;&lt;p&gt;
Now we&#039;re done with the consumer module. Let&#039;s move to the provider module, &lt;em&gt;BritishEnglishDict&lt;/em&gt;. It&#039;s pretty simple as well -- it contains one class that implements the SpellDictionary interface, called &quot;MyOED&quot;:&lt;/p&gt;
&lt;strong&gt;&lt;pre&gt;
package org.hulles.dictionaryprovider;

import org.hulles.spellcheck.SpellDictionary;

public class MyOED implements SpellDictionary {

    public boolean isCorrect(String word) {
        return (&quot;cheque&quot;.equals(word));
    }

}
&lt;/pre&gt;&lt;/strong&gt;
&lt;p&gt;
Great! Now we have a provider that, in spite of its Oxonian title, just fulfills its contract with the interface by returning true if the word passed to it is &#039;cheque&#039;. That&#039;s good enough for our purposes. The only step remaining is to announce our new spelling dictionary to the world by adapting the layer file for the &quot;org.hulles.dictionaryprovider&quot; package.
&lt;/p&gt;&lt;br /&gt;&lt;p&gt;
The layer file that I&#039;m talking about is a file generally called &quot;layer.xml&quot; in NetBeans and is an XML file that informs the NetBeans platform of, well, stuff pertaining to its owning module. See other documentation wherever you can find it for more information on this; further discussion here is beyond our scope. The layer.xml file can be created automatically when you create the module in NetBeans, and if you think you&#039;re going to use one you should check the appropriate box at creation time to let NetBeans do this for you. If you didn&#039;t you can create your own, but &lt;strong&gt;be sure to add it to the module&#039;s  manifest&lt;/strong&gt; by adding a line to the manifest similar to:&lt;/p&gt;
&lt;strong&gt;&lt;pre&gt;
OpenIDE-Module-Layer: org/hulles/dictionaryprovider/layer.xml
&lt;/pre&gt;&lt;/strong&gt;&lt;p&gt;
If you don&#039;t do this, NetBeans won&#039;t find your layer file and none of these great Lookup gizmos will work correctly. If you generate it when you create the module, this is added to the manifest automatically.
&lt;/p&gt;&lt;br /&gt;&lt;p&gt;
To add the necessary declaration for Lookup to find your provider, in the layer.xml file you should add lines similar to the following to whatever may already be in there:&lt;/p&gt;
&lt;strong&gt;&lt;pre&gt;
&amp;lt;folder name=&quot;Services&quot;&amp;gt;
&amp;lt;folder name=&quot;Hidden&quot;&amp;gt;
&amp;lt;file name=&quot;org-hulles-dictionaryprovider-MyOED.instance&quot;&amp;gt;
&amp;lt;attr name=&quot;instanceClass&quot;
    stringvalue=&quot;org.hulles.dictionaryprovider.MyOED&quot;/&amp;gt;
&amp;lt;attr name=&quot;instanceOf&quot;
    stringvalue=&quot;org.hulles.spellcheck.SpellDictionary&quot;/&amp;gt;
&amp;lt;attr name=&quot;position&quot; intvalue=&quot;400&quot;/&amp;gt;
&amp;lt;/file&amp;gt;
&amp;lt;/folder&amp;gt;
&amp;lt;/folder&amp;gt;
&lt;/pre&gt;&lt;/strong&gt;&lt;p&gt;
I didn&#039;t format the XML very nicely here because I don&#039;t want the lines to run too long (!), but you can automatically format the XML in your own file by right-clicking in the editor and selecting &quot;Format&quot;.  Formatting aside, you can see that we&#039;re just informing the NetBeans platform that our class MyOED exists as an implementation of SpellDictionary. This is all Lookup needs to find it within our module suite. Simple, no? By using a layer file, &lt;em&gt;you don&#039;t need to use cookies nor do you need to use a &quot;META-INF/Services&quot; folder&lt;/em&gt;. It works just fine as it is.&lt;/p&gt;
&lt;br /&gt;&lt;p&gt;
I should also mention that you can include more than one provider class in your module. If you should write another class called, say, &quot;MyWebsters&quot; in the same module, you just need to add another definition to the &quot;Services/Hidden&quot; structure in your layer file. This was not crystal clear to me from the documentation until I tried it, but it works just fine. Your new layer file would then contain:
&lt;strong&gt;&lt;pre&gt;
&amp;lt;folder name=&quot;Services&quot;&amp;gt;
&amp;lt;folder name=&quot;Hidden&quot;&amp;gt;
&amp;lt;file name=&quot;org-hulles-dictionaryprovider-MyOED.instance&quot;&amp;gt;
&amp;lt;attr name=&quot;instanceClass&quot;
    stringvalue=&quot;org.hulles.dictionaryprovider.MyOED&quot;/&amp;gt;
&amp;lt;attr name=&quot;instanceOf&quot;
    stringvalue=&quot;org.hulles.spellcheck.SpellDictionary&quot;/&amp;gt;
&amp;lt;attr name=&quot;position&quot; intvalue=&quot;400&quot;/&amp;gt;
&amp;lt;/file&amp;gt;
&amp;lt;file name=&quot;org-hulles-dictionaryprovider-MyWebsters.instance&quot;&amp;gt;
&amp;lt;attr name=&quot;instanceClass&quot;
    stringvalue=&quot;org.hulles.dictionaryprovider.MyWebsters&quot;/&amp;gt;
&amp;lt;attr name=&quot;instanceOf&quot;
    stringvalue=&quot;org.hulles.spellcheck.SpellDictionary&quot;/&amp;gt;
&amp;lt;attr name=&quot;position&quot; intvalue=&quot;300&quot;/&amp;gt;
&amp;lt;/file&amp;gt;
&amp;lt;/folder&amp;gt;
&amp;lt;/folder&amp;gt;
&lt;/pre&gt;&lt;/strong&gt;&lt;p&gt;
Of course you could also put your &quot;MyWebsters&quot; class into a different module, and probably should if your module is called &quot;BritishEnglishDict&quot; to avoid confusion. If you were to do this, I might then include both of your modules in my suite to check my word against both of your dictionaries. In this case, the position attribute in the layer.xml file comes into play. Lookup will return its results &lt;em&gt;in the order specified by the position attribute in the layer file if it is present&lt;/em&gt;. Thus, my spell checker would first check to see if its word was in &quot;MyWebsters&quot;, then it would check &quot;MyOED&quot; (via the List implementation in the example). There. We&#039;re done!
&lt;/p&gt;
&lt;br /&gt;&lt;h3&gt;Too Bad It Doesn&#039;t Work&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;
If you followed the above steps exactly, your Lookup should work just fine. But if it doesn&#039;t, it&#039;s harder than hell to debug it because the compiler doesn&#039;t know about the relationship between the provider and the consumer! Of course this is the trade-off for the decoupling we sought, but it can certainly make our life difficult until we get it right. And that&#039;s the real reason I&#039;m writing this.
&lt;/p&gt;&lt;br /&gt;&lt;p&gt;
I actually modified a spell checker recently (Jazzy) to work with an application, and I could not get my Lookup to find my providers no matter what I tried. This is how I learned the things I did about Lookup that I&#039;ve been discussing in this article, but it&#039;s all to no avail if the sucker don&#039;t work! Finally, after much agony and shouting of &quot;Ogg Vorbis!&quot; and &quot;Bogofilter!&quot;, I realized that my provider modules were declared as type &quot;Autoload&quot; in the &quot;API Versioning&quot; section of the module properties. This meant that the modules weren&#039;t being loaded because there was no direct invocation of the provider modules anywhere, and modules of type &quot;Autoload&quot; are only loaded when they are invoked. I did away with direct references to the code in the providers when I used Lookup, so when the consumer called for providers it was whistling in the dark -- none were loaded. (The layer file for a module is only added to the application&#039;s layers when the module is loaded.) I switched the modules&#039; types to &quot;Regular&quot;, cleaned and rebuilt the modules, and it worked wonderfully at last. But to find this I really had to understand how it all worked, so I thought I would once again try to pass the hard-won knowledge along to you. Hope it helps someone!&lt;/p&gt;&lt;br /&gt; 
    </content:encoded>

    <pubDate>Wed, 14 Jan 2009 07:43:27 +0100</pubDate>
    <guid isPermaLink="false">http://hulles.supersized.org/archives/23-guid.html</guid>
    
</item>
<item>
    <title>...And Sorry For The Long Lines!</title>
    <link>http://hulles.supersized.org/archives/22-...And-Sorry-For-The-Long-Lines!.html</link>
            <category>NetBeans</category>
    
    <comments>http://hulles.supersized.org/archives/22-...And-Sorry-For-The-Long-Lines!.html#comments</comments>
    <wfw:comment>http://hulles.supersized.org/wfwcomment.php?cid=22</wfw:comment>

    <slash:comments>0</slash:comments>
    <wfw:commentRss>http://hulles.supersized.org/rss.php?version=2.0&amp;type=comments&amp;cid=22</wfw:commentRss>
    

    <author>nospam@example.com (Hulles)</author>
    <content:encoded>
    I try to remember to be RSS feed-friendly but my blog site editor wraps the lines when I preview the post and I have to manually cut them up for them to display correctly when I post. Obviously I forgot to do that with the last entry. That&#039;s probably why I&#039;m on the blutwurst-refried beans-watery beer diet: it was preemptive retribution.

-- Hulles
 
    </content:encoded>

    <pubDate>Sat, 03 Jan 2009 06:52:46 +0100</pubDate>
    <guid isPermaLink="false">http://hulles.supersized.org/archives/22-guid.html</guid>
    
</item>
<item>
    <title>NB 6.5 Twitter SaaS Bug</title>
    <link>http://hulles.supersized.org/archives/21-NB-6.5-Twitter-SaaS-Bug.html</link>
            <category>NetBeans</category>
    
    <comments>http://hulles.supersized.org/archives/21-NB-6.5-Twitter-SaaS-Bug.html#comments</comments>
    <wfw:comment>http://hulles.supersized.org/wfwcomment.php?cid=21</wfw:comment>

    <slash:comments>0</slash:comments>
    <wfw:commentRss>http://hulles.supersized.org/rss.php?version=2.0&amp;type=comments&amp;cid=21</wfw:commentRss>
    

    <author>nospam@example.com (Hulles)</author>
    <content:encoded>
    &lt;em&gt;This post concerns a bug in the Twitter SaaS service in NetBeans 6.5.&lt;/em&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;p&gt;
If you&#039;re using or have tried to use the Twitter SaaS service in NetBeans 6.5, you may have encountered a bug in NetBeans that gives you an error that looks something like this:
&lt;/p&gt;
&lt;pre&gt;
javax.xml.bind.UnmarshalException: unexpected element (uri:&quot;&quot;, local:&quot;nilclasses&quot;).
            Expected elements are &amp;lt;{}authenticated&amp;gt;,&amp;lt;{}authorized&amp;gt;,&amp;lt;
            {}direct-messages&amp;gt;,&amp;lt;{}direct_message&amp;gt;,&amp;lt;{}friends&amp;gt;,&amp;lt;
            {}hash&amp;gt;,&amp;lt;{}nil-classes&amp;gt;,&amp;lt;{}ok&amp;gt;,
            &amp;lt;{}status&amp;gt;,&amp;lt;{}statuses&amp;gt;,&amp;lt;{}user&amp;gt;
        at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext...
        at com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader.reportError(Lo...
        at com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader.reportError(Lo...
        at com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader.reportUnexpe...
        at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContex...
        at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContex...
...
&lt;/pre&gt;
&lt;p&gt;
I &lt;a onclick=&quot;javascript:urchinTracker(&#039;/extlink/www.netbeans.org/issues/show_bug.cgi?id=155882&#039;);&quot; href=&quot;http://www.netbeans.org/issues/show_bug.cgi?id=155882&quot; title=&quot;NetBeans bug report&quot; target=&quot;_blank&quot;&gt;reported the bug&lt;/a&gt; via the official NetBeans channel but it&#039;s my first occasion to do that. Hopefully I did it correctly and will not be harshly reprimanded by the &lt;strong&gt;NetBeans Ministry of Love&lt;/strong&gt; and put on a diet of blutwurst, refried beans and watery beer. Although come to think of it I&#039;m on that diet anyway; I must have done something else bad recently to warrant it.
&lt;/p&gt;&lt;br /&gt;&lt;p&gt;
The error results from a small bug in the updated Twitter SaaS service code in NetBeans 6.5. It occurs when you (for example) try to get direct messages and there are no new ones. Under these circumstances Twitter now replies with a 200 (everything went swell) code but returns NilClasses, whereas it used to give a 310 error code. I can only imagine that someone somewhere is trying to migrate away from returning error codes when things happen normally so that consumer code doesn&#039;t throw exceptions.
&lt;/p&gt;&lt;br /&gt;&lt;p&gt;
Speaking of which, if you mask out the exception handling in the caller function &lt;strong&gt;you won&#039;t see the above message&lt;/strong&gt;, you&#039;ll just get anomalous results in your direct messages (e.g.). This drove me crazy for a couple of days  until I tracked down the result. &quot;Bogofilter!&quot; I muttered. &quot;Ogg Vorbis!&quot; (I swear when I&#039;m frustrated.) Hence this short article to pass along the hard-won information.
&lt;/p&gt;&lt;br /&gt;&lt;p&gt;
I am also including a modified &lt;a href=&quot;http://hulles.supersized.org/uploads/canary/TwitterResponse.jar&quot; title=&quot;TwitterResponse.jar&quot;&gt;TwitterResponse.jar&lt;/a&gt; that you can plug into your code as a workaround until the bug is fixed by the NetBeans folks. Please note that you &lt;strong&gt;use it at your own risk&lt;/strong&gt;. It was a simple change and I&#039;ve used it without a hitch many many times since I created it, but it hasn&#039;t gone through the (presumably) rigorous QA process that happens at NetBeans HQ, wherever the hell that is. So my recommendation is that you should only download and install the jar if you work in a production environment and need the fix to stay productive; otherwise I strongly urge you to wait for the official fix. Also, if you don&#039;t already know what to do with the attached jar file you should probably wait as well. This patch is just to keep the fires burning for &lt;strong&gt;American industry&lt;/strong&gt; until the real fix. You non-Americans can use the patch as well of course but you should delay installing it for a bit so that we in the United States can get a much-needed head start toward regaining our technical edge.
&lt;/p&gt;&lt;br /&gt;&lt;p&gt;
I hope this helps any others out there (American or not) who are currently muttering &quot;Bogofilter!&quot; to themselves. Let me know how it goes. Namaste.
&lt;/p&gt;&lt;br /&gt;
-- Hulles
&lt;br /&gt;&lt;br /&gt; 
    </content:encoded>

    <pubDate>Sat, 03 Jan 2009 05:06:55 +0100</pubDate>
    <guid isPermaLink="false">http://hulles.supersized.org/archives/21-guid.html</guid>
    
</item>
<item>
    <title>Guerrilla Software Developers' Reality Check</title>
    <link>http://hulles.supersized.org/archives/20-Guerrilla-Software-Developers-Reality-Check.html</link>
            <category>NetBeans</category>
    
    <comments>http://hulles.supersized.org/archives/20-Guerrilla-Software-Developers-Reality-Check.html#comments</comments>
    <wfw:comment>http://hulles.supersized.org/wfwcomment.php?cid=20</wfw:comment>

    <slash:comments>0</slash:comments>
    <wfw:commentRss>http://hulles.supersized.org/rss.php?version=2.0&amp;type=comments&amp;cid=20</wfw:commentRss>
    

    <author>nospam@example.com (Hulles)</author>
    <content:encoded>
    &lt;br /&gt;&lt;p&gt;
In my last two articles (&lt;a href=&quot;http://hulles.supersized.org/archives/18-NetBeans-Tip-Prism-Break.html&quot; title=&quot;Link to earlier blog article&quot; target=&quot;_blank&quot;&gt;NetBeans Tip: Prism Break&lt;/a&gt; and &lt;a href=&quot;http://hulles.supersized.org/archives/19-More-About-Prism-And-NetBeans.html&quot; title=&quot;Link to another earlier blog article&quot; target=&quot;_blank&quot;&gt;More About Prism And NetBeans&lt;/a&gt;) I discussed using the Mozilla Labs &lt;a onclick=&quot;javascript:urchinTracker(&#039;/extlink/labs.mozilla.com/projects/prism/&#039;);&quot; href=&quot;http://labs.mozilla.com/projects/prism/&quot; title=&quot;Link to Prism project at Mozilla Labs&quot; target=&quot;_blank&quot;&gt;Prism&lt;/a&gt; application to create dedicated &quot;mini-browser&quot; windows to reduce the overhead involved in (e.g.) looking up JavaDocs within the NetBeans IDE. My primary stated reason for using Prism from within NetBeans was to improve the performance of &lt;strong&gt;Lucille II&lt;/strong&gt;, my aging laptop, bless her little 1.6GHz CPU.
&lt;/p&gt;&lt;br /&gt;&lt;p&gt;
However, one mustn&#039;t lose sight of one&#039;s overall goals as one mucks about in the fascinating but stinky cesspool of software development, and if results matter the number one rule for improving the performance of any piece of software should be: &lt;strong&gt;throw hardware at it.&lt;/strong&gt; This is because since the 70&#039;s hardware costs have trended dramatically downward and software (i.e. people) costs have done the reverse until recently. It is nearly always a better idea economically to upgrade hardware before even &lt;em&gt;thinking&lt;/em&gt; about software solutions, let alone discussing such solutions, let alone actually implementing them.
&lt;/p&gt;&lt;br /&gt;&lt;p&gt;
To reinforce this point, I&#039;d like to point out that a certain unnamed consumer electronics retail chain is currently offering what seems to be a Best Buy for laptop owners like myself: 1GB memory modules for US$19.99. To max out Lucille&#039;s memory at 2GB (currently she packs 512MB under her skirt) costs under US$40! That is substantially less than it cost me to write this blog entry in terms of person costs, assuming that I had something better to do.
&lt;/p&gt;&lt;br /&gt;&lt;p&gt;
The moral of the story is that if you&#039;re a guerrilla software developer or otherwise work in the real world you should keep the big picture in mind and Do The Right Thing, no matter how attractive it might otherwise be to f*** around with the latest and greatest SSB or any of my other blog posts for that matter (except this one). If on the other hand you work for &lt;strong&gt;Sun&lt;/strong&gt; you probably couldn&#039;t care less. I understand they actually &lt;em&gt;pay&lt;/em&gt; you guys to write blogs! (Hire me and I&#039;ll quit giving you shit and go back to making fun of Microsoft Windows operating systems.)
&lt;/p&gt;&lt;br /&gt;&lt;p&gt;
Namaste and Happy New Year!
&lt;/p&gt;&lt;br /&gt;
-- Hulles
&lt;br /&gt;&lt;br /&gt;&lt;p&gt;
P.S. Of course the purpose for my writing these articles is to entertain and elucidate, as opposed to trying to stroke and bore your &lt;strong&gt;Sony VAIO&lt;/strong&gt; so that it kicks the ass of all the other laptops in your coffee shop. But still... US$40! I&#039;m off to buy memory, which at my age is an unexpected luxury.
&lt;/p&gt;&lt;br /&gt; 
    </content:encoded>

    <pubDate>Thu, 01 Jan 2009 05:23:22 +0100</pubDate>
    <guid isPermaLink="false">http://hulles.supersized.org/archives/20-guid.html</guid>
    
</item>
<item>
    <title>More About Prism And NetBeans</title>
    <link>http://hulles.supersized.org/archives/19-More-About-Prism-And-NetBeans.html</link>
            <category>NetBeans</category>
    
    <comments>http://hulles.supersized.org/archives/19-More-About-Prism-And-NetBeans.html#comments</comments>
    <wfw:comment>http://hulles.supersized.org/wfwcomment.php?cid=19</wfw:comment>

    <slash:comments>0</slash:comments>
    <wfw:commentRss>http://hulles.supersized.org/rss.php?version=2.0&amp;type=comments&amp;cid=19</wfw:commentRss>
    

    <author>nospam@example.com (Hulles)</author>
    <content:encoded>
    &lt;p&gt;
As I mentioned in my last post, &lt;a href=&quot;http://hulles.supersized.org/archives/18-NetBeans-Tip-Prism-Break.html&quot; title=&quot;Link to Prism Break blog post&quot; target=&quot;_blank&quot;&gt;NetBeans Tip: Prism Break&lt;/a&gt;, the Prism application from Mozilla Labs has changed my (NetBeans) life. I can now use &lt;strong&gt;Lucille II&lt;/strong&gt;, my laptop, to actually do NetBeans development work in a productive and efficient manner. I have been using the Prism SSB a &lt;em&gt;lot&lt;/em&gt; from within the IDE for a couple of weeks now and I love it, particularly since finding out how to navigate back and forth between pages &lt;em&gt;[see &quot;special bonus tip&quot; at the bottom of the &lt;a href=&quot;http://hulles.supersized.org/archives/18-NetBeans-Tip-Prism-Break.html&quot; title=&quot;Link to Prism Break blog post&quot; target=&quot;_blank&quot;&gt;Prism Break&lt;/a&gt; article]&lt;/em&gt;. So, since I&#039;m still pretty excited about how well it works for me, I thought I&#039;d discuss it a little bit more in this follow-up post.
&lt;/p&gt;
&lt;br /&gt;&lt;h3&gt;Prism As A Self-Contained Browser&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;
In the closing paragraph of the last post I said, &quot;One thing I didn&#039;t mention earlier is that I think Prism will work as well with Internet Explorer as it does with Firefox.&quot; What I should have said was that &quot;Prism will work as well &lt;strong&gt;on a machine that uses&lt;/strong&gt; Internet Explorer as its default (or only) browser as well as one that uses Firefox.&quot; Prism seems to be a self-contained browser and does not appear to rely on the presence or absence of Firefox. I have been reluctant to de-install Firefox on any of my boxes to test this out but the more I have worked with it and researched it the more this seems to be the case. So I hope that clarifies what I stated in the previous article. And as I said there, please let me know of your experiences in this regard.
&lt;/p&gt;
&lt;br /&gt;&lt;h3&gt;Prism On Windows&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;
Since I published the Prism Break entry I decided to install Prism on &lt;strong&gt;Aspen&lt;/strong&gt;, a custom-built desktop PC that I have in my home office to provide extra heat in the winter. Aspen is a little less underpowered than Lucille II; as opposed to her it has Delrin&amp;reg; gears and discrete transistors and doesn&#039;t require a bevy, underdressed or otherwise. It boots both Windows 2000 and Ubuntu, so I fired up W2K and downloaded and installed Prism on it. It installed flawlessly. (You can&#039;t imagine how elated I am every time I get to say that.) When I started Prism up in W2K it gave me a slightly different dialog than the Ubuntu Linux one, viz:
&lt;/p&gt;&lt;br /&gt;
&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/blogger/ss1.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:200 --&gt;&lt;img width=&quot;90&quot; height=&quot;68&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/blogger/ss1.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;
&lt;br /&gt;&lt;br /&gt;&lt;p&gt;
One gets a few more options than in Linux, but it is fundamentally the same. I created a miniapp to open up Mozilla Labs and it worked just fine.
&lt;/p&gt;&lt;br /&gt;
&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/blogger/ss2.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:201 --&gt;&lt;img width=&quot;90&quot; height=&quot;68&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/blogger/ss2.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;
&lt;br /&gt;&lt;br /&gt;&lt;p&gt;
Is that great or what? Now I use Prism on Aspen all the time as well as on good old Lucille II. Incidentally, I don&#039;t seem to get nav buttons in W2K either but I&#039;m happy to say that the special bonus tip from last time works as well there as it does in Linux.
&lt;/p&gt;
&lt;br /&gt;&lt;h3&gt;Other Software Development Uses For Prism&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;
I&#039;ve found that, in addition to the intra-IDE JavaDoc usage I mentioned in the previous post, Prism is quite useful in other ways as I carefully craft clean custom code (and concatenate consonants) on my computer. For example with &lt;strong&gt;Sancho&lt;/strong&gt;, my Big NetBeans Platform Rich Client Application (BNPRCA), I need to be able to document changes that I make on a web site to be able to better communicate with my non-paying clients. To do this now I just leave a Prism window open to the web site and switch over to it whenever I need to. Because of Prism&#039;s dainty footprint I can afford to do this resource-wise, where once I couldn&#039;t and instead had to keep copious illegible paper notes laying around in my workspace for my cat to play in.
&lt;/p&gt;
&lt;br /&gt;&lt;p&gt;
Still other uses I&#039;ve found for Prism &lt;em&gt;vis-&amp;agrave;-vis&lt;/em&gt; software development include: keeping the old social networking applications open while I work (as I mentioned last time), having an on-line dictionary readily available to look up &quot;vis-&amp;agrave;-vis&quot;, tuning in to Last.fm (can&#039;t code without tunes!), and leaving a feed window open to catch the latest scoops in &lt;a onclick=&quot;javascript:urchinTracker(&#039;/extlink/planetnetbeans.org/&#039;);&quot; href=&quot;http://planetnetbeans.org/&quot; title=&quot;Link to Planet NetBeans&quot; target=&quot;_blank&quot;&gt;Planet NetBeans&lt;/a&gt;. And at this point I will somewhat shamefacedly admit that every time &lt;a href=&quot;http://hulles.supersized.org/&quot; title=&quot;Link to Hulles blog&quot; target=&quot;_blank&quot;&gt;Hulles&lt;/a&gt; pops up in Planet NetBeans I &lt;strong&gt;whoop with delight&lt;/strong&gt; and scare the shit out of my cat who then knocks more papers off my desk in retaliation. No one said the life of a renegade NetBeans Java programmer was going to be easy, I suppose. If they had I would have gotten it in writing then sued the pants off &#039;em.
&lt;/p&gt;
&lt;br /&gt;&lt;h3&gt;The Prism Firefox Add-In&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;
I didn&#039;t mention it before but there also exists a plug-in for Firefox called (I believe) &lt;strong&gt;Refractor&lt;/strong&gt; that can generate a Prism desktop miniapp for whatever web page you happen to be on at the time. I used this for a short while but I found that, as seldom as I used it, it didn&#039;t add much value compared to simply starting Prism from the desktop and filling in the dialog fields. Ironically, adding the Prism plug-in into Firefox makes Firefox just that much more bloated, which is why I started using Prism in the first place! So I 86&#039;ed the plug-in out of Firefox. You may like it however so give it a try if you want. It can be found in the normal Firefox add-in arena under Prism.
&lt;/p&gt;
&lt;br /&gt;&lt;h3&gt;Heh Heh. The Cat&#039;s Asleep On The Scanner.&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;
So that&#039;s it for this Prism follow-up. I&#039;m going to post it now and wait for it to show up in Planet NetBeans. Namaste and Happy New Year!
&lt;/p&gt;&lt;br /&gt;
-- Hulles

 
    </content:encoded>

    <pubDate>Tue, 30 Dec 2008 03:53:56 +0100</pubDate>
    <guid isPermaLink="false">http://hulles.supersized.org/archives/19-guid.html</guid>
    
</item>
<item>
    <title>NetBeans Tip: Prism Break</title>
    <link>http://hulles.supersized.org/archives/18-NetBeans-Tip-Prism-Break.html</link>
            <category>NetBeans</category>
    
    <comments>http://hulles.supersized.org/archives/18-NetBeans-Tip-Prism-Break.html#comments</comments>
    <wfw:comment>http://hulles.supersized.org/wfwcomment.php?cid=18</wfw:comment>

    <slash:comments>2</slash:comments>
    <wfw:commentRss>http://hulles.supersized.org/rss.php?version=2.0&amp;type=comments&amp;cid=18</wfw:commentRss>
    

    <author>nospam@example.com (Hulles)</author>
    <content:encoded>
    &lt;br /&gt;&lt;h3&gt;Let&#039;s Gain Ourselves An Extra Hour or Two of Productivity Per Day!&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;
Hoo boy and way doggies, are you going to thank me for this one, especially if you are like me and do most of your work on an underpowered laptop. I&#039;m not necessarily saying my laptop&#039;s &lt;em&gt;old&lt;/em&gt;, but it has big clanky iron gears and uses vacuum tubes and requires a bevy of underdressed 20-something brunettes to continually fan it with ostrich feathers to keep it cool. While there are some advantages to the last requirement, it is a non-trivial exercise for me to actually &lt;em&gt;go&lt;/em&gt; anywhere with my laptop because I have to wait forever while the bevy primps and gets ready to go out. Underdressing is an art, not a science, they tell me.
&lt;/p&gt;&lt;br /&gt;&lt;p&gt;
So what does that have to do with this NetBeans tip? Well, because my laptop is creaky and slow I find myself constantly &lt;strong&gt;avoiding looking up JavaDocs&lt;/strong&gt; from within the NetBeans IDE Java Editor no matter how much I might need the information. This is because it takes as long for my laptop to start up Firefox and display the JavaDocs as it does for the aforementioned bevy to get ready to go out. Mind you, I would never call NetBeans &lt;em&gt;fat&lt;/em&gt;, but it is certainly &lt;strong&gt;big-boned&lt;/strong&gt;, and Firefox is no anorexic supermodel either. Between them they suck resources like -- well, never mind, just take my word for it that they suck a lot of resources.
&lt;/p&gt;
&lt;br /&gt;&lt;h3&gt;Enter Prism. Alarums And Excursions.&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;
Because of all this you can imagine my elation when I accidentally stumbled across a simple, lightweight, dedicated browser application called &lt;a onclick=&quot;javascript:urchinTracker(&#039;/extlink/labs.mozilla.com/projects/prism/&#039;);&quot; href=&quot;http://labs.mozilla.com/projects/prism/&quot; title=&quot;Link to Prism project at Mozilla Labs&quot; target=&quot;_blank&quot;&gt;Prism&lt;/a&gt; the other day. Prism is a XULRunner-based project of &lt;a onclick=&quot;javascript:urchinTracker(&#039;/extlink/labs.mozilla.com/&#039;);&quot; href=&quot;http://labs.mozilla.com/&quot; title=&quot;Link to Mozilla Labs&quot; target=&quot;_blank&quot;&gt;Mozilla Labs&lt;/a&gt;, the people who bring you the Firefox browser among other things, and the goal of the project is to provide an application for your desktop that starts a small no-frills browser for a single purpose -- like displaying JavaDocs, just to pick an example at random. This concept is called a &lt;a onclick=&quot;javascript:urchinTracker(&#039;/extlink/en.wikipedia.org/wiki/Site-specific_browser&#039;);&quot; href=&quot;http://en.wikipedia.org/wiki/Site-specific_browser&quot; title=&quot;Link to Wikipedia SSB article&quot; target=&quot;_blank&quot;&gt;&quot;Site Specific Browser&quot; (SSB)&lt;/a&gt;. Perfect! I downloaded some Prism-started apps like Google Reader, Google Mail (GMail), Facebook and Twitter(!) to try out and they all have their own desktop menu shortcuts and they all bring up a lightweight dedicated browser window with just their own specific application web site in it. See this Twitter screenshot for an example:
&lt;/p&gt;&lt;br /&gt;
&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/blogger/Screenshot0.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:189 --&gt;&lt;img width=&quot;90&quot; height=&quot;67&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/blogger/Screenshot0.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;
&lt;br /&gt;&lt;br /&gt;&lt;p&gt;
Notice the lack of nearly all normal browser accoutrements like menus, tabs, etc. Excellent! For our purposes all we typically need to do is look at a damn JavaDoc, not shop for ostrich feathers on eBay (at least while we&#039;re working). Note also the picture of the distinguished-looking rogue in the corner. Looks hungry and thirsty, doesn&#039;t he? See &quot;Donate&quot; button. His children run naked and his cat needs scotch.
&lt;/p&gt;&lt;br /&gt;&lt;p&gt;
So here&#039;s what we&#039;re going to do: we&#039;re going to create ourselves a little mini-application to display JavaDocs (and any other web application started from NetBeans) in a Prism window instead of in our bloated browser. The steps that we will perform are these:
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Obtain the Prism application and install it on our computer.&lt;/li&gt;
&lt;li&gt;Create a dummy Prism instance that we can use for displaying JavaDocs.&lt;/li&gt;
&lt;li&gt;Add our new NetBeans Prism instance to the list of browsers in the NetBeans IDE.&lt;/li&gt;
&lt;li&gt;Test the new NetBeans Prism browser by looking up JavaDocs in the IDE.&lt;/li&gt;
&lt;li&gt;Sit back and feel smug.&lt;/li&gt;
&lt;/ol&gt;
&lt;br /&gt;&lt;h3&gt;Acquiring And Installing Prism&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;


The Prism application is free and available for download from Mozilla Labs at this URL: &lt;a onclick=&quot;javascript:urchinTracker(&#039;/extlink/labs.mozilla.com/projects/prism/&#039;);&quot; href=&quot;http://labs.mozilla.com/projects/prism/&quot; title=&quot;Link to Mozilla Labs Prism page&quot;&gt;http://labs.mozilla.com/projects/prism/&lt;/a&gt;. It is available for Windows, Mac OS X, and Linux. I personally use Ubuntu 8.10 Linux and I installed Prism from the Ubuntu Debian repository without any problems via the Applications menu &quot;Add/Remove...&quot; command, but your experience may vary. If you have trouble installing Prism look around on the Mozilla Labs site or in the Mozilla Wiki for help. As a last resort you can ask me, but I&#039;m not sure how much help I can offer in this.
&lt;/p&gt;&lt;br /&gt;&lt;p&gt;
At the same time you download and install Prism itself you may want to download other Prism-driven apps like the ones I mentioned earlier just to try them out. I particularly find the Twitter and FaceBook Prism applications useful so I can maintain contact with my vast extended social network (both people) while slaving away inside the big-boned NetBeans IDE.
&lt;/p&gt;&lt;br /&gt;&lt;p&gt;
Incidentally, you can also try out your new Prism gizmo to create your very own Prism launcher, simply by starting Prism. It will give you a window that looks like this:
&lt;/p&gt;&lt;br /&gt;
&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/blogger/Screenshot0A.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:190 --&gt;&lt;img width=&quot;90&quot; height=&quot;71&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/blogger/Screenshot0A.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;
&lt;br /&gt;&lt;br /&gt;&lt;p&gt;
You can then fill in the URL and name of &lt;strong&gt;whatever web site you choose&lt;/strong&gt; and Prism will place an icon on your desktop that opens the site in an SSB window like this:
&lt;/p&gt;&lt;br /&gt;
&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/blogger/Screenshot0B.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:188 --&gt;&lt;img width=&quot;90&quot; height=&quot;66&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/blogger/Screenshot0B.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;
&lt;br /&gt;&lt;br /&gt;&lt;p&gt;
Pretty nifty, isn&#039;t it? I can think of about a gazillion uses for this, but right now let&#039;s get back to our project at hand.
&lt;/p&gt;
&lt;br /&gt;&lt;h3&gt;Creating A NetBeans Prism Launcher&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;
This step is easy, and it&#039;s the same process as the one described in the previous section for creating your own Prism Launcher. First you need to start Prism:
&lt;/p&gt;&lt;br /&gt;
&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/blogger/Screenshot10.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:197 --&gt;&lt;img width=&quot;90&quot; height=&quot;68&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/blogger/Screenshot10.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;
&lt;br /&gt;&lt;br /&gt;&lt;p&gt;
In the spare and terse Prism window that comes up you need to enter a URL and a name for the NetBeans JavaDoc launcher:
&lt;/p&gt;&lt;br /&gt;
&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/blogger/Screenshot2.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:187 --&gt;&lt;img width=&quot;90&quot; height=&quot;71&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/blogger/Screenshot2.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;
&lt;br /&gt;&lt;br /&gt;&lt;p&gt;
I used a real JavaDoc local file URI in my example but you can actually use any URL; we&#039;re going to override it in the launcher later on. And by the way, while I called my launcher &quot;NetBeans JavaDoc&quot;, it should more precisely be called &quot;NetBeans Prism&quot; or something of that nature because NetBeans will use it to open &lt;strong&gt;all&lt;/strong&gt; web sites from within the IDE, not just JavaDocs.
&lt;/p&gt;&lt;br /&gt;&lt;p&gt;
Once you hit the &quot;OK&quot; button in the dialog, it will create a shortcut on your desktop to open the URL you specified inside a cute little Prism window. Next we&#039;re going to get the shortcut off the desktop and into an application start menu so we don&#039;t clutter up our desktop. This step is optional of course but I recommend it. In most operating systems you can accomplish this by dragging the icon from the desktop into your &quot;Start Menu&quot; or equivalent:
&lt;/p&gt;&lt;br /&gt;
&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/blogger/Screenshot14.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:199 --&gt;&lt;img width=&quot;90&quot; height=&quot;68&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/blogger/Screenshot14.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;
&lt;br /&gt;&lt;br /&gt;&lt;p&gt;
If you don&#039;t or can&#039;t do this last step however, don&#039;t worry about it. Everything should be fine if you just leave it on your desktop. You can even try deleting it altogether and seeing if the following steps work without any shortcut at all (in spite of what the Prism dialog says) but I haven&#039;t done this myself. I suspect it will work just fine anyway because I&#039;ve seen artifacts of deleted launchers lurking around in the Prism internal directory. Let me know if you try this and it works (or doesn&#039;t work, for that matter).
&lt;/p&gt;&lt;br /&gt;&lt;p&gt;
Once you&#039;re done, start the launcher from wherever you put it and you should get your chosen web site, a JavaDoc in my case, displayed in a Prism window like this:
&lt;/p&gt;&lt;br /&gt;
&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/blogger/Screenshot13.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:198 --&gt;&lt;img width=&quot;90&quot; height=&quot;68&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/blogger/Screenshot13.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;
&lt;br /&gt;
&lt;br /&gt;&lt;h3&gt;Adding The Prism Launcher To The IDE&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;
Now we&#039;re going to do the most difficult part: adding a browser definition to the NetBeans IDE. It really isn&#039;t all that difficult, especially for you since I&#039;m going to tell you how to do it. Of course &lt;em&gt;I&lt;/em&gt; had to hunt for a Prism command line syntax reference document that apparently doesn&#039;t exist so I once again had to dig into the source code to find it, but hey, that&#039;s why I have this blog, so I can share the hard-won information. &lt;em&gt;[Pats self on back, clears throat deprecatingly.]&lt;/em&gt;
&lt;/p&gt;&lt;br /&gt;&lt;p&gt; 
To add our new browser definition to the IDE, you should first open the properties window of the shortcut that you created in the previous step:
&lt;/p&gt;&lt;br /&gt;
&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/blogger/Screenshot6.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:196 --&gt;&lt;img width=&quot;90&quot; height=&quot;68&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/blogger/Screenshot6.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;
&lt;br /&gt;&lt;br /&gt;&lt;p&gt;
This will give you the correct Prism command line for your operating system. Copy the command line to the clipboard and paste it into a new text document. It should look something like this (all on one line), though it will vary with different operating systems and versions:&lt;/p&gt;&lt;br /&gt;
&lt;strong&gt;&lt;code&gt;xulrunner-1.9 /usr/share/prism/application.ini -webapp netbeans.javadoc@prism.app&lt;/code&gt;  
&lt;/strong&gt;&lt;br /&gt;&lt;p&gt;
Now start the NetBeans IDE, go into &quot;Tools / Options&quot; from the main menu, and click on the &quot;General&quot; icon.
&lt;/p&gt;&lt;br /&gt;
&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/blogger/Screenshot7.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:192 --&gt;&lt;img width=&quot;90&quot; height=&quot;68&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/blogger/Screenshot7.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;
&lt;br /&gt;&lt;br /&gt;&lt;p&gt;
Click the &quot;Edit...&quot; button next to the &quot;Web Browser&quot; combo box. In the next window, click the &quot;Add...&quot; button.
&lt;/p&gt;&lt;br /&gt;
&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/blogger/Screenshot8.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:191 --&gt;&lt;img width=&quot;90&quot; height=&quot;56&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/blogger/Screenshot8.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;
&lt;br /&gt;&lt;br /&gt;&lt;p&gt;
You should now be at a screen that looks similar to this:
&lt;/p&gt;&lt;br /&gt;
&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/blogger/Screenshot9.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:193 --&gt;&lt;img width=&quot;90&quot; height=&quot;56&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/blogger/Screenshot9.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;
&lt;br /&gt;&lt;br /&gt;&lt;p&gt;
Replace the default name with the same name that you used when you created your shortcut. From the text document containing your system&#039;s command line, copy the program portion of the command line into the &quot;Process&quot; field (&lt;strong&gt;xulrunner-1.9&lt;/strong&gt; in my case) and paste the rest of the command line into the &quot;Arguments&quot; field. In the &quot;Arguments&quot; field, add the following text to the end of the line:&lt;/p&gt;&lt;br /&gt;&lt;strong&gt;
&lt;code&gt;-uri {URL}&lt;/code&gt;&lt;/strong&gt;&lt;br /&gt;
&lt;p&gt;
This tells Prism to override the URL in its application definition with the one that NetBeans provides via the &quot;{URL}&quot; template, as it helpfully mentions in the forms &quot;Arguments Hint&quot; box.
&lt;/p&gt;&lt;br /&gt;
&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/blogger/Screenshot11.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:194 --&gt;&lt;img width=&quot;90&quot; height=&quot;56&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/blogger/Screenshot11.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;
&lt;br /&gt;&lt;br /&gt;&lt;p&gt;
Hit &quot;OK&quot; to save your new browser definition. Make sure that the new definition is selected in the &quot;Web Browser&quot; combo box. Close the Options window. You should be all set!
&lt;/p&gt;
&lt;br /&gt;&lt;h3&gt;Testing The New Browserito (Browserita?)&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;
To test your new Prism launcher, open any Java file in the editor, right-click on a class or method name, and choose &quot;Show JavaDoc&quot;. If the element has an associated JavaDoc, it should open in a Prism window like this:
&lt;/p&gt;&lt;br /&gt;
&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/blogger/Screenshot12.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:195 --&gt;&lt;img width=&quot;90&quot; height=&quot;66&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/blogger/Screenshot12.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;
&lt;br /&gt;&lt;br /&gt;&lt;p&gt;
&quot;Viola!&quot;, as we used to say in Iowa. You now have a lightweight JavaDoc browser at your beck and call within the IDE, ready to spring forth with speedy goodness to show you the errors of your coding ways. No more waiting for chubby old FireFox (or obese old Internet Explorer) to huff and puff into the foreground.
&lt;/p&gt;
&lt;br /&gt;&lt;h3&gt;But Now We Have A New Problem&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;
If your working machine is anything like my laptop &lt;strong&gt;Lucille II&lt;/strong&gt;, this tip should cut out at least an hour a day of nonproductive time when all is said and done, maybe more. See &quot;Donate&quot; button. Show &quot;Donate&quot; button to boss and explain new productivity gains. (Seriously.) But as foreshadowed by the title to this section, this leaves us with a whole new problem: our bevy of ostrich feather wavers now have an extra hour or two of free time and nothing to do. How to occupy them? This I leave to you.
&lt;/p&gt;
&lt;br /&gt;&lt;h3&gt;SPECIAL BONUS TIP!&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;
One Prism anomaly that I ran across in my experimentation, at least on my Linux system, is that even if you check the &quot;navigation buttons&quot; box on the Prism application creation dialog box &lt;strong&gt;you don&#039;t get navigation buttons&lt;/strong&gt; (i.e. back arrow and forward arrow) in your resulting Prism browser window. After trying everything I could think of I finally discovered that you can navigate backwards and forwards through visited web pages by holding down the &quot;Alt&quot; key and using the mouse scroll button. This works regardless of whether you checked the &quot;navigation buttons&quot; box at app creation time. And it&#039;s FAST! Try it. And that solves the one problem I was having with the Prism SSB, so now I&#039;m happy and I hope you are too.
&lt;/p&gt;
&lt;br /&gt;&lt;h3&gt;In Conclusion&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;
I hope you found this article useful and possibly even entertaining. I also hope it helps your productivity as much as it helped mine. One thing I didn&#039;t mention earlier is that I think Prism will work as well with Internet Explorer as it does with FireFox. I think this because of the Mozilla documentation, but I haven&#039;t tested it. Let me and others know of your experiences by commenting here. Good luck and good coding. Namaste.&lt;/p&gt;
 
    </content:encoded>

    <pubDate>Sun, 28 Dec 2008 05:09:18 +0100</pubDate>
    <guid isPermaLink="false">http://hulles.supersized.org/archives/18-guid.html</guid>
    
</item>
<item>
    <title>Canary III: It's Not A Turkey (Yet)</title>
    <link>http://hulles.supersized.org/archives/17-Canary-III-Its-Not-A-Turkey-Yet.html</link>
            <category>NetBeans</category>
    
    <comments>http://hulles.supersized.org/archives/17-Canary-III-Its-Not-A-Turkey-Yet.html#comments</comments>
    <wfw:comment>http://hulles.supersized.org/wfwcomment.php?cid=17</wfw:comment>

    <slash:comments>0</slash:comments>
    <wfw:commentRss>http://hulles.supersized.org/rss.php?version=2.0&amp;type=comments&amp;cid=17</wfw:commentRss>
    

    <author>nospam@example.com (Hulles)</author>
    <content:encoded>
    &lt;em&gt;&lt;strong&gt;Canary&lt;/strong&gt; is a lightweight Java desktop application that monitors the Twitter online message service. Links to the complete source code and a zipped jar are included at the end of the article. You can run the jar directly once you unzip it if you just want to try out the application or use it without futzing with the source code. Try &quot;java -jar [wherever you put the jar file]&quot; from your command line.&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;
&lt;p&gt;
Well, the holiday season is upon us here in the USA, a time when everyone seems to put on a few unneeded pounds. Since the Canary project seems to have become yet another part-time job for me that pays nothing, I figured I ought to make our tiny little bird a bit heavier as well by adding a few more enhancements to it and fixing a bug that I overlooked in the original Canary incarnation. 
&lt;/p&gt;
&lt;br /&gt;&lt;h3&gt;A Bug! [Audience Gasps]&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;
It seems that when I adapted this project from my original module implementation I forgot to include code that starts the query timer to wake Canary up to check statuses. This means that if you&#039;ve followed along so far, you&#039;ve probably been sitting patiently by your computer screen for the last several weeks waiting for your so-called friends to update their statuses and feeling like a complete loser. So let&#039;s fix that bug. And don&#039;t worry, you&#039;re not a loser. The reason I know this is: &lt;em&gt;Losers don&#039;t read Hulles, and Hulles doesn&#039;t read losers.&lt;/em&gt;
&lt;/p&gt;&lt;br /&gt;&lt;p&gt;
The code to start the timer is pretty simple. It lives in the initialization paragraph of CanaryView.java and looks like this:
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;pre&gt;
	...        
        queryInterval = pref.getInt(&quot;queryInterval&quot;, 300000);
        queryTimer = new Timer(queryInterval, new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                if (verbose) System.out.println(&quot;CanaryView: querying server from timer&quot;);
                refresh();
            }
        });
        queryTimer.setRepeats(true);
	...
        refresh();
        queryTimer.start();
	...
&lt;/pre&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;
Nothing to it. You just have to remember to include the code in your program is all. &lt;em&gt;[Embarrassed cough....]&lt;/em&gt;
&lt;/p&gt;
&lt;br /&gt;&lt;h3&gt;Making The Canary Hop&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;
&lt;em&gt;In this paragraph I originally intended to document a way for us to pop the Canary window up if it was iconified and we received a new message but I decided to cut that part out in the interest of time -- mine.  Sorry; it wasn&#039;t cool enough to make the cut though. If you try this on your own, you&#039;ll need to pop up the window when either friendsStatus or directMessages change; I lied last time when I said friendsStatus includes DMs. [Another embarrassed cough.]&lt;/em&gt;
&lt;/p&gt;&lt;br /&gt;
&lt;p&gt;&lt;em&gt;By the way, we could also make the window &quot;flash&quot; if its already displayed to signal a new message, possibly by changing the window focus, but I have not tried this.&lt;/em&gt;
&lt;/p&gt;

&lt;br /&gt;&lt;h3&gt;Making The Canary Chirp More&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;
As you can see, we&#039;re still not done beating the canary metaphor to death. Now we&#039;re going alter the way the verbose console messages work by changing the constant &quot;VERBOSE&quot; in CanaryView to a variable that we get from CanaryApp. We&#039;ll also change CanaryApp by adding Java code to look for a &quot;--verbose&quot; or &quot;-v&quot; argument in the command line. Since Canary is built on the &quot;Java Desktop Application&quot; framework, it may be non-trivial to figure out how to grab the command line args (unless of course you&#039;re a RTFM&#039;er). One does this by overriding the &quot;initialize&quot; method in the Application object. We are also adding a property &quot;getter&quot; to CanaryApp to return the value of the &quot;verbose&quot; variable, and adding a small method to print the command line usage message to the console in something close to the standard that most console-based programs use.
&lt;p&gt;&lt;br /&gt;
Incidentally, one of the nice things about specifying the verbose option on the command line is that you can open the properties window of the Canary project in the NetBeans IDE and put the &quot;--verbose&quot; option in the Arguments field (see screenshot) so that it applies when you run Canary from the IDE but does not take effect when you run Canary as a standalone application.
&lt;/p&gt;&lt;br /&gt;
&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/canary/ss2_002.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:177 --&gt;&lt;img width=&quot;90&quot; height=&quot;61&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/canary/ss2_002.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;
&lt;p&gt;&lt;strong&gt;&lt;pre&gt;
    @Override
    protected void initialize(String[] args) {
        for (String arg : args) {
            if ((&quot;--verbose&quot;.equals(arg)) || (&quot;-v&quot;.equals(arg))) {
                verbose = true;
            } else if ((&quot;--help&quot;.equals(arg)) || (&quot;-h&quot;.equals(arg))) {
                showUsage();
                exit();
            } else {
                System.err.println(&quot;Error: invalid option for Canary_II&quot;);
                showUsage();
                exit();
            }
        }
    }

    private void showUsage() {
        System.out.println();
        System.out.println(&quot;Usage: Canary_II [options]&quot;);
        System.out.println();
        System.out.println(&quot;Options:&quot;);
        System.out.println(&quot;\t--verbose, -v\tPrint messages of interest on stdout&quot;);
        System.out.println();
    }

    protected boolean beVerbose() {
        return verbose;
    }
&lt;/pre&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;br /&gt;&lt;h3&gt;Making The Canary Title Metaphor Finally Die&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;
Next we&#039;re going to add an item to the menu bar that allows us to force a Twitter status check without waiting for the timer to expire. I thought about making this a button, but I like having the status screen uncluttered so I made it a menu item instead, called &quot;Check Now&quot;. I also added mnemonics and accelerators to the menu items. (For some reason they keep disappearing though, wonder if that&#039;s fixed in 6.5?) The design screen for CanaryView looks like this once you add the menu item:
&lt;/p&gt;&lt;br /&gt;
&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/canary/ss2_000.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:176 --&gt;&lt;img width=&quot;90&quot; height=&quot;61&quot; border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/canary/ss2_000.serendipityThumb.png&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;
&lt;br /&gt;&lt;p&gt;&lt;br /&gt;
The simple action that we add to CanaryView looks like this. Notice that it resets the timer as well:
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;pre&gt;
    @Action
    public void checkNow() {
        refresh();
        queryTimer.restart();
    }
&lt;/pre&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;br /&gt;&lt;h3&gt;Improving The Refresh And Archive Tasks&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;
&lt;em&gt;And at this point I was going to talk about (and include code for) making the Refresh and Archive tasks more user-friendly by making them Application Tasks, but it proved to be too daunting to explain here since there is some dependency on what gets done when so I gave it up for a bad job. Sorry again! I did take the progress bar relics from the initial project template out of the source code in the zip files at the end of the article though since we (I) won&#039;t be using them after all.&lt;/em&gt;&lt;/p&gt;
&lt;br /&gt;&lt;h3&gt;Setting The Cursor To The Top Of The Documents&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;
Finally we are going to fix something that has bugged me for a while by moving the cursor to the top of the documents after an update rather than leaving it positioned at the bottom. It&#039;s been like having a pebble in my shoe; at last I&#039;m finally taking the shoe off and emptying out the damn rock. The code to accomplish this is trivial, I just never got around to it before. All we have to do is call JEditorPane&#039;s setCaretPosition method with an argument of 0 in the methods that update the form&#039;s text panes, like this:
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;pre&gt;
	...
        sBuf.append(&quot;&lt;/body&gt;&lt;/html&gt;&quot;);
        archivePane.setText(sBuf.toString());
        archivePane.setCaretPosition(0);
	...
&lt;/pre&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;br /&gt;&lt;h3&gt;Going Further&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;
These are just some of the additions, changes and enhancements that can be made to Canary. Note that this list contains the &quot;Going Furthers&quot; of the previous two articles for your convenience.
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Change the AboutBox to reflect Canary&#039;s coolicity&lt;/li&gt;
&lt;li&gt;Actually use the busy icons, status bar, etc. from the stock desktop application framework or get rid of the code &lt;em&gt;[Got rid of the code in Canary III]&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Create secure account ID and password storage&lt;/li&gt;
&lt;li&gt;Create functioning tweet hyperlinks (the hyperlinks in the EditorPanes don&#039;t actually work, and making them do so is beyond the scope of this article)&lt;/li&gt;
&lt;li&gt;The Direct Msgs tab should have its own way of sending direct messages as well as displaying them (drop down friend box, etc.)&lt;/li&gt;
&lt;li&gt;Add fins and an automatic headlight dimmer to Canary (i.e. make it prettier; I was sorely tempted to do this but managed to restrain myself just in time so I can leave it for you to do) (but the orignal one I did for my clients has a purple gradient background...) &lt;/li&gt;
&lt;li&gt;Improve the printed format of the archived tweets&lt;/li&gt;
&lt;li&gt;Add a date/time stamp to the archived tweet output&lt;/li&gt;
&lt;li&gt;Save the archived output to a file instead of (as well as?) copying it to the clipboard&lt;/li&gt;
&lt;li&gt;When saving the archive to the clipboard, add some additional white space between tweets&lt;/li&gt;
&lt;li&gt;Actually parse the XML returned from Twitter in case of an error, as opposed to just printing it as a string&lt;/li&gt;
&lt;li&gt;Notify the user in some way (popup window, flash window, see above) that a new message has been received&lt;/li&gt;
&lt;li&gt;Adapt Canary to NetBeans 6.5&lt;/li&gt;
&lt;li&gt;Make the refresh and archive tasks Application Tasks, or at least get them off the dispatch thread&lt;/li&gt;
&lt;li&gt;Add the ability to store multiple Twitter accounts and allow the user to select which one to use on the fly. (Thanks to &lt;a onclick=&quot;javascript:urchinTracker(&#039;/extlink/www.naan.net/trac/wiki/TwitterFox&#039;);&quot; href=&quot;http://www.naan.net/trac/wiki/TwitterFox&quot; title=&quot;Link to TwitterFox Firefox Add-In&quot;&gt;TwitterFox&lt;/a&gt; for the idea.)&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;&lt;h3&gt;Yay, We&#039;re Done&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;
So that wraps it up for this installment of the Canary project. To resurrect our canary metaphor -- stop groaning, we&#039;re almost done -- we&#039;ve fed it some suet and fattened it up a little bit in the interest of user-friendliness, and we&#039;ve learned some things about RESTful web services, desktop applications and metaphor beating in the process. Yet our little canary remains fairly small. It has a long way to go before it&#039;s a twenty-pound turkey. Although I don&#039;t seem to be able to stop adding stuff to it, so by next Thanksgiving maybe it &lt;strong&gt;will&lt;/strong&gt; be a twenty-pound turkey. Great. I&#039;ll probably have to hire a staff of five to support the application....
&lt;/p&gt;&lt;br /&gt;
&lt;p&gt;As always, the source code and a distributable jar follow the article. (It&#039;s Canary_III now, by the way, at least in the zip file names.) And if anyone would like, I can whip this project into Google Code, Source Forge or maybe even NetBeans and it can become a community project. Just let me know is all. Namaste.&lt;/p&gt;

&lt;br /&gt;&lt;h3&gt;Source Code and Jar&lt;/h3&gt;&lt;br /&gt;

&lt;br /&gt;Source code:&lt;br /&gt;&lt;br /&gt;
&lt;a href=&quot;http://hulles.supersized.org/uploads/canary/Canary_III_Source.tar.gz&quot; alt=&quot;&quot;&gt;Canary_III_Source.tar.gz&lt;/a&gt;&lt;br /&gt;
&lt;a onclick=&quot;javascript:urchinTracker(&#039;/download/uploads/canary/Canary_III_Source.zip&#039;);&quot; href=&quot;http://hulles.supersized.org/uploads/canary/Canary_III_Source.zip&quot; alt=&quot;&quot;&gt;Canary_III_Source.zip&lt;/a&gt;
&lt;br /&gt;&lt;br /&gt;Jar file (Java executable):&lt;br /&gt;&lt;br /&gt;
&lt;a onclick=&quot;javascript:urchinTracker(&#039;/download/uploads/canary/Canary_III_Dist.zip&#039;);&quot; href=&quot;http://hulles.supersized.org/uploads/canary/Canary_III_Dist.zip&quot; alt=&quot;&quot;&gt;Canary_III_Dist.zip&lt;/a&gt;
&lt;br /&gt;&lt;br /&gt;
 
    </content:encoded>

    <pubDate>Wed, 17 Dec 2008 02:45:31 +0100</pubDate>
    <guid isPermaLink="false">http://hulles.supersized.org/archives/17-guid.html</guid>
    
</item>
<item>
    <title>Canary II: Feather Straightening and Cage Gilding</title>
    <link>http://hulles.supersized.org/archives/16-Canary-II-Feather-Straightening-and-Cage-Gilding.html</link>
            <category>NetBeans</category>
    
    <comments>http://hulles.supersized.org/archives/16-Canary-II-Feather-Straightening-and-Cage-Gilding.html#comments</comments>
    <wfw:comment>http://hulles.supersized.org/wfwcomment.php?cid=16</wfw:comment>

    <slash:comments>0</slash:comments>
    <wfw:commentRss>http://hulles.supersized.org/rss.php?version=2.0&amp;type=comments&amp;cid=16</wfw:commentRss>
    

    <author>nospam@example.com (Hulles)</author>
    <content:encoded>
    &lt;br /&gt;&lt;h3&gt;So I Lied...&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;
The ink is still wet on my last entry and already I&#039;m writing a follow-up article. Sigh. One would think I&#039;m trying to get the NetBeans Merit Badge or something. But I thought to clarify a couple points that I didn&#039;t get to in the first article and as an extra bonus add an archive feature to Canary. So here goes.
&lt;/p&gt;
&lt;br /&gt;&lt;h3&gt;Straightening A Couple Of Feathers&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;
I&#039;ll start with mentioning a couple things that I neglected to talk about in &lt;a href=&quot;http://hulles.supersized.org/archives/15-The-Canary-Project.html&quot; title=&quot;Original Canary Project Article&quot;&gt;The Canary Project&lt;/a&gt;. First, I forgot to tell you that I deleted the &lt;strong&gt;properties file&lt;/strong&gt; that NetBeans created in org.netbeans.saas.twitter because we don&#039;t need it; we got rid of the popup login window that used it. 
&lt;/p&gt;&lt;br /&gt;&lt;p&gt;
Second, I didn&#039;t tell you that, when you first drag a Twitter web service node onto your source code method, NetBeans also creates a library for you called &lt;strong&gt;TwitterResponse.jar&lt;/strong&gt;. You can find it in the &quot;Libraries&quot; section of Canary in the project tab. Of course it only does this the first time you drag a node, which is why I forgot to mention it. Be aware though, that if you just grab the source zip for Twitter without actually working the first example where we dragged the node onto our source, you won&#039;t have the library in your project. If you don&#039;t have it and need it, the easiest way to get it is to create a dummy method and drag a Twitter node onto it. A caveat, however: if the node you drag onto your source is not one that&#039;s already there, NetBeans will add code to TWAYDS.java to support the new node. This doesn&#039;t hurt anything but I wanted you to be aware of it. You can also grab the library from the distribution Jar zip file at the end of this (and the previous) article should you prefer.
&lt;/p&gt;&lt;br /&gt;&lt;p&gt;
I tested Canary in NetBeans 6.5 and all went well. I did notice, however, that if you actually drag a Twitter node onto your source in 6.5 the generated code is different. In 6.5, a typical Twitter call aftermath looks like this:
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;pre&gt;
    if (result.getDataAsObject(Statuses.class) instanceof Statuses) {
        Statuses resultObj = result.getDataAsObject(Statuses.class);
    } else if (result.getDataAsObject(NilClasses.class) instanceof NilClasses) {
        NilClasses resultObj = result.getDataAsObject(NilClasses.class);
    }
&lt;/pre&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The NilClasses class is new in 6.5, and for it to work the TwitterResponse.jar library in your project also must be the version that 6.5 creates when you drag a node. In other words, don&#039;t mix 6.1 and 6.5 code and libraries. Normally this won&#039;t be a problem if you start a project from scratch, but again it&#039;s something I thought I should bring up.
&lt;/p&gt;&lt;br /&gt;&lt;p&gt;
Finally, I should also mention that if you try and incorporate Canary into a NetBeans module you will need to create a library wrapper for TwitterResponse.jar to include it in your module project. Create a dummy non-module (i.e. normal) Java project, drag a node onto some dummy source, then swipe the library from the dummy project for your module project and create a wrapper for it. That is really beyond the scope of this project, but I thought that bringing it up might be helpful to someone.
&lt;/p&gt;
&lt;br /&gt;&lt;h3&gt;Gilding The Cage&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;
And so we get to the enhancement, a new tab for Canary that lists an &quot;archive&quot; of your past tweets. Author &lt;em&gt;par excellence&lt;/em&gt; that I am, every tweet I send is a &lt;strong&gt;literary gem&lt;/strong&gt; and worth saving for posterity, so a means of saving them on my own laptop is a must. How else can I incorporate them into my novel? So let&#039;s add an archiving function without further ado.   
&lt;/p&gt;&lt;br /&gt;&lt;p&gt;
First we are going to modify CanaryView.java and add a new tab called, appropriately, &quot;Archive&quot;. It&#039;s going to look like this:&lt;/p&gt;
&lt;br /&gt;
&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/canary/ss1_000.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:175 --&gt;&lt;img width=&quot;90&quot; height=&quot;51&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/canary/ss1_000.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;p&gt;
As you can see, we are creating this tab a little differently than the others. Since we want to be good Twitter citizens, we don&#039;t want to automatically grab our entire archive every time we start Canary, so there is a &quot;Fetch&quot; button to explicitly get our archive when we do indeed want it. You will also notice a &quot;Copy to Clipboard&quot; button. This will copy the entire contents of the archive pane to the system clipboard, mostly so I don&#039;t have to include file writing logic in this application. But it works just fine for our purposes, and the copy-to-clipboard functionality is frighteningly simple to implement, as you will see shortly.
&lt;/p&gt;&lt;br /&gt;&lt;p&gt;
The Java code that we need to change in CanaryView is pretty straightforward. There are two new actions for the buttons and code to populate the archive tab JEditorPane. The new code looks like this:
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;pre&gt;
    private void showUserStatus() {
        List&amp;lt;StatusType&amp;gt; archiveList;
        List&amp;lt;StatusType&amp;gt; statusList;
        StringBuffer sBuf;
        String text;
        
        archivePane.setText(UPDATINGMSG);
        sBuf = new StringBuffer();
        archiveList = new ArrayList&amp;lt;StatusType&amp;gt;(MAXARCHIVE);
        for (int page = 1; page &amp;lt;= MAXPAGES ; page++) {
            statusList = twitter.getArchive(Integer.toString(page));
            if ((statusList == null) || (statusList.isEmpty())) {
                break;
            }
            if (VERBOSE) System.out.println(&quot;CanaryView: archive page count = &quot;
                   + statusList.size());
            archiveList.addAll(statusList);
        }
        
        if (VERBOSE) System.out.println(&quot;CanaryView: archive total count = &quot;
                  + archiveList.size());
        
        if (archiveList.isEmpty()) {
            archivePane.setText(NOHISTORYMSG);
            return;
        }
        
        sBuf.append(&quot;&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;&quot;);
        for (StatusType type : archiveList) {
            sBuf.append(&quot;&amp;lt;p&amp;gt;&quot;);
            text = TwitterUtils.tweetToHTML(type.getText());
            sBuf.append(text);
            sBuf.append(&quot;&amp;lt;/p&amp;gt;&quot;);
        }
        sBuf.append(&quot;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&quot;);
        archivePane.setText(sBuf.toString());
    }
    
    @Action
    public void fetchArchive() {
        showUserStatus();
    }

    @Action
    public void copyToClipboard() {
        archivePane.selectAll();
        archivePane.copy();
        JOptionPane.showMessageDialog(mainFrame, &quot;Archive copied to clipboard.&quot;, 
		&quot;Copy to Clipboard&quot;, JOptionPane.INFORMATION_MESSAGE);
    }
&lt;/pre&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The method ShowUserStatus() gets our archive from the Twitter servers, one page at a time, to a maximum of MAXPAGES (10, in this case) pages. We put a limit in it just to make it reasonably sane and not overload the Twitter servers. If you do change MAXPAGES, please have a little compassion for the Twitter folks and don&#039;t make it too large. While we&#039;re here, note the small amount of code in the copyToClipboard() action. That&#039;s all it takes to slam the text into the system clipboard with JEditorPane. Isn&#039;t that nice?
&lt;/p&gt;&lt;br /&gt;
&lt;p&gt;Next we&#039;re going to modify TwitterUtils and add the function getArchive(...). It looks like this, very similar to the other Twitter functions in the class:
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;pre&gt;
    @SuppressWarnings(&quot;unchecked&quot;)
    public List&amp;lt;StatusType&amp;gt; getArchive (String page) {
        RestResponse result = null;
        List&amp;lt;StatusType&amp;gt; statusList;
        Statuses resultObj;
        String since = null;
        String sinceID = null;
        String format = &quot;xml&quot;;
        
        statusList = Collections.EMPTY_LIST;
        try {
             result = TwitterWhatAreYouDoingService.getUserTimeline(since, sinceID, page, format);
             resultObj = result.getDataAsObject(Statuses.class);
             statusList = resultObj.getStatus();
        } catch (Exception ex) {
            if (LOGERRORS) {
                Logger.getLogger(TwitterUtils.class.getName()).log(Level.SEVERE, null, ex);
            }
            statusList = null;
        }
        return statusList;
    }
&lt;/pre&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;
Finally, we&#039;re going to add a function to TwitterWhatAreYouDoingService.java, know as TWAYDS.java from here to save my aching fingers. This function also strongly resembles others of its ilk in TWAYDS.
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;pre&gt;
    /**
     * Retrieves representation of an instance of webtest.GetUserTimelineResource
     * @param since resource URI parameter
     * @param sinceId resource URI parameter
     * @param page resource URI parameter
     * @param count resource URI parameter
     * @param format resource URI parameter
     * @return an instance of RestResponse
     */
    public static RestResponse getUserTimeline(String since, String sinceId, 
             String page, String format) throws IOException {
        TwitterWhatAreYouDoingServiceAuthenticator.login();

        String[][] pathParams = new String[][]{{&quot;{format}&quot;, format}};
        String[][] queryParams = new String[][]{{&quot;since&quot;, since}, {&quot;since_id&quot;, sinceId}, {&quot;page&quot;, page}};
        RestConnection conn = new RestConnection(&quot;http://twitter.com/statuses/user_timeline.{format}&quot;, 
		pathParams, queryParams);

        return conn.get(null);
    }
&lt;/pre&gt;&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;And there you have it. When you run it, the &quot;Archive&quot; tab on Canary should look something like this:
 &lt;/p&gt;
&lt;br /&gt;
 &lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/canary/ss1_001.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:171 --&gt;&lt;img width=&quot;90&quot; height=&quot;68&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/canary/ss1_001.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;
&lt;br /&gt;&lt;br /&gt;
 &lt;p&gt;Woohoo! Of course, your output won&#039;t contain my literary gems, it will contain your own gems.
 &lt;/p&gt;&lt;br/&gt;&lt;p&gt;
 To recap, we added functionality to three classes, TwitterUtils.java, TWAYDS.java and CanaryView.java. These should be the only differences from the original Canary project. Even so, I am including updated source and jar files at the end of the article. The project is called Canary_II in these, however, to distinguish it from the original project so I can support both (to a greater or lesser extent). At any rate, I hope you enjoy the newly-gilded cage for your Canary. Namaste.
 &lt;br /&gt;
&lt;br /&gt;&lt;h3&gt;Going Further&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;
There are many enhancements that can be made to Canary_II; here are a few:
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Improve the printed format of the archived tweets.&lt;/li&gt;
&lt;li&gt;Add a date/time stamp to the archived tweet output.&lt;/li&gt;
&lt;li&gt;Save the archived output to file instead of / as well as copying it to the clipboard.&lt;/li&gt;
&lt;li&gt;When saving to the clipboard, add some additional white space between tweets.&lt;/li&gt;
&lt;/ul&gt;

&lt;br /&gt;&lt;h3&gt;Source Code and Jar&lt;/h3&gt;&lt;br /&gt;

&lt;br /&gt;Source code:&lt;br /&gt;&lt;br /&gt;
&lt;a href=&quot;http://hulles.supersized.org/uploads/canary/Canary_II_Source.tar.gz&quot; alt=&quot;&quot;&gt;Canary_II_Source.tar.gz&lt;/a&gt;&lt;br /&gt;
&lt;a onclick=&quot;javascript:urchinTracker(&#039;/download/uploads/canary/Canary_II_Source.zip&#039;);&quot; href=&quot;http://hulles.supersized.org/uploads/canary/Canary_II_Source.zip&quot; alt=&quot;&quot;&gt;Canary_II_Source.zip&lt;/a&gt;
&lt;br /&gt;&lt;br /&gt;Jar file (Java executable):&lt;br /&gt;&lt;br /&gt;
&lt;a onclick=&quot;javascript:urchinTracker(&#039;/download/uploads/canary/Canary_II_Dist.zip&#039;);&quot; href=&quot;http://hulles.supersized.org/uploads/canary/Canary_II_Dist.zip&quot; alt=&quot;&quot;&gt;Canary_II_Dist.zip&lt;/a&gt;
&lt;br /&gt;&lt;br /&gt;
 
    </content:encoded>

    <pubDate>Tue, 25 Nov 2008 18:39:30 +0100</pubDate>
    <guid isPermaLink="false">http://hulles.supersized.org/archives/16-guid.html</guid>
    
</item>
<item>
    <title>The Canary Project</title>
    <link>http://hulles.supersized.org/archives/15-The-Canary-Project.html</link>
            <category>NetBeans</category>
    
    <comments>http://hulles.supersized.org/archives/15-The-Canary-Project.html#comments</comments>
    <wfw:comment>http://hulles.supersized.org/wfwcomment.php?cid=15</wfw:comment>

    <slash:comments>2</slash:comments>
    <wfw:commentRss>http://hulles.supersized.org/rss.php?version=2.0&amp;type=comments&amp;cid=15</wfw:commentRss>
    

    <author>nospam@example.com (Hulles)</author>
    <content:encoded>
    &lt;em&gt;&lt;strong&gt;Canary&lt;/strong&gt; is a lightweight Java desktop application that monitors the Twitter online message service. Links to the complete source code and a zipped jar are included at the end of the article. You can run the jar directly once you unzip it if you just want to try out the application or use it without futzing with the source code. Try &quot;java -jar [wherever you put the jar file]&quot; from your command line.&lt;/em&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;br /&gt;&lt;h3&gt;Hey, A New Project!&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;
Well, it&#039;s been a while since I&#039;ve put anything new up, but I think you&#039;ll like this project. Since GeeWhiz Prolog I&#039;ve been working on a massive &quot;rich-client application&quot; based on the NetBeans IDE. This application involves creating a complete suite of programs to write and produce screenplays. It&#039;s an ambitious project to say the least. 
&lt;/p&gt;&lt;br /&gt;&lt;p&gt;
Recently while working on this mare&#039;s nest (called &quot;Sancho&quot;, by the way) I decided to incorporate a feature that allows access to the &lt;a onclick=&quot;javascript:urchinTracker(&#039;/extlink/twitter.com&#039;);&quot; href=&quot;http://twitter.com&quot; title=&quot;Twitter&quot;&gt;Twitter online service&lt;/a&gt; from within the application. This feature was not part of the original design specification but when I thought of it I knew it was just too cool to pass up, so I built it and stuck it into the project. Like the guy in &quot;Field of Dreams,&quot; my philosophy has always been, &quot;Build it and they will come, or at least do some heavy panting.&quot; Anyway, the clients (who introduced me to Twitter in the first place) loved it so I decided to make a stand-alone version for this blog so you can try it out for yourself.&lt;/p&gt;
&lt;br /&gt;
&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/canary/ss012.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:167 --&gt;&lt;img width=&quot;90&quot; height=&quot;68&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/canary/ss012.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;
&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;
&lt;br /&gt;&lt;h3&gt;What The Hell Is Twitter?&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;
At this point I suppose I should explain what the Twitter service is for those of you who are unfamiliar with it. Twitter is a pretty straightforward free online messaging service where you set up a Twitter account then post messages to it that others who follow you can read. Likewise, you receive Twitter messages (called &quot;tweets&quot;) from those people you follow. You can send and receive tweets from your computer or from your cell phone; by far the most common usage seems to be &quot;twittering&quot; from your cell phone. If you&#039;re standing in the grocery store you can bitch about the price of kohlrabi right from the produce aisle, for example, and your friends following you on Twitter will roll their eyes and decide to remove you from their follow list. I am &lt;strong&gt;&lt;a onclick=&quot;javascript:urchinTracker(&#039;/extlink/twitter.com/hulles&#039;);&quot; href=&quot;http://twitter.com/hulles&quot;&gt;&quot;hulles&quot;&lt;/a&gt;&lt;/strong&gt; on Twitter, as you might expect. Should you want to follow me you can do that very simply from your Twitter account home page, though if you should choose to do so I should warn you my tweets are often a little bizarre. As you might expect.
&lt;/p&gt;
&lt;br /&gt;&lt;h3&gt;Design Goals&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;
This application, named &lt;strong&gt; Canary &lt;/strong&gt; by me to continue the &quot;twitter/tweet&quot; metaphor, runs as a standalone Java application on your computer just like any other. It monitors Twitter using its RESTful Web Service and keeps you updated on what your friends are bitching about in the grocery store by querying Twitter&#039;s servers every 5 minutes to get updated tweets. It also allows you to send tweets, get replies and get direct messages as well.
&lt;/p&gt;&lt;br /&gt;&lt;p&gt;
Like all the stuff I put up here, this project is intended to teach someone relatively new to the NetBeans platform how to use various features of that environment without much in the way of prior knowledge. Finding documentation on those features is often challenging, especially if those features have been recently introduced, so I try to explain myself as I go and take you through building the project step by step without assuming too much background on your part.
&lt;/p&gt;&lt;br /&gt;&lt;p&gt;
For this project, we are going to be learning about and using two interesting pieces of the NetBeans platform, &lt;strong&gt;&quot;RESTful Web Services&quot;&lt;/strong&gt; and &lt;strong&gt;&quot;Java Desktop Applications&quot;&lt;/strong&gt;. Originally I was going to make this a NetBeans plugin module (like I did for my clients in Sancho, see above) but doing so incorporated too much unrelated overhead that I have already covered in GeeWhiz Prolog. Thus I went with a Java Desktop Application instead. I think it actually turned out cooler that way, and besides I don&#039;t have much call to Twitter while working on Java projects anyway and doubt that many people do.
&lt;/p&gt;&lt;br /&gt;&lt;p&gt;
Besides illustrating the Java Desktop Application framework, another major design goal for &lt;strong&gt;Canary&lt;/strong&gt; is to be a &quot;thin client&quot; for the online service. That is, most of the documentation you see about Web Services in NetBeans assumes that you want to create a service provider as well as a service consumer / client. We don&#039;t want to do that. Twitter already provides the service; we just want to consume it with as little additional overhead as we can get away with. So this is going to be a &lt;strong&gt;skinny&lt;/strong&gt; standalone Canary. No WADL / WSDL shit for us manly programmers, no sir. Just give us the bottom line and we will be on our merry way, thank you very much.
&lt;/p&gt;&lt;br /&gt;&lt;p&gt;
A final design goal for Canary is to be a good Twitter citizen. We don&#039;t want to be constantly hitting the Twitter servers with spurious requests; instead we want to conserve bandwidth and the Twitter servers&#039; disk drives by limiting our Twitter access to only what we need and by allowing a decent amount of time to pass between requests. So this is going to be a &lt;strong&gt;saintly&lt;/strong&gt;, skinny, standalone Canary by the time we&#039;re done. A Canary to feel proud of, a Canary to brag about, a Canary to.... You get the picture.
&lt;/p&gt;

&lt;br /&gt;&lt;h3&gt;Requirements&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;
To build and run this project you need the following:
&lt;/p&gt;&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;NetBeans 6.1 (should work on 6.5 but I haven&#039;t tried it yet....)&lt;/li&gt;
&lt;li&gt;RESTful Web Services plugin module&lt;/li&gt;
&lt;li&gt;Web Services plugin module (automatically included when you select the RESTful Web Services plugin)&lt;/li&gt;
&lt;li&gt;Internet access and a (free) Twitter account with which to test the project&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;&lt;p&gt;
You can download and install the plugin modules from the NetBeans IDE menu via &quot;Tools / Plugins.&quot; The modules are located in the standard NetBeans repository.
&lt;/p&gt;
&lt;br /&gt;&lt;h3&gt;Before We Get Started&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;
Prior to starting the project, I suggest that you select the &quot;Services&quot; tab in NetBeans, expand &quot;Web Services&quot;, and check out the available services. (Don&#039;t have it? See &lt;em&gt;Requirements&lt;/em&gt;, above.) I have no idea what half of the services are for, but the one we are interested in is the Twitter &quot;What Are You Doing Service&quot; (&lt;strong&gt;TWAYDS&lt;/strong&gt; here from now on). Notice that if you right click the TWAYDS node, you get a choice of selecting the API documentation:
&lt;/p&gt;&lt;br /&gt;
&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/canary/ss000.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:154 --&gt;&lt;img width=&quot;90&quot; height=&quot;61&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/canary/ss000.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;
&lt;br /&gt;&lt;br /&gt;&lt;p&gt;
I strongly recommend that you do so and read Twitter&#039;s API documentation. It is succinct and well-written and should be your bible if you do any further work with Canary or the Twitter RESTful Service beyond the scope of this project.
&lt;/p&gt;
&lt;br /&gt;&lt;h3&gt;Creating The Project Framework&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;
To get started with Canary, you need to create a new project. In the NetBeans IDE menu, select &quot;File / New Project...&quot;, then on the first screen of the wizard select the &quot;Java&quot; category and &quot;Java Desktop Application&quot; as the project type.
&lt;/p&gt;&lt;br /&gt;
&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/canary/ss001.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:155 --&gt;&lt;img width=&quot;90&quot; height=&quot;58&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/canary/ss001.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;
&lt;br /&gt;&lt;br /&gt;&lt;p&gt;
In the &quot;Name and Location&quot; screen of the wizard, enter &quot;Canary&quot; as the project name and &quot;canary.CanaryApp&quot; as the application class.
&lt;/p&gt;&lt;br /&gt;
&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/canary/ss002.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:156 --&gt;&lt;img width=&quot;90&quot; height=&quot;58&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/canary/ss002.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;
&lt;br /&gt;&lt;br /&gt;&lt;p&gt;
Make sure the application shell is set to &quot;Basic Application,&quot; &lt;em&gt;not&lt;/em&gt; &quot;Database Application&quot;. We don&#039;t need no stinkin&#039; database for this project. Hit &quot;Finish&quot; on the wizard and let it set up your new project for you. The wizard automatically creates resources folders, CanaryApp.java, CanaryAboutBox.java, and CanaryView.java.
&lt;/p&gt;&lt;br /&gt;
&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/canary/ss003.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:157 --&gt;&lt;img width=&quot;90&quot; height=&quot;61&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/canary/ss003.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;
&lt;br /&gt;&lt;br /&gt;&lt;p&gt;
Take a look at the basic desktop application shell the wizard creates if you&#039;ve never done this before. You can even try it out at this point by clicking the &quot;Run&quot; button on the toolbar, just to see what it does. When you&#039;re finished playing with it, close all tabs for the files that the wizard created.
&lt;/p&gt;

&lt;br /&gt;&lt;h3&gt;Adding Twitter Functionality&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;
Now we&#039;ll start adding Twitter-related structure to Canary. Create a new Java class called &quot;TwitterUtils&quot; by right-clicking the &quot;canary&quot; source folder in the project and selecting &quot;New / Java Class...&quot;.
&lt;/p&gt;&lt;br /&gt;
&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/canary/ss004.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:158 --&gt;&lt;img width=&quot;90&quot; height=&quot;61&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/canary/ss004.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;
&lt;br /&gt;&lt;br /&gt;&lt;p&gt;
On the wizard screen, the class name should be &quot;TwitterUtils&quot;, the project should be &quot;Canary&quot; and the package should be &quot;canary&quot;.
&lt;/p&gt;&lt;br /&gt;
&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/canary/ss005.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:159 --&gt;&lt;img width=&quot;90&quot; height=&quot;58&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/canary/ss005.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;
&lt;br /&gt;&lt;br /&gt;&lt;p&gt;
Hit the &quot;Finish&quot; button and the wizard will create a new empty Java class for you. In the TwitterUtils class, create a new public method called getFriendsStatus. The class should now be as follows (except for the author, of course):
&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;&lt;strong&gt;&lt;pre&gt;
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package canary;

/**
 *
 * @author hulles
 */

public class TwitterUtils {

	public void getFriendsStatus() {
	
	}
}
&lt;/pre&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;br /&gt;&lt;p&gt;
Perfect. Now we work our magic. Select the &quot;Services&quot; tab in the IDE and expose &quot;Twitter / What Are You Doing Service / statuses / [friends_timeline] / [{id}.{format}] / getFriendsTimelineById&quot;. Drag the &quot;getFriendsTimelineById&quot; node from the Services window onto the &quot;getFriendsStatus&quot; method you just created above in the editor pane.
&lt;/p&gt;&lt;br /&gt;
&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/canary/ss006.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:160 --&gt;&lt;img width=&quot;90&quot; height=&quot;61&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/canary/ss006.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;
&lt;br /&gt;&lt;br /&gt;&lt;p&gt;
Accept (for now) the default values in the next screen:
&lt;/p&gt;&lt;br /&gt;
&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/canary/ss007.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:161 --&gt;&lt;img width=&quot;90&quot; height=&quot;48&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/canary/ss007.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;
&lt;br /&gt;&lt;br /&gt;&lt;p&gt;
Woohoo! I told you it was magic. NetBeans, bless its heart, creates application code in your method and creates &quot;org.netbeans.saas&quot; and &quot;org.netbeans.saas.twitter&quot; folders for you.
&lt;/p&gt;&lt;br /&gt;
&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/canary/ss008.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:162 --&gt;&lt;img width=&quot;90&quot; height=&quot;61&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/canary/ss008.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;
&lt;br /&gt;&lt;br /&gt;&lt;p&gt;
Before we move on, notice that there is &lt;em&gt;no server code anywhere&lt;/em&gt;, implying (at least) that we can indeed use these clients as standalone functions without worrying about writing any web service provider crap. I wasn&#039;t sure of that before I tried this the first time, even after reading all the documentation I could find about Web Services and RESTful Web Services. Most of the documentation spends a lot of time talking about the server side of things, which we don&#039;t need at all. And that is, of course, why I&#039;m taking the trouble to write this for you! Consuming RESTful Web Services is &lt;em&gt;easy&lt;/em&gt;! So far.
&lt;/p&gt;&lt;br /&gt;&lt;p&gt;
By the way, the &quot;saas&quot; in the folder names stands for &quot;Software as a Service,&quot; the acronym for which is usually seen as &quot;SaaS&quot; outside of code.
&lt;/p&gt;&lt;br /&gt;&lt;p&gt;
As you look at the new folders you now have in your project, notice especially that a method was added to org.netbeans.saas.twitter.TwitterWhatAreYouDoingService.java for the getFriendsTimelineById gizmo we dragged in above.
&lt;/p&gt;&lt;br /&gt;
&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/canary/ss009.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:163 --&gt;&lt;img width=&quot;90&quot; height=&quot;61&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/canary/ss009.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;
&lt;br /&gt;&lt;br /&gt;&lt;p&gt;
Every time you drag a new node onto your project, NetBeans adds the appropriate method to this class. This is important to remember if you branch out beyond what we cover in Canary, because it happens behind the scenes and you don&#039;t notice it at the time. Until, of course, it &lt;em&gt;doesn&#039;t&lt;/em&gt; happen behind the scenes because you just copy and paste a method without dragging it from the Services tab.... Guess how I know that.
&lt;/p&gt;&lt;br /&gt;&lt;p&gt;
There is one more thing to point out in the generated code before we move on. TWAYDSAuthenticator has code for a popup login screen to collect the Twitter account and password. Don&#039;t worry about this for now because we&#039;re going to get rid of it shortly.
&lt;/p&gt;
&lt;br /&gt;&lt;h3&gt;Some Magic Of Our Own&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;
Now it&#039;s time to hack. We&#039;re going to replace the TwitterUtils.java class you created earlier (sorry, but you didn&#039;t have to type &lt;em&gt;that&lt;/em&gt; much) with the following code:
&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;&lt;strong&gt;&lt;pre&gt;
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package canary;

import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.netbeans.saas.RestResponse;
import org.netbeans.saas.twitter.TwitterWhatAreYouDoingService;
import twitter.whatareyoudoingservice.twitterresponse.DirectMessageType;
import twitter.whatareyoudoingservice.twitterresponse.DirectMessages;
import twitter.whatareyoudoingservice.twitterresponse.StatusType;
import twitter.whatareyoudoingservice.twitterresponse.Statuses;

/**
 *
 * @author hulles
 */
public class TwitterUtils {
    private final static boolean LOGERRORS = false;
    
    public TwitterUtils() {
    }
    
    @SuppressWarnings(&quot;unchecked&quot;)
    public List&amp;lt;StatusType&amp;gt; getFriendsStatus (String sinceID) {
        RestResponse result = null;
        List&amp;lt;StatusType&amp;gt; statusList;
        Statuses resultObj;
        String since = null;
        String page = &quot;1&quot;;
        String format = &quot;xml&quot;;
        
        statusList = Collections.EMPTY_LIST;
        try {
             result = TwitterWhatAreYouDoingService.getFriendsTimeline(since, 
                         sinceID, page, format);
             resultObj = result.getDataAsObject(Statuses.class);
             statusList = resultObj.getStatus();
        } catch (Exception ex) {
            if (LOGERRORS) {
                Logger.getLogger(TwitterUtils.class.getName()).log(Level.SEVERE, null, ex);
            }
            statusList = null;
        }
        return statusList;
    }
    
    @SuppressWarnings(&quot;unchecked&quot;)
    public List&amp;lt;StatusType&amp;gt; getReplies(String sinceID) {
        RestResponse result = null;
        List&amp;lt;StatusType&amp;gt; statusList;
        Statuses resultObj;
        String since = null;
        String page = &quot;1&quot;;
        String format = &quot;xml&quot;;
        
        statusList = Collections.EMPTY_LIST;
        try {
             result = TwitterWhatAreYouDoingService.getReplies(since, 
                         sinceID, page, format);
             resultObj = result.getDataAsObject(Statuses.class);
             statusList = resultObj.getStatus();
        } catch (Exception ex) {
            if (LOGERRORS) {
                Logger.getLogger(TwitterUtils.class.getName()).log(Level.SEVERE, null, ex);
            }
            statusList = null;
        }
        return statusList;
    }

    @SuppressWarnings(&quot;unchecked&quot;)
    public List&amp;lt;DirectMessageType&amp;gt; getDirectMsgs(String sinceID) {
        RestResponse result = null;
        List&amp;lt;DirectMessageType&amp;gt; msgList;
        DirectMessages resultObj;
        String since = null;
        String page = &quot;1&quot;;
        String format = &quot;xml&quot;;
        
        msgList = Collections.EMPTY_LIST;
        try {
             result = TwitterWhatAreYouDoingService.getDirectMessagesToMe(since, 
                     sinceID, page, format);
             resultObj = result.getDataAsObject(DirectMessages.class);
             msgList = resultObj.getDirectMessage();
        } catch (Exception ex) {
            if (LOGERRORS) {
                Logger.getLogger(TwitterUtils.class.getName()).log(Level.SEVERE, null, ex);
            }
            msgList = null;
        }
        return msgList;
    }

    public RestResponse updateStatus(String status) {
        String format = &quot;xml&quot;;
//        StatusType resultObj;
        RestResponse result;
        
        try {
             result = TwitterWhatAreYouDoingService.updateStatus(status, format);
//             resultObj = result.getDataAsObject(StatusType.class);
        } catch (Exception ex) {
            if (LOGERRORS) {
                Logger.getLogger(TwitterUtils.class.getName()).log(Level.SEVERE, null, ex);
            }
            result = null;
        }
        return result;
    }
    
    public RestResponse verifyLogin() {
        String format = &quot;xml&quot;;
        RestResponse result;
        
        try {
             result = TwitterWhatAreYouDoingService.verifyCredentials(format);
        } catch (Exception ex) {
            if (LOGERRORS) {
                Logger.getLogger(TwitterUtils.class.getName()).log(Level.SEVERE, null, ex);
            }
            result = null;
        }
        return result;
    }
    
    public void signOut() {
        RestResponse result;
        
        try {
            result = TwitterWhatAreYouDoingService.endSession();
        } catch (Exception ex) {
            if (LOGERRORS) {
                Logger.getLogger(TwitterUtils.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
    
    public static String tweetToHTML(String text) {
        String[] splitText;
        StringBuffer sBuf;
        
        sBuf = new StringBuffer();
        splitText = text.split(&quot; &quot;);
        for (String s : splitText) {
            if (sBuf.length() &amp;gt; 0) {
                sBuf.append(&quot; &quot;);
            }
            if (s.startsWith(&quot;http://&quot;)) {
                sBuf.append(&quot;&amp;lt;a href=&#039;&quot;);
                sBuf.append(s);
                sBuf.append(&quot;&#039;&amp;gt;&quot;);
                sBuf.append(s);
                sBuf.append(&quot;&amp;lt;/a&amp;gt;&quot;);
            } else if (s.startsWith(&quot;@&quot;) &amp;amp;&amp;amp; (s.length() &amp;gt; 1)) {
                // doesn&#039;t check for malformed urls...
                sBuf.append(&quot;&amp;lt;a href=&#039;&quot;);
                sBuf.append(&quot;http://twitter.com/&quot; + s.substring(1));
                sBuf.append(&quot;&#039;&amp;gt;@&quot;); // follow twitter format, @ is not part of link
                sBuf.append(s.substring(1));
                sBuf.append(&quot;&amp;lt;/a&amp;gt;&quot;);
            } else {
                sBuf.append(s);
            }
        }
        return sBuf.toString();
    }
    
/*  Return codes (from Twitter API):   
    * 200 OK: everything went awesome.
    * 304 Not Modified: there was no new data to return.
    * 400 Bad Request: your request is invalid, and we&#039;ll return an error message 
              that tells you why. This is the status code returned if you&#039;ve exceeded the 
              rate limit (see below). 
    * 401 Not Authorized: either you need to provide authentication credentials, or 
             the credentials provided aren&#039;t valid.
    * 403 Forbidden: we understand your request, but are refusing to fulfill it.  An 
             accompanying error message should explain why.
    * 404 Not Found: either you&#039;re requesting an invalid URI or the resource in question 
             doesn&#039;t exist (ex: no such user). 
    * 500 Internal Server Error: we did something wrong.  Please post to the group about it 
             and the Twitter team will investigate.
    * 502 Bad Gateway: returned if Twitter is down or being upgraded.
    * 503 Service Unavailable: the Twitter servers are up, but are overloaded with requests.  
             Try again later.
*/
    public static String requestResponse(RestResponse response) {
        String responseString;
        
        if (response.getResponseCode() == 200) {
            responseString = null;
        } else {
            responseString = response.getResponseMessage();
        }
        return responseString;
    }
}
&lt;/pre&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;
This is really the heart of our Canary. There are four methods for the Twitter functions we need:
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;getFriendsStatus&lt;/strong&gt;, to get tweets from the people we&#039;re following&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;getReplies&lt;/strong&gt;, to get replies to our tweets (these are also included in getFriendsStatus, but it can be nice to look at them separately)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;getDirectMessages&lt;/strong&gt;, to get direct messages (&quot;dm&#039;s&quot;) to us (also included in getFriendsStatus, and ditto)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;updateStatus&lt;/strong&gt;, to allow us to send new tweets from our desktop&lt;/li&gt;
&lt;/ul&gt;
&lt;/p&gt;&lt;br /&gt;&lt;p&gt;
The new TwitterUtils also includes a few utility functions: requestResponse, verifyLogin and signOut, that we won&#039;t worry about for now. However, there is also a method called &lt;strong&gt;tweetToHTML&lt;/strong&gt; that does deserve some explanation. When Twitter serves up a tweet it doesn&#039;t do so in the nice format that everyone who uses Twitter is used to: hyperlinked @replies, hyperlinked external links, etc. The static method tweetToHTML just formats the text received from the Twitter server into HTML that looks like the tweets that Twitter users have come to expect. See your own Twitter home page, TwitterFox, et. al. for examples of this.
&lt;/p&gt;&lt;br /&gt;&lt;p&gt;
Next we&#039;re going to replace the initial TWAYDS class that NetBeans created with a new one that has all the methods we need in TwitterUtils. It looks like this:
&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;&lt;strong&gt;&lt;pre&gt;
/*
 *  TwitterWhatAreYouDoingService
 *
 * Created on September 2, 2008, 8:33 AM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package org.netbeans.saas.twitter;

import java.io.IOException;
import org.netbeans.saas.RestConnection;
import org.netbeans.saas.RestResponse;

/**
 * TwitterWhatAreYouDoingService Service
 *
 * @author hulles
 */

public class TwitterWhatAreYouDoingService {

    /** Creates a new instance of TwitterWhatAreYouDoingService */
    public TwitterWhatAreYouDoingService() {
    }

    /**
     * Retrieves representation of an instance of webtest.GetFriendsTimelineResource
     * @param since resource URI parameter
     * @param sinceId resource URI parameter
     * @param page resource URI parameter
     * @param format resource URI parameter
     * @return an instance of RestResponse
     */
    public static RestResponse getFriendsTimeline(String since, String sinceId, 
                String page, String format) throws IOException {
        TwitterWhatAreYouDoingServiceAuthenticator.login();

        String[][] pathParams = new String[][]{{&quot;{format}&quot;, format}};
        String[][] queryParams = new String[][]{{&quot;since&quot;, since}, {&quot;since_id&quot;, sinceId}, 
                {&quot;page&quot;, page}};
        RestConnection conn = new RestConnection(&quot;http://twitter.com/statuses/friends_timeline.{format}&quot;, 
                pathParams, queryParams);






        return conn.get(null);
    }
    
    /**
     * Retrieves representation of an instance of webtest.GetDirectMessagesToMeResource
     * @param since resource URI parameter
     * @param sinceId resource URI parameter
     * @param page resource URI parameter
     * @param format resource URI parameter
     * @return an instance of RestResponse
     */
    public static RestResponse getDirectMessagesToMe(String since, String sinceId, String page, 
                String format) throws IOException {
        TwitterWhatAreYouDoingServiceAuthenticator.login();

        String[][] pathParams = new String[][]{{&quot;{format}&quot;, format}};
        String[][] queryParams = new String[][]{{&quot;since&quot;, since}, {&quot;since_id&quot;, sinceId}, {&quot;page&quot;, page}};
        RestConnection conn = new RestConnection(&quot;http://twitter.com/direct_messages.{format}&quot;, 
                pathParams, queryParams);

        return conn.get(null);
    }

    /**
     * Retrieves representation of an instance of webtest.GetRepliesResource
     * @param since resource URI parameter
     * @param sinceId resource URI parameter
     * @param page resource URI parameter
     * @param format resource URI parameter
     * @return an instance of RestResponse
     */
    public static RestResponse getReplies(String since, String sinceId, String page, 
                String format) throws IOException {
        TwitterWhatAreYouDoingServiceAuthenticator.login();

        String[][] pathParams = new String[][]{{&quot;{format}&quot;, format}};
        // NOTE: differs from the NetBeans saas insert 
//        String[][] queryParams = new String[][]{{&quot;page&quot;, page}};
        String[][] queryParams = new String[][]{{&quot;since&quot;, since}, {&quot;since_id&quot;, sinceId}, 
                {&quot;page&quot;, page}};
        RestConnection conn = new RestConnection(&quot;http://twitter.com/statuses/replies.{format}&quot;, 
                 pathParams, queryParams);

        return conn.get(null);
    }

    /**
     * Retrieves representation of an instance of webtest.EndSessionResource
     * @return an instance of RestResponse
     */
    public static RestResponse endSession() throws IOException {
        TwitterWhatAreYouDoingServiceAuthenticator.login();

        RestConnection conn = new RestConnection(&quot;http://twitter.com/account/end_session&quot;);

        return conn.get(null);
    }

    /**
     * Retrieves representation of an instance of webtest.UpdateStatusResource
     * @param status resource URI parameter
     * @param format resource URI parameter
     * @return an instance of RestResponse
     */
    public static RestResponse updateStatus(String status, String format) throws IOException {
        TwitterWhatAreYouDoingServiceAuthenticator.login();

        String[][] pathParams = new String[][]{{&quot;{format}&quot;, format}};
        String[][] queryParams = new String[][]{{&quot;status&quot;, status}};
        RestConnection conn = new RestConnection(&quot;http://twitter.com/statuses/update.{format}&quot;, 
                pathParams, queryParams);

        return conn.post(null, queryParams);
    }
    

    /**
     * Retrieves representation of an instance of webtest.VerifyCredentialsResource
     * @param format resource URI parameter
     * @return an instance of RestResponse
     */
    public static RestResponse verifyCredentials(String format) throws IOException {
        TwitterWhatAreYouDoingServiceAuthenticator.login(true);

        String[][] pathParams = new String[][]{{&quot;{format}&quot;, format}};
        RestConnection conn = new RestConnection(&quot;http://twitter.com/account/verify_credentials.{format}&quot;, 
                pathParams, null);

        return conn.get(null);
    }
}
&lt;/pre&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;
Note that this class isn&#039;t really created by us. You could have created the same class by dragging the appropriate nodes from the Services tab onto the editor pane like the first one we did; I just thought I&#039;d save you the trouble by presenting the finished product.
&lt;/p&gt;&lt;br /&gt;&lt;p&gt;
Next we&#039;re going to hack the TWADSAuthenticator class and excise the popup login screen so we can use one more suited to the desktop application framework. We&#039;re also going to use the Java &quot;Preferences.userNodeForPackage&quot; feature so we don&#039;t have to create our own external storage for accounts and passwords. I had never used this before but tried it out here and it seems to be just the ticket. I really didn&#039;t want to have to add file access code to this skinny little bird just to store the couple pieces of data that we need to remember from session to session. In the original version of this created for the rich-client app I was able to use NbPreferences and change the data in the Options panel but this works just as well, perhaps better. But please note: &lt;strong&gt;&lt;em&gt;Twitter accounts and passwords are not stored securely using this method!&lt;/em&gt;&lt;/strong&gt; It works just fine for a teaching example here but may not be the solution you need in a more robust application. You were warned.
&lt;/p&gt;&lt;br /&gt;&lt;p&gt;
Having said all that, here is the TWAYDSAuthenticator code:
&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;&lt;strong&gt;&lt;pre&gt;
/*
 * TwitterWhatAreYouDoingServiceAuthenticator.java
 *
 * Created on September 7, 2008, 6:46 AM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package org.netbeans.saas.twitter;

import java.io.IOException;
import java.net.Authenticator;
import java.net.PasswordAuthentication;
import java.util.prefs.Preferences;
import canary.LoginDialog;

/**
 *
 * @author hulles
 */
public class TwitterWhatAreYouDoingServiceAuthenticator extends Authenticator {
    private static String userID;
    private static String password;

    private static TwitterWhatAreYouDoingServiceAuthenticator singleton = new 
            TwitterWhatAreYouDoingServiceAuthenticator();
    
    public static void login() throws IOException {
        login(false);
    }
    public static void login(boolean force) throws IOException {
        Preferences pref;
        LoginDialog loginDialog = null;
        
        if (!isValidUsernamePassword()) {
            pref = Preferences.userNodeForPackage(TwitterWhatAreYouDoingServiceAuthenticator.class);
            userID = pref.get(&quot;userID&quot;, &quot;&quot;);
            password = pref.get(&quot;password&quot;, &quot;&quot;);
            if (!isValidUsernamePassword()) {
                if (loginDialog == null) {
                    loginDialog = new LoginDialog(null);
                }
                loginDialog.setVisible(true);
                userID = pref.get(&quot;userID&quot;, &quot;&quot;);
                password = pref.get(&quot;password&quot;, &quot;&quot;);
                if (!isValidUsernamePassword()) {
                    throw new IOException(&quot;Invalid username and/or password&quot;);
                }
            }
        }
        
        Authenticator.setDefault(singleton);
    }
        
    private static boolean isValidUsernamePassword() {
        return (userID != null &amp;amp;&amp;amp; userID.length() &amp;gt; 0 &amp;amp;&amp;amp;
                password != null &amp;amp;&amp;amp; password.length() &amp;gt; 0);
    }

    private TwitterWhatAreYouDoingServiceAuthenticator() {
    }

    @Override
    protected PasswordAuthentication getPasswordAuthentication() {
        return new PasswordAuthentication(userID, password.toCharArray());
    }

}
&lt;/pre&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;
Not much to it, is there? It doesn&#039;t fatten up our Canary very much at all.
&lt;/p&gt;
&lt;br /&gt;&lt;h3&gt;Creating The Login Screen&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;
Now we&#039;re going to make the login screen, where we collect the Twitter account and password information for our app. Making a separate form for the login fits in nicely with our desktop application framework, and allows us to once again use Preferences.userNodeForPackage to store the data. And once again I will warn you, this storage method is not secure and you are storing potentially sensitive information with it, so please use good judgment and common sense here, people.
&lt;/p&gt;&lt;br /&gt;
&lt;p&gt;
The login screen is pretty simple, and looks like this:
&lt;/p&gt;&lt;br /&gt;
&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/canary/ss009A.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:164 --&gt;&lt;img width=&quot;90&quot; height=&quot;61&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/canary/ss009A.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;
&lt;br /&gt;&lt;br /&gt;&lt;p&gt;
You can either make your own in NetBeans using the following code as a guideline or just swipe the files from the complete source at the end of this article. If you&#039;re new to this, please note that you can&#039;t just paste the code below somewhere and expect it to work, however. You have to actually create the form if you want to do it yourself!
&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;&lt;strong&gt;&lt;pre&gt;
/*
 * LoginDialog.java
 *
 * Created on September 8, 2008, 3:57 PM
 */

package canary;

import java.awt.Frame;
import java.util.prefs.Preferences;
import javax.swing.JOptionPane;
import org.jdesktop.application.Action;
import org.netbeans.saas.RestResponse;
import org.netbeans.saas.twitter.TwitterWhatAreYouDoingServiceAuthenticator;

/**
 *
 * @author  hulles
 */
public class LoginDialog extends javax.swing.JDialog {
    private final Preferences pref;
    private String userID;
    private String password;
    private final TwitterUtils twitter;
    private Frame parentFrame;
    private boolean loggedIn = false;

    /** Creates new form LoginDialog */
    public LoginDialog(Frame parent) {
        super(parent,true);
        pref = Preferences.userNodeForPackage(TwitterWhatAreYouDoingServiceAuthenticator.class);
        this.parentFrame = parent;
        initComponents();
        getRootPane().setDefaultButton(okButton);
        twitter = new TwitterUtils();
        userID = pref.get(&quot;userID&quot;, &quot;&quot;);
        password = pref.get(&quot;password&quot;, &quot;&quot;);
        userIDField.setText(userID);
        passwordField.setText(password);
    }

    @Action
    public void cancelAction() {
        loggedIn = false;
        setVisible(false);
    }

    public boolean successfulLogin() {
        return loggedIn;
    }
    
    @Action
    public void okAction() {
        String saveUserID;
        String savePassword;
        RestResponse result;
        String resultString;
        
        setVisible(false);
        loggedIn = false;
        
        // TODO: returns clear text, might want to change that
        userID = userIDField.getText();
        password = new String(passwordField.getPassword());
        saveUserID = pref.get(&quot;userID&quot;, &quot;&quot;);
        savePassword = pref.get(&quot;password&quot;, &quot;&quot;);
        pref.put(&quot;userID&quot;, userID);
        pref.put(&quot;password&quot;, password);
        result = twitter.verifyLogin();
        if (result == null) {
            JOptionPane.showMessageDialog(LoginDialog.this, 
                    &quot;Unable to verify Twitter login at this time.&quot;, &quot;Log In Error&quot;, JOptionPane.ERROR_MESSAGE);
            pref.put(&quot;userID&quot;, saveUserID);
            pref.put(&quot;password&quot;, savePassword);
            userID = saveUserID;
            password = savePassword;
        } else {
            System.out.println(&quot;LoginDialog: result = &quot; + result.getDataAsString());
            resultString = TwitterUtils.requestResponse(result);
            if (resultString != null) {
                JOptionPane.showMessageDialog(LoginDialog.this, resultString, &quot;Log In Error&quot;, 
                        JOptionPane.ERROR_MESSAGE);
                pref.put(&quot;userID&quot;, saveUserID);
                pref.put(&quot;password&quot;, savePassword);
                userID = saveUserID;
                password = savePassword;
            } else { 
                // it worked, leave prefs as is
                JOptionPane.showMessageDialog(LoginDialog.this, &quot;Login for &quot; + userID + &quot; successful.&quot;, 
                        &quot;Logged In To Twitter&quot;, JOptionPane.INFORMATION_MESSAGE);
                loggedIn = true;
            }
        }
    }

    // Variables declaration - do not modify                     
    private javax.swing.JButton cancelButton;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JButton okButton;
    private javax.swing.JPasswordField passwordField;
    private javax.swing.JTextField userIDField;
    // End of variables declaration                   

}
&lt;/pre&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;
All the login form really does is get the account ID and password from the user, try them out with Twitter&#039;s verifyLogin function and indicate success or failure. 
&lt;/p&gt;

&lt;br /&gt;&lt;h3&gt;Creating The Main Application Form&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;
We&#039;re getting close to having a bird that tweets, but we have yet to create the main application screen CanaryView.java. It is going to look like this when we&#039;re done:
&lt;/p&gt;&lt;br /&gt;
&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/canary/ss010.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:165 --&gt;&lt;img width=&quot;90&quot; height=&quot;61&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/canary/ss010.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;
&lt;br /&gt;&lt;br /&gt;&lt;p&gt;
We are using tabs for the four main Twitter functions we&#039;re providing: &quot;Recent&quot;, &quot;Update&quot;, &quot;Replies&quot; and &quot;Direct Msgs&quot;. &quot;Recent&quot; shows the most recent tweets from those we follow, &quot;Update&quot; lets us update our Twitter status (i.e. send a tweet), &quot;Replies&quot; shows @replies to us, and &quot;Direct Msgs&quot; shows direct messages to us.
&lt;/p&gt;&lt;br /&gt;&lt;p&gt;
The only &quot;File&quot; menu actions that we have are &quot;Log In&quot;, which invokes our login dialog, and &quot;Exit&quot;, which does pretty much what you would expect. I left the &quot;Help / About&quot; thing in the menu so you could play with it as you wish. I customized the application title and some other stuff in the resources for CanaryView; certainly you can modify this as well if you so desire. I also left the status bar, progress bar and timer stuff from the original Java Desktop Application framework intact so you can also tweak that.
&lt;/p&gt;&lt;br /&gt;&lt;p&gt;
Functionally, the form queries the Twitter servers once every five minutes for updated tweets from your pals. It caches status updates, dm&#039;s and replies in internal ArrayLists so we only ask for new stuff when we query the server (using the &quot;sinceID&quot; parameter which we also store). This is of course to satisfy our &quot;good Twitter citizen&quot; design goal, and Canary does just that quite nicely I believe. However, note that in this incarnation of Canary the caches are never cleaned out, just appended to, so if you let it run forever you&#039;re going to use up a lot of memory!
&lt;/p&gt;&lt;br /&gt;&lt;p&gt;
Something that may not be obvious from perusing the code is that, once we initially connect to the Twitter service, if routine status queries return errors Canary does not bitch or whine a bit, it just keeps displaying the queued tweets it has and silently keeps trying periodic queries like the saintly bird it is. There&#039;s a moral lesson in that somewhere I&#039;m sure. 
&lt;/p&gt;&lt;br /&gt;&lt;p&gt;
Another subtle feature of this application is the use of JEditorPanes to display the tweets&#039; HTML. I have a love/hate relationship with JEditorPanes, but for this project they work out okay. A JEditorPane will do a decent job of displaying HTML without much accompanying overhead &lt;em&gt;if you initialize it properly first&lt;/em&gt;. The little method &lt;strong&gt;initHTMLPane&lt;/strong&gt; does this for us. And this paragraph alone is worth the price of admission, ladies and gentlemen. I have lost many of my few remaining hairs wrestling with JEditorPanes in the last few months.
&lt;/p&gt;&lt;br /&gt;&lt;p&gt;
Yet another thing worth pointing out is that the update screen has a character counter that is updated as you type. This is because Twitter tweets are (essentially) limited to a maximum of 140 characters. This is important enough for user friendliness that the means of counting characters and enabling and disabling buttons is relatively sophisticated, which is why the DocumentListener stuff is in there.
&lt;/p&gt;&lt;br /&gt;&lt;p&gt;
And speaking of &quot;there,&quot; I suppose I should include the code for the form here as well. See the earlier caveat about copying and pasting it.
&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;&lt;strong&gt;&lt;pre&gt;
/*
 * CanaryView.java
 */

package canary;

import org.jdesktop.application.Action;
import org.jdesktop.application.ResourceMap;
import org.jdesktop.application.SingleFrameApplication;
import org.jdesktop.application.FrameView;
import org.jdesktop.application.TaskMonitor;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import java.util.prefs.Preferences;
import javax.swing.Timer;
import javax.swing.Icon;
import javax.swing.JDialog;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import org.netbeans.saas.RestResponse;
import twitter.whatareyoudoingservice.twitterresponse.DirectMessageType;
import twitter.whatareyoudoingservice.twitterresponse.StatusType;

/**
 * The application&#039;s main frame.
 */
public class CanaryView extends FrameView {
    private final Preferences pref;
    private static final boolean VERBOSE = true;
    private static final String UPDATINGMSG = &quot;&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;(updating...)&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&quot;;
    private static final String CONNECTMSG = &quot;&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;(unable to connect to Twitter)&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&quot;;
    private static final int MAXLIST = 10;
    private TwitterUtils twitter;
    private List&amp;lt;StatusType&amp;gt; recentList;
    private List&amp;lt;StatusType&amp;gt; repliesList;
    private List&amp;lt;DirectMessageType&amp;gt; msgsList;
    private int queryInterval;
    private Timer queryTimer;

    private final Timer messageTimer;
    private final Timer busyIconTimer;
    private final Icon idleIcon;
    private final Icon[] busyIcons = new Icon[15];
    private int busyIconIndex = 0;

    private JDialog aboutBox;
    private LoginDialog loginDialog;
    private final JFrame mainFrame;

    public CanaryView(SingleFrameApplication app) {
        super(app);
        
        twitter = new TwitterUtils();
        pref = Preferences.userNodeForPackage(CanaryView.class);
        initComponents();

        // status bar initialization - message timeout, idle icon and busy animation, etc
        ResourceMap resourceMap = getResourceMap();
        int messageTimeout = resourceMap.getInteger(&quot;StatusBar.messageTimeout&quot;);
        messageTimer = new Timer(messageTimeout, new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                statusMessageLabel.setText(&quot;&quot;);
            }
        });
        messageTimer.setRepeats(false);
        int busyAnimationRate = resourceMap.getInteger(&quot;StatusBar.busyAnimationRate&quot;);
        for (int i = 0; i &amp;lt; busyIcons.length; i++) {
            busyIcons[i] = resourceMap.getIcon(&quot;StatusBar.busyIcons[&quot; + i + &quot;]&quot;);
        }
        busyIconTimer = new Timer(busyAnimationRate, new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                busyIconIndex = (busyIconIndex + 1) % busyIcons.length;
                statusAnimationLabel.setIcon(busyIcons[busyIconIndex]);
            }
        });
        idleIcon = resourceMap.getIcon(&quot;StatusBar.idleIcon&quot;);
        statusAnimationLabel.setIcon(idleIcon);
        progressBar.setVisible(false);

        // connecting action tasks to status bar via TaskMonitor
        TaskMonitor taskMonitor = new TaskMonitor(getApplication().getContext());
        taskMonitor.addPropertyChangeListener(new java.beans.PropertyChangeListener() {
            public void propertyChange(java.beans.PropertyChangeEvent evt) {
                String propertyName = evt.getPropertyName();
                if (&quot;started&quot;.equals(propertyName)) {
                    if (!busyIconTimer.isRunning()) {
                        statusAnimationLabel.setIcon(busyIcons[0]);
                        busyIconIndex = 0;
                        busyIconTimer.start();
                    }
                    progressBar.setVisible(true);
                    progressBar.setIndeterminate(true);
                } else if (&quot;done&quot;.equals(propertyName)) {
                    busyIconTimer.stop();
                    statusAnimationLabel.setIcon(idleIcon);
                    progressBar.setVisible(false);
                    progressBar.setValue(0);
                } else if (&quot;message&quot;.equals(propertyName)) {
                    String text = (String)(evt.getNewValue());
                    statusMessageLabel.setText((text == null) ? &quot;&quot; : text);
                    messageTimer.restart();
                } else if (&quot;progress&quot;.equals(propertyName)) {
                    int value = (Integer)(evt.getNewValue());
                    progressBar.setVisible(true);


                    progressBar.setIndeterminate(false);
                    progressBar.setValue(value);
                }
            }
        });
        
        updateField.getDocument().addDocumentListener(new FieldListener());
        
        queryInterval = pref.getInt(&quot;queryInterval&quot;, 300000);
	queryTimer = new Timer(queryInterval, new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                refresh();
            }
        });

        if (VERBOSE) System.out.println(&quot;CanaryView: init&quot;);
        mainFrame = app.getMainFrame();
//        mainFrame = CanaryApp.getApplication().getMainFrame();
        if (!initialLogin()) {
            app.exit();
        }
        initHTMLPane(recentPane);
        initHTMLPane(directMsgsPane);
        initHTMLPane(repliesPane);
        recentList = new ArrayList&amp;lt;StatusType&amp;gt;(MAXLIST);
        repliesList = new ArrayList&amp;lt;StatusType&amp;gt;(MAXLIST);
        msgsList = new ArrayList&amp;lt;DirectMessageType&amp;gt;(MAXLIST);

        refresh();
    }

/***** STATUS UPDATES *****/
    
    private void showFriendsStatus() {
        List&amp;lt;StatusType&amp;gt; statusList;
        StringBuffer sBuf;
        String text;
        String startID;
        
        recentPane.setText(UPDATINGMSG);
        sBuf = new StringBuffer();
        startID = (recentList.isEmpty() ? null : recentList.get(0).getId());
        statusList = twitter.getFriendsStatus(startID);
        if (statusList == null) {
            if (startID == null) {
                // first time
                recentPane.setText(CONNECTMSG);
            }
            return;
        }
        statusList.addAll(recentList);
        recentList = statusList;
        sBuf.append(&quot;&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;&quot;);
        for (StatusType type : statusList) {
            sBuf.append(&quot;&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;&quot;);
            sBuf.append(type.getUser().getScreenName());
            sBuf.append(&quot;: &quot;);
            sBuf.append(&quot;&amp;lt;/strong&amp;gt;&quot;);
            text = TwitterUtils.tweetToHTML(type.getText());
            sBuf.append(text);
            sBuf.append(&quot;&amp;lt;/p&amp;gt;&quot;);
        }
        sBuf.append(&quot;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&quot;);
        recentPane.setText(sBuf.toString());
    }
    
    private void showReplies() {
        List&amp;lt;StatusType&amp;gt; statusList;
        StringBuffer sBuf;
        String text;
        String startID;
        
        repliesPane.setText(UPDATINGMSG);
        sBuf = new StringBuffer();
        startID = (repliesList.isEmpty() ? null : repliesList.get(0).getId());
        statusList = twitter.getReplies(startID);
        if (statusList == null) {
            if (startID == null) {
                // first time
                repliesPane.setText(CONNECTMSG);
            }
            return;
        } 
        statusList.addAll(repliesList);
        repliesList = statusList;
        sBuf.append(&quot;&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;&quot;);
        for (StatusType type : statusList) {
            sBuf.append(&quot;&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;&quot;);
            sBuf.append(type.getUser().getScreenName());
            sBuf.append(&quot;: &quot;);
            sBuf.append(&quot;&amp;lt;/strong&amp;gt;&quot;);
            text = TwitterUtils.tweetToHTML(type.getText());
            sBuf.append(text);
            sBuf.append(&quot;&amp;lt;/p&amp;gt;&quot;);
        }
        sBuf.append(&quot;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&quot;);
        repliesPane.setText(sBuf.toString());
    }
    
    private void showDirectMessages() {
        List&amp;lt;DirectMessageType&amp;gt; msgList;
        StringBuffer sBuf;
        String text;
        String startID;

        directMsgsPane.setText(UPDATINGMSG);
        sBuf = new StringBuffer();
        startID = (msgsList.isEmpty() ? null : msgsList.get(0).getId());
        msgList = twitter.getDirectMsgs(startID);
        if (msgList == null) {
            if (startID == null) {
                // first time
                directMsgsPane.setText(CONNECTMSG);
            }
            return;
        }
        msgList.addAll(msgsList);
        msgsList = msgList;
        sBuf.append(&quot;&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;&quot;);
        for (DirectMessageType type : msgList) {
            sBuf.append(&quot;&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;&quot;);
            sBuf.append(type.getSenderScreenName());
            sBuf.append(&quot;: &quot;);
            sBuf.append(&quot;&amp;lt;/strong&amp;gt;&quot;);
            text = TwitterUtils.tweetToHTML(type.getText());
            sBuf.append(text);
            sBuf.append(&quot;&amp;lt;/p&amp;gt;&quot;);
        }
        sBuf.append(&quot;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&quot;);
        directMsgsPane.setText(sBuf.toString());
    }
    
/***** HELPER FUNCTIONS *****/
    
    private void initHTMLPane(JEditorPane pane) {
        pane.setContentType(&quot;text/html;charset=UTF-8&quot;);
        pane.getEditorKit().createDefaultDocument();
        pane.setText(&quot;&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;(not updated)&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&quot;);
    }
    
    private final class FieldListener implements DocumentListener {

        public void insertUpdate(DocumentEvent dEvt) {
            updateCharCount();
        }

        public void removeUpdate(DocumentEvent arg0) {
            updateCharCount();
        }

        public void changedUpdate(DocumentEvent arg0) {
            updateCharCount();
        }
        
        private void updateCharCount() {
            int charCount;
            
            charCount = updateField.getText().length();
            charCountLabel.setText(charCount + &quot; of 140&quot;);
            sendButton.setEnabled((charCount &amp;lt;= 140) &amp;amp;&amp;amp; (charCount &amp;gt; 0));
            clearButton.setEnabled(charCount &amp;gt; 0);
        }
    }

    private boolean initialLogin() {
        RestResponse result;
        String resultString;
        boolean workedOK = false;
        
        result = twitter.verifyLogin();
        if (result == null) {
            JOptionPane.showMessageDialog(mainFrame, 
                    &quot;Unable to verify Twitter login at this time.&quot;, &quot;Log In Error&quot;, JOptionPane.ERROR_MESSAGE);
        } else {
            if (VERBOSE) System.out.println(&quot;LoginDialog: result = &quot; + result.getDataAsString());
            resultString = TwitterUtils.requestResponse(result);
            if (resultString != null) {
                JOptionPane.showMessageDialog(mainFrame, resultString, &quot;Log In Error&quot;, JOptionPane.ERROR_MESSAGE);
            } else { 
                workedOK = true;
            }
        }
        return workedOK;
    }
    
/***** ACTIONS *****/
    
    @Action
    public void logIn() {
        if (loginDialog == null) {
            loginDialog = new LoginDialog(mainFrame);
            loginDialog.setLocationRelativeTo(mainFrame);
        }
        CanaryApp.getApplication().show(loginDialog);
        if (loginDialog.successfulLogin()) {
            refresh();
        }
    }
    
    @Action
    public void showAboutBox() {
        if (aboutBox == null) {
            aboutBox = new CanaryAboutBox(mainFrame);
            aboutBox.setLocationRelativeTo(mainFrame);
        }
        CanaryApp.getApplication().show(aboutBox);
    }

    @Action
    public void refresh() {
        if (VERBOSE) System.out.println(&quot;CanaryView: refresh&quot;);
        showFriendsStatus();
        showReplies();
        showDirectMessages();
    }
    
    @Action
    public void clearAction() {
        updateField.setText(null);        
    }
    
    @Action
    public void sendUpdate() {
        RestResponse result;
        String resultString;

        result = twitter.updateStatus(updateField.getText());
        if (result == null) {
            JOptionPane.showMessageDialog(mainFrame, 
                    &quot;Unable to update your Twitter status at this time.&quot;,&quot;Status Update Error&quot;, 
                    JOptionPane.ERROR_MESSAGE);
            return;
        }
        resultString = TwitterUtils.requestResponse(result);
        if (resultString != null) {
            JOptionPane.showMessageDialog(mainFrame, resultString, &quot;Status Update Error&quot;, 
                    JOptionPane.ERROR_MESSAGE);
            return;
        }
        updateField.setText(null);   
    }

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JLabel charCountLabel;
    private javax.swing.JButton clearButton;
    private javax.swing.JEditorPane directMsgsPane;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JPanel jPanel2;
    private javax.swing.JPanel jPanel3;
    private javax.swing.JPanel jPanel4;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JScrollPane jScrollPane2;
    private javax.swing.JScrollPane jScrollPane3;
    private javax.swing.JScrollPane jScrollPane4;
    private javax.swing.JTabbedPane jTabbedPane1;
    private javax.swing.JMenuItem loginMenuItem;
    private javax.swing.JPanel mainPanel;
    private javax.swing.JMenuBar menuBar;
    private javax.swing.JProgressBar progressBar;
    private javax.swing.JEditorPane recentPane;
    private javax.swing.JEditorPane repliesPane;
    private javax.swing.JButton sendButton;
    private javax.swing.JLabel statusAnimationLabel;
    private javax.swing.JLabel statusMessageLabel;
    private javax.swing.JPanel statusPanel;
    private javax.swing.JTextArea updateField;
    // End of variables declaration//GEN-END:variables
}
&lt;/pre&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;br /&gt;

&lt;br /&gt;&lt;h3&gt;So Let&#039;s See If It Flies!&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;
At last it&#039;s time to see if our Canary can fly. Hit the &quot;Run&quot; button on the IDE task bar (assuming that Canary is set as the main project) and let &#039;er rip. Canary should compile without errors and give you a small form that looks like this, once you&#039;ve entered your account and password:
&lt;/p&gt;&lt;br /&gt;
&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/canary/ss012.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:167 --&gt;&lt;img width=&quot;90&quot; height=&quot;68&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/canary/ss012.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;
&lt;br /&gt;&lt;br /&gt;&lt;p&gt;
Woohoo! It works! (Like it worked for me the first time. As if.) Let it run on your PC and you can send and receive Twitter tweets to your heart&#039;s content without starting your bloated web browser every time you think your buddy might have finally left the grocery store and actually be doing something &lt;em&gt;interesting&lt;/em&gt; for a change.
&lt;/p&gt;&lt;br /&gt;&lt;p&gt;
If you want to get fancy, create a starter icon in your OS that invokes Canary (probably &quot;java -jar [wherever your Canary jar is]&quot;, per your compilation) and stick it onto your desktop or task bar. Congratulations. And if it &lt;strong&gt;doesn&#039;t&lt;/strong&gt; work, please let me know. I am unable to promise any support for this project on an individual basis but if I screwed something up I&#039;d like to know about it.
&lt;/p&gt;

&lt;br /&gt;&lt;h3&gt;What We&#039;ve Done&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;
If you&#039;ve gotten this far, you&#039;ve done something to be proud of -- you&#039;ve created a fun, useful and very sophisticated yet reasonably lightweight application without using exotic technology beyond POJOs (Plain Old Java Objects). Of course you had a lot of help from the frabjous NetBeans platform and, hopefully, yours truly. But still. The Canary is yours to do with what you will, so go crazy. And don&#039;t forget that other acronym, WORA: write once, run anywhere. Though I haven&#039;t spent the time to test this incarnation of the project, Canary should run flawlessly and seamlessly on Unix/Linux, Windows and even (some) Mac platforms. We like WORA. WORA good.
&lt;/p&gt;

&lt;br /&gt;&lt;h3&gt;Going Further&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;
There are lots of additions, changes and enhancements that can be made to Canary; here are some that I&#039;ve thought of:
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Change AboutBox to reflect Canary&#039;s coolicity&lt;/li&gt;
&lt;li&gt;Actually usse the busy icons, status bar, etc. from the stock desktop application framework or get rid of the code&lt;/li&gt;
&lt;li&gt;Create secure account ID and password storage&lt;/li&gt;
&lt;li&gt;Create functioning tweet hyperlinks (the hyperlinks in the EditorPanes don&#039;t actually work, and making them do so is beyond the scope of this article)&lt;/li&gt;
&lt;li&gt;The Direct Msgs tab should have its own way of sending direct messages as well as displaying them (drop down friend box, etc.)&lt;/li&gt;
&lt;li&gt;Add fins and an automatic headlight dimmer to Canary (i.e. make it prettier; I was sorely tempted to do this but managed to restrain myself just in time so I can leave it for you to do) (but the orignal one I did for my clients has a purple gradient background...) &lt;/li&gt;
&lt;/ul&gt;

&lt;br /&gt;&lt;h3&gt;Source Code and Jar&lt;/h3&gt;&lt;br /&gt;

&lt;br /&gt;Source code:&lt;br /&gt;&lt;br /&gt;
&lt;a href=&quot;http://hulles.supersized.org/uploads/canary/Canary_Source.tar.gz&quot; alt=&quot;&quot;&gt;Canary_Source.tar.gz&lt;/a&gt;
&lt;a onclick=&quot;javascript:urchinTracker(&#039;/download/uploads/canary/Canary_Source.zip&#039;);&quot; href=&quot;http://hulles.supersized.org/uploads/canary/Canary_Source.zip&quot; alt=&quot;&quot;&gt;Canary_Source.zip&lt;/a&gt;
&lt;br /&gt;&lt;br /&gt;Jar file (Java executable):&lt;br /&gt;&lt;br /&gt;
&lt;a onclick=&quot;javascript:urchinTracker(&#039;/download/uploads/canary/Canary_Dist.zip&#039;);&quot; href=&quot;http://hulles.supersized.org/uploads/canary/Canary_Dist.zip&quot; alt=&quot;&quot;&gt;Canary_Dist.zip&lt;/a&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;br /&gt;&lt;h3&gt;A Final Note&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;
I am currently &lt;em&gt;not&lt;/em&gt; planning on doing a follow-up article on Canary revisions and enhancements because I can&#039;t afford the time that these things take (LOTS!) but I may not be able to help myself. Also, see &quot;Donate&quot; button.
&lt;/p&gt;&lt;br /&gt;&lt;p&gt;
I hope you found this article entertaining, enlightening and useful. And if you have comments, I would love to hear from you. Namaste.
&lt;/p&gt;&lt;br /&gt;
-- Hulles




 
    </content:encoded>

    <pubDate>Mon, 24 Nov 2008 15:59:23 +0100</pubDate>
    <guid isPermaLink="false">http://hulles.supersized.org/archives/15-guid.html</guid>
    
</item>
<item>
    <title>Son Of GeeWhiz Prolog: Olio</title>
    <link>http://hulles.supersized.org/archives/14-Son-Of-GeeWhiz-Prolog-Olio.html</link>
            <category>NetBeans</category>
    
    <comments>http://hulles.supersized.org/archives/14-Son-Of-GeeWhiz-Prolog-Olio.html#comments</comments>
    <wfw:comment>http://hulles.supersized.org/wfwcomment.php?cid=14</wfw:comment>

    <slash:comments>0</slash:comments>
    <wfw:commentRss>http://hulles.supersized.org/rss.php?version=2.0&amp;type=comments&amp;cid=14</wfw:commentRss>
    

    <author>nospam@example.com (Hulles)</author>
    <content:encoded>
    &lt;p&gt;&lt;em&gt;This article describes an enhancement to GeeWhiz Prolog, a project to implement the Prolog language in the NetBeans IDE. See the &lt;a href=&quot;http://hulles.supersized.org/pages/geewhiz.html&quot; alt=&quot;GeeWhiz Prolog Home Page&quot;&gt;GeeWhiz Prolog home page&lt;/a&gt; for more information.&lt;/em&gt;&lt;/p&gt;
&lt;br /&gt;
&lt;br /&gt;&lt;h3&gt;What We&#039;ll Be Doing&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;In this installment of &lt;strong&gt;Son of GeeWhiz Prolog&lt;/strong&gt; there really won&#039;t be much tutorial content. I&#039;ll do my best, but to be honest it&#039;s been long enough since I made the changes described here that I haven&#039;t been able to reassemble my notes properly, and I misplaced my screenshots. However I thought it would be better to post the new version of the source and NBM on the &lt;a href=&quot;http://hulles.supersized.org/pages/geewhiz.html&quot; alt=&quot;GeeWhiz Prolog Home Page&quot;&gt;GeeWhiz Prolog home page&lt;/a&gt; rather than wait any longer. So here you go. I am not including separate source code in this article, so you&#039;ll need to download the source on the Home Page to find the changes. Dang, sorry.&lt;/p&gt;
&lt;br /&gt;&lt;h3&gt;Moving the Satellite Window to the Navigator&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;Probably the most obvious change is that the satellite view of the Prolog diagram has been transformed from a standalone JFrame window into a TopComponent that nestles into the Navigator window area. This is more convenient as it stays out of the way but remains accessible as you scroll around the larger diagram. It also makes us a &quot;better NetBeans citizen&quot;: standalone JFrames &lt;strong&gt;bad&lt;/strong&gt;, TopComponents &lt;strong&gt;good&lt;/strong&gt;. We&#039;re leaving the popup code JFrame windows alone however, so I guess we&#039;re not &lt;em&gt;that&lt;/em&gt; good a NetBeans citizen.&lt;/p&gt;&lt;br /&gt;
&lt;p&gt;Another change is that we got rid of VPrologAction.java, which was the action living in the &quot;Window&quot; menu that opened up the demo/dummy diagram. Along with that, we eliminated the code in VPrologGraphScene to create the dummy diagram, and we added code to VPrologTopComponent to open and close the satellite navigator component. The final change regarding the satellite window is that we changed the &quot;show / hide satellite&quot; action in the right-click context menu in the main diagram. It is now just &quot;show satellite&quot;, since you can close the satellite window like any other window whenever you want.&lt;/p&gt;

&lt;br /&gt;&lt;h3&gt;Minor Stuff&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;In the process of cleaning up GeeWhiz, it seemed to make more sense for the popup editor code to live in SceneUtils, so there it now resides. I also changed the phrase &quot;1 instances&quot; to read &quot;1 instance&quot; in the case where there is only 1 instance. Hey, it is &lt;em&gt;too&lt;/em&gt; rocket science.&lt;/p&gt;

&lt;br /&gt;&lt;h3&gt;Problems, Setbacks and Failures&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;For Son 2 (this article) I wanted to experiment with FreeMarker, which is a scripting language that one can use to insert text into a template. This can automatically insert licensing information or whatever else you want into our trusty Prolog program template. Unfortunately I could not get it to work reliably. In the newest version of the source code I left the FreeMarker code in, but every time I tried to execute it outside of the top level of the source code I got a cryptic error:&lt;/p&gt;
&lt;br /&gt;
&lt;strong&gt;&lt;pre&gt;
WARNING [org.openide.WizardDescriptor]
java.lang.StringIndexOutOfBoundsException: String index out of range: -1
	at java.lang.String.substring(String.java:1938)
	at java.lang.String.substring(String.java:1905)
	...
&lt;/pre&gt;&lt;/strong&gt;
&lt;br /&gt;
&lt;p&gt;Ouch. As I mentioned this only happened when the template was not in the top level of the source, for some reason. I puzzled over it for a while then gave it up as a bad job. But I thought I&#039;d pass it along anyway. I guess GeeWhiz Prolog was not destined to be the kind of application that has source code licenses....&lt;/p&gt;&lt;br /&gt;
&lt;p&gt;Another experiment I thought I would try was to use the capability of the Visual Library to directly associate objects with scene nodes. This would allow us to eliminate the Prolog object lookup we need to do to translate from node to Prolog clause. Hmmm, I thought, simpler is better, it sounds good. However, I hammered away at associating any kind of object with the nodes in our diagram scene and kept getting assertion errors in the Visual Library that didn&#039;t tell me what the hell was wrong. I finally traced the VL source code and found out that in a GraphScene there is already an object associated with a node that lurks behind the scenes. At least I think that&#039;s what&#039;s happening; it looks like a String is hooked up to a node as an object, and you can only have one object per node. Consequently, we can&#039;t hook up our Prolog clauses to nodes that way unless we quit using GraphScene, which is a poor trade. So the list association we started with remains intact, bloody but unbowed. As a footnote, I&#039;ve used the Visual Library a great deal since the experiment I described above, and every time I get an accursed assertion error from VL I know that it&#039;s a problem with trying to add multiple objects to a node. That would probably be a good thing to fix in the next release of the Visual Library. And it&#039;s a good thing to know if you&#039;re working with it.&lt;/p&gt;&lt;br /&gt;
&lt;p&gt;Almost forgot - another problem I encountered has nothing directly to do with NetBeans: I have been trying to get some HTML windows to pop up from this blog platform that don&#039;t have the sidebars etc. so I can better display a few &lt;strong&gt;&lt;large&gt;movies&lt;/large&gt;&lt;/strong&gt; I made using Wink that demonstrate the use of GeeWhiz Prolog. I haven&#039;t been able accomplish that so I am posting the Flash videos just as links on the GeeWhiz Prolog home page. Check them out. I created the movies so that people who are interested in Prolog but who don&#039;t care to know anything about NetBeans can see what the program does without going through the process of downloading NetBeans and learning the IDE. I&#039;m nice like that.&lt;/p&gt;

&lt;br /&gt;&lt;h3&gt;Going Further&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;Well, sort of a lame &lt;strong&gt;Son of GeeWhiz Prolog&lt;/strong&gt;, I know, but I didn&#039;t want to post the updated source code and not provide an explanation to go with it. I don&#039;t have plans to post anything new here for a while as I am in the middle of a large project utilizing NetBeans, unless.... Well, we&#039;ll see.&lt;/p&gt;
&lt;br /&gt;
&lt;br /&gt;&lt;h3&gt;Files From This Entry&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;(see the newly posted source code zip files and NBM on the &lt;a href=&quot;http://hulles.supersized.org/pages/geewhiz.html&quot; alt=&quot;GeeWhiz Prolog Home Page&quot;&gt;GeeWhiz Prolog home page&lt;/a&gt;.)&lt;/p&gt;
&lt;br /&gt; 
    </content:encoded>

    <pubDate>Thu, 07 Aug 2008 21:10:57 +0200</pubDate>
    <guid isPermaLink="false">http://hulles.supersized.org/archives/14-guid.html</guid>
    
</item>
<item>
    <title>Son Of GeeWhiz Prolog: Improving The Diagram</title>
    <link>http://hulles.supersized.org/archives/12-Son-Of-GeeWhiz-Prolog-Improving-The-Diagram.html</link>
            <category>NetBeans</category>
    
    <comments>http://hulles.supersized.org/archives/12-Son-Of-GeeWhiz-Prolog-Improving-The-Diagram.html#comments</comments>
    <wfw:comment>http://hulles.supersized.org/wfwcomment.php?cid=12</wfw:comment>

    <slash:comments>5</slash:comments>
    <wfw:commentRss>http://hulles.supersized.org/rss.php?version=2.0&amp;type=comments&amp;cid=12</wfw:commentRss>
    

    <author>nospam@example.com (Hulles)</author>
    <content:encoded>
    &lt;p&gt;&lt;em&gt;This article describes an enhancement to GeeWhiz Prolog, a project to implement the Prolog language in the NetBeans IDE. See the &lt;a href=&quot;http://hulles.supersized.org/pages/geewhiz.html&quot; alt=&quot;GeeWhiz Prolog Home Page&quot;&gt;GeeWhiz Prolog home page&lt;/a&gt; for more information.&lt;/em&gt;&lt;/p&gt;
&lt;br /&gt;
&lt;br /&gt;&lt;h3&gt;What We&#039;ll Be Doing&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;In this add-on to our GeeWhiz Prolog project, we&#039;re going to add some new features and Geewhizability to our predicate modeler. Big thanks go to David Kaspar who provided the Visual Library examples from which much of this material is &lt;strike&gt;stolen&lt;/strike&gt; taken. The examples are located in the &quot;main/contrib&quot; section of the NetBeans Mercurial repository (although I haven&#039;t updated my own library yet to reflect this). You can find them at this link: &lt;a onclick=&quot;javascript:urchinTracker(&#039;/extlink/hg.netbeans.org/main/contrib/file/&#039;);&quot; href=&quot;http://hg.netbeans.org/main/contrib/file/&quot; title=&quot;Visual Library examples&quot;&gt;http://hg.netbeans.org/main/contrib/file/&lt;/a&gt; under &quot;visual.examples&quot;.&lt;/p&gt;
&lt;br /&gt;&lt;h3&gt;Adding An Export Function&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;The first change we&#039;re going to make to our nifty Prolog predicate diagrammer is that we&#039;re going to add a means by which we can export our diagram to a Portable Network Graphics (PNG) file. Once we have a PNG file, we can print it, email it to Aunt Margaret in Philly, or (hypothetically) stick it on our GeeWhiz Prolog home page. So let&#039;s get started.&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;First, create a new Java class in your project called &quot;SceneExport&quot;. Replace the empty class with this:&lt;/p&gt;
&lt;br /&gt;
&lt;strong&gt;&lt;pre&gt;
public class SceneExport {

    public void exportScene(VMDGraphScene scene) {
        JComponent view;
        Dimension dim;
        BufferedImage bufImage;
        Graphics2D graphics;
        File file;
        
        view = scene.getView();
        dim = view.getSize();
        bufImage = new BufferedImage (dim.width, dim.height,
                BufferedImage.TYPE_4BYTE_ABGR);
        graphics = bufImage.createGraphics ();
        scene.paint (graphics);
        graphics.dispose ();
        file = getPNGSaveFile(view);
        if (file != null) {
            diagramToPNG(file, bufImage);
        }
    }
    
    private File getPNGSaveFile (JComponent view) {
        JFileChooser chooser;
        File file;
        
        //TODO: save selected directory as default for next time
        chooser = new JFileChooser();
        chooser.setDialogTitle(&quot;Export Scene As ...&quot;);
        chooser.setDialogType(JFileChooser.SAVE_DIALOG);
        chooser.setMultiSelectionEnabled(false);
        chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
        chooser.setFileFilter(new FileFilter() {
            public boolean accept(File file) {
                if (file.isDirectory())
                    return true;
                return file.getName().toLowerCase().endsWith(&quot;.png&quot;);
            }
            public String getDescription () {
                return &quot;Portable Network Graphics (.png)&quot;;
            }
        });
        if (chooser.showSaveDialog (view) != JFileChooser.APPROVE_OPTION) {
            return null;
        }
        
        file = chooser.getSelectedFile ();
        if (! file.getName ().toLowerCase ().endsWith (&quot;.png&quot;))
            file = new File (file.getParentFile (), file.getName () + &quot;.png&quot;);
        if (file.exists ()) {
            DialogDescriptor descriptor = new DialogDescriptor (
                    &quot;File (&quot; + file.getAbsolutePath () + 
                    &quot;) already exists. Do you want to overwrite it?&quot;,
                    &quot;File Exists&quot;, true, DialogDescriptor.YES_NO_OPTION,
                    DialogDescriptor.NO_OPTION, null);
            DialogDisplayer.getDefault().createDialog(descriptor).setVisible(true);
            if (descriptor.getValue() != DialogDescriptor.YES_OPTION) {
                return null;
            }
        }
        return file;
    }
    
    private void diagramToPNG(File file, BufferedImage bufImage) {
        
        try {
            ImageIO.write (bufImage, &quot;png&quot;, file); // NOI18N
        } catch (IOException e) {
            ErrorManager.getDefault ().notify (e);
        }
    }
}
&lt;/pre&gt;&lt;/strong&gt;
&lt;br /&gt;
&lt;p&gt;What the above code does is copy the predicate diagram graphics to a BufferedImage then save the image to a PNG file. Pretty straightforward, and taken from Visual Library example code that I haven&#039;t been able to find again. So fix imports, save and close the SceneExport class then create a new Java class called &quot;SceneUtils&quot;. Replace the empty class with this code:&lt;/p&gt;
&lt;strong&gt;&lt;pre&gt;
public class SceneUtils {
    private static final String EXPORT_SCENE = &quot;export&quot;;
    private static final String ACTION_MOVE = &quot;move&quot;;
    private static final String REMOVE_LABEL = &quot;remove&quot;;
    private static final String REPLACE_LABEL = &quot;replace&quot;;
    private LabelWidget label;
    private Scene scene = null;
    private boolean isVMD;
    
    public SceneUtils(Scene scene) {
        this.scene = scene;
        isVMD = scene instanceof VMDGraphScene;
    }

    public void addContextMenu() {
        WidgetAction popup;
        
        if (scene == null) {
            return;
        }
        label = new LabelWidget (scene, &quot;Right-click to open popup menu.&quot;);
        label.setPreferredLocation (new Point (100, 100));
        scene.addChild (label);
        
        popup = ActionFactory.createPopupMenuAction (new MyPopupProvider ());

        scene.getActions ().addAction (popup);

        // leave popup menu up (?)
        scene.createActions (ACTION_MOVE).addAction (popup); 
        label.createActions (ACTION_MOVE).addAction (ActionFactory.
                createMoveAction ());
    }

    private void exportScene() {
        SceneExport exporter;

        if (isVMD) {
            // TODO: thread
            exporter = new SceneExport();
            exporter.exportScene((VMDGraphScene) scene);
        }
    }
    
    private void addMoveAction() {
        
        scene.setActiveTool(ACTION_MOVE);
    }
    
    private void removeLabel() {
        
        // removes it from the scene, doesn&#039;t delete it
        this.label.removeFromParent();
    }
    
    private void replaceLabel() {
        if (label.getParentWidget() == null) {
            scene.addChild(label);
            scene.validate();
        }
    }
    
    private final class MyPopupProvider implements PopupMenuProvider, 
            ActionListener {
        private JPopupMenu menu;

        public MyPopupProvider () {
            menu = new JPopupMenu (&quot;Popup menu&quot;);
            JMenuItem item;

            if (isVMD) {
                item = new JMenuItem (&quot;Export scene to PNG...&quot;);
                item.setActionCommand (EXPORT_SCENE);
                item.addActionListener (this);
                menu.add (item);
            }

            item = new JMenuItem (&quot;Move the label&quot;);
            item.setActionCommand (ACTION_MOVE);
            item.addActionListener (this);
            menu.add (item);

            item = new JMenuItem (&quot;Delete the label&quot;);
            item.setActionCommand (REMOVE_LABEL);
            item.addActionListener (this);
            menu.add (item);

            item = new JMenuItem (&quot;Replace the label&quot;);
            item.setActionCommand (REPLACE_LABEL);
            item.addActionListener (this);
            menu.add (item);

        }

        public JPopupMenu getPopupMenu (Widget widget, Point localLocation) {
            return menu;
        }

        public void actionPerformed (ActionEvent e) {
            String cmd;
            
            cmd = e.getActionCommand ();
            if (cmd.equals(EXPORT_SCENE)) {
                exportScene();
            } else if (cmd.equals(ACTION_MOVE)) {
                addMoveAction();
            } else if (cmd.equals(REMOVE_LABEL)) {
                removeLabel();
            } else if (cmd.equals(REPLACE_LABEL)) {
                replaceLabel();
            }
        }
    }
}
&lt;/pre&gt;&lt;/strong&gt;
&lt;br /&gt;
&lt;p&gt;In SceneUtils we&#039;re creating a context menu that pops up when you right-click on our lovely model diagram. In this incarnation it was written to cope with any kind of Scene even though the export function only handles VMDGraphScene. The menu includes some choices that are of limited utility in practice but serve to demonstrate the capability of the right-click popup. We also introduce the LabelWidget, which we haven&#039;t seen before. So fix imports, save and close the SceneUtils file.&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;Finally, we&#039;re going to change the VPrologGraphScene class to use our SceneUtils class. At the beginning of each constructor, add the following code:&lt;/p&gt;
&lt;br /&gt;
&lt;strong&gt;&lt;pre&gt;
        SceneUtils sceneUtils;

        sceneUtils = new SceneUtils(this);
        sceneUtils.addContextMenu();
&lt;/pre&gt;&lt;/strong&gt;

&lt;br /&gt;&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/geewhiz/son1-0a.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:125 --&gt;&lt;img width=&quot;90&quot; height=&quot;61&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/geewhiz/son1-0a.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;&lt;br /&gt;

&lt;p&gt;At the very end of each constructor, add this line:
&lt;br /&gt;
&lt;strong&gt;&lt;pre&gt;
        layoutScene();
&lt;/pre&gt;&lt;/strong&gt;
&lt;br /&gt;
&lt;p&gt;I found the &quot;layoutScene&quot; method by accident when I was working through some of David Kaspar&#039;s Visual Library examples. It automatically cleans up our diagram! Evidentally there is a more robust orthogonal link router in the works, but for our purposes this one works just fine and eliminates much of the haphazardness of the random way we were placing nodes before. Woohoo!&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;Save and close the file. Now we&#039;re ready to test the beast. Clean and build the project, then install it to the target IDE. (You missed doing that since completing the original GeeWhiz project, didn&#039;t you?) Open your Algorithms project and wait for the indexing to stop. Now we&#039;re ready to check out our new popup menu. Open the sieve.pro file (or any other Prolog file) and wait for the Navigator to fill in the statements and for the syntax to color. Then go to the menu and click &quot;View&quot; &quot;Show Prolog Diagram&quot; to bring up the predicate model of our program. (&quot;Gee whiz!&quot;) Right-click on the diagram to get our popup menu.

&lt;br /&gt;&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/geewhiz/son1-1a.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:126 --&gt;&lt;img width=&quot;90&quot; height=&quot;63&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/geewhiz/son1-1a.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;&lt;br /&gt;

First, try moving, deleting and re-adding the label. Then, when you&#039;re tired of playing with that, try exporting the diagram to a PNG file. Cool, eh? If you monkey around with it enough, however, you&#039;ll notice that if you zoom and pan the diagram prior to exporting the PNG the export doesn&#039;t work exactly right. For now my solution for this glitch is: &lt;em&gt;don&#039;t zoom or pan the diagram prior to exporting the PNG&lt;/em&gt;. Someday I&#039;ll look into fixing this; I think that maybe I need to get the dimensions from something other than the view graphics. But we have other things to do now, so let&#039;s leave it alone.&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;And by the way, don&#039;t forget to gasp with delight at the way layoutScene() fixes up our diagram when it is first displayed! When you&#039;re done testing and fooling around you can close the target IDE.&lt;/p&gt;
&lt;br /&gt;&lt;h3&gt;Adding Editor Windows To Nodes&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;The next feature we&#039;re going to add is a window containing the text Prolog source code for the predicate that opens up when we double-click on a predicate node. You wondered why we built the text into our PrologClause class, didn&#039;t you? This is why. First, create a new  JFrame Java class called &quot;NodeContents&quot; by right-clicking on the package and selecting &quot;New&quot; &quot;JFrame Form...&quot; This is going to be the window in which we display our source code. In the &quot;Design&quot; view, add a scroll pane that snaps to the preferred borders of the frame and then add a text area that fills the scroll pane. Rename the text area to &quot;textArea&quot;, then in Properties give it a descriptive tool tip, unclick the &quot;editable&quot; property and change the tab size to 4. In the JFrame properties, change the defaultCloseOperation to &quot;HIDE&quot;. Switch to &quot;Source&quot; and delete the Main method. Right below the constructor, insert the following code:&lt;/p&gt;
&lt;br /&gt;
&lt;strong&gt;&lt;pre&gt;
    public void setText(String text) {
        textArea.setText(text);
    }
    
    public void changeTitle(String title) {
        this.setTitle(title);
    }
&lt;/pre&gt;&lt;/strong&gt;
&lt;br /&gt;
&lt;p&gt;Save and close the file. Now open VPrologGraphScene and replace the whole VPrologGraphScene class with this:&lt;/p&gt;
&lt;br /&gt;
&lt;strong&gt;&lt;pre&gt;
 public class VPrologGraphScene extends VMDGraphScene {
    private static final Image IMAGE_NODE = Utilities.
            loadImage (&quot;org/hulles/geewhiz/node.png&quot;); // NOI18N
    private static final Image IMAGE_EXTERNAL = Utilities.
            loadImage (&quot;org/hulles/geewhiz/external.png&quot;); // NOI18N
    private static final Image IMAGE_ITEM = Utilities.
            loadImage (&quot;org/hulles/geewhiz/item.gif&quot;); // NOI18N
    private static int pinID = 1;
    private static int edgeID = 1;
    private List&amp;lt;PrologClause&amp;gt; clauses;

    /** Creates a new instance of VPrologGraphScene */
    public VPrologGraphScene() { // demo
        SceneUtils sceneUtils;

        sceneUtils = new SceneUtils(this);
        sceneUtils.addContextMenu();
        
        createNode (this, 100, 100, IMAGE_NODE, &quot;Clause1&quot;, &quot;Internal&quot;, null);
        createPin (this, &quot;Clause1&quot;, &quot;start&quot;, IMAGE_ITEM, &quot;Start&quot;, &quot;Element&quot;);
        createNode (this, 400, 100, IMAGE_NODE, &quot;Clause2&quot;, &quot;External&quot;,
                Arrays.asList (IMAGE_EXTERNAL));
        createPin (this, &quot;Clause2&quot;, &quot;ok&quot;, IMAGE_ITEM, &quot;okCommand1&quot;, &quot;Command&quot;);
        createEdge (this, &quot;start&quot;, &quot;Clause2&quot;);
        createEdge (this, &quot;ok&quot;, &quot;Clause1&quot;);
        layoutScene();
    }
    public VPrologGraphScene(DataObject dObj) {
        PrologAST pTree;
        List&amp;lt;PrologClause&amp;gt; embedded;
        String nodeID;
        Integer instances;
        String newPinID;
        PrologClause existingClause;
        String eNodeID;
        List&amp;lt;String&amp;gt; eNodes;
        SceneUtils sceneUtils;
        VMDNodeWidget node;

        sceneUtils = new SceneUtils(this);
        sceneUtils.addContextMenu();
        
        pTree = new PrologAST(dObj);
        clauses = pTree.getClauses();
        // create all primary nodes first so they&#039;re available
        //   to create edges to....
        for (PrologClause clause : clauses) {
            instances = clause.getInstanceCount();
            nodeID = makeNodeID(clause);
            node = createNode(this, randXPoint(), randYPoint(), 
                    IMAGE_NODE, nodeID, instances.toString() + &quot; instances&quot;, null);
            addEditor(node, clause);
        }
        // now create pins and edges
        eNodes = new ArrayList&amp;lt;String&amp;gt;();
        for (PrologClause clause : clauses) {
            nodeID = makeNodeID(clause);
            embedded = clause.getBody();
            for (PrologClause e : embedded) {
                eNodeID = makeNodeID(e);
                newPinID = &quot;pin&quot; + VPrologGraphScene.pinID++;
                createPin(this, nodeID, newPinID, 
                        IMAGE_ITEM, eNodeID, &quot;Embedded&quot;);
                existingClause = PrologClause.findClause(clauses, e);
                if (existingClause == null) {
                    if (!eNodes.contains(eNodeID)) {
                    // externally defined (?)
                        createNode(this, randXPoint(), randYPoint(), 
                                IMAGE_NODE, eNodeID, &quot;External&quot;,
                                Arrays.asList (IMAGE_EXTERNAL));
                        eNodes.add(eNodeID);
                    }
                }
                createEdge(this, newPinID, eNodeID);
            }
        }
        layoutScene();
    }

    private String makeNodeID(PrologClause clause) {
        String name;
        Integer arity;
        
        name = clause.getName();
        arity = clause.getArity();
        return name + &quot;/&quot; + arity.toString();
    }
    
    private int randXPoint() {
        return (int) (Math.random() * 800);
    }
    
    private int randYPoint() {
        return (int) (Math.random() * 800);
    }
    
    private VMDNodeWidget createNode (VMDGraphScene scene, int x, int y, 
            Image image, String name, String type, List&amp;lt;Image&amp;gt; glyphs) {
        VMDNodeWidget widget;
        
        widget = (VMDNodeWidget) scene.addNode (name);
        widget.setPreferredLocation (new Point (x, y));
        widget.setNodeProperties (image, name, type, glyphs);
        scene.addPin (name, name + VMDGraphScene.PIN_ID_DEFAULT_SUFFIX);
        return widget;
    }

    private void createPin (VMDGraphScene scene, String nodeID, String pinID, 
            Image image, String name, String type) {
        VMDPinWidget pinWidget;
        
        pinWidget = (VMDPinWidget) scene.addPin (nodeID, pinID);
        pinWidget.setProperties (name, null);
    }

    private void createEdge (VMDGraphScene scene, String sourcePinID, 
            String targetNodeID) {
        String localEdgeId;
        
        localEdgeId = &quot;edge&quot; + VPrologGraphScene.edgeID ++;
        scene.addEdge (localEdgeId);
        scene.setEdgeSource (localEdgeId, sourcePinID);
        scene.setEdgeTarget (localEdgeId, targetNodeID + 
                VMDGraphScene.PIN_ID_DEFAULT_SUFFIX);
    }

    private void addEditor(VMDNodeWidget node, PrologClause clause) {
        NodeEditor editor;
        WidgetAction nodeAction;

        editor = new NodeEditor(clause);
        nodeAction = ActionFactory.createEditAction(editor);
        node.getActions().addAction(nodeAction);
    }
    
    public void newLayout() {
        layoutScene();
    }
    
    private class NodeEditor implements EditProvider {
        private final PrologClause clause;
        private final String popupTitle;
        private final String popupText;
        private NodeContents popup;
        
        public NodeEditor(PrologClause clause) {
            this.clause = clause;
            this.popup = null;
            popupTitle = makeNodeID(clause);
            popupText = clause.getText();
        }

        public void edit(Widget node) {
            // assumes read-only text
            VMDNodeWidget myNode;
            
            myNode = (VMDNodeWidget) node;
            
            // lazy edit window construction
            if (popup == null) {
                java.awt.EventQueue.invokeLater(new Runnable() {
                    public void run() {
                        popup = new NodeContents();
                        popup.changeTitle(popupTitle);
                        popup.setText(popupText);
                        popup.setVisible(true);
                    }
                });
            } else {
                popup.setVisible(true);
            }
        }
        
    }
}
&lt;/pre&gt;&lt;/strong&gt;
&lt;br /&gt;
&lt;p&gt;What we&#039;re doing here is, for every defined node (as opposed to external node) that we create, we&#039;re adding an EditAction that creates a NodeContents window when the node is double-clicked. We hold off actually creating the NodeContents window until the node is double-clicked so we can be a good NetBeans neighbor. Also, notice that instead of disposing of the window when we close it we just hide it so we can set it visible again the next time it&#039;s clicked.&lt;/p&gt;
&lt;br /&gt;&lt;h3&gt;Adding A Satellite Window&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;Before we test the editor functionality we&#039;re going to go ahead and make another window that shows the entire diagram in miniature, called in Visual Library terms a satellite view. We&#039;re going to show it and hide it &lt;em&gt;via&lt;/em&gt; our context menu. So create another new JFrame called NodeSatelliteView.&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;In &quot;Design&quot; mode add a panel to the frame that takes up the whole frame and make it &quot;BorderLayout&quot;. Change the name from jPanel1 to satellitePanel and give it a tooltip &quot;Prolog diagram satellite view&quot;. Change the JFrame defaultCloseOperation to &quot;HIDE&quot; and the title to &quot;Prolog Satellite View&quot;.
Switch to &quot;Source&quot; mode, remove the Main method, and add this method below the constructor.&lt;/p&gt;
&lt;br /&gt;
&lt;strong&gt;&lt;pre&gt;
    public void addView(JComponent view) {
        
        satellitePanel.removeAll();
        satellitePanel.add(view);
    }
&lt;/pre&gt;&lt;/strong&gt;
&lt;br  /&gt;
&lt;p&gt;Save and close the file. Now open &quot;SceneUtils&quot; and replace the entire class with this.&lt;/p&gt;
&lt;br /&gt;
&lt;strong&gt;&lt;pre&gt;
public class SceneUtils {
    private static final String EXPORT_SCENE = &quot;export&quot;; // NOI18N
    private static final String LAYOUT_SCENE = &quot;layout&quot;; // NOI18N
    private static final String PRINT_SCENE = &quot;print&quot;; // NOI18N
    private static final String SATELLITE_VIEW = &quot;satellite&quot;; // NOI18N
    private VPrologGraphScene scene = null;
    private NodeSatelliteView viewer;
    
    public SceneUtils(VPrologGraphScene scene) {
        this.scene = scene;
    }

    public void addContextMenu() {
        WidgetAction popup;
        
        if (scene == null) {
            return;
        }
        
        popup = ActionFactory.createPopupMenuAction (new MyPopupProvider ());
        scene.getActions ().addAction (popup);
        // leave popup menu up (?)
        scene.createActions (LAYOUT_SCENE).addAction (popup);
    }

    private void exportScene() {
        SceneExport exporter;

        // TODO: thread
        exporter = new SceneExport();
        exporter.exportScene((VMDGraphScene) scene);
    }
    
    private void reLayoutScene() {
        
         scene.newLayout();
    }
    
    private void printScene() {
        // not yet implemented
    }
    
    private void makeSatelliteView() {
        
        if (viewer == null) {
            java.awt.EventQueue.invokeLater(new Runnable() {
                public void run() {
                    JComponent view;

                    viewer = new NodeSatelliteView();
                    view = scene.createSatelliteView();
                    viewer.addView(view);
                    viewer.setVisible(true);
                }
            });
        } else {
            if (viewer.isVisible()) {
                viewer.setVisible(false);
            } else {
                viewer.setVisible(true);
            }
        }
        
    }
    
    private final class MyPopupProvider implements PopupMenuProvider, ActionListener {
        private JPopupMenu menu;

        public MyPopupProvider () {
            menu = new JPopupMenu (&quot;Popup menu&quot;);
            JMenuItem item;

            item = new JMenuItem (&quot;Print scene...&quot;);
            item.setActionCommand (PRINT_SCENE);
            item.addActionListener (this);
            menu.add (item);

            item = new JMenuItem (&quot;Export scene to PNG...&quot;);
            item.setActionCommand (EXPORT_SCENE);
            item.addActionListener (this);
            menu.add (item);

            item = new JMenuItem (&quot;Redo scene layout&quot;);
            item.setActionCommand (LAYOUT_SCENE);
            item.addActionListener (this);
            menu.add (item);

            item = new JMenuItem (&quot;Show / hide satellite view&quot;);
            item.setActionCommand (SATELLITE_VIEW);
            item.addActionListener (this);
            menu.add (item);

        }

        public JPopupMenu getPopupMenu (Widget widget, Point localLocation) {
            return menu;
        }

        public void actionPerformed (ActionEvent e) {
            String cmd;
            
            cmd = e.getActionCommand ();
            if (cmd.equals(EXPORT_SCENE)) {
                exportScene();
            } else if (cmd.equals(PRINT_SCENE)) {
                printScene();
            } else if (cmd.equals(LAYOUT_SCENE)) {
                reLayoutScene();
            } else if (cmd.equals(SATELLITE_VIEW)) {
                makeSatelliteView();
            }
        }

    }
}
&lt;/pre&gt;&lt;/strong&gt;
&lt;br /&gt;
&lt;p&gt;Notice that we got rid of the label and label actions and tailored the class to be specific to our Prolog needs instead of generic. We added an empty printing function, a layout function, and a satellite view function to our context menu and implemented the satellite view within the class. Fix imports, save and close the file. Now we&#039;re ready to test the work we&#039;ve done. You&#039;ll like this.&lt;/p&gt;
&lt;br /&gt;&lt;h3&gt;Testing The New Diagram&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;Clean and build the main project, then install to the target IDE. Open the &quot;Algorithms&quot; project and open &quot;sieve.pro&quot;. When the Navigator comes up, start up the modeler (&quot;View&quot; &quot;Show Prolog Diagram&quot;) and double-click on a node. If the node is defined and not external then a window should open with the text of all the clauses that make up the predicate.

&lt;br /&gt;&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/geewhiz/son1-2a.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:127 --&gt;&lt;img width=&quot;90&quot; height=&quot;68&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/geewhiz/son1-2a.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;&lt;br /&gt;

Notice that you can open more than one predicate window at a time but only one window per node, which makes sense within the context of the diagram.

&lt;br /&gt;&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/geewhiz/son1-2b.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:128 --&gt;&lt;img width=&quot;90&quot; height=&quot;68&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/geewhiz/son1-2b.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;&lt;br /&gt;

Now right-click on the diagram to bring up our context menu.

&lt;br /&gt;&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/geewhiz/son1-3a.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:129 --&gt;&lt;img width=&quot;90&quot; height=&quot;68&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/geewhiz/son1-3a.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;&lt;br /&gt;

Move a node or two then try out the &quot;Redo layout&quot; function. Hold your breath, then try &quot;Show / hide satellite view&quot;.

&lt;br /&gt;&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/geewhiz/son1-3b.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:130 --&gt;&lt;img width=&quot;90&quot; height=&quot;68&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/geewhiz/son1-3b.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;&lt;br /&gt;

Move the cursor around on the satellite view and watch what happens to your underlying diagram.  Gee whiz! Pretty awesome, right? To close the satellite view you can click &quot;Show / hide...&quot; again in the context menu or just close the window. As you might suspect, the satellite view is not very useful with small diagrams like the one sieve.pro generates, but with a larger diagram like that in the screen shot it can be very useful indeed. Plus, it&#039;s just plain fun to have it around.&lt;/p&gt;
&lt;br /&gt;&lt;h3&gt;Going Further&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;One of the funky things about what we accomplished in this entry is that the screens for the node text and the screen for the satellite window are all JFrames independent of the diagram TopComponent. If you close the diagram, the frames stick around anyway. While you could possibly make a case that the little text windows are useful independently of the diagram, at least the satellite window should be dependent on the TopComponent and go away when it goes away. It is not useful without the diagram. So a reasonable next step would be to make the &quot;pop up&quot; screens for node text and the satellite window JDialogs or close them when the TopComponent goes away.&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;Now that we have all these new features on our diagram, what else could we do? The first thing that comes to mind is to allow the user to edit the code in the popup node window and have the changes reflected in the editor window source code. This would take some careful design because of the way multiple clauses make up a predicate, but it could certainly be done using techniques we&#039;ve already looked at. We could disembowel the PrologAST class and store line and column numbers in PrologClause, then use the same way we pop into the source code from the output window to go into the editor and change the text. But the usefulness of editing directly in the popup window is pretty questionable, at least for Prolog programming, so we won&#039;t go that route. I won&#039;t, anyway, but &lt;em&gt;you&lt;/em&gt; certainly can...&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;An even more interesting (to me) idea is to use the stuff we learned in GeeWhiz to write a &lt;strong&gt;whole new language interface&lt;/strong&gt;. It would be based on Java but instead of writing code you would just manipulate nodes and edges then compile the diagram! Theoretically a person would not even have to know Java to &quot;program&quot; with this new language. I know, I know, it sounds like UML but it would be way cooler. Actually, this is something I&#039;ve always wanted to develop, so look for &quot;Project PipeDreams&quot; coming your way soon....&lt;/p&gt;
&lt;br /&gt;&lt;h3&gt;Files From This Entry&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href=&#039;http://hulles.supersized.org/uploads/geewhiz/geewhiz_son1.tar.gz&#039;&gt;geewhiz_son1.tar.gz&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a onclick=&quot;javascript:urchinTracker(&#039;/download/uploads/geewhiz/geewhiz_son1.zip&#039;);&quot; href=&#039;http://hulles.supersized.org/uploads/geewhiz/geewhiz_son1.zip&#039;&gt;geewhiz_son1.zip&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;
&lt;br /&gt;
 
    </content:encoded>

    <pubDate>Tue, 27 May 2008 00:33:51 +0200</pubDate>
    <guid isPermaLink="false">http://hulles.supersized.org/archives/12-guid.html</guid>
    
</item>
<item>
    <title>GeeWhiz Prolog - Part Eight - Creating An NBM</title>
    <link>http://hulles.supersized.org/archives/11-GeeWhiz-Prolog-Part-Eight-Creating-An-NBM.html</link>
            <category>NetBeans</category>
    
    <comments>http://hulles.supersized.org/archives/11-GeeWhiz-Prolog-Part-Eight-Creating-An-NBM.html#comments</comments>
    <wfw:comment>http://hulles.supersized.org/wfwcomment.php?cid=11</wfw:comment>

    <slash:comments>10</slash:comments>
    <wfw:commentRss>http://hulles.supersized.org/rss.php?version=2.0&amp;type=comments&amp;cid=11</wfw:commentRss>
    

    <author>nospam@example.com (Hulles)</author>
    <content:encoded>
    &lt;em&gt;&lt;p&gt;This is Part Eight, the ninth and final part of a series of entries describing an implementation of the Prolog language in the NetBeans IDE. &lt;/p&gt;
&lt;br/&gt;&lt;a href=&quot;http://hulles.supersized.org/archives/2-GeeWhiz-Prolog-Part-Zero-About-the-Project.html&quot; alt=&quot;About the Project&quot;&gt;Part Zero, &quot;About The Project,&quot; is here.&lt;/a&gt;
&lt;br /&gt;&lt;a href=&quot;http://hulles.supersized.org/archives/3-GeeWhiz-Prolog-Part-One-Before-We-Start.html&quot; alt=&quot;Before We Start&quot;&gt;Part One, &quot;Before We Start,&quot; is here.&lt;/a&gt;
&lt;br /&gt;&lt;a href=&quot;http://hulles.supersized.org/archives/4-GeeWhiz-Prolog-Part-Two-Creating-A-File-Type.html&quot; alt=&quot;Creating A File Type&quot;&gt;Part Two, &quot;Creating A File Type,&quot; is here.&lt;/a&gt;
&lt;br /&gt;
&lt;a href=&quot;http://hulles.supersized.org/archives/5-GeeWhiz-Prolog-Part-Three-Adding-Language-Support.html&quot; alt=&quot;Adding Language Support&quot;&gt;Part Three, &quot;Adding Language Support,&quot; is here.&lt;/a&gt;
&lt;br /&gt;
&lt;a href=&quot;http://hulles.supersized.org/archives/6-GeeWhiz-Prolog-Part-Four-A-Visual-Prolog-Modeler.html&quot; alt=&quot;A Visual Prolog Modeler&quot;&gt;Part Four, &quot;A Visual Prolog Modeler&quot; is here.&lt;/a&gt;
&lt;br /&gt;
&lt;a href=&quot;http://hulles.supersized.org/archives/7-GeeWhiz-Prolog-Part-Five-Adding-The-Compiler.html&quot; alt=&quot;Adding The Compiler&quot;&gt;Part Five, &quot;Adding The Compiler&quot; is here.&lt;/a&gt;
&lt;br /&gt;
&lt;a href=&quot;http://hulles.supersized.org/archives/8-GeeWhiz-Prolog-Part-Six-Creating-Prolog-Projects.html&quot; alt=&quot;Creating Prolog Projects&quot;&gt;Part Six, &quot;Creating Prolog Projects&quot; is here.&lt;/a&gt;
&lt;br /&gt;
&lt;a href=&quot;http://hulles.supersized.org/archives/9-GeeWhiz-Prolog-Part-Seven-Adding-An-Option-Panel.html&quot; alt=&quot;Adding An Option Panel&quot;&gt;Part Seven, &quot;Adding An Option Panel&quot; is here.&lt;/a&gt;&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;
&lt;br /&gt;&lt;h3&gt;What We&#039;ll Be Doing&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;Well, we&#039;ve come a long way. Our NetBeans-embedded Prolog IDE has grown like Topsy (whoever Topsy is) and now threatens to take over NetBeans entirely and make everyone program in Prolog instead of Java. Okay, maybe not, but it &lt;em&gt;has&lt;/em&gt; come a long way as I said. We will look at what we&#039;ve accomplished in a &lt;em&gt;post mortem&lt;/em&gt; in a little bit, but right now let&#039;s create a NetBeans plug-in module (NBM) from GeeWhiz Prolog and install the module into our development IDE at last.&lt;/p&gt;
&lt;br /&gt;&lt;h3&gt;Creating The NBM&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;As I&#039;m sure you&#039;ve noticed, one of the options on the GeeWhiz project context menu in NetBeans has always been &quot;Create NBM&quot;. Go ahead and clean and build GeeWhiz, right-click on the project and select &quot;Create NBM&quot; now.

&lt;br /&gt;&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/geewhiz/nbm-0a.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:108 --&gt;&lt;img width=&quot;90&quot; height=&quot;63&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/geewhiz/nbm-0a.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;&lt;br /&gt;

After some whirring and clanking &lt;em&gt;(note to self: backup Aspen files!)&lt;/em&gt; the IDE settles down again and you&#039;ve created the NBM file. Unless you have the output window open you might not even notice that anything happened as there is no user interaction for this process.&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;What we&#039;ve done is generate a nifty little zip file called (in my case) &quot;org-hulles-geewhiz.nbm&quot; in the build folder of our project.

&lt;br /&gt;&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/geewhiz/nbm-0b.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:109 --&gt;&lt;img width=&quot;90&quot; height=&quot;68&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/geewhiz/nbm-0b.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;&lt;br /&gt;

&lt;strong&gt;At this point, you should save the NBM file to another directory.&lt;/strong&gt; I realized to my chagrin while writing this that the &quot;clean&quot; process in cleaning and building your project wipes out the build directory, including the NBM file. It&#039;s easy enough to regenerate, as we just saw, but still.... One wants to be sure the NBM file one thinks one has matches the file one thought one had, doesn&#039;t one? Yes one does, by golly.&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;I&#039;m not sure you noticed in the above screenshot, but I had the properties for the NBM file open so you could see what I saw the first time I went through this process. &quot;Hmm,&quot; says I to myself. &quot;The size is a little larger than I thought it would be. And I guess the NetBeans folks forgot to put the &#039;kB&#039; on the size property. It&#039;s still too large to host as a download, though.&quot; Then I went and looked at the file in the folder just to make sure and it&#039;s &lt;em&gt;47kB&lt;/em&gt;! Dang, that&#039;s like buying a case of beer for a dollar, it just doesn&#039;t happen that often these days. So it&#039;s a little file, is my point, and I can stick it on my blog site as a download without a further thought. Hell, I can even email it as an attachment to my Aunt Margaret in Philly who still has dial-up. (Not sure she has NetBeans, though....)&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;So now what do we do with our brand new (and shrimpy little) NBM file? For one thing, if we wanted to we could make it available to the whole world &lt;em&gt;via&lt;/em&gt; the NetBeans Plug-In Download Center (or whatever it&#039;s called) but that&#039;s a bit outside the scope of this project and besides, it&#039;s not ready for implementation yet. But we &lt;em&gt;can&lt;/em&gt; use it to finally install GeeWhiz Prolog in our development IDE if we want to. So let&#039;s do that.&lt;/p&gt;
&lt;br /&gt;&lt;h3&gt;Installing The NBM In Your IDE&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;There is probably a more dignified way to install a local NBM into your IDE, but the way I found works pretty well so we&#039;ll use that. In the main menu of your development IDE click &quot;Tools&quot;, then &quot;Plugins&quot;. Once the Plugins manager has started, select the Downloads tab. Click the &quot;Add Plugins...&quot; button and navigate to your NBM file and select it.

&lt;br /&gt;&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/geewhiz/nbm-1a.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:110 --&gt;&lt;img width=&quot;90&quot; height=&quot;59&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/geewhiz/nbm-1a.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;&lt;br /&gt;

Now your new module looks all shiny and ready to go, doesn&#039;t it? You can change the information that shows up on the right by right-clicking the GeeWhiz project and selecting &quot;Properties&quot; and filling out the &quot;Display&quot; and &quot;Packaging&quot; tabs in the Properties window. Then of course you have to recreate your NBM file. Once you&#039;re ready to bite the bullet, select &quot;GeeWhiz Prolog&quot; then click the &quot;Install&quot; button.

&lt;br /&gt;&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/geewhiz/nbm-1b.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:111 --&gt;&lt;img width=&quot;90&quot; height=&quot;78&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/geewhiz/nbm-1b.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;&lt;br /&gt;

On the first screen there isn&#039;t much to do but sit and admire the professional look of your work. When you&#039;re ready, click the &quot;Next&quot; button.

&lt;br /&gt;&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/geewhiz/nbm-1c.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:112 --&gt;&lt;img width=&quot;90&quot; height=&quot;78&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/geewhiz/nbm-1c.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;&lt;br /&gt;

On the next screen, unless you created a GeeWhiz license when I wasn&#039;t looking, you can just go ahead and click &quot;Install&quot;. Ignore the &quot;not digitally signed&quot; messages you will generate unless you also signed it when you created the license, you rascal.&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;Eventually the IDE will come back and tell  you the module is installed. To check it, select &quot;Tools&quot; &quot;Module Manager&quot; from the main menu and there GeeWhiz Prolog is, under &quot;Language Support&quot;.

&lt;br /&gt;&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/geewhiz/nbm-2a.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:113 --&gt;&lt;img width=&quot;90&quot; height=&quot;67&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/geewhiz/nbm-2a.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;&lt;br /&gt;

Gee whiz! Ain&#039;t it great? Check it out in your IDE; all the functions we tested in the target IDE should be there. You might consider copying or moving the Algorithms project from the target project directory to your real project directory, just to have test fodder available.&lt;/p&gt;
&lt;br /&gt;&lt;h3&gt;Guess What - We&#039;re Done!&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;That wraps up our adventures with GeeWhiz Prolog, at least for now. I am in the process of creating a &lt;a href=&quot;http://hulles.supersized.org/pages/geewhiz.html&quot; title=&quot;GeeWhiz Prolog Home Page&quot;&gt;home page for GeeWhiz Prolog&lt;/a&gt; on my blog that you can check occasionally if you want. I will post the NBM there, plus a complete set of source files, plus a PDF of all the GeeWhiz blog entries for your convenience in downloading and reading offline. Any addenda and errata (as if) will be posted there as well. I already thought of one thing I forgot to mention, and that&#039;s that you can zoom in and out (ctrl + mouse button) and pan (hold middle mouse button) on the modeling diagrams we created earlier.&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;As a special bonus for acting now, you will also receive an additional Java class in the complete source file archive called SceneExport.java that may or may not create a .PNG file from the modeling diagram. I&#039;m still working on it and haven&#039;t incorporated it into the diagram yet, but I&#039;m sure I&#039;ll write about it when I do. It&#039;s based on a slightly cryptic code segment in the Visual Library API and... Oh, just wait and read the article when I write it.&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;It&#039;s hard to end this series. I feel like we just scratched the surface with GeeWhiz Prolog, but hey, the surface itched. So... ciao, it&#039;s been fun. I hope you found this series useful, informative and occasionally entertaining. -- Hulles&lt;/p&gt;
&lt;br /&gt;
&lt;br /&gt;&lt;h3&gt;GeeWhiz Prolog Feature Summary&lt;/h3&gt;&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Incorporated as an integral part of the NetBeans IDE&lt;/li&gt;
&lt;li&gt;Has a WYSIWYG source code editor&lt;/li&gt;
&lt;li&gt;Provides Prolog syntax checking and highlighting&lt;/li&gt;
&lt;li&gt;Works with the sidebar statement navigator&lt;/li&gt;
&lt;li&gt;Generates a visual and interactive Prolog predicate model that personally rocks my world&lt;/li&gt;
&lt;li&gt;Supports in-line Prolog source code compilation with error lines linked back to source&lt;/li&gt;
&lt;li&gt;Is O/S- and compiler-independent&lt;/li&gt;
&lt;li&gt;Is installable as a plug-in module to the NetBeans IDE&lt;/li&gt;
&lt;li&gt;Is free&lt;/li&gt;
&lt;li&gt;The source code is easily modifiable since you build the damn thing yourself&lt;/li&gt;
&lt;li&gt;Did I mention it&#039;s free?&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;&lt;h3&gt;Going Further - My GeeWhiz Notes&lt;/h3&gt;&lt;br /&gt;
&lt;h4&gt;Fixes And Improvements To Current Features&lt;/h4&gt;
&lt;dl&gt;
&lt;dt&gt;Use public API for Generic Languages Framework&lt;/dt&gt;
&lt;dd&gt;Change PrologAST to not user ParserManagerImpl to get the AST root node so we can go back to the public API and not use an implementation version of GLF.&lt;/dd&gt;
&lt;dt&gt;Change Names To Be More Descriptive&lt;/dt&gt;
&lt;dd&gt;Change the name of the PrologClause class to PrologPredicate, e.g. In other words, refactor refactor refactor.&lt;/dd&gt;
&lt;dt&gt;Improve the Prolog grammar definition&lt;/dt&gt;
&lt;dd&gt;Improve the grammar defined in language.nbs for Prolog. I think it can be simplified and improved.&lt;/dd&gt;
&lt;dt&gt;Improve threading in GeeWhiz&lt;/dt&gt;
&lt;dd&gt;Analyze GeeWhiz and introduce threading, concurrency, Runnables and all of that kind of shit so it looks like I know what I&#039;m doing in Java but that makes the code five times more complicated and harder to understand.&lt;/dd&gt;
&lt;dt&gt;Take care of compiler warnings&lt;/dt&gt;
&lt;dd&gt;Analyze the warnings produced by the IDE when it starts up the target IDE and see what I can fix and what I should leave alone.&lt;/dd&gt;
&lt;dt&gt;Experiment with other SWI Prolog compiler options&lt;/dt&gt;
&lt;dd&gt;Try the -s option instead of the -c option and actually read the SWI documentation a little more closely.&lt;/dd&gt;
&lt;dt&gt;Fix remaining TODOs in code comments&lt;/dt&gt;
&lt;dd&gt;Probably I&#039;ll need to look at the good old Task List (ctrl+6) sometime. Jeez, it&#039;s like being married....&lt;/dd&gt;
&lt;dt&gt;Fix the *&amp;amp;$%!* coffee cup icon on my new Prolog projects&lt;/dt&gt;
&lt;dd&gt;Enough said. Although while I&#039;m at it I can see if there&#039;s anything else I can get rid of (or add) for new Prolog projects.&lt;/dd&gt;
&lt;dt&gt;Add license and digital certificate to GeeWhiz&lt;/dt&gt;
&lt;dd&gt;I should maybe make this an entry since a first glance at the docs available seemed less than promising.&lt;/dd&gt;
&lt;dt&gt;Find out how to create a target platform that is leaner for performance reasons&lt;/dt&gt;
&lt;dd&gt;Either that or buy a faster machine.&lt;/dd&gt;
&lt;/dl&gt;
&lt;br /&gt;
&lt;h4&gt;Additional Features That Could Be Added To GeeWhiz&lt;/h4&gt;
&lt;dl&gt;
&lt;dt&gt;Localize messages, strings, etc.&lt;/dt&gt;
&lt;dd&gt;Be a good world citizen and stick all messages etc. in Bundle&lt;/dd&gt;
&lt;dt&gt;Add help&lt;/dt&gt;
&lt;dd&gt;Add JavaHelp to the project. This might be another entry as well. It&#039;s pretty cool how it works, though....&lt;/dd&gt;
&lt;dt&gt;Make the static predicate visual model dynamic and refer back to source&lt;/dt&gt;
&lt;dd&gt;This is complicated by the fact that the predicate / clause relationship is not 1:1, but maybe light up all source clauses when a widget is selected....&lt;/dd&gt;
&lt;dt&gt;Add &lt;strong&gt;freemarker&lt;/strong&gt; support&lt;/dt&gt;
&lt;dd&gt;Oops, I was going to do that as part of the original project. Hopefully no one noticed.&lt;/dd&gt;
&lt;dt&gt;Interact with SWI Prolog via IOProvider Reader&lt;/dt&gt;
&lt;dd&gt;Just for fun we should try out the Reader in the output window.&lt;/dd&gt;
&lt;dt&gt;Create a satellite view for the modeler&lt;/dt&gt;
&lt;dd&gt;Make a satellite view of the diagram &lt;em&gt;a la&lt;/em&gt; Geertjan&#039;s example but preferably in a different window (popup?) as opposed to a sidebar.&lt;/dd&gt;
&lt;dt&gt;Add XPCE support&lt;/dt&gt;
&lt;dd&gt;Add support for SWI Prolog&#039;s buddy XPCE somehow. That would be a challenge, I bet... Wonder if it&#039;s Windows-compatible.&lt;/dd&gt;
&lt;/dl&gt;
&lt;br /&gt;
&lt;h4&gt;Either They&#039;re IDE Bugs Or I&#039;m Doing Something Wrong Dept.&lt;/h4&gt;
&lt;dl&gt;
&lt;dt&gt;On-again / off-again DataObject-dependent icons and menu items&lt;/dt&gt;
&lt;dd&gt;Find out once and for all if the above is me or the IDE. Then yell at the IDE people regardless.&lt;/dd&gt;
&lt;dt&gt;Wizards should be able to deal with icons already in the project directory&lt;/dt&gt;
&lt;dd&gt;For creating actions e.g. the wizard shouldn&#039;t give an error if the icon is already in the directory.&lt;/dd&gt;
&lt;dt&gt;Better reload support in &quot;Install / reload to target&quot;&lt;/dt&gt;
&lt;dd&gt;I shouldn&#039;t have to clean the project every damn time I retest. Look into security errors etc.&lt;/dd&gt;
&lt;/dl&gt;
&lt;br /&gt;
&lt;h4&gt;Areas For Further Research&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;NetBeans platforms and their creation&lt;/li&gt;
&lt;li&gt;Visual Library, especially the VMD part&lt;/li&gt;
&lt;li&gt;JavaHelp&lt;/li&gt;
&lt;li&gt;DataObjects, cookies, and their ilk&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;

 
    </content:encoded>

    <pubDate>Fri, 23 May 2008 15:18:12 +0200</pubDate>
    <guid isPermaLink="false">http://hulles.supersized.org/archives/11-guid.html</guid>
    
</item>
<item>
    <title>GeeWhiz Prolog - Part Seven - Adding An Option Panel</title>
    <link>http://hulles.supersized.org/archives/9-GeeWhiz-Prolog-Part-Seven-Adding-An-Option-Panel.html</link>
            <category>NetBeans</category>
    
    <comments>http://hulles.supersized.org/archives/9-GeeWhiz-Prolog-Part-Seven-Adding-An-Option-Panel.html#comments</comments>
    <wfw:comment>http://hulles.supersized.org/wfwcomment.php?cid=9</wfw:comment>

    <slash:comments>4</slash:comments>
    <wfw:commentRss>http://hulles.supersized.org/rss.php?version=2.0&amp;type=comments&amp;cid=9</wfw:commentRss>
    

    <author>nospam@example.com (Hulles)</author>
    <content:encoded>
    &lt;em&gt;&lt;p&gt;This is Part Seven, the eighth part of a series of entries describing an implementation of the Prolog language in the NetBeans IDE. &lt;/p&gt;
&lt;br/&gt;&lt;a href=&quot;http://hulles.supersized.org/archives/2-GeeWhiz-Prolog-Part-Zero-About-the-Project.html&quot; alt=&quot;About the Project&quot;&gt;Part Zero, &quot;About The Project,&quot; is here.&lt;/a&gt;
&lt;br /&gt;&lt;a href=&quot;http://hulles.supersized.org/archives/3-GeeWhiz-Prolog-Part-One-Before-We-Start.html&quot; alt=&quot;Before We Start&quot;&gt;Part One, &quot;Before We Start,&quot; is here.&lt;/a&gt;
&lt;br /&gt;&lt;a href=&quot;http://hulles.supersized.org/archives/4-GeeWhiz-Prolog-Part-Two-Creating-A-File-Type.html&quot; alt=&quot;Creating A File Type&quot;&gt;Part Two, &quot;Creating A File Type,&quot; is here.&lt;/a&gt;
&lt;br /&gt;
&lt;a href=&quot;http://hulles.supersized.org/archives/5-GeeWhiz-Prolog-Part-Three-Adding-Language-Support.html&quot; alt=&quot;Adding Language Support&quot;&gt;Part Three, &quot;Adding Language Support,&quot; is here.&lt;/a&gt;
&lt;br /&gt;
&lt;a href=&quot;http://hulles.supersized.org/archives/6-GeeWhiz-Prolog-Part-Four-A-Visual-Prolog-Modeler.html&quot; alt=&quot;A Visual Prolog Modeler&quot;&gt;Part Four, &quot;A Visual Prolog Modeler&quot; is here.&lt;/a&gt;
&lt;br /&gt;
&lt;a href=&quot;http://hulles.supersized.org/archives/7-GeeWhiz-Prolog-Part-Five-Adding-The-Compiler.html&quot; alt=&quot;Adding The Compiler&quot;&gt;Part Five, &quot;Adding The Compiler&quot; is here.&lt;/a&gt;
&lt;br /&gt;
&lt;a href=&quot;http://hulles.supersized.org/archives/8-GeeWhiz-Prolog-Part-Six-Creating-Prolog-Projects.html&quot; alt=&quot;Creating Prolog Projects&quot;&gt;Part Six, &quot;Creating Prolog Projects&quot; is here.&lt;/a&gt;&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;
&lt;br /&gt;&lt;h3&gt;What We&#039;ll Be Doing&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;Our Prolog IDE is quickly maturing into a comfortable, fun and feature-rich programming environment. But if you&#039;re like me -- and I hope for your sake you are not -- you&#039;ve been laying awake at night, sweating, thinking to yourself, &lt;/p&gt;
&lt;blockquote&gt;&quot;Gee whiz! I created this great Prolog IDE but I totally ruined it when I hard-coded the compiler command stuff into the project (see &lt;a href=&quot;http://hulles.supersized.org/archives/7-GeeWhiz-Prolog-Part-Five-Adding-The-Compiler.html&quot; alt=&quot;Adding The Compiler&quot;&gt;Part Five, &quot;Adding The Compiler&quot;&lt;/a&gt;). Now I can&#039;t make it into an NBM and distribute it and have everybody in the world use it and love it and have somebody write an article for NetBeans.org called &#039;Meet A NetBeans Module Writer: Hulles&#039; with my picture in it and everything.&quot;&lt;/blockquote&gt;
&lt;p&gt;Well, we all need our sleep, so lay awake no longer. This time we&#039;re going to add an IDE-level Prolog options screen where we can set the compiler command and arguments outside of our code and have it retained between sessions. We&#039;re also going to clean up some code while we&#039;re at it so our project is a little more compatible with the NetBeans IDE -- a &quot;better NetBeans citizen,&quot; as they say.&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;This entry is partially based on a Geertjan article in NetBeans called &lt;a onclick=&quot;javascript:urchinTracker(&#039;/extlink/platform.netbeans.org/tutorials/60/nbm-options.html&#039;);&quot; href=&quot;http://platform.netbeans.org/tutorials/60/nbm-options.html&quot; title=&quot;NetBeans link&quot;&gt;&quot;NetBeans Options Window Module Tutorial&quot;&lt;/a&gt;. I recommend checking it out prior to doing this entry if you feel so inclined; he gives you more background than I give you here. And thanks to the author for that tutorial and all the other fine NetBeans articles he has done.&lt;/p&gt;
&lt;br /&gt;&lt;h3&gt;Adding An Options Panel&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;First off we&#039;re going to start up the New Options Wizard. Right-click on your GeeWhiz project and select &quot;New&quot; &quot;Other&quot;.

&lt;br /&gt;&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/geewhiz/opt-1a.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:97 --&gt;&lt;img width=&quot;90&quot; height=&quot;61&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/geewhiz/opt-1a.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;&lt;br /&gt;

Select the &quot;Module Development&quot; category and choose &quot;Options Panel&quot;, then click &quot;Next&quot;. On the next screen of the wizard we&#039;re going to create a tab for Prolog in the &quot;Miscellaneous&quot; section of the &quot;Tools/Options&quot; menu.

&lt;br /&gt;&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/geewhiz/opt-1b.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:98 --&gt;&lt;img width=&quot;90&quot; height=&quot;61&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/geewhiz/opt-1b.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;&lt;br /&gt;

Select &quot;Create Miscellaneous Panel&quot; then enter &quot;Prolog&quot; for the title. Type &quot;Prolog project options&quot; for the tooltip text and hit &quot;Next&quot;.

&lt;br /&gt;&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/geewhiz/opt-1c.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:99 --&gt;&lt;img width=&quot;90&quot; height=&quot;61&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/geewhiz/opt-1c.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;&lt;br /&gt;

The next screen should come up already filled in for you, but check and make sure the project is &quot;GeeWhiz Prolog&quot;, the class name prefix is &quot;Geewhiz&quot;, and the package name is filled in correctly (&quot;org.myorg.geewhiz&quot;). Then click &quot;Finish&quot; and let the wizard generate the files.&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;Now we&#039;re going to take the panel that the wizard created and modify it for our own nefarious purposes. Open &quot;GeewhizPanel.java&quot; in design mode and from the Swing palette add a panel that fills the frame, then add two labels and two text fields to the panel, as in the screenshot.

&lt;br /&gt;&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/geewhiz/opt-2a.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:100 --&gt;&lt;img width=&quot;90&quot; height=&quot;63&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/geewhiz/opt-2a.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;&lt;br /&gt;

Rename the top text field by right-clicking it and selecting &quot;Change Variable Name...&quot;. Call it &quot;compilerName&quot;. Add a tooltip to it in Properties that says &quot;Enter the command to invoke the compiler, e.g. &quot;swipl&quot;&quot;. Rename the bottom text field to &quot;compilerArgs&quot; in the same way and add a tooltip that says &quot;Enter arguments to the compiler command, e.g. &quot;-c -r&quot;&quot;. When you&#039;re satisfied, switch to source view and change the load and store methods to:&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;&lt;strong&gt;&lt;pre&gt;
    void load() {
        compilerName.setText(NbPreferences.forModule(GeewhizPanel.class).
                get(&quot;compilerName&quot;, &quot;swipl&quot;));        
        compilerArgs.setText(NbPreferences.forModule(GeewhizPanel.class).
                get(&quot;compilerArgs&quot;, &quot;-c&quot;));        
    }

    void store() {
        NbPreferences.forModule(GeewhizPanel.class).put(&quot;compilerName&quot;, 
                compilerName.getText());
        NbPreferences.forModule(GeewhizPanel.class).put(&quot;compilerArgs&quot;, 
               compilerArgs.getText());
    }
&lt;/pre&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;What we&#039;re doing here is loading and storing the two data we need for our compiler, the compiler command and arguments string, in NetBeans Preferences. It may be necessary at some point to add environment and working directory information here, but for now we&#039;ll just leave it at the command and arguments.&lt;/p&gt;

&lt;br /&gt;&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/geewhiz/opt-2b.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:101 --&gt;&lt;img width=&quot;90&quot; height=&quot;61&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/geewhiz/opt-2b.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;&lt;br /&gt;

&lt;p&gt;Save and close the GeewhizPanel class.&lt;/p&gt;
&lt;br /&gt;&lt;h3&gt;Modifying The Compiler Class&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;Now we need to change our PrologCompiler class to use the NbPreferences we created in the Options panel. I went through several iterations of changes and testing with this class, and here is the final version (as if any programming code was ever final). &lt;em&gt;Note: if you&#039;ve made any changes to the class yourself from the version we created earlier, you should save your old version of PrologCompiler.java prior to making these changes.&lt;/em&gt; Whenever you&#039;re ready, change the class definition to the one listed below:&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;&lt;strong&gt;&lt;pre&gt;
public class PrologCompiler {
    private DataObject dataObject;
    private String fileName;
    private String compilerName;
    private String compilerArgs;
    
    public PrologCompiler() {
        Preferences pref;
        
        this.dataObject = null;
        pref = NbPreferences.forModule(GeewhizPanel.class);
        this.compilerName = pref.get(&quot;compilerName&quot;, &quot;&quot;);
        this.compilerArgs = pref.get(&quot;compilerArgs&quot;, &quot;&quot;);
        
        pref.addPreferenceChangeListener(new PreferenceChangeListener() {
            public void preferenceChange(PreferenceChangeEvent evt) {
                if (evt.getKey().equals(&quot;compilerName&quot;)) {
                    compilerName = (evt.getNewValue());
                } else if (evt.getKey().equals(&quot;compilerArgs&quot;)) {
                    compilerArgs = (evt.getNewValue());
                }
            }
        });
    }
    
    public void compileDataObject(DataObject dObj) {
        ProcessBuilder procBuilder;
        Process process;
        List&amp;lt;String&amp;gt; cmd;
        Map&amp;lt;String, String&amp;gt; env;
        String line;
        InputOutput io;
        OutputWriter outputWriter;
        SaveCookie sCookie;

        
        // set DataObject and file name
        this.dataObject = dObj;
        File file = FileUtil.toFile(dObj.getPrimaryFile());
        this.fileName = file.getAbsolutePath();
        
        // save file first if it&#039;s been modified
        sCookie = dObj.getCookie (SaveCookie.class);
        if (sCookie != null) {
            try {
                sCookie.save();
                StatusDisplayer.getDefault().setStatusText(&quot;Save finished&quot;);
            } catch (IOException ex) {
                Exceptions.printStackTrace(ex);
            }
        }
        
        // get an output window tab
        io = IOProvider.getDefault().getIO(&quot;Prolog&quot;, false);
        io.select();
        outputWriter = io.getOut();

        // first see if we have a compiler available
        if (this.compilerName.isEmpty()) {
            outputWriter.println(&quot;You need to set up the compiler first.&quot;);
            outputWriter.print(&quot;In the main menu go to &#039;Tools / Options&quot;);
            outputWriter.println(&quot;/ Miscellaneous / Prolog&#039;.&quot;);
            whiteLines(outputWriter);
            outputWriter.close();
            return;
        }

        // construct the SWI Prolog process command
        cmd = new ArrayList&amp;lt;String&amp;gt;();
        cmd.add(this.compilerName);
        cmd.add(this.compilerArgs);
        cmd.add(fileName);
        
        procBuilder = new ProcessBuilder(cmd);
        procBuilder.redirectErrorStream(true);
        try {
            process = procBuilder.start();
            InputStream is = process.getInputStream();
            InputStreamReader isr = new InputStreamReader(is);
            BufferedReader br = new BufferedReader(isr);
            // TODO: might want to clear the output window first...
            outputWriter.printf(&quot;Output of running %s is:\n\n&quot;,
                        commandString(cmd));
            while ((line = br.readLine()) != null) {
                if (lineIsNotable(line)) {
                    outputWriter.println(line, listener);
                } else {
                    outputWriter.println(line);
                }
            }
        } catch (IOException ex) {
            outputWriter.print(&quot;Error occurred running Prolog compiler. &quot;);
            outputWriter.println(&quot;Check options.&quot;);
            outputWriter.print(&quot;In the main menu go to &#039;Tools / Options&quot;);
            outputWriter.println(&quot;/ Miscellaneous / Prolog&#039;.&quot;);
            outputWriter.printf(&quot;Command string is %s.\n&quot;, commandString(cmd));
     //       Exceptions.printStackTrace(ex);
        } finally {
            whiteLines(outputWriter);
            outputWriter.close();
        }
    }

    private OutputListener listener = new OutputListener() {
        public void outputLineAction(OutputEvent ev) {
            String outputLine;
            Line editorLine;
            LineCookie lCookie;
            int lineNumber;
            int columnNumber;
            
            outputLine = ev.getLine();
            lineNumber = parseLineNumber(outputLine);
            columnNumber = parseColumnNumber(outputLine);
            lCookie = dataObject.getCookie (LineCookie.class);
            editorLine = lCookie.getLineSet ().getOriginal (lineNumber-1);
            editorLine.show (Line.SHOW_GOTO, columnNumber);

            StatusDisplayer.getDefault().setStatusText(&quot;Fix me!&quot;);
        }
        public void outputLineSelected(OutputEvent ev) {
            // Let&#039;s not do anything special.
        }
        public void outputLineCleared(OutputEvent ev) {
            // Leave it blank, no state to remove.
        }

    };

    
    private boolean lineIsNotable(String line) {
        boolean result = false;
        
        if (line.startsWith(&quot;Warning:&quot;) || line.startsWith(&quot;ERROR:&quot;)) {
            result = true;
        }
        return result;
    }
    
/*
 * SAMPLE SWI PROLOG LINES:
ERROR: /home/.../sieve.pro:16:0: Syntax error: Operator expected
Warning: (/home/.../sieve.pro:18): Singleton variables: [P]
*/
    
    private int parseLineNumber(String line) {
        int fpos;
        int startpos;
        int lineNumber;
        Character ch;
        StringBuffer numBuf;
        
        fpos = line.indexOf(this.fileName);
        // assuming there are no colons in file name!
        startpos = line.indexOf(&quot;:&quot;, fpos);
        if (startpos == -1) {
            // lineNumber is converted to lineNumber - 1
            //     this causes an exception in caller if this is 0
            //     so return 1 instead
            return 1;
        }
        numBuf = new StringBuffer();
        for (int i = startpos+1; i &amp;lt; line.length(); i++) {
            ch = line.charAt(i);
            if (Character.isDigit(ch)) {
                numBuf.append(ch);
            } else {
                break;
            }
        }
        lineNumber = Integer.parseInt(numBuf.toString());
        return lineNumber;
    }
    
    private int parseColumnNumber(String line) {
        int fpos;
        int startpos;
        int colNumber;
        Character ch;
        StringBuffer numBuf;
        int colstart = -1;
        
        fpos = line.indexOf(this.fileName);
        // assuming there are no colons in file name!
        startpos = line.indexOf(&quot;:&quot;, fpos);
        if (startpos == -1) {
            return 0;
        }
        for (int i = startpos+1; i &amp;lt; line.length(); i++) {
            ch = line.charAt(i);
            if (Character.isDigit(ch)) {
                // skip line number
            } else {
                colstart = i;
                break;
            }
        }
        if ((colstart == -1) || (line.charAt(colstart) != &#039;:&#039;)) {
            return 0;
        }
        numBuf = new StringBuffer();
        for (int i = colstart+1; i &amp;lt; line.length(); i++) {
            ch = line.charAt(i);
            if (Character.isDigit(ch)) {
                numBuf.append(ch);
            } else {
                break;
            }
        }
        colNumber = Integer.parseInt(numBuf.toString());
        return colNumber;
    }
    
    private void whiteLines(OutputWriter ow) {
        final int limit = 3;
        
        for(int i = 0; i &amp;lt; limit; i++) {
            ow.println();
        }
    }
    
    private String commandString(List&amp;lt;String&amp;gt; cmds) {
        StringBuffer sBuf;
        
        sBuf = new StringBuffer();
        for (String cmd : cmds) {
            sBuf.append(cmd);
            sBuf.append(&#039; &#039;);
        }
        // leave extra space on the end
        return sBuf.toString();
    }
}
&lt;/pre&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;Actually, rather a lot has changed. Notice in the constructor that we are now getting the compiler command and arguments from NbPreferences and starting a listener to see if they change. Actually, the listener may be overkill for this application but as I mentioned above we&#039;re trying to be better NetBeans citizens so let&#039;s leave it in just in case we have a very quick typist on our hands.&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;Also notice that now we are checking for a SaveCookie before we start the compilation. If one exists, and it will if our source file has been modified since the last save, we grab it and use it to save the file prior to compiling it. I love how easy that is in NetBeans. We follow the NetBeans Java example of not asking prior to doing the save; we just go ahead and do it.&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;Note that we also are adding a couple helper methods to clean up our code (and the output) a little bit. White space rocks. And we&#039;re adding more cleanup -- we are finally closing the OutputWriter in our class.&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;Since we opened up the compiler command and arguments to user input, now we have to do some reality checks on the data prior to using it. First we check to make sure the NbPreference is there for the compiler command and issue an error if it is not. (The command arguments should be optional.) The compiler command will &lt;strong&gt;not&lt;/strong&gt; be there if the user has not set up the compiler in our new options panel so we firmly order her/him to go to the options panel and set it up right the hell now. Hmph. The nerve.&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;We also changed the try/catch for the compiler invocation, since now we&#039;re executing whatever the user typed in for a command and arguments. Instead of doing a stack trace we&#039;re issuing an error message in the catch, since 

&lt;br /&gt;&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/geewhiz/opt-4d.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:102 --&gt;&lt;img width=&quot;90&quot; height=&quot;45&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/geewhiz/opt-4d.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;&lt;br /&gt;

is not considered good user feedback in most circles. Never mind how I got the screenshot, why do you think I changed it? Of course I left the stack trace in place but commented out, since no good Java programmer feels completely comfortable about a catch statement without a stack trace laying around somewhere.&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;I think that about covers the changes. Let&#039;s try it out. But before we do that, you might want to grab a new PrologAST from the archive to this article. I modified it to not print all the debugging information unless a constant is set, and if printing is requested to not keep the OutputWriter tied up quite so long. Good citizenship again, but not worth including the source code in this article.&lt;/p&gt;
&lt;br /&gt;&lt;h3&gt;Testing The Option Panel And The Compiler&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;You know the drill -- clean and build the GeeWhiz project then install it to the target IDE. &lt;em&gt;Don&#039;t&lt;/em&gt; go into the options menu yet. Open up the &quot;Algorithms&quot; project (!) and open sieve.pro. Try to compile it with the menu bar button. You should get an error.

&lt;br /&gt;&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/geewhiz/opta-5a.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:103 --&gt;&lt;img width=&quot;90&quot; height=&quot;68&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/geewhiz/opta-5a.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;&lt;br /&gt;

Next, from the main menu, select &quot;Tools&quot; &quot;Options&quot;, then click the &quot;Miscellaneous&quot; tab then the &quot;Prolog&quot; tab.

&lt;br /&gt;&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/geewhiz/opta-5b.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:104 --&gt;&lt;img width=&quot;90&quot; height=&quot;62&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/geewhiz/opta-5b.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;&lt;br /&gt;

Our options panel should appear with the default values from our load statements in GeewhizPanel. Click &quot;OK&quot;. Now try compiling sieve.pro again. You should get a normal compilation.

&lt;br /&gt;&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/geewhiz/opta-5c.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:105 --&gt;&lt;img width=&quot;90&quot; height=&quot;68&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/geewhiz/opta-5c.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;&lt;br /&gt;

Now go back into our option panel and change the values to something invalid for your compiler and platform, like :

&lt;br /&gt;&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/geewhiz/opta-5d.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:106 --&gt;&lt;img width=&quot;90&quot; height=&quot;62&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/geewhiz/opta-5d.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;&lt;br /&gt;

Click &quot;OK&quot; to save them, then try compiling again. You should get an error.

&lt;br /&gt;&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/geewhiz/opta-5e.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:107 --&gt;&lt;img width=&quot;90&quot; height=&quot;68&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/geewhiz/opta-5e.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;&lt;br /&gt;

You don&#039;t need to change the options back since they&#039;ll go away once the target IDE is destroyed in our clean and build. However, before you quit testing you should make sure you can still create a diagram with the modified PrologAST class without printing any text in the Prolog output tab if you installed the class from this article&#039;s archives. Then you can quit.&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;So now GeeWhiz is back to being platform- and compiler-independent and it&#039;s a little easier on the NetBeans enviroment. Now maybe we can all go get some sleep. Next time we&#039;ll create a NetBeans module (NBM) file for sharing with others, a fairly trivial task, and sum up what we&#039;ve accomplished so far. Zzzzzz....&lt;/p&gt;
&lt;br /&gt;&lt;h3&gt;Files From This Entry&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href=&#039;http://hulles.supersized.org/uploads/geewhiz/geewhiz_partseven.tar.gz&#039;&gt;geewhiz_partseven.tar.gz&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a onclick=&quot;javascript:urchinTracker(&#039;/download/uploads/geewhiz/geewhiz_partseven.zip&#039;);&quot; href=&#039;http://hulles.supersized.org/uploads/geewhiz/geewhiz_partseven.zip&#039;&gt;geewhiz_partseven.zip&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;
&lt;br /&gt;

 
    </content:encoded>

    <pubDate>Thu, 22 May 2008 17:05:13 +0200</pubDate>
    <guid isPermaLink="false">http://hulles.supersized.org/archives/9-guid.html</guid>
    
</item>
<item>
    <title>GeeWhiz Prolog - Part Six - Creating Prolog Projects</title>
    <link>http://hulles.supersized.org/archives/8-GeeWhiz-Prolog-Part-Six-Creating-Prolog-Projects.html</link>
            <category>NetBeans</category>
    
    <comments>http://hulles.supersized.org/archives/8-GeeWhiz-Prolog-Part-Six-Creating-Prolog-Projects.html#comments</comments>
    <wfw:comment>http://hulles.supersized.org/wfwcomment.php?cid=8</wfw:comment>

    <slash:comments>3</slash:comments>
    <wfw:commentRss>http://hulles.supersized.org/rss.php?version=2.0&amp;type=comments&amp;cid=8</wfw:commentRss>
    

    <author>nospam@example.com (Hulles)</author>
    <content:encoded>
    &lt;em&gt;This is Part Six, the seventh part of a series of entries describing an implementation of the Prolog language in the NetBeans IDE. 
&lt;br/&gt;&lt;a href=&quot;http://hulles.supersized.org/archives/2-GeeWhiz-Prolog-Part-Zero-About-the-Project.html&quot; alt=&quot;About the Project&quot;&gt;Part Zero, &quot;About The Project,&quot; is here.&lt;/a&gt;
&lt;br /&gt;&lt;a href=&quot;http://hulles.supersized.org/archives/3-GeeWhiz-Prolog-Part-One-Before-We-Start.html&quot; alt=&quot;Before We Start&quot;&gt;Part One, &quot;Before We Start,&quot; is here.&lt;/a&gt;
&lt;br /&gt;&lt;a href=&quot;http://hulles.supersized.org/archives/4-GeeWhiz-Prolog-Part-Two-Creating-A-File-Type.html&quot; alt=&quot;Creating A File Type&quot;&gt;Part Two, &quot;Creating A File Type,&quot; is here.&lt;/a&gt;
&lt;br /&gt;
&lt;a href=&quot;http://hulles.supersized.org/archives/5-GeeWhiz-Prolog-Part-Three-Adding-Language-Support.html&quot; alt=&quot;Adding Language Support&quot;&gt;Part Three, &quot;Adding Language Support,&quot; is here.&lt;/a&gt;
&lt;br /&gt;
&lt;a href=&quot;http://hulles.supersized.org/archives/6-GeeWhiz-Prolog-Part-Four-A-Visual-Prolog-Modeler.html&quot; alt=&quot;A Visual Prolog Modeler&quot;&gt;Part Four, &quot;A Visual Prolog Modeler&quot; is here.&lt;/a&gt;
&lt;br /&gt;
&lt;a href=&quot;http://hulles.supersized.org/archives/7-GeeWhiz-Prolog-Part-Five-Adding-The-Compiler.html&quot; alt=&quot;Adding The Compiler&quot;&gt;Part Five, &quot;Adding The Compiler&quot; is here.&lt;/a&gt;&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;
&lt;br /&gt;&lt;h3&gt;What We&#039;ll Be Doing&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;So far we&#039;ve cobbled together a pretty decent Prolog IDE within the NetBeans environment. As far as features, it has... well, you can review the previous articles in the series yourself but it has a lot of Cool Stuff. Now we&#039;re going to create a new project template so when you want to start working on a new Prolog project you don&#039;t have to call it a Java class library or a web application just to be able to create new Prolog source files.&lt;/p&gt;
&lt;br /&gt;&lt;h3&gt;Create A New Project Template&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;First we need to create a sample project to use in the new project template wizard. This project should have what just what we need when we start a new Prolog project and no more. Since we&#039;re only working with text source files, that&#039;s pretty much all the project needs. We&#039;ll just copy one source file into the project since both Nature and Hulles abhor a vacuum. In the main menu, select &quot;File&quot; &quot;New Project&quot; and create yourself a new Java class library project called, appropriately enough,  &quot;NewProject&quot;.&lt;/p&gt;

&lt;br /&gt;&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/geewhiz/npp-1a.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:85 --&gt;&lt;img width=&quot;90&quot; height=&quot;55&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/geewhiz/npp-1a.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;&lt;br /&gt;

&lt;p&gt;Find a copy of &lt;strong&gt;sieve.pro&lt;/strong&gt; in your target IDE directory and copy it into the src folder of NewProject. If you would prefer, you can extract a copy from the archive files at the end of this article.&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;Now we&#039;re going to pare down the project as much as we can, which isn&#039;t much at this point. Right-click on NewProject and select &quot;Properties&quot;. In &quot;Categories&quot; select &quot;Libraries&quot;, then click the &quot;Compile Tests&quot; tab. Delete the test libraries that are there.

&lt;br /&gt;&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/geewhiz/npp-1b.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:86 --&gt;&lt;img width=&quot;90&quot; height=&quot;63&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/geewhiz/npp-1b.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;&lt;br /&gt;

Click &quot;OK&quot; to close the properties window. Now we have a simple project we can use as a base for creating new Prolog projects. Time to create our new project template. Right-click the GeeWhiz project and select &quot;New&quot; &quot;Project Template...&quot;. In the first screen of the wizard select &quot;NewProject&quot;. 

&lt;br /&gt;&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/geewhiz/npp-2a.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:87 --&gt;&lt;img width=&quot;90&quot; height=&quot;64&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/geewhiz/npp-2a.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;&lt;br /&gt;

Click &quot;Next&quot;. On the next screen of the wizard, enter &quot;NewProject&quot; for the template name, &quot;Prolog Application&quot; for the display name, select &quot;Other&quot; for the category, and make sure the package is correctly filled in.

&lt;br /&gt;&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/geewhiz/npp-2c.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:88 --&gt;&lt;img width=&quot;90&quot; height=&quot;88&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/geewhiz/npp-2c.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;&lt;br /&gt;

Once everything is correct, click &quot;Finish&quot;.&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;Now we&#039;re going to customize our new project template a little bit. Edit &quot;NewProjectDescription.html&quot; and change the description to simply &quot;Prolog project&quot;.

&lt;br /&gt;&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/geewhiz/npp-3a.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:89 --&gt;&lt;img width=&quot;90&quot; height=&quot;63&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/geewhiz/npp-3a.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;&lt;br /&gt;

Save and close the file. Open the layer.xml file and traverse &quot;this layer&quot; down to &quot;Templates&quot; &quot;Project&quot; &quot;Other&quot;.

&lt;br /&gt;&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/geewhiz/npp-3b.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:90 --&gt;&lt;img width=&quot;90&quot; height=&quot;63&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/geewhiz/npp-3b.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;&lt;br /&gt;

Right-click on &quot;Prolog Application&quot; and select &quot;Pick Icon...&quot;. Navigate to your package source folder and select &quot;prolog-star.png&quot;, the dreaded Black Star of Prolog.

&lt;br /&gt;&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/geewhiz/npp-3c.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:91 --&gt;&lt;img width=&quot;90&quot; height=&quot;49&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/geewhiz/npp-3c.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;&lt;br /&gt;

Click &quot;OK&quot; and you&#039;re done. You can now delete the &quot;NewProject&quot; project because we won&#039;t need it anymore. The necessary stuff from &quot;NewProject&quot; was zipped into our &quot;GeeWhiz&quot; project by the new project template wizard.&lt;/p&gt;
&lt;br /&gt;&lt;h3&gt;Testing The New Template&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;Our new Prolog project template should now be ready to test. Clean and build your main project and install it to the Target IDE. Once your &quot;personal domain&quot; is created, in the main menu select &quot;File&quot; &quot;New Project&quot; and you should see our new Prolog project option, complete with BSofP icon.

&lt;br /&gt;&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/geewhiz/npp-4a.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:92 --&gt;&lt;img width=&quot;90&quot; height=&quot;61&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/geewhiz/npp-4a.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;&lt;br /&gt;
Select &quot;Prolog Application&quot; and fill in the necessary fields, calling your new project &quot;Algorithms&quot; in honor of our two hard-working sample Prolog programs, fib1.pro and sieve.pro.

&lt;br /&gt;&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/geewhiz/npp-4b.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:93 --&gt;&lt;img width=&quot;90&quot; height=&quot;61&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/geewhiz/npp-4b.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;&lt;br /&gt;

Check out &quot;Algorithms&quot; and note that &quot;sieve.pro&quot; is include in the default package of our new project. If you want to be really loyal, copy &lt;strong&gt;fib1.pro&lt;/strong&gt; into our new project and delete the &quot;BraveNewWorld&quot; project since we won&#039;t need it anymore.&lt;/p&gt;

&lt;br /&gt;&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/geewhiz/npp-4c.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:94 --&gt;&lt;img width=&quot;90&quot; height=&quot;63&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/geewhiz/npp-4c.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;&lt;br /&gt;

&lt;p&gt;While we&#039;re here, note also that there is a &lt;strong&gt;disturbing Java coffee cup icon&lt;/strong&gt; on our new Prolog project. There &lt;em&gt;is&lt;/em&gt; a way to change that, but that procedure is beyond the scope of this how-to series. This is because I have no idea what the procedure is. If you do, please let me know. &lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;There, wasn&#039;t that easy? Now we have a way to create a Prolog project as a home for our precious Prolog source code. Next we&#039;ll do some housework in GeeWhiz and make it a &quot;good NetBeans IDE citizen&quot; (or at least a better one). In the process we&#039;ll make GeeWhiz Prolog platform- and compiler-independent once again. (It was until the last entry when we added the compiler.) Until next time.&lt;/p&gt;
&lt;br /&gt;&lt;h3&gt;Files From This Entry&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href=&#039;http://hulles.supersized.org/uploads/geewhiz/geewhiz_partsix.tar.gz&#039;&gt;geewhiz_partsix.tar.gz&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a onclick=&quot;javascript:urchinTracker(&#039;/download/uploads/geewhiz/geewhiz_partsix.zip&#039;);&quot; href=&#039;http://hulles.supersized.org/uploads/geewhiz/geewhiz_partsix.zip&#039;&gt;geewhiz_partsix.zip&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;
&lt;br /&gt;


 
    </content:encoded>

    <pubDate>Thu, 22 May 2008 13:45:23 +0200</pubDate>
    <guid isPermaLink="false">http://hulles.supersized.org/archives/8-guid.html</guid>
    
</item>
<item>
    <title>GeeWhiz Prolog - Part Five - Adding The Compiler</title>
    <link>http://hulles.supersized.org/archives/7-GeeWhiz-Prolog-Part-Five-Adding-The-Compiler.html</link>
            <category>NetBeans</category>
    
    <comments>http://hulles.supersized.org/archives/7-GeeWhiz-Prolog-Part-Five-Adding-The-Compiler.html#comments</comments>
    <wfw:comment>http://hulles.supersized.org/wfwcomment.php?cid=7</wfw:comment>

    <slash:comments>0</slash:comments>
    <wfw:commentRss>http://hulles.supersized.org/rss.php?version=2.0&amp;type=comments&amp;cid=7</wfw:commentRss>
    

    <author>nospam@example.com (Hulles)</author>
    <content:encoded>
    &lt;em&gt;This is Part Five, the sixth part of a series of entries describing an implementation of the Prolog language in the NetBeans IDE. 
&lt;br/&gt;&lt;a href=&quot;http://hulles.supersized.org/archives/2-GeeWhiz-Prolog-Part-Zero-About-the-Project.html&quot; alt=&quot;About the Project&quot;&gt;Part Zero, &quot;About The Project,&quot; is here.&lt;/a&gt;
&lt;br /&gt;&lt;a href=&quot;http://hulles.supersized.org/archives/3-GeeWhiz-Prolog-Part-One-Before-We-Start.html&quot; alt=&quot;Before We Start&quot;&gt;Part One, &quot;Before We Start,&quot; is here.&lt;/a&gt;
&lt;br /&gt;&lt;a href=&quot;http://hulles.supersized.org/archives/4-GeeWhiz-Prolog-Part-Two-Creating-A-File-Type.html&quot; alt=&quot;Creating A File Type&quot;&gt;Part Two, &quot;Creating A File Type,&quot; is here.&lt;/a&gt;
&lt;br /&gt;
&lt;a href=&quot;http://hulles.supersized.org/archives/5-GeeWhiz-Prolog-Part-Three-Adding-Language-Support.html&quot; alt=&quot;Adding Language Support&quot;&gt;Part Three, &quot;Adding Language Support,&quot; is here.&lt;/a&gt;
&lt;br /&gt;
&lt;a href=&quot;http://hulles.supersized.org/archives/6-GeeWhiz-Prolog-Part-Four-A-Visual-Prolog-Modeler.html&quot; alt=&quot;A Visual Prolog Modeler&quot;&gt;Part Four, &quot;A Visual Prolog Modeler&quot; is here.&lt;/a&gt;&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;
&lt;br /&gt;&lt;h3&gt;What We&#039;ll Be Doing&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;Up to this point we&#039;ve created a Prolog editor within the NetBeans IDE with built-in syntax highlighting, navigation, a visual modeler, and some other stuff that I forget but that&#039;s probably pretty cool. Now we&#039;re going to add in-place compilation so we don&#039;t need to switch between NetBeans and whatever Prolog compiler we&#039;re using just to fix syntax errors and such.&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;In this entry I&#039;ll be using &lt;a onclick=&quot;javascript:urchinTracker(&#039;/extlink/www.swi-prolog.org/&#039;);&quot; href=&quot;http://www.swi-prolog.org/&quot; title=&quot;SWI Prolog home page&quot;&gt;SWI Prolog&lt;/a&gt; as my Prolog compiler. It&#039;s a free, robust open-source compiler that runs on multiple platforms including, probably, yours. One thing it does &lt;em&gt;not&lt;/em&gt; have, however, is a feature-rich IDE -- hence this project. When we&#039;re done with this segment, we&#039;ll be able to compile our Prolog source code directly from the NetBeans IDE and hotlink (whatever that means) the error lines back to our source code. In other words, it will function much like the Java compilation process within NetBeans that we all know and love.&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;By the way, if you currently use a different Prolog compiler, don&#039;t worry. Once we&#039;re done here it should be pretty obvious how to adapt this project to incorporate your favorite compiler as long as you can run it from a terminal command line. In fact part of the reason I&#039;m writing this how-to is to allow you to adapt &lt;strong&gt;ANY&lt;/strong&gt; language to the NetBeans IDE. I&#039;m trying to assemble the knowledge that&#039;s scattered around the NetBeans universe in one place so that this series of articles gives you the ability to quickly and dirtily add your favorite language to the NetBeans IDE. We&#039;ll work on the &quot;dirtily&quot; part later; for now our goal is &lt;em&gt;results&lt;/em&gt;, baby.&lt;/p&gt;
&lt;br /&gt;&lt;h3&gt;Adding Compiler Support&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;Adding compiler support to GeeWhiz turns out to be surprisingly easy. It&#039;s easy if you know where to look for info, that is. It took me for-effing-ever to find the information I needed to do this, but once I did, implementing support for an external compiler turned out to be almost trivial. But I digress. The first thing we need to do is create an action in our project to invoke the compiler. Right-click on our GeeWhiz project and select &quot;New&quot; &quot;Action&quot;.&lt;/p&gt;

&lt;br /&gt;&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/geewhiz/ss6-1a.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:77 --&gt;&lt;img width=&quot;90&quot; height=&quot;64&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/geewhiz/ss6-1a.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;&lt;br /&gt;

&lt;p&gt;In the first screen of the New Action Wizard, once again select &quot;Conditionally Enabled&quot; and &quot;User Selects One Node&quot;. Set the cookie class to &quot;DataObject&quot; since we want to only compile Prolog source files with our compiler. Hit the &quot;Next&quot; button.

&lt;br /&gt;&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/geewhiz/ss6-1b.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:78 --&gt;&lt;img width=&quot;90&quot; height=&quot;85&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/geewhiz/ss6-1b.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;&lt;br /&gt;

On this wizard screen select &quot;File&quot; for a category (just for something different; we chose &quot;Other&quot; for our &quot;ShowDiagram&quot; action). &lt;em&gt;(I&#039;ve always wondered why it takes so long for this window to come up on my machine. I suspect that this is the place where NetBeans is sending encrypted information about my unspeakable habits to our shadowy overlords.)&lt;/em&gt; Uncheck &quot;Global Menu Item&quot; and check &quot;Global Toolbar Button&quot; (again, for something different). We&#039;re going to stick our toolbar button at the very end of the Build toolbar so select &quot;Build&quot; for the toolbar and &quot;Profile Main Project... - HERE&quot; as the position. Click &quot;Next&quot;.&lt;/p&gt;

&lt;br /&gt;&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/geewhiz/ss6-1c.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:79 --&gt;&lt;img width=&quot;90&quot; height=&quot;85&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/geewhiz/ss6-1c.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;&lt;br /&gt;

&lt;p&gt;On this screen, enter &quot;CompileProlog&quot; for a class name and &quot;Compile Prolog Program&quot; for a display name. For icons, you&#039;ll need both a 16x16 and 24x24 icon in the same directory, called (in our case) compile16.png and compile24.png, otherwise the wizard will grumpily issue error messages until you do. You can use these if you&#039;d like, another modified Black Star of Prolog. &lt;!-- s9ymdb:73 --&gt;&lt;img width=&quot;16&quot; height=&quot;16&quot; style=&quot;float: left; border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/geewhiz/compile16.png&quot; alt=&quot;&quot;  /&gt;  &lt;!-- s9ymdb:74 --&gt;&lt;img width=&quot;24&quot; height=&quot;24&quot; style=&quot;float: left; border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/geewhiz/compile24.png&quot; alt=&quot;&quot;  /&gt; Right-click on them in your browser and save them to a directory that is not your project directory and let the wizard copy them. Fill in the path and file name of the 16x16 icon in the icon field in the wizard. Make sure your package node (&quot;org.myorg.geewhiz&quot;) is filled in in the package field and click &quot;Finish&quot;.&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;Note: in the last screenshot ignore the GeeWhiz2 reference. I didn&#039;t make screenshots of this process the first time around, probably because I didn&#039;t expect it to work the first time! I thought I&#039;d have to do 8 or 9 trials to get it right like I did for the other stuff in this project. Gee whiz, I must be learning something....&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;Once the wizard creates our new action, you can close the icon files in the editor and add the following lines to the &lt;strong&gt;CompileProlog&lt;/strong&gt; class, where it says &quot;TODO: use DataObject&quot;:
&lt;br /&gt;
&lt;p&gt;&lt;strong&gt;&lt;pre&gt;
        PrologCompiler compiler = new PrologCompiler();
        compiler.compileDataObject(dataObject);
&lt;/pre&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;br /&gt;

&lt;br /&gt;&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/geewhiz/ss6-2a.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:80 --&gt;&lt;img width=&quot;90&quot; height=&quot;61&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/geewhiz/ss6-2a.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;&lt;br /&gt;

&lt;p&gt;Save and close the file. Great. We have a toolbar action, now all we need to do is write a PrologCompiler class. So let&#039;s do that.&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;Create a new Java class called &quot;PrologCompiler&quot; and replace the empty class with:&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;&lt;strong&gt;&lt;pre&gt;
public class PrologCompiler {
    DataObject dataObject;
    String fileName;
    
    public PrologCompiler() {
        this.dataObject = null;
    }
    
    public void compileDataObject(DataObject dObj) {
        ProcessBuilder procBuilder;
        Process process;
        Map&amp;lt;String, String&amp;gt; env;
//        File currDir;
        List&amp;lt;String&amp;gt; cmd;
        String line;
        InputOutput io;
        OutputWriter outputWriter;

        // TODO: should save file first if it&#039;s been modified
        
        // set DataObject and file name
        this.dataObject = dObj;
        File file = FileUtil.toFile(dObj.getPrimaryFile());
        fileName = file.getAbsolutePath();
        
        // get an output window tab
        io = IOProvider.getDefault().getIO(&quot;Prolog&quot;, false);
        io.select();
        outputWriter = io.getOut();

        // construct the SWI Prolog process command
        cmd = new ArrayList&amp;lt;String&amp;gt;();
        cmd.add(&quot;swipl&quot;);
        cmd.add(&quot;-c&quot;);
        cmd.add(fileName);
        
        procBuilder = new ProcessBuilder(cmd);
        procBuilder.redirectErrorStream(true);
        // also s/b able to merge it into OutputWriter
//        env = procBuilder.environment();
//        env.put(&quot;VAR1&quot;, &quot;myValue&quot;);
//        env.remove(&quot;OTHERVAR&quot;);
//        env.put(&quot;VAR2&quot;, env.get(&quot;VAR1&quot;) + &quot;suffix&quot;);
//        currDir = procBuilder.directory();
//        if (currDir != null) {
//            System.out.printf(&quot;Current directory is %s.&quot;, currDir.toString());
//        }
//        procBuilder.directory(new File(&quot;myDir&quot;));
        try {
            process = procBuilder.start();
            InputStream is = process.getInputStream();
            InputStreamReader isr = new InputStreamReader(is);
            BufferedReader br = new BufferedReader(isr);
            // TODO: might want to clear the output window first...
            outputWriter.printf(&quot;Output of running %s is:\n\n&quot;, cmd.toString());
            while ((line = br.readLine()) != null) {
                outputWriter.println(line);
            }
            // TODO: close outputwriter
        } catch (IOException ex) {
            Exceptions.printStackTrace(ex);
        }
    }
}
&lt;/pre&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;What we want this to do is invoke the SWI Prolog compiler with our source file, the name of which we retrieved from our trusty Prolog DataObject. We&#039;re using the IOProvider/OutputWriter we saw last time to write the output to the Output tab, just like a real adult language module does. We&#039;re also using ProcessBuilder to build a Process so we can execute an external command.&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;Just a couple of other things worth mentioning about the above class: one is that I left in some comments showing how to access the environment and change the working directory, just in case you need the info to make your compiler work. Also, note that the command line I used is for the Linux version of SWI Prolog. If you run on another platform, you&#039;ll probably need to change this. For example, I believe Windows uses &quot;plwin&quot;. See the &lt;a onclick=&quot;javascript:urchinTracker(&#039;/extlink/www.swi-prolog.org/&#039;);&quot; href=&quot;http://www.swi-prolog.org/&quot; title=&quot;SWI Prolog home page&quot;&gt;SWI Prolog&lt;/a&gt; site for more information.&lt;/p&gt;
&lt;br /&gt;&lt;h3&gt;Testing The First Version&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;So let&#039;s try it: clean and build your main project and install to the target IDE. Open up the BraveNewWorld project. Wait until the excitement dies down. Open up sieve.pro. Add a line somewhere in the program that says:&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;&lt;strong&gt;&lt;pre&gt;
	gruesome(ugly error).
&lt;/pre&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;This introduces an error in our otherwise nearly pristine Prolog program. Save sieve.pro. Find our new compiler button on the toolbar and click it.

&lt;br /&gt;&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/geewhiz/ss6-3a.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:81 --&gt;&lt;img width=&quot;90&quot; height=&quot;68&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/geewhiz/ss6-3a.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;&lt;br /&gt;

At this point my original notes say &quot;holy shit it worked!&quot; As indeed it did, the first time. &lt;em&gt;Nothing&lt;/em&gt; works the first time, ever. Imagine my elation. If you look at the &quot;Prolog&quot; output tab (!), you&#039;ll see the gruesome ugly error we introduced does indeed give us an error in the compiler output. Also note in passing that our syntax parser did flag our gruesome ugly error statement as an error. Heh heh.&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;The compiler also gives us a warning that there is a singleton variable in one of the clauses. This means that the variable &quot;P&quot; in the statement &quot;remove(P,[],[]).&quot; isn&#039;t used. In Prolog, instead of using a named variable here you should use an anonymous variable, the underscore. If you want you can change the statement to read &quot;remove(_,[],[]).&quot; and recompile it to see that the warning goes away. When you&#039;re done muttering &quot;Gee whiz!&quot; you can close the target IDE.&lt;/p&gt;
&lt;br /&gt;&lt;h3&gt;Add Hotlinks (Whatever They Are)&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;Now we&#039;re going to add hyperlinks to our compiler output so when you click an error line in the output you are magically transported to the source code at the point of the error. I know, you&#039;re saying &quot;Gee whiz!&quot; to yourself again. Just wait....&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;In trying to add this feature, I knew exactly what I wanted to do but I really wasn&#039;t sure how to accomplish it. Until, that is, I found an article called &lt;a onclick=&quot;javascript:urchinTracker(&#039;/extlink/platform.netbeans.org/articles/nbm_interview_jens.html&#039;);&quot; href=&quot;http://platform.netbeans.org/articles/nbm_interview_jens.html&quot; title=&quot;NetBeans link&quot;&gt;&quot;Meet A NetBeans Module Writer: Jens Trapp&quot;&lt;/a&gt;. In the article is a code snippet that does exactly what I wanted to do in about three lines of code! Thank you Mr. Trapp; may the rest of your days be happy ones. Who knew there was a LineCookie that plunked you right down in the middle of the source code editor? Probably the people who RTFD, that&#039;s who.... But for the rest of us there&#039;s Hulles.&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;So now we&#039;re going to change our PrologCompiler class to &quot;hotlink&quot; our compiler code to our source code. Open the class file and replace the class with this:&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;&lt;strong&gt;&lt;pre&gt;
public class PrologCompiler {
    DataObject dataObject;
    String fileName;
    
    public PrologCompiler() {
        this.dataObject = null;
    }
    
    public void compileDataObject(DataObject dObj) {
        ProcessBuilder procBuilder;
        Process process;
        Map&amp;lt;String, String&amp;gt; env;
        List&amp;lt;String&amp;gt; cmd;
        String line;
        InputOutput io;
        OutputWriter outputWriter;

        // TODO: should save file first if it&#039;s been modified
        
        // set DataObject and file name
        this.dataObject = dObj;
        File file = FileUtil.toFile(dObj.getPrimaryFile());
        fileName = file.getAbsolutePath();
        
        // get an output window tab
        io = IOProvider.getDefault().getIO(&quot;Prolog&quot;, false);
        io.select();
        outputWriter = io.getOut();

        // construct the SWI Prolog process command
        cmd = new ArrayList&amp;lt;String&amp;gt;();
        cmd.add(&quot;swipl&quot;);
        cmd.add(&quot;-c&quot;);
        cmd.add(fileName);
        
        procBuilder = new ProcessBuilder(cmd);
        procBuilder.redirectErrorStream(true);
        // also s/b able to merge it into OutputWriter
        try {
            process = procBuilder.start();
            InputStream is = process.getInputStream();
            InputStreamReader isr = new InputStreamReader(is);
            BufferedReader br = new BufferedReader(isr);
            // TODO: might want to clear the output window first...
            outputWriter.printf(&quot;Output of running %s is:\n\n&quot;, cmd.toString());
            while ((line = br.readLine()) != null) {
                if (lineIsNotable(line)) {
                    outputWriter.println(line, listener);
                } else {
                    outputWriter.println(line);
                }
            }
            // TODO: close outputwriter
        } catch (IOException ex) {
            Exceptions.printStackTrace(ex);
        }
    }

    private OutputListener listener = new OutputListener() {
        public void outputLineAction(OutputEvent ev) {
            String outputLine;
            Line editorLine;
            LineCookie lCookie;
            int lineNumber;
            int columnNumber;
            
            outputLine = ev.getLine();
            lineNumber = parseLineNumber(outputLine);
            columnNumber = parseColumnNumber(outputLine);
//          System.out.printf(&quot;Showing line %d column %d.\n&quot;, 
//                      lineNumber, columnNumber);
            lCookie = (LineCookie) dataObject.getCookie (LineCookie.class);
            editorLine = lCookie.getLineSet ().getOriginal (lineNumber-1);
            editorLine.show (Line.SHOW_GOTO, columnNumber);

            StatusDisplayer.getDefault().setStatusText(&quot;Fix me!&quot;);
        }
        public void outputLineSelected(OutputEvent ev) {
            // Let&#039;s not do anything special.
        }
        public void outputLineCleared(OutputEvent ev) {
            // Leave it blank, no state to remove.
        }

    };
    
    private boolean lineIsNotable(String line) {
        boolean result = false;
        
        if (line.startsWith(&quot;Warning:&quot;) || line.startsWith(&quot;ERROR:&quot;)) {
            result = true;
        }
        return result;
    }
    
/*
SAMPLE SWI PROLOG ERROR AND WARNING LINES:
ERROR: /home/.../bravenewworld/sieve.pro:16:0: Syntax error: Operator expected
Warning: (/home/.../bravenewworld/sieve.pro:18): Singleton variables: [P]
*/
    
    private int parseLineNumber(String line) {
        int fpos;
        int startpos;
        int lineNumber;
        Character ch;
        StringBuffer numBuf;
        
        fpos = line.indexOf(this.fileName);
		// assumes there are no colons in file name!
        startpos = line.indexOf(&quot;:&quot;, fpos);
        if (startpos == -1) {
            return 1;
	// in caller, lineNumber is converted to lineNumber - 1,
	//    this causes an exception if this is 0
        }
        numBuf = new StringBuffer();
        for (int i = startpos+1; i &amp;lt; line.length(); i++) {
            ch = line.charAt(i);
            if (Character.isDigit(ch)) {
                numBuf.append(ch);
            } else {
                break;
            }
        }
        lineNumber = Integer.parseInt(numBuf.toString());
        return lineNumber;
    }
    
    private int parseColumnNumber(String line) {
        int fpos;
        int startpos;
        int colNumber;
        Character ch;
        StringBuffer numBuf;
        int colstart = -1;
        
        fpos = line.indexOf(this.fileName);
		// assumes there are no colons in file name!
        startpos = line.indexOf(&quot;:&quot;, fpos); 
        if (startpos == -1) {
            return 0;
        }
        for (int i = startpos+1; i &amp;lt; line.length(); i++) {
            ch = line.charAt(i);
            if (Character.isDigit(ch)) {
                // skip line number
            } else {
                colstart = i;
                break;
            }
        }
        if ((colstart == -1) || (line.charAt(colstart) != &#039;:&#039;)) {
            return 0;
        }
        numBuf = new StringBuffer();
        for (int i = colstart+1; i &amp;lt; line.length(); i++) {
            ch = line.charAt(i);
            if (Character.isDigit(ch)) {
                numBuf.append(ch);
            } else {
                break;
            }
        }
        colNumber = Integer.parseInt(numBuf.toString());
        return colNumber;
    }
}
&lt;/pre&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;As you can see, we added an OutputListener that does the work of popping us into the editor at exactly the right place where the error is located. The parsing of the error / warning lines to get the line number and column number is ugly, but that&#039;s because &lt;strong&gt;A)&lt;/strong&gt; I didn&#039;t want to have to relearn regular expression syntax for the umpteenth time, and &lt;strong&gt;B)&lt;/strong&gt; I didn&#039;t have 244 Swiss Francs for the ISO Prolog error message standard so I was winging it from the SWI Prolog output. Actually, there are &lt;em&gt;two&lt;/em&gt; ISO Prolog standards documents, so it would have been 488CHF. As &lt;em&gt;if&lt;/em&gt;.&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;I suppose I should also mention the StatusDisplayer line. This is one of those things that I couldn&#039;t resist putting in but that gets obnoxious very quickly. When you go to an error line, it displays &quot;Fix me!&quot; on the status line at the bottom of the IDE. Hah hah. So take it out, you don&#039;t like it.&lt;/p&gt;
&lt;br /&gt;&lt;h3&gt;Testing The Final Version&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;Okay, let&#039;s give &#039;er a spin. Clean and build your project then install it to the target IDE as before. Open up the BraveNewWorld project. Wait. Open up sieve.pro. Unfix the error and the warning if you fixed them last time so we have something to work with. Click our &quot;Compile Prolog&quot; button on the toolbar.

&lt;br /&gt;&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://hulles.supersized.org/uploads/geewhiz/ss6-4a.png&#039; target=&quot;_blank&quot;&gt;&lt;!-- s9ymdb:82 --&gt;&lt;img width=&quot;90&quot; height=&quot;68&quot; style=&quot;border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://hulles.supersized.org/uploads/geewhiz/ss6-4a.serendipityThumb.png&quot; alt=&quot;&quot;  /&gt;&lt;/a&gt;&lt;br /&gt;

We have hyperlinks! Click on an unlinked (white) line in the Prolog output tab to make that window current. Click on one hyperlink, then the other. Notice how the cursor is moved to the editor window to the line and column where the error occurred. Now leave sieve.pro open and open up fib1.pro. Introduce an error into the code, save it, and compile it with our shiny new button. The error hyperlink from that compile should take you to the error in fib1.pro. Now scroll up the Prolog output window to our first compile and click one of the original hyperlinks. Notice that &lt;em&gt;sieve.pro is reselected&lt;/em&gt; and the error position is displayed again. Go ahead, say it, you know you want to: &quot;Gee whiz!&quot;&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;So now we can compile our little Prolog hearts out in the NetBeans IDE. We still need to open SWI Prolog to run the stuff, but that&#039;s not so bad. It &lt;em&gt;should&lt;/em&gt; be possible to connect up a Reader in the same way we did with the OutputWriter to allow the interaction, however. Hmmm.... You&#039;re welcome to do that if you want. I haven&#039;t yet, but I&#039;m sure it&#039;s only a matter of time before I have to try it. If you do, let me know how it comes out.&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;We now have a pretty decent Prolog IDE embedded within the NetBeans architecture. What&#039;s next? We&#039;re going to create a Prolog project template so we don&#039;t have to stick our lovely, elegant and erudite nonprocedural Prolog source code into an icky stinky Java class library, that&#039;s what&#039;s next. Stay tuned.&lt;/p&gt;
&lt;br /&gt;&lt;h3&gt;Files From This Entry&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href=&#039;http://hulles.supersized.org/uploads/geewhiz/geewhiz_partfive.tar.gz&#039;&gt;geewhiz_partfive.tar.gz&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a onclick=&quot;javascript:urchinTracker(&#039;/download/uploads/geewhiz/geewhiz_partfive.zip&#039;);&quot; href=&#039;http://hulles.supersized.org/uploads/geewhiz/geewhiz_partfive.zip&#039;&gt;geewhiz_partfive.zip&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;
&lt;br /&gt;

 
    </content:encoded>

    <pubDate>Wed, 21 May 2008 20:46:21 +0200</pubDate>
    <guid isPermaLink="false">http://hulles.supersized.org/archives/7-guid.html</guid>
    
</item>

</channel>
</rss>