Wednesday, March 19, 2014

RatingBar madness and solution!

I've been working with Android recently and I just ran into an irritating "quark" with the RatingBar object and I couldn't find a solution through the usual googling and StackExchange, fortunately I stumbled upon a solution!

The Problem:
The ratingBar has a numStars setting which should indicate the maximum number of stars to display for  a rating. In my instance I have this value set to 5, only problem is it displays a many stars and it can possibly fit into the viewable space.. somewhere around 8 stars, uh... hello, I set it to 5!  I love it when things are easy!


This wont do, So here's the code, perfectly reasonable stuff here. (For details on how to do a custom style check out this great blog post, no point in me rewriting this one.)

 <RatingBar
    android:id="@+id/rtbDvcMgmt"
    style="@style/CustomRatingBar"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginRight="25dp"
    android:layout_marginTop="5dp"
    android:max="5"
    android:maxHeight="50dp"
    android:minHeight="28dp"
    android:numStars="5"
    android:rating="0.0"

    android:stepSize="1.0" />

This code has nothing wrong with it, so why is it no working as expected? The explanation can be found in the RatingBar source code java doc! 

When using a RatingBar that supports user interaction, placing widgets to the left or right of the RatingBar is discouraged.
What is meant by "discouraged" is it wont work! All sorts of things go wrong, the stars don't fill properly, there are more stars displayed then you requested... Yes don't do this, it doesn't work.

The cause in my case was that I was using a TextView next to the RatingBar because that's the requirement! I also had the whole form laid out in a Tablerow which doesn't play nicely with RatingBar either as I discovered when I moved the RatingBar onto its own row. 

The Solution!
The solution is surprisingly simple. The RatingBar works when it's all by itself snuggled up in a Layout with nothing to the right or left. so I wrapped it in its own LinearLayout INSIDE of the TableRow with a TextView right next to it! That did the Trick! 



<TableRow
       android:id="@+id/tableRowDvcMgmt"
       android:layout_width="fill_parent"
       android:layout_height="wrap_content"
       android:padding="5sp" >

       <TextView
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           ... />
                
       <LinearLayout
              android:layout_width="fill_parent"
              android:layout_height="wrap_content"
              android:orientation="vertical" >
                       <RatingBar
                           android:id="@+id/rtbDvcMgmt"
                           style="@style/CustomRatingBar"
                           android:layout_width="wrap_content"
                           android:layout_height="wrap_content"
                           android:layout_marginRight="25dp"
                           android:layout_marginTop="5dp"
                           android:max="5"
                           android:maxHeight="50dp"
                           android:minHeight="28dp"
                           android:numStars="5"
                           android:rating="0.0"
                           android:stepSize="1.0" />
       </LinearLayout>
</TableRow>

Monday, January 7, 2013

My WeatherPi - Weather reports from a Raspberry PI

Ever find yourself pining away at Wuderground and wishing you had one of those really cool weather stations proudly poking its uber-scientific like visage over your neighbors fence? Yes me too, not only that but it runs in the family because my dad also had that goal. Being a sucker for electronics, weather stations and a great gift idea I bought my dad a wh1080 Weather Station for Christmas. We got it all set up and found the software to be a bit.. dated. It doesn’t upload to the internet and though there are programs out there to take care of that for you, Dad really didn’t want want his weather station shackled to a PC via USB.

Now that I have amassed a small army of R-Pis to do my bidding (4 and counting) I figured it was time to start gifting them with useful software to family and friends. So I set up a WeatherPi, the nickname I have assigned this project and the WeatherPi is rocking!  The best part is that setting up a WeatherPi is a great way to get to know a lot of the key features of the Linux/Pi OS and prepare your *nix skills for future projects.

First up, lets look at the stats:

Raspberry Pi - Older 256Ram version.
SD - 8mg
Edimax EW-7811 (Usb WiFi)
Pywws  with Python
TP1080WC ProWeatherStation aka: wh1080 (kinda a must have)


Now I must admit that this is more of an OS based project then electronics build but, if you're just getting started and you really want to master some of the Linux skills you really can’t go wrong with this project. Here’s some of the fun new skills you could pick up while getting your weather station data to Wunderground.com
  1. Basic RPi setup  - I used the Debian wheezy install right of the RaspberryPi site.
  2. Usb-wif and ssh because I like my big monitor and I didn’t want the RPi hooked up to the TV.
  3. Setting a static ip -  I want to know where this guy is on my network so I can connect easily.
  4. Pywws - a great open source tool for uploading to Wunderground and 4 or 5 other sites and more.
  5. Chrontab configuration and shell scripts - used to set up the Pywws to run the LiveLog every 5 minutes and upload WeatherStation data.

