If you're interested in functional programming, you might also want to checkout my second blog which i'm actively working on!!

Tuesday, December 17, 2013

Even Guava doesn't make Java appealing anymore once you tasted Scala

I just hacked some small test case together to give an impression of how one could use Guava. Still I don't like the verbosity... sigh :(

Monday, October 14, 2013

Modeling a poker game in Scala

WORK IN PROGRESS. If I find the time ideally i'd like to develop a PLAY2.x app using AKKA actors to orchestrate the poker game. Would be a very nice exercise. :)

Saturday, October 12, 2013

Tuesday, October 8, 2013

A taste of programming in ML

Taking a new course Proglang which started this week. So expect more to follow on what I learn.

Thursday, September 12, 2013

Spring batch demo

In this article I will show you how easy it is to create a batch job by configuring out-of-the-box components. Today we will read a [CSV file] and convert it to an [XML file] and while we are at it we filter out all persons who don't live in New York. I also stick with using annotations to configure my complete application context. Unfortunately this code currently runs into a nullpointer which I expect to be [this issue].

Tuesday, September 10, 2013

Unit testing with Mockito

In this article I created a realistic scenario of services depending on each other. This article shows you how you can easily test business logic in isolation.

Wednesday, July 24, 2013

Using self-type (Scala)

Some examples of working with case classes

Finding second highest frequency of characters in String

Write a method that accepts a String and returns the character with the second highest frequency. For example "aabbbc" would return 'a'. If the string contains spaces, they can be disregarded from the count. Note: my implementation returns a tuple (char, count) but that is just a matter of only returning the char itself.

Tuesday, July 23, 2013

Creating immutable datastructures in Scala

I decided to take a shot at creating a simplified version of an immutable list structure and this is the result.

Tuesday, July 9, 2013

constructing simple pipelines in Scala

Playing a bit with constructing pipelines logically in Scala. A pipeline MUST start with a generator, folllowed by 0 to many transformers and MUST end with a serializer.

Monday, April 15, 2013

Using Bootstrap with Play 2

The easiest way to get bootstrap working with Play 2 is not to separate the bootstrap files in the corresponding stylesheets, javascripts and images folders but just drop the sources in like shown below.

Loading ....

Thursday, March 14, 2013

Finding duplicate values with XQuery

As I'm always interested in benchmarks and optimized solutions I compared 3 strategies of finding duplicate values in a big sequence. This test will use 5000 randomly generated numbers and compare performance.
The first thing I needed to do was creating a sequence of 5000 randomly generated numbers. Scala comes to the rescue
Loading ....

This generates a file with following content. Remark: in reality that sequence contains 5000 numbers.
let $values := (1012,5345,2891,3833,2854, 2236)

Now I ran the following XQueries on Zorba to get a feeling about how fast they are.

XQuery 1: (takes about 5 seconds for 5000 numbers)
let $values := (1012,5345,2891,3833,2854, 2236)
let $distinctvalues := distinct-values($values)
let $nonunique := for $value in $distinctvalues return if (count(index-of($values, $value)) > 1) then $value else ()
return $nonunique

XQuery 2: (takes about 5 seconds for 5000 numbers)
let $values := (1012,5345,2891,3833,2854, 2236)
return $values[index-of($values, .)[2]]

XQuery 3: (takes about 1 seconds for 5000 numbers)
let $values := (1012,5345,2891,3833,2854, 2236)

return distinct-values(for $value in $values
  return if (count($values[. eq $value]) > 1)
         then $value
         else ())

Of course I got intrigued how Sedna would perform. I only tried XQuery1 (3 to 4 seconds) and XQuery3 (around 13 seconds) on my local machine, which only shows that you cannot always take the same approach while trying to optimize.

Tuesday, February 26, 2013

Debugging circular references in Maps

