Showing posts with label programming. Show all posts
Showing posts with label programming. Show all posts

Friday, January 10, 2014

Gmail Inbox Notification Widget

I wanted to display my Gmail Inbox unread message count on my personal web page on Google Sites, so I made a tiny Google Apps Script widget to do this.



The code turned out very simple.

code.gs:
function doGet() {
  return HtmlService.createTemplateFromFile('page')
    .evaluate()
    .setSandboxMode(HtmlService.SandboxMode.NATIVE);
}

function getInboxUnreadCount() {
  return GmailApp.getInboxUnreadCount();
}

page.html:
<style type="text/css">
#refresh { color: blue; text-decoration: underline; }
#refresh:hover { cursor: pointer; }
</style>

<div id="inbox">
Inbox (<span id="count">...</span>)
<span id="refresh">refresh</span>
</div>

<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script>
function check() {
  clearTimeout(check.timer);
  google.script.run
    .withSuccessHandler(function(result) {
      $('#count').text(result);
      $('#inbox').toggleClass('unread', result > 0);
    })
    .getInboxUnreadCount();
  $('#count').text("...");
  check.timer = setTimeout(check, 600000);
}

$(function() {
  check();
  
  $('#refresh').click(check);
});

</script>

The getInboxUnreadCount() function needed to be run from the script editor to grant the needed permissions to the script, after that it worked from my personal home page.

Interactive Javascript Development

When I was traveling to a friend's place to give her a Christmas not alone I got picked up by someone who suggested I learn jQuery... so I did, and in the process wrote a JavaScript Read Eval Print Loop that makes jQuery and JavaScript development as easy as interactive Python or BASH programming.

The result is a single file web application, JavaScript Read, Eval, Print, Loop. View the source to see what was needed to make it work.

Monday, January 6, 2014

Facebook notification widget

I wanted to display my facebook notification information on my personal home screen.

I ended up with a single HTML file which I have hosted on Google Drive and embedded in an iframe in my personal home screen on Google Sites.

The end result is a single line where the numbers change if there are any notifications to report.



<html><head><title>Facebook Quick Check</title></head><body>
<style type="text/css">
body { margin: 0px; }
</style>

<div id="fb-root"></div>

<div>
    <span id="login"><a href="javascript:doLogin()">FB Login</a>: </span>
    Notice (<span id="notice">...</span>)
    Friend (<span id="friend">...</span>)
    Inbox (<span id="inbox">...</span>)
    <a href="javascript:loadStatus()">refresh</a>
</div>

<div id="out"></div>

<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script>
I wanted a fluent API that felt like Google Apps Script server calls instead of the regular Facebook API, so I made a quick one...
function FBRun(parent) {
  this.successHandler = parent ? parent.successHandler : function(result) { };
  this.errorHandler = parent ? parent.errorHandler : function(result) { };
};
FBRun.prototype.withSuccessHandler = function(handler) {
  var obj = new FBRun(this);
  obj.successHandler = handler;
  return obj;
};
FBRun.prototype.withErrorHandler = function(handler) {
  var obj = new FBRun(this);
  obj.errorHandler = handler;
  return obj;
};
FBRun.prototype.getHandler = function() {
  var obj = this;
  return function(response) {
    if(!response || response.error) {
      return obj.errorHandler(response);
    }
    return obj.successHandler(response);
  }
};
FBRun.prototype.get = function(url, data) {
  FB.api(url, 'get', data, this.getHandler());
  return this;
};
FBRun.prototype.login = function(data) {
  FB.login(this.getHandler(), data);
  return this;
}
FBRun.prototype.loginStatus = function() {
  FB.getLoginStatus(this.getHandler());
  return this;
}
fbrun = new FBRun();
Load the Facebook API and check the login status on success...
$(document).ready(function() {
  $.ajaxSetup({ cache: true });
  $.getScript('//connect.facebook.net/en_UK/all.js', function(){
    FB.init({
      appId: 'APPLICATION_KEY',
    });
    fbrun
      .withSuccessHandler(onLogin)
      .loginStatus();
  });
});
The code for the login link...
function doLogin() {
  fbrun
    .withErrorHandler(display)
    .withSuccessHandler(onLogin)
    .login({scope: 'manage_notifications,read_requests,read_mailbox'});
}

var perms = {};

function onLogin(response) {
  if(response.status === 'connected') {
    fbrun
      .withErrorHandler(display)
      .withSuccessHandler(function(response) {
        perms = response.data[0];
        
        if(perms.manage_notifications && perms.read_requests && perms.read_mailbox) {
          $('#login').hide();
        }
        start();
      })
      .get('/me/permissions');
  }
}

function display(response) {
  $('#out').text(JSON.stringify(response));
}
Load the information and start the refresh timer...
function start() {
  loadStatus();
  
  if(start.active) {
    return;
  }
  start.active = true;
  
  function check() {
    loadStatus();
    setTimeout(check, 600000);
  }
  setTimeout(check, 600000);
}

