/* * Copyright (c) 2014 Darren Scott - All Rights Reserved * * This program is distributed under LGPL Version 2.1 in the hope that * it will be useful, but WITHOUT ANY WARRANTY. */ package org.bootstrapjsp.support; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import javax.servlet.jsp.JspException; import javax.servlet.jsp.tagext.JspTag; import javax.servlet.jsp.tagext.SimpleTagSupport; import org.bootstrapjsp.util.Config; public abstract class NestedTagSupport extends SimpleTagSupport implements BaseTag { public static final int BEFORE_BODY = -1; public static final int AROUND_BODY = 0; public static final int AFTER_BODY = 1; private final List<Class<? extends BaseTag>> parents = new ArrayList<Class<? extends BaseTag>>(); private final List<BaseTag> beforeChildren = new ArrayList<BaseTag>(); private final List<BaseTag> aroundChildren = new ArrayList<BaseTag>(); private final List<BaseTag> afterChildren = new ArrayList<BaseTag>(); private JspTag parent; @Override public void doTag() throws JspException, IOException { this.doBeforeBody(); if (this.aroundChildren.size() > 0) { BaseTag aroundChild = this.aroundChildren.get(0); aroundChild.setJspContext(this.getJspContext()); aroundChild.setJspBody(this.getJspBody()); aroundChild.doTag(); } else if (this.getJspBody() != null) { this.getJspBody().invoke(null); } this.doAfterBody(); } protected void doBeforeBody() throws JspException, IOException { this.renderChildren(this.beforeChildren); } protected void doAfterBody() throws JspException, IOException { this.renderChildren(this.afterChildren); } @Override public void setParent(JspTag parent) { if (parent != null && this.parents.size() > 0) { boolean valid = false; for (Class<? extends BaseTag> clazz : this.parents) { if (clazz.isAssignableFrom(parent.getClass())) { valid = true; break; } } if (!valid) { final StringBuilder error = new StringBuilder("Invalid parent for "); error.append(this).append(": ").append(parent); throw new IllegalArgumentException(error.toString()); } } this.parent = parent; super.setParent(parent); } public JspTag getParent() { return this.parent; } public void appendChild(BaseTag child) { this.appendChild(child, AROUND_BODY); } public void appendChild(BaseTag child, int position) { if (position == BEFORE_BODY) { this.beforeChildren.add(child); child.setParent(this); return; } else if (position == AROUND_BODY) { this.aroundChildren.add(child); child.setParent(this); return; } else if (position == AFTER_BODY) { this.afterChildren.add(child); child.setParent(this); return; } final StringBuilder message = new StringBuilder(); message.append("Position ").append(position).append(" not recognized"); throw new IllegalArgumentException(message.toString()); } public void setValidParents(Class<? extends BaseTag>... parents) { if (Config.validateParents()) { this.parents.addAll(Arrays.asList(parents)); } } @SuppressWarnings("unchecked") protected <T> T findAncestor(Class<? extends BaseTag> clazz) { return (T) SimpleTagSupport.findAncestorWithClass(this, clazz); } protected void renderChildren(List<BaseTag> children) throws JspException, IOException { for (final BaseTag child : children) { child.setJspContext(this.getJspContext()); child.doTag(); } } protected void wrapIn(NestedTagSupport wrapper) throws JspException, IOException { wrapper.appendChild(this); wrapper.setJspContext(super.getJspContext()); wrapper.setJspBody(super.getJspBody()); wrapper.doTag(); } }