JSP e Tag Extensions
("Custom Tags" ou "Taglibs")

Introdução

Por quê?

Conceitos-chave de tags

Tag Handlers e Tag Library Descriptors (TLDs)

Usos típicos de tags

The good news

Primeiro Exemplo: Simple Tag

Hello, world.
My name is <classe de implementação do tag handler> and it's <data e horas>

O que queremos

<%@ taglib uri="/hello" prefix="examples" %>
<html>
  <head>
    <title>First custom tag</title>
  </head>
  <body>
Saida estatica.
<p/>
<i>
  <examples:hello>
  </examples:hello>
</i>
Mais saida estatica.
  </body>
</html>

O tag handler

package tagext;

import java.io.IOException;
import java.util.Date;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;

/**
*    Implementation of a tag to generate a single piece of HTML.
*    @author    Rod Johnson
*/
public class HelloTag extends TagSupport {

    /**
    *    This method will be called when the JSP engine encounters the start
    *    of a tag implemented by this class
    */
    public int doStartTag() throws JspTagException {
        // This return value means that the JSP engine should evaluate
        // the contents and any child tags of this tag
        return EVAL_BODY_INCLUDE;
    }

    /**
    *    This method will be called when the JSP engine encounters the end
    *    of a tag implemented by this class
    */
    public int doEndTag() throws JspTagException {
        String dateString = new Date().toString();
        try {
            pageContext.getOut().write("Hello world.<br/>");
            pageContext.getOut().write("My name is " + getClass().getName() +
                                        " and it's " + dateString + "<p/>");
        }
        catch (IOException ex) {
            throw new JspTagException("Fatal error: hello tag could not write to JSP out");
        }

        // This return value means that the JSP engine should continue to
        // evaluate the rest of this page
        return EVAL_PAGE;
    }
}    // class HelloTag 

O Tag Library Descriptor

<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE taglib
        PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN"
        "http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd">
<taglib>
  <tlibversion>1.0</tlibversion>
  <jspversion>1.1</jspversion>
  <shortname>examples</shortname>

  <info>Simple example library.</info>

  <tag>
    <name>hello</name>
    <tagclass>tagext.HelloTag</tagclass>
    <bodycontent>JSP</bodycontent>
    <info>Simple example</info>
  </tag>
</taglib>

Colocando para funcionar

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE web-app PUBLIC '-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN' 'http://java.sun.com/j2ee/dtds/web-app_2.2.dtd'>

<web-app>
  <display-name>tagext</display-name>
  <description>Tag extensions examples</description>
  
  <session-config>
    <session-timeout>0</session-timeout>
  </session-config>
  
  <taglib>
  	<taglib-uri>/hello</taglib-uri>
  	<taglib-location>/WEB-INF/tlds/hello.tld</taglib-location>
  </taglib>
  
</web-app>
jar -cvf hello.war WEB-INF hello.jsp

Segundo Exemplo: Um tag com atributos

O que queremos

package org.apache.struts.webapp.example;

import java.io.IOException;
import javax.servlet.http.HttpSession;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.TagSupport;
import org.apache.struts.action.Action;
import org.apache.struts.util.BeanUtils;
import org.apache.struts.util.MessageResources;


/**
 * Check for a valid User logged on in the current session.  If there is no
 * such user, forward control to the logon page.
 *
 * @author Craig R. McClanahan
 * @author Marius Barduta
 * @version $Revision: 1.2 $ $Date: 2001/04/14 12:53:07 $
 */

public final class CheckLogonTag extends TagSupport {
    // --------------------------------------------------- Instance Variables
    /**
     * The key of the session-scope bean we look for.
     */
    private String name = Constants.USER_KEY;

    /**
     * The page to which we should forward for the user to log on.
     */
    private String page = "/logon.jsp";

    // ----------------------------------------------------------- Properties
    /**
     * Return the bean name.
     */
    public String getName() {
        return (this.name);
    }

    /**
     * Set the bean name.
     *
     * @param name The new bean name
     */
    public void setName(String name) {
        this.name = name;
    }


    /**
     * Return the forward page.
     */
    public String getPage() {
        return (this.page);
    }


