Tuesday, January 29, 2008

Java Bug Date equals?

Who would have thought that the following test would fail:

private Date getDate14April() {
Calendar calendar = Calendar.getInstance();
calendar.set(2007, 3, 14, 15, 30); // 14 april 15:30
return calendar.getTime();
}

public void testCompareDate() {
Date date = getDate14April();
for (int i=0; i < 100; i++) {
assertEquals(getDate14April, date);
}

Stacktrace (edited brackets):

junit.framework.AssertionFailedError: 60 expected: [Sat Apr 14 15:30:40 CEST 2007] but was:[Sat Apr 14 15:30:40 CEST 2007]
at org.westen.act.document.TestTemplateManager.testCompareDate(TestTemplateManager.java:212)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)


JavaDoc of Date.equals:
Compares two dates for equality.
The result is true if and only if the argument is not null> and is a Date object that represents the same point in time, to the millisecond, as this object.

Hmm, I would have though that the milliseconds would have been initialized to 0 when I create a date object.





Hibernate many-to-many & inverse

I had the problem that two entities A and B had a many-to-many relation but deleting an object a resulted in a constraint violation because the object was used by an object b.

To my surprise fiddling with the inverse="true", inverse="false" and cascade="none" fixed the problem.

My understanding is that deleting an object always deletes the row from the join table. (cascade="none"). Setting cascade to "delete" will also force the deletion of the object(s) on the other side.

However why inverse="true" and "false" make a difference I do not understand. You would assume that specifying one would be enough and imply the other. Apparently there is more to it than I understand. Needs a follow up...

So I'm not the first one to run into this. Found this blog "Hibernates bizarre interpretation of inverse". He basically says that inverse tells Hibernate which side of the relation to ignore. Although this does make complete sense to me, it fits my problem.

Hmmmm

Saturday, January 19, 2008

GWT-EXT

The last few weeks i've been struggling with GWT-Ext 1. Conclusion: the lay-out capabilities of GWT-Ext 1 are poor.

For example a table-layout is not possible. When using a form you can only place a limited number of widgets on it. Separate label widget is not present.

Ext v2 has much richer lay-our support. So we have to wait for GWT-Ext 2....

Wednesday, December 19, 2007

Writing a test to verify ODT content

Sample to test the content of an Open Office document containing three lines ('1234', empty line, 'description'):

import javax.xml.parsers.DocumentBuilderFactory;
...
import com.artofsolving.jodconverter.DefaultDocumentFormatRegistry;
import com.artofsolving.jodconverter.DocumentConverter;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

ByteArrayOutputStream output = templateManager.applyModelToTemplate(inputStream, map);

String content = getZipEntry(new ByteArrayInputStream(output.toByteArray()), "content.xml");
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
Document doc = dbf.newDocumentBuilder().parse(new ByteArrayInputStream(content.getBytes()));

Element docElement = doc.getDocumentElement();
assertEquals("office:document-content", docElement.getTagName());

Element bodyElement = (Element) docElement.getElementsByTagName("text:p").item(0);
assertEquals("1234", bodyElement.getTextContent());
// skip empty line
bodyElement = (Element) docElement.getElementsByTagName("text:p").item(2);
assertEquals("description", bodyElement.getTextContent());

Thursday, December 13, 2007

Getting the entry from a ZipInputStream

Finally I found the code to get an entry from an InputStream that contains zipped data. For a ZipFileInputStream this is easy, but not for a regular input stream because the size of the entry can be unknown (-1) and there is no alternative then just go through the stream.

sorry for the messy mark-up...

disclaimer: code is not optimized!

// based on http://java.sun.com/developer/technicalArticles/Programming/compression/

public static String unzipEntry(InputStream zippedInputStream, String entryName) throws Exception {
String result = null;
final int BUFFER = 2048;
BufferedOutputStream dest = null;
ZipInputStream zis = new ZipInputStream(new BufferedInputStream(zippedInputStream));
ZipEntry entry;
while((entry = zis.getNextEntry()) != null) {
int count;
byte data[] = new byte[BUFFER];
StringOutputStream fos = new StringOutputStream();
dest = new BufferedOutputStream(fos, BUFFER);
while ((count = zis.read(data, 0, BUFFER)) != -1) {
dest.write(data, 0, count);
}
dest.flush();
dest.close();
if (entryName.equals(entry.getName())) {
result = fos.toString();
}
}
zis.close();
return result;
}

public class StringOutputStream extends OutputStream {

// This buffer will contain the stream
protected StringBuffer buf = new StringBuffer();

public StringOutputStream() {}

public void close() {}

public void flush() {}

public void write(byte[] b) {
String str = new String(b);
this.buf.append(str);
}

public void write(byte[] b, int off, int len) {
String str = new String(b, off, len);
this.buf.append(str);
}

public void write(int b) {
String str = Integer.toString(b);
this.buf.append(str);
}

public String toString() {
return buf.toString();
}

public int contains(String string) {
return StringUtils.countOccurrencesOf(buf.toString(), string);
}


}



Pairs

While driving to work I realized something funny:

If I have to work on something, for example produce an article, I will write it, put it away and pick it up a couple of days later to look at it with a fresh mind.

Within our team we also (sometimes) practice pair programming. The idea is that two know and see more than one (the power of interdependence ;-).

Effectively these two practices are one and the same; in the first case you use yourself as the second person. Time will make you a different person than you were. The two practices differ in their usage of the dimensions time and resource. Tradeoffzz..

Monday, December 10, 2007

Red/green/refactor & spikes

I've developed a new way of coding new functionality. In the past I distinguished between spikes and production code development. The spike was meant for prototype code, throwaway code.

Today I do it like this:
I start with a new test that has some basic code that will do the basics of the job i'm after. I can very easily run this test from my IDE (IntelliJ in my case). After a while the test(s) will succeed. This proves that I understand the basics of the code needed. Next step is to move some code to the main folder - to production level. I will use Extract methods refactoring to do this.

This way I slowly, but steadily move from prototype to production without redoing work.