PyScan 0.6

10. October, 2009

I’ve done some more work on PyScan. Most work is under the hood but I’ve got a system to load and save projects and a bug was fixed: If you pressed the Scan button too quickly with 0.4, the last image could have been overwritten. PyScan is now based on hplip 3.9.8.

PyScan 0.6.tar.gz (16KB, MD5 Sum: 2b5e23099be438ceceb69ec23d64cec6)

See the original post for features and the system requirements.


Attaching Sources To Eclipse Artifacts

8. October, 2009

After running mvn eclipse:make-artifacts -DstripQualifier=true -DeclipseDir=...path/to/eclipse, you might have lots of source JARs but they aren’t in the right place for Maven 2 to pick them up.

This little Python script fixes that. Just run it after eclipse:make-artifacts.

Note: You may be wondering why I use eclipse:make-artifacts instead of the recommended eclipse:to-maven. Simple: I don’t like to have a dozen core-*.jar in my project.

"""Maven 2 Eclipse Artifact Source resolver

After importing Eclipse artifacts into an M2 repository with

> mvn eclipse:make-artifacts -DstripQualifier=true -DeclipseDir=.../eclipse

run this script to move all source JARs in the right place for
Maven 2 to pick them up.
"""
import os, sys
from shutil import copyfile

def processGroup(path):
    print 'group %s' % path
    for dir in os.listdir(path):
        path2 = os.path.join(path, dir)
        if '.' in dir:
            processArtifact(path2)
        else:
            processGroup(path2)

def processArtifact(path):
    srcPath = path + '.source'
    #print 'processArtifact',srcPath
    if os.path.exists(srcPath):
        processArtifactWithSource(path)

def processArtifactWithSource(basePath):
    binPath = basePath
    srcPath = basePath + '.source'
    baseName = os.path.basename(basePath)
    print 'artifact', baseName
    
    for version in os.listdir(binPath):
        vbinPath = os.path.join(binPath, version)
        vsrcPath = os.path.join(srcPath, version)
        
        srcJar = os.path.join(vsrcPath, '%s.source-%s.jar' % (baseName, version))
        destJar = os.path.join(vbinPath, '%s-%s-sources.jar' % (baseName, version))
        
        if os.path.exists(srcJar):
            print '%s -> %s' % (srcJar, destJar)
            copyfile(srcJar, destJar)

m2repo = os.environ['M2_REPO']
if not m2repo:
    raise Exception('Env variable M2_REPO is not set')

root = os.path.join(m2repo, 'org', 'eclipse')
for dir in os.listdir(root):
    path = os.path.join(root, dir)
    processGroup(path)

Updated 01. December 2009: The script now figures out where your M2 repo is. Plus a minimum of documentation.


Testing the Impossible: Shifting IDs

7. October, 2009

So you couldn’t resist and wrote tests against the database. With Hibernate, no less. And maybe some Spring. And to make things more simple, toString() contains the IDs of the objects in the database. Now, you want to check the results of queries and other operations using my advice how to verify several values at once.

During your first run, your DB returns some values, you copy them into a String. When you run the tests again, the tests fail because all the IDs have changed. Bummer. Now what? Just verify that the correct number of results was returned?

Don’t. assertEquals (3, list.size()) doesn’t give you a clue where to search for the error when the assert fails.

The solution is to give each ID a name. The name always stays the same and when you look at the test results, you’ll immediately know that the ID has been “fixated” (unlike when you’d replace the number with another, for example). Here is the code:

import java.util.*;

/** Helper class to fixate changing object IDs */
public class IDFixer
{
    private Map<String, String> replacements = new HashMap<String, String> ();
    private Map<String, Exception> usedNames = new HashMap<String, Exception> ();
    
    /** Assign a name to the number which follows the string <code>id=</code> in the object's
     * <code>toString()</code> method. */
    public void register (Object o, String name)
    {
        if (o == null)
            return;
        
        String s = o.toString ();
        int pos = s.indexOf ("id=");
        if (pos != -1)
            pos = s.indexOf (',', pos);
        if (pos == -1)
            throw new RuntimeException ("Can't find 'id=' in " + s);
        String search = s.substring (0, pos + 1);
        
        pos = search.lastIndexOf ('=');
        String replace = search.substring (0, pos + 1) + name + ",";
        
        add (name, search, replace);
        
        registerSpecial (o, name);
    }

