Thursday, December 5, 2013

Simple and super practical note book

All the notebooks I have kept in my bag so far seemed lacking in little ways: I couldn't easily tuck things into them; sometimes finding the next blank page was a challenge; they consume a lot more space than I feel they need to; taking pages out of them quickly became unsightly.

So I started pondering how to make one where I could freely add and remove pages without having an effect on the feel of the note book.

I made the notebook from plastic canvas, and sewed it with strips of shopping bags.  The shell does not care about getting wet, but the pages inside do.


It is just larger than a quarter sheet of letter sized paper, so it is very easy to make new pages as I need them.


The pages are folded over a strip of plastic, so they can be completely rearranged, and anything thin that can be folded can be tucked between any of the pages.  I did not realize in advance that I can always be writing on the top few pages by just moving blank pages up to the top of the stack.  I had printed driving directions on the left side earlier in the week, and now they have been flipped over to be reused as note pages.

In the future I am going to try some graph paper or lined paper in here, but this is also working very well.

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>

Friday, November 8, 2013

YouTube to iTunes

Sometimes I come across a video on YouTube that I would like to turn into a song in iTunes.

When thinking about puzzles, especially programming puzzles, often my thoughts are around what I have, what I want, and how I can bring the haves and wants closer to each other. As I put effort into the ways to bring the haves and wants together I end up with more haves and more options for wants. I am done when I find that I already have what I want.

I have a video on YouTube. I want a song in iTunes. iTunes can import mp3 files as songs, so I can also want a mp3 file.  I also have Chrome, and a freshly installed copy of Mac OSX Snow Leopard.

One option is YouTube to MP3 Converter, but it can only handle videos up to 20 minutes, and I want to convert a longer one.

I found a Chrome plugin that allows downloads of the YouTube media file that gets streamed to the browser, YouTube Options.  After installing that I can also have a .mp4 or .flv video file.

I have a script that I wrote a while ago that turns video files into mp3 files, but to run the script wants ffmpeg installed.  MacPorts has package for ffmpeg, but to install MacPorts it wants Xcode installed. Xcode is on the Snow Leopard Installation DVD, so I install it. Now I can install MacPorts, so I do. Now I can install ffmpeg, so I do (sudo port install ffmpeg) .

Now the script works, so I have what I wanted when I started, almost like magic.

A video on YouTube --> download the video from YouTube  --> convert the downloaded video to mp3 --> import the generated mp3 into iTunes --> a song in iTunes.

Thursday, November 7, 2013

fleece mittens

The mittens I used last winter shrank in the wash, so they did not fit well anymore, so I pondered how to make some out of fleece for a while, made a pattern by tracing another pair of mittens, and made myself some mittens.


They are symmetric so I don't need to figure out which one goes on which hand.

Monday, November 4, 2013

rainbow shoulder bag

One day a month or so ago I decided to make a new rainbow shoulder bag, one with a wider strap that would be more comfortable when I am wearing it for long periods of time. Today I got most of the work done.



The strap is filled with crocheted plastic bags giving it a sturdy padded structure. There are some loops of webbing to hang carabiners for holding things like my water bottle or hanging the bag off my backpack.  This bag is a bit larger than my previous one, I sized it using a mesh bag from the Dollarama.

In the future I will add a waterproof liner to keep the contents dry in the rain, and a zipper for easy closing.

Update: I didn't know at the time, but this bag also works as a backpack, with plenty of space.  I just clip a string across the bag and around the strap to make two spaces to put my arms, and because the strap is so wide it is very comfortable when it is weighted down.






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>

Google Drive Hosting Hello World

Go to Google Drive

Create a folder, I called mine "public html", set the sharing to "Public on the web", in the details for the folder there should now be a link for hosting.  Upload whatever static files you would like into the shared folder.

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.

Monday, July 29, 2013

rainbow water bottle