Today I learned another useful feature to debug circular references which can cause stackoverflows when not handled with caution. The below render method takes a parameter of type Map. In some cases the value is again a HashMap. Invoking entry.getValue() implicitly invokes the toString method in the Log.debug. When there are any circular references, you will get a nice stackoverflow.

    public String render(final String template,
            final Map<String, Object> parameters)
            throws IOException {

        final ST stringTemplate = new ST(template, '$', '$');

        if (parameters == null || parameters.isEmpty()) {
            LOG.warn("There are not any parameters passed to the template.");
        } else {
            for (Map.Entry<String, Object> entry : parameters.entrySet()) {
                stringTemplate.add(entry.getKey().replace(".", "_"),
                        (entry.getValue() instanceof String)
                        ? StringEscapeUtils.escapeXml(
                        entry.getValue().toString())
                        : entry.getValue());

                LOG.debug("Passing pipeline parameter as attribute: key={}"
                        + ", value={}", entry.getKey(), entry.getValue());
            }
        }

        return stringTemplate.render();
   

So you could e.g. write your own routine to find a clue where the problem is located but by following the Cocoon mailinglist I learned from Francesco that Commons Collections has a nice method to print a Map's content verbose. So I decided to experiment myself a bit to see how quickly I would find the issue using this approach and modified the code a bit.
for (Map.Entry<String, Object> entry : parameters.entrySet()) {
    stringTemplate.add(entry.getKey().replace(".", "_"),
        (entry.getValue() instanceof String)
         ? StringEscapeUtils.escapeXml(
         entry.getValue().toString())
         : entry.getValue());

     if (entry.getKey() == "cocoon")   MapUtils.verbosePrint(System.out, "cocoon", (Map) entry.getValue());
}

Now the output that got logged is listed below. I indented it a bit more properly but we can quickly see that "cocoon" has a "controller" which points back to "cocoon". --> (ancestor[0] Map)
cocoon = 
{
  response = org.apache.cocoon.servletservice.HttpServletResponseBufferingWrapper@12b13004
  settings = Settings:
      running mode : dev
      org.apache.cocoon.reload-delay : 1000
      org.apache.cocoon.reloading : false
      org.apache.cocoon.classloader.load.classes : 
      org.apache.cocoon.cache.directory : C:\workspaces\..\target\work\cache-dir
      org.apache.cocoon.work.directory : C:\workspaces\..\target\work
      org.apache.cocoon.formencoding : null
      org.apache.cocoon.containerencoding : UTF-8
  request = org.apache.cocoon.servlet.util.ObjectModelProvider$ObjectModelRequest@77d67cee
  context = org.apache.cocoon.servletservice.ServletServiceContext@7d91ec17
  controller = 
  {
    baseUrl = file:/C:/workspaces/apache/../src/main/resources/COB-INF/
    id = abc
    javax.servlet.http.HttpServletResponse = org.apache.cocoon.servletservice.HttpServletResponseBufferingWrapper@12b13004
    testProperty = test
    source = file:/C:/workspaces/apache/cocoon/cocoon3/trunk/cocoon-sample/src/main/resources/COB-INF/controller/demo.html
    javax.servlet.ServletContext = org.apache.cocoon.servletservice.ServletServiceContext@7d91ec17
    javax.servlet.http.HttpServletRequest = org.apache.cocoon.servletservice.util.ServletServiceRequest@5513fab7
    org.apache.cocoon.configuration.Settings = Settings:
        Running mode : dev
        org.apache.cocoon.reload-delay : 1000
        org.apache.cocoon.reloading : false
        org.apache.cocoon.classloader.load.classes : 
        org.apache.cocoon.cache.directory : C:\workspaces\..\target\work\cache-dir
        org.apache.cocoon.work.directory : C:\workspaces\..\target\work
        org.apache.cocoon.formencoding : null
        org.apache.cocoon.containerencoding : UTF-8
    name = foo
    cocoon = (ancestor[0] Map)
    reqparam = 1
  }
}


Sunday, February 24, 2013

Constraints and Triggers - RDBMS

This article is meant as a quick lookup of constraints and trigger syntax. Besides most RDBMS's don't even implement the full SQL standard. Sqlite should be able to run below statements.

---------------------------------------------------------------------------
-----------------------------  CONSTRAINTS---------------------------------
---------------------------------------------------------------------------

***************************************************************************
create table Apply(sID int, cName text, major text, decision text, 
check(decision = 'N' or cName <> 'Stanford' or major <> 'CS'));


insert into Apply values(123, 'Stanford', 'CS', 'N');
insert into Apply values(123, 'MIT', 'CS', 'Y');
insert into Apply values(123, 'Stanford', 'CS', 'Y');

***************************************************************************
create table Student(sID int, sName text, GPA real, sizeHS int);

/* check constraints are not supported currently and neither are subqueries in constraints */
create table Apply(sID int, cName text, major text, decision text, 
  check(sID in (select sID from Student))); 
 
/* But this is an example of referential integrity */
  
  
***************************************************************************
/* Using assertions -- Currently not supported by RDBMS's */

create assertion Key
check ((select count(distinct A) from T) = (select count(*) from T)));