    /** Add a search&replace pattern. */
    protected void add (String name, String search, String replace)
    {
        if (usedNames.containsKey (replace))
            throw new RuntimeException ("Name "+name+" is already in use", usedNames.get (replace));
        
        //System.out.println ("+++ ["+search+"] -> ["+replace+"]");
        usedNames.put (replace, new Exception ("Name was registered here"));
        replacements.put (search, replace);
    }
    
    /** Allow for special mappings */
    protected void registerSpecial (Object o, String name)
    {
        // NOP
    }

    /** Turn a <code>Collection</code> into a <code>String</code>, replacing all IDs with names. */
    public String toString (Collection c)
    {
        StringBuilder buffer = new StringBuilder (10240);
        String delim = "";
        for (Object o: c)
        {
            buffer.append (delim);
            delim = "\n";
            buffer.append (o);
        }
        return toString (buffer);
    }
    
    /** Turn a <code>Map</code> into a <code>String</code>, replacing all IDs with names. */
    public String toString (Map m)
    {
        StringBuilder buffer = new StringBuilder (10240);
        String delim = "";
        for (Iterator iter=m.entrySet ().iterator (); iter.hasNext (); )
        {
            Map.Entry e = (Map.Entry)iter.next ();
            
            buffer.append (delim);
            delim = "\n";
            buffer.append (e.getKey ());
            buffer.append ('=');
            buffer.append (e.getValue ());
        }
        return toString (buffer);
    }
    
    /** Turn an <code>Object</code> to a <code>String</code>, replacing all IDs with names.
     * 
     *  <p>If the object is a <code>Collection</code> or a <code>Map</code>, the special collection handling methods will be called. */
    public String toString (Object o)
    {
        if (o instanceof Collection)
        {
            return toString ((Collection)o);
        }
        else if (o instanceof Map)
        {
            return toString ((Map)o);
        }
        
        if (o == null)
            return "null";

        String s = o.toString ();
        for (Map.Entry<String, String> entry: replacements.entrySet ())
        {
            s = s.replace (entry.getKey (), entry.getValue ());
        }
        
        return s;
    }

    public boolean knowsAbout (String key)
    {
        return replacements.containsKey (key);
    }
}

To use the IDFixer, call register(object, name) to assign a name to whatever follows the number after id=. The method will search for this substring in the result of calling object.toString().

If you call IDFixer.toString(object) for a single object or a collection, it will replace id=number with id=name everywhere. If you have references between objects, you can register additional pattern to be replaced by overriding registerSpecial().

To make it easier to compare lists and maps in assertEquals(), each item gets its own line.


Updating Eclipse to 3.5.1 with p2

28. September, 2009

Maybe you’ve tried to update Eclipse to 3.5.1 and … you failed. Maybe it’s because the update site has gone. I’ve opened bug 290723. See you there.


Adobe Flash 10 Just Sent me Through the Roof [Update]

17. September, 2009

I just got the latest security fixes for Firefox 3.5.3. Finally, the browser warns me about outdated and insecure plugins. I like it!

Top of the list? Adobe flash player. Hm. The page needs JavaScript. Guys, other people managed to build click buttons with an image without JavaScript. *sigh* “Temporarily Allow adobe.com”. Huh? What’s that? “Free McAffee security scan (optional)”? Optional my a**! Die, die, die!

So I click the link. “get.adobe.com tries to install software on your computer. Enable?” Sure.

WTF? What the hell is “getplusplus”? And what’s it doing on my computer? *lot’s of swearing and cursing that you don’t want to hear* I hate it when someone smuggles unwanted software on my computer. Adobe, if you’re listening: THIS IS MY F***** COMPUTER! SO HANDS OFF!!!

After a restart, I can finally download … ah, crap, again this stupid McAffee! *ARGH*

