Hello All,
So I'm working on hand-coding a forum-like website, just for the experience, and I want to have a similar feature as you'll see above where a thread will have its ancestors linked: "Web Development > JSP > Parent traversal of tree-like Structure"
I have several beans for different things, post, thread, category, forum and so on, all have a name property and all (will shortly, still adding) have a parent property.
I know I could do this all in Java code inside a tag handler that would just print the whole thing, but I'd rather do it in the JSP so that formatting will be more easy to tweak. In psudocode it would look something like:

while(currentthing.parent!=null)
    add currentthing.parent to ancestors
    currentthing=currentthing.parent
reverse ancestors
for thing in ancestors
    format and print link to thing and " > "
print this pages title inline with links

For now I do intend to just implement this in Java as a standalone custom tag (a bit difficult because everything is a different class, no ABC for all my beans, though i could make one for the name and parent attributes), but I want to know if there's a better way to do it.

Recommended Answers

All 2 Replies

Member Avatar for LastMitch

For now I do intend to just implement this in Java as a standalone custom tag (a bit difficult because everything is a different class, no ABC for all my beans, though i could make one for the name and parent attributes), but I want to know if there's a better way to do it.

I'm not very familiar with Java as a standalone custom tag.

You can take a look at this:

http://myarch.com/treeiter/traditways

I already understand tree traversal I just wanted to know if there was a good way of doing it with JSTL, this is what I came up with:

<mp:ParentLoop object="${thread}">
    <a class="parentlink" href="${curUrl}">${curName}</a> > 
</mp:ParentLoop>
${thread.name}

And the handler for it looks like this:

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.jsp.JspContext;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.JspFragment;
import javax.servlet.jsp.tagext.SimpleTagSupport;

/**
 *
 * @author William Matrix Peckham
 */
public class ParentLoop extends SimpleTagSupport {

    private Object object;

    /**
     * Called by the container to invoke this tag. 
     * The implementation of this method is provided by the tag library developer,
     * and handles all tag processing, body iteration, etc.
     */
    @Override
    public void doTag() throws JspException {
        JspWriter out = getJspContext().getOut();

        try {

            ArrayList<Object> names = new ArrayList<Object>();
            ArrayList<Object> urls = new ArrayList<Object>();

            while (object != null) {
                try {
                    Method getParent = object.getClass().getMethod("getParent");
                    Method getName = object.getClass().getMethod("getName");
                    Method getUrl = object.getClass().getMethod("getUrl");
                    names.add(getName.invoke(object));
                    urls.add(getUrl.invoke(object));
                    object = getParent.invoke(object);
                } catch (IllegalAccessException ex) {
                    Logger.getLogger(ParentLoop.class.getName()).
                            log(Level.SEVERE, null, ex);
                } catch (IllegalArgumentException ex) {
                    Logger.getLogger(ParentLoop.class.getName()).
                            log(Level.SEVERE, null, ex);
                } catch (InvocationTargetException ex) {
                    Logger.getLogger(ParentLoop.class.getName()).
                            log(Level.SEVERE, null, ex);
                } catch (NoSuchMethodException ex) {
                    Logger.getLogger(ParentLoop.class.getName()).
                            log(Level.SEVERE, null, ex);
                } catch (SecurityException ex) {
                    Logger.getLogger(ParentLoop.class.getName()).
                            log(Level.SEVERE, null, ex);
                }

            }

            Collections.reverse(names);//reverse them so that oldest ancestor is first
            Collections.reverse(urls);

            JspContext context = getJspContext();

            for (int i = 0; i < names.size()-1; i++) {//the -1 here keeps the tag from printing the link to the current page
                context.setAttribute("curName", names.get(i));
                context.setAttribute("curUrl", urls.get(i));
                JspFragment f = getJspBody();
                if (f != null) {
                    f.invoke(out);
                }

            }
            context.removeAttribute("curName");
            context.removeAttribute("curUrl");

        } catch (java.io.IOException ex) {
            throw new JspException("Error in ParentLoop tag", ex);
        }
    }

    public void setObject(Object object) {
        this.object = object;
    }
}

Reflection is used above to avoid requiring a base class for all objects. The only requirement is that the objects involved have a getParent() method and a getUrl() method. Besides the need for reflection this is a simple tag.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.