References
I would like to go into the details on how each part should be set up and configured but, I think it’s only right to give credit where it is due so I’ll provide links to all the great blogs where I gathered my info.


Overall Project info as well as specific Pywws installation advice:
http://blog.retep.org/2012/07/30/installing-a-usb-weather-station-on-a-raspberry-pi-part-1/

Edimax usb wifi setup: (With notes on a static ip)
http://etchingpathways.blogspot.com/2012/12/raspberry-pi-edimax-ew-7811un-wifi.html

Chrontab (second half covers chrontab)
http://blog.davidsingleton.org/raspberry-pi-webcam-a-gentle-intro-to-crontab/

Weatherstation: (It’s on amazon as well)
http://www.rainmanweather.com/site/catalog/Weather-Stations/Tycon-Power



Wrap-up and Next Steps
I originally picked up this project to help out my Dad with his new Weather Station but after working on the set up for a couple hours at my parents house using my Tablet to ssh into the PI and configure I decided to take the PI back home to finish things off. Of course then I didn’t have a Weather Station to verify all the config and installs... they live half an hour away so only one choice, buy my own weather station!  Really I didn’t plan it this way, honest I didn’t! I wrapped up the Pywws install and all the chrontab stuff at home and then made a copy of the SD card image with all the good configurations already done and poof! 2 WeatherPis ready to go.

Now as any IT pro can tell you, the moment you build, config, install or even look seriously at a device, you own support! With this in mind I have already set up a PI script to call my own REST Web Services to report to me when there are problems. This has led me on to start developing one of my next projects, HomePi, which will allow all my little Pi ‘s t check in with the HomePi and tell me how they are doing and what sort of errors they encounter while doing their thing. It’s pretty basic right now but I am hoping to allow for code updates and other support functions in the future.

I have plans to integrate IFTTT to work to provide Freeze and drought warnings for my Garden and most likely I’ll be logging my weather data over at COSM (formally known as Pachube) just because I can.

Tuesday, December 4, 2012

Spring, Autowire and EasyMock

I've been stalling on writing a number of tests I needed because I was caught up on how easily inject a Mock  into a Service that I wanted to test. The problem  was that I didn't want to go to the trouble of cooking up a separate spring context just for tests not to mention I couldn't rely on data in the DB for the DAO to utilize      after all the point of EasyMock is... EASY!  Well that avoiding writing that test that needed a mock finally bit me when some fast refactoring caused something fail because of the lacking test. That's what ya get, right?

After a good deal of hunting I come across a simple solution that I am surprised was so hard to find...

ReflectionTestUtils.setField(serviceYourTesting, "autowireBeanName", yourMock);

That's it! No, really.  OK OK, that's not all you have to do. You are messing with the Service Beans properties so if you have other tests elsewhere that expect that mock object to not be a mock object you'll have to undo what you did.  This isn't such a hassle though. simply call the following and stash that object to restore after you done with the mock.

Object saveBean = ReflectionTestUtils.getField(serviceYourTesting, "autowireBeanName");

I would suggest wrapping your test in a try/finally so you can make sure that the original bean value is set back before the tests concludes. If you don't do this other tests that expect the original bean may fail.

Happy Spring Mocking!

Wednesday, October 10, 2012

java.lang.reflect.MalformedParameterizedTypeException or (WTH is wrong with my deployment now?)

So the fun part of the MalformedParameterizedTypeException error is that it seems to be caused by many different things at least that is the impression that I get from the Google searches I conducted and the various suggestions for a fix offered on StackOverflow. I think it really comes down to a simple incompatible jar file hanging out on your web server.

I'n my case I am using WebLogic and the local version of WL that I have running right now is 10.3.6, the version on my Dev server is 10.3.5; you wouldn't expects that a version that close together would be such trouble but I cost me a day to figure out what's up. First off I still don't know the name and version of whatever jar is causing the trouble in Dev. I don't have the access to sort it out and now that I know the cause... I just wont do it, problem avoided though not solved. Good enough under a tight deadline.

So on to the point!  Lets say your using Spring as I am and you have a DAO that doesn't require a dataSource because it's just not that kind of DAO. So you don't have to inject anything into the object. Well when you define this DAO and you deploy only to get our friend MalformedParameterizedTypeException  popping up, the cause is defining the bean in an abbreviated xml node such as the following:


