tag:blogger.com,1999:blog-48361098462755616342024-02-19T16:48:49.367+01:00cafebab3.blogspot.comDaniel Dietrich's Bloghttp://www.blogger.com/profile/02492628698632995681noreply@blogger.comBlogger13125tag:blogger.com,1999:blog-4836109846275561634.post-27571052202056463522012-03-11T01:07:00.001+01:002012-03-11T01:07:21.271+01:00Hello danieldietrich.net<b>This blog moved to <a href="http://danieldietrich.net">danieldietrich.net</a>.</b>Daniel Dietrich's Bloghttp://www.blogger.com/profile/02492628698632995681noreply@blogger.com2tag:blogger.com,1999:blog-4836109846275561634.post-9194995682236558512011-01-15T06:14:00.000+01:002011-01-15T06:14:35.432+01:00GWT Editor Framework: @Identity Path<p>
The new GWT 2.1 features are a pleasure to use! But the following additional feature of the
<a href="http://code.google.com/intl/de-DE/webtoolkit/doc/trunk/DevGuideUiEditors.html">Editor framework</a>
would be great:
</p>
<p>
Imagine the situation you have to edit a
<a href="http://code.google.com/intl/de-DE/webtoolkit/doc/trunk/DevGuideUiEditors.html#Very_large_objects">very large object</a>
and there is the need that the
<a href="http://code.google.com/intl/de-DE/webtoolkit/doc/trunk/DevGuideUiEditors.html#Driver_types">Driver</a>
passes the current value to sub editors when traversing the tree.
Then this would be a solution:
</p>
<!-- START:RBOX3 -->
<div class="rbox3">
<div class="rbox3 topleft">
<div class="rbox3 topright">
<div class="rbox3 top"></div>
</div>
</div>
<div class="rbox3 left">
<div class="rbox3 right">
<div class="rbox3 body">
<script type="syntaxhighlighter" class="brush: java; highlight: [3, 11, 20, 22, 28, 36]">
@Entity
public class Profile {
@Transient Profile identity = this;
String name;
String city;
// ...
}
@ProxyFor(Profile.class)
public interface ProfileProxy extends EntityProxy {
ProfileProxy getIdentity();
String getName();
void setName(String name);
String getCity();
void setCity(String city);
// ...
}
class ProfileEditor extends Composite implements Editor<ProfileProxy> {
@Path("identity")
PersonEditor personEditor;
@Path("identity")
AddressEditor addressEditor;
// ...
}
class PersonEditor extends Composite
implements Editor<ProfileProxy> {
TextBox nameEditor = new TextBox();
public PersonEditor() {
initWidget(nameEditor);
}
}
class AddressEditor extends Composite
implements Editor<ProfileProxy> {
TextBox cityEditor = new TextBox();
public AddressEditor() {
initWidget(cityEditor);
}
}
</script>
</div>
</div>
</div>
<div class="rbox3 bottomleft">
<div class="rbox3 bottomright">
<div class="rbox3 bottom"></div>
</div>
</div>
</div>
<!-- END:RBOX3 -->
<p>
Instead it would be nice to have an @Identity annotation:
</p>
<!-- START:RBOX3 -->
<div class="rbox3">
<div class="rbox3 topleft">
<div class="rbox3 topright">
<div class="rbox3 top"></div>
</div>
</div>
<div class="rbox3 left">
<div class="rbox3 right">
<div class="rbox3 body">
<script type="syntaxhighlighter" class="brush: java; highlight: [3, 11, 20, 22]">
@Entity
public class Profile {
// no need for transient identity attribute
String name;
String city;
// ...
}
@ProxyFor(Profile.class)
public interface ProfileProxy extends EntityProxy {
// no need for identity getter
String getName();
void setName(String name);
String getCity();
void setCity(String city);
// ...
}
class ProfileEditor extends Composite implements Editor<ProfileProxy> {
@Identity
PersonEditor personEditor;
@Identity
AddressEditor addressEditor;
// ...
}
</script>
</div>
</div>
</div>
<div class="rbox3 bottomleft">
<div class="rbox3 bottomright">
<div class="rbox3 bottom"></div>
</div>
</div>
</div>
<!-- END:RBOX3 -->
<p>
That would be nice :)
</p>Daniel Dietrich's Bloghttp://www.blogger.com/profile/02492628698632995681noreply@blogger.com3tag:blogger.com,1999:blog-4836109846275561634.post-49141126839223926162010-01-20T00:19:00.005+01:002010-01-20T01:08:29.964+01:00Non-intrusive GWT 2 mod [2nd part]<p>
As mentioned in the last post I want to describe how to register custom element
parsers for GWT 2.0 ui binding.
I use a Java Annotation <tt>@ElementParserToUse(className="<i>your.Parser</i>")</tt>
(as <a href="http://groups.google.com/group/google-web-toolkit-contributors/browse_thread/thread/4d79f729030527c5">suggested</a>
a few days ago by George Georgopoulos (ggeorg)). This Annotation is added to
custom widgets which should be parsed in a custom way.
</p>
<!-- START:RBOX3 -->
<div class="rbox3">
<div class="rbox3 topleft">
<div class="rbox3 topright">
<div class="rbox3 top"></div>
</div>
</div>
<div class="rbox3 left">
<div class="rbox3 right">
<div class="rbox3 body">
<script type="syntaxhighlighter" class="brush: java">
public @interface ElementParserToUse {
String className();
}
</script>
</div>
</div>
</div>
<div class="rbox3 bottomleft">
<div class="rbox3 bottomright">
<div class="rbox3 bottom"></div>
</div>
</div>
</div>
<!-- END:RBOX3 -->
<p>
Element Parsers are registered on-demand (if not registered yet) by
adding the highlighted lines to <tt>UiBinderWriter.getParserForClass(JClassType uiClass)</tt>:
</p>
<!-- START:RBOX3 -->
<div class="rbox3">
<div class="rbox3 topleft">
<div class="rbox3 topright">
<div class="rbox3 top"></div>
</div>
</div>
<div class="rbox3 left">
<div class="rbox3 right">
<div class="rbox3 body">
<script type="syntaxhighlighter" class="brush: java; highlight: [5, 6, 10]">
private Class<? extends ElementParser> getParserForClass(JClassType uiClass) {
// Find the associated parser.
String uiClassName = uiClass.getQualifiedSourceName();
String parserClassName = elementParsers.get(uiClassName);
if (parserClassName == null) {
parserClassName = getAnnotatedParserForClass(uiClass);
if (parserClassName == null) {
return null;
}
}
// And instantiate it.
try {
return Class.forName(parserClassName).asSubclass(ElementParser.class);
} catch (ClassNotFoundException e) {
throw new RuntimeException("Unable to instantiate parser", e);
} catch (ClassCastException e) {
throw new RuntimeException(parserClassName + " must extend ElementParser");
}
}
</script>
</div>
</div>
</div>
<div class="rbox3 bottomleft">
<div class="rbox3 bottomright">
<div class="rbox3 bottom"></div>
</div>
</div>
</div>
<!-- END:RBOX3 -->
<p>
Additionally the following method has to be added to <tt>UiBinderWriter</tt>:
</p>
<!-- START:RBOX3 -->
<div class="rbox3">
<div class="rbox3 topleft">
<div class="rbox3 topright">
<div class="rbox3 top"></div>
</div>
</div>
<div class="rbox3 left">
<div class="rbox3 right">
<div class="rbox3 body">
<script type="syntaxhighlighter" class="brush: java">
private String getAnnotatedParserForClass(JClassType uiClass) {
String parserClassName = null;
if (uiClass.isAnnotationPresent(ElementParserToUse.class)) {
String uiClassName = uiClass.getQualifiedSourceName();
parserClassName = uiClass.getAnnotation(ElementParserToUse.class).className();
elementParsers.put(uiClassName, parserClassName);
}
return parserClassName;
}
</script>
</div>
</div>
</div>
<div class="rbox3 bottomleft">
<div class="rbox3 bottomright">
<div class="rbox3 bottom"></div>
</div>
</div>
</div>
<!-- END:RBOX3 -->
<h1>Note</h1>
<p>
The method <tt>getParserForClass(JClassType uiClass)</tt> is called
within <tt>getParser<b>s</b>ForClass(JClassType type)</tt> which iterates
the class hierarchy of the current Widget (corresponding to the
current .ui.xml element). Because of the class hierarchy is created
by a breadth-first search algorithm it is ensured that parsers
are returned from most specific to most general (in oo manner).
That means that custom parsers do their job first. That's necessary
because xml artifacts are consumed by the ui binder framework (at
least they should be) and so there's no second chance to parse them.
</p>
<p>
In the next post [3rd part] I will introduce custom parsers for the Ext GWT Widgets
shown in the post <a href="http://cafebab3.blogspot.com/2009/12/gwt-2-declarative-layout-beyond.html">GWT 2 Declarative Layout: Beyond UIBinder</a>.
</p>Daniel Dietrich's Bloghttp://www.blogger.com/profile/02492628698632995681noreply@blogger.com1tag:blogger.com,1999:blog-4836109846275561634.post-36914372937026815692010-01-18T09:49:00.012+01:002010-01-24T15:26:02.781+01:00Non-intrusive GWT 2 mod [1st part]<p>
With the following modification 3rd party widget libraries are enabled to leverage
GWT's new declarative ui binding feature. The goal is to add custom element parsers
to the GWT framework. These parsers are responsible for generating java code from
xml representations of layouts based on GWT widgets. The ability to add custom
parsers is the key to ensure correct java code generation of (complex) custom
widgets.
</p>
<p>
Currently the parsers are held in a private variable in UiBinderWriter. There's
no way to access it, so UiBinderWriter has to be modified in some way. Rewriting
UiBinderWriter and overwriting it in the corresponding jar (gwt-user.jar) is a brutal
way which I don't recommend. The main reason is that all GWT applications based on
that jar are affected. The following how-to describes a non-intrusive method which
applies the modification at runtime (in memory).
</p>
<p>Table of contents:</p>
<ol>
<li>Hook into UiBinder</li>
<li>Create custom Generator</li>
<li>Modify UiBinderWriter</li>
<li>Note</li>
</ol>
<h1>Hook into UiBinder</h1>
<p>
GWT generates an implementation of UiBinder at runtime which is specific for
the root widget of the .ui.xml and the java class which is aware of the widget
instance. Here's the well known code which triggers the code generation:
</p>
<!-- START:RBOX3 -->
<div class="rbox3">
<div class="rbox3 topleft">
<div class="rbox3 topright">
<div class="rbox3 top"></div>
</div>
</div>
<div class="rbox3 left">
<div class="rbox3 right">
<div class="rbox3 body">
<script type="syntaxhighlighter" class="brush: java">
public class Example extends EntryPoint {
interface Binder extends UiBinder<CustomWidget, Example> { }
private static final Binder binder = GWT.create(Binder.class);
public void onModuleLoad() {
// create the UI defined in Example.ui.xml
CustomWidget ui = binder.createAndBindUi(this);
RootLayoutPanel.get().add(ui);
}
}
</script>
</div>
</div>
</div>
<div class="rbox3 bottomleft">
<div class="rbox3 bottomright">
<div class="rbox3 bottom"></div>
</div>
</div>
</div>
<!-- END:RBOX3 -->
<p>
The code generator UiBinderGenerator instantiates UiBinderWriter which is aware
of the mentioned element parsers. So replacing UiBinderGenerator is a way to
also replace UiBinderWriter. Because GWT offers the ability to hook in custom
generators within the GWT module configuration, com.google.gwt.uibinder.rebind.UiBinderGenerator
can be overwritten with a custom one (here: cafebab3.rebind.custom.UiBinderGenerator).
The GWT.create(...) method is taking care of this.
Corresponding snippet of the main .gwt.xml:
</p>
<!-- START:RBOX3 -->
<div class="rbox3">
<div class="rbox3 topleft">
<div class="rbox3 topright">
<div class="rbox3 top"></div>
</div>
</div>
<div class="rbox3 left">
<div class="rbox3 right">
<div class="rbox3 body">
<script type="syntaxhighlighter" class="brush: xml">
<generate-with class="cafebab3.rebind.custom.UiBinderGenerator">
<when-type-assignable class="com.google.gwt.uibinder.client.UiBinder"/>
</generate-with>
</script>
</div>
</div>
</div>
<div class="rbox3 bottomleft">
<div class="rbox3 bottomright">
<div class="rbox3 bottom"></div>
</div>
</div>
</div>
<!-- END:RBOX3 -->
<p>
As a result GWT binds every .ui.xml via cafebab3.rebind.custom.UiBinderGenerator.
</p>
<h1>Create custom Generator</h1>
<p>
I didn't want to re-implement the whole UiBinderGenerator. Instead I use
delegation where the delegate is the original UiBinderGenerator. The 'trick'
is to load the delegate with a custom ClassLoader which returns a modified
UiBinderWriter class if it is asked for the original one. We know it is
asked eventually because the original UiBinderGenerator instantiates one.
</p>
<!-- START:RBOX3 -->
<div class="rbox3">
<div class="rbox3 topleft">
<div class="rbox3 topright">
<div class="rbox3 top"></div>
</div>
</div>
<div class="rbox3 left">
<div class="rbox3 right">
<div class="rbox3 body">
<script type="syntaxhighlighter" class="brush: java">
// this is cafebab3.rebind.custom.UiBinderGenerator
public class UiBinderGenerator extends Generator {
private Generator delegate;
public UiBinderGenerator() throws Exception {
delegate = (Generator) new UiBinderClassLoader()
.loadClass("com.google.gwt.uibinder.rebind.UiBinderGenerator")
.newInstance();
}
@Override
public String generate(TreeLogger logger, GeneratorContext genCtx, String fqInterfaceName) throws UnableToCompleteException {
return delegate.generate(logger, genCtx, fqInterfaceName);
}
}
</script>
</div>
</div>
</div>
<div class="rbox3 bottomleft">
<div class="rbox3 bottomright">
<div class="rbox3 bottom"></div>
</div>
</div>
</div>
<!-- END:RBOX3 -->
<p>
Here's the (one hell of a) ClassLoader. It needs the modified UiBinderWriter.class
to be renamed to UiBinderWriter.bytecode and placed in the same package.<br>
<b>UPDATE:</b> Fixed an issue with requesting specific classes several times.
</p>
<!-- START:RBOX3 -->
<div class="rbox3">
<div class="rbox3 topleft">
<div class="rbox3 topright">
<div class="rbox3 top"></div>
</div>
</div>
<div class="rbox3 left">
<div class="rbox3 right">
<div class="rbox3 body">
<script type="syntaxhighlighter" class="brush: java">
// this is cafebab3.rebind.custom.UiBinderClassLoader
class UiBinderClassLoader extends ClassLoader {
UiBinderClassLoader() {
super(UiBinderClassLoader.class.getClassLoader());
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
Class<?> result = null;
try {
// entry point
if ("com.google.gwt.uibinder.rebind.UiBinderGenerator".equals(name)) {
result = findLoadedClass(name);
if (result == null) {
byte[] bytecode = Bytecode.loadClass(name).bytecode;
result = defineClass(name, bytecode, 0, bytecode.length);
}
}
// return modified UiBinderWriter
else if ("com.google.gwt.uibinder.rebind.UiBinderWriter".equals(name)) {
result = _findLoadedClass(name);
if (result == null) {
byte[] bytecode = Bytecode.loadResource(
"/de/fs/prototype/rebind/UiBinderWriter.bytecode").unlock().bytecode;
result = _defineClass(name, bytecode);
}
}
// return already loaded classes or create them
else {
result = _findLoadedClass(name);
if (result == null) {
byte[] bytecode = Bytecode.loadClass(name).unlock().bytecode;
result = _defineClass(name, bytecode);
}
}
} catch (Exception x) {
throw new ClassNotFoundException("cannot load class " + name, x);
}
return result;
}
private Class<?> _defineClass(String name, byte[] bytecode) throws Exception {
Method m = ClassLoader.class.getDeclaredMethod("defineClass", String.class,
byte[].class, int.class, int.class);
m.setAccessible(true);
return (Class<?>) m.invoke(getParent(), name, bytecode, 0, bytecode.length);
}
private Class<?> _findLoadedClass(String name) throws Exception {
Method m = ClassLoader.class.getDeclaredMethod("findLoadedClass",
String.class);
m.setAccessible(true);
return (Class<?>) m.invoke(getParent(), name);
}
}
</script>
</div>
</div>
</div>
<div class="rbox3 bottomleft">
<div class="rbox3 bottomright">
<div class="rbox3 bottom"></div>
</div>
</div>
</div>
<!-- END:RBOX3 -->
<p>
The *magic* Bytecode class used in the UiBinderClassLoader is needed to locate all
classes, fields and methods with default visibility (= package private visibility)
(or which are protected) and make them public (= unlock them).
This is necessary because two classes A and B with default visiblity, which are
in the same package, don't see each other if they are loaded by different
ClassLoaders (internally the ClassLoader is part of the namespace in addition to
the package).
I thought about using an existing bytecode manipulation library like
<a href="http://adm.ow2.org">ASM</a>
but these libraries are huge (asm.jar > 40 mb) and so I decided to implement my
own one.
The result is a tiny class which only modifies access flags. This 'unlocking'
of bytecode takes round about 10 ms when called first time, and 0 to 1 ms when
called subsequently (tested on my notebook).
</p>
<!-- START:RBOX3 -->
<div class="rbox3">
<div class="rbox3 topleft">
<div class="rbox3 topright">
<div class="rbox3 top"></div>
</div>
</div>
<div class="rbox3 left">
<div class="rbox3 right">
<div class="rbox3 body">
<script type="syntaxhighlighter" class="brush: java">
// this is cafebab3.rebind.custom.Bytecode
class Bytecode {
// TEST -->
public void main(String[] argv) throws Throwable {
Bytecode.loadClass(ClassLoader.class.getName()).unlock();
}
// <-- TEST
final byte[] bytecode;
private Bytecode(byte[] bytecode) {
this.bytecode = bytecode;
}
public static Bytecode loadClass(String binaryName) throws IOException {
String resource = "/" + binaryName.replace(".", "/") + ".class";
return loadResource(resource);
}
public static Bytecode loadResource(String resource) throws IOException {
InputStream in = Bytecode.class.getResourceAsStream(resource);
ByteArrayOutputStream buf = new ByteArrayOutputStream();
int read;
while ((read = in.read()) != -1) {
buf.write(read);
}
in.close();
return new Bytecode(buf.toByteArray());
}
public Bytecode unlock() throws IOException {
// init
final int[] index = new int[] {0};
DataInputStream in = new DataInputStream(new InputStream() {
@Override public int read() throws IOException {
return (index[0] < bytecode.length) ? (bytecode[index[0]++] & 0xff) : -1;
}
});
DataOutputStream out = new DataOutputStream(new OutputStream() {
@Override public void write(int b) throws IOException {
bytecode[index[0]++] = (byte) (b & 0xff);
}
});
// header
if (in.readInt() != 0xCAFEBABE) {
throw new ClassFormatError("invalid magic number");
}
index[0] += 4;
// constant pool
int count = in.readUnsignedShort() - 1;
for (int i = 1; i <= count; i++) {
byte tag = in.readByte();
switch (tag) {
case 1: index[0] += in.readUnsignedShort() + 2; break;
case 3:
case 4: index[0] += 4; break;
case 5:
case 6: index[0] += 8; i++; break;
case 7:
case 8: index[0] += 2; break;
case 9:
case 10:
case 11:
case 12: index[0] += 4; break;
default: throw new IllegalStateException("unknown tag: " + tag);
}
}
// class info
setAccessFlags(index, in, out, 0x0001, 0x0001, 0x0000); // !PUBLIC => SET PUBLIC
index[0] += 4; // this, super
int interfaceCount = in.readUnsignedShort();
index[0] += 2 * interfaceCount;
for (int i = 0; i < 2; i++) { // i=0: fields, i=1: methods
count = in.readUnsignedShort();
for (int j = 0; j < count; j++) {
setAccessFlags(index, in, out, 0x0003, 0x0001, 0x0004); // !PUBLIC & !PRIVATE => SET PUBLIC & DEL PROTECTED
index[0] += 4;
readAttributes(index, in);
}
}
// attribute info
readAttributes(index, in);
if (index[0] != bytecode.length) {
throw new IllegalStateException("end of bytecode not reached");
}
return this;
}
private void setAccessFlags(int[] index, DataInputStream in, DataOutputStream out, int testFlags, int setFlags, int removeFlags) throws IOException {
int accessFlags = in.readUnsignedShort();
if ((accessFlags & testFlags) == 0) {
accessFlags |= setFlags;
accessFlags ^= accessFlags & removeFlags;
index[0] -= 2;
out.writeShort(accessFlags);
}
}
private void readAttributes(int[] index, DataInputStream in) throws IOException {
int count = in.readUnsignedShort();
for (int i = 0; i < count; i++) {
index[0] += 2;
index[0] += in.readInt() + 4;
}
}
}
</script>
</div>
</div>
</div>
<div class="rbox3 bottomleft">
<div class="rbox3 bottomright">
<div class="rbox3 bottom"></div>
</div>
</div>
</div>
<!-- END:RBOX3 -->
<p>
Caution: Bytecode manipulation should be considered as a hack. Changing the visibility
of classes, methods or fields may also be a change of the semantic of the java program
(if it uses reflection for example).
</p>
<h1>Modify UiBinderWriter</h1>
<p>
All the steps above are nice but not necessary if the modified UiBinderWriter is
copied directly into the corresponding jar (gwt-user.jar) which I decided to be
no good idea because it is intrusive.
</p>
<p>
Recalling that the goal is to supply custom element parsers.
At compile time the custom 3rd party widget parsers are not known by the GWT framework.
My preferred solution is to annotate widgets with a hint which parser to use.
When initializing the pool of parsers, all packages have to be searched for Widgets.
Finding such an annotation containing a parser hint this hint will be taken into account.
</p>
<p>
The implementation of the UiBinderWriter modification will be shown in my next post.
Additionally I will give an example for ui binding GXT widgets. Stay tuned!
</p>
<h1>Note</h1>
<p>
Manually copying the modified UiBinderWriter.class is crude.
A simple way to provide the file UiBinderWriter.bytecode is to create an
additional project (next to the GWT project), including the GWT libraries
and the modified UiBinderWriter (in the package com.google.gwt.uibinder.rebind).
Deployment to the GWT project can be done like this:
</p>
<!-- START:RBOX3 -->
<div class="rbox3">
<div class="rbox3 topleft">
<div class="rbox3 topright">
<div class="rbox3 top"></div>
</div>
</div>
<div class="rbox3 left">
<div class="rbox3 right">
<div class="rbox3 body">
<script type="syntaxhighlighter" class="brush: java">
public class Build {
public static void main(String[] argv) throws IOException {
copy("./bin/com/google/gwt/uibinder/rebind/UiBinderWriter.class",
"../ProjectGwt/src/cafebab3/rebind/custom/UiBinderWriter.bytecode");
}
private static void copy(String src, String dest) throws IOException {
/*DEBUG*/System.out.println("copying " + src + " to " + dest);
InputStream in = new FileInputStream(src);
OutputStream out = new FileOutputStream(dest);
int b;
while ((b = in.read()) != -1) {
out.write(b);
}
out.close();
in.close();
}
}
</script>
</div>
</div>
</div>
<div class="rbox3 bottomleft">
<div class="rbox3 bottomright">
<div class="rbox3 bottom"></div>
</div>
</div>
</div>
<!-- END:RBOX3 -->
<p>
Don't forget to refresh the workspace when manually copying files into it!
</p>Daniel Dietrich's Bloghttp://www.blogger.com/profile/02492628698632995681noreply@blogger.com3tag:blogger.com,1999:blog-4836109846275561634.post-52643386042974843512009-12-31T15:05:00.005+01:002009-12-31T15:52:41.961+01:00Denmark 09/10<p>Greetings from Denmark and best wishes for 2010!</p>
<p>
<img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiOd5U5oY5hNKESqZ1yd_tDRXE0X93G2XUPe-Kf8UdxonK4sSZYOSbzH_6xJUpiDmPv9th3ymMHHTz9lOHTsirKiHRSwoG6FcCDDV8qk4-Of-JZQz-RQdXSY4CnSxAUeIq10c3F23yiu0Vu/"/>
</p>
<p>
<img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjsUSQseYpgPhKv2bJuNrEV0WYQaFjtf5_07oMGdoCKaLhyphenhyphen7YNkdaYbzo5OC21huTBUQow4F1qeLFcxgcobj1AeoIc1hyphenhyphenH0cKM1setBP63g1xtg7IwpVoUtfLR28AE74VWhnJZwBcyeR3-b/s512/DSC_0107-Bearbeitet.jpg"/>
</p>Daniel Dietrich's Bloghttp://www.blogger.com/profile/02492628698632995681noreply@blogger.com4tag:blogger.com,1999:blog-4836109846275561634.post-77704654576369263102009-12-20T01:50:00.002+01:002009-12-20T01:52:49.735+01:00[UPDATE] GWT 2 Declarative Layout<p>
Studying the Google-Web-Toolkit code led me to the method <tt>UiBinderWriter.registerParsers()</tt>:
</p>
<!-- START:RBOX3 -->
<div class="rbox3">
<div class="rbox3 topleft">
<div class="rbox3 topright">
<div class="rbox3 top"></div>
</div>
</div>
<div class="rbox3 left">
<div class="rbox3 right">
<div class="rbox3 body">
<script type="syntaxhighlighter" class="brush: java; highlight: [2, 3]">
private void registerParsers() {
// TODO(rjrjr): Allow third-party parsers to register themselves
// automagically
addElementParser("com.google.gwt.dom.client.Element",
"com.google.gwt.uibinder.elementparsers.DomElementParser");
// Register widget parsers.
addWidgetParser("UIObject");
addWidgetParser("HasText");
addWidgetParser("HasHTML");
addWidgetParser("HasWidgets");
addWidgetParser("HTMLPanel");
addWidgetParser("DockPanel");
addWidgetParser("StackPanel");
addWidgetParser("DisclosurePanel");
addWidgetParser("TabPanel");
addWidgetParser("MenuItem");
addWidgetParser("MenuBar");
addWidgetParser("RadioButton");
addWidgetParser("CellPanel");
addWidgetParser("CustomButton");
addWidgetParser("DialogBox");
addWidgetParser("LayoutPanel");
addWidgetParser("DockLayoutPanel");
addWidgetParser("StackLayoutPanel");
addWidgetParser("TabLayoutPanel");
}
</script>
</div>
</div>
</div>
<div class="rbox3 bottomleft">
<div class="rbox3 bottomright">
<div class="rbox3 bottom"></div>
</div>
</div>
</div>
<!-- END:RBOX3 -->
<p>
Ray Ryan (rjrjr) seems to already have the infrastructur for plugging additional parsers into the ui binder.
I'll go deeper into this...
</p>Daniel Dietrich's Bloghttp://www.blogger.com/profile/02492628698632995681noreply@blogger.com0tag:blogger.com,1999:blog-4836109846275561634.post-66616831624581890582009-12-19T19:11:00.029+01:002010-01-19T23:29:13.508+01:00GWT 2 Declarative Layout: Beyond UiBinder<h1>Google Web Toolkit (GWT) 2 brings Declarative Layout</h1>
<p>
Google brought with GWT 2.0.0 a new major release of
<a href="http://code.google.com/intl/de-DE/webtoolkit/overview.html">Google Web Toolkit (GWT)</a>
to us some days ago. This toolkit enables java guys (including girls!) to write rich internet applications based
on javascript and ajax.<br>
The version 2 of the toolkit is bundled with great new
<a href="http://code.google.com/intl/de-DE/webtoolkit/doc/latest/ReleaseNotes.html">tools and features</a>
(also have a look <a href="http://timepedia.blogspot.com/2009/12/gwt-20-so-good-its-ridiculous.html">here</a>).
One of these features is
<a href="http://code.google.com/intl/de-DE/webtoolkit/doc/latest/DevGuideUiBinder.html">declarative layout with UiBinder</a>.
UiBinder "now allows you to create user interfaces declaratively in XML instead of having to assemble them programmatically".
Here's an example...
</p>
<h2>Programmatical approach (<i>common style</i>):</h2>
<!-- START:RBOX3 -->
<div class="rbox3">
<div class="rbox3 topleft">
<div class="rbox3 topright">
<div class="rbox3 top"></div>
</div>
</div>
<div class="rbox3 left">
<div class="rbox3 right">
<div class="rbox3 body">
<script type="syntaxhighlighter" class="brush: java">
SplitLayoutPanel panel = new SplitLayoutPanel();
Label westComp = new Label();
westComp.setText("west");
panel.addWest(westComp, 200.0d);
Label centerComp = new Label();
centerComp.setText("center");
panel.add(centerComp);
</script>
</div>
</div>
</div>
<div class="rbox3 bottomleft">
<div class="rbox3 bottomright">
<div class="rbox3 bottom"></div>
</div>
</div>
</div>
<!-- END:RBOX3 -->
<p>
<i>
*) With the use of setters instead of constructor arguments I intend
to understate the nature of layouting gwt applications programmatically.
</i>
</p>
<h2>Corresponding declarative code (<i>new style</i>):</h2>
<!-- START:RBOX3 -->
<div class="rbox3">
<div class="rbox3 topleft">
<div class="rbox3 topright">
<div class="rbox3 top"></div>
</div>
</div>
<div class="rbox3 left">
<div class="rbox3 right">
<div class="rbox3 body">
<script type="syntaxhighlighter" class="brush: xml">
<g:SplitLayoutPanel>
<g:west size='200'>
<g:Label text='west'/>
</g:west>
<g:center>
<g:Label text='center'/>
</g:center>
</g:SplitLayoutPanel>
</script>
</div>
</div>
</div>
<div class="rbox3 bottomleft">
<div class="rbox3 bottomright">
<div class="rbox3 bottom"></div>
</div>
</div>
</div>
<!-- END:RBOX3 -->
<p>
In real-world projects your program will be a mixture of the two approaches.
The xml based ui declarations will unclutter your code and separate
the layout from the logic. Because xml is static (like a plain html page)
the declared Widgets (or UIObjects in general) can be injected into java fields
and dynamically modified at runtime. Because GWT translates your code
into javascript, the modifications will occur at the client side.
Sounds easy - and that's what it is. Thank you, Google!
</p>
<h1>Do 3rd Party GWT Libraries work with GWT 2?</h1>
<p>
Yes and no. Considering the programmatical point of view all should work fine.
The only point of interest is the case where third party Widgets are build on
top of standard GWT Widgets which were changed with the new version in some way
(UPDATE: this is theory - I don't know if there are api changes).
This should be a minor problem because if the third party library is a good
choice then it will be updated soon. If not it should be replaced by a good
choice ;-)
</p>
<p>
The more interesting point of view is the one where declarative layouts are
considered. GWT 2 ui declarations are allowing the import and usage of 3rd
party Widgets. For example let's substitute the center component of the above
standard GWT example with a DatePicker of the Ext GWT (GXT) 2.1 library.
You see the complete ui.xml because the use of namespaces is essential here:
</p>
<!-- START:RBOX3 -->
<div class="rbox3">
<div class="rbox3 topleft">
<div class="rbox3 topright">
<div class="rbox3 top"></div>
</div>
</div>
<div class="rbox3 left">
<div class="rbox3 right">
<div class="rbox3 body">
<script type="syntaxhighlighter" class="brush: xml; highlight: [4, 11]">
<ui:UiBinder
xmlns:ui='urn:ui:com.google.gwt.uibinder'
xmlns:g='urn:import:com.google.gwt.user.client.ui'
xmlns:x='urn:import:com.extjs.gxt.ui.client.widget'>
<g:SplitLayoutPanel>
<g:west size='200'>
<g:Label text='west'/>
</g:west>
<g:center>
<x:DataPicker/>
</g:center>
</g:SplitLayoutPanel>
</ui:UiBinder>
</script>
</div>
</div>
</div>
<div class="rbox3 bottomleft">
<div class="rbox3 bottomright">
<div class="rbox3 bottom"></div>
</div>
</div>
</div>
<!-- END:RBOX3 -->
<p>
I will not describe the technical details of how to integrate a third party
GWT library and get it running. The focus here is set on the ability to
declare layouts with (a mixture of) Widgets from different GWT libraries.
Let's take a look at the following GXT snippet:
</p>
<!-- START:RBOX3 -->
<div class="rbox3">
<div class="rbox3 topleft">
<div class="rbox3 topright">
<div class="rbox3 top"></div>
</div>
</div>
<div class="rbox3 left">
<div class="rbox3 right">
<div class="rbox3 body">
<script type="syntaxhighlighter" class="brush: java">
ContentPanel west = new ContentPanel();
west.addText("west");
ContentPanel center = new ContentPanel();
center.addText("center");
BorderLayoutData westData = new BorderLayoutData(LayoutRegion.WEST, 200);
westData.setSplit(true);
westData.setCollapsible(true);
westData.setMargins(new Margins(5));
BorderLayoutData centerData = new BorderLayoutData(LayoutRegion.CENTER);
centerData.setMargins(new Margins(5, 0, 5, 0));
LayoutContainer container = new LayoutContainer(new BorderLayout());
container.setSize(300, 300);
container.setBorders(true);
container.add(west, westData);
container.add(center, centerData);
</script>
</div>
</div>
</div>
<div class="rbox3 bottomleft">
<div class="rbox3 bottomright">
<div class="rbox3 bottom"></div>
</div>
</div>
</div>
<!-- END:RBOX3 -->
<p>
<b>
The question is now how to formulate this in a declarative manner...
The answer is simple but dissatisfying: It is currently not possible.
</b>
</p>
<p>
As we see with DatePicker in the example above, GWT is able to
construct simple Widgets (by calling their constructor with no arguments).
If GWT finds attributes in a declaration it will call the corresponding
setters of the associated java object (follwing the Java Beans standard).
But there are more complex Widgets where this strategy does not work.
The ContentPanel of GXT can be configured with a specific layout strategy.
The default is FlowLayout but in the example BorderLayout is used.
Depending on the layout the LayoutData has to be set appropriately
when adding children to the ContentPanel. This leads to the assertion
that the declarative layout parser has to know the structure of the
xml presentation of each (3rd party) Widget and how to wire together the
underlying Widgets to an object graph.
</p>
<p>
How would <i>you</i> formulate the above example as ui declaration? <strike>Here's one
of several possible answers:</strike> UPDATE: Here's an answer, depending on a specific parser:
</p>
<!-- START:RBOX3 -->
<div class="rbox3">
<div class="rbox3 topleft">
<div class="rbox3 topright">
<div class="rbox3 top"></div>
</div>
</div>
<div class="rbox3 left">
<div class="rbox3 right">
<div class="rbox3 body">
<script type="syntaxhighlighter" class="brush: xml">
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
xmlns:x='urn:import:com.extjs.gxt.ui.client.widget'
xmlns:l='urn:import:com.extjs.gxt.ui.client.widget.layout'>
<x:LayoutContainer size='300,300' borders='true'>
<l:BorderLayout>
<!-- BorderLayoutData with LayoutRegion.WEST -->
<l:west size='200' split='true' collapsible='true' margins='5'>
<x:ContentPanel>
<x:text value='west'/>
</x:ContentPanel>
</l:west>
<!-- BorderLayoutData with LayoutRegion.CENTER -->
<l:center margins='5,0,5,0'>
<x:ContentPanel>
<x:text value='center'/>
</x:ContentPanel>
</l:center>
</l:BorderLayout>
</x:LayoutContainer>
</ui:UiBinder>
</script>
</div>
</div>
</div>
<div class="rbox3 bottomleft">
<div class="rbox3 bottomright">
<div class="rbox3 bottom"></div>
</div>
</div>
</div>
<!-- END:RBOX3 -->
<h1>How to get it work?</h1>
<p>
Currently I work on a solution for the described problem of declaring
layouts containing arbitrary Widgets of 3rd party libraries.
<strike>I am focusing on these key topics:</strike>
</p>
<ol>
<li><strike>Hooking into the code generator of GWT (<i>finished</i>)</strike></li>
<li><strike>Creating a custom code generator (<i>work in progress</i>)</strike></li>
<li><strike>Defining a plugin language for 3rd party customization of the code generator (<i>next step</i>)</strike></li>
</ol>
<p>
UPDATE: I realized, that it is possible to write specific element parsers which are
bound to an UiBinder element via reflection. There are two things to do now:
</p>
<ol>
<li>Write specific parsers for 3rd party Widgets (<i>few code because recursive</i>)</li>
<li>Register these parsers (<i>access private var UiBinderWriter.elementParsers</i>)</li>
</ol>
<p>
<strike>The discussion of these three topics will follow soon on this blog...</strike>
A parser for the last example will be posted on this blog soon...
</p>Daniel Dietrich's Bloghttp://www.blogger.com/profile/02492628698632995681noreply@blogger.com3tag:blogger.com,1999:blog-4836109846275561634.post-79931411962099220472009-12-10T23:24:00.023+01:002009-12-11T09:21:23.380+01:00XML Schema: Element References<p>
Consider a xml based language consisting of elements (here: <i>nodes</i>)
and references to elements (here: <i>nodeRefs</i>).
In the following I want to discuss if it is possible to create a xml
schema for such a language which
</p>
<ul>
<li>allows to express reference dependencies</li>
<li>has a base type for an arbitrary number of nodes; a node reference is a node too!</li>
<li>is aware of referential integrity</li>
</ul>
<p>
Here is an example of a xml document based on a language which
consists of two different nodes and a node reference:
</p>
<!-- START:RBOX3 -->
<div class="rbox3">
<div class="rbox3 topleft">
<div class="rbox3 topright">
<div class="rbox3 top"></div>
</div>
</div>
<div class="rbox3 left">
<div class="rbox3 right">
<div class="rbox3 body">
<script type="syntaxhighlighter" class="brush: xml">
<?xml version="1.0" encoding="UTF-8"?>
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="example.xsd">
<node xsi:type="node1" id="_1">
<node xsi:type="node2">
<node xsi:type="nodeRef" ref="_1"/>
<node xsi:type="nodeRef" ref="_4"/> <!-- referential integrity violated! -->
</node>
<node xsi:type="node1"/>
<node xsi:type="node2"/>
</node>
</root>
</script>
</div>
</div>
</div>
<div class="rbox3 bottomleft">
<div class="rbox3 bottomright">
<div class="rbox3 bottom"></div>
</div>
</div>
</div>
<!-- END:RBOX3 -->
<p>
A node either has an unique id (and therefor is enabled to be referenced) or a reference to an id.
This property can be denoted with an <tt>attributeGroup</tt>:
</p>
<!-- START:RBOX3 -->
<div class="rbox3">
<div class="rbox3 topleft">
<div class="rbox3 topright">
<div class="rbox3 top"></div>
</div>
</div>
<div class="rbox3 left">
<div class="rbox3 right">
<div class="rbox3 body">
<script type="syntaxhighlighter" class="brush: xml">
<!-- nodes can be defined and referenced -->
<xs:attributeGroup name="defRef">
<xs:attribute name="id" type="xs:NCName"/> <!-- not xs:ID! -->
<xs:attribute name="ref" type="xs:QName"/>
</xs:attributeGroup>
</script>
</div>
</div>
</div>
<div class="rbox3 bottomleft">
<div class="rbox3 bottomright">
<div class="rbox3 bottom"></div>
</div>
</div>
</div>
<!-- END:RBOX3 -->
<p>
The type of id is not xs:ID because it is possible to omit the id.
In this case the node is not enabled to be referenced.
The second reason is that references are nodes too. The type xs:ID
would force that all nodes have an id - even node references.
</p>
<p>
In general there are multiple types of nodes which can be referenced.
The reference itself is a node too. So we need a base type for node
definitions and node references which is an abstract node. In this
example a node can have child nodes.
</p>
<!-- START:RBOX3 -->
<div class="rbox3">
<div class="rbox3 topleft">
<div class="rbox3 topright">
<div class="rbox3 top"></div>
</div>
</div>
<div class="rbox3 left">
<div class="rbox3 right">
<div class="rbox3 body">
<script type="syntaxhighlighter" class="brush: xml">
<!-- abstract node which can be defined or referenced -->
<xs:complexType name="node" abstract="true">
<xs:sequence>
<xs:element name="node" type="node" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attributeGroup ref="defRef"/>
</xs:complexType>
</script>
</div>
</div>
</div>
<div class="rbox3 bottomleft">
<div class="rbox3 bottomright">
<div class="rbox3 bottom"></div>
</div>
</div>
</div>
<!-- END:RBOX3 -->
<p>
The node definition has optionally an unique id which enables it to be referenced.
The ref attribute is prohibited. Because the node type is a restriction the child
element has to be denoted again! The node definition is abstract because there are
several concrete types of node definitions.
</p>
<!-- START:RBOX3 -->
<div class="rbox3">
<div class="rbox3 topleft">
<div class="rbox3 topright">
<div class="rbox3 top"></div>
</div>
</div>
<div class="rbox3 left">
<div class="rbox3 right">
<div class="rbox3 body">
<script type="syntaxhighlighter" class="brush: xml">
<!-- node definition -->
<xs:complexType name="nodeDef" abstract="true">
<xs:complexContent>
<xs:restriction base="node">
<xs:sequence>
<xs:element name="node" type="node" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="id" type="xs:NCName" use="optional"/>
<xs:attribute name="ref" use="prohibited"/>
</xs:restriction>
</xs:complexContent>
</xs:complexType>
</script>
</div>
</div>
</div>
<div class="rbox3 bottomleft">
<div class="rbox3 bottomright">
<div class="rbox3 bottom"></div>
</div>
</div>
</div>
<!-- END:RBOX3 -->
<p>In this example there are two different types of concrete node definitions:</p>
<!-- START:RBOX3 -->
<div class="rbox3">
<div class="rbox3 topleft">
<div class="rbox3 topright">
<div class="rbox3 top"></div>
</div>
</div>
<div class="rbox3 left">
<div class="rbox3 right">
<div class="rbox3 body">
<script type="syntaxhighlighter" class="brush: xml">
<xs:complexType name="node1">
<xs:complexContent>
<xs:extension base="nodeDef"/>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="node2">
<xs:complexContent>
<xs:extension base="nodeDef"/>
</xs:complexContent>
</xs:complexType>
</script>
</div>
</div>
</div>
<div class="rbox3 bottomleft">
<div class="rbox3 bottomright">
<div class="rbox3 bottom"></div>
</div>
</div>
</div>
<!-- END:RBOX3 -->
<p>
A node reference has no child elements! The id attribute is ommitted
because node references cannot be referenced. This can be modeled by
using direct references to concrete nodes (no need for transitivity).
</p>
<!-- START:RBOX3 -->
<div class="rbox3">
<div class="rbox3 topleft">
<div class="rbox3 topright">
<div class="rbox3 top"></div>
</div>
</div>
<div class="rbox3 left">
<div class="rbox3 right">
<div class="rbox3 body">
<script type="syntaxhighlighter" class="brush: xml">
<!-- node reference -->
<xs:complexType name="nodeRef">
<xs:complexContent>
<xs:restriction base="node">
<xs:sequence/> <!-- no child nodes allowed -->
<xs:attribute name="id" use="prohibited"/>
<xs:attribute name="ref" type="xs:QName" use="required"/>
</xs:restriction>
</xs:complexContent>
</xs:complexType>
</script>
</div>
</div>
</div>
<div class="rbox3 bottomleft">
<div class="rbox3 bottomright">
<div class="rbox3 bottom"></div>
</div>
</div>
</div>
<!-- END:RBOX3 -->
<p>
The root element encapsulates an arbitrary number of nodes
and defines the referential integritiy constraints.
</p>
<!-- START:RBOX3 -->
<div class="rbox3">
<div class="rbox3 topleft">
<div class="rbox3 topright">
<div class="rbox3 top"></div>
</div>
</div>
<div class="rbox3 left">
<div class="rbox3 right">
<div class="rbox3 body">
<script type="syntaxhighlighter" class="brush: xml">
<!-- root -->
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="node" type="node" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<!-- xs:key does not work here because id is
prohibited for nodes of xsi:type="nodeRef"
and optional for nodes of xsi:type="nodeDef -->
<xs:unique name="nodeKey">
<xs:selector xpath=".//node"/> <!-- each descent node -->
<xs:field xpath="@id"/>
</xs:unique>
<!-- xs:keyref may only refer to a xs:key,
not to a xs:unique (which is less restrictive) -->
<!--
<xs:keyref name="nodeKeyRef" refer="nodeKey">
<xs:selector xpath=".//node"/>
<xs:field xpath="@ref"/>
</xs:keyref>
-->
</xs:element>
</script>
</div>
</div>
</div>
<div class="rbox3 bottomleft">
<div class="rbox3 bottomright">
<div class="rbox3 bottom"></div>
</div>
</div>
</div>
<!-- END:RBOX3 -->
<p>
There is one flaw: Xml schema does not allow to create a keyref where the
corresponding key is a unique constraint. I don't understand that! It
would make sense to allow the specification of attributes which have to
be unique within a document for those elements for which they are defined.
@W3C: What the reason for that restriction!?
</p>
<p>Here is the complete example.xsd:</p>
<!-- START:RBOX3 -->
<div class="rbox3">
<div class="rbox3 topleft">
<div class="rbox3 topright">
<div class="rbox3 top"></div>
</div>
</div>
<div class="rbox3 left">
<div class="rbox3 right">
<div class="rbox3 body">
<script type="syntaxhighlighter" class="brush: xml">
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!--
PROS:
- id not neccessary, but unique:
we have id type=xs:NCName (and not type=xs:ID) in conjunction with
xs:unique constraint. So we are able to omit the id attribute.
For all nodes which define the attribute id the value has to be unique.
- no use of choice:
the node is abstract. nodeDef and nodeRef are of type node, so there's
no need to use a choice.
CONS:
- no keyref constraint:
keyref doesn't work because it refers to key with unique id of node
element and nodeRef is a node with no id. workaround: runtime keyref
constraint check by application
-->
<!-- root -->
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="node" type="node" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<!-- xs:key does not work here because id is
prohibited for nodes of xsi:type="nodeRef" -->
<xs:unique name="nodeKey">
<xs:selector xpath=".//node"/> <!-- each descent node -->
<xs:field xpath="@id"/>
</xs:unique>
<!-- xs:keyref may only refer to a xs:key,
not to a xs:unique (which is less restrictive) -->
<!--
<xs:keyref name="nodeKeyRef" refer="nodeKey">
<xs:selector xpath=".//node"/>
<xs:field xpath="@ref"/>
</xs:keyref>
-->
</xs:element>
<!-- nodes can be defined and referenced -->
<xs:attributeGroup name="defRef">
<xs:attribute name="id" type="xs:NCName"/> <!-- type="xs:NCName" -->
<xs:attribute name="ref" type="xs:QName"/>
</xs:attributeGroup>
<!-- abstract node which can be defined or referenced -->
<xs:complexType name="node" abstract="true">
<xs:sequence>
<xs:element name="node" type="node" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attributeGroup ref="defRef"/>
</xs:complexType>
<!-- node definition -->
<xs:complexType name="nodeDef" abstract="true">
<xs:complexContent>
<xs:restriction base="node">
<xs:sequence>
<xs:element name="node" type="node" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="id" type="xs:NCName" use="optional"/>
<xs:attribute name="ref" use="prohibited"/>
</xs:restriction>
</xs:complexContent>
</xs:complexType>
<!-- node reference -->
<xs:complexType name="nodeRef">
<xs:complexContent>
<xs:restriction base="node">
<xs:sequence/> <!-- no child nodes allowed -->
<xs:attribute name="id" use="prohibited"/>
<xs:attribute name="ref" type="xs:QName" use="required"/>
</xs:restriction>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="node1">
<xs:complexContent>
<xs:extension base="nodeDef"/>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="node2">
<xs:complexContent>
<xs:extension base="nodeDef"/>
</xs:complexContent>
</xs:complexType>
</xs:schema>
</script>
</div>
</div>
</div>
<div class="rbox3 bottomleft">
<div class="rbox3 bottomright">
<div class="rbox3 bottom"></div>
</div>
</div>
</div>
<!-- END:RBOX3 -->
<p>
Of cause there are several other possibilities to model a xml schema for such a language.
Here is another (which doesn't match the starting example.xml exactly but shows a different
approach with choices):
</p>
<!-- START:RBOX3 -->
<div class="rbox3">
<div class="rbox3 topleft">
<div class="rbox3 topright">
<div class="rbox3 top"></div>
</div>
</div>
<div class="rbox3 left">
<div class="rbox3 right">
<div class="rbox3 body">
<script type="syntaxhighlighter" class="brush: xml">
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!--
PROS:
- full constraint check (TODO: but doesn't work yet):
nodeDef and nodeRef have different types. so key and keyref works fine.
CONS:
- every node has an id (even if not referenced):
because id has a key constraint, it has to be defined (and unique).
- use of choice:
because nodeDef and nodeRef have different types a choice has to be
used where they can appear both.
-->
<!-- root -->
<xs:element name="root">
<xs:complexType>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="node" type="node"/>
<xs:element name="nodeRef" type="nodeRef"/>
</xs:choice>
</xs:complexType>
<!-- key/ref -->
<xs:key name="nodeKey">
<xs:selector xpath=".//node"/> <!-- each descent node -->
<xs:field xpath="@id"/>
</xs:key>
<xs:keyref name="nodeKeyRef" refer="nodeKey"> <!-- TODO: does not work, yet -->
<xs:selector xpath=".//nodeRef"/> <!-- each descent nodeRef -->
<xs:field xpath="@ref"/>
</xs:keyref>
</xs:element>
<!-- node definition-->
<xs:complexType name="node">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="node" type="node"/>
<xs:element name="nodeRef" type="nodeRef"/>
</xs:choice>
<xs:attribute name="id" type="xs:NCName" use="required"/>
</xs:complexType>
<!-- TODO: node should be abstract, concrete elements extend node,
nodeRef refers to conrete elements -->
<!-- node reference -->
<xs:complexType name="nodeRef">
<xs:attribute name="ref" type="xs:QName" use="required"/>
</xs:complexType>
</xs:schema>
</script>
</div>
</div>
</div>
<div class="rbox3 bottomleft">
<div class="rbox3 bottomright">
<div class="rbox3 bottom"></div>
</div>
</div>
</div>
<!-- END:RBOX3 -->
<p>
If you use jaxb to unmarshall a xml file (based on a xsd) I discourage
the use of choice elements. They are mapped to multiple java instance
variables which are null if not used. Instead I recommend the usage of
abstract elements which are mapped to abstract classes. This is a more
OO like way.
</p>Daniel Dietrich's Bloghttp://www.blogger.com/profile/02492628698632995681noreply@blogger.com0tag:blogger.com,1999:blog-4836109846275561634.post-23796071542775048652009-11-24T00:19:00.007+01:002009-12-02T19:38:48.997+01:00Local Subversion repository with Eclipse<p>Today I installed Subversion (SVN) on my notebook. I needed versioning for local development. A simple way is to create a file-based repository and connect to it with an eclipse svn client. Follow these steps:</p>
<ul>
<li>Download & install Subversion for Mac</li>
<li>Install Eclipse Subversion client plugins</li>
<li>Share an Eclipse project in a local repository</li>
</ul>
<p>Here are the details:</p>
<h1>Download & install Subversion for Mac</h1>
<p>You can get Subversion for Mac (and other OS's) <a href="http://subversion.tigris.org/getting.html#osx">here</a>. I used the universal openCollabNet <a href="http://downloads.open.collab.net/binaries.html">binary</a>. Just follow the install instructions of the installer...</p>
<p>It is recommended to add the Subversion bin directory to the system <tt>$PATH</tt> variable (if you're lazy like me). For example you can edit one of the following files:</p>
<ul>
<li><tt>/etc/profile</tt> (system wide .profile for sh)</li>
<li><tt>/Users/<you>/.profile</tt> (users .profile, loaded at login)</li>
<li><tt>/Users/<you>/.bash_profile</tt> (users .profile, (re-)loaded with new terminal window)</li>
</ul>
<p>The <tt>.bash_profile</tt> was my choice but you can use all of them. Add the following lines:</p>
<!-- START:RBOX3 -->
<div class="rbox3">
<div class="rbox3 topleft">
<div class="rbox3 topright">
<div class="rbox3 top"></div>
</div>
</div>
<div class="rbox3 left">
<div class="rbox3 right">
<div class="rbox3 body">
<script type="syntaxhighlighter" class="brush: shell"><![CDATA[
# subversion
echo $PATH | grep -q -s "/opt/subversion/bin"
if [ $? -eq 1 ] ; then
PATH=$PATH:/opt/subversion/bin
export PATH
fi
]]></script>
</div>
</div>
</div>
<div class="rbox3 bottomleft">
<div class="rbox3 bottomright">
<div class="rbox3 bottom"></div>
</div>
</div>
</div>
<!-- END:RBOX3 -->
<p>Btw: Currently I use the non-commercial editor <a href="http://www.barebones.com/products/TextWrangler/">TextWrangler</a>. It's a great tool! The shell command <tt>edit <file></tt> opens a gui editor for <tt><file></tt>.</p>
<h1>Install Eclipse Subversion client plugins</h1>
<p>There are currently two popular SVN plugins for Eclipse: Subclipse and Subversive. The latter is my favourite:</p>
<ol>
<li>Click <tt>Help</tt> and <tt>Install New Software</tt></li>
<li>Install Subversive SVN Team Provider
<ul>
<li>Choose Site: Galileo (included in Eclipse 3.5)</li>
<li>Install: Subversive SVN Team Provider (Incubation)</li>
</ul>
</li>
<li>Install Subversive SVN Connectors
<ul>
<li>Choose site: <tt> http://www.polarion.org/projects/subversive/download/eclipse/2.0/update-site/</tt></li>
<li>Install: Subversive SVN Connectors
SVNKit 1.3.0 Implementation</li>
</ul>
</li>
<li>Restart Eclipse, if asked</li>
</ol>
<h1>Share an Eclipse project in a local repository</h1>
<p>Open a terminal (current location: <tt>/Users/<you></tt>) and create a local SVN repository:</p>
<!-- START:RBOX3 -->
<div class="rbox3">
<div class="rbox3 topleft">
<div class="rbox3 topright">
<div class="rbox3 top"></div>
</div>
</div>
<div class="rbox3 left">
<div class="rbox3 right">
<div class="rbox3 body">
<script type="syntaxhighlighter" class="brush: shell"><![CDATA[
mkdir svn.repository
cd svn.repository
svnadmin create myFirstRepository
]]></script>
</div>
</div>
</div>
<div class="rbox3 bottomleft">
<div class="rbox3 bottomright">
<div class="rbox3 bottom"></div>
</div>
</div>
</div>
<!-- END:RBOX3 -->
<p>In the Eclipse <i>Project Explorer</i> richt-click on a project and...</p>
<ul>
<li>Choose Team -> Share Project...</li>
<li>Repository Type: SVN</li>
<li>Create a new repository location</li>
<li>Enter URL: <tt>file:///Users/<you>/svn.repository/myFirstRepository</tt> and press Finish</li>
<li>Finally add the project to repository</li>
</ul>
<p>Use the <i>Team Synchronization</i> perspective to sync your changes.</p>Daniel Dietrich's Bloghttp://www.blogger.com/profile/02492628698632995681noreply@blogger.com0tag:blogger.com,1999:blog-4836109846275561634.post-66224167493510489112009-09-12T00:54:00.026+02:002009-12-20T00:51:52.418+01:00The Generic Chaining Pattern<p>
Have a look at the following situation:
</p>
<!-- START:RBOX3 -->
<div class="rbox3">
<div class="rbox3 topleft">
<div class="rbox3 topright">
<div class="rbox3 top"></div>
</div>
</div>
<div class="rbox3 left">
<div class="rbox3 right">
<div class="rbox3 body">
<script type="syntaxhighlighter" class="brush: java">
public interface Interface {
public Interface method();
}
public class ClassA implements Interface {
@Override
public Interface method() {
// implementation A of method()
return this;
}
public ClassA methodA1() {
// specific method of ClassA
}
}
public class ClassB implements Interface {
@Override
public Interface method() {
// implementation B of method()
return this;
}
public ClassB methodB1() {
// specific method of ClassB
}
}
public class Example1 {
public static void main(String[] argv) {
// this is crude, isn't it?
((ClassA) new ClassA().method()).methodA1();
((ClassB) new ClassB().method()).methodB1();
}
}
</script>
</div>
</div>
</div>
<div class="rbox3 bottomleft">
<div class="rbox3 bottomright">
<div class="rbox3 bottom"></div>
</div>
</div>
</div>
<!-- END:RBOX3 -->
<p>
Another example:
</p>
<!-- START:RBOX3 -->
<div class="rbox3">
<div class="rbox3 topleft">
<div class="rbox3 topright">
<div class="rbox3 top"></div>
</div>
</div>
<div class="rbox3 left">
<div class="rbox3 right">
<div class="rbox3 body">
<script type="syntaxhighlighter" class="brush: java">
((ClassA) ((ClassA) ((ClassA) new ClassA().method()).methodA1()).methodA2()).methodA3()...;
</script>
</div>
</div>
</div>
<div class="rbox3 bottomleft">
<div class="rbox3 bottomright">
<div class="rbox3 bottom"></div>
</div>
</div>
</div>
<!-- END:RBOX3 -->
<p>
I used Generics to get around this flaw:
</p>
<!-- START:RBOX3 -->
<div class="rbox3">
<div class="rbox3 topleft">
<div class="rbox3 topright">
<div class="rbox3 top"></div>
</div>
</div>
<div class="rbox3 left">
<div class="rbox3 right">
<div class="rbox3 body">
<script type="syntaxhighlighter" class="brush: java">
public interface GenericInterface<T extends GenericInterface<?>> {
public T method();
}
public class GenericClassA implements GenericInterface<GenericClassA> {
@Override
public GenericClassA method() {
// implementation A of method()
return this;
}
public GenericClassA methodA1() {
// specific method of GenericClassA
}
}
public class GenericClassB implements GenericInterface<GenericClassB> {
@Override
public GenericClassB method() {
// implementation B of method()
return this;
}
public GenericClassB methodB1() {
// specific method of GenericClassB
}
}
public class Example2 {
public static void main(String[] argv) {
// no more casting
new GenericClassA().method().methodA1();
new GenericClassB().method().methodB1();
}
}
</script>
</div>
</div>
</div>
<div class="rbox3 bottomleft">
<div class="rbox3 bottomright">
<div class="rbox3 bottom"></div>
</div>
</div>
</div>
<!-- END:RBOX3 -->
<p>
The example above therefore could be handled as follows:
</p>
<!-- START:RBOX3 -->
<div class="rbox3">
<div class="rbox3 topleft">
<div class="rbox3 topright">
<div class="rbox3 top"></div>
</div>
</div>
<div class="rbox3 left">
<div class="rbox3 right">
<div class="rbox3 body">
<script type="syntaxhighlighter" class="brush: java">
new ClassA().method().methodA1().methodA2().methodA3()...;
</script>
</div>
</div>
</div>
<div class="rbox3 bottomleft">
<div class="rbox3 bottomright">
<div class="rbox3 bottom"></div>
</div>
</div>
</div>
<!-- END:RBOX3 -->
<p>
Used this <b>Generic Chaining Pattern</b> while developing an abstraction layer
for database access in an old fashioned jdbc environment. It looked like this:
</p>
<!-- START:RBOX3 -->
<div class="rbox3">
<div class="rbox3 topleft">
<div class="rbox3 topright">
<div class="rbox3 top"></div>
</div>
</div>
<div class="rbox3 left">
<div class="rbox3 right">
<div class="rbox3 body">
<script type="syntaxhighlighter" class="brush: java; ; highlight: [37]">
abstract class AbstractQuery<T> {
// ...
protected PreparedStatement getPreparedStatement() { ... }
protected void prepareStatement(PreparedStatement p) {
// to be overridden
}
public abstract T execute();
public T close() {
try { getPreparedStatement().close(); } catch(Exception x) { ... }
return this;
}
}
public abstract class Select extends AbstractQuery<Select> {
// ...
public Select execute() {
PreparedStatement p = getPreparedStatement();
prepareStatement(p);
ResultSet r = p.execute();
while (r.next() && getResults(r)) { }
return this;
}
protected abstract boolean getResults(ResultSet r);
}
public class MyDAO {
public List<ResultType> myBusinessMethod() {
final List<ResultType> result = new ArrayList<ResultType>();
new Select(namedQuery) {
@Override protected void prepareStatement(PreparedStatement p) { ... }
@Override protected boolean getResults(ResultSet r) {
ResultType item = new ResultType();
// item.setXxx(...);
result.add(item);
return true; // read next result
}
}.execute().close();
return result
}
}
</script>
</div>
</div>
</div>
<div class="rbox3 bottomleft">
<div class="rbox3 bottomright">
<div class="rbox3 bottom"></div>
</div>
</div>
</div>
<!-- END:RBOX3 -->Daniel Dietrich's Bloghttp://www.blogger.com/profile/02492628698632995681noreply@blogger.com0tag:blogger.com,1999:blog-4836109846275561634.post-79139115371882668782009-09-10T23:56:00.006+02:002009-12-02T19:37:02.392+01:000xCAFEBABE<div><span class="Apple-style-span" style="font-size: small;">For
those who are curious about the URL of this blog (http://</span><b><span
class="Apple-style-span" style="font-size: small;">cafebab3</span></b><span
class="Apple-style-span" style="font-size: small;">.blogspot.com)
take a look at this </span><a
href="http://www.artima.com/insidejvm/whyCAFEBABE.html"><span
class="Apple-style-span" style="font-size: small;">discussion</span></a><span
class="Apple-style-span" style="font-size: small;">.</span></div>Daniel Dietrich's Bloghttp://www.blogger.com/profile/02492628698632995681noreply@blogger.com0tag:blogger.com,1999:blog-4836109846275561634.post-2153608940130867012009-09-09T16:36:00.003+02:002009-12-02T19:38:31.550+01:00Solid State Disk vs. Sudden Motion Sensor<div><span class="Apple-style-span" style="font-family:'trebuchet ms', serif;"><span class="Apple-style-span" style="font-size: small;">Lucky owners of a Solid State Disk (SSD) should think about disabling their Suddden Motion Sensor (SMS) which is primary used to protect mechanic hard disks from head crashes. With SSDs head crashes are history - you can work without fear, even if your hardware rumbles.</span></span></div><div><span class="Apple-style-span" style="font-family:'trebuchet ms', serif;"><span class="Apple-style-span" style="font-size: small;"><br /></span></span></div><div><span class="Apple-style-span" style="font-family:'trebuchet ms', serif;"><span class="Apple-style-span" style="font-size: small;">However, follow these instructions to disable the sensor:</span></span></div><div><ul><li><span class="Apple-style-span" style="font-family: 'trebuchet ms', serif; font-size: small; ">open a terminal</span></li><li><span class="Apple-style-span" style="font-family: 'trebuchet ms', serif; font-size: small; ">disable sensor: <span class="Apple-style-span" style="font-family:'courier new';">sudo pmset -a sms 0</span><span class="Apple-tab-span" style="white-space:pre"> </span>(0 = disable, 1 = enable)</span></li><li><span class="Apple-style-span" style="font-family: 'trebuchet ms', serif; font-size: small; ">check actual settings: <span class="Apple-style-span" style="font-family:'courier new';">sudo pmset -g</span></span></li></ul></div>Daniel Dietrich's Bloghttp://www.blogger.com/profile/02492628698632995681noreply@blogger.com0tag:blogger.com,1999:blog-4836109846275561634.post-78406524012511655612009-09-09T01:31:00.026+02:002009-12-02T20:00:04.253+01:00Switching to Snow Leopard (SL)<div><span class="Apple-style-span" style="font-family:'trebuchet ms', serif;"><b><span class="Apple-style-span" style="font-size:large;">Mac OS 10.6 aka Snow Leopard</span></b></span></div><div><span class="Apple-style-span" style="font-family:'trebuchet ms';"><span class="Apple-style-span" style="font-size:small;">Unfortunately I wasn't fast enough snatching one of the popular copies of SL at the local store. After waiting four cruelly days I got my copy and spent another day preparing my system for installation. In contrast to the opinion that only windows users have to format their hard disk when installing a new system I chose that way because I like the nerdy 'fresh' feeling of a vanilla system :)</span></span></div><div><span class="Apple-style-span" style="font-family:'trebuchet ms', serif;"><span class="Apple-style-span" style="font-size:small;"><br /></span></span></div><div><span class="Apple-style-span" style="font-family:'trebuchet ms', serif;"><span class="Apple-style-span" style="font-size:small;">Installation ran smoothly on my unibody MBP. In addition to the installation of several applications I manually copied settings and application data from a time machine backup:</span></span></div><div><ul><li><span class="Apple-style-span" style="font-family:'trebuchet ms', serif;"><span class="Apple-style-span" style="font-size:small;">Address Book (</span><span class="Apple-style-span" style="font-family:'courier new';"><home><span class="Apple-style-span" style="font-size:small;">/Library/Application Support/AddressBook</span></home></span><span class="Apple-style-span" style="font-size:small;">)</span></span></li><li><span class="Apple-style-span" style="font-family:'trebuchet ms', serif;"><span class="Apple-style-span" style="font-size:small;">iCal (</span><span class="Apple-style-span" style="font-family:'courier new';"><home><span class="Apple-style-span" style="font-size:small;">/Library/Calendars</span></home></span><span class="Apple-style-span" style="font-size:small;">)</span></span></li><li><span class="Apple-style-span" style="font-family:'trebuchet ms', serif;"><span class="Apple-style-span" style="font-size:small;">Mail (</span><span class="Apple-style-span" style="font-family:'courier new';"><home><span class="Apple-style-span" style="font-size:small;">/Library/Mail</span></home></span><span class="Apple-style-span" style="font-size:small;">)</span></span></li><li><span class="Apple-style-span" style="font-family:'trebuchet ms', serif;"><span class="Apple-style-span" style="font-size:small;">Safari (</span><span class="Apple-style-span" style="font-family:'courier new';"><home><span class="Apple-style-span" style="font-size:small;">/Library/Safari</span></home></span><span class="Apple-style-span" style="font-size:small;">)</span></span></li><li><span class="Apple-style-span" style="font-family:'trebuchet ms', serif;"><span class="Apple-style-span" style="font-size:small;">VirtualBox (</span><span class="Apple-style-span" style="font-family:'courier new';"><home><span class="Apple-style-span" style="font-size:small;">/Library/VirtualBox</span></home></span><span class="Apple-style-span" style="font-size:small;">)</span></span></li></ul></div><div><span class="Apple-style-span" style="font-family:'trebuchet ms', serif;"><span class="Apple-style-span" style="font-size:small;">- et voilĂ , it works!</span><i><span class="Apple-style-span" style="font-size:small;"> (basically at least)</span></i></span></div><div><span class="Apple-style-span" style="font-family:'trebuchet ms', serif;"><span class="Apple-style-span" style="font-size:small;"><br /></span></span></div><div><span class="Apple-style-span" style="font-family:'trebuchet ms', serif;"><span class="Apple-style-span" style="font-family:Georgia, serif;"><div><b><span class="Apple-style-span" style="font-family:'trebuchet ms';"><span class="Apple-style-span" style="font-size:large;">Deleting Time Machine Backup</span></span></b></div><div><span class="Apple-style-span" style="font-family:'trebuchet ms', serif;"><span class="Apple-style-span" style="font-size:small;">After manually copying the latest Time Machine backup (@see </span><span class="Apple-style-span" style="font-family:'courier new';"><tmvolume><span class="Apple-style-span" style="font-size:small;">/Backups.backupdb</span></tmvolume></span><span class="Apple-style-span" style="font-size:small;">) to a safe location I tried to purge the Time Machine backup dir. Moving that dir to trash and emptying trash results in an </span><a href="http://discussions.apple.com/thread.jspa?messageID=6794987"><span class="Apple-style-span" style="font-size:small;">error</span></a><span class="Apple-style-span" style="font-size:small;">. Also a simple </span><span class="Apple-style-span" style="font-family:'courier new';"><span class="Apple-style-span" style="font-size:small;">sudo rm -rdf Backups.backupdb</span></span><span class="Apple-style-span" style="font-size:small;"> doesn't work. </span><span class="Apple-style-span" style="font-family:'trebuchet ms';"><span class="Apple-style-span" style="font-size:small;">The reason is that apple uses Access Control Lists (ACLs) for Time Machine backups as you can read</span></span><span class="Apple-style-span" style="font-size:small;"> </span><a href="http://www.thirdbit.net/articles/2009/03/03/time-machine-tweaking-fixing-acls-and-extended-attributes-using-fsaclctl-and-chflags-a-howto/"><span class="Apple-style-span" style="font-size:small;">here</span></a><span class="Apple-style-span" style="font-size:small;"> and </span><a href="http://unbounded.org/leopard-acls/"><span class="Apple-style-span" style="font-size:small;">here</span></a><span class="Apple-style-span" style="font-size:small;">. After spending a night trying to delete that skit I reconnected Time Machine to my Leopard backup an manually deleted each backup :-/ The remove all option didn't worked for me (nothing happened).</span></span></div></span></span></div><div><span class="Apple-style-span" style="font-family:'trebuchet ms', serif;"><span class="Apple-style-span" style="font-size:small;"><br /></span></span></div><div><span class="Apple-style-span" style="font-family:'trebuchet ms', serif;"><b><span class="Apple-style-span" style="font-size:large;">Activate 64-bit Kernel</span></b></span></div><div><span class="Apple-style-span" style="font-family:'trebuchet ms', serif;"><span class="Apple-style-span" style="font-size:small;">SL </span><a href="http://www.appleinsider.com/articles/08/10/28/road_to_mac_os_x_snow_leopard_64_bit_to_the_kernel.html"><span class="Apple-style-span" style="font-size:small;">brings 64-bit</span></a><span class="Apple-style-span" style="font-size:small;"> to the kernel. But you have to activate it manually! Out of the box only applications run in 64-bit mode, the kernel is 32-bit. The actual configuration can be checked by</span></span><span class="Apple-style-span" style="font-size:small;"><br /></span><ol><li><span class="Apple-style-span" style=" color: rgb(51, 51, 51); line-height: 18px; "><span class="Apple-style-span" style="font-family:'trebuchet ms';"><span class="Apple-style-span" style="font-size:small;">Choose Apple menu > About This Mac.</span></span></span></li><li><span class="Apple-style-span" style=" color: rgb(51, 51, 51); line-height: 18px; "><span class="Apple-style-span" style="font-family:'trebuchet ms';"><span class="Apple-style-span" style="font-size:small;">Click More Info to open System Profiler. (Or open the Apple System Profiler application located in /Applications/Utilities.)</span></span></span></li><li><span class="Apple-style-span" style=" color: rgb(51, 51, 51); line-height: 18px; "><span class="Apple-style-span" style="font-family:'trebuchet ms';"><span class="Apple-style-span" style="font-size:small;">Click the topic 'Software' on the left. On the right hand you can read the value of '64-bit kernel and extensions'.</span></span></span></li></ol></div><div><span class="Apple-style-span" style="font-family:'trebuchet ms', serif;"><span class="Apple-style-span" style="font-size:small;">To activate the 64-bit kernel follow these steps:</span></span></div><div><span class="Apple-style-span" style="font-family:'trebuchet ms', serif;"><div><span class="Apple-style-span" style="font-size:small;"><br /></span></div><div><span class="Apple-style-span" style="font-weight: bold; "><span class="Apple-style-span" style="font-family:'trebuchet ms';"><span class="Apple-style-span" style="font-size:small;">Test your CPU</span></span></span><span class="Apple-style-span" style="font-size:small;"><br />Not every CPU is capable of running the kernel in 64-bit mode. Open a console and type</span></div><div><span class="Apple-style-span" style="font-family:'courier new', serif;"><span class="Apple-style-span" style="font-size:small;">ioreg -l -p IODeviceTree | grep firmware-abi</span></span><span class="Apple-style-span" style="font-size:small;"><br />If the output contains the string </span><span class="Apple-style-span" style="font-family:'courier new';"><span class="Apple-style-span" style="font-size:small;">EFI64</span></span><span class="Apple-style-span" style="font-size:small;"> then your CPU is ready for the 64-bit kernel.</span></div><div><span class="Apple-style-span" style="font-size:small;"><br /></span></div><div><b><span class="Apple-style-span" style="font-size:small;">Testrun the 64-bit kernel</span></b></div><div><span class="Apple-style-span" style="font-size:small;">There are some secret keys with SL. When booting the machine, press 6 4 (at the same time) to boot the 64-bit kernel. </span><i><span class="Apple-style-span" style="font-size:small;">(If you are using the 64-bit kernel by default you can press 3 2 (at the same time) to boot the 32-bit kernel.)</span></i></div><div><span class="Apple-style-span" style="font-size:small;"><br /></span></div><div><b><span class="Apple-style-span" style="font-size:small;">Make changes permanent</span></b></div><div><span class="Apple-style-span" style="font-size:small;">If you feel comfortable running the 64-bit kernel (here are some </span><a href="http://forums.macrumors.com/showthread.php?t=775884"><span class="Apple-style-span" style="font-size:small;">benchmarks</span></a><span class="Apple-style-span" style="font-size:small;">) edit the file</span></div><div><span class="Apple-style-span" style="font-family:'courier new';"><span class="Apple-style-span" style="font-size:small;">/Library/Preferences/SystemConfiguration/com.apple.Boot.plist</span></span></div><div><span class="Apple-style-span" style="font-size:small;">to make changes permanent.</span></div><div><span class="Apple-style-span" style="font-size:small;">Change the key </span><span class="Apple-style-span" style="font-family:'courier new';"><span class="Apple-style-span" style="font-size:small;">Kernel Flags</span></span><span class="Apple-style-span" style="font-size:small;"> to the value </span><span class="Apple-style-span" style="font-family:'courier new';"><span class="Apple-style-span" style="font-size:small;">arch=x86_64</span></span><span class="Apple-style-span" style="font-size:small;">. That's all. </span><i><span class="Apple-style-span" style="font-size:small;">(but beware of the side effects)</span></i></div></span></div><div><span class="Apple-style-span" style="font-family:'trebuchet ms', serif;"><span class="Apple-style-span" style="font-size:small;"><br /></span></span></div><div><span class="Apple-style-span" style="font-family:'trebuchet ms', serif;"><b><span class="Apple-style-span" style="font-size:small;">Side Effects</span></b></span></div><div><span class="Apple-style-span" style="font-family:'trebuchet ms', serif;"><span class="Apple-style-span" style="font-size:small;">Installing MySQL 5.1 x86_64 from .DMG doesn't work actually because the .DMG is packed with the HFS file system, which is a 32-bit kernel extension and <a href="http://bugs.mysql.com/bug.php?id=46999">doesn't run</a> on the 64-bit kernel. Workaround: Boot with the magic keys 3 2 and install mysql. Reboot and you're fine.</span></span></div><div><span class="Apple-style-span" style="font-family:'trebuchet ms', serif;"><span class="Apple-style-span" style="font-size:small;">Another glitch is that the Safari hack <a href="http://hetima.com/safari/stand-e.html">Safari Stand</a> does not work with 64-bit (<a href="http://hackd.net/2009/06/18/enabling-simbl-on-snow-leopard/">workaround</a>). It is build on <a href="http://www.culater.net/software/SIMBL/SIMBL.php">SIMBL</a> (32-bit) which is an InputManager, but InputManagers have to be 64-bit with the 64-bit kernel. <b>Update:</b> Yesterday a new Snow Leopard ready beta of SIMBL was released - but I haven't tried it out yet.</span></span></div><div><span class="Apple-style-span" style="font-family:'trebuchet ms', serif;"><span class="Apple-style-span" style="font-size:small;"><br /></span></span></div>Daniel Dietrich's Bloghttp://www.blogger.com/profile/02492628698632995681noreply@blogger.com0