function loadStatus() {
  if(perms.manage_notifications) {
    $('#notice').text('...');
    fbrun
      .withErrorHandler(display)
      .withSuccessHandler(function(response) {
        if(response.summary.unseen_count != undefined) {
          $('#notice').text(response.summary.unseen_count);
        } else {
          $('#notice').text("0");
        }
      })
      .get('/me/notifications', { limit: 0 });
  } else {
    $('#notice').text('-');
  }
  if(perms.read_requests) {
    $('#friend').text('...');
    fbrun
      .withErrorHandler(display)
      .withSuccessHandler(function(response) {
        $('#friend').text(response.summary.unread_count);
      })
      .get('/me/friendrequests', { limit: 0 });
  } else {
    $('#friend').text('-');
  }
  if(perms.read_mailbox) {
    $('#inbox').text('...');
    fbrun
      .withErrorHandler(display)
      .withSuccessHandler(function(response) {
        $('#inbox').text(response.summary.unseen_count);
      })
      .get('/me/inbox', { limit: 0 });
  } else {
    $('#inbox').text('-');
  }
}
</script>
</body></html>

Update: limit the data returned to just the summaries by setting the item limit to zero.

Saturday, January 4, 2014

Google Apps Script execution model

I was wondering if Google Apps Script web applications retained state between invocations, so I did a small experiment.

Using a Script created in Google Drive:

Code.gs:
function doGet() {
  return HtmlService.createTemplateFromFile('page')
    .evaluate()
    .setSandboxMode(HtmlService.SandboxMode.NATIVE);
}

var date = new Date();
var last = -1;

function func(value) {
  Utilities.sleep(100);
  var out = "date = " + date + ", last = " + last;
  last = value;
  return out;
}

page.html:
<style type="text/css">
pre {
  border: 1px solid green;
}
</style>

<div id="out">
<pre>loading...</pre>
</div>

<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script>
$(function() {
  for(var i = 0; i < 2; i++) {
    launch(i);
  }
});

setTimeout(function() {
  for(var i = 2; i < 4; i++) {
    launch(i);
  }
}, 2000);

function launch(id) {
  $("<pre />").text("started " + id).appendTo('#out');
  google.script.run
    .withSuccessHandler(function(result) {
      $("<pre />").text("finished " + id + ": " + result).appendTo('#out');
    })
    .func(id);
}
</script>

This output was produced:
loading...
started 0
started 1
finished 0: date = Sat Jan 04 2014 12:03:06 GMT-0400 (AST), last = -1
finished 1: date = Sat Jan 04 2014 12:03:06 GMT-0400 (AST), last = -1
started 2
started 3
finished 2: date = Sat Jan 04 2014 12:03:08 GMT-0400 (AST), last = -1
finished 3: date = Sat Jan 04 2014 12:03:08 GMT-0400 (AST), last = -1

Which leads me to the conclusion that the server is stateless, at least from the point of view of the scripts running on it.

Friday, January 3, 2014

lazy initialization in JavaScript

I have a function that is not often used and has a long initialization step, when it is used it is used often, making it a very good candidate for lazy initialization. This function is in a Google Apps Script web application, so minimizing processing time is desirable.

Functions in JavaScript are assignable closures, so it is easy to write a lazy initializing function without any if statements.

var getImage = function(name) {
  var images = { };

  var files = MESSAGES_FOLDER.getFiles();
  while(files.hasNext()) {
    var file = files.next();
    images[file.getName()] = file.getDownloadUrl();
  }

  return (getImage = function(name) {
    return images[name];
  })(name);
}

After the initialization part of the function (making a map of file names to download urls) it is replaced by a new function that does not include the initialization, and that new function is called to get the result for the first use.

This is also an implementation of the State pattern.

Thursday, November 21, 2013

Swing Components in HTML GUI in Java

I would like to use Swing components in a HTML based layout. Initially I tried to have empty elements with id attributes in the loaded text, but they were pruned from the internal representation, so I put text inside of them and replaced the whole element with the Swing component.

HtmlGui.java:
package ca.sarah_happy.sandbox;

import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.net.URL;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JSlider;
import javax.swing.JTextPane;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Element;
import javax.swing.text.html.HTMLDocument;