Ok, it’s downloading. *grmbl* … installing. Hm. Well? Now, what? Anything happening anymore? Let’s check … nope, FF is still unhappy and about:plugins says 9.x.

Again. “Installer was already downloaded”. Really? So why isn’t it installed? Has Adobe finally managed to write software that can lie? Great. That’s what I really needed first thing in the morning.

Okay, np_gp.dll obviously isn’t worth the disk space it occupies. But since Adobe managed to infest most web pages on the Internet with their flash crap, a solution is necessary. Here goes:

The download shit from Adobe puts the files into %ALLUSERSPROFILE%\*\NOS\Adobe_Downloads\. Replace “*” with “Application Data” or “Anwendungsdaten” or your local name for that folder. In that folder, you should find a file “install_flash_player.exe”. Save that somewhere.

Open the addons and delete “getplusplus”. Exit Firefox. Now delete “np_gp.dll” in the plugins folder of Firefox. That should get you rid of that unwanted crap.

Before starting Firefox, run the installer manually. Check the details. Make sure that the installer installed the plugin in the Firefox directory instead of somewhere else. On my computer, it ended up in Opera’s plugins directory. If that’s the case with you, too, then copy the files “NPSWF32.dll”, “NPSWF32_FlashUtil.exe” and “flashplayer.xpt” manually.

Start Firefox and open about:plugins again. Search for “flash” and make sure that the version is 10.x. To make 100% sure, visit http://en.www.mozilla.com/en/firefox/3.5.3/whatsnew/. The security warning should now have gone away.

[Update] Apparently, the download manager is a security risk. So remove it ASAP.


Marketing in the 21st Century

16. September, 2009

If you ever wanted to know everything important about 21st century marketing (especially for software), there is a great 1 hour talk by Seth Godin which sums it up in a really funny and understandable way.

Note: The socks are great, aren’t they?


Powerful Wiki Engine for Eclipse

1. September, 2009

I’m toying with the idea to write a powerful wiki engine for Eclipse. What I have in mind should

  • Allow multiple markups (because all markups suck, so there is no point to prefer one over the other)
  • Should offer a side-by-side editor (source and preview because WYSIWYG is impossible to get right)
  • Should support automatic links (just like in the Java editor)

Right now, I’m not sure whether I should start with Mylin WikiText or Xtext.

Both look promising. WikiText support multiple markups. I just don’t like the two-page editor (where you have to flip pages to see what you’re doing). Also, I’m not sure how flexible the whole framework is.

Xtext would allow for much more complex markups but I’ll probably have to start with a more basic framework.

Links: Extending RIM with Xtext


Code Generators

26. August, 2009

Code generators can save a lot of time and effort which leads to the question: Why isn’t everyone using them? A lot of code in any project is repetition.

The main reason: With todays IDEs, you can’t debug generated code. Well, you can debug the result of the code generation but when hunting bugs in generated code, chances are that you want to hunt them in the input and/or the transformer and no IDE has a notion of “this line of code came from these inputs, so show them as well”.

Example: You need POJOs which map database tables. Easy enough: You create a small program that connects to the database, maps the types in the tables to types in your favorite programming language (or rather the one you have to use for your project) and dump some code.

But unlike C/C++, modern languages don’t have a #line directive which says “this piece of code was originally part of another file” let alone “this information came from the database XXX”. So when there is some problem, you need to dig through the layers of the code generator, the templates and the input (the database) yourself, running the transformation in your head. Bad.

Approaches like MDD will keep failing until they support this.


UI Design: Why is That Button Gray?

13. August, 2009

Here is one tip for your UI design that can really make life easier for your users: “Why is that button gray?” – or smart tooltips for disabled elements.

One additional comment: The tooltip shows a lot of information why the button is disabled. Why not simply set the right tooltip in the save place where you disable the button? At that time, you’ll know exactly why you do it and you can give the user specific directions what to do now (instead of having her read and pick from a list).


Code Comments Stink

27. July, 2009

If you ever need a good argument why code should be readable without comments, look here.

Dead languages are dead because they don’t fit today anymore. Let them rest.