    /**
     * Set the forward page.
     *
     * @param page The new forward page
     */
    public void setPage(String page) {
        this.page = page;
    }

    // ------------------------------------------------------- Public Methods
    /**
     * Defer our checking until the end of this tag is encountered.
     *
     * @exception JspException if a JSP exception has occurred
     */
    public int doStartTag() throws JspException {
        return (SKIP_BODY);
    }

    /**
     * Perform our logged-in user check by looking for the existence of
     * a session scope bean under the specified name.  If this bean is not
     * present, control is forwarded to the specified logon page.
     *
     * @exception JspException if a JSP exception has occurred
     */
    public int doEndTag() throws JspException {
        // Is there a valid user logged on?
        boolean valid = false;
        HttpSession session = pageContext.getSession();
        if ((session != null) && (session.getAttribute(name) != null))
            valid = true;
    
        // Forward control based on the results
        if (valid)
            return (EVAL_PAGE);
        else {
            try {
                pageContext.forward(page);
            } catch (Exception e) {
                throw new JspException(e.toString());
            }
            return (SKIP_PAGE);
        }
    }

    /**
     * Release any acquired resources.
     */
    public void release() {
        super.release();
        this.name = Constants.USER_KEY;
        this.page = "/logon.jsp";
    }
}

Terceiro Exemplo: Outro tag com parâmetros

O que queremos

present - Generate the nested body content of this tag if the specified value is present in this request.

Depending on which attribute is specified, this tag checks the current request, and evaluates the nested body content of this tag only if the specified value is present. Only one of the attributes may be used in one occurrence of this tag, unless you use the property attribute, in which case the name attribute is also required.

Attribute Name Description
cookie

Checks for the existence of a cookie with the specified name.

[RT Expr] [Jacques: significa que pode ser um "request-time expression"]
header

Checks for the existence of an HTTP header with the specified name. The name match is performed in a case insensitive manner.

[RT Expr]
name

Checks for the existence of a JSP bean, in any scope, with the specified name. If property is also specified, checks for a non-null property value for the specified property.

[RT Expr]
parameter

Checks for the existence of at least one occurrence of the specified request parameter on this request, even if the parameter value is a zero-length string.

[RT Expr]
property

Checks for the existence of a non-null property value, returned by a property getter method on the JSP bean (in any scope) that is specified by the name attribute. Property references can be simple, nested, and/or indexed.

[RT Expr]
role

Checks whether the currently authenticated user (if any) has been associated with the specified security role.

[RT Expr]
scope

The bean scope within which to search for the bean named by the name property, or "any scope" if not specified.

[RT Expr]
user

Checks whether the currently authenticated user principal has the specified name.

[RT Expr]
package org.apache.struts.taglib.logic;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport;
import org.apache.struts.util.MessageResources;

/**
 * Abstract base class for the various conditional evaluation tags.
 *
 * @author Craig R. McClanahan
 * @version $Revision: 1.5 $ $Date: 2001/02/12 21:49:54 $
 */
public abstract class ConditionalTagBase extends TagSupport {
    // ------------------------------------------------------------- Properties
    /**
     * The name of the cookie to be used as a variable.
     */
    protected String cookie = null;

    public String getCookie() {
        return (this.cookie);
    }

    public void setCookie(String cookie) {
        this.cookie = cookie;
    }

    /**
     * The name of the HTTP request header to be used as a variable.
     */
    protected String header = null;

    public String getHeader() {
        return (this.header);
    }

    public void setHeader(String header) {
        this.header = header;
    }

    /**
     * The message resources for this package.
     */
    protected static MessageResources messages =
     MessageResources.getMessageResources
        ("org.apache.struts.taglib.logic.LocalStrings");

    /**
     * The name of the JSP bean to be used as a variable (if
     * <code>property</code> is not specified), or whose property is to be
     * accessed (if <code>property</code> is specified).
     */
    protected String name = null;

    public String getName() {
        return (this.name);
    }

    public void setName(String name) {
        this.name = name;
    }

    /**
     * The name of the HTTP request parameter to be used as a variable.
     */
    protected String parameter = null;

    public String getParameter() {
        return (this.parameter);
    }

