GWT and Java Script Native Interface (JSNI) calls

package com.mattiz.jsni.client;

import java.util.Date;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.JsDate;
import com.google.gwt.dev.jjs.ast.JStringLiteral;
import com.google.gwt.dev.js.ast.JsStringLiteral;
import com.google.gwt.dev.js.ast.JsVars.JsVar;
import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TextBox;

public class HelloJsni implements EntryPoint {

	@Override
	public void onModuleLoad() {
		Label label = new Label(
				"Enter date in any of these formats: MM/dd/yyyy, M/dd/yyyy, MM/d/yyyy M/d/yyyy, "
						+ "MM.dd.yyyy, M.dd.yyyy, MM.d.yyyy,M.d.yyyy, yyyy/MM/dd, yyyy/MM/d, yyyy/M/dd, yyyy/M/d, "
						+ "yyyy.MM.dd, yyyy.MM.d, yyyy.M.dd, yyyy.M.d, MM/dd/yy, M/d/yy,MM/d/yy M/dd/yy, "
						+ "MM.dd.yy M.d.yy MM.d.yy M.dd.yy ,MMddyyyy");
		label.setStyleName("gwt-Label"); // Change css to use this
		HorizontalPanel hp = new HorizontalPanel();
		RootPanel.get().add(label);
		final TextBox tb = new TextBox();
		tb.addBlurHandler(new BlurHandler() {
			@Override
			public void onBlur(BlurEvent event) {
				String stringJavaDate = tb.getValue();
				String formattedDate = loadDateValidator(stringJavaDate);
				tb.setValue(formattedDate);
			}
		});
		hp.add(tb);
		RootPanel.get().add(label);
		RootPanel.get().add(hp);
	}

	private static native String loadDateValidator(String stringJsVar) /*-{
		return $wnd.convertDateFormat(stringJsVar);//calls method in js file.
	}-*/;
}

Note the js call using JSNI (Java Script Native Interface) in the native function private static native String loadDateValidator().

Source code hierarchy

dateformatter.js (this has been replicated in other posts on this blog)

// Format the Date entered in the any below format to MM/dd/yyyy format:
// Add more date formats here to increase date format range accepted
function convertDateFormat(dateValue) {
    if (!isValidJSDate(dateValue)) {
        alert("Unexpected Date Format");
    }   
    var dateArray = new Array();
    dateArray[0] = "MM/dd/yyyy";
    dateArray[1] = "M/dd/yyyy";
    dateArray[2] = "MM/d/yyyy";
    dateArray[3] = "M/d/yyyy";
 
    dateArray[4] = "yyyy/MM/dd";
    dateArray[5] = "yyyy/MM/d";
    dateArray[6] = "yyyy/M/dd";
    dateArray[7] = "yyyy/M/d";
 
    dateArray[8] = "MM/dd/yy";
    dateArray[9] = "M/d/yy";
    dateArray[10] = "MM/d/yy";
    dateArray[11] = "M/dd/yy";
 
    dateArray[12] = "MM.dd.yyyy";
    dateArray[13] = "M.dd.yyyy";
    dateArray[14] = "MM.d.yyyy";
    dateArray[15] = "M.d.yyyy";
 
    dateArray[16] = "yyyy.MM.dd";
    dateArray[17] = "yyyy.MM.d";
    dateArray[18] = "yyyy.M.dd";
    dateArray[19] = "yyyy.M.d";
 
    dateArray[20] = "MM.dd.yy";
    dateArray[21] = "M.d.yy";
    dateArray[22] = "MM.d.yy";
    dateArray[23] = "M.dd.yy";
 
    dateArray[24] = "MMddyyyy";
 
    if (dateValue != "") {
        for ( var i = 0; i < dateArray.length; ++i) {
            var date = getDateFromFormat(dateValue, dateArray[i]);
            if (date != 0) {
                dateValue = formatDate(new Date(date));
            }
        }
    }
    return dateValue;
}
// to validate the below formats for frontend
// Add more date formats here to increase date format range accepted
function isValidJSDate(dateValue) {
    var flag = false;
    if (dateValue == "") {
        return true;
    }
    var dateArray = new Array();
    dateArray[0] = "MM/dd/yyyy";
    dateArray[1] = "M/dd/yyyy";
    dateArray[2] = "MM/d/yyyy";
    dateArray[3] = "M/d/yyyy";
 
    dateArray[4] = "yyyy/MM/dd";
    dateArray[5] = "yyyy/MM/d";
    dateArray[6] = "yyyy/M/dd";
    dateArray[7] = "yyyy/M/d";
 
    dateArray[8] = "MM/dd/yy";
    dateArray[9] = "M/d/yy";
    dateArray[10] = "MM/d/yy";
    dateArray[11] = "M/dd/yy";
 
    dateArray[12] = "MM.dd.yyyy";
    dateArray[13] = "M.dd.yyyy";
    dateArray[14] = "MM.d.yyyy";
    dateArray[15] = "M.d.yyyy";
 
    dateArray[16] = "yyyy.MM.dd";
    dateArray[17] = "yyyy.MM.d";
    dateArray[18] = "yyyy.M.dd";
    dateArray[19] = "yyyy.M.d";
 
    dateArray[20] = "MM.dd.yy";
    dateArray[21] = "M.d.yy";
    dateArray[22] = "MM.d.yy";
    dateArray[23] = "M.dd.yy";
 
    dateArray[24] = "MMddyyyy";
 
    for ( var i = 0; i < dateArray.length; ++i) {
        var date = getDateFromFormat(dateValue, dateArray[i]);
        if (date != 0) {
            flag = true;
        }
    }
    return flag;
}
 