create assertion ReferentialIntegrity
check (not exists (select * from Apply where sID not in (select sID from Student)));

check assertion AvgAccept
check (3.0 < (select avg(GPA) from Student where sID in (select sID from Apply where decision = 'Y')));


***************************************************************************
create table College(cName text primary key, state text, enrollment int);
create table Student(sID int primary key, sName text, GPA real, sizeHS int);
create table Apply(sID int references Student(sID), cName text references College(cName), major text, decision text);

//using cascading update
create table Apply(sID int references Student(sID) on delete set null, 
                   cName text references College(cName) on update cascade, 
                   major text, decision text);
***************************************************************************


---------------------------------------------------------------------------
-----------------------------  TRIGGERS  ----------------------------------
---------------------------------------------------------------------------
***************************************************************************
create trigger R1
after insert on Student
for each row
when New.GPA > 3.3 and New.GPA <= 3.6
begin
  insert into Apply values (New.sID, 'Stanford', 'geology', null);
  insert into Apply values (New.sID, 'MIT', 'biology', null);
end;  
***************************************************************************
create trigger R2
after delete on Student
for each row 
begin
  delete from Apply where sID = Old.sID;
end;
***************************************************************************
create trigger R3
after update of cName on College
for each row
begin
  update Apply
  set cName = New.cName
  where cName = Old.cName;
end;
***************************************************************************
create trigger R4
before insert on College
for each row
when exists (select * from College where cName = New.cName)
begin
  select raise(ignore);
end;
***************************************************************************
create trigger R5
before update of cName on College
for each row 
when exists (select * from College where cName = New.cName)
begin
  select raise(ignore);
end;
***************************************************************************
create trigger R6
after insert on Apply
for each row
when (select count(*) from Apply where cName  = New.cName) > 10
begin
  update College set cName = cName || '-Done'
  where cName = New.cName;
end;  

***************************************************************************
create trigger R7
before insert on Student
for each row 
when New.sizeHS < 100 or New.sizeHS > 5000
begin
  select raise(ignore);
end;
***************************************************************************
create trigger AutoAccept
after insert on Apply
for each row 
when (New.cName = 'Berkeley' and
      3.7 < (select GPA from Student where sID = New.sID) and
      1200 < (select sizeHS from Student where sID = New.sID))
begin
  update Apply
  set decision = 'Y'
  where sID = New.sID
  and cName = New.cName;
end;
***************************************************************************
create trigger TooMany
after update of enrollment on College
for each row
when (Old.enrollment <= 16000 and New.enrollment > 16000)
begin
  delete from Apply where cName = New.cName and major = 'EE';
  update Apply
  set decision = 'U'
  where cName = New.cName
  and decision = 'Y';
end;
     

Thursday, February 21, 2013

Using sqlite with Firefox sqlite-manager extension

The Stanford database course is using sqlite to experiment with SQL, Constraints, Triggers and so on. As the downloadable executable from this site only has a command line interface I decided to search for a GUI manager for sqlite. I am currently trying the firefox add-on which seems to work just fine.

/* First we create a table with a tuple constraint */
create table Apply(sID int, cName text, major text, decision text, 
check(decision = 'N' or cName <> 'Stanford' or major <> 'CS'));

/* next we try to insert a few tuples but the 3rd one should throw a constraint violation exception */
insert into Apply values(123, 'Stanford', 'CS', 'N');
insert into Apply values(123, 'MIT', 'CS', 'Y');
insert into Apply values(123, 'Stanford', 'CS', 'Y');

