Grzegorz Kossakowski skrev:
Hi,
It's been three times that I reverted my changes introducing ObjectModel
interface and using it in Template generator. I must admit that I got
stuck in unproductive state because I'm constantly unhappy with the
design.
You are probably striving for perfection and think that you can achieve
it by staying in the design phase just a little bit longer. But it
doesn't work that way. Perfection takes for ever and we don't have that
much time ;) Be happy with what is good enough.
Also you need to start implementing your design. Choose a design (NOW!),
stick with it for a while and implement the important parts of it in the
simplest possible way. By doing this you get *real* experience with the
pros and cons of your design choices and can refactor and redesign if
necessary until it becomes good enough. You will also get better
feedback from the rest of us by committing code.
As a good rule of thumb: when the design phase stops to be stimulating
and has become frustrating it is better to start to implement something ;)
Just as an example, the servlet service framework in its current shape
is the fifth mayor reimplementation since we started to actually
implement it. Before that the community did design for a couple of
years, which in the end was really frustrating for everyone involved.
Also we spent so much time implementing things that would have been
certainly would have been nice to have but that is not part of the
current implementation.
Let me share my troubles in a hope that someone is much clever
than me and will give some advise.
As discussed earlier[1][2], Object Model will be simple Map that can
contain more than item for each key and preserves order in which items
were added. I found (after several attempts to invent one myslef)
MultiMap[3] interesting and close to what I want to achieve. It resulted
in very simple ObjectModelImpl[4]
I'm not convinced. I would prefer to just use an implementation of the
ordinary Map that returns *one* item for each key (respecting the stack
order in the underlying stack based model).
While the MultiMap idea solves the problem of "../1" patterns in the
map: EL, it complicates all the rest of the ELs. Normally, there is no
point in being able to be able to access values for a parameter name
that are shadowed by newer values in a stack based context. The only
reason that this is needed in the map language is that the parameter
naming is automatic 1,2,3,... so that values that you might need are
shadowed automatically. It would have been better if the map language
would have had user named parameters instead, it would have been easier
to understand and we wouldn't have the current problem.
I would propose that we create a new interface that extends Map and
provide a method like:
List getStack(Object key);
or something similar. Then the map language implementation could depend
on this interface for handling its stack access, while the rest of the
ELs only needs to do access on a Map.
that is only a prototype and will be
replaced by Spring's, scoped bean that will utilize CallStack as Daniel
proposed earlier.
I've changed my mind ;) I still believe that using a custom Spring scope
that is similar to the CallStack is a good idea. But I think it might be
overly complicated to evolve the current CallStack implementation so
that it covers all our stack based bean access needs. I think it is
better to have an own stack for the object model. Then we can see at a
later stage if we can find some common abstraction. Right now I'm afraid
that it would just complicate things.
I wanted to use ObjectModel interface in cocoon-template-impl and
cocoon-expression-langauge-impl modules. After reading all the code I
found that current implementation is little unsatisfying. I'll explain
my concerns.
Let's examine how ExpressionContext[5] class looks like and what's
purpose.
The design was based on Jex
http://svn.apache.org/repos/asf/jakarta/commons/dormant/jex/ a
whiteboard project in Jakarta commons. Compared to that it was
simplified, Avalon based and adapted to Cocoon's needs. More about the
design can be found here
http://marc.info/?l=xml-cocoon-dev&m=110595826010124&w=2.
Basically, it's a holder for data passed to expression
evaluators. It can contain several named variables, context bean and
namespace table. This class is really vague for me because:
* it's not clear what's the difference between context bean and named
variables (you can put everything everywhere)
* namespace table is very mysterious thing when it comes to general
data holder and seems to be very specific
To find out what's the purpose for variabales/context bean you must take
a look at all implementations of Expression interface and find that it's
rather JXPath-specific feature. If ExpressionContext holds something as
variable ("variable1") you can access that data by using
"$variable1/[...]" expression, if the same data is held as context bean
it's available by "./[...]".
It is a JXPath specific feature.
Other expression languages implementations
ignore variables completely so if you happened to rely on this feature
you are in big trouble. How intuitive...
Might be. But it is not that easy, JXPath is supposed to be applied on a
bean (or DOM) and optionally parameters. Jexl on a map. I felt that
restricting to a common subset of input had been to limiting. You will
be in big trouble if you try to access an XML DOM with Jexl, but it
works fine with JXPath.
For our current use, my design was probably overly generic. We could
remove the contextBean and replace it with a reserved variable name in
the map in the context.
Namespace table is also very specific to JXPath (because only JXPath has
concept of namespace implemented) and I'll not discuss in detail how it
works. It only makes me wonder why it is put in rather general
ExpressionContext class.
No idea, I was not involved in implementing this part.
Situation is even more worst because Template
generator uses this namespace table for its very internal reasons
(emitting right SAX events, cleaning up namespace declarations and so on).
I'll show you, as an example, execute() method of
o.a.c.template.script.event.Event class:
public Event execute(final XMLConsumer consumer,
ExpressionContext expressionContext,
ExecutionContext executionContext,
MacroContext macroContext, Event startEvent,
Event endEvent)
throws SAXException {
return getNext();
}
You can see that method's signature is already quite fat - there are
three different contexts. What I'm going to propose is not much better
because I want to introduce one more argument (NamespaceTable
namespaces) and change existing ExpressionContext expressionContext to
ObjectModel objectModel. I just want to move namespace handling out of
general interface for data holders.
Before calling Expression#evaluate() I would like to add namespace table
to Object Model with "namespace" (or similiar) key so it's available for
JXPath implementation.
Sounds reasonable.
Ok, I'm not sure if such approach of slimming interface to absolute
minimum and putting things in a Map is right but nothing better comes to
my mind.
Could you give your advice? I'm really afraid that I'd made a commit
only to realize that my changes are dumb and there is better design.
You are taking things to seriously. Don't worry, just commit things and
refactor and change them later if you or anyone else get a better idea.
By not committing, you can be absolutely sure that your work will be of
no use.
/Daniel