Sunday, September 7, 2014

[Linux] Create a Mind Map (Freemind) from a Directory Structure

I wanted to have a mindmap of the Cyanogenmod source tree.  It is a huge bunch of folders and I just want a nice way to have an overview of the file structure and make notes.

FreeMind, the open source mind mapping tool, has a native utility to import a directory structure, but I found that the resulting mind map was on the heavyweight side.  In fact, using FreeMind 0.9, the native import tool never completed the import -- after 30 minutes or so I had to kill the application.

So I just want to create a FreeMind file that has the directory entries as text nodes, and that's it.  How about using the awesome "tree" command line tool in Linux?

Here is what the output looks like from my /usr/share folder (note I'm using the -d option to show only directories):

wskellenger@marquette /usr/share $ tree -d
.
├── aclocal
├── acpi-support
├── adduser
├── alsa
│   ├── alsa.conf.d
│   ├── cards
│   │   └── SI7018
│   ├── init
│   ├── pcm
│   ├── speaker-test
│   └── ucm
│       ├── apq8064-tabla-snd-card
│       ├── DAISY-I2S
│       ├── Manta-I2S
│       ├── Manta-SPDIF
│       ├── PandaBoard
│       ├── PandaBoardES
│       ├── SDP4430
│       ├── tegraalc5632
│       ├── tegra-rt5640
│       └── Tuna
├── alsa-base
...snip...

Now if you take a look at the options for tree, there already is an option to output XML!  Try "tree --help" at the commandline, and you'll see something like this:

  ------- Graphics options ------
  -i            Don't print indentation lines.
  -A            Print ANSI lines graphic indentation lines.
  -S            Print with ASCII graphics indentation lines.
  -n            Turn colorization off always (-C overrides).
  -C            Turn colorization on always.
  ------- XML/HTML options -------
  -X            Prints out an XML representation of the tree.
  -H baseHREF   Prints out HTML format with baseHREF as top directory.
  -T string     Replace the default HTML title and H1 header with string.
  --nolinks     Turn off hyperlinks in HTML output.
  ---- Miscellaneous options ----
  --version     Print version and exit.
  --help        Print usage and this help message and exit.

So, awesome, shall we see what this option does?

wskellenger@marquette ~/cm11 $ tree -d -X | more
<?xml version="1.0" encoding="UTF-8"?>
<tree>
  <directory name=".">
    <directory name="abi">
      <directory name="cpp">
        <directory name="include">
        </directory>
        <directory name="src">
        </directory>
      </directory>
    </directory>
    <directory name="android">
    </directory>
    <directory name="art">
      <directory name="build">
      </directory>
      <directory name="compiler">
        <directory name="dex">
          <directory name="portable">
          </directory>
          <directory name="quick">
            <directory name="arm">
            </directory>
            <directory name="mips">
            </directory>
            <directory name="x86">
            </directory>
          </directory>
        </directory>
        <directory name="driver">
        </directory>
        <directory name="jni">
          <directory name="portable">
...snip...

And here is what a FreeMind file looks like:

<map version="0.9.0">
<!-- To view this file, download free mind mapping software FreeMind from http://freemind.sourceforge.net -->
<node CREATED="1410111246409" ID="ID_718284210" MODIFIED="1410122249321" TEXT="Android build env">
<node CREATED="1410122250455" HGAP="11" ID="ID_1487627158" MODIFIED="1410122267863" POSITION="right" TEXT="cm11" VSHIFT="-41">
<node CREATED="1410111257071" ID="ID_181927416" MODIFIED="1410111257071" TEXT="abi"/>
<node CREATED="1410111257072" ID="ID_1980677560" MODIFIED="1410111257072" TEXT="android"/>
<node CREATED="1410111257072" ID="ID_584603213" MODIFIED="1410111257072" TEXT="art"/>
<node CREATED="1410111257073" ID="ID_821356947" MODIFIED="1410111257073" TEXT="bionic"/>
<node CREATED="1410111257073" ID="ID_1847225096" MODIFIED="1410111257073" TEXT="bootable"/>
<node CREATED="1410111257074" ID="ID_777789702" MODIFIED="1410111257074" TEXT="build"/>
<node CREATED="1410111257074" ID="ID_1798981557" MODIFIED="1410111257074" TEXT="cts"/>
<node CREATED="1410111257075" ID="ID_1299267080" MODIFIED="1410111257075" TEXT="dalvik"/>
<node CREATED="1410111257076" ID="ID_283530905" MODIFIED="1410111257076" TEXT="developers"/>
<node CREATED="1410111257077" ID="ID_1162422382" MODIFIED="1410111257077" TEXT="development"/>
<node CREATED="1410111257078" ID="ID_772625792" MODIFIED="1410111257078" TEXT="device">
<node CREATED="1410122307224" FOLDED="true" ID="ID_981437287" MODIFIED="1410122649648" TEXT="common">
<node CREATED="1410122647210" ID="ID_1480883631" MODIFIED="1410122647823" TEXT="gps"/>
</node>
...snip...

To dump the output of "tree" we just do a simple redirect of the output:

wskellenger@marquette ~/cm11 $ tree -d -X > out.xml

Now we just need to make some simple substitutions in the "out.xml" file from "tree" to make it compatible with FreeMind.  This is really simple with Vim.

It looks like we need to do the following:

  1. Rename the "directory" elements to "node".
  2. Add the "CREATED" attribute with today's date.
  3. Add the "ID" attribute (this appears to hold a random number).
  4. Add the "MODIFIED" attribute, which also contains a date.
  5. Rename the "name" attribute to "TEXT"
  6. Get rid of the <?xml> element at the top.
  7. Replace the <tree> tag with <map version="0.9.0"> (use version="1.0.1" for the newer version of FreeMind).
  8. Replace </tree> at the bottom of the file with </map>
  9. Bonus hint: If you have a huge directory structure, like the Cyanogenmod file tree, you might want to add another attribute, FOLDED="true" to each "node" element.

This is really a search/replace problem.  If you don't know regular expressions, you should learn them, because this problem becomes pretty easy, except for the random number part, but even that can be solved with Vim.

So generally we will do some searching and replacing with Vim:

:%s/<directory/<node/gc
:%s/directory>/node>/gc
:%s/name=/TEXT=/gc
:%! perl -pne '$random=int(rand 100000000); s/<node/<node CREATED="1410111247071" ID="ID_$random" MODIFIED="1410111247072" /gc'
:%s/ID=/FOLDED="true" ID=/gc

I manually removed the <tree> tags and added the <map> tags.  I also manually removed the <?xml> tag at the top.  

The tricky part is in yellow: generating the random numbers.  I found the solution for that at StackOverflow.  In that line, we basically callupon perl to generate a random integer, and stuff it into a variable "$random".  After that, perl creates a vim-like search/replace string, searching for <node and updating it as shown.  Vim executes this for each and every line and performs the search/replace.  Slick.

After this, save the file and open it with FreeMind.

WARNING:  FreeMind 0.9.0 is really terrible about handling large maps.  With a huge directory tree (like what is in Cyanogenmod), you may open a few nodes and have it crash on you.  FreePlane seems to behave better with large maps.  Here is the result opened in FreePlane:


WARNING2: After you save the file in FreePlane, you probably won't be able to open it in FreeMind.  So save it as a new filename.

UPDATE: FreeMind 1.0.1 is much better at handling large maps.





4 comments:

  1. You can run:

    tree -d -X | sed 's/directory/node/g'|sed 's/file/node/g'|sed "s/name/TEXT/g"|sed 's/ out.xml

    to do most of the replacement with a oneliner. However you still need to use vi for the perl/random bit :)

    cheers
    Enrico Glerean

    ReplyDelete
    Replies
    1. oops blogspot filters out the greaterthan/lowerthen symbols :) But you got the idea...

      Delete
  2. I used the following: https://gist.github.com/karlarao/c14413ba48e84f4de4dac84a297da1f6

    ReplyDelete