Friday, February 15, 2013

Merging CSV and XML data with fast performance

This blogpost will show a simple demo of how you can merge XML data with additional data coming from a Comma Separated File. Again I just made up some testdata for demo purpose. But just to give some numbers, my real use case merged data from a 242MB XML file and a CSV file in less than 5 seconds using of course Saxon.
nxp10009@NXL01366 /c/xsltdemo
$ ls -la
total 4786
drwxr-xr-x    6 nxp10009 Administ        0 Feb 15 16:35 .
drwxr-xr-x    1 nxp10009 Administ    12288 Feb 15 16:19 ..
-rw-r--r--    1 nxp10009 Administ  9788993 Oct 30 13:42 Saxon-HE-9.4.jar
drwxr-xr-x    4 nxp10009 Administ        0 Feb 15 16:34 input
drwxr-xr-x    4 nxp10009 Administ        0 Feb 15 17:18 output
drwxr-xr-x    4 nxp10009 Administ        0 Feb 15 17:03 xslt


nxp10009@NXL01366 /c/xsltdemo/input
$ ls -la
total 1
drwxr-xr-x    4 nxp10009 Administ        0 Feb 15 16:34 .
drwxr-xr-x    6 nxp10009 Administ        0 Feb 15 16:35 ..
-rw-r--r--    1 nxp10009 Administ       47 Feb 15 16:46 studentinfo.csv
-rw-r--r--    1 nxp10009 Administ      576 Feb 15 17:13 students.xml

studentinfo.csv
1, m, developer
2, m, developer
3, f, model

students.xml
<?xml version="1.0" encoding="UTF-8" ?>
<students>
  <student>
    <firstname>Robby</firstname>
    <lastname>Pelssers</lastname>
    <dateofbirth>1977-02-07</dateofbirth>
    <studentid>1</studentid>
  </student>
  <student>
    <firstname>Ivan</firstname>
    <lastname>Lagunov</lastname>
    <dateofbirth>1987-04-30</dateofbirth>
    <studentid>2</studentid>
  </student>
  <student>
    <firstname>Pamela</firstname>
    <lastname>Anderson</lastname>
    <dateofbirth>1967-07-01</dateofbirth>
    <studentid>3</studentid>
  </student>  
</students>

So the first thing I did was creating an XML representation of that CSV file using below xslt.
studentinfo.xslt
<?xml version="1.0" encoding="UTF-8"?>
<!--
Author: Robby Pelssers

Transforms studentinfo.csv into XML representation

Usage from DOS-Shell:
java -jar Saxon-HE-9.4.jar -o:C:/xsltdemo/output/studentinfo.xml -it:main -xsl:C:/xsltdemo/xslt/studentinfo.xslt studentinfoCSV=file:/C:/xsltdemo/input/studentinfo.csv

-->

<xsl:stylesheet version="2.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  
  <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
  <xsl:param name="studentinfoCSV" />
  

  <xsl:variable name="linefeed" select="'\r?\n'"/>
  <xsl:variable name="csv" select="unparsed-text($studentinfoCSV)"/>  

  
  <xsl:template match="/" name="main">
    <info>
      <xsl:for-each select="tokenize($csv, $linefeed)">
        <xsl:variable name="fields" select="tokenize(., ',')"/>
        <studentinfo id="{normalize-space($fields[1])}">
          <gender><xsl:sequence select="normalize-space($fields[2])"/></gender>
          <profession><xsl:sequence select="normalize-space($fields[3])"/></profession>
        </studentinfo>  
      </xsl:for-each>
    </info>
  </xsl:template>

</xsl:stylesheet>

So after the transformation a new file studentinfo.xml gets generated as below.
<?xml version="1.0" encoding="UTF-8"?>
<info>
   <studentinfo id="1">
      <gender>m</gender>
      <profession>developer</profession>
   </studentinfo>
   <studentinfo id="2">
      <gender>m</gender>
      <profession>developer</profession>
   </studentinfo>
   <studentinfo id="3">
      <gender>f</gender>
      <profession>model</profession>
   </studentinfo>