    public void setParameter(String parameter) {
        this.parameter = parameter;
    }

    /**
     * The name of the bean property to be used as a variable.
     */
    protected String property = null;

    public String getProperty() {
        return (this.property);
    }

    public void setProperty(String property) {
        this.property = property;
    }

    /**
     * The name of the security role to be checked for.
     */
    protected String role = null;

    public String getRole() {
        return (this.role);
    }

    public void setRole(String role) {
        this.role = role;
    }

    /**
     * The scope to search for the bean named by the name property, or
     * "any scope" if null.
     */
    protected String scope = null;

    public String getScope() {
        return (this.scope);
    }

    public void setScope(String scope) {
        this.scope = scope;
    }

    /**
     * The user principal name to be checked for.
     */
    protected String user = null;

    public String getUser() {
        return (this.user);
    }

    public void setUser(String user) {
        this.user = user;
    }

    // --------------------------------------------------------- Public Methods

    /**
     * Perform the test required for this particular tag, and either evaluate
     * or skip the body of this tag.
     *
     * @exception JspException if a JSP exception occurs
     */
    public int doStartTag() throws JspException {
        if (condition())
            return (EVAL_BODY_INCLUDE);
        else
            return (SKIP_BODY);
    }

    /**
     * Evaluate the remainder of the current page normally.
     *
     * @exception JspException if a JSP exception occurs
     */
    public int doEndTag() throws JspException {
        return (EVAL_PAGE);
    }


    /**
     * Release all allocated resources.
     */
    public void release() {
        super.release();
        cookie = null;
        header = null;
        name = null;
        parameter = null;
        property = null;
        role = null;
        scope = null;
        user = null;
    }

    // ------------------------------------------------------ Protected Methods
    /**
     * Evaluate the condition that is being tested by this particular tag,
     * and return <code>true</code> if the nested body content of this tag
     * should be evaluated, or <code>false</code> if it should be skipped.
     * This method must be implemented by concrete subclasses.
     *
     * @exception JspException if a JSP exception occurs
     */
    protected abstract boolean condition() throws JspException;
}
package org.apache.struts.taglib.logic;

import java.security.Principal;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import org.apache.struts.util.PropertyUtils;
import org.apache.struts.util.RequestUtils;

/**
 * Evalute the nested body content of this tag if the specified value
 * is present for this request.
 *
 * @author Craig R. McClanahan
 * @version $Revision: 1.7.2.1 $ $Date: 2001/06/11 17:34:40 $
 */
public class PresentTag extends ConditionalTagBase {
    // ------------------------------------------------------ Protected Methods
    /**
     * Evaluate the condition that is being tested by this particular tag,
     * and return <code>true</code> if the nested body content of this tag
     * should be evaluated, or <code>false</code> if it should be skipped.
     * This method must be implemented by concrete subclasses.
     *
     * @exception JspException if a JSP exception occurs
     */
    protected boolean condition() throws JspException {
        return (condition(true));
    }

    /**
     * Evaluate the condition that is being tested by this particular tag,
     * and return <code>true</code> if the nested body content of this tag
     * should be evaluated, or <code>false</code> if it should be skipped.
     * This method must be implemented by concrete subclasses.
     *
     * @param desired Desired outcome for a true result
     *
     * @exception JspException if a JSP exception occurs
     */
    protected boolean condition(boolean desired) throws JspException {
        // Evaluate the presence of the specified value
        boolean present = false;
        if (cookie != null) {
            Cookie cookies[] =
                ((HttpServletRequest) pageContext.getRequest()).
                getCookies();
            if (cookies == null)
                cookies = new Cookie[0];
            for (int i = 0; i < cookies.length; i++) {
                if (cookie.equals(cookies[i].getName())) {
                    present = true;
                    break;
                }
            }
        } else if (header != null) {
            String value =
                ((HttpServletRequest) pageContext.getRequest()).
                getHeader(header);
            present = (value != null);
        } else if (name != null) {
            Object value = null;
            try {
                value =
                    RequestUtils.lookup(pageContext, name, property, scope);
            } catch (JspException e) {
                value = null;
            }
            present = (value != null);
        } else if (parameter != null) {
            String value =
                pageContext.getRequest().getParameter(parameter);
            present = (value != null);
        } else if (role != null) {
            HttpServletRequest request =
                (HttpServletRequest) pageContext.getRequest();
            present = request.isUserInRole(role);
        } else if (user != null) {
            HttpServletRequest request =
                (HttpServletRequest) pageContext.getRequest();
            Principal principal = request.getUserPrincipal();
            present = (principal != null) &&
                user.equals(principal.getName());
        } else {
            JspException e = new JspException
                (messages.getMessage("logic.selector"));
            RequestUtils.saveException(pageContext, e);
            throw e;
        }
        return (present == desired);
    }
}

