DO NOT REPLY TO THIS EMAIL, BUT PLEASE POST YOUR BUG 
RELATED COMMENTS THROUGH THE WEB INTERFACE AVAILABLE AT
<http://nagoya.apache.org/bugzilla/show_bug.cgi?id=14094>.
ANY REPLY MADE TO THIS MESSAGE WILL NOT BE COLLECTED AND 
INSERTED IN THE BUG DATABASE.

http://nagoya.apache.org/bugzilla/show_bug.cgi?id=14094

use of Attribute limits types of content, bloats code

           Summary: use of Attribute limits types of content, bloats code
           Product: Struts
           Version: 1.1 Beta 2
          Platform: All
        OS/Version: All
            Status: NEW
          Severity: Enhancement
          Priority: Other
         Component: Tiles framework
        AssignedTo: [EMAIL PROTECTED]
        ReportedBy: [EMAIL PROTECTED]


low low priority....

This came up because I was writing my own templating system, that groks
dreamweaver templates, with custom bits done as jsps (this lets us track the
work of the web designer more faithfully). The way I'd started writing it I had
four distinct subsystems: a parser, a templating engine (with no custom taggery)
in the middle, a servlet to render the templates, and some custom tags for
customisation jsps (they put content into the templates prior to rendering). 

It occurred to me that I'd like to be able to use the tiles or template tags
instead of my own, to increase the amount of documentation available to our jsp
developers, and rely on many eyes for less bugs in the custom tags. The
abstractions I'd used for the template engine were reasonable enough and I
assumed this could be done; so, I read the code looking for an integration point. 

Bottom line is I couldnt do it, because there was no way to pass the knowledge
that, for example 'this is an URL which needs the context path added' through
tiles, without editing the code of tiles itself. The types of content it
supports are hardcoded everywhere using 'instanceof'. Eg (from InsertTag):
public TagHandler processTypedAttribute(AttributeDefinition value)
      throws JspException
                {
    if( value instanceof DirectStringAttribute )
      return new DirectStringHandler( (String)value.getValue() );
     else if( value instanceof DefinitionAttribute )
      return processDefinition( (ComponentDefinition)value.getValue() );
     else if( value instanceof DefinitionNameAttribute )
      {
      return processDefinitionName( (String)value.getValue() );
    }
   }

ewwwww...  this code would clearly make more sense (and be more extensible)
refactored to:

     public TagHandler processTypedAttribute(AttributeDefinition value)
      throws JspException {
        return value.getTagHandler();
     }

..., with appropriate getTagHandler() methods, and then removing the method
entirely. Big switches are clearly useful creating Attribute objects, since you
only have the string arguments of the Put tag as a guide, but it doesnt make
sense to limit yourself at output time to only objects you can define via the
Put tag.

A concrete example: you can include content from Beans using tiles, but only by
calling 'toString'. If you have a large chunk of text behind this (eg from a
content management system) the bean may be more efficiently written if it can
stream the content. 

I realise adding an abstraction like this may be a non-goal, especially for
getting struts 1.1 out the door, but I'm just going to add some notes on the api
I ended up with in case this proves useful later. I'm sure you'd have better ideas! 

My parse tree from the templates is built mainly from Content objects, like so:
public interface Content {
    public void service(PageContext context,ContentMap[] stack)
        throws ServletException, IOException;
    // named (replaceable) content in a template is distinguished
    // from anonymous content simply by having a non-null name.
    public String getName();
}
(nb the names of some of the classes here may resemble those in tiles/templates,
but the apis are often different). 

The PageContext passed in is similar to the JSP page context, and for similar
reasons - it gives Content access to beans in all the scopes. (I dropped a
couple of methods from JSP's PageContext - the ones to do with the 'body'). This
allows me to implement includes like so:
    public void service(PageContext context, ContentMap[] stack) throws
ServletException, IOException {
         context.include(value);
    }
The relevance to tiles would be having the Put tag create PathAttribute objects
with this method, so they know how to render /themselves/.

The ContentMap[] passed is the equivalent of ContentMapStack in the 'templates'
library. The implementation of the service method of a Template, which uses
this, looks like:
    public void service(PageContext context,ContentMap[] stack) throws
ServletException, IOException {
        if (this.template!=null) {
            context.getPage().service(context,template);
            return;
        }
        Iterator iter=contentList.iterator();
        Content content=null;
        String cname=null;
        while(iter.hasNext()) {
            content=(Content)iter.next();
            cname=content.getName();
            if (cname!=null) {
                Content replacement=null;
                for (int i=0; i<stack.length; i++) {
                    replacement=stack[i].getContent(cname);
                    if (replacement!=null) {
                        content=replacement;
                        break;
                    }
                }
            }
            content.service(context,stack);
        }
    }
Obviously this isnt quite so relevant to tiles as you're using jsp templates, 
but it demonstrates how replacement of bits of content kicks in in my stuff.

finally, an implementation of BeanContent (going back to the 'concrete example'
above where I had a bean from a CMS that knew how to stream its content):

    public void service(PageContext context,ContentMap[] stack) throws
ServletException, IOException {
        // find the bean
        Object bean=context.getAttribute(beanName,beanScope);
        // code using BeanUtils to get the bean from a property elided
        if (bean instanceof Content) {
            // bean, render thyself...
            ((Content)bean).service(context,stack);
        } else {
            // ok the bean is dumb - so just use 'toString'
            value=bean.toString();
            if (direct!=null) { // ok testing for null here is hackery.        
      
                context.getOut().write(value);
            } else {
                context.include(value);
            }
        }
    }

Code like the above would replace much of the ugly if/else if/else if stuff in
InsertTag.

--
To unsubscribe, e-mail:   <mailto:struts-dev-unsubscribe@;jakarta.apache.org>
For additional commands, e-mail: <mailto:struts-dev-help@;jakarta.apache.org>

Reply via email to