<bean class="com.myco.webservices.dao.impl.MyDaoImpl" id="myDao"/>

The fix is so simple:  (Note the ending tag)

<bean class="com.myco.webservices.dao.impl.MyDaoImpl" id="myDao"></bean>


Of course finding that issue in a dozen files with multi-line changes, imports of new packages and implementation of new functionality is um... not so easy.

Wednesday, June 20, 2012

The In and Outs of MongoDB and Jackson


MongoDB has been a great NoSQL solution to work with so far but, there are a few gotchas that I found needed some exploring to get sorted out properly for a Mongo noob.

I have been working with RESTful services for the past couple years but to date, my NoSQL solution has primarily been Membase. Membase is good at what it does but, it is a bit sparse on features compared to the RDBMs that I am accustomed to working with. No, don't worry I am not about to jump into a debate of NoSQL vs RDBMs, I use my soldering iron to solder and my multi-meter to measure volts, each tool for its job! On the matter of Membase vs MongoDB though I have to say that I have quicky developed a strong preference for Mongo. This new appreciation comes from the field indexing and the ability to use regex to search for specific data. Perhaps this is just me finding some similarities between RDBM and Mongo? Sure that and living with out those features in the past has simply resulted in writing code for things that are usually standard issue.

In my current project I am using MongoDB with Java JAX-RS and Jackson. So far things have fit together quite nicely though I did come across a few issues that I had a hard time tracking down detailed information about. I had to just puzzle out these issues and thought someone might find the info useful, not to mention I can look them up when I inevitably forget them in 6 months once all that information is replaced with whatever comes next.

One of those things I love about a MySQL db driver is that it thoughtfully returns the ID for a newly created record so you can easily access it for further use or abuse. MongoDB also does this but, it wasn't at all obvious how the Java driver was returning this value. At first I was doing a query back to the collection to grab the new _id but, this method left me just feeling less then confident that things would always turn out right. What I didn't realize was the Java driver was thoughtfully returning the value by inserting it in the BasicDBObject used to create the insert. I felt a little silly not thinking to look there first but.. well it just wasn't what I was expecting. Here's the code to illustrate.


BasicDBObject dbObject = MongoModelFactory.buildDBObject(model);
DBCollection dbColl = getCollection(collectionName.getValue());
WriteResult result = dbColl.insert(dbObject);

//The _id values is populated in the dbObject after the insert.
dbObject.get("_id");

Related to getting the id comes a Jackson issue. Because the _id is not stored as a String, but as an ObjectId which makes deserializing the value with the Jackson ObjectMapper a bit on the complicated side. The trick here is to create a MongoID object which contains a JsonCreator that knows how to construct the _id.$oid value from the JSON. This was by far the simplest way to use Jackson to get this information out and the object is reusable across all of my DataObjects.  Here's the code:

public class MongoID {
  private String $oid;

  public MongoID(){  
  }
 
  public String get$oid() {
    return $oid;
  }
 
  public void set$oid(String $oid) {
    this.$oid = $oid;
  }
 
  @JsonCreator
  public static String fromJSON(String val) 
      throws JsonParseException, JsonMappingException, IOException {
    ObjectMapper mapper = new ObjectMapper();
    MongoID a = mapper.readValue(val,MongoID.class);
    return a.get$oid();
  }
}

The above class is utilized in my BaseModel abstract object to provide a hint to the Jackson ObjectMapper as to what field this is basically by providing a fromJSON method for Mongo's ObjectId . 

The last challenge I ran into was also a Jackson related issue, specifically in serializing an object to JSON and back again. Jackson doesn't like really complicated JSON lists and due to class erasure it has a hard time figuring out what sort of objects are contained in a list. Such a case is simular to the following JSON:

{
    "_id": "4fc151c9ebb11be7d1ae4905",
    "mapPreferences": {
        "bgColor": "#fff",
        "gridColor": "#000"
    },
    "maps": [
        {
            "cols": 30,
            "title": "TestMap",
            "rows": 30
        },
        {
            "cols": 32,
            "title": "TestMap2",
            "rows": 32
        }
    ]
}

Jackson doesn't really have issues with a list of strings but try a list of objects or a multi-dimensional array and Jackson is stumped as to what to do because of  alack of information.  The JsonCreator again provides a solution to this issue! In the above example we have a field called "maps" which contains an array of MapData Objects, with out providing a JsonCreator, Jackson won't know what object is contained in the list and can't instantiate a list with objects of that type. To solve this List of unknown objects issue I have written the folloing code in the MapData Object :