public class HtmlGui implements Runnable {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new HtmlGui());
    }

    private JTextPane viewer;
    private HTMLDocument document;

    @Override
    public void run() {
        try {
            viewer = new JTextPane();
            viewer.setEditable(false);
            viewer.setPreferredSize(new Dimension(400, 300));

            JFrame frame = new JFrame("screen");
            frame.setContentPane(new JScrollPane(viewer));
            frame.pack();
            frame.setLocationByPlatform(true);
            frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);

            URL screen = HtmlGui.class.getResource("screen.html");
            viewer.setPage(screen);
            document = (HTMLDocument) viewer.getDocument();
            viewer.addPropertyChangeListener("page", onLoad);

            frame.setVisible(true);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private JSlider slider;

    private PropertyChangeListener onLoad = new PropertyChangeListener() {
        @Override
        public void propertyChange(PropertyChangeEvent e) {
            JButton button = new JButton("Button");
            button.addActionListener(onButton);
            insertComponent("button", button);

            slider = new JSlider();
            slider.addChangeListener(onSlider);
            insertComponent("slider", slider);
        }
    };

    private ChangeListener onSlider = new ChangeListener() {
        @Override
        public void stateChanged(ChangeEvent e) {
            addOutput("changed slider: " + slider.getValue());
        }
    };

    private void insertComponent(String id, Component component) {
        Element e = document.getElement(id);
        viewer.setCaretPosition(e.getStartOffset());
        viewer.moveCaretPosition(e.getEndOffset());
        viewer.insertComponent(component);
    }

    private ActionListener onButton = new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent arg0) {
            addOutput("pressed button");
        }
    };

    private void addOutput(String text) {
        try {
            Element out = document.getElement("output");
            document.insertString(out.getEndOffset() - 1, text + "\n", null);
        } catch (BadLocationException ex) {
            ex.printStackTrace();
        }
    }
}

screen.html:
<html><body>
Some stuff...

<p><span id="button">button</span> <span id="slider">slider</span></p>

<p><i>Output:</i>
<div id="output"></div></p>
</body></html>

first HTML GUI in Java

Java GUI layout can be cumbersome, and HTML layout is less cumbersome. I wanted to have a screen in a Java application based on a HTML document.

For an initial attempt I made a screen with two buttons and an output area using a JEditorPane, the buttons are made from links and the output area is made from a document element.

HtmlText.java:
package ca.sarah_happy.sandbox;

import java.awt.Dimension;
import java.net.URL;

import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Element;
import javax.swing.text.html.HTMLDocument;

public class HtmlText implements Runnable {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new HtmlText());
    }

    private JEditorPane viewer;
    private HTMLDocument document;

    @Override
    public void run() {
        try {
            URL text = HtmlText.class.getResource("screen.html");
            viewer = new JEditorPane(text);
            viewer.setEditable(false);
            viewer.setPreferredSize(new Dimension(400, 300));

            JFrame frame = new JFrame("screen");
            frame.setContentPane(new JScrollPane(viewer));
            frame.pack();
            frame.setLocationByPlatform(true);
            frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);

            document = (HTMLDocument) viewer.getDocument();
            viewer.addHyperlinkListener(onLink);

            frame.setVisible(true);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private HyperlinkListener onLink = new HyperlinkListener() {
        @Override
        public void hyperlinkUpdate(HyperlinkEvent e) {
            if (e.getEventType() != HyperlinkEvent.EventType.ACTIVATED) {
                return;
            }

            String message = "pressed <" + e.getDescription() + ">";

            try {
                Element out = document.getElement("output");
                document.insertString(out.getEndOffset() - 1,
                        message + "\n", null);
            } catch (BadLocationException ex) {
                ex.printStackTrace();
            }
        }
    };
}

screen.html:
<html><body>
This is a test.
<p><a href="#link1">link 1</a>
<a href="#link2">link 2</a></p>

<p>output:
<div id="output"></div>
</p>
</body></html>

Sunday, October 20, 2013

Facebook API Hello World

After working through the Facebook API Getting Started guide I decided to write an even simpler Hello World application that also displays some debugging information. This is my first Facebook Application.

facebook-hello.html:
<html>
<head><title>Facebook API Hello World</title></head>
<body>
<script type="text/javascript">
  window.fbAsyncInit = function() {
    // https://developers.facebook.com/docs/reference/javascript/FB.init/
    FB.init({
      appId: '222165227950143' /* registered application id */,
      status: true /* check the login status */,
      cookie: true /* set the session cookie */,
      xfbml: true /* enable social plugins */
    });

    // get the initial login status
    // https://developers.facebook.com/docs/reference/javascript/FB.getLoginStatus/
    FB.getLoginStatus(onLogin);
  };

  // Load the SDK Asynchronously
  (function(d, s, id){
    if (d.getElementById(id)) {return;}
    var js, fjs = d.getElementsByTagName(s)[0];
    js = d.createElement(s); js.id = id;
    js.src = "//connect.facebook.net/en_US/all.js";
    fjs.parentNode.insertBefore(js, fjs);
  }(document, 'script', 'facebook-jssdk'));

  function onLogin(response) {
    document.getElementById('onlogin-count').innerHTML++;
    document.getElementById('login-status').innerHTML = response.status;

    if(response.status === 'connected') {
      var uid = response.authResponse.userID;
      document.getElementById('user-id').innerHTML = uid;
      
      readName();
    }
  }

  function readName() {
    // https://developers.facebook.com/docs/reference/api/user/
    FB.api(
      'https://graph.facebook.com/me',
      'get',
      function(response) {
        document.getElementById("user-count").innerHTML++;
         
        var e = document.getElementById('user-name');
        if(!response) {
          e.innerHTML = '<i>no object</i>';
        } else if(response.error) {
          e.innerHTML = '<i>Error: ' + response.error.message + '</i>';
        } else {
          e.innerHTML = response.name;
        }
      }
    );
  }