Just before I left to hitchhike across the country to deliver one of my rainbow lighters to a close friend in a campground in BC, I painted myself a rainbow swirl metal water bottle.


The bottle has served me very well and even has a tendency of finding me when I leave it by itself for a while outside of where I am residing at the moment.

Friday, May 3, 2013

Wool lined hammock

I just finished my wool lined hammock for traveling Sunday...


It feels like like a tent on the inside when zipped up, though the similarity ends at there being a dark floor and light walls and ceiling.


The outside is nice and plain, all windbreaker nylon. That is my tarp tucked to the side.


The brown part is the wool, the rest is light nylon.

The hammock does not take up much room in my bag, and I have taken it down to zero with my heavy clothes on for a night, before the zippers were added, I expect I can sleep in this just past zero now.

Thursday, April 18, 2013

Dehydrated orange cookies

I dehydrated some oranges, but instead of just slicing them thinly, I peeled them and sliced them in four, making round slices.  They took a long time to dry, but they look like and feel like cookies, but taste like orange.  Two cookies makes half an orange.  I also dried the peeling since it is edible.



So yummy, the oranges don't last long like this.

Thursday, March 7, 2013

funky food jar

I had another empty jar taped and ready to paint... This time I decided to put a drop of paint on it, spread it using a brush without overlapping paint that was already on it, change colors, and repeat until it was covered.


updated sunrise jar

I was not feeling pleased with my sunset jar, so I got out the paint and turned it into a nice sunrise jar.


I like this more, but it still feels like something is missing, when I figure out what I will updated it again.

Wednesday, February 13, 2013

wool sweater coat

I decided it was time to try out a wool layer for my rainbow sweater coat, here is what it looks like.  This has been inside the rainbow fleece layer since it was made yesterday.  The fabric is coating weight 80% wool and 20% polyester.


Also, being barefoot in the winter is getting easier with practice.

Thursday, January 31, 2013

painted "Loved" pins

I recently got myself a set of lighter acrylic paints, and had a pack of blank clothespins around, then this happened:


Six colors of paint, many clothespins, and a set of sharpies met up for a party and created something beautiful.

It took me about a day to paint, write on, and re-assemble the clothespins.  The result is so pleasing that I almost don't want to give them away, but the knowledge that I can always make balances that feeling out.

Wednesday, January 30, 2013

simple yellow back bag

One afternoon I decided to stick a strap on my yellow nylon packing bag, and it turned into a very nice back sling bag.


I have everything I need on the road except for hammocks and food in this bag.  I haven't tried walking with this bag for 5km yet, but I expect it would work fine.

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);
    }

}

Sunday, January 27, 2013

More rainbow lighters

I wanted another rainbow lighter, so I went to the hardware store, got a five pack of BIC lighters, taped them, and painted them over a game of risk.


I used someone else's paint for the upper five colours, and I like the shade that I ended up with more than the shade on my existing lighter.

Wednesday, January 23, 2013

Long rainbow fleece scarf

Today I decided that I had spent enough time flattening my single layer rainbow fleece scarf out, so I doubled the thickness and made it longer.  Now it is about 2.25m long and about 25cm wide.  I also did not full close up the cross seams connecting the sections of fleece together, giving me the ability to stick small things in the resulting pockets on the ends of the scarf.


The scarf barely shows up on the rainbow sweater, it is interesting how rainbow blends into rainbow so well.

Rainbow fleece sweater jacket

Yesterday I made another rainbow fleece sweater jacket, for wearing under my rainbow fleece jacket, or over it.  This one has ties instead of buttons and I am not going to put any elastics in the sleeves, so they will stay covering my hands when they fall down.

 

I actually have a scarf on in the picture on the left, but it blends into the outfit extremely well.

Blue flower print sweater

A while ago I made another sweater like the rainbow jacket, using the same pattern, but as an under layer to go with the jacket, it is blue flower printed fleece that formerly was dollar store blankets.  The ties on the front are ribbons.