Ruben Laguna’s blog

Import Delicious Bookmarks Into Evernote

I tried to use the delicious online import tool from Evernote and I have to say that I’m not satisfied with it. It’s limited to 1000 tags (that’s due to important performance issues in Evernote when the number of tags is big) and if you don’t import the tags the tag information is lost.

I was expecting that it will add the delicious tags and link notes as body text in the Evernote note but they don’t. So I decided to do my own import to preserve that information.

That’s how bookmarks look like when using the online tool provided by Evernote:

My tool uses a delicious backup in Netscape Bookmarks file format created with in delicious with “Settings ⇒ Export/Backup bookmarks” as input. And uses the Mac Scripting for Evernote to create notes in a local notebook named del2 (that has to be previously created).

The delicious bookmarks looks like this once they are imported to evernote

Here is the ruby script (also as a gist) that I used to do the import (note that you need to install rubyosa on htmlentities gems prior to use:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<span class='line'>#!/usr/bin/ruby -w
</span><span class='line'>require "rubygems"
</span><span class='line'>require "rbosa"
</span><span class='line'>require "cgi"
</span><span class='line'>require "htmlentities"
</span><span class='line'>require 'iconv'
</span><span class='line'>
</span><span class='line'>
</span><span class='line'>OSA.utf8_strings = true
</span><span class='line'>evernote = OSA.app('Evernote')
</span><span class='line'>coder = HTMLEntities.new
</span><span class='line'>
</span><span class='line'>i=1
</span><span class='line'>url = nil
</span><span class='line'>add_date = nil
</span><span class='line'>title = nil
</span><span class='line'>last_visit = nil
</span><span class='line'>tags = nil
</span><span class='line'>notes = ""
</span><span class='line'>File.new("delicious-20090807.htm").each{|line|
</span><span class='line'>  #next unless i >2261
</span><span class='line'>
</span><span class='line'>  if line =~ /&lt;DT>&lt;A HREF="(.*)" LAST_VISIT="(.*)" ADD_DATE="(.*)" TAGS="(.*)">(.*)&lt;\/A>$/
</span><span class='line'>    if url
</span><span class='line'>      #puts "#{i} url:#{url} add:#{Time.at(add_date.to_i)} last:#{Time.at(last_visit.to_i)} tags:#{tags} title:#{title} notes:#{notes}"
</span><span class='line'>      #title = CGI.escapeHTML(title)
</span><span class='line'>      #notes = CGI.escapeHTML(notes)
</span><span class='line'>      #titleunencoded = Iconv.conv("UTF-8","ISO-8859-1",title)
</span><span class='line'>      titleunencoded = title[0,254]
</span><span class='line'>      title = coder.encode(title,:named)
</span><span class='line'>      notes = coder.encode(notes,:named)
</span><span class='line'>      tags = coder.encode(tags,:named)
</span><span class='line'>      body = "h1. #{title}
</span><span class='line'>      body += "p. &lt;a href=\"#{url}\">#{title}&lt;/a>
</span><span class='line'>      body += "h2. tags
</span><span class='line'>      body += "p. #{tags}
</span><span class='line'>      body += "h2. description and notes
</span><span class='line'>      body += "p. #{notes}
</span><span class='line'>      body += "h2. dates
</span><span class='line'>      body += "p. add date: #{Time.at(add_date.to_i)}
</span><span class='line'>      body += "p. last visit date: #{Time.at(last_visit.to_i)}
</span><span class='line'>      puts body
</span><span class='line'>      notehandle = evernote.create_note(:with_html => body, :title => titleunencoded, :notebook => "del2" )
</span><span class='line'>      notehandle.source_url = url
</span><span class='line'>     
</span><span class='line'>      i += 1
</span><span class='line'>      url,last_visit,add_date,tags,title,notes = nil,nil,nil,nil,nil,""
</span><span class='line'>    end
</span><span class='line'>    url,last_visit,add_date,tags,title = $1, $2, $3, $4, $5
</span><span class='line'>  else
</span><span class='line'>    notes += line unless url.nil?
</span><span class='line'>  end
</span><span class='line'>}</span>

Fixing “Invalid XML: Too Many Tags” [Google Webmaster Tools]

I tried to use my blog feed as a sitemap in Google Webmaster Tools, but it Google complains about “Invalid XML: too many tags describing this tags” in several points in the feed.

I found in several places that this due to the format of the feed, a simple change to ATOM will fix it. But I’m using a Wordpress Feedburner plugin to redirect my blog’s feed to the Feedburner one. This plugin redirects all feeds (/wp/feed, /wp/wp-atom.php, etc.) to the feedburner feed (that for some reason Google doesn’t like).

The solution described in some blogs is to change the sitemaps address to point to the wp-atom.php directly but that won’t work in my case because the plugin redirects that too to feedburner.

So I had to update the plugin code to skip redirection if the user agent is “googlebot” (currently only skip redirections if the user agent is feedburner or feedvalidator).

That solved the issue. I changed the sitemap url in Google Webmaster Tools to point to /wp/wp-atom.php and now Google doesn’t complain about “too many tags”

If you want to do the same just edit $WORDPRESS INSTALLATION/wp-content/plugins/FeedBurner_FeedSmith_Plugin.php and look for a line containing “feedvalidator”. You should change it to look like this (it’s just adding “|googlebot” after feedvalidator):

if (!preg_match("/feedburner|feedvalidator|googlebot/i", $_SERVER['HTTP_USER_AGENT'])) {
	add_action('template_redirect', 'ol_feed_redirect');
	add_action('init','ol_check_url');
}

CGLib, NetBeans Modules and Class Loaders

My first try at using CGLib from a Netbeans RCP ended up in a ClassNotFoundException for net.sf.cglib.proxy.Factory

I had two modules, MainModule and cglib and MainModule was depending on cglib. And the following snippet was raising the ClassNotFoundException.

1
2
3
4

1
2
3
4
<span class='line'>Enhancer enhancer = new Enhancer();
</span><span class='line'>        enhancer.setSuperclass(Graph.class);
</span><span class='line'>        enhancer.setCallback(NoOp.INSTANCE); 
</span><span class='line'>        Graph g = (Graph) enhancer.create(new Class[]{FrameListener.class,InterpreterAbstraction.class},new Object[]{fl,ia}) ;</span>

As I later found out from Enhancer documentation and DevFaqClassLoaders it seems that Enhancer it taking the System class loader instead of taking the Module classloader. I explicitly set the classloader used by the Enhancer via the setClassLoader method and now it seems to work.

1
2
3
4
5

1
2
3
4
5
<span class='line'>Enhancer enhancer = new Enhancer();
</span><span class='line'>        enhancer.setSuperclass(Graph.class);
</span><span class='line'>        enhancer.setClassLoader(getClass().getClassLoader());    //Use the module classloader instead of the system classloader    
</span><span class='line'>        enhancer.setCallback(NoOp.INSTANCE); 
</span><span class='line'>        Graph g = (Graph) enhancer.create(new Class[]{FrameListener.class,InterpreterAbstraction.class},new Object[]{fl,ia}) ;</span>

This is the exception stacktrace I got (just for the record):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
<span class='line'>java.lang.ClassNotFoundException: net.sf.cglib.proxy.Factory
</span><span class='line'>     at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
</span><span class='line'>     at java.security.AccessController.doPrivileged(Native Method)
</span><span class='line'>     at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
</span><span class='line'>     at java.lang.ClassLoader.loadClass(ClassLoader.java:319)
</span><span class='line'>     at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:330)
</span><span class='line'>     at java.lang.ClassLoader.loadClass(ClassLoader.java:254)
</span><span class='line'>     at org.netbeans.ProxyClassLoader.loadClass(ProxyClassLoader.java:257)
</span><span class='line'>Caused: java.lang.ClassNotFoundException: net.sf.cglib.proxy.Factory starting from ModuleCL@56466f1d[com.rubenlaguna.graphbrowser.guess] with possible defining loaders [ModuleCL@4808e0e1[com.rubenlaguna.graphbrowser.cglibmodule]] and declared parents []
</span><span class='line'>     at org.netbeans.ProxyClassLoader.loadClass(ProxyClassLoader.java:259)
</span><span class='line'>     at java.lang.ClassLoader.loadClass(ClassLoader.java:254)
</span><span class='line'>     at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:402)
</span><span class='line'>Caused: java.lang.NoClassDefFoundError: net/sf/cglib/proxy/Factory
</span><span class='line'>     at java.lang.ClassLoader.defineClass1(Native Method)
</span><span class='line'>     at java.lang.ClassLoader.defineClass(ClassLoader.java:703)
</span><span class='line'>Caused: java.lang.reflect.InvocationTargetException
</span><span class='line'>     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
</span><span class='line'>     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
</span><span class='line'>     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
</span><span class='line'>     at java.lang.reflect.Method.invoke(Method.java:597)
</span><span class='line'>     at net.sf.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:384)
</span><span class='line'>     at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:219)
</span><span class='line'>Caused: net.sf.cglib.core.CodeGenerationException: java.lang.reflect.InvocationTargetException-->null
</span><span class='line'>     at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:237)
</span><span class='line'>     at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:377)
</span><span class='line'>     at net.sf.cglib.proxy.Enhancer.create(Enhancer.java:304)
</span><span class='line'>     at com.rubenlaguna.graphbrowser.mainmodule.CircularLayout.performGraphLayout(CircularLayout.java:81)
</span><span class='line'>     at org.netbeans.api.visual.graph.layout.GraphLayout.layoutGraph(GraphLayout.java:116)
</span><span class='line'>     at org.netbeans.api.visual.layout.LayoutFactory$1.performLayout(LayoutFactory.java:284)
</span><span class='line'>     at org.netbeans.api.visual.layout.SceneLayout$LayoutSceneListener.sceneValidated(SceneLayout.java:122)
</span><span class='line'>     at org.netbeans.api.visual.widget.Scene.validate(Scene.java:420)
</span><span class='line'>     at org.netbeans.api.visual.widget.SceneComponent.addNotify(SceneComponent.java:92)
</span><span class='line'>     at java.awt.Container.addImpl(Container.java:1068)
</span><span class='line'>     at java.awt.Container.add(Container.java:927)
</span><span class='line'>     at com.rubenlaguna.graphbrowser.mainmodule.GraphTopComponent.setDotFile(GraphTopComponent.java:182)
</span><span class='line'>     at com.rubenlaguna.graphbrowser.mainmodule.DotFileOpenSupport.createCloneableTopComponent(DotFileOpenSupport.java:45)
</span><span class='line'>     at org.openide.windows.CloneableOpenSupport.openCloneableTopComponent(CloneableOpenSupport.java:197)
</span><span class='line'>     at org.openide.windows.CloneableOpenSupport$1.run(CloneableOpenSupport.java:98)
</span><span class='line'>     at org.openide.util.Mutex.doEvent(Mutex.java:1335)
</span><span class='line'>     at org.openide.util.Mutex.writeAccess(Mutex.java:452)
</span><span class='line'>     at org.openide.windows.CloneableOpenSupport.open(CloneableOpenSupport.java:95)
</span><span class='line'>     at org.openide.actions.OpenAction.performAction(OpenAction.java:81)
</span><span class='line'>     at org.openide.util.actions.NodeAction$DelegateAction$1.run(NodeAction.java:589)
</span><span class='line'>     at org.netbeans.modules.openide.util.ActionsBridge.doPerformAction(ActionsBridge.java:77)
</span><span class='line'>     at org.openide.util.actions.NodeAction$DelegateAction.actionPerformed(NodeAction.java:585)
</span><span class='line'>     at org.openide.explorer.view.TreeView$PopupSupport.mouseClicked(TreeView.java:1515)
</span><span class='line'>     at java.awt.AWTEventMulticaster.mouseClicked(AWTEventMulticaster.java:253)
</span><span class='line'>     at java.awt.AWTEventMulticaster.mouseClicked(AWTEventMulticaster.java:252)
</span><span class='line'>     at java.awt.AWTEventMulticaster.mouseClicked(AWTEventMulticaster.java:252)
</span><span class='line'>     at java.awt.AWTEventMulticaster.mouseClicked(AWTEventMulticaster.java:252)
</span><span class='line'>     at java.awt.Component.processMouseEvent(Component.java:6304)
</span><span class='line'>     at javax.swing.JComponent.processMouseEvent(JComponent.java:3265)
</span><span class='line'>     at java.awt.Component.processEvent(Component.java:6066)
</span><span class='line'>     at java.awt.Container.processEvent(Container.java:2085)
</span><span class='line'>     at java.awt.Component.dispatchEventImpl(Component.java:4667)
</span><span class='line'>     at java.awt.Container.dispatchEventImpl(Container.java:2143)
</span><span class='line'>     at java.awt.Component.dispatchEvent(Component.java:4497)
</span><span class='line'>     at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4600)
</span><span class='line'>     at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4273)
</span><span class='line'>     at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4194)
</span><span class='line'>     at java.awt.Container.dispatchEventImpl(Container.java:2129)
</span><span class='line'>     at java.awt.Window.dispatchEventImpl(Window.java:2475)
</span><span class='line'>     at java.awt.Component.dispatchEvent(Component.java:4497)
</span><span class='line'>[catch] at java.awt.EventQueue.dispatchEvent(EventQueue.java:635)
</span><span class='line'>     at org.netbeans.core.TimableEventQueue.dispatchEvent(TimableEventQueue.java:104)
</span><span class='line'>     at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:296)
</span><span class='line'>     at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:211)
</span><span class='line'>     at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:201)
</span><span class='line'>     at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:196)
</span><span class='line'>     at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:188)
</span><span class='line'>     at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)</span>

