So, we want to create a meta-data bus? And merging facilities? Here is
my take on this proposal, my official proposal :-) I had some discussion
with Aslak in Y! messenger. It's a bit verbose, but to make it easy to
reference it later it's written this way.

First of all, I stress that 'the design is in the code' and with source
code and some @tags we can keep all uml/db-schema/code/etc synced. The
proof is TogetherJ which keeps almost all the uml diagram in source code
as, guess what, some @tags. So put some @tags in code and you know how
db schema might look actually, and so on. My conclusion: code+@tags sits
in between always, then we map back and forth to/from
db/uml/xml/dd/whatever. Another conclusion: We need a facility to
merge/diff from code to external form, to code from external form. In
other words we need two generic interfaces and we'll be done defining an
abstraction for the problem at hand. Now if you look at it you see that
for example we always map a program element such as an attribute or
property or method or a class to another external form such as a
table/column/blabla. So we need something like FromPropertyToDBColumn
and ToPropertyFromDBColumn syncer classes. My point is we want
merge/diff, right? We need to make it happen in fine grained level,
merge/diff for a property/column for example. Now it looks ok, right?
But how does it fit with our big Velocity files which take a class and
generate a deployment descriptor or a remote interface? Ah yeah, they
are not fine grained, very raw, just outputting some text.

So here is a solution:

Instead of defining raw template file we'll use an xml file, and define
some chunks in it like this:

<template name="remote-intf">

 <mergeTo>
  <chunk name="remote-intf-prg-element">
    #velocity template here such as:
    public interface $remoteIntfClassName {
      #foreach method in clazz.methods
      #xdoclet.includeChunk("remote-intf-remote-method", method)
    }
  </chank>

  <chunk name="remote-intf-remote-method">
    public $method.methodType $method.name($method.parameters) throws
$method.exceptions;
  </chank>
 </mergeTo>

 <mergefrom>
   chunks for generating the bean impl class and remote intf
implementation methods
 </mergefrom>
</template>

So each generated element is defined in a chunk. So when user adds a new
method with @ejb:interface-method tag we do a diff and only generate
that single method with that single chunk. Each chunk is backed by a
command-like class: IntfPrgElementChunk derived from PrgElementChunk
which has isUpToDate/mergeFromExternalForm/mergeToExternalForm methods.
So we first generate the output for the newly added method, then do a
diff between new and old stuff and mergeTo if not identical. And the
reverse too: if someone adds a new remote intf method to the remote intf
class, we do a diff and add the impl method in bean impl class.
PrgElementChunk is like a command object, you can even implement
Undoable interface and undo the last merge operation! I'm sure we can
come up with a good DefaultPrgElementChunk too. The diff is done on the
AST of the generated stuff, using the static methods of CodeUnit.
Another good thing about chunks is that because they are fine-grained we
can easily reuse them, create composite chunks and such. So for example
local-interface might use remote-intf-remote-method chunk and also
derive from RemoteIntfPrgElementChunk, or RemoteIntfClassChunk can be
composed of RemoteIntfPrgElementChunk chunk objects and ask each chunk
in diff/merge procedure, or decorate chunk classes. The chunk class is
where much of the logic and dirty work is. 

How does it fit with xgg? Well, xgg should generate chunks and the
command classes. How is XTag used? Look at this link:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide
/html/cpconwritingcustomattributes.asp and that's exactly what I suggest
to do: a class for each @tag so we'll have EjbBeanTag which will have
the ejbName/jndiName/etc parameters and we define the rest of the
metadata for the @tag like .Net, i.e. @attribute required="true" for a
mandatory parameter of a @tag and so on. @tag validation is performed
here, plus some other stuff: a utility getJndiName() method for example
in EjbTagBean which returns jndi-name parameter if it's specified or
"jndi/$ejb-name" if not specified.