</script>

<p>When we are connected to facebook, read the name of the current user and
display it here.  If you would like to read the name of the current user again
or before being connected to facebook, press the "read name" button.</p>

<p>
<!-- https://developers.facebook.com/docs/reference/plugins/login -->
<div><div 
  class="fb-login-button"
  data-scope=""
  data-onlogin="onLogin">
</div></div>

(Status: <span id="login-status"><i>???</i></span>)
(User ID: <span id="user-id"><i>???</i></span>)
(onLogin calls: <span id="onlogin-count">0</span>)
</p>

<p>
<input type="button" value="read name" onclick="readName()">
<span id="user-name"><i>???</i></span>
(readName calls: <span id="user-count">0</span>)
</p>

</body>
</html>

This is my first time working with the Facebook API. I like how all the API calls execute in the background, and not block the browser.

Friday, October 18, 2013

Escape HTML Text

Often when I am putting code examples up I have been manually escaping all the html tag characters to get it into the post. Now I have a piece of JavaScript to do it for me. I will present this as a short, self contained, correct, example.

escape.html:
<!DOCTYPE html>
<html><head><title>Escape HTML Text</title></head><body><form>

<div>text = <br>
<textarea id="text" rows="10" cols="80"></textarea></div>
<div><button type="button" onclick="setText(escapeHtml(getText()))">text = escapeHtml(text)</button></div>
<div><button type="button" onclick="setText(unescapeHtml(getText()))">text = unescapeHtml(text)</button></div>

<script type="text/javascript">
//<![CDATA[
    function getText() { return document.getElementById("text").value; }
    function setText(text) { document.getElementById("text").value = text; }
    function escapeHtml(text) {
        return text.replace(/&/g,"&amp;").replace(/"/g,"&quot;")
            .replace(/</g,"&lt;").replace(/>/g,"&gt;");
    }
    function unescapeHtml(text) {
        return text.replace(/&lt;/g,"<").replace(/&gt;/g,">")
            .replace(/&quot;/g,"\"").replace(/&amp;/g,"&");
    }
//]]>
</script>
</form></body></html>

Friday, October 11, 2013

Multi-media message save format and parsing

I have yet to have a phone that can save multi-media messages, so I took it upon myself to manually save them.

I use a very simple text based key-value format to save them. The keys are free text, and the values may contain arbitrary text, including multiple lines.

small
 value
multi-line
 line 1
 line 2
 line 3

Using this format I just choose some field names for the parts of the message, and separated messages with a field name of "done". Field names may be freely repeated.

type
 incoming
subject
 A Picture/Video Message!
date
 2012-11-27 23:38
from
 +15555555555
 Sender Name
to
 +15656565656
 My Name
text
 the text of the message goes here...
image
 imagejpeg_3.jpg
 5e35003a0e65df1065982c07587cf147ccb76b5d.jpg
done

the first line of a from or to is the number, the second line is the name of the contact. the first line of an image is the file name in the message, the second is the file name of the image in the directory. The other fields names are type, subject, date, text, and slide.

I broke the parsing of the records into usable data structures in Google Apps Script in two phases, the first one extracted the blocks of fields, and the other turned the fields into a data structure.

function forEachRecord(blob, body) {
  var fields = new Array();
  var field = { name: "", value: new Array() };
  
  var lines = blob.getDataAsString().split(/\r?\n/, -1);
  for(var lineIndex = 0; lineIndex < lines.length; lineIndex++) {
    var line = lines[lineIndex];

    // indented lines are the field value
    if(line.substr(0, 1) == " ") {
      field.value.push(line.substr(1));
      continue;
    }
    
    // blank lines are completely ignored
    if(line == "") {
      continue;
    }
    
    // starting a new field
    field = { name: line, value: new Array() };
    
    // records end when a field name of "done" is found
    if(line == "done") {
      body(fields);
      fields = new Array();
      continue;
    }

    // this field is part of the record
    fields.push(field);
  }
  
  if(fields.length) {
    body(fields);
  }
}

function forEachMessage(blob, body) {
  forEachRecord(blob, function(fields) {
    var m = {
      type: "",
      subject: "",
      date: "",
      from: new Array(),
      to: new Array(),
      body: new Array(),
    };
    
    for(var fieldIndex = 0; fieldIndex < fields.length; fieldIndex++) {
      var f = fields[fieldIndex];
      
      if(f.name == "type") {
        m.type = f.value[0];
        continue;
      }
      
      if(f.name == "to") {
        m.to.push({ address: f.value[0], name: f.value[1] });
        continue;
      }
      
      if(f.name == "from") {
        m.from.push({ address: f.value[0], name: f.value[1] });
        continue;
      }
      
      if(f.name == "date") {
        m.date = f.value[0];
        continue;
      }
      
      if(f.name == "image") {
        m.body.push({ type: "image", name: f.value[0], file: f.value[1] });
        continue;
      }
      
      if(f.name == "text") {
        m.body.push({ type: "text", value: f.value.join("\n") });
        continue;
      }
      
      if(f.name == "slide") {
        m.body.push({ type: "slide" });
        continue;
      }
      
      // if we get here we found an unknown field
    }
    
    body(m);
  });
}

This looks similar to a loop in the function that uses it.

forEachMessage(file.getBlob(), function(message) {
  messages.push(message);
});

Turning the parsed messages into an easily human readable output is not difficult from here, and that was my overall objective.

Wednesday, October 9, 2013

LG Neon message parsing

Sometimes I enjoy looking back over very old text messages from my phone. My current phone is an old LG Neon. It can save text messages to a text file on the SD card so I don't loose them when the text message memory on the phone fills up, but can not save the multimedia messages. I desire to keep all of these and view them later. I also decided that I would like to keep the data on Google Drive, and to get some experience with Google Apps Script. This all leads to taking the save files from my phone and making them useful with a Google Apps Script.

The LG Neon saves text messages as UTF-16LE text, however the leading character of the text indicates that they are saved as UTF-16BE. The only character set that is documented to be supported in apps script is UTF-8, so I did the UTF-16 decoding manually in the script.

function decodeLgText(bytes) {
  // The LG Neon saves text messages in UTF-16LE, with the header bytes for UTF-16BE
  
  var str = "";
  for (var i = 2; i < bytes.length; i += 2) {
    var charcode = bytes[i] & 0xff | ((bytes[i+1] & 0xff) << 8);
    if (charcode < 0xd800 || charcode >= 0xe000) {
      str += String.fromCharCode(charcode);
    } else {
      i += 2;
      var charcode1 = bytes[i] & 0xff | ((bytes[i + 1] & 0xff) << 8);
      charcode = 0x10000 + ( ((charcode & 0x3ff) << 10) | (charcode1 & 0x3ff) );
      str += String.fromCharCode(charcode);
    }
  }
  return str;
}

Two blocks of text from the export file looks like this:
1) From : +1555555555(Sample Name)
   Sent : 2013/04/11 17:58
   Contents :
   See you