References

project.displayName and project.name Don’t Get Set for Netbeans Platform Application Projects

I just reported it to netbeans issuezilla issue 169074.

I was trying to use these variables as pointed out by Michal Hlavac in my post about templates. And then I realized that these variables don’t seem to work for Netbeans Plaform Application projects. Instead of the project name and display name they (the template variables, I mean) get replaced by an error message

Expression project.name is undefined on line 10, column 19 in Templates/Licenses/license-gplv3.txt

Then I tried to use those same variables in a regular java application project and they seem to work fine for that kind of project. So I think is an issue involving only Netbeans Platform Application projects. Let’s see what they reply to the issue.

Adding Support for Opening Dot Files to GraphBrowser

I spent some time on GraphBrowser, starting off the project, the first step has been to add basic support for opeining Graphviz DOT files.

There are already some pages on the net talking about how to add open file support for new file types in NetBeans.

So there is no need to explain how to do it in a detailed way.

The first main step is to invoke New File Type wizard on the module (in my case MainModule) to add the basic files to support the file type.


  1. Navigate to the MainModules and click Add ⇒ New ⇒ File Type

  2. Fill the MIME-type for Graphviz DOT files which is text/vnd.graphviz and the filename extensions .dot,.DOT

  3. Click Next

  4. Class Name Prefix : DotFile

  5. Select a 16×16 icon file. You can create your own PNG icon file by using an icon creator/designer/editor/maker like IcoFX

  6. NetBeans will create a bunch of files for you and will also update layer.xml

    • Bundle.properties

    • DotFileDataObject.java

    • DotFileResolver.xml

    • DotFileTemplate.dot

    • dot.gif

