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