// Format the Date object to MM/dd/yyyy format:
function formatDate(date) {
    var dateString = "";
    if (date) {
        var d = date.getDate();
        var day = (d < 10) ? '0' + d : d;
        var m = date.getMonth() + 1;
        var month = (m < 10) ? '0' + m : m;
        var yy = date.getFullYear();
        yy += '';
        switch (yy.length) {
        case 0:
            yy = '0000';
            break;
        case 1:
            yy = '000' + yy;
            break;
        case 2:
            yy = '00' + yy;
            break;
        case 3:
            yy = '0' + yy;
            break;
        }
        dateString = month + "/" + day + "/" + yy;
    }
    return dateString;
}
 
// Utility function. returns date in time format.
var MONTH_NAMES = new Array('January', 'February', 'March', 'April', 'May',
        'June', 'July', 'August', 'September', 'October', 'November',
        'December', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug',
        'Sep', 'Oct', 'Nov', 'Dec');
var DAY_NAMES = new Array('Sunday', 'Monday', 'Tuesday', 'Wednesday',
        'Thursday', 'Friday', 'Saturday', 'Sun', 'Mon', 'Tue', 'Wed', 'Thu',
        'Fri', 'Sat');
function LZ(x) {
    return (x < 0 || x > 9 ? "" : "0") + x
}
 