227) To : +15555555555(Dear Friend)
   Sent : 2013/04/03 20:37
   Contents :
   Back in my very warm fun fur hammock tonight, ther
   e was some pretty snow today, and i get to sleep i
   n a bit tomorrow :)
Sweet dreams! <3

The contents section of the records is interesting, the raw text message is broken up with "\r\n" line breaks, and if there was a line break in the text message it only has a "\n". The record ends with a double "\r\n" line break. The solution to parsing this that I finally settled on is two parts, the first decodes each record into an array of lines, with the indentation and message number stripped off, and the second turns the array of lines into a useful data structure.
function forEachLgBlock(blob, body) {
  var blocks = decodeLgText(blob.getBytes());
  blocks = blocks.split("\r\n\r\n");
  
  for(var blockIndex = 0; blockIndex < blocks.length; blockIndex++) {
    var block = blocks[blockIndex];
    if(block == "") {
      continue;
    }
    
    var lines = block.split("\r\n", -1);
    lines[0] = lines[0].replace(/^\d+\)/, "  ");
    for(var lineIndex = 0; lineIndex < lines.length; lineIndex++) {
      lines[lineIndex] = lines[lineIndex].replace(/^   /, "");
    }
    
    body(lines);
  }
}

function forEachLgSms(blob, body) {
  forEachLgBlock(blob, function(lines) {
    var m = {
      type: "",
      subject: "",
      date: "",
      from: new Array(),
      to: new Array(),
      body: [ { type: "text", value: "" } ]
    };
    
    var lineIndex;
    for(lineIndex = 0; lineIndex < lines.length; lineIndex++) {
      var line = lines[lineIndex];
      var match;

      match = line.match(/^To : (.*?)\((.*?)\)$/);
      if(match) {
        m.to.push([ { address: match[1], name: match[2] } ]);
        m.type = "outgoing";
        continue;
      }
      
      match = line.match(/^To : (.*)$/);
      if(match) {
        m.to.push([ { address: match[1] } ]);
        m.type = "outgoing";
        continue;
      }
      
      match = line.match(/^From : (.*?)\((.*?)\)$/);
      if(match) {
        m.from.push([ { address: match[1], name: match[2] } ]);
        m.type = "incoming";
        continue;
      }
      
      match = line.match(/^From : (.*)$/);
      if(match) {
        m.from.push([ { address: match[1] } ]);
        m.type = "incoming";
        continue;
      }
      
      match = line.match(/^Sent : (\d\d\d\d)\/(\d\d)\/(\d\d) (\d\d:\d\d)$/);
      if(match) {
        m.date = match[1] + "-" + match[2] + "-" + match[3] + " " + match[4];
        continue;
      }
      
      match = line.match(/^Contents :$/);
      if(match) {
        break;
      }
    }

    for(lineIndex++; lineIndex < lines.length; lineIndex++) {
      m.body[0].value = m.body[0].value + lines[lineIndex];
    }
    m.body[0].value.replace(/\n\r/g, "\n");
    
    body(m);
  });
}
In the main program this parser is called with a callback, making it appear similar to a loop.
var messages = new Array();

  // sms messages
  var folder = DriveApp.getFolderById(...);
  var files = folder.getFiles();
  while(files.hasNext()) {
    var file = files.next();
    forEachLgSms(file.getBlob(), function(message) {
      messages.push(message);
    });
  }