Quarto  Exemplo: Um tag com corpo

O que queremos

iterate - Repeat the nested body content of this tag over a specified collection.

Repeats the nested body content of this tag once for every element of the specified collection, which must be an Iterator, a Collection, a Map (whose values are to be iterated over), or an array. The collection to be iterated over must be specified in one of the following ways:

  • As a runtime expression specified as the value of the collection attribute.
  • As a JSP bean specified by the name attribute.
  • As the property, specified by the property, of the JSP bean specified by the name attribute.

The collection to be iterated over MUST conform to one of the following requirements in order for iteration to be successful:

  • An array of Java objects (but not primitive data types such as "int")
  • An implementation of java.util.Collection, including ArrayList and Vector.
  • An implementation of java.util.Enumeration.
  • An implementation of java.util.Iterator.
  • An implementation of java.util.Map, including HashMap, Hashtable, and TreeMap. NOTE - See below for additional information about accessing Maps.

Normally, each object exposed by the iterate tag is an element of the underlying collection you are iterating over. However, if you iterate over a Map, the exposed object is of type Map.Entry that has two properties:

  • key - The key under which this item is stored in the underlying Map.
  • value - The value that corresponds to this key.

So, if you wish to iterate over the values of a Hashtable, you would implement code like the following:

<logic:iterate id="element" name="myhashtable">
Next element is <bean:write name="element" property="value"/>
</logic:iterate>

If the collection you are iterating over can contain null values, the loop will still be performed but no page scope attribute (named by the id attribute) will be created for that loop iteration. You can use the <logic:present> and <logic:notPresent> tags to test for this case.

WARNING - Currently, this tag cannot deal with arrays of primitive data types. Only arrays of Java objects (including Strings) are supported.

Attribute Name Description
collection

A runtime expression that evaluates to a collection (conforming to the requirements listed above) to be iterated over.

[RT Expr]
id

The name of a page scope JSP bean that will contain the current element of the collection on each iteration, if it is not null.

[Required] [RT Expr]
indexId

The name of a page scope JSP bean that will contain the current index of the collection on each iteration.

[RT Expr]
length

The maximum number of entries (from the underlying collection) to be iterated through on this page. This can be either an integer that directly expresses the desired value, or the name of a JSP bean (in any scope) of type java.lang.Integer that defines the desired value. If not present, there will be no limit on the number of iterations performed.

[RT Expr]
name

The name of the JSP bean containing the collection to be iterated (if property is not specified), or the JSP bean whose property getter returns the collection to be iterated (if property is specified).

[RT Expr]
offset

