/*
* Copyright 2004, 2005, 2006 Odysseus Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.odysseus.calyxo.base.taglib.html;
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.Tag;
/**
* Abstract base class for all HTML tags.
* <p/>
* Subclasses pass the tag name and a boolean indicating if the tag
* takes a body to the constructor.
* <p/>
* Subclasses may override the <code>init()</code> method, which is
* called when tag execution begins.
* <p/>
* Subclasses should override the <code>reset()</code> method to
* reset their additional fields.
* <p/>
* Subclasses will have to provide public Setters for their additional
* tag properties. Subclasses should implement <code>protected</code>
* methods to receive attributes to be rendered (at least for attributes
* that are considered to customized by subclasses).
* By convention, these methods should look like
* <pre>
* protected String getXyzAttribute() throws Exception {
* ...
* }
* </pre>
* to get their additional attributes rendered, subclasses should then
* override the <code>appendAttributes()</code> method like this:
* <pre>
* protected void appendAttributes(StringBuffer buffer) throws Exception {
* super.appendAttributes(buffer);
* ...
* append(buffer, "xyz", getXyzAttribute());
* ...
* }
* </pre>
* That way, all attributes with a non-<code>null</code> value will get
* rendered.
*
* @author Christoph Beck
*/
public abstract class AbstractTag implements Tag {
private String id;
protected PageContext pageContext;
private Tag parent;
private String name;
private boolean body;
/**
* Constructor.
*
* @param name tag name
* @param body <code>true</code> iff this tag has a body
*/
protected AbstractTag(String name, boolean body) {
this.name = name;
this.body = body;
}
/**
* Answer nearest <code>AbstractTag</code> ancestor
* which is assignable to the specified class.
*/
protected final AbstractTag findAncestor(Class type) {
if (type == null) {
return null;
}
Tag tag = parent;
while (tag != null) {
if (tag instanceof AbstractTag) {
if (type.isAssignableFrom(tag.getClass())) {
return (AbstractTag)tag;
}
}
tag = tag.getParent();
}
return null;
}
/**
* Answer nearest <code>AbstractTag</code> anchestor.
*/
protected final AbstractTag findParent() {
return findAncestor(AbstractTag.class);
}
/**
* Escape special characters
* <code><</code>,
* <code>></code>,
* <code>&</code>,
* <code>"</code>.
* Subclasses are responsible to call this method to provide
* filtered attribute values and body content.
*/
protected String filter(String value) {
if (value == null)
return null;
char chars[] = new char[value.length()];
value.getChars(0, value.length(), chars, 0);
StringBuffer result = new StringBuffer(chars.length + 20);
for (int i = 0; i < chars.length; i++) {
switch (chars[i]) {
case '<':
result.append("<");
break;
case '>':
result.append(">");
break;
case '&':
result.append("&");
break;
case '"':
result.append(""");
break;
default:
result.append(chars[i]);
}
}
return result.toString();
}
/**
* Append a single attribute <code>" name=value"</code> (with a leading
* blank) to specified buffer. This method may be called by
* {@link #appendAttributes(StringBuffer)} to render attributes.
* The attribute gets appended, iff the <code>value</code> parameter
* is not <code>null</code>.
* <p/>
*/
protected final void append(StringBuffer buffer, String name, String value) {
if (value != null) {
buffer.append(" ");
buffer.append(name);
buffer.append("=\"");
buffer.append(value);
buffer.append("\"");
}
}
/**
* Append attributes to be rendered to the specified buffer.
* Should be overridden by subclasses to add their additional
* attributes.
* By convention, the String for attribute <code>xyz</code> is
* accessed via method <code>getXyzAttribute()</code>, which
* might declare to throw an exception.
*/
protected void appendAttributes(StringBuffer buffer) throws Exception {
append(buffer, "id", getIdAttribute());
}
/**
* Initialize tag execution. This method is called
* after tag attribute properties have been populated.
* May be overridden by subclasses.
* This base implementation does nothing.
* @throws Exception
*/
protected void init() throws Exception {
}
/**
* Reset fields to their initial values.
* This method is called at the end of tag execution.
* Should be overridden by subclasses.
*/
protected void reset() {
id = null;
}
/**
* Get the tag body contents to be rendered.
* May be overridden by subclasses, that specify <code>body == true</code>.
* If this method returns <code>null</code>, the jsp tag body will be
* evaluated and included. Otherwise, the returned string will be
* inserted.
*/
protected String getBodyContent() throws Exception {
return null;
}
private void render(StringBuffer s) throws JspException {
if (s.length() > 0) {
try {
pageContext.getOut().print(s.toString());
} catch (IOException e) {
throw new JspException(e);
}
}
}
/*
* (non-Javadoc)
* @see javax.servlet.jsp.tagext.Tag#doStartTag()
*/
public final int doStartTag() throws JspException {
try {
init();
} catch (Exception e) {
throw new JspException(e);
}
StringBuffer s = new StringBuffer();
if (name != null) {
s.append("<");
s.append(name);
try {
appendAttributes(s);
} catch (Exception e) {
throw new JspException(e);
}
s.append(body ? ">" : "/>");
}
if (body) {
try {
String content = getBodyContent();
if (content != null) {
s.append(content);
render(s);
return SKIP_BODY;
}
} catch (Exception e) {
throw new JspException(e);
}
render(s);
return EVAL_BODY_INCLUDE;
} else {
render(s);
return SKIP_BODY;
}
}
/*
* (non-Javadoc)
* @see javax.servlet.jsp.tagext.Tag#doEndTag()
*/
public final int doEndTag() throws JspException {
if (name != null && body) {
StringBuffer s = new StringBuffer();
s.append("</");
s.append(name);
s.append(">");
render(s);
}
reset();
return EVAL_PAGE;
}
/*
* (non-Javadoc)
* @see javax.servlet.jsp.tagext.Tag#release()
*/
public void release() {
}
/**
* Get id property
*/
public final String getId() {
return id;
}
/**
* Set id property
*/
public final void setId(String string) {
id = string;
}
/**
* Get id attribute
*/
protected String getIdAttribute() throws Exception {
return id;
}
/* (non-Javadoc)
* @see javax.servlet.jsp.tagext.Tag#getParent()
*/
public final Tag getParent() {
return parent;
}
/* (non-Javadoc)
* @see javax.servlet.jsp.tagext.Tag#setPageContext(javax.servlet.jsp.PageContext)
*/
public final void setPageContext(PageContext value) {
pageContext = value;
}
/* (non-Javadoc)
* @see javax.servlet.jsp.tagext.Tag#setParent(javax.servlet.jsp.tagext.Tag)
*/
public final void setParent(Tag value) {
parent = value;
}
}