The two examples above would parse into this:
[
  {
    type: "incoming",
    subject: "",
    date: "2013-04-11 17:58",
    from: [ { address: "+1555555555", name: "Sample Name" } ],
    to: [ ],
    body: [ { type: "text", value: "See you" } ]
  },
  {
    type: "outgoing",
    subject: "",
    date: "2013-04-03 20:37",
    from: [ ],
    to: [ { address: "+1555555555", name: "Dear Friend" } ],
    body: [ { type: "text", value: "Back in my very warm fun fur hammock tonight, there was some pretty snow today, and i get to sleep in a bit tomorrow :)\nSweet dreams! <3" } ]
  }
]

What I did with the multi-media messages will be covered another time.

Tuesday, January 29, 2013

java regular expression on byte array

Ever wanted to use a regular expresson on a byte array in Java? It turns out that regular expressions are eight bit safe in Java, and bytes can safely map into the lower half of the character type. With a simple adapter it becomes a trivial task. Demonstration:
package org.yi.happy.binary_regex;

import static org.junit.Assert.assertEquals;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.junit.Test;

public class BinaryRegexTest {
    /**
     * Find line endings in a byte array using a regular expression.
     */
    @Test
    public void testExpression() {
        byte[] data = new byte[] { 'a', '\r', '\r', 'c' };
        Pattern p = Pattern.compile("\r\n?|\n\r?");
        Matcher m = p.matcher(new ByteCharSequence(data));

        assertEquals(true, m.find(0));
        assertEquals(1, m.start());
        assertEquals(2, m.end());

        assertEquals(true, m.find(2));
        assertEquals(2, m.start());
        assertEquals(3, m.end());

        assertEquals(false, m.find(3));
    }

    /**
     * Find null bytes in a byte array using a regular expression.
     */
    @Test
    public void testNull() {
        byte[] data = new byte[] { 'a', 0, 'b', 0 };

        Pattern p = Pattern.compile("\0");
        Matcher m = p.matcher(new ByteCharSequence(data));

        assertEquals(true, m.find(0));
        assertEquals(1, m.start());
        assertEquals(2, m.end());

        assertEquals(true, m.find(2));
        assertEquals(3, m.start());
        assertEquals(4, m.end());

        assertEquals(false, m.find(4));
    }
}
And the adapter is as one might expect,
package org.yi.happy.binary_regex;

public class ByteCharSequence implements CharSequence {

    private final byte[] data;
    private final int length;
    private final int offset;

    public ByteCharSequence(byte[] data) {
        this(data, 0, data.length);
    }

    public ByteCharSequence(byte[] data, int offset, int length) {
        this.data = data;
        this.offset = offset;
        this.length = length;
    }

    @Override
    public int length() {
        return this.length;
    }

    @Override
    public char charAt(int index) {
        return (char) (data[offset + index] & 0xff);
    }

    @Override
    public CharSequence subSequence(int start, int end) {
        return new ByteCharSequence(data, offset + start, end - start);
    }

}

Monday, December 6, 2010

wide character text file conversion

My LG Neon phone has a feature where it will save all the text messages as a text file on the memory card, however this is not a nice UTF-8 text file, rater one that is almost UTF-16 with reversed byte order, which confuses the local text editors on my Mac.

Upon investigation of the raw text file I found that the format follows
0xff 0xfe ( char 0x00 )*
where the data I want is the char bytes.

Following this state machine


I wrote a small state machine perl script to convert the file
#!/usr/bin/perl -w
use strict;

my $char;

sub unexpected;
sub read_header_2;

sub read_header_1 {
    read(\*STDIN, $char, 1) or return \&unexpected;
    ord($char) == 0xfe or return \&unexpected;
    return \&read_header_2;
}

sub read_character;

sub read_header_2 {
    read(\*STDIN, $char, 1) or return undef;
    ord($char) == 0xff or return \&unexpected;
    return \&read_character;
}

sub write_character;

sub read_character {
    read(\*STDIN, $char, 1) or return undef;
    return \&write_character;
}

sub read_null;

sub write_character {
    print $char;
    return \&read_null;
}

sub read_null {
    read(\*STDIN, $char, 1) or return undef;
    ord($char) == 0x00 or return \&unexpected;
    return \&read_character;
}

sub unexpected {
    print "unexpected situation\n";
    if(length $char) {
        print "found character: ". ord($char), "\n";
    }
    else {
        print "found enf of stream\n";
    }
    return undef;
}

my $state = \&read_header_1;
while($state) {
    $state = &$state();
}
which worked perfectly, and implemented the state machine directly.

Now I can save my text messages and have them readable.

Friday, December 3, 2010

shell script state machine

