Thursday, September 16, 2010

Apache Ant - How to search a and replace regular expressions inside a text

As reported by documentation, Ant provides a task, <ReplaceRegExp>, "for replacing the occurrence of a regular expression with a substitution pattern in a selected file or set of files". Now the question is: do Ant also provide a task for doing the same on texts? Well... the answer is no.

Anyway, this doesn't mean that it's not possible to achieve the same effect on texts. Actually there are at least two ways: the first is to use AntContrib's <PropertyRegex> task. The second (that works only starting from Ant 1.7.1) is to combine some standard Ant task to do the same: just read on.

Let's start from the <concat> task. The documentation says: "Since Ant 1.7.1, this task can be used as a Resource Collection that will return exactly one resource". Moreover "since Ant 1.6 it supports nested FilterChains". This is important because FilterChains are formed also by TokenFilters and a TokenFilter can be a ReplaceRegex string filter. Confused? Take a look at the following code:
<macrodef name="replaceStringWithRegExp">
<attribute name="string"/>
<attribute name="searchPattern"/>
<attribute name="replacementPattern"/>
<attribute name="property"/>
<sequential>
<tokens id="id">
<concat>
<string value="@{string}"/>
<filterchain>
<tokenfilter>
<replaceregex pattern="@{searchPattern}"
replace="@{replacementPattern}"
flags="g"/>
</tokenfilter>
</filterchain>
</concat>
</tokens>
<property name="@{property}" value="${toString:id}"/>
</sequential>
</macrodef>
Here I've defined a macro named "replaceStringWithRegExp". It takes four input parameters:
  1. string: text to match against a regular expression
  2. searchPattern: regular expression
  3. replacementPattern: substitution pattern
  4. property: name for a new property that will contain the result of the replacement
The input string is treated as a String resource (see <string>), filtered using a FilterChain (see <filterchain>) that's made of a TokenFilter (see <tokenfilter>). The TokenFilter is a ReplaceRegex string filter (see <replaceregex>).

To extract the result of <concat> task, I've wrapped this task around a <tokens> task and I've assigned an id to the external task. Finally, using the expression ${toString:id} it's possible to extract the toString value from the <tokens> task: this is the result of the search and replacement. Pretty tricky, isn't it?

Now let's try it.
 <replaceStringWithRegExp string="James Bond" 
searchPattern="(\w+)\s+(\w+)"
replacementPattern="My name is \2, \1 \2"
property="result"/>
<echo message="${result}"/>
Here we get: "My name is Bond, James Bond". Well, it works.

Wednesday, September 15, 2010

Apache Ant - How to find empty directories

To find out empty directories just using standard Ant tasks is not a trivial job. Here I'll show you how this can be accomplished applying a technique that involves an accurate use of <copy> task and <present> selector.

Let's take a look at the following script excerpt:
<property name="dirToCheck" value="c:/temp"/>

<copy todir="${java.io.tmpdir}/_copy_" includeEmptyDirs="false">
<fileset dir="${dirToCheck}"/>
</copy>

<dirset dir="${dirToCheck}" id="emptyDirs">
<present present="srconly" targetdir="${java.io.tmpdir}/_copy_"/>
</dirset>
In the previous code we want to check for empty directories starting from a base directory identified by property named "dirToCheck".

The idea is to use the <copy> task to clone the directory structure into a temp dir (${java.io.tmpdir}/_copy_) without including empty directories (includeEmptyDirs="false"). This way we get a clone of the original directory structure without its empty directories.

Empty directories can now appear from the comparison between the original directory structure and the cloned one: they simply emerge as those directories that are present only in the original structure, as the <present> selector shows.

Tuesday, August 10, 2010

How to get Huawei E1750 USB modem working on Ubuntu Lucid

Some times ago I installed Ubuntu 10.4 LTS (aka Lucid) on my laptop. At that point I wasn't sure whether my Huawei E1750 USB modem were recognized or not by the new OS, so I plugged it into a USB port. The result was the USB modem was recognized as a mere USB drive, so no internet connection could be established.
In order to solve this problem, I discovered that I needed to install a package named "usb-modeswitch".
As the documentation says, several new USB devices have their proprietary Windows drivers onboard, especially WAN dongles. When plugged in for the first time, they act like a flash storage and start installing the driver from there. If the driver is already installed, the storage device vanishes and a new device, such as an USB modem, shows up. This is called the "ZeroCD" feature.
On Debian, this is not needed, since the driver is included as a Linux kernel module, such as "usbserial". However, the device still shows up as "usb-storage" by default. usb-modeswitch solves that issue by sending the command which actually performs the switching of the device from "usb-storage" to "usbserial".
Also I discovered that before installing "usb-modeswitch" the package "usb-modeswitch-data" must be installed.
I then downloaded these two packages using a Windows machine with a working internet connection. The URL I used were:
Using a USB storage key I moved the downloaded packages on the Ubuntu machine.
Then I installed them using the following terminal commands:
    sudo dpkg -i usb-modeswitch-data.deb
    sudo dpkg -i usb-modeswitch.deb
I restarted Ubuntu, connected the USB modem and now it was perfectly recognized. As a proof for this I noticed that Ubuntu's network menu (located top right) showed a new item, Mobile Broadband, with the option to connect to a mobile network.
Please note that this solution is working not only for Huawei E1750 USB modem but also for many other devices, as specified here.