</info>

So now we need to execute a second transform using as input students.xml and studentinfo.xml.
student_addinfo.xslt
<?xml version="1.0" encoding="UTF-8"?>
<!--
Author: Robby Pelssers

java -Xmx1024m -jar Saxon-HE-9.4.jar -s:C:/xsltdemo/input/students.xml -o:C:/xsltdemo/output/students-full.xml -xsl:C:/xsltdemo/xslt/student_addinfo.xslt studentinfoXML=file:/C:/xsltdemo/output/studentinfo.xml
-->

<xsl:stylesheet version="2.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
  <xsl:param name="studentinfoXML" />

  <xsl:variable name="studentinfoDocument" select="document($studentinfoXML)"/>

  <xsl:key name="student-lookup" match="studentinfo" use="@id"/>

  <xsl:template match="/">
    <xsl:apply-templates/>
  </xsl:template>

  <xsl:template match="student">
    <student>
      <!-- we copy all present attributes and children -->
      <xsl:apply-templates select="@* | node()"/>
      <!-- now we also want to add the additional information as children --> 
      <xsl:apply-templates select="key('student-lookup', studentid, $studentinfoDocument)/*"/>
    </student>
  </xsl:template>
 
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*"/>
      <xsl:apply-templates/>
    </xsl:copy>
  </xsl:template> 


</xsl:stylesheet>


And finally we get the merged result:
<?xml version="1.0" encoding="UTF-8"?>
<students>
  <student>
      <firstname>Robby</firstname>
      <lastname>Pelssers</lastname>
      <dateofbirth>1977-02-07</dateofbirth>
      <studentid>1</studentid>
      <gender>m</gender>
      <profession>developer</profession>
   </student>
  <student>
      <firstname>Ivan</firstname>
      <lastname>Lagunov</lastname>
      <dateofbirth>1987-04-30</dateofbirth>
      <studentid>2</studentid>
      <gender>m</gender>
      <profession>developer</profession>
   </student>
  <student>
      <firstname>Pamela</firstname>
      <lastname>Anderson</lastname>
      <dateofbirth>1967-07-01</dateofbirth>
      <studentid>3</studentid>
      <gender>f</gender>
      <profession>model</profession>
   </student>  
</students>

Tuesday, January 22, 2013

XQuery demo using Zorba for Stanford DBClass

I decided to build a more appealing demo showing the potential of XQuery for building webpages. In this demo I render a table showing countries with a population greater than 100 million and which have languages listed. The languages are listed as pie charts using the Google Chart API.

XQuery used on countries XML data.
import module namespace http = "http://expath.org/ns/http-client";

declare %an:sequential function local:doc($href as xs:string) {
  http:send-request(<http:request href="{$href}" method="GET" />)[2] 
};


let $doc := local:doc("http://prod-c2g.s3.amazonaws.com/db/Winter2013/files/countries.xml")
let $bigCountries := $doc/countries/country[@population > 100000000 and exists(language)]