The basic structure of a shell script state machine is an endless loop with a case branch structure inside that responds to a current state variable. To end the loop "break" out of the while loop.

state="start"
while true; do
  case "$state" in 
  "start")       
    echo "initial state"
    state="next"
    ;;
  "next") 
    echo "next state"
    state="done"
    ;;
  "done") 
    echo "done"
    break
    ;;
  *)
    echo "invalid state \"$state\""
    break
    ;;
  esac
done

running the above state machine prints
initial state
next state
done

This structure is useful for programs that are easier to express using a flow chart (state chart) than a linear structure, such as an interactive script with retry options.

temporary file space snippet

Often I need a temporary directory for a shell script to do work in, one that gets cleaned up even if the script is aborted by the user with CTRL-C. I spent an hour or so figuring out how to make the cleanup happen reliably and now use the following snippet for the task of making and cleaning up the temporary directory.

tmp=/tmp/$$
mkdir $tmp || exit 1;
cleanup() { rm -r -f "$tmp"; }
trap "cleanup" EXIT

Saturday, November 6, 2010

three implimentations of a stack in C

I decided to write up three implementations of the basic stack data structure in C, complete with tests.

#include <stdio.h>
#include <stdlib.h>

void check(char *message, int success) {
 printf("%s %s\n", success ? "pass" : "FAIL", message);
}

/*
 * array fixed stack. create an array of length one greater than the maximum
 * stack depth, and call aInit on it to set it up.
 */
void aInit(int *stack) {
 stack[0] = 0;
}

void aPush(int *stack, int value) {
 stack[++stack[0]] = value;
}

int aPeek(int *stack) {
 if(stack[0] == 0) {
  return 0;
 }

 return stack[stack[0]];
}

int aPop(int *stack) {
 if(stack[0] == 0) {
  return 0;
 }

 return stack[stack[0]--];
}

int aSize(int *stack) {
 return stack[0];
}

void aPrint(int *stack) {
 int i;

 /* print the stack from top to bottom */
 for(i = stack[0]; i > 0; i--) {
  printf("%d ", stack[i]);
 }
 printf("\n");
}

void test_a() {
 int stack[10];
 aInit(stack);
 check("a size", aSize(stack) == 0);

 aPush(stack, 1);
 aPush(stack, 2);
 check("a size", aSize(stack) == 2);

 check("a peek", aPeek(stack) == 2);
 check("a peek", aPeek(stack) == 2);

 check("a pop", aPop(stack) == 2);
 check("a size", aSize(stack) == 1);

 aPush(stack, 3);
 check("a size", aSize(stack) == 2);
 check("a peek", aPeek(stack) == 3);
}

/*
 * struct fixed stack
 */
struct sStack {
 int size;
 int data[10];
};

void sInit(struct sStack *stack) {
 stack->size = 0;
}

int sSize(struct sStack *stack) {
 return stack->size;
}

void sPush(struct sStack *stack, int value) {
 stack->data[stack->size] = value;
 stack->size += 1;
}

int sPeek(struct sStack *stack) {
 if(stack->size == 0) {
  return 0;
 }

 return stack->data[stack->size - 1];
}

int sPop(struct sStack *stack) {
 if(stack->size == 0) {
  return 0;
 }

 stack->size -= 1;
 return stack->data[stack->size];
}

void sPrint(struct sStack *stack) {
 int i;

 /* print the stack from top to bottom */
 for(i = stack->size; i > 0; i--) {
  printf("%d ", stack->data[i - 1]);
 }
 printf("\n");
}

void test_s() {
 struct sStack stack;
 sInit(&stack);
 check("s size", sSize(&stack) == 0);

 sPush(&stack, 1);
 sPush(&stack, 2);
 check("s size", sSize(&stack) == 2);

 check("s peek", sPeek(&stack) == 2);
 check("s peek", sPeek(&stack) == 2);

 check("s pop", sPop(&stack) == 2);
 check("s size", sSize(&stack) == 1);

 sPush(&stack, 3);
 check("s size", sSize(&stack) == 2);
 check("s peek", sPeek(&stack) == 3);
}

/*
 * linked dynamic stack
 */
struct lNode {
 struct lNode *next;
 int data;
};

struct lStack {
 struct lNode *head;
};

void lInit(struct lStack *stack) {
 stack->head = NULL;
}

int lSize(struct lStack *stack) {
 int size = 0;
 struct lNode *i;

 i = stack->head;
 while(i != NULL) {
  size += 1;
  i = i->next;
 }
 return size;
}

void lPush(struct lStack *stack, int value) {
 struct lNode *head;

 head = (struct lNode*)calloc(1, sizeof(struct lNode));
 head->data = value;
 head->next = stack->head;

 stack->head = head;
}

int lPeek(struct lStack *stack) {
 if(stack->head == NULL) {
  return 0;
 }

 return stack->head->data;
}

int lPop(struct lStack *stack) {
 struct lNode *head;
 int out;

 head = stack->head;

 if(head == NULL) {
  return 0;
 }

 stack->head = head->next;

 out = head->data;

 free(head);

 return out;
}

