Friday, February 25, 2011

XmlGregorianCalendar, what you get isn't what you want

Question: What you get when you do such thing?

variable calendar is an instance of XMLGregorianCalendar (year=2011,month=2,day=1, hour, minute and seconds are not set).


Calendar result = Calendar.getInstance();
result.clear();
result.set(calendar.getYear(), calendar.getMonth() - 1, calendar.getDay(), calendar.getHour(), calendar.getMinute(),calendar.getSecond());
result.set(Calendar.MILLISECOND, calendar.getMillisecond());


You might be thinking that you will get:

Year=2011
Month=1 (February)
Day=1
Hour, minute, seconds = 0

Be aware of doing this! While some field in gregorian calendar is not set then it's value is "-2147483648"

So, If you do such a conversion then you endup in 247120 years B.C.E ;-)


Solution:

use method:
calendar.toGregorianCalendar();


Implementation of this method:

if(month != -2147483648)
gregoriancalendar.set(2, month - 1);
if(day != -2147483648)
gregoriancalendar.set(5, day);
if(hour != -2147483648)
gregoriancalendar.set(11, hour);
if(minute != -2147483648)
gregoriancalendar.set(12, minute);
if(second != -2147483648)
gregoriancalendar.set(13, second);
if(fractionalSecond != null)
gregoriancalendar.set(14, getMillisecond());
return gregoriancalendar;


It's a kind of magic :-)

Thursday, February 17, 2011

JAXB, Equals and hashCode methods

I have no need to have a equals and hashCode methods on generated java class yet but I need it now.

It is really simple. You have to use a specific jaxb plugin (in case of Maven) and plugins for this plugin ;-)

- Maven2 plugin: org.jvnet.jaxb2.maven2:maven-jaxb2-plugin
- configuration: -XtoString, -Xequals, -XhashCode
- plugin for Maven2 plugin: org.jvnet.jaxb2_commons:jaxb2-basics

While java classes are being generated this plugin hooks at the process and add equals and hashCode methods. My pom definition:


<plugin>
<groupId>org.jvnet.jaxb2.maven2</groupId>
<artifactId>maven-jaxb2-plugin</artifactId>
<version>0.7.4</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<clearOutputDir>true</clearOutputDir>
<extension>true</extension>
<schemaDirectory>target/schemas</schemaDirectory>

</configuration>
</execution>
</executions>
<configuration>
<args>
<arg>-XtoString</arg>
<arg>-Xequals</arg>
<arg>-XhashCode</arg>
</args>
<plugins>
<plugin>
<groupId>org.jvnet.jaxb2_commons</groupId>
<artifactId>jaxb2-basics</artifactId>
<version>${version.jaxb2-basics}</version>
</plugin>
</plugins>
</configuration>
</plugin>


That's it!

Tuesday, February 15, 2011

JAXB, XJC and unmappable chars

Say we have a XSD contains documentation with special characters:

<xs:annotation>
<xs:documentation>Tady je použita čeština se vším všudy<xs:documentation>
</xs:annotation>

Once you setup maven build and "jaxb2-maven-plugin", goal "xjc" and let JAXB to generate Java classes according to your schema you may endup with following error:

unmappable character for encoding UTF-8


Well, you have schema in UTF8 already, you have setup maven build to use UTF8, so why it says that some characters are not in UTF8?

I've found solution described here.

Reason

com.sun.codemodel.writer.CodeWriter#openSource uses the OutputStreamWriter encoding to set the CharsetEncoder to use :

{ OutputStreamWriter bw = new OutputStreamWriter(openBinary(pkg,fileName)); (...) CharsetEncoder encoder = EncoderFactory.createEncoder(bw.getEncoding()); }

It SHOULD instead build a CharsetEncoder based on the user-requested encoding (may fallback to default platform encoding) and THEN create the OutputStreamWriter with this encoder...

{ CharsetEncoder encoder = EncoderFactory.createEncoder( getUserDefinedEncoding() ); OutputStreamWriter bw = new OutputStreamWriter(openBinary(pkg,fileName), encoder); }


Solution

You have to setup build to use native encoding for your OS.

I've added profile (for mac):

<profile>
<activation>
<os>
<family>mac</family>
</os>
</activation>

<properties>
<project.build.sourceEncoding>MacRoman</project.build.sourceEncoding>
</properties>
</profile>