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 :(
Labels:
guava
Saturday, October 26, 2013
Thursday, October 24, 2013
Saturday, October 19, 2013
A few examples of how lexical scope works (ML)
A function body is evaluated in the environment where the function was defined (created).
Labels:
ML
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. :)
Labels:
Scala
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.
Labels:
ML
Friday, September 27, 2013
Thursday, September 26, 2013
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].
Labels:
spring,
spring batch
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.
Tuesday, August 6, 2013
Tuesday, July 30, 2013
Wednesday, July 24, 2013
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.
Labels:
Scala
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.
Labels:
Scala
Thursday, July 18, 2013
Tuesday, July 16, 2013
Monday, July 15, 2013
Thursday, July 11, 2013
Wednesday, July 10, 2013
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.
Labels:
Scala
Monday, July 8, 2013
Friday, July 5, 2013
Monday, April 15, 2013
Using Bootstrap with Play 2
Sunday, April 14, 2013
Thursday, March 28, 2013
Friday, March 22, 2013
Tuesday, March 19, 2013
Monday, March 18, 2013
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
This generates a file with following content. Remark: in reality that sequence contains 5000 numbers.
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)
XQuery 2: (takes about 5 seconds for 5000 numbers)
XQuery 3: (takes about 1 seconds for 5000 numbers)
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.
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.
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.
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)
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.
studentinfo.csv
students.xml
So the first thing I did was creating an XML representation of that CSV file using below xslt.
studentinfo.xslt
So after the transformation a new file studentinfo.xml gets generated as below.
So now we need to execute a second transform using as input students.xml and studentinfo.xml.
student_addinfo.xslt
And finally we get the merged result:
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.
Result from executing XQuery on Zorba
So what does this page look like in a browser?
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).
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.
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.
Subscribe to:
Posts (Atom)