void lPrint(struct lStack *stack) {
 struct lNode *i;

 for(i = stack->head; i != NULL; i = i->next) {
  printf("%d ", i->data);
 }
 printf("\n");
}

void lDestroy(struct lStack *stack) {
 struct lNode *head;
 struct lNode *next;

 head = stack->head;
 while(head != NULL) {
  next = head->next;
  free(head);
  head = next;
 }
 stack->head = NULL;
}

void test_l() {
 struct lStack stack;
 lInit(&stack);
 check("l size", lSize(&stack) == 0);

 lPush(&stack, 1);
 lPush(&stack, 2);
 check("l size", lSize(&stack) == 2);

 check("l peek", lPeek(&stack) == 2);
 check("l peek", lPeek(&stack) == 2);

 check("l pop", lPop(&stack) == 2);
 check("l size", lSize(&stack) == 1);

 lPush(&stack, 3);
 check("l size", lSize(&stack) == 2);
 check("l peek", lPeek(&stack) == 3);

 lDestroy(&stack);
}

int main() {
 test_a();
 test_s();
 test_l();
 return 0;
}

I hope the code is clean and clear, but there is a good chance that is is not.

Sunday, July 4, 2010

Push API vs Pull API

I have found that Pull APIs are much harder to implement than Push APIs, but often the Pull API is much easier to use.

First I shall show an example of each, suppose I want an API for generating the numbers from 1 to 100.

As a Pull API I get the following.

public class RangePull {
    private int i;

    public RangePull() {
        i = 1;
    }

    public boolean hasNext() {
        return i <= 100;
    }

    public Integer next() {
        int out = i;
        i++;
        return out;
    }

    public static void main(String[] args) {
        RangePull r = new RangePull();
        while (r.hasNext()) {
            Integer i = r.next();
            // do something with i
            System.out.println(i);
        }
    }
}
as a Push API I get the following.
public class RangePush {
    public interface Visitor {
        void accept(int i);
    }

    public void visit(Visitor v) {
        for (int i = 1; i <= 100; i++) {
            v.accept(i);
        }
    }

    public static void main(String[] args) {
        RangePush r = new RangePush();
        r.visit(new Visitor() {
            @Override
            public void accept(int i) {
                // do something with i
                System.out.println(i);
            }
        });
    }
}

The push version is smaller, and in some ways simpler, but the control is inverted so it is not as versatile.

For example, only the pull version can be used to do a side by side comparison of the range generator results, since two of them would be iterating at the same time.

It is also trivial to turn a Pull API into a Push API, by just looping over the result generator and calling back to the visitor.

I have found that the complexity of changing a Push API into a Pull API is braking up the algorithm at the result emitting step, which often requires it to be re-worked as a state-machine. This is especially challenging where the algorithm is recursive.

Friday, May 21, 2010

parallel index searching

I tried searching the archive system index files in parallel (two at a time) and the wall time went down a little bit. This is a sign that the problem is likely IO bound, since there are no locks involved.

real    1m39.428s
user    2m23.523s
sys     0m7.205s

Tuesday, May 18, 2010

Commons-CLI repeated options

I was curious about how repeated options are handled in commons-cli... Here is a test with the conclusion of that curiosity.

    /**
     * An option that may be specified more than once.
     * 
     * @throws ParseException
     */
    @Test
    public void testMultipleInstance() throws ParseException {
        Options o = new Options().addOption("a", "apple", true, "Apples");

        String[] args = { "-a", "one", "-a", "two" };
        CommandLine c = new GnuParser().parse(o, args);

        assertArrayEquals(new String[] { "one", "two" }, c
                .getOptionValues("apple"));

        assertEquals("one", c.getOptionValue("apple"));
    }

Commons-CLI test

I started using commons-cli yesterday, and was curious about the case where an option is given, but not defined. In these situations I end up either writing tests to check the condition, or small example programs to exercise the condition. In this case I wrote a JUnit test.

It turns out that options that are not defined raise an error condition.

package org.yi.happy.archive;

import static org.junit.Assert.assertArrayEquals;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.UnrecognizedOptionException;
import org.junit.Test;

/**
 * Experimental tests for the commons-cli library.
 */
public class CommandLineTest {
    /**
     * Show what happens when an invalid option is given.
     * 
     * @throws ParseException
     */
    @Test(expected = UnrecognizedOptionException.class)
    public void testBadOption() throws ParseException {
        Options o = new Options().addOption("a", "apple", true, "Apples");

        String[] arguments = { "--orange", "5" };
        new GnuParser().parse(o, arguments);
    }

    /**
     * End the argument list to catch an option with dashes.
     * 
     * @throws ParseException
     */
    @Test
    public void testArguments() throws ParseException {
        Options o = new Options().addOption("a", "apple", true, "Apples");

        String[] arguments = { "--", "--orange", "5" };
        CommandLine c = new GnuParser().parse(o, arguments);

        assertArrayEquals(new String[] { "--orange", "5" }, c.getArgs());
    }

}