@JsonCreator
  public AdventureData(@JsonProperty("adventureId") String advId, 
          @JsonProperty("mapPreferences") MapPreferences mapPrefs,
       @JsonProperty("maps") Object maps) 
         throws JsonParseException, JsonMappingException, 
                IOException
  {
    this.adventureId = advId;
    this.mapPreferences = mapPrefs;
   
    ObjectMapper mapper = new ObjectMapper();
    String mapsString = mapper.writeValueAsString(maps);
    this.maps = mapper.readValue(mapsString, 
        TypeFactory.collectionType(List.class, MapData.class));
  }

In the constructor above the parameters define the objects but in the case of the "maps" field  it is left to be an Object because it will be deseralized separately since casting to a List is obfuscated by Type Erasure. To construct the List of MapData objects I then used the ObjectMapper to convert the list back to a JSON String then deserialize it! To make it clear to the ObjectMapper what sort of object is in the List I use the TypeFactory to indicate that we have a Collection of the List type here and it contains MapData.  That's about all there is too that. You will need to give the MapData object it's own JsonCreator similar to the one above but that's all you have to do. If only it had been that easy to find a sample on how this is done! I will say that if you know of another way to make this happen please let me know because the Object to string conversion feels a little bit like a Hack though it works perfectly.

I hope this helps you out when your working with Mongo and Jackson for the first time! (After reviewing this post I have come to realize that I will need to describe my generics based DAO that I am using for MongoDB to better explain what I'm doing with the _id value. Look for it in the future!)


Monday, June 6, 2011

A solution to speed up an old Droid

Is your Droid phone running so slow that you can barely answer a phone call before it rolls to voice mail? Yea that was my phone last week! I couldn't accomplish anything with my phone without long delays and that included just answering the phone! I have a venerable but, much loved Droid 1 and with 4 months remaining on the contract there was just no way I could "tough it out" at the current response time. Discussing the issue with a few Verizon reps got me the well worn mantra of the old Windows OS days, Reset (Just like Reinstall when you had Windows speed problems right?).

Fortunately, I found a better solution. I didn't want to invest the time into re-configuring and downloading everything on my phone, it's just the way I like it and I have better things to do such as working on my Game Programming or gaming with my son! I had freed up more memory then the average amount of free memory found on a Droid so I knew that the OS had enough space to do it's thing which lead me to suspect something was getting in the way of Androids ability to do things quickly. The first thing that came to mind? Cache. Nothing like a nice fat Cache full of useless junk to keep an old processor spinning its wheels right? Sure enough!

So my suggestion, before you reset your Android, clear the system Cache, it just might save you a couple hours!  Here's the surprisingly easy Steps (Getting into the System Menu is specific to the Droid 1, but the steps should be the same on other Droids)

1. Turn the Droid off, take the battery out then place it back in.

2. Open the keyboard and press the X key while pressing and holding Power. The device will power on. Keep holding the buttons down until a yellow triangle appears on the screen.

3. Press and hold the Volume Up and then also press Camera button button to get to the "Android System Recovery" Menu.

4. Use the direction pad to the right of the keyboard to select the "wipe cache partition"  (This is it right here!)

5. Once complete, use the direction pad to select the first option "reboot"


Believe it or not this did the trick! My phone has been running much faster, it's still a near two year old phone so its not as peppy as it once was but hey we all get a little bit slower with age...

Tuesday, April 26, 2011

Moving to Siena from Play Framework JPA

I Recently built a REST service using the Play Framework and once complete I started looking for a place to deploy the app. Not having a hosted server that runs Java meant I needed a cloud solution and the first to come to mind was GAE. I have used GAE before but this was the first time with Play. (Play and GAE go great together btw.)

Having done little deployment planning from the beginning, it wasnt until a blew through the coding and did a successful deploy that I realized that JPA just doesn't seem to work well with Play's JPA (nothing against Play, GAE just does JPA differently). After a bit of reading that I had skipped past the first time it became evident that I needed to use the Play Siena Module with my GAE app. This turned out to be easier then I though but, I had to hunt around a little to piece together the details so I wanted to share the migration here. This should also be helpful if you are planning to use Siena from the start and already know Play's JPA.

Moving to Siena from the Play JPA was surprisingly simple, I spent more time thinking over weather I wanted to mess with it and looking up the details then actually doing it!

1. Install Siena into Play. (I'm assuming you have Play set up and GAE already installed.)
play install siena-1.5

2. Include in App.conf. Add Siena right under the GAE module like so.

# ---- MODULES ----
module.gae=${play.path}/modules/gae-1.4
module.siena=${play.path}/modules/siena-1.5


3. Change your Models from play.db.jpa.Model to siena.Model. Your just changing an import statement here.

//import play.db.jpa.Model;
import siena.Model;


4. Add an ID field. Play takes care of this for you in the Model but you'll have to add it in for Siena, or make a new Object to inherit from.

  @Id
  public Long id;


5. Update method calls. A few methods have slightly different names but they are pretty obvious and should slow you down.
  1. Model.save() calls become Model.insert()
  2. You'll need to change up your searching, I usually just code methods in the Model to wrap the search queries which means I only have to change the search code in one spot, the Model. Here's a sample of a new search:
  3. Model.all(MonitoredFeed.class).filter("suspended", false).fetch();
  4. Change findAll(), all() calls to Model.All(Your.class) or simply provide a helper method like below.(This could again go in some type of BaseModel reducing your code changes.)
  5. public static Query all() {     return Model.all(MonitoredFeed.class); }
6. Update your Test Fixtures! Of course you have to update the Tests! If your using Fixtures you'll just have to change your imports from Fixture to SienaFixture.

That's all there its to it! Now you can deploy your app on GAE!

Saturday, April 16, 2011

Serve Up Static Content Even When Mapping /* to a FrontController Servlet.

I am blogging this to share the info as much as to remind myself in the future how simple setting this up really is. It's the easy solutions that can be the hardest to remember..

Say you have a RESTful services framework running in a web app and all of your requests get routed to CXF or some other Framework for supporting such implementations. Simply put, there isn't a UI for the app and the Controller Servlet does't support a UI. Now say that you really want to be able to serve up say.. a JS file and some css to style generated API documentation for your services. No problem since this is Tomcat except you have a /* url-pattern routing all traffic to a REST Servlet.

Here's the solution: Most Java servers have a default servlet of some sort which will simply handle HTTP requests. If you set up a url-pattern to /static or some other path you can direct that request to your static files. One note, you'll be requesting /static/js/jquery-min.js but, the fiel will reside at /js/jquery-min.js this is because the /static is really acting as a indicator to rout to the default servlet. It's almost like a query string in the path.. no it makes since, really.


<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>/static/*</url-pattern>
</servlet-mapping>

Thursday, April 14, 2011

Scripting Groovy to process csv files.

I have a lot of experience with um, "manhandling" flat file data into usable formats. Usually this "data" comes to me in the form of excel spreadsheets, crafted with presentation in mind. They look great but, have little flexibility when it comes to importing the data into relational databases. There are hundreds of solutions for doing this sort of manipulation in bulk and if that's your lot in life by all means beg the IT department to buy you a good data transformation tool!

For some reason my situations are frequently one offs of user data that is begging to get into a database but, as of yet is still running in the wild. My solution has come down to quick Groovy scripts and splicing spreed sheets into multiple CSV files Groovy to chew through. This can be done with Python and many other script languages but, I'm fully fluent in Java so Groovy is my fastest path to a database at this point in time. This is a great place to learn a new scripting language if you have the time but, I usually want this over in a flash. On that note, if you would like to just grab the code and run jump to the bottom to grab My CsvUtils code which takes care of this whole process in a more formal utility object.

Reading a file and working with it is pretty darn simple in Groovy, the following code will open up a CSV and move all the data into nicely structured arrays. (This provides the same results as calling CSVUtils.csvToList())

 //load room types from file
def f= new File('roomtypemap-min.csv');

//set up a List of 'Rows' which will contain a list of 'columns' or you can think of it as array[][]
roomTypes = [];
def lines = 0;
f.splitEachLine(',') {
    def row = [];
    row = it;
    roomTypes << row;
    lines++
}
println "Processed $lines Lines"

The above chewed through a CSV file with name,value pairs of data and placed them in a multi-dimensional array types as a Java list. If your not up on Java collections, it's a whole lot like an array but, better.

Now lets say we did the above code with a few files then process all that data into a new CSV file ready for your code. The code below shows the building of the final results. You'll notice that along with the roomTypes list imported from a CSV file there are now a few others resortList and amenities. All these were pulled from random table dumps and such and I turned them in to usable data.


def aId = 0;    
roomTypes.each{
  def indx = roomTypes.indexOf(it);
  def resortList = roomResortMap[indx];
  def roomType  = it[0];
  
  resortList.each() {
    def facIdx = it.toInteger();
    def facilityId = resorts[it.toInteger()];
    
    amenities.each() {
      def ai = amenities.indexOf(it);
      def avail = values[ai];
      if(avail[facIdx] == true) {
        def description = it;
        println "\"$facilityId\",$roomType,$aId,\"$description\"";
      aId++;
      }
    }
  }

There are a couple things to point out if your new to Groovy. While there is a whole lot of Java in Groovy it also looks a lot like JavaScript making Groovy pretty easy for JavaScript developers to pick up despite deep Java knowledge.

The first thing to note is the iteration I am performing with the .each callback. Personally, I think it looks a good bit like a JavaScript closure. Your simply passing in code to the .each(){} iterator to execute on each item in the List. OH the () is optional when creating this closure so add if you wish.

Second, in the version of Groovy I am using, 1.7.10, you have to do a little bit of work to get the current index position of the for loop. To do this simply call yourListName.indexOf(it). It looks like the upcoming 1.8 is going to have an additional closure called eachWithIndex() which will provide you it and i variables where the i will be the current index. I'm looking forward to that feature!

Finally I just want to point out one of my favorite things for keeping life simple, string replacement. Notice on the next to list line before the braces cascade I'm calling println but, the cool part is the replacements in the string using $fieldName. When your trying to print out complicated strings nothing is worse for readability and bugs then concatenating or calling tons of append() methods. It just leads to errors. You can do simple string replacement with variables using the $fieldName inside your string or for more complicated inserts ${} will work for things like ${Object.field}.

Hope this will help you to simplify the tedious task of data mining text files!

My CSVUtils Object:

/**
 * CSVUtils is released into the public domain, do what ya want with it.
 * By Lee Clarke
 */
public class CSVUtils {  
 
  /**   
   * Removed quotes from around imported csv values if present.
   */
  public static String stripQuotes(String strIn) {  
    def valOut = strIn;  
    if(valOut==null)  
      valOut == "";  
    if(valOut.startsWith("\""))   
      valOut = valOut.substring(1);  
    if(valOut.endsWith("\""))   
      valOut = valOut.substring(0,valOut.length()-1);  
    return valOut;  
  }
    
  /**   
   * Load the csv into a Map using indexed position of values for key and value. 
   * @param keyPos - col index of the key value in a row
   * @param valPos - pull value data from index/col position in row
   * @param csvFilePath - full path to file
   */  
  public static Map csvToMap(int keyPos, int valPos, String csvFilePath) {  
    def fp= new File(csvFilePath);  
    def rtnMap = [:];  
  
    def palines = 0;  
    fp.splitEachLine(',') {  
      if(palines > 0)//skip col header line  
      {  
        def row = [];  
                row = it;  
        def key = CSVUtils.stripQuotes( row[keyPos]);  
        def val = CSVUtils.stripQuotes( (valPos < 0)?row.last():row[valPos]);  
        rtnMap.putAt(key, val);  
      }  
      palines++  
    }  
    return rtnMap;  
  }  
  
  /**
   * loads values in csv file into multi-dimentional like List of rows and cols.
   * @param csvFilePath - full file path.
   * @param skipFirstRow - skip first row if it contains column names.
   */
  public static List csvToList( String csvFilePath, boolean skipFirstRow) {
    def fr= new File(csvFilePath);
    def rtnList = [];
    def rlines = 0;
    fr.splitEachLine(',') {
      if(!(skipFirstRow && rlines == 0)) {
        println "row= $it"
        def row = [];
        row = it;
        rtnList << row;
      }
      rlines++
    }
    return rtnList;
  }
  
  /**
   * loads values in csv file into multi-dimentional like List of rows and cols.
   * @param csvFilePath - full file path.
   */
  public static List csvToList( String csvFilePath) { 
    return csvToList(csvFilePath,true);
  }
}  

Friday, April 8, 2011

New hRecipeHelper for Web Browsers released!

Do you like to blog recipes but don't use Chrome? No problem! Now you can use my new web version of hRecipeHelper in any moder browser. I know it works with FireFox 3.6 and up as well as IE8, I can only assume it works with IE9 if IE8 works. I haven't tested on IE9 and Safari because I don't have them and can't even run IE9. If you trying it out and find bugs please post a comment here and I'll have it fixed asap.

Just like the Chrome Extension its free, Enjoy!