return
<html>
  <head>
    <title>Visualization of languages for countries with population greater than 100 million</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> </meta>
    <style type="text/css">
      table {{
        border-collapse: collapse;
      }}
      caption {{
        background-color: #ADAEAF;
        text-align: left;
      }}
      .header {{
        background-color: #E6F1F3;
      }}
      td, th {{
        border: 1px dotted #B3D4DB;
        padding: 2px;
        vertical-align: top;
        text-align: left;
      }}
    </style>
    <script type="text/javascript" src="https://www.google.com/jsapi"> </script>
    <script type="text/javascript">

      // Load the Visualization API and the piechart package.
      google.load('visualization', '1.0', {{'packages':['corechart']}});
  
      // Set a callback to run when the Google Visualization API is loaded.
      google.setOnLoadCallback(drawCharts);

      function drawChart(percentages, countryName) {{
        // Create the data table.
        var data = new google.visualization.DataTable();
        data.addColumn('string', 'Country');
        data.addColumn('number', 'percentage');
        data.addRows(percentages);

        // Set chart options
        var options = {{'title':'Languages',
                       'width':400,
                       'height':300}};

        // Instantiate and draw our chart, passing in some options.
        var chart = new google.visualization.PieChart(document.getElementById(countryName));
        chart.draw(data, options);
      }}
   
      function drawCharts() {{
        {
           for $country in $bigCountries
           let $data := string-join(for $lang in $country/language return concat('["', data($lang), '",', data($lang/@percentage), ']'), ',')
           return concat('drawChart([', $data, '],  "', data($country/@name),'");
')
        }
      }}  
    </script> 
  </head>
  <body>
    <table>
      <caption>Countries with population greater than 100 million</caption>
      <thead>
        <tr class="header">
          <th>Name</th>
          <th>Population</th>
          <th>Area</th>
          <th>Languages</th>
        </tr>
      </thead>
      <tbody>
        {
           for $country in $bigCountries
           order by count($country/language) descending
           return
             <tr>
               <td>{data($country/@name)}</td>
               <td>{data($country/@population)}</td>
               <td>{data($country/@area)}</td>
               <td><div id="{data($country/@name)}"></div></td>
             </tr>
        }
      </tbody>
    </table>
  </body>
</html>

Result from executing XQuery on Zorba
<html>
  <head>
    <title>Visualization of languages for countries with population greater than 100 million</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <style type="text/css">
      table {
        border-collapse: collapse;
      }
      caption {
        background-color: #ADAEAF;
        text-align: left;
      }
      .header {
        background-color: #E6F1F3;
      }
      td, th {
        border: 1px dotted #B3D4DB;
        padding: 2px;
        vertical-align: top;
        text-align: left;
      }
    </style>
    <script type="text/javascript" src="https://www.google.com/jsapi"> </script>
    <script type="text/javascript">

      // Load the Visualization API and the piechart package.
      google.load('visualization', '1.0', {'packages':['corechart']});
  
      // Set a callback to run when the Google Visualization API is loaded.
      google.setOnLoadCallback(drawCharts);

      function drawChart(percentages, countryName) {
        // Create the data table.
        var data = new google.visualization.DataTable();
        data.addColumn('string', 'Country');
        data.addColumn('number', 'percentage');
        data.addRows(percentages);

        // Set chart options
        var options = {'title':'Languages',
                       'width':400,
                       'height':300};

        // Instantiate and draw our chart, passing in some options.
        var chart = new google.visualization.PieChart(document.getElementById(countryName));
        chart.draw(data, options);
      }
   
      function drawCharts() {
        drawChart([["Hindi",30]],  "India");
        drawChart([["Japanese",100]],  "Japan");
        drawChart([["Pashtu",8],["Urdu",8],["Punjabi",48],["Sindhi",12],["Balochi",3],["Hindko",2],["Brahui",1],["Siraiki",10]],  "Pakistan");
        drawChart([["Russian",100]],  "Russia");
      }  
    </script>
  </head>
  <body>
    <table>
      <caption>Countries with population greater than 100 million</caption>
      <thead>
        <tr class="header">
          <th>Name</th>
          <th>Population</th>
          <th>Area</th>
          <th>Languages</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>Pakistan</td>
          <td>129275664</td>
          <td>803940</td>
          <td>
            <div id="Pakistan"/>
          </td>
        </tr>
        <tr>
          <td>India</td>
          <td>952107712</td>
          <td>3287590</td>
          <td>
            <div id="India"/>
          </td>
        </tr>
        <tr>
          <td>Japan</td>
          <td>125449704</td>
          <td>377835</td>
          <td>
            <div id="Japan"/>
          </td>
        </tr>
        <tr>
          <td>Russia</td>
          <td>148178480</td>
          <td>17075200</td>
          <td>
            <div id="Russia"/>
          </td>
        </tr>
      </tbody>
    </table>
  </body>
</html>

So what does this page look like in a browser?

Friday, January 18, 2013

Generating HTML and Excel with Xquery

I just wanted to share a small sample how you can generate a table in both HTML and Excel with one XQuery. Just in case the sample gets lost I will still share the code in this article as well.
A few remarks. You can save the result to a file test.html and test.xls. For the excel file you will need to remove the xml declaration (first line).
let $orders :=
<orders>
  <order id="1231">
    <product>Samsung Galaxy S3</product>
    <price valuta="EURO">467</price>
    <quantity>3</quantity>
  </order>
  <order id="1232">
    <product>iPhone 5</product>
    <price valuta="EURO">689</price>
    <quantity>5</quantity>
  </order>
  <order id="1233">
    <product>LG E610 Optimus L5</product>
    <price valuta="EURO">140</price>
    <quantity>2</quantity>
  </order>   
</orders>

return
<html xmlns:x="urn:schemas-microsoft-com:office:excel">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> </meta>
    <meta http-equiv="Content-Type" content="application/vnd.ms-excel; charset=utf-8"> </meta>
    <style type="text/css">
      table {{
        border-collapse: collapse;
      }}
      .header {{
        background-color: #E6F1F3;
        text-align: left;
      }}
      td, th {{
        border: 1px dotted #B3D4DB;
        padding: 2px;
      }}
      .number {{
        text-align: right;
      }}
      .subtotal {{
        background-color: #E3E4E5; 
        text-align: right;
        font-weight: bold;
      }}
      .total {{
        background-color: #ADAEAF;
        text-align: right;
        font-weight: bold;        
      }}
    </style>
  </head>
  <body>
    <table>
      <colgroup>
        <col style="width: 100px;"/>
        <col style="width: 200px;"/>
        <col style="width: 100px;"/>
        <col style="width: 100px;"/>
      </colgroup>          
      <thead>
        <tr>
          <th class="header">Order No</th>
          <th class="header">Product</th>
          <th class="header">Price</th>
          <th class="header">Quantity</th>
        </tr>
      </thead>
      <tbody>
      {
        for $order in $orders/order
        let $price := data($order/price)
        let $quantity := data($order/quantity)
        let $subtotal := $price * $quantity
        return
        (
        <tr>
          <td>{data($order/@id)}</td>
          <td>{data($order/product)}</td>
          <td class="number">{$price}</td>
          <td class="number">{$quantity}</td>
        </tr>,
        <tr>
          <td class="subtotal"/>
          <td class="subtotal"/>
          <td class="subtotal">Subtotal</td>
          <td class="subtotal">{$subtotal}</td>
        </tr>
        )
      }
        <tr>
          <td class="total"/>
          <td class="total"/>
          <td class="total">Total</td>
          <td class="total">{sum(for $order in $orders/order return data($order/price) * data($order/quantity))}</td>
        </tr>
      </tbody>
    </table>
  </body>
</html>  

You will get a warning from Excel but I already looked into this and there is no workaround available at this moment. I also noticed that when opening such a file from Internet Explorer it seems to hang the first time. A workaround it to Save the file the first time and open it. From that moment on Internet Explorer will be able to open the files correctly.




Friday, January 11, 2013

Rendering tweets with Play2.1


Tweet Controller
package controllers

import play.api.mvc.{Action, Controller}
import play.api.libs.functional.syntax._

import play.api.libs.json._
import play.api.libs.concurrent.Execution.Implicits.defaultContext
import play.api.libs.ws.WS

case class Tweet(from: String, text: String)

object Tweets extends Controller {

  implicit val tweetReads = (
    (__ \ "from_user_name").read[String] and
    (__ \ "text").read[String]
    )(Tweet)


  def tweetList(query: String) = Action {
    Async {
      val results = 10
      val responsePromise =
        WS.url("http://search.twitter.com/search.json")
          .withQueryString("q" -> query, "rpp" -> results.toString).get
      responsePromise.map {
        response =>
          val tweets = Json.parse(response.body).\("results").as[Seq[Tweet]]
          Ok(views.html.tweetlist(tweets))
      }
    }
  }
}

routes
GET     /tweets/:query              controllers.Tweets.tweetList(query: String)

scala template:
@(tweets: Seq[Tweet])
@main("Tweets!") {
    

Tweets:

@tweets.map { tweet =>
  • @tweet.from: @tweet.text
} }

So invoking following URL http://localhost:9000/tweets/playframework now gives me the last 10 tweets containing playframework (case insensitive). I think it's pretty slick how little code is needed.