The zero-relative index of the starting point at which entries from the underlying collection will be iterated through. This can be either an integer that directly expresses the desired value, or the name of a JSP bean (in any scope) of type java.lang.Integer that defines the desired value. If not present, zero is assumed (meaning that the collection will be iterated from the beginning.

[RT Expr]
property

Name of the property, of the JSP bean specified by name, whose getter returns the collection to be iterated.

[RT Expr]
scope

The bean scope within which to search for the bean named by the name property, or "any scope" if not specified.

[RT Expr]
type

Fully qualified Java class name of the element to be exposed through the JSP bean named from the id attribute. If not present, no type conversions will be performed. NOTE: The actual elements of the collection must be assignment-compatible with this class, or a request time ClassCastException will occur.

[RT Expr]
package org.apache.struts.taglib.logic;

import java.util.Arrays;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Map;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.BodyTagSupport;

import org.apache.struts.util.IteratorAdapter;
import org.apache.struts.util.MessageResources;
import org.apache.struts.util.PropertyUtils;
import org.apache.struts.util.RequestUtils;
import org.apache.struts.util.ResponseUtils;

/**
 * Custom tag that iterates the elements of a collection, which can be
 * either an attribute or the property of an attribute.  The collection
 * can be any of the following:  an array of objects, an Enumeration,
 * an Iterator, a Collection (which includes Lists, Sets and Vectors),
 * or a Map (which includes Hashtables) whose elements will be iterated over.
 *
 * @author Craig R. McClanahan
 * @version $Revision: 1.11.2.1 $ $Date: 2001/06/14 04:24:27 $
 */

public class IterateTag extends BodyTagSupport {
    // ----------------------------------------------------- Instance Variables
    /**
     * Iterator of the elements of this collection, while we are actually
     * running.
     */
    protected Iterator iterator = null;

    /**
     * The number of elements we have already rendered.
     */
    protected int lengthCount = 0;

    /**
     * The actual length value (calculated in the start tag).
     */
    protected int lengthValue = 0;

    /**
     * The message resources for this package.
     */
    protected static MessageResources messages =
    MessageResources.getMessageResources
    ("org.apache.struts.taglib.logic.LocalStrings");

    /**
     * The actual offset value (calculated in the start tag).
     */
    protected int offsetValue = 0;

    /**
     * Has this tag instance been started?
     */
    protected boolean started = false;

    // ------------------------------------------------------------- Properties
    /**
     * The collection over which we will be iterating.
     */
    protected Object collection = null;

    public Object getCollection() {
        return (this.collection);
    }

    public void setCollection(Object collection) {
        this.collection = collection;
    }


    /**
     * The name of the scripting variable to be exposed.
     */
    protected String id = null;

    public String getId() {
        return (this.id);
    }

    public void setId(String id) {
        this.id = id;
    }

    /**
     * <p>Return the zero-relative index of the current iteration through the
     * loop.  If you specify an <code>offset</code>, the first iteration
     * through the loop will have that value; otherwise, the first iteration
     * will return zero.</p>
     *
     * <p>This property is read-only, and gives nested custom tags access to
     * this information.  Therefore, it is <strong>only</strong> valid in
     * between calls to <code>doStartTag()</code> and <code>doEndTag()</code>.
     * </p>
     */
    public int getIndex() {
        if (started)
            return (offsetValue + lengthCount - 1);
        else
            return (0);
    }

    /**
     * The name of the scripting variable to be exposed as the current index.
     */
    protected String indexId = null;

    public String getIndexId() {
        return (this.indexId);
    }

    public void setIndexId(String indexId) {
        this.indexId = indexId;
    }

    /**
     * The length value or attribute name (<=0 means no limit).
     */
    protected String length = null;

    public String getLength() {
        return (this.length);
    }

    public void setLength(String length) {
        this.length = length;
    }

    /**
     * The name of the collection or owning bean.
     */
    protected String name = null;

    public String getName() {
        return (this.name);
    }

    public void setName(String name) {
        this.name = name;
    }

    /**
     * The starting offset (zero relative).
     */
    protected String offset = null;

    public String getOffset() {
        return (this.offset);
    }

    public void setOffset(String offset) {
        this.offset = offset;
    }

    /**
     * The property name containing the collection.
     */
    protected String property = null;

    public String getProperty() {
        return (this.property);
    }

    public void setProperty(String property) {
        this.property = property;
    }

    /**
     * The scope of the bean specified by the name property, if any.
     */
    protected String scope = null;

    public String getScope() {
        return (this.scope);
    }

    public void setScope(String scope) {
        this.scope = scope;
    }

    /**
     * The Java class of each exposed element of the collection.
     */
    protected String type = null;

    public String getType() {
        return (this.type);
    }

    public void setType(String type) {
        this.type = type;
    }

    // --------------------------------------------------------- Public Methods
    /**
     * Construct an iterator for the specified collection, and begin
     * looping through the body once per element.
     *
     * @exception JspException if a JSP exception has occurred
     */
    public int doStartTag() throws JspException {

    // Acquire the collection we are going to iterate over
        Object collection = this.collection;
    if (collection == null)
            collection =
                RequestUtils.lookup(pageContext, name, property, scope);
        if (collection == null) {
            JspException e = new JspException
                (messages.getMessage("iterate.collection"));
            RequestUtils.saveException(pageContext, e);
            throw e;
        }


    // Construct an iterator for this collection
    if (collection.getClass().isArray())
        collection = Arrays.asList((Object[]) collection);
    if (collection instanceof Collection)
        iterator = ((Collection) collection).iterator();
    else if (collection instanceof Iterator)
        iterator = (Iterator) collection;
    else if (collection instanceof Map)
        iterator = ((Map) collection).entrySet().iterator();
    else if (collection instanceof Enumeration)
        iterator = new IteratorAdapter((Enumeration)collection);
       else {
        JspException e = new JspException
            (messages.getMessage("iterate.iterator"));
            RequestUtils.saveException(pageContext, e);
            throw e;
        }

    // Calculate the starting offset
    if (offset == null)
        offsetValue = 0;
    else {
        try {
            offsetValue = Integer.parseInt(offset);
        } catch (NumberFormatException e) {
        Integer offsetObject =
          (Integer) pageContext.findAttribute(offset);
        if (offsetObject == null)
            offsetValue = 0;
        else
            offsetValue = offsetObject.intValue();
        }
    }
    if (offsetValue < 0)
        offsetValue = 0;

    // Calculate the rendering length
    if (length == null)
        lengthValue = 0;
    else {
        try {
        lengthValue = Integer.parseInt(length);
        } catch (NumberFormatException e) {
        Integer lengthObject =
          (Integer) pageContext.findAttribute(length);
        if (lengthObject == null)
            lengthValue = 0;
        else
            lengthValue = lengthObject.intValue();
        }
    }
    if (lengthValue < 0)
        lengthValue = 0;
    lengthCount = 0;

    // Skip the leading elements up to the starting offset
    for (int i = 0; i < offsetValue; i++) {
        if (iterator.hasNext()) {
            Object element = iterator.next();
        }
    }

    // Store the first value and evaluate, or skip the body if none
    if (iterator.hasNext()) {
        Object element = iterator.next();
            if (element == null)
                pageContext.removeAttribute(id);
            else
                pageContext.setAttribute(id, element);
        lengthCount++;
            started = true;
            if (indexId != null)
                pageContext.setAttribute(indexId, new Integer(getIndex()));
        return (EVAL_BODY_TAG);
        } else
            return (SKIP_BODY);

    }

    /**
     * Make the next collection element available and loop, or
     * finish the iterations if there are no more elements.
     *
     * @exception JspException if a JSP exception has occurred
     */
    public int doAfterBody() throws JspException {

        // Render the output from this iteration to the output stream
        if (bodyContent != null) {
            ResponseUtils.writePrevious(pageContext, bodyContent.getString());
            bodyContent.clearBody();
        }

        // Decide whether to iterate or quit
        if ((lengthValue > 0) && (lengthCount >= lengthValue))
            return (SKIP_BODY);
        if (iterator.hasNext()) {
            Object element = iterator.next();
            if (element == null)
                pageContext.removeAttribute(id);
            else
                pageContext.setAttribute(id, element);
            lengthCount++;
            if (indexId != null)
                pageContext.setAttribute(indexId, new Integer(getIndex()));
            return (EVAL_BODY_TAG);
        } else
            return (SKIP_BODY);
    }

    /**
     * Clean up after processing this enumeration.
     *
     * @exception JspException if a JSP exception has occurred
     */
    public int doEndTag() throws JspException {
        // Clean up our started state
        started = false;

        // Continue processing this page
        return (EVAL_PAGE);
    }


    /**
     * Release all allocated resources.
     */
    public void release() {
        super.release();
        iterator = null;
        lengthCount = 0;
        lengthValue = 0;
        offsetValue = 0;

        id = null;
        collection = null;
        length = null;
        name = null;
        offset = null;
        property = null;
        scope = null;
        started = false;
    }
}

tags programa