Now that we have the basic support in place. The files generated by the New File Type wizard provide a default behaviour that consist on opening the file in a text editor, we don’t want that so the next step is to do changes to DotFileDataObject.java. We must replace the DataEditorSupport.create with a custom OpenSupport.

public DotFileDataObject(FileObject pf, MultiFileLoader loader) throws DataObjectExistsException, IOException {
        super(pf, loader);
        CookieSet cookies = getCookieSet();
        //cookies.add((Node.Cookie) DataEditorSupport.create(this, getPrimaryEntry(), cookies));
        cookies.add(new DotFileOpenSupport(getPrimaryEntry()));
    }

And then we create a DotFileOpenSupport class that extends OpenSupport and implements Node.Cookie and OpenCookie


package com.rubenlaguna.graphbrowser.mainmodule;

import org.openide.cookies.OpenCookie;
import org.openide.loaders.MultiDataObject.Entry;
import org.openide.loaders.OpenSupport;
import org.openide.nodes.Node;
import org.openide.windows.CloneableTopComponent;

/**
*

  • @author ECERULM
    */
    class DotFileOpenSupport extends OpenSupport implements Node.Cookie, OpenCookie {
public DotFileOpenSupport(Entry primaryEntry) { super(primaryEntry); } @Override protected CloneableTopComponent createCloneableTopComponent() { //throw new UnsupportedOperationException(“Not supported yet.”); GraphTopComponent tc = GraphTopComponent.findInstance(); tc.setDisplayName(entry.getDataObject().getName()); tc.setDotFile(entry); return tc; }

}

This newly created DotFileOpenSupport opens a TopComponent(GraphTopComponent), sets the display name of the TopComponent and load the file into the TopComponent.

So now we have to create GraphTopComponent. We can use the Window Component wizard. Just right click on the MainModule and New ⇒ Window Component. The wizard will do everything for us, the only information that we need to provide it’s specify the window position (editor) and class name prefix (Graph) and an icon. After clicking Finish, Netbeans generates a bunch of files and entries in layer.xml. We should open GraphTopComponent and change it to extend CloneableTopComponent instead of TopComponent as OpenSupport.createCloneableTopComponent() requires a CloneableTopComponent. We also need to add a setDotFile implementation. We will add it empty for now.

    void setDotFile(Entry entry) {
        //throw new UnsupportedOperationException("Not yet implemented");
    }

Now we can test our progress so far.
1

Double clicking on a dot file will open the GraphTopComponent and update the display name nothing more. It’s possible to review the changes did so far in kenai.

Now it’s time to add some real stuff to the GraphTopComponent. This means to to add some real logic to setDotFile and actually parse the dot file and create a visual representation of it using Netbeans Visual Library.

For parsing the DOT file we will use JPGD. We will wrap JPGD in a netbeans module to make thing easier.

So download JPGD 0.8.6beta and untar it. Then click on the project and click on Add new library. Then select “com.alexmerz.graphviz.jar” and “LICENSE”. Click next. You can the com.alexmerz.graphviz as project name.
Code name base: com.alexmerz.graphviz and module display name leave also “com.alexmerz.graphviz”. Click Finish. Netbeans will create a Netbeans Library Wrapper module. To learn how to attach the source code to this newly created module follow the instructions on this post. Now the module is added to the project and we need to add a dependency in the MainModule to our newly created “com.alexmerz.graphviz” module so we can use it from MainModule. So right click on MainModule ⇒ Properties ⇒ Libraries ⇒ Add dependency ⇒ and select “com.alexmerz.graphviz”.

Now that we are handling the dependencies, we should add add a dependency to Visual Library API from MainModule. So first, we add a Visual Library API to the project. From the project properties, add dependency ⇒ platform 10 ⇒ Visual Library API. Then go to MainModule properties and add a dependency to Visual Library API too (Properties ⇒ Libraries ⇒ Visual Library API).

Now that we have the dependencies on place, we can add a JScrollPane to GraphTopComponent and a JPanel to the JScrollPanel. Then right-click on the JPanel and set the layout to BorderLayout.

Now it’s time to provide the implementation for GraphTopComponent.setDotFile:

void setDotFile(Entry entry) { //throw new UnsupportedOperationException(“Not yet implemented”); this.scene = new DotGraphScene(entry); GraphLayout graphLayout = GraphLayoutFactory.createHierarchicalGraphLayout(scene, false); this.layout = LayoutFactory.createSceneGraphLayout(scene, graphLayout); Logger.getLogger(GraphTopComponent.class.getName()).info(“invoking layout”); layout.invokeLayoutImmediately(); jPanel1.removeAll(); JComponent view = scene.createView(); jPanel1.add(view, BorderLayout.WEST); JComponent satelliteView = scene.createSatelliteView(); jPanel1.add(satelliteView, BorderLayout.EAST); //scene.mainLayerToFront(); //Logger.getLogger(GraphTopComponent.class.getName()).info(“invoking paint”); //scene.paint(); }

We can test the application again.
2
Now the GraphTopComponent shows a graph representation of the GraphViz DOT file.

You can see the corresponding changes in mercurial by looking at these two changesets

The next step will be add dynamic layout and some of the layouts provided by GUESS.

How Not to Do a Parcel Tracking System

The SEUR tracking system in amazing in the wrong way.

I got a tracking number from a bike shop that dispatched a bike from spain to sweden.

Ok, so I go to http://www.seur.es (because http://seur.es didn’t work) and put my tracking number there. Nothing, the tracking number is not yet in the system. Disappointment number 1. But this is not the first time I see this, you get a tracking number and it takes a while until you can actually find it in the system so I’m OK.

After a couple of hours I try again and there it is. It only says “T1 EXPEDICION DOCUMENTADA” (expedition documented) not a very clear message I would say. I choose to believe that means the shipment has been recorded. The entry has a date but no time it just says 00:00:00. Disapointment number 2.

So the next morning I check again, it says the same thing. How come? Something should have happened with my bike, I’m sure it has moved to somewhere. So after digging in the FAQs of seur.es I find that if I want to track an international shipment I should use http://www.seurinternacional.com instead. Disappointement number 3. What?!? At least they could say so when I search in seur.es, they now it’s an international shipment, why don’t they tell me there that I’m looking in the wrong place? That I should go to seurinternacional instead.

We are in 2009 and they didn’t get this right yet.

At least now I know that the bike arrived to barcelona at 8:00 this morning.

Conceptronics CSATACOMBO Is Not a Hardware RAID Card

As clearly stated in Linux SATA RAID FAQ the Conceptronics CSATACOMBO card based on VIA vt6421 chipset is not a Hardware RAID.

The CSATACOMBO is called a “fake raid”, “BIOS RAID” or “BIOS-assisted software RAID” that means that actually a driver at OS level is needed to get the RAID features (see more info about BIOS RAID at novell page).

And that’s why Linux (gparted for example) doesn’t recognize the RAID arrays created with CSATACOMBO because there’s no Linux driver to handle the proprietary BIOS RAID in CSATACOMBO.

You can always create a Sofware RAID.

Python Support for SSL and HTTPS Is Not Installed

I was getting “Python support for SSL and HTTPS is not installed” while trying to use Mercurial on Mac OS X 10.5 Leopard. I upgraded python to 2.5.2

sudo port upgrade python

and mercurial too (1.2.1)

sudo port -u upgrade mercurial

But that didn’t fix it. I’m glad that I found the solution on Twitter

Just do an

sudo port install py25-socket-ssl

Painting LabelWidgets on Top of the ConnectionWidget - Netbeans Visual Library

I was fiddling with the Netbeans Visual Library again, trying to show a complex graph on screen and I run into a problem: There were so many connection in the graph that some widgets were hard to read because the ConnectionWidget arrows were printed over them.

LabelWidget hidden under ConnectionWidget

So I tried to I try to rearrange the order of my LayerWidgets to paint the LabelWidgets on top of ConnectionWidgets. A call to mainLayer.bringToFront() should be enough, but this is what I got when I tried that:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<span class='line'>java.lang.IllegalStateException: Widget (org.netbeans.api.visual.widget.LabelWidget@10014f0) was not added into the scene
</span><span class='line'>        at org.netbeans.api.visual.anchor.Anchor.getRelatedSceneLocation(Anchor.java:213)
</span><span class='line'>        at org.netbeans.modules.visual.anchor.RectangularAnchor.compute(RectangularAnchor.java:111)
</span><span class='line'>        at org.netbeans.modules.visual.router.DirectRouter.routeConnection(DirectRouter.java:65)
</span><span class='line'>        at org.netbeans.api.visual.widget.ConnectionWidget.calculateRouting(ConnectionWidget.java:527)
</span><span class='line'>        at org.netbeans.modules.visual.layout.ConnectionWidgetLayout.layout(ConnectionWidgetLayout.java:109)
</span><span class='line'>        at org.netbeans.api.visual.widget.Widget.layout(Widget.java:1350)
</span><span class='line'>        at org.netbeans.api.visual.widget.Widget.layout(Widget.java:1342)
</span><span class='line'>        at org.netbeans.api.visual.widget.LayerWidget.layout(LayerWidget.java:86)
</span><span class='line'>        at org.netbeans.api.visual.widget.Widget.layout(Widget.java:1342)
</span><span class='line'>        at org.netbeans.api.visual.widget.Scene.layoutScene(Scene.java:312)
</span><span class='line'>        at org.netbeans.api.visual.widget.Scene.validate(Scene.java:393)
</span><span class='line'>        at org.netbeans.api.visual.widget.SceneComponent.addNotify(SceneComponent.java:92)
</span><span class='line'>        at java.awt.Container.addImpl(Container.java:1039)
</span><span class='line'>        at java.awt.Container.add(Container.java:896)
</span><span class='line'>        at com.rubenlaguna.modules.mainmodule.GraphTopComponent.setDotFile(GraphTopComponent.java:80)
</span><span class='line'>        at com.rubenlaguna.modules.mainmodule.DotOpenSupport.createCloneableTopComponent(DotOpenSupport.java:31)
</span><span class='line'>        at org.openide.windows.CloneableOpenSupport.openCloneableTopComponent(CloneableOpenSupport.java:197)
</span><span class='line'>        at org.openide.windows.CloneableOpenSupport$1.run(CloneableOpenSupport.java:98)
</span><span class='line'>        at org.openide.util.Mutex.doEvent(Mutex.java:1335)
</span><span class='line'>        at org.openide.util.Mutex.writeAccess(Mutex.java:452)
</span><span class='line'>        at org.openide.windows.CloneableOpenSupport.open(CloneableOpenSupport.java:95)
</span><span class='line'>        at org.openide.actions.OpenAction.performAction(OpenAction.java:81)
</span><span class='line'>        at org.openide.util.actions.NodeAction$DelegateAction$1.run(NodeAction.java:589)
</span><span class='line'>        at org.netbeans.modules.openide.util.ActionsBridge.doPerformAction(ActionsBridge.java:77)
</span><span class='line'>        at org.openide.util.actions.NodeAction$DelegateAction.actionPerformed(NodeAction.java:585)
</span><span class='line'>        at org.openide.explorer.view.TreeView$PopupSupport.mouseClicked(TreeView.java:1515)
</span><span class='line'>        at java.awt.AWTEventMulticaster.mouseClicked(AWTEventMulticaster.java:253)
</span><span class='line'>        at java.awt.AWTEventMulticaster.mouseClicked(AWTEventMulticaster.java:252)
</span><span class='line'>        at java.awt.AWTEventMulticaster.mouseClicked(AWTEventMulticaster.java:252)
</span><span class='line'>        at java.awt.AWTEventMulticaster.mouseClicked(AWTEventMulticaster.java:252)
</span><span class='line'>        at java.awt.Component.processMouseEvent(Component.java:6137)
</span><span class='line'>        at javax.swing.JComponent.processMouseEvent(JComponent.java:3265)
</span><span class='line'>        at java.awt.Component.processEvent(Component.java:5899)
</span><span class='line'>        at java.awt.Container.processEvent(Container.java:2023)
</span><span class='line'>        at java.awt.Component.dispatchEventImpl(Component.java:4501)
</span><span class='line'>        at java.awt.Container.dispatchEventImpl(Container.java:2081)
</span><span class='line'>        at java.awt.Component.dispatchEvent(Component.java:4331)
</span><span class='line'>        at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4301)
</span><span class='line'>        at java.awt.LightweightDispatcher.processMouseEvent(Container.java:3974)
</span><span class='line'>        at java.awt.LightweightDispatcher.dispatchEvent(Container.java:3895)
</span><span class='line'>        at java.awt.Container.dispatchEventImpl(Container.java:2067)
</span><span class='line'>        at java.awt.Window.dispatchEventImpl(Window.java:2458)
</span><span class='line'>        at java.awt.Component.dispatchEvent(Component.java:4331)
</span><span class='line'>[catch] at java.awt.EventQueue.dispatchEvent(EventQueue.java:599)
</span><span class='line'>        at org.netbeans.core.TimableEventQueue.dispatchEvent(TimableEventQueue.java:104)
</span><span class='line'>        at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
</span><span class='line'>        at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
</span><span class='line'>        at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
</span><span class='line'>        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
</span><span class='line'>        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
</span><span class='line'>        at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)</span>

