On Dec 19, 2007, at 12:10, Vincent Hennebert wrote:
Andreas L Delmelle wrote:
<snip />
Actually, the equivalent of addChildNode() for FOText is more or less
what currently happens in FObjMixed.flushText().
There’s a difference (AFAIU): addChildNode won’t be called if
validateChildNode failed.
More precisely: the child node itself will never even be created.
We're only validating a name at this point.
Whereas in the case of flushText validation is
performed at the same time the text is added. It’s “too late”.
Yep, in a way.
For things like white-space-handling, it is ideal to wait until the
block of text is complete, but to detect offending/spurious non-white-
space there is no need to do so.
The addCharacters() method does little more than record the
characters
/so far/.
Reason is mainly that the SAX parser is not obliged to report an
uninterrupted block of text as one single instance of char[], so the
consolidation into a single FOText instance (or multiple instances,
Which IMO doesn’t prevent to perform validation at the addCharacters
call.
I agree. Right now, we simply get a call to a method with an empty body.
We might as well implement that method in FObj, to always check the
incoming characters.
<snip />
No, the idea would be more that a call to super.validateChildNode()
would automatically take care of this particular aspect.
The only difference would be that, if the node is allowed to be
non-empty (i.e. does not implement the marker interface),
it does not even need to make this call... it can simply rely on
its own
implementation. :-)
I'm more inclined, actually, to put this very basic validation as
high
as possible in the class hierarchy.
This is already more or less the case with the invalidChildError
method.
I’d just add something like an acceptsEmptyContent method which would
call invalidChildError with a more meaningful message (“Content model:
empty”).
Moreover an if test would now be performed for each FObj instance.
Nope, precisely not if the non-empty FOs properly implement their own
rules, and do not call super.validateChildNode().
It is only true that, /iff/ they rely on the superclass'
implementation,
/then/ this test will be performed.
That should, normally, only be the case for all the empty FOs. If
they
can have children, those will need to be validated.
Then the EmptyFObj marker class wouldn’t be necessary,
neither the if test in FObj.validateChildNode, if only empty FObjs
make call to
super.validateChildNode!?
Indeed! Maybe they aren't, but I'm thinking in terms of the
extensibility of FONode. 'EmptyNode' would probably have been a
better choice for a name than 'EmptyFObj'...
A FONode always has a parent, never has any children, only (possibly)
siblings.
This is the common superclass for FObj (which optionally can have
FONode children), FOText (which never has any children), and a
handful of our proprietary extensions.
There is a major difference between a customized extension-node that
simply does not care about validation (any child is allowed), and one
that allows no children whatsoever.
If the subclass does not explicitly implement the method, it does not
necessarily mean that it accepts no children.
It does leave room, though, for creating a FONode that implements the
empty-interface, and still overrides that method...
Then again, in standard Java one can also design an object like:
<snip />
Sorry, don’t follow you, what are you trying to explain here?
That, no matter what design-choice you make, there will always be
room for people to abuse that design to do precisely the contrary of
what you intended.
Usually, one would expect people only to do so just for fun (to
demonstrate that it is possible).
That kills a bit the object-oriented design IMO. It seems more
consistent to me to rely on method
re-definition, after all this is the very purpose of object
inheritance.
Hmm... The purpose of inheritance to me is more: to rely on the
superclass implementation where possible, and implement specific
behavior only where needed.
Paraphrased: use method redefinition if necessary, and where it is
necessary, try to implement parts of the process in abstract
superclasses as much as possible, if they can be shared between
subclasses.
No: the abstract super-class implements only the bits common to /
every/
sub-class.
Moreover it must be sub-class agnostic: no “if this
instanceof ThatParticularSubClass” test. Simply because if I (or even
a non yet existing third party!) create a new sub-class, then I
mustn’t
have to change the abstract super-class to take this new class into
account.
That's exactly where the interface steps in, it has nothing to do
with breaking encapsulation: if the current node implements the
interface, and does not explicitly implement the method, any child
leads to an error. If it does not implement the interface, the child
is allowed (and still, the node could choose to ignore it, by not
implementing addChildNode()).
The abstract FONode class does not become less subclass-agnostic by
this. That is almost the same as saying that java.lang.Object.clone()
is not subclass-agnostic because it checks to see whether the passed
object is a Cloneable...?
In the particular case of FObj, the validateChildNode method
implemented
in this class would apply /only/ to fobjs with empty content.
No, both for empty nodes *and* nodes that allow any child.
This is wrong; either each and every class in the FObj hierarchy
should make
a call to super.validateChildNode, and add specific code in its own
method, or none of it.
Well, I don't agree with this either-or thinking.
If a class implements a method, it always has the /option/ of calling
super.someMethod() to delegate part of the processing to the
superclass, if it is applicable/possible/necessary.
Whether it does so or not, is up to the particular subclass. It is
not a matter of 'always' or 'never'.
In this case, having the nodes in question implement the interface
avoids the need to make that call, without throwing an error for any
node that chooses not to implement the method.
FObj is an abstract class: it defines method that each concrete
sub-class must implement, and that method must remain empty if no
behaviour can be extracted out of all of the concrete FObjs.
By adding the interface, we add a little something to extract, and in
doing so, we give third-parties a very clean option to make their
customized nodes either accept no children, or any child.
<snip />
And what if there were /two/ widespread behaviours? Which one to
choose?
This is the case anyway, with all those objects which accept (%
block;)+
as children.
There's something that had crossed my mind as well. Same goes for
accepting markers as initial children. That is also behavior that is
common to a number of FOs.
Those could be handled in a similar way, using marker-interfaces, or
as you suggested, by object composition.
Cheers
Andreas