Now another question is how can we define abstract merge/isUpToDate/etc
methods? Should it accept a ClassDeclaration AST class? Or a
DBDeclaration maybe for a db meta-data? Well, imho for all OO source
codes we can define an abstract AST interface (of course for different
languages of course 'cause I'm absolutely sure in a year or so we'll
have to support C# too, and we already have FRs for @tags in AspectJ and
C++ too). For example ClassDeclaration and then a javacc generated
subclass JavaClassDeclaration. So an
AbstractClassDeclarationPrgElementChunk chunk class can accept a
ClassDeclaration parameter. Now what about xml output or db schema? Imho
all *externall data structures* such as a db schema or a uml diagram
should be dealt like an XML file, in other words with commons-sql we
convert the db schema to xml and then like XMLUnit (which afaik uses AST
for comparison too) do the compare/diff/merge.

What's left? Well, a very important thing: the velocity chunks are still
raw text and have no sense of what they are generating. What's wrong
with that? You gotta know what kind of element you're generating, is it
a method, a property or just some random text. Why? Take a look at some
of the TagHandlers which check ejbCreate methods, and see how fucking
sophisticated and tricky the checks for ejbCreate overriding checks are.
They are tricky because what I'm doing there is that I try to somehow
predict that the generated ejbCreate method I'm working on for class B
is defined in B's superclass's generated home intf too. In other words
I'm checking to see whether I should generate a create() method or not,
but note that the check is actually performed on the generated class
itself rather than the class which is the source. So if a create method
is already defined in a class which is the parent of the home intf I'm
generating I shouldn't generate it again. Imho we should let xdoclet
know whether it's generating a class, a method or a method body. How?
Well something like this:

  <chunk name="remote-intf-prg-element">
    <definition type="class" name="$remoteIntfClassName"/>
    <body>
    {
      #foreach method in clazz.methods
      #xdoclet.includeChunk("remote-intf-remote-method", method)
    }
    </body>
  </chank>

So the piece of code which analyzes <chunk/> elements know that it's
generating a class named AIntf for example. If we do that same thing for
ejbCreate case too we'll easily be able to know that ejbCreate is
generated and is overridden or now. Other possibilities: with a nested
<parameter/> element of <definition/> we'll know that what parameters
the generated method has, so we can just do something like:
generated_method.getParameters() and loop over those parameters in say
setData method of the CMP subclass. Clearer code imho, instead of again
doing something like the current <XDtParameter:forAllParameters/> which
is a redundant and error prone code imho. Why error prone? Let me tell
you about a temaplte I wrote recently: this template generated a
subclass for bean impl class and in some cases adds a new parameter
",UserObject user" to the parameters list of some methods. So I first do
some dirty loops and checks for the part which adds the new parameter
and another series of checks when I'm looping over parameters and
generating something like a "data.add(paramX);" for each param. If
generated_method.getParameters() were used I could reuse
"generated_method.getParameters()" and just put it there for the loop
but currently I have to call another custom method. I hope you get the
point.

Ah, another obvious thing I forgot to talk about: for the chunks we
should have "grammar fragments" so we'll be able to parse a code
fragment, say a single method. Not a big deal imho.

Another thing regarding this whole chunks and such: I'm sure you'll say
that it's complicated and ordinary users won't like to write templates
this way. I more or less agree, but wait a minute! Nothing stops you of
creating a chunk which covers the whole generated class or db schema or
whatever! Like our templates. And if you don't want to know about
generated_method.getParameters() too, then just don't define
<parameter/> and just write the <body/> part. Easier than that? I mean
this solution lets us write easier to super complicated templates, from
dumb output to smart output, from one way output to n-way.

As you can see it's an xdoclet core service and all other chunks and the
meta-data bus is grown on this root.

Was it clear? I hope so :-) Thoughts?

Ara. 




-------------------------------------------------------
This sf.net email is sponsored by:ThinkGeek
Welcome to geek heaven.
http://thinkgeek.com/sf
_______________________________________________
Xdoclet-devel mailing list
[EMAIL PROTECTED]
https://lists.sourceforge.net/lists/listinfo/xdoclet-devel

Reply via email to