So I posted a question on netbeans graph mailing list but the suggestions there didn’t resolved my problem.

After digging a little bit into the Visual Library source code I come up with a really easy solution. Just call mainLayer.bringToFront() AFTER the scene was painted for the first time on the screen. I should have tried that first!. The result shows LabelWidgets painted overt the ConnectionWidgets so the text in the LabelWidgets is always readable.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<span class='line'>class GraphTopComponent extends CloneableTopComponent {
</span><span class='line'>//.... omitted ....
</span><span class='line'>  private showScene() {
</span><span class='line'>        JComponent c = scene.createView();
</span><span class='line'>        GraphLayout graphLayout = GraphLayoutFactory.createHierarchicalGraphLayout(scene, false);
</span><span class='line'>        ForceDirectedLayout forceDirectedGraphLayout = new ForceDirectedLayout(scene);
</span><span class='line'>        SceneLayout sceneGraphLayout = LayoutFactory.createSceneGraphLayout(scene, graphLayout);
</span><span class='line'>        sceneGraphLayout.invokeLayout();
</span><span class='line'>        this.forceDirectedSceneLayout = LayoutFactory.createSceneGraphLayout(scene, forceDirectedGraphLayout);
</span><span class='line'>        jPanel1.removeAll();
</span><span class='line'>        jPanel1.add(c);
</span><span class='line'>        scene.bringMainLayerToFront();
</span><span class='line'>  }
</span><span class='line'>}
</span><span class='line'>class DotGraphScene extends GraphScene.StringGraph {
</span><span class='line'>//... omitted ...
</span><span class='line'>    void bringMainLayerToFront() {
</span><span class='line'>        mainLayer.bringToFront();
</span><span class='line'>    }
</span><span class='line'>
</span><span class='line'>}</span>

LabelWidgets on top of ConnectionWidgets

Anchor will throw an IllegalStateException if the relatedWidget has no location in the scene. Those widget will not get a position until the LayerWidget that contains them is processed. So you keep the original order of the LayerWidgets until the scene is painted for the first time, by then all widgets have a location in the scene and then it’s posible to change the relative order of the LayerWidgets using LayerWidget.bringToFront().

The only problem with this approach is that is your are using a dynamic layout like me there is a chance that the layout changes the position of the LabelWidget by a noticiable amount and the Anchor will not be updated accordingly until the next iteration of the dynamic layout. Which lead to the artifact marked in red in the previous figure. This is only a real problem if your dynamic layout doesn’t stabilize or if is not very smooth.

Copyright © 2015 - Ruben Laguna - Powered by Octopress