function getDateFromFormat(val, format) {
    val = val + "";
    format = format + "";
    var i_val = 0;
    var i_format = 0;
    var c = "";
    var token = "";
    var token2 = "";
    var x, y;
    var now = new Date();
    var year = now.getYear();
    var month = now.getMonth() + 1;
    var date = 1;
    var hh = now.getHours();
    var mm = now.getMinutes();
    var ss = now.getSeconds();
    var ampm = "";
    while (i_format < format.length) {
        // Get next token from format string
        c = format.charAt(i_format);
        token = "";
        while ((format.charAt(i_format) == c) && (i_format < format.length)) {
            token += format.charAt(i_format++);
        }
        // Extract contents of value based on format token
        if (token == "yyyy" || token == "yy" || token == "y") {
            if (token == "yyyy") {
                x = 4;
                y = 4;
            }
            if (token == "yy") {
                x = 2;
                y = 2;
            }
            if (token == "y") {
                x = 2;
                y = 4;
            }
            year = _getInt(val, i_val, x, y);
            if (year == null) {
                return 0;
            }
            i_val += year.length;
            if (year.length == 2) {
                if (year > 70) {
                    year = 1900 + (year - 0);
                } else {
                    year = 2000 + (year - 0);
                }
            }
        } else if (token == "MMM" || token == "NNN") {
            month = 0;
            for ( var i = 0; i < MONTH_NAMES.length; i++) {
                var month_name = MONTH_NAMES[i];
                if (val.substring(i_val, i_val + month_name.length)
                        .toLowerCase() == month_name.toLowerCase()) {
                    if (token == "MMM" || (token == "NNN" && i > 11)) {
                        month = i + 1;
                        if (month > 12) {
                            month -= 12;
                        }
                        i_val += month_name.length;
                        break;
                    }
                }
            }
            if ((month < 1) || (month > 12)) {
                return 0;
            }
        } else if (token == "EE" || token == "E") {
            for ( var i = 0; i < DAY_NAMES.length; i++) {
                var day_name = DAY_NAMES[i];
                if (val.substring(i_val, i_val + day_name.length).toLowerCase() == day_name
                        .toLowerCase()) {
                    i_val += day_name.length;
                    break;
                }
            }
        } else if (token == "MM" || token == "M") {
            month = _getInt(val, i_val, token.length, 2);
            if (month == null || (month < 1) || (month > 12)) {
                return 0;
            }
            i_val += month.length;
        } else if (token == "dd" || token == "d") {
            date = _getInt(val, i_val, token.length, 2);
            if (date == null || (date < 1) || (date > 31)) {
                return 0;
            }
            i_val += date.length;
        } else if (token == "hh" || token == "h") {
            hh = _getInt(val, i_val, token.length, 2);
            if (hh == null || (hh < 1) || (hh > 12)) {
                return 0;
            }
            i_val += hh.length;
        } else if (token == "HH" || token == "H") {
            hh = _getInt(val, i_val, token.length, 2);
            if (hh == null || (hh < 0) || (hh > 23)) {
                return 0;
            }
            i_val += hh.length;
        } else if (token == "KK" || token == "K") {
            hh = _getInt(val, i_val, token.length, 2);
            if (hh == null || (hh < 0) || (hh > 11)) {
                return 0;
            }
            i_val += hh.length;
        } else if (token == "kk" || token == "k") {
            hh = _getInt(val, i_val, token.length, 2);
            if (hh == null || (hh < 1) || (hh > 24)) {
                return 0;
            }
            i_val += hh.length;
            hh--;
        } else if (token == "mm" || token == "m") {
            mm = _getInt(val, i_val, token.length, 2);
            if (mm == null || (mm < 0) || (mm > 59)) {
                return 0;
            }
            i_val += mm.length;
        } else if (token == "ss" || token == "s") {
            ss = _getInt(val, i_val, token.length, 2);
            if (ss == null || (ss < 0) || (ss > 59)) {
                return 0;
            }
            i_val += ss.length;
        } else if (token == "a") {
            if (val.substring(i_val, i_val + 2).toLowerCase() == "am") {
                ampm = "AM";
            } else if (val.substring(i_val, i_val + 2).toLowerCase() == "pm") {
                ampm = "PM";
            } else {
                return 0;
            }
            i_val += 2;
        } else {
            if (val.substring(i_val, i_val + token.length) != token) {
                return 0;
            } else {
                i_val += token.length;
            }
        }
    }
    // If there are any trailing characters left in the value, it doesn't match
    if (i_val != val.length) {
        return 0;
    }
    // Is date valid for month?
    if (month == 2) {
        // Check for leap year
        if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)) { // leap
                                                                            // year
            if (date > 29) {
                return 0;
            }
        } else {
            if (date > 28) {
                return 0;
            }
        }
    }
    if ((month == 4) || (month == 6) || (month == 9) || (month == 11)) {
        if (date > 30) {
            return 0;
        }
    }
    // Correct hours value
    if (hh < 12 && ampm == "PM") {
        hh = hh - 0 + 12;
    } else if (hh > 11 && ampm == "AM") {
        hh -= 12;
    }
    var newdate = new Date(year, month - 1, date, hh, mm, ss);
    return newdate.getTime();
}
// Utility functions for parsing in getDateFromFormat()
function _isInteger(val) {
    var digits = "1234567890";
    for ( var i = 0; i < val.length; i++) {
        if (digits.indexOf(val.charAt(i)) == -1) {
            return false;
        }
    }
    return true;
}
function _getInt(str, i, minlength, maxlength) {
    for ( var x = maxlength; x >= minlength; x--) {
        var token = str.substring(i, i + x);
        if (token.length < minlength) {
            return null;
        }
        if (_isInteger(token)) {
            return token;
        }
    }
    return null;
}

JSNITest.gwt.xml
Note how the js file has been included in the gwt.xml file and not in the default html file.

<?xml version="1.0" encoding="UTF-8"?>
<module rename-to='JSNITest'>
  <!-- Inherit the core Web Toolkit stuff.                        -->
  <inherits name='com.google.gwt.user.User'/>
 
  <inherits name='com.google.gwt.user.theme.standard.Standard'/>
 
  <!-- Specify the app entry point class.                         -->
  <entry-point class='com.mattiz.jsni.client.HelloJsni'/>
  <script src="./dateFormatter.js">
    <![CDATA[
      if ($wnd.$) {
        return true;
      }
      else {
        return false;
      }
    ]]>
  </script>
</module>

JSNITest.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
    <link type="text/css" rel="stylesheet" href="JSNITest.css">
    <title>My First GWT application</title>
    <script type="text/javascript" language="javascript" src="jsnitest/jsnitest.nocache.js"></script>
  </head>
  <body>
    <!-- OPTIONAL: include this if you want history support -->
    <a href=""></a>
  </body>
</html>

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
    <!-- Default page to serve -->
    <welcome-file-list>
        <welcome-file>JSNITest.html</welcome-file>
    </welcome-file-list>
</web-app>

Snapshot below:

About cuppajavamattiz
Matty Jacob - Avid technical blogger with interests in J2EE, Web Application Servers, Web frameworks, Open source libraries, Relational Databases, Web Services, Source control repositories, ETL, IDE Tools and related technologies.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: