/*
 * Qualiti Software Processes
 *
 * Qualiti Coder
 *
 * Tipo: PrettyPrintingVisitor
 *
 * Esta classe é responsável por realizar a clonagem da árvore sintática de
 * um programa JaTS
 *
 * Autor Fernando Castor
 * Autor Adeline Sousa
 *
 * Version 01/07/2004
 */
package cin.jats.engine.visitors;


import cin.jats.engine.parser.nodes.*;
import cin.jats.engine.parser.nodes.exceptions.InconsistentNodeException;
import cin.jats.engine.util.NodeList;
import cin.jats.engine.util.NodeStack;


/**
 * Classe que implementa a interface <code>JaTSVisitor</code> por meio de um
 * visitor que realiza a clonagem de uma árvore sintática.
 *
 * @see JaTSNode
 * @see JaTSVisitor
 *
 * @author Fernando Castor
 * @author Adeline Sousa
 */
public class CloningVisitor implements Visitor{


    // Pilha responsável por guardar o estado atual da clonagem.
    private NodeStack state;


    /**
     * Construtor vazio da classe.
     */
    public CloningVisitor() {
        state = new NodeStack();
    }


    /**
     *  Método auxiliar para evitar repetição de código na clonagem de
     * declarações  condicionais.
     */
    public void cloneConditionalDeclaration(JConditionalDeclaration node,
                                            JConditionalDeclaration resultNode)
            throws IllegalArgumentException, InconsistentNodeException {

        if (node == null) {
            throw new IllegalArgumentException();
        }

        // Clona a lista de declarações que é processada caso a condição avalie
        // para false.
        resultNode.setFalseDeclarationSet(
                this.cloneFalseDeclarationSetOfConditionalDeclaration(node, resultNode));

        // Clona a lista de declarações que é processada caso a condição avalie
        // para true.
        resultNode.setTrueDeclarationSet(
                this.cloneTrueDeclarationSetOfConditionalDeclaration(node, resultNode));

        // Clona a condição da declaração condicional.
        resultNode.setCondition(
                this.cloneConditionConditionalDeclaration(node));

        this.doCommonCloningWork(node, resultNode);
    }

    /**
     * Método auxiliar para a clonagem de declarações condicionais
     */
    private JExpression cloneConditionConditionalDeclaration(
            JConditionalDeclaration node) throws InconsistentNodeException {

        JExpression resultNode = null;

        if (node.getCondition() == null
                || !(this.state.top() instanceof JExpression)) {
            throw new InconsistentNodeException(node);
        }

        resultNode = (JExpression) this.state.pop();
        return resultNode;
    }

    /**
     *  Método auxiliar na clonagem de declarações iterativas.
     */
    private NodeList cloneDeclarationSetOfIterativeDeclaration(
            IterativeDeclaration node, IterativeDeclaration resultNode) {

        NodeList decSet = new NodeList();

        int i = node.getDeclarationSet().size() - 1;
        while (i >= 0) {
            JaTSNode dec = this.state.pop();
            decSet.addElement(dec);
            if (dec instanceof JConditionalDeclaration) {
                ((JConditionalDeclaration) dec).setParentNode(resultNode);
            }
            i--;
        }

        return decSet;
    }

    /**
     * Método auxiliar para realizar a clonagem de declarações executáveis.
     */
    private JaTSNode cloneExecutableDeclaration(JaTSNode node, JaTSNode resultNode)
            throws IllegalArgumentException, InconsistentNodeException {

        if (node == null) {
            throw new IllegalArgumentException();
        }

        JaTSNode retorno = null;

        JaTSNode topoPilha = this.state.top();

        if (!((topoPilha instanceof JExecutableDeclaration)
                || (topoPilha instanceof NestableExecutableDeclaration))
                && node.isExecutable()) {
            throw new InconsistentNodeException(node);
        } else if (((topoPilha instanceof JExecutableDeclaration)
                || (topoPilha instanceof NestableExecutableDeclaration))
                && (node.isExecutable())) {
            retorno = this.state.pop();
        }

        if (retorno instanceof NestableExecutableDeclaration) {
            ((NestableExecutableDeclaration) retorno).setParentNode(resultNode);
        }

        return retorno;
    }

    /**
     * Método auxiliar na clonagem de declarações condicionais.
     */
    private NodeList cloneFalseDeclarationSetOfConditionalDeclaration(
            JConditionalDeclaration node, JConditionalDeclaration resultNode) {

        NodeList decSet = new NodeList();
        NodeList fDecSet = node.getFalseDeclarationSet();

        if (fDecSet != null) {
            int i = fDecSet.size() - 1;
            while (i >= 0) {
                JaTSNode dec = this.state.pop();
                decSet.addElement(dec);
                i--;
            }
            this.setParentNode(decSet, resultNode);
        }
        return decSet;
    }

    /**
     * Método auxiliar na clonagem de declarações iterativas.
     */
    private JaTSNode cloneIterationSetOfIterativeDeclaration(
            IterativeDeclaration node) throws InconsistentNodeException {

        JaTSNode iterSet = null;

        if (node.getIterationSet() == null) {
            throw new InconsistentNodeException(node);
        }
        iterSet = this.state.pop();

        return iterSet;
    }

    /**
     *  Método auxiliar para evitar repetição de código na clonagem de declarações
     *  iterativas.
     */
    private void cloneIterativeDeclaration(IterativeDeclaration node,
                                           IterativeDeclaration resultNode)
            throws IllegalArgumentException, InconsistentNodeException {

        if (node == null) {
            throw new IllegalArgumentException();
        }

        // Clona o iteration set do nó.
        resultNode.setIterationSet(
                this.cloneIterationSetOfIterativeDeclaration(node));

        // Clona o declaration set do nó.
        resultNode.setDeclarationSet(
                this.cloneDeclarationSetOfIterativeDeclaration(node, resultNode));

        // Clona o placeholder do nó.
        resultNode.setPlaceholder(
                this.clonePlaceholderOfIterativeDeclaration(node));

        CloningVisitor.setParentNode(resultNode.getDeclarationSet(), resultNode);


        resultNode.setParentNode(node.getParentNode());

        this.doCommonCloningWork(node, resultNode);
    }

    /**
     * Método auxiliar que coloca <code>parent</code> como o nó pai de todas
     * as instâncias da <code>NestableExecutableDeclaration</code>.
     */
    private static void setParentNode(NodeList elements, JaTSNode parent) {
        int i = 0;
        int max = elements.size();

        while (i < max) {
            JaTSNode tempNode = elements.elementAt(i);

            if (tempNode instanceof NestableExecutableDeclaration) {
                ((NestableExecutableDeclaration) tempNode).
                        setParentNode(parent);
            }
            i++;
        }
    }


    /**
     * Método auxiliar para a clonagem de declarações condicionais
     */
    private JaTSNode cloneParentNodeConditionalDeclaration(
            JConditionalDeclaration node) throws InconsistentNodeException {

        JaTSNode resultNode = null;

        if (node.getParentNode() == null) {
            throw new InconsistentNodeException(node);
        }

        resultNode = this.state.pop();
        return resultNode;
    }

    /**
     * Método auxiliar na clonagem de declarações iterativas.
     */
    private JaTSNode clonePlaceholderOfIterativeDeclaration(
            IterativeDeclaration node) throws InconsistentNodeException {

        JaTSNode ph = null;

        if (node.getPlaceholder() == null) {
            throw new InconsistentNodeException(node);
        }
        ph = this.state.pop();

        return ph;
    }

    /**
     * Método auxiliar na clonagem de declarações condicionais.
     */
    private NodeList cloneTrueDeclarationSetOfConditionalDeclaration(
            JConditionalDeclaration node, JConditionalDeclaration resultNode) {

        NodeList decSet = new NodeList();
        NodeList tDecSet = node.getTrueDeclarationSet();

        if (tDecSet != null) {
            int i = tDecSet.size() - 1;
            while (i >= 0) {
                JaTSNode dec = this.state.pop();
                decSet.addElement(dec);
                i--;
            }
			CloningVisitor.setParentNode(decSet, resultNode);
        }

        return decSet;
    }

    /**
     * Método auxiliar para evitar repetição de código. Realiza algumas tarefas
     * comuns à clonagem de todos os tipos de nó, exceto às declarações
     * executáveis e iterativas e às pré-condições.
     */
    private void doCommonCloningWork(JaTSNode node, SimpleNode resultNode)
            throws IllegalArgumentException, InconsistentNodeException {

        if (node == null || resultNode == null) {
            throw new IllegalArgumentException();
        }
        resultNode.setIndentLevel(node.getIndentLevel());
        resultNode.cloneVariable(node);
        resultNode.setExecutableDeclaration(this.cloneExecutableDeclaration(node, resultNode));
        resultNode.setLine(node.getLine());
        SimpleNode newNode = (SimpleNode) node;
        if (newNode.isOrDeclaration()) {
            resultNode.setOrDeclaration(newNode.getOrDeclaration());
        }
        this.state.push(resultNode);
    }

    /**
     * Retorna a raiz da árvore clonada.
     *
     * @return Um objeto do tipo <code>JaTSNode</code> correspondendo à raiz
     * da árvore clonada.
     */
    public JaTSNode getClone() {
        return this.state.pop();
    }

    /**
     * Métodos visit para objetos do tipo <code>JAllocationExpression</code>.
     *
     * @param node O nó a ser clonado.
     * @param data Um objeto que pode conter informação adicional necessária ao
     * visitor.
     *
     * @return Um objeto qualquer.
     *
     * @exception java.lang.IllegalArgumentException Levantada quando o
     * parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     * apresenta uma estrutura interna inválida.
     */
    public Object visit(JAllocationExpression node, Object data)
            throws InconsistentNodeException, IllegalArgumentException {
        if (node == null) {
            throw new IllegalArgumentException();
        }

        JAllocationExpression resultNode = new JAllocationExpression();

        if (node.getAllocationType() == JAllocationExpression.OBJECTALLOCATION) {

            //se tiver o corpo
            if (node.getInternalClassBody() != null) {

                if (!(this.state.top() instanceof TypeBody)) {
                    throw new InconsistentNodeException(node);
                }
                TypeBody body = (TypeBody) this.state.pop();
                resultNode.setInternalClassBody(body);

            }
            if (node.getArguments() == null
                    || !(this.state.top() instanceof JExpressionList)) {
                throw new InconsistentNodeException(node);
            }
            JExpressionList args = (JExpressionList) this.state.pop();
            resultNode.setArguments(args);

        } else if (node.getAllocationType() ==
                JAllocationExpression.PRIMITIVETYPEARRAYALLOCATION
                || node.getAllocationType() ==
                JAllocationExpression.OBJECTARRAYALLOCATION) {

            if (node.getArrayDimsAndInits() == null
                    || !(this.state.top() instanceof JArrayDimsAndInits)) {
                throw new InconsistentNodeException(node);
            }

            JArrayDimsAndInits adi = (JArrayDimsAndInits) this.state.pop();
            resultNode.setArrayDimsAndInits(adi);
        }


        if (node.getTypeName() == null
                || !(this.state.top() instanceof JName)) {
            throw new InconsistentNodeException(node);
        }
        resultNode.setName((JName) this.state.pop());


        if (node.getReferencedObject() != null) {
            System.out.println("JAllocationExpression" + this.state.top());
            resultNode.setReferencedObject(this.state.pop());
        }

        resultNode.setAllocationType(node.getAllocationType());
        this.doCommonCloningWork(node, resultNode);
        return resultNode;
    }

    /**
     * @see JAnonymousClass
     */
    public Object visit(JAnonymousClass node, Object data)
            throws InconsistentNodeException, IllegalArgumentException,
            ExecutionException {

        JAnonymousClass resultNode = new JAnonymousClass();
        if (node.isAJavaNode()) {

            if ((node.getName() == null) || !(this.state.top() instanceof JName)) {
                throw new InconsistentNodeException(node);
            }
            JName name = (JName) this.state.pop();
            resultNode.setName(name);

            if (node.getArguments() != null &&
                    !(this.state.top() instanceof JExpressionList)) {
                throw new InconsistentNodeException(node);
            }
            JExpressionList args = (JExpressionList) this.state.pop();
            resultNode.setArguments(args);

            if (node.getBody() == null ||
                    !(this.state.top() instanceof TypeBody)) {
                throw new InconsistentNodeException(node);
            }

            TypeBody classBody = (TypeBody) this.state.pop();
            resultNode.setBody(classBody);
        }

        this.doCommonCloningWork(node, resultNode);
        return resultNode;
    }

    /**
     * Revisado em 01/07/2004
     * @see JArrayAccess
     */
    public Object visit(JArrayAccess node, Object data)
            throws InconsistentNodeException, IllegalArgumentException,
            ExecutionException {
        if (node == null) {
            throw new IllegalArgumentException();
        }

        JArrayAccess resultNode = new JArrayAccess();

        if (node.isAJavaNode()) {
            JaTSNode arrayName = null;
            if (node.getArrayName() == null) {
                throw new InconsistentNodeException(node);
            }
            arrayName = this.state.pop();

            if (!(arrayName instanceof JaTSNode)) {
                throw new InconsistentNodeException(node);
            }
            resultNode.setArrayName(arrayName);

            if (node.getIndexers() != null) {
                if (!(this.state.top() instanceof JExpressionList)) {
                    throw new InconsistentNodeException(node);
                }
                resultNode.setIndexers((JExpressionList) this.state.pop());

            }

        }

        this.doCommonCloningWork(node, resultNode);
        return resultNode;
    }

    /**
     * Métodos visit para objetos do tipo <code>JArrayDimsAndInits</code>.
     * @param node O nó a ser clonado.
     * @param data Um objeto que pode conter informação adicional necessária ao
     * visitor.
     *
     * @return Um objeto qualquer.
     *
     * @exception java.lang.IllegalArgumentException Levantada quando o
     * parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     * apresenta uma estrutura interna inválida.
     */
    public Object visit(JArrayDimsAndInits node, Object data)
            throws InconsistentNodeException, IllegalArgumentException {
        if (node == null) {
            throw new IllegalArgumentException();
        }

        JArrayDimsAndInits resultNode = new JArrayDimsAndInits();
        NodeList dimSizes = new NodeList();

        int i = 0;

        resultNode.setDimensions(node.getDimensions());

        if (node.getArrayInitializer() != null) {
            JaTSNode tempNode = this.state.pop();
            if (!(tempNode instanceof JArrayInitializer))
                throw new InconsistentNodeException(node);

            resultNode.setArrayInitializer((JArrayInitializer) tempNode);
        }

        if (node.getNumberOfDimensionSizesSpecified() > 0) {
            i = node.getNumberOfDimensionSizesSpecified();
            while (i > 0) {
                // Os dimension sizes são armazenados de trás pra frente, ou
                // seja, para um array declarado int[3][4][1+x], os tamanhos
                // serão armazenados na lista como < "1+x", "4", "3">.
                dimSizes.addElement(this.state.pop());
                i--;
            }
            int j = 1, max = dimSizes.size();
            while (j <= max) {
                JaTSNode tempNode = dimSizes.elementAt(max - j);
                if (!(tempNode instanceof JExpression))
                    throw new InconsistentNodeException(node);

                resultNode.setSizeOfDimension((JExpression) tempNode, j);
                j++;
            }
        }

        this.doCommonCloningWork(node, resultNode);

        return resultNode;
    }

    /**
     * Métodos visit para objetos do tipo <code>JArrayInitializer</code>.
     *
     * @param node O nó a ser clonado.
     * @param data Um objeto que pode conter informação adicional necessária ao
     * visitor.
     *
     * @return Um objeto qualquer.
     *
     * @exception java.lang.IllegalArgumentException Levantada quando o
     * parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     * apresenta uma estrutura interna inválida.
     */
    public Object visit(JArrayInitializer node, Object data)
            throws InconsistentNodeException, IllegalArgumentException {
        if (node == null) {
            throw new IllegalArgumentException();
        }

        JArrayInitializer resultNode = new JArrayInitializer();
        int i = node.size();
        NodeList inits = new NodeList();

        while (i > 0) {
            inits.addElement(this.state.pop());
            i--;
        }

        int j = 0, max = inits.size();
        while (j < max) {
            JaTSNode tempNode = inits.elementAt(max - (j + 1));

            if (!(tempNode instanceof JExpression
                    || tempNode instanceof JArrayInitializer))
                throw new InconsistentNodeException(node);
            resultNode.addInitializer(tempNode);
            j++;
        }

        this.doCommonCloningWork(node, resultNode);

        return resultNode;
    }

    /**
     * Métodos visit para objetos do tipo <code>JBinaryExpression</code>.
     *
     * @param node O nó a ser clonado.
     * @param data Um objeto que pode conter informação adicional necessária ao
     * visitor.
     *
     * @return Um objeto qualquer.
     *
     * @exception java.lang.IllegalArgumentException Levantada quando o
     * parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     * apresenta uma estrutura interna inválida.
     */
    public Object visit(JBinaryExpression node, Object data)
            throws InconsistentNodeException, IllegalArgumentException {
        if (node == null) {
            throw new IllegalArgumentException();
        }

        JBinaryExpression resultNode = new JBinaryExpression();

        if (node.getRightHandOperand() == null
                || !(this.state.top() instanceof JExpression))
            throw new InconsistentNodeException(node);
        JExpression rightHand = (JExpression) this.state.pop();

        if (node.getLeftHandOperand() == null
                || !(this.state.top() instanceof JExpression))
            throw new InconsistentNodeException(node);
        JExpression leftHand = (JExpression) this.state.pop();

        resultNode.setLeftHandOperand(leftHand);
        resultNode.setRightHandOperand(rightHand);

        resultNode.setOperator(node.getOperator());
        this.doCommonCloningWork(node, resultNode);

        return resultNode;
    }

    /**
     * Métodos visit para objetos do tipo <code>JBlock</code>.
     *
     * @param node O nó a ser clonado.
     * @param data Um objeto que pode conter informação adicional necessária ao
     * visitor.
     *
     * @return Um objeto qualquer.
     *
     * @exception java.lang.IllegalArgumentException Levantada quando o
     * parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     * apresenta uma estrutura interna inválida.
     */
    public Object visit(JBlock node, Object data)
            throws InconsistentNodeException, IllegalArgumentException {

        if (node == null) {
            throw new IllegalArgumentException();
        }
        JBlock resultNode = new JBlock();

        if (node.isAJavaNode()) {

            NodeList statements = node.getStatements();
            NodeList newList = new NodeList();

            for (int i = 0; i < statements.size(); i++) {
                newList.addElement(this.state.pop());

                //seta o pai das declaracoes iterativas e condicionais
                if (newList.elementAt(i) instanceof NestableExecutableDeclaration) {
                    ((NestableExecutableDeclaration)
                            newList.elementAt(i)).setParentNode(resultNode);
                }
            }
            resultNode.setStatements(newList);
        }
        this.doCommonCloningWork(node, resultNode);

        return resultNode;
    }

    /**
     * Métodos visit para objetos do tipo <code>CastExpression</code>.
     *
     * @param node O nó a ser clonado.
     * @param data Um objeto que pode conter informação adicional necessária ao
     * visitor.
     *
     * @return Um objeto qualquer.
     *
     * @exception java.lang.IllegalArgumentException Levantada quando o
     * parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     * apresenta uma estrutura interna inválida.
     */
    public Object visit(CastExpression node, Object data)
            throws InconsistentNodeException, IllegalArgumentException {
        if (node == null) {
            throw new IllegalArgumentException();
        }

        CastExpression resultNode = new CastExpression();

        // clona o tipo
        if (node.getType() == null
                || !(this.state.top() instanceof JType)) {
            throw new InconsistentNodeException(node);
        }
        JType type = (JType) this.state.pop();

        // clona o operando
        if (node.getOperand() == null
                || !(this.state.top() instanceof JExpression)) {
            throw new InconsistentNodeException(node);
        }
        JExpression operand = (JExpression) this.state.pop();

        // seta o nó resultado
        resultNode.setType(type);
        resultNode.setOperand(operand);

        this.doCommonCloningWork(node, resultNode);
        return resultNode;
    }

    /**
     * Métodos visit para objetos do tipo <code>TypeBody</code>.
     *
     * @param node O nó a ser clonado.
     * @param data Um objeto que pode conter informação adicional necessária ao<br>
     * visitor.
     *
     * @return Um objeto qualquer.
     *
     * @exception java.lang.IllegalArgumentException Levantada quando o
     * parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     * apresenta uma estrutura interna inválida.
     */
    public Object visit(TypeBody node, Object data)
            throws InconsistentNodeException, IllegalArgumentException {
        if (node == null) {

            throw new IllegalArgumentException("Corpo da classe nulo");

        }
        TypeBody resultNode = new TypeBody();
        BodyFieldDeclarations fields = new BodyFieldDeclarations();
        ClassBodyConstructorDeclarations constructors =
                new ClassBodyConstructorDeclarations();
        OtherDeclarationsSet idecs = new OtherDeclarationsSet();
        BodyMethodDeclarations methods = new BodyMethodDeclarations();
        ClassBodyInitializers inits = new ClassBodyInitializers();
        BodyTypeDeclarations types = new BodyTypeDeclarations();

        if (node.getFieldDeclarationSet() == null ||
                node.getOtherDeclarationsSet() == null ||
                node.getMethodDeclarationSet() == null ||
                node.getConstructorDeclarationSet() == null) {
            throw new InconsistentNodeException(node);
        }

        // Conjunto de declarações de construtores
        if (!(this.state.top() instanceof ClassBodyConstructorDeclarations)) {
            throw new InconsistentNodeException(node);
        }
        constructors = (ClassBodyConstructorDeclarations) this.state.pop();
        resultNode.setConstructorDeclarationSet(constructors);


        // Conjunto de declarações de métodos
        if (!(this.state.top() instanceof BodyMethodDeclarations)) {
            throw new InconsistentNodeException(node);
        }

        methods = (BodyMethodDeclarations) this.state.pop();
        resultNode.setMethodDeclarationSet(methods);

        // Conjunto de outras declarações.
        if (!(this.state.top() instanceof OtherDeclarationsSet)) {
            System.out.println("this.state.top() = " + this.state.top().getClass().getName());
            throw new InconsistentNodeException(node);
        }
        idecs = (OtherDeclarationsSet) this.state.pop();
        resultNode.setOtherDeclarationsSet(idecs);

        // Conjunto de declarações de atributo
        if (!(this.state.top() instanceof BodyFieldDeclarations)) {
            throw new InconsistentNodeException(node);
        }
        fields = (BodyFieldDeclarations) this.state.pop();
        resultNode.setFieldDeclarationSet(fields);

        // Conjunto de inicializadores
        if (!(this.state.top() instanceof ClassBodyInitializers)) {
            throw new InconsistentNodeException(node);
        }
        inits = (ClassBodyInitializers) this.state.pop();
        resultNode.setInitializerSet(inits);

        // Conjunto de tipos aninhados
        if (!(this.state.top() instanceof BodyTypeDeclarations)) {
            throw new InconsistentNodeException(node);
        }
        types = (BodyTypeDeclarations) this.state.pop();
        resultNode.setTypeDeclarationSet(types);


        this.doCommonCloningWork(node, resultNode);
        return resultNode;
    }

    /**
     * Métodos visit para objetos do tipo <code>JCatchClause</code>.
     *
     * @param node O nó a ser clonado.
     * @param data Um objeto que pode conter informação adicional necessária ao
     * visitor.
     *
     * @return Um objeto qualquer.
     *
     * @exception java.lang.IllegalArgumentException Levantada quando o
     * parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     * apresenta uma estrutura interna inválida.
     */
    public Object visit(JCatchClause node, Object data)
            throws InconsistentNodeException, IllegalArgumentException,
            ExecutionException {

        if (node == null) {
            throw new IllegalArgumentException();
        }

        JCatchClause resultNode = new JCatchClause();
        if (node.isAJavaNode()) {

            // clona o comentário
            if (node.getComment() != null) {
                resultNode.setComment(node.getComment());
            }
            // clona o parametro
            if (node.getParameter() != null) {
                if (!(this.state.top() instanceof JFormalParameter)) {
                    throw new InconsistentNodeException(node);
                }
                resultNode.setParameter((JFormalParameter) this.state.pop());
            }

            // clona o bloco
            if (node.getBlock() != null) {
                if (!(this.state.top() instanceof JBlock)) {
                    throw new InconsistentNodeException(node);
                }
                resultNode.setBlock((JBlock) this.state.pop());
            }
        }
        this.doCommonCloningWork(node, resultNode);
        return resultNode;
    }

    /**
     * Métodos visit para objetos do tipo <code>JClassDeclaration</code>.
     *
     * @param node O nó a ser clonado.
     * @param data Um objeto que pode conter informação adicional necessária ao<br>
     * visitor.
     *
     * @return Um objeto qualquer.
     *
     * @exception java.lang.IllegalArgumentException Levantada quando o
     * parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     * apresenta uma estrutura interna inválida.
     */
    public Object visit(JClassDeclaration node, Object data)
            throws InconsistentNodeException, IllegalArgumentException {

        if (node == null) {
            throw new IllegalArgumentException("Declaracao de classe nula");
        }

        JClassDeclaration resultNode = new JClassDeclaration();

        if (node.isAJavaNode()) {
            if (node.getModifiers() == null ||
                    !(this.state.top() instanceof JModifierList)) {
                throw new InconsistentNodeException(node);
            }

            if (node.hasComment()) {
                resultNode.setComment(node.getComment());
            }


            JModifierList mods = (JModifierList) this.state.pop();
            resultNode.setModifiers(mods);
            JIdentifier name = new JIdentifier();

            if ((node.getName() == null) || !(this.state.top() instanceof JIdentifier)) {
                throw new InconsistentNodeException(node);
            }

            name = (JIdentifier) this.state.pop();

            resultNode.setName(name);

            if (node.getSuperClass() != null && (this.state.top() instanceof JName)) {

                JName superClass = new JName();
                superClass = (JName) this.state.pop();
                resultNode.setSuperClass(superClass);
            }

            if (node.getInterfaces() != null) {

                if (!(this.state.top() instanceof JNameList)) {
                    throw new InconsistentNodeException(node);
                }

                JNameList interfaces = (JNameList) this.state.pop();
                resultNode.setInterfaces(interfaces);
            }

            if (node.getBody() == null ||
                    !(this.state.top() instanceof TypeBody)) {
                throw new InconsistentNodeException(node);
            }

            TypeBody classBody = (TypeBody) this.state.pop();
            resultNode.setBody(classBody);
        }

        this.doCommonCloningWork(node, resultNode);

        return resultNode;
    }

    /**
     * Métodos visit para objetos do tipo <code>IType</code>.
     *
     * @param node O nó a ser clonado.
     * @param data Um objeto que pode conter informação adicional necessária ao<br>
     * visitor.
     *
     * @return Um objeto qualquer.
     *
     * @exception java.lang.IllegalArgumentException Levantada quando o
     * parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     * apresenta uma estrutura interna inválida.
     */
    public Object visit(JCompilationUnit node, Object data)
            throws InconsistentNodeException, IllegalArgumentException {

        if (node == null) {
            throw new IllegalArgumentException("IType nulo");
        }

        JCompilationUnit resultNode = new JCompilationUnit();

        if ((node.getTypeDeclarationList()) == null) {
            throw new InconsistentNodeException(node);
        }

        // Pré-condição
        if (node.getPrecondition() != null) {
            JaTSNode tempPre = this.state.pop();
            if (!(tempPre instanceof JPreconditionDeclaration)) {
                throw new InconsistentNodeException(node);
            }
            JPreconditionDeclaration pre = (JPreconditionDeclaration) tempPre;
            resultNode.setPrecondition(pre);
        }

        // Pacote
        if (node.getPackage() != null) {
            JaTSNode tempPack = this.state.pop();
            if (!(tempPack instanceof JPackageDeclaration)) {
                throw new InconsistentNodeException(node);
            }
            JPackageDeclaration packageName = (JPackageDeclaration) tempPack;
            resultNode.setPackage(packageName);
        }
        // Imports
        if (node.getImports() != null) {
            JaTSNode tempImport = this.state.pop();
            if (!(tempImport instanceof ImportDeclarations)) {
                throw new InconsistentNodeException(node);
            }
            ImportDeclarations imports = (ImportDeclarations) tempImport;
            resultNode.setImports(imports);
        }

        // Declarações de tipo
        NodeList types = new NodeList();
        JTypeDeclaration t;
        int i = 0;
        int size = node.getTypeDeclarationList().size();
        while (i < size) {
            JaTSNode tmp2 = this.state.top();
            if (tmp2 instanceof JTypeDeclaration) {
                t = (JTypeDeclaration) this.state.pop();
                types.addElement(t);
            }
            i++;
        }

        resultNode.setTypeDeclarationList(types);

        this.doCommonCloningWork(node, resultNode);

        return resultNode;
    }


    /**
     * Métodos visit para objetos do tipo <code>JConditionalDeclaration</code>.*
     * @param node O nó a ser clonado.
     * @param data Um objeto que pode conter informação adicional necessária ao<br>
     * visitor.
     *
     * @return Um objeto qualquer.
     *
     * @exception java.lang.IllegalArgumentException Levantada quando o
     * parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     * apresenta uma estrutura interna inválida.
     */
    public Object visit(JConditionalDeclaration node, Object data)
            throws InconsistentNodeException, IllegalArgumentException {

        if (node == null) {
            throw new IllegalArgumentException();
        }

        JConditionalDeclaration resultNode = new JConditionalDeclaration();
        this.cloneConditionalDeclaration(node, resultNode);

        return resultNode;
    }


    /**
     * Métodos visit para objetos do tipo <code>JConstructorDeclaration</code>.
     * @param node O nó a ser clonado.
     * @param data Um objeto que pode conter informação adicional necessária ao
     *        visitor.
     * @return Um objeto qualquer.
     * @exception java.lang.IllegalArgumentException Levantada quando o
     *            parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     *            apresenta uma estrutura interna inválida.
     */
    public Object visit(JConstructorDeclaration node, Object data)
            throws InconsistentNodeException, IllegalArgumentException {

        if (node == null) {
            throw new IllegalArgumentException("Declaracao de construtor nula");
        }

        JConstructorDeclaration resultNode = new JConstructorDeclaration();
        JModifierList modifier = new JModifierList(JModifierList.CONSTRUCTOR_DECLARATION);
        JIdentifier id = new JIdentifier();
        JParameterList params = new JParameterList();
        JNameList excs = null;
        JBlock body = null;

        if (node.hasComment()) {
            resultNode.setComment(node.getComment());
        }

        if (node.isAJavaNode()) {
            if (node.getName() == null) {
                throw new InconsistentNodeException(node);
            }

            // clona o modificador
            if (!(this.state.top() instanceof JModifierList)) {
                throw new InconsistentNodeException(node);
            }
            modifier = (JModifierList) this.state.pop();

            resultNode.setModifiers(modifier);

            // clona o identificador
            if (!(this.state.top() instanceof JIdentifier)) {
                throw new InconsistentNodeException(node);
            }
            id = (JIdentifier) this.state.pop();
            resultNode.setName(id);

            // clona os parametros do construtor
            if (!(this.state.top() instanceof JParameterList)) {
                throw new InconsistentNodeException(node);
            }
            params = (JParameterList) this.state.pop();
            resultNode.setParameters(params);

            // clona a lista de excecoes
            if (!(this.state.top() instanceof JNameList)) {
                throw new InconsistentNodeException(node);
            }
            excs = (JNameList) this.state.pop();
            resultNode.setExceptions(excs);

            // clona o corpo od construtor
            if (!(this.state.top() instanceof JBlock)) {
                throw new InconsistentNodeException(node);
            }
            body = (JBlock) this.state.pop();
            resultNode.setBody(body);
        }

        // nivel de indentacao
        if (node.getIndentLevel() > 0) {
            resultNode.setIndentLevel(node.getIndentLevel());
        } else {
            resultNode.setIndentLevel(1);
        }
        this.doCommonCloningWork(node, resultNode);
        return resultNode;
    }


    /**
     * Métodos visit para objetos do tipo <code>JConstructorDeclarationSet</code>.
     * @param node O nó a ser clonado.
     * @param data Um objeto que pode conter informação adicional necessária ao<br>
     *        visitor.
     * @return Um objeto qualquer.
     * @exception java.lang.IllegalArgumentException Levantada quando o
     *            parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     *            apresenta uma estrutura interna inválida.
     */
    public Object visit(JConstructorDeclarationSet node, Object data)
            throws InconsistentNodeException, IllegalArgumentException {

        if (node == null) {
            throw new IllegalArgumentException();
        }

        JConstructorDeclarationSet resultNode = new JConstructorDeclarationSet();
        JConstructorDeclaration tmp = new JConstructorDeclaration();
        int i = 0;
        int size = node.size();

        while (i < size) {
            if (this.state.top() instanceof JConstructorDeclaration) {
                tmp = (JConstructorDeclaration) this.state.pop();
                resultNode.add(tmp);
            }
            i++;
        }
        this.doCommonCloningWork(node, resultNode);
        return resultNode;
    }

    /**
     * Métodos visit para objetos do tipo
     * <code>ClassBodyConstructorDeclarations</code>.
     * @param node O nó a ser impresso.
     * @param data Um objeto que pode conter informação adicional necessária
     * ao visitor.
     *
     * @return Um objeto qualquer.
     *
     * @exception java.lang.IllegalArgumentException Levantada quando o
     * parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     * apresenta uma estrutura interna inválida.
     */
    public Object visit(ClassBodyConstructorDeclarations node, Object data)
            throws InconsistentNodeException, IllegalArgumentException {

        if (node == null) {
            throw new IllegalArgumentException("JConstructorDeclaration nula");
        }

        ClassBodyConstructorDeclarations resultNode =
                new ClassBodyConstructorDeclarations();

        // Clonagem das declarações de construtor
        if (node.getConstructorsSet() == null) {
            throw new InconsistentNodeException(node);
        }

        JConstructorDeclarationSet constructorsSet =
                new JConstructorDeclarationSet();

        if (!(this.state.top() instanceof JConstructorDeclarationSet)) {
            throw new InconsistentNodeException(node);
        }
        constructorsSet = (JConstructorDeclarationSet) this.state.pop();

        resultNode.setConstructorsSet(constructorsSet);

        // Clonagem das declarações do tipo ConstructorVarDeclaration
        if (node.getConstructorVarSet() == null) {
            throw new InconsistentNodeException(node);
        }

        NodeList constructorVarSet = new NodeList();
        int numConstructorVars = node.getConstructorVarSet().size();
        int j = 0;

        while (j < numConstructorVars) {
            if (!(this.state.top() instanceof JConstructorDeclaration)) {
                throw new InconsistentNodeException(node);
            }
            JConstructorDeclaration tempConstructorVar =
                    (JConstructorDeclaration) this.state.pop();
            constructorVarSet.addElement(tempConstructorVar);
            j++;
        }

        resultNode.setConstructorVarSet(constructorVarSet);

        // Clonagem das declarações do tipo ConstructorsVarDeclaration
        if (node.getConstructorsVarSet() == null) {
            throw new InconsistentNodeException(node);
        }

        NodeList constructorsVarSet = new NodeList();
        int numConstructorsVars = node.getConstructorsVarSet().size();
        int k = 0;

        while (k < numConstructorsVars) {
            if (!(this.state.top() instanceof JConstructorDeclarationSet)) {
                throw new InconsistentNodeException(node);
            }
            JConstructorDeclarationSet tempConstructorsVar =
                    (JConstructorDeclarationSet) this.state.pop();
            constructorsVarSet.addElement(tempConstructorsVar);
            k++;
        }

        resultNode.setConstructorsVarSet(constructorsVarSet);

        this.doCommonCloningWork(node, resultNode);
        return resultNode;
    }


    /**
     * Métodos visit para objetos do tipo <code>JExecutableDeclaration</code>.
     *
     * @param node O nó a ser clonado.
     * @param data Um objeto que pode conter informação adicional necessária
     * ao visitor.
     *
     * @return Um objeto qualquer.
     *
     * @exception java.lang.IllegalArgumentException Levantada quando o
     * parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     * apresenta uma estrutura interna inválida.
     */
    public Object visit(JExecutableDeclaration node, Object data)
            throws InconsistentNodeException, IllegalArgumentException {

        if (node == null) {
            throw new IllegalArgumentException();
        }

        JExecutableDeclaration resultNode = null;
        NodeList nodes = new NodeList();
        JaTSNode resNode = null;

        int i = 0;
        int max = node.size();

        while (i < max) {
            if (!(this.state.top() instanceof JExpression)) {
                throw new InconsistentNodeException(node);
            }
            JExpression exp = (JExpression) this.state.pop();
            nodes.addElement(exp);
            i++;
        }

        if (node.getResultNode() != null) {
            resNode = this.state.pop();
        }

        // Dois tipos de declaração executável são possíveis.
        // Information-Extracting Declarations ...
        if (resNode == null && nodes.size() == 1) {
            resultNode = new JExecutableDeclaration((JExpression) nodes.elementAt(0));
        }
        // ... e Information-Modifying Declarations.
        else if (resNode != null && node.getReferencedVariable() != null) {
            resultNode = new JExecutableDeclaration(node.getReferencedVariable(),
                    resNode, nodes);
        } else {
            throw new InconsistentNodeException(node);
        }

        this.doCommonCloningWork(node, resultNode);

        return resultNode;
    }

    /**
     * Revisado por Adeline, em 01/07//2004
     *
     * Métodos visit para objetos do tipo <code>JExpressionList</code>.
     * @param node O nó a ser clonado.
     * @param data Um objeto que pode conter informação adicional necessária ao
     * visitor.
     *
     * @return Um objeto qualquer.
     *
     * @exception java.lang.IllegalArgumentException Levantada quando o
     * parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     * apresenta uma estrutura interna inválida.
     */
    public Object visit(JExpressionList node, Object data)
            throws InconsistentNodeException, IllegalArgumentException {
        if (node == null) {
            throw new IllegalArgumentException();
        }
        JExpressionList resultNode = new JExpressionList();
        if (node.isAJavaNode()) {
            int max = node.size();
            for (int i = 0; i < max; i++) {
                JaTSNode tempNode = this.state.pop();
                if (!(tempNode instanceof JExpression)) {
                    throw new InconsistentNodeException(node);
                }
                resultNode.addExpression((JExpression) tempNode);
            }

        }
        resultNode.setType(node.getType());
        this.doCommonCloningWork(node, resultNode);

        return resultNode;
    }

    /**
     * Métodos visit para objetos do tipo <code>JFieldAccess</code>.
     *
     * @param node O nó a ser clonado.
     * @param data Um objeto que pode conter informação adicional necessária ao
     * visitor.
     *
     * @return Um objeto qualquer.
     *
     * @exception java.lang.IllegalArgumentException Levantada quando o
     * parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     * apresenta uma estrutura interna inválida.
     */
    public Object visit(JFieldAccess node, Object data)
            throws InconsistentNodeException, IllegalArgumentException {
        if (node == null) {
            throw new IllegalArgumentException();
        }

        JFieldAccess resultNode = new JFieldAccess();

        if (node.isAJavaNode()) {

            // clona o comentário
            if (node.getComment() != null) {
                resultNode.setComment(node.getComment());
            }
            JaTSNode fieldName = null;
            if (node.getFieldName() == null) {
                throw new InconsistentNodeException(node);
            }
            fieldName = this.state.pop();
            if (!(fieldName instanceof JIdentifier))
                throw new InconsistentNodeException(node);

            resultNode.setFieldName((JIdentifier) fieldName);

            JaTSNode refObj = null;
            if (node.getReferencedObject() != null) {
                refObj = this.state.pop();
                resultNode.setReferencedObject(refObj);
            }
        }

        this.doCommonCloningWork(node, resultNode);
        return resultNode;
    }

    /**
     * Métodos visit para objetos do tipo <code>JFieldDeclaration</code>.
     *
     * @param node O nó a ser impresso.
     * @param data Um objeto que pode conter informação adicional necessária ao<br>
     * visitor.
     *
     * @return Um objeto qualquer.
     *
     * @exception java.lang.IllegalArgumentException Levantada quando o
     * parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     * apresenta uma estrutura interna inválida.
     */
    public Object visit(JFieldDeclaration node, Object data)
            throws InconsistentNodeException, IllegalArgumentException {

        if (node == null) {
            throw new IllegalArgumentException("JFieldDeclaration nulo");
        }

        JFieldDeclaration resultNode = new JFieldDeclaration();

        if (node.hasComment()) {
            resultNode.setComment(node.getComment());
        }

        // Se for executável, a declaração de atributo não tem modificadores,
        // tipo ou declaradores.
        if (node.isAJavaNode()) {
            // Clonagem dos modificadores
            if (node.getModifiers() == null ||
                    !(this.state.top() instanceof JModifierList)) {
                throw new InconsistentNodeException(node);
            }
            resultNode.setModifiers((JModifierList) this.state.pop());

            // Clonagem do tipo do atributo
            if (node.getType() == null ||
                    !(this.state.top() instanceof JType)) {
                throw new InconsistentNodeException(node);
            }
            JType type = (JType) this.state.pop();
            resultNode.setType(type);

            // Clonagem dos nomes dos atributos e suas respectivas inicializações
            if (node.getVariables() == null) {
                throw new InconsistentNodeException(node);
            }

            JVariableDeclarator temp = new JVariableDeclarator();
            JVariableDeclaratorSet varDec = new JVariableDeclaratorSet();
            int vd = node.size();
            int i = 0;

            while (i < vd) {
                if (!(this.state.top() instanceof JVariableDeclarator)) {
                    throw new InconsistentNodeException(node);
                }
                temp = (JVariableDeclarator) this.state.pop();
                varDec.add(temp);

                i++;
            }

            resultNode.setVariables(varDec);
        }

        this.doCommonCloningWork(node, resultNode);

        return resultNode;
    }

    /**
     * Métodos visit para objetos do tipo <code>JFieldDeclarationSet</code>.
     *
     * @param node O nó a ser clonado.
     * @param data Um objeto que pode conter informação adicional necessária ao<br>
     * visitor.
     *
     * @return Um objeto qualquer.
     *
     * @exception java.lang.IllegalArgumentException Levantada quando o
     * parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     * apresenta uma estrutura interna inválida.
     */
    public Object visit(JFieldDeclarationSet node, Object data)
            throws InconsistentNodeException, IllegalArgumentException {

        if (node == null) {
            throw new IllegalArgumentException();
        }

        JFieldDeclarationSet resultNode = new JFieldDeclarationSet();
        JVariableDeclaration tmp = new JFieldDeclaration();
        int i = 0;
        int size = node.size();

        while (i < size) {
            if (this.state.top() instanceof JVariableDeclaration) {
                tmp = (JVariableDeclaration) this.state.pop();
                resultNode.add(tmp);
            }
            i++;
        }

        this.doCommonCloningWork(node, resultNode);

        return resultNode;
    }

    /**
     * Métodos visit para objetos do tipo <code>JForStatement</code>.
     *
     * @param node O nó a ser clonado.
     * @param data Um objeto que pode conter informação adicional necessária ao<br>
     * visitor.
     *
     * @return Um objeto qualquer.
     *
     * @exception java.lang.IllegalArgumentException Levantada quando o
     * parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     * apresenta uma estrutura interna inválida.
     */
    public Object visit(JForStatement node, Object data)
            throws InconsistentNodeException, IllegalArgumentException,
            ExecutionException {

        JForStatement resultNode = new JForStatement();
        if (node == null) {
            throw new IllegalArgumentException();
        }

        if (node.isAJavaNode()) {

            // todos os filhos de um for podem ser nulos, por isso é preciso checar
            // se cada um deles é diferente de null, antes de dar um pop
            // inicialização
            if (node.getForInit() != null) {
                if (!(this.state.top() instanceof JaTSNode)) {
                    throw new InconsistentNodeException(node);
                }
                resultNode.setForInit(this.state.pop());
            }

            // condição de parada
            if (node.getExpression() != null) {
                if (!(this.state.top() instanceof JExpression)) {
                    throw new InconsistentNodeException(node);
                }
                resultNode.setExpression((JExpression) this.state.pop());
            }

            // atualização
            if (node.getForUpdate() != null) {
                if (!(this.state.top() instanceof JExpressionList)) {
                    throw new InconsistentNodeException(node);
                }
                resultNode.setForUpdate((JExpressionList) this.state.pop());
            }
            // clona o statement
            if (node.getStatement() != null) {
                if (!(this.state.top() instanceof JaTSNode)) {
                    throw new InconsistentNodeException(node);
                }
                resultNode.setStatement(this.state.pop());
            }
        }

        this.doCommonCloningWork(node, resultNode);
        return resultNode;
    }

    /**
     * Métodos visit para objetos do tipo <code>BodyFieldDeclarations</code>.
     *
     * @param node O nó a ser impresso.
     * @param data Um objeto que pode conter informação adicional necessária
     * ao visitor.
     *
     * @return Um objeto qualquer.
     *
     * @exception java.lang.IllegalArgumentException Levantada quando o
     * parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     * apresenta uma estrutura interna inválida.
     */
    public Object visit(BodyFieldDeclarations node, Object data)
            throws InconsistentNodeException, IllegalArgumentException {

        if (node == null) {
            throw new IllegalArgumentException("JFieldDeclaration nulo");
        }

        BodyFieldDeclarations resultNode = new BodyFieldDeclarations();

        // Clonagem das declarações de atributo
        if (node.getFieldsSet() == null) {
            throw new InconsistentNodeException(node);
        }

        JFieldDeclarationSet fieldsSet = new JFieldDeclarationSet();
        if (!(this.state.top() instanceof JFieldDeclarationSet)) {
            throw new InconsistentNodeException(node);
        }
        fieldsSet = (JFieldDeclarationSet) this.state.pop();
        resultNode.setFieldsSet(fieldsSet);

        // Clonagem das declarações do tipo FieldVarDeclaration
        if (node.getFieldVarSet() == null) {
            throw new InconsistentNodeException(node);
        }
        NodeList attributeSet = new NodeList();
        int numFieldVars = node.getFieldVarSet().size();
        int j = 0;
        while (j < numFieldVars) {
            if (!(this.state.top() instanceof JVariableDeclaration)) {
                throw new InconsistentNodeException(node);
            }
            JVariableDeclaration tempFieldVar = (JVariableDeclaration) this.state.pop();
            attributeSet.addElement(tempFieldVar);
            j++;
        }
        resultNode.setFieldVarSet(attributeSet);

        // Clonagem das declarações do tipo FieldsVarDeclaration
        if (node.getFieldsVarSet() == null) {
            throw new InconsistentNodeException(node);
        }
        NodeList attributesSet = new NodeList();
        int numFieldsVars = node.getFieldsVarSet().size();
        int k = 0;
        while (k < numFieldsVars) {
            if (!(this.state.top() instanceof JFieldDeclarationSet)) {
                throw new InconsistentNodeException(node);
            }
            JFieldDeclarationSet tempFieldsVar =
                    (JFieldDeclarationSet) this.state.pop();
            attributesSet.addElement(tempFieldsVar);
            k++;
        }

        resultNode.setFieldsVarSet(attributesSet);
        this.doCommonCloningWork(node, resultNode);
        return resultNode;
    }


    /**
     * Métodos visit para objetos do tipo <code>ClassBodyInitializers</code>.
     *
     * @param node O nó a ser impresso.
     * @param data Um objeto que pode conter informação adicional necessária
     * ao visitor.
     *
     * @return Um objeto qualquer.
     *
     * @exception java.lang.IllegalArgumentException Levantada quando o
     * parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     * apresenta uma estrutura interna inválida.
     */
    public Object visit(ClassBodyInitializers node, Object data)
            throws InconsistentNodeException, IllegalArgumentException {

        if (node == null) {
            throw new IllegalArgumentException("Argumento nulo");
        }

        ClassBodyInitializers resultNode = new ClassBodyInitializers();

        // Clonagem das declarações de atributo
        if (node.getInitializersSet() == null) {
            throw new InconsistentNodeException(node);
        }

        JInitializerSet initsSet = new JInitializerSet();
        if (!(this.state.top() instanceof JInitializerSet)) {
            throw new InconsistentNodeException(node);
        }
        initsSet = (JInitializerSet) this.state.pop();
        resultNode.setInitializersSet(initsSet);

        // Clonagem das declarações do tipo InitVar
        if (node.getInitializerVarSet() == null) {
            throw new InconsistentNodeException(node);
        }
        NodeList initVarSet = new NodeList();
        int numFieldVars = node.getInitializerVarSet().size();
        int j = 0;
        while (j < numFieldVars) {
            if (!(this.state.top() instanceof JInitializer)) {
                throw new InconsistentNodeException(node);
            }
            JInitializer tempFieldVar = (JInitializer) this.state.pop();
            initVarSet.addElement(tempFieldVar);
            j++;
        }
        resultNode.setInitializerVarSet(initVarSet);

        // Clonagem das declarações do tipo InitializerSet
        if (node.getInitializerVarSet() == null) {

            throw new InconsistentNodeException(node);
        }
        NodeList attributesSet = new NodeList();
        int numFieldsVars = node.getInitializersVarSet().size();
        int k = 0;
        while (k < numFieldsVars) {
            if (!(this.state.top() instanceof JInitializerSet)) {
                throw new InconsistentNodeException(node);
            }
            JInitializerSet tempFieldsVar =
                    (JInitializerSet) this.state.pop();
            attributesSet.addElement(tempFieldsVar);
            k++;
        }

        resultNode.setInitializersVarSet(attributesSet);
        this.doCommonCloningWork(node, resultNode);
        return resultNode;
    }


    /**
     * Métodos visit para objetos do tipo <code>JFormalParameter</code>.
     *
     * @param node O nó a ser clonado.
     * @param data Um objeto que pode conter informação adicional necessária ao
     * visitor.
     *
     * @return Um objeto qualquer.
     *
     * @exception java.lang.IllegalArgumentException Levantada quando o
     * parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     * apresenta uma estrutura interna inválida.
     */
    public Object visit(JFormalParameter node, Object data)
            throws InconsistentNodeException, IllegalArgumentException {
        if (node == null) {
            throw new IllegalArgumentException("Parametro formal nulo");
        }

        JFormalParameter resultNode = new JFormalParameter();
        JModifierList mod =
                new JModifierList(JModifierList.PARAMETER_DECLARATION);
        JIdentifier id = null;
        JType t = null;

        if (node.isAJavaNode()) {
            if (node.getIdentifier() == null) {
                throw new InconsistentNodeException(node);
            }

            // clona o modificador
            if (node.getModifiers() != null) {
                if (!(this.state.top() instanceof JModifierList)) {
                    throw new InconsistentNodeException(node);
                }
                mod = (JModifierList) this.state.pop();
            }
            resultNode.setModifiers(mod);

            // clona o tipo
            if (!(this.state.top() instanceof JType)) {
                throw new InconsistentNodeException(node);
            }
            t = (JType) this.state.pop();
            resultNode.setType(t);

            // clona o identificador
            if (!(this.state.top() instanceof JIdentifier)) {
                throw new InconsistentNodeException(node);
            }
            id = (JIdentifier) this.state.pop();
            resultNode.setIdentifier(id);
        }

        this.doCommonCloningWork(node, resultNode);

        return resultNode;
    }

    /**
     * Métodos visit para objetos do tipo <code>JIdentifier</code>.
     *
     * @param node O nó a ser clonado.
     * @param data Um objeto que pode conter informação adicional necessária ao
     * visitor.
     *
     * @return Um objeto qualquer.
     *
     * @exception java.lang.IllegalArgumentException Levantada quando o
     * parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     * apresenta uma estrutura interna inválida.
     */
    public Object visit(JIdentifier node, Object data)
            throws InconsistentNodeException, IllegalArgumentException {
        if (node == null) {
            throw new IllegalArgumentException();
        }

        JIdentifier resultNode = new JIdentifier();

        if (!node.isExecutable()) {
            if (node.getValue() == null) {
                throw new InconsistentNodeException(node);
            }
            resultNode.setValue(node.getValue());
        }

        this.doCommonCloningWork(node, resultNode);

        return resultNode;
    }

    /**
     * Métodos visit para objetos do tipo <code>JIfStatement</code>.
     *
     * @param node O nó a ser clonado.
     * @param data Um objeto que pode conter informação adicional necessária ao
     * visitor.
     *
     * @return Um objeto qualquer.
     *
     * @exception java.lang.IllegalArgumentException Levantada quando o
     * parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     * apresenta uma estrutura interna inválida.
     */
    public Object visit(JIfStatement node, Object data)
            throws InconsistentNodeException, IllegalArgumentException,
            ExecutionException {
        if (node == null) {
            throw new IllegalArgumentException("JWhileStatement nulo");
        }

        JIfStatement resultNode = new JIfStatement();

        if (node.isAJavaNode()) {

            // clona a expressão
            if (node.getExpression() != null) {
                if (!(this.state.top() instanceof JExpression)) {
                    throw new InconsistentNodeException(node);
                }
                resultNode.setExpression((JExpression) this.state.pop());
            }

            // clona o comentário do if
            if (node.getIfComment() != null) {
                resultNode.setIfComment(node.getIfComment());

            }
            // clona o statement qd a condição do if é true
            if (node.getIfStatement() != null) {
                if ((this.state.top() instanceof JStatement)) {
                    resultNode.setIfStatement(this.state.pop());
                }
            }

            // clona o comentário do else
            if (node.getElseComment() != null) {
                resultNode.setElseComment(node.getElseComment());

            }
            // clona o statement qd a condição do if é false
            if (node.getElseStatement() != null) {
                if ((this.state.top() instanceof JStatement)) {
                    resultNode.setElseStatement(this.state.pop());
                }
            }
        }

        this.doCommonCloningWork(node, resultNode);
        return resultNode;
    }

    /**
     * Métodos visit para objetos do tipo <code>JImportDeclaration</code>.
     *
     * @param node O nó a ser clonado.
     * @param data Um objeto que pode conter informação adicional necessária ao
     * visitor.
     *
     * @return Um objeto qualquer.
     *
     * @exception java.lang.IllegalArgumentException Levantada quando o
     * parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     * apresenta uma estrutura interna inválida.
     */
    public Object visit(JImportDeclaration node, Object data)
            throws InconsistentNodeException, IllegalArgumentException {
        if (node == null) {
            throw new IllegalArgumentException();
        }

        JImportDeclaration resultNode = new JImportDeclaration();

        if (node.isAJavaNode()) {
            if (node.getPackageName() == null) {
                throw new InconsistentNodeException(node);
            }
            if (!(this.state.top() instanceof JName)) {
                throw new InconsistentNodeException(node);
            }
            JName packageName = (JName) this.state.pop();
            resultNode.setPackageName(packageName);

            if (node.getClassName() != null) {
                if (!(this.state.top() instanceof JIdentifier)) {
                    throw new InconsistentNodeException(node);
                }
                JIdentifier className = (JIdentifier) this.state.pop();
                resultNode.setClassName(className);
            }
            resultNode.setImportAllClasses(node.importAllClasses());
        }

        this.doCommonCloningWork(node, resultNode);

        return resultNode;
    }

    /**
     * Métodos visit para objetos do tipo <code>JImportDeclarationSet</code>.
     * @param node O nó a ser clonado.
     * @param data Um objeto que pode conter informação adicional necessária
     * ao visitor.
     * @return Um objeto qualquer.
     * @exception java.lang.IllegalArgumentException Levantada quando o
     *            parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     *            apresenta uma estrutura interna inválida.
     */
    public Object visit(JImportDeclarationSet node, Object data)
            throws InconsistentNodeException, IllegalArgumentException {

        if (node == null) {
            throw new IllegalArgumentException();
        }

        JImportDeclarationSet resultNode = new JImportDeclarationSet();
        JImportDeclaration tmp = new JImportDeclaration();
        int i = 0;
        int size = node.size();

        while (i < size) {
            if (this.state.top() instanceof JImportDeclaration) {
                tmp = (JImportDeclaration) this.state.pop();
                resultNode.add(tmp);
            }
            i++;
        }

        this.doCommonCloningWork(node, resultNode);
        return resultNode;
    }

    /**
     * Métodos visit para objetos do tipo <code>JInitializer</code>.
     *
     * @param node O nó a ser clonado.
     * @param data Um objeto que pode conter informação adicional necessária ao
     * visitor.
     *
     * @return Um objeto qualquer.
     *
     * @exception java.lang.IllegalArgumentException Levantada quando o
     * parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     * apresenta uma estrutura interna inválida.
     */
    public Object visit(JInitializer node, Object data)
            throws InconsistentNodeException, IllegalArgumentException,
            ExecutionException {

        if (node == null) {
            throw new IllegalArgumentException();
        }

        JInitializer resultNode = new JInitializer();
        if (node.isAJavaNode()) {
            resultNode.setStatic(node.isStatic());

            // clona o bloco
            if (node.getBlock() != null) {
                if (!(this.state.top() instanceof JBlock)) {
                    throw new InconsistentNodeException(node);
                }
                resultNode.setBlock((JBlock) this.state.pop());
            }
        }
        this.doCommonCloningWork(node, resultNode);
        return resultNode;
    }

    /**
     * Métodos visit para objetos do tipo <code>JInitializerSet</code>.
     *
     * @param node O nó a ser clonado.
     * @param data Um objeto que pode conter informação adicional necessária ao
     * visitor.
     *
     * @return Um objeto qualquer.
     *
     * @exception java.lang.IllegalArgumentException Levantada quando o
     * parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     * apresenta uma estrutura interna inválida.
     */
    public Object visit(JInitializerSet node, Object data)
            throws InconsistentNodeException, IllegalArgumentException,
            ExecutionException {

        if (node == null) {
            throw new IllegalArgumentException();
        }
        JInitializerSet resultNode = new JInitializerSet();
        if (node.getDeclarationSet() != null) {
            int size = node.getDeclarationSet().size();
            for (int i = 0; i < size; i++) {
                if (!(this.state.top() instanceof JInitializer)) {
                    throw new InconsistentNodeException(node);
                }
                resultNode.add(this.state.pop());
            }
        }
        this.doCommonCloningWork(node, resultNode);
        return resultNode;
    }

    /**
     * Métodos visit para objetos do tipo <code>JInterfaceDeclaration</code>.
     *
     * @param node O nó a ser clonado.
     * @param data Um objeto que pode conter informação adicional necessária
     * ao visitor.
     *
     * @return Um objeto qualquer.
     *
     * @exception java.lang.IllegalArgumentException Levantada quando o
     * parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     * apresenta uma estrutura interna inválida.
     */
    public Object visit(JInterfaceDeclaration node, Object data)
            throws InconsistentNodeException, IllegalArgumentException {

        // ordem de escrita no accept:
        // corpo
        // interfaces
        // name
        // modifiers
        if (node == null) {
            throw new IllegalArgumentException("Declaracao de classe nula");
        }
        JInterfaceDeclaration resultNode = new JInterfaceDeclaration();
        if (node.getModifiers() == null ||
                !(this.state.top() instanceof JModifierList)) {
            throw new InconsistentNodeException(node);
        }
        if (node.hasComment()) {
            resultNode.setComment(node.getComment());
        }

        JModifierList mods = (JModifierList) this.state.pop();
        resultNode.setModifiers(mods);
        JIdentifier name = new JIdentifier();

        if ((node.getName() == null) || !(this.state.top() instanceof JIdentifier)) {
            throw new InconsistentNodeException(node);
        }
        name = (JIdentifier) this.state.pop();
        resultNode.setName(name);
        if (node.getInterfaces() != null) {
            if (!(this.state.top() instanceof JNameList)) {
                throw new InconsistentNodeException(node);
            }
            JNameList interfaces = (JNameList) this.state.pop();
            resultNode.setInterfaces(interfaces);
        }
        if (node.getBody() != null) {
            if (!(this.state.top() instanceof TypeBody)) {
                throw new InconsistentNodeException(node);
            }
            TypeBody body = (TypeBody) this.state.pop();
            resultNode.setBody(body);

        }

        this.doCommonCloningWork(node, resultNode);
        return resultNode;
    }

    /**
     * Métodos visit para objetos do tipo <code>JImportDeclarationSet</code>.
     *
     * @param node O nó a ser impresso.
     * @param data Um objeto que pode conter informação adicional necessária
     * ao visitor.
     *
     * @return Um objeto qualquer.
     *
     * @exception java.lang.IllegalArgumentException Levantada quando o
     * parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     * apresenta uma estrutura interna inválida.
     */
    public Object visit(ImportDeclarations node, Object data)
            throws InconsistentNodeException, IllegalArgumentException {

        if (node == null) {
            throw new IllegalArgumentException("JImportDeclaration nula");
        }

        ImportDeclarations resultNode = new ImportDeclarations();

        // Clonagem das declarações de importação (tipo: import java.io.File;)
        if (node.getImportsSet() == null) {
            throw new InconsistentNodeException(node);
        }

        JImportDeclarationSet importsSet = new JImportDeclarationSet();

        if (!(this.state.top() instanceof JImportDeclarationSet)) {
            throw new InconsistentNodeException(node);
        }
        importsSet = (JImportDeclarationSet) this.state.pop();

        resultNode.setImportsSet(importsSet);

        // Clonagem das declarações do tipo ImportVarDeclaration
        if (node.getImportVarSet() == null) {
            throw new InconsistentNodeException(node);
        }

        NodeList importVarSet = new NodeList();
        int numImportVars = node.getImportVarSet().size();
        int j = 0;

        while (j < numImportVars) {
            if (!(this.state.top() instanceof JImportDeclaration)) {
                throw new InconsistentNodeException(node);
            }
            JImportDeclaration tempImportVar = (JImportDeclaration) this.state.pop();
            importVarSet.addElement(tempImportVar);
            j++;
        }

        resultNode.setImportVarSet(importVarSet);

        // Clonagem das declarações do tipo ImportsVarDeclaration
        if (node.getImportsVarSet() == null) {
            throw new InconsistentNodeException(node);
        }

        NodeList importsVarSet = new NodeList();
        int numImportsVars = node.getImportsVarSet().size();
        int k = 0;

        while (k < numImportsVars) {
            if (!(this.state.top() instanceof JImportDeclarationSet)) {
                throw new InconsistentNodeException(node);
            }
            JImportDeclarationSet tempImportsVar = (JImportDeclarationSet) this.state.pop();
            importsVarSet.addElement(tempImportsVar);
            k++;
        }

        resultNode.setImportsVarSet(importsVarSet);
        this.doCommonCloningWork(node, resultNode);

        return resultNode;
    }

    /**
     * Métodos visit para objetos do tipo <code>InstanceOfExpression</code>.
     *
     * @param node O nó a ser clonado.
     * @param data Um objeto que pode conter informação adicional necessária ao
     * visitor.
     *
     * @return Um objeto qualquer.
     *
     * @exception java.lang.IllegalArgumentException Levantada quando o
     * parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     * apresenta uma estrutura interna inválida.
     */
    public Object visit(InstanceOfExpression node, Object data)
            throws InconsistentNodeException, IllegalArgumentException {

        if (node == null) {
            throw new IllegalArgumentException();
        }

        InstanceOfExpression resultNode = new InstanceOfExpression();

        if (node.getType() == null
                || !(this.state.top() instanceof JType))
            throw new InconsistentNodeException(node);
        JType type = (JType) this.state.pop();

        if (node.getOperand() == null
                || !(this.state.top() instanceof JaTSNode))
            throw new InconsistentNodeException(node);
        JaTSNode operand = this.state.pop();

        resultNode.setOperand(operand);
        resultNode.setType(type);
        this.doCommonCloningWork(node, resultNode);
        return resultNode;
    }


    /**
     * Métodos visit para objetos do tipo <code>IterativeDeclaration</code>.
     *
     * @param node O nó a ser clonado.
     * @param data Um objeto que pode conter informação adicional necessária ao
     * visitor.
     *
     * @return Um objeto qualquer.
     *
     * @exception java.lang.IllegalArgumentException Levantada quando o
     * parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     * apresenta uma estrutura interna inválida.
     */
    public Object visit(IterativeDeclaration node, Object data)
            throws InconsistentNodeException, IllegalArgumentException {

        IterativeDeclaration resultNode = new IterativeDeclaration();

        this.cloneIterativeDeclaration(node, resultNode);

        return resultNode;
    }

    /**
     * Métodos visit para objetos do tipo <code>JLiteral</code>.
     *
     * @param node O nó a ser clonado.
     * @param data Um objeto que pode conter informação adicional necessária ao
     * visitor.
     *
     * @return Um objeto qualquer.
     *
     * @exception java.lang.IllegalArgumentException Levantada quando o
     * parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     * apresenta uma estrutura interna inválida.
     */
    public Object visit(JLiteral node, Object data)
            throws InconsistentNodeException, IllegalArgumentException {
        if (node == null) {
            throw new IllegalArgumentException();
        }

        JLiteral resultNode = new JLiteral();

        if (!node.isVariable() && node.getLiteralValue() == null) {
            throw new InconsistentNodeException(node);
        } else if (node.getLiteralValue() != null) {
            resultNode.setLiteralValue((node.getLiteralValue()).toString());
            resultNode.setLiteralType(node.getLiteralType());
        }

        this.doCommonCloningWork(node, resultNode);
        return resultNode;
    }

    public Object visit(JLocalVariableDeclaration node, Object data)
            throws InconsistentNodeException, IllegalArgumentException,
            ExecutionException {

        if (node == null) {
            throw new IllegalArgumentException("JLocalVariableDeclaration nulo");
        }

        JLocalVariableDeclaration resultNode = new JLocalVariableDeclaration();

        // Se for executável, a declaração de atributo não tem modificadores,
        // tipo ou declaradores.
        if (node.isAJavaNode()) {

            // clona o comentário
            if (node.getComment() != null) {
                resultNode.setComment(node.getComment());
            }

            // Clonagem dos modificadores
            if (node.getModifiers() == null ||
                    !(this.state.top() instanceof JModifierList)) {
                throw new InconsistentNodeException(node);
            }
            resultNode.setModifiers((JModifierList) this.state.pop());

            // Clonagem do tipo do atributo
            if (node.getType() == null ||
                    !(this.state.top() instanceof JType)) {
                throw new InconsistentNodeException(node);
            }
            JType type = (JType) this.state.pop();
            resultNode.setType(type);

            // Clonagem dos nomes dos atributos e suas respectivas inicializações
            if (node.getVariables() == null) {
                throw new InconsistentNodeException(node);
            }

            JVariableDeclarator temp = new JVariableDeclarator();
            JVariableDeclaratorSet varDec = new JVariableDeclaratorSet();
            int vd = node.size();
            int i = 0;

            while (i < vd) {
                if (!(this.state.top() instanceof JVariableDeclarator)) {
                    throw new InconsistentNodeException(node);
                }
                temp = (JVariableDeclarator) this.state.pop();
                varDec.add(temp);
                i++;
            }
            resultNode.setVariables(varDec);
        }

        this.doCommonCloningWork(node, resultNode);

        return resultNode;
    }

    /**
     * Métodos visit para objetos do tipo <code>MapTable</code>.
     * @param node O nó onde a substituição ocorrerá.
     * @param data Um objeto que pode conter informação adicional necessária ao
     *        visitor.
     * @return Um objeto qualquer.
     * @exception java.lang.IllegalArgumentException Levantada quando o
     *            parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     *            apresenta uma estrutura interna inválida.
     */
    public Object visit(MapTable node, Object data)
            throws InconsistentNodeException, IllegalArgumentException,
            ExecutionException {

        if (node == null) {
            throw new IllegalArgumentException();
        }

        MapTable resultNode = new MapTable();

        if (!node.isExecutable()) {
            if (node.getMap() == null) {
                throw new InconsistentNodeException(node);
            }
            resultNode.setMap(node.getMap());
        }

        this.doCommonCloningWork(node, resultNode);

        return resultNode;
    }


    /**
     * Métodos visit para objetos do tipo <code>JMethodDeclarationSet</code>.
     * @param node O nó a ser clonado.
     * @param data Um objeto que pode conter informação adicional necessária ao<br>
     *        visitor.
     * @return Um objeto qualquer.
     * @exception java.lang.IllegalArgumentException Levantada quando o
     *            parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     *            apresenta uma estrutura interna inválida.
     */
    public Object visit(JMethodDeclarationSet node, Object data)
            throws InconsistentNodeException, IllegalArgumentException {

        if (node == null) {
            throw new IllegalArgumentException();
        }
        JMethodDeclarationSet resultNode = new JMethodDeclarationSet();
        JMethodDeclaration tmp = new JMethodDeclaration();
        int i = 0;
        int size = node.size();
        while (i < size) {
            if (this.state.top() instanceof JMethodDeclaration) {
                tmp = (JMethodDeclaration) this.state.pop();
                resultNode.add(tmp);
            }
            i++;
        }
        this.doCommonCloningWork(node, resultNode);
        return resultNode;
    }


    /**
     * Métodos visit para objetos do tipo <code>BodyMethodDeclarations</code>.
     *
     * @param node O nó a ser impresso.
     * @param data Um objeto que pode conter informação adicional necessária
     * ao visitor.
     *
     * @return Um objeto qualquer.
     *
     * @exception java.lang.IllegalArgumentException Levantada quando o
     * parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     * apresenta uma estrutura interna inválida.
     */
    public Object visit(BodyMethodDeclarations node, Object data)
            throws InconsistentNodeException, IllegalArgumentException {

        if (node == null) {
            throw new IllegalArgumentException("JMethodDeclaration nula");
        }

        BodyMethodDeclarations resultNode = new BodyMethodDeclarations();

        // Clonagem das declarações de metodo (tipo: private #rt #a (#fp) {})
        if (node.getMethodsSet() == null) {
            throw new InconsistentNodeException(node);
        }

        JMethodDeclarationSet methodsSet = new JMethodDeclarationSet();
        if (!(this.state.top() instanceof JMethodDeclarationSet)) {
            throw new InconsistentNodeException(node);
        }
        methodsSet = (JMethodDeclarationSet) this.state.pop();

        resultNode.setMethodsSet(methodsSet);

        // Clonagem das declarações do tipo MethodVarDeclaration
        if (node.getMethodVarSet() == null) {
            throw new InconsistentNodeException(node);
        }

        NodeList methodVarSet = new NodeList();
        int numMethodVars = node.getMethodVarSet().size();
        int j = 0;

        while (j < numMethodVars) {
            if (!(this.state.top() instanceof JMethodDeclaration)) {
                throw new InconsistentNodeException(node);
            }
            JMethodDeclaration tempMethodVar =
                    (JMethodDeclaration) this.state.pop();
            methodVarSet.addElement(tempMethodVar);
            j++;
        }

        resultNode.setMethodVarSet(methodVarSet);

        // Clonagem das declarações do tipo MethodsVarDeclaration
        if (node.getMethodsVarSet() == null) {
            throw new InconsistentNodeException(node);
        }

        NodeList methodsVarSet = new NodeList();
        int numMethodsVars = node.getMethodsVarSet().size();
        int k = 0;

        while (k < numMethodsVars) {
            if (!(this.state.top() instanceof JMethodDeclarationSet)) {
                throw new InconsistentNodeException(node);
            }
            JMethodDeclarationSet tempMethodsVar = (JMethodDeclarationSet) this.state.pop();
            methodsVarSet.addElement(tempMethodsVar);
            k++;
        }

        resultNode.setMethodsVarSet(methodsVarSet);
        this.doCommonCloningWork(node, resultNode);
        return resultNode;
    }


    /**
     * Métodos visit para objetos do tipo <code>BodyMethodDeclarations</code>.
     *
     * @param node O nó a ser impresso.
     * @param data Um objeto que pode conter informação adicional necessária
     * ao visitor.
     *
     * @return Um objeto qualquer.
     *
     * @exception java.lang.IllegalArgumentException Levantada quando o
     * parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     * apresenta uma estrutura interna inválida.
     */
    public Object visit(BodyTypeDeclarations node, Object data)
            throws InconsistentNodeException, IllegalArgumentException {

        if (node == null) {
            throw new IllegalArgumentException("BodyTypeDeclarations nula");
        }

        BodyTypeDeclarations resultNode = new BodyTypeDeclarations();

        // Clonagem das declarações de tipos completas
        if (node.getTypesSet() == null) {
            throw new InconsistentNodeException(node);
        }

        JTypeDeclarationSet typesSet = new JTypeDeclarationSet();
        if (!(this.state.top() instanceof JTypeDeclarationSet)) {
            throw new InconsistentNodeException(node);
        }
        typesSet = (JTypeDeclarationSet) this.state.pop();

        resultNode.setTypesSet(typesSet);

        // Clonagem das declarações do tipo #type:ClassDeclaration
        if (node.getTypeVarSet() == null) {
            throw new InconsistentNodeException(node);
        }

        NodeList typeVarSet = new NodeList();
        int numTypeVars = node.getTypeVarSet().size();
        int j = 0;

        while (j < numTypeVars) {
            if (!(this.state.top() instanceof JTypeDeclaration)) {
                throw new InconsistentNodeException(node);
            }
            JTypeDeclaration tempTypeVar =
                    (JTypeDeclaration) this.state.pop();
            typeVarSet.addElement(tempTypeVar);
            j++;
        }

        resultNode.setTypeVarSet(typeVarSet);

        // Clonagem das declarações do tipo MethodsVarDeclaration
        if (node.getTypesVarSet() == null) {
            throw new InconsistentNodeException(node);
        }

        NodeList typesVarSet = new NodeList();
        int numTypesVars = node.getTypesVarSet().size();
        int k = 0;

        while (k < numTypesVars) {
            if (!(this.state.top() instanceof JTypeDeclarationSet)) {
                throw new InconsistentNodeException(node);
            }
            JTypeDeclarationSet tempTypesVar =
                    (JTypeDeclarationSet) this.state.pop();
            typesVarSet.addElement(tempTypesVar);
            k++;
        }

        resultNode.setTypesVarSet(typesVarSet);
        this.doCommonCloningWork(node, resultNode);
        return resultNode;
    }

    /**
     * Métodos visit para objetos do tipo <code>JMethodInvocation</code>.
     *
     * @param node O nó a ser clonado.
     * @param data Um objeto que pode conter informação adicional necessária ao
     * visitor.
     *
     * @return Um objeto qualquer.
     *
     * @exception java.lang.IllegalArgumentException Levantada quando o
     * parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     * apresenta uma estrutura interna inválida.
     */
    public Object visit(JMethodInvocation node, Object data)
            throws InconsistentNodeException, IllegalArgumentException {
        if (node == null) {
            throw new IllegalArgumentException();
        }

        JMethodInvocation resultNode = new JMethodInvocation();

        if (node.getArgumentList() == null
                || !(this.state.top() instanceof JExpressionList)) {
            throw new InconsistentNodeException(node);
        }
        JExpressionList args = (JExpressionList) this.state.pop();

        JaTSNode methodName = null;
        if (node.getMethodName() == null) {
            throw new InconsistentNodeException(node);
        }
        methodName = this.state.pop();

        // clona o comentário
        if (node.getComment() != null) {
            resultNode.setComment(node.getComment());
        }

        JaTSNode refObj = null;
        if (node.getReferencedObject() != null) {
            refObj = this.state.pop();
            resultNode.setReferencedObject(refObj);
        }

        resultNode.setMethodName(JName.unwrapName(methodName));
        resultNode.setArgumentList(args);

        this.doCommonCloningWork(node, resultNode);
        return resultNode;
    }

    /**
     * Métodos visit para objetos do tipo <code>JModifierList</code>.
     *
     * @param node O nó a ser clonado.
     * @param data Um objeto que pode conter informação adicional necessária ao
     * visitor.
     *
     * @return Um objeto qualquer.
     *
     * @exception java.lang.IllegalArgumentException Levantada quando o
     * parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     * apresenta uma estrutura interna inválida.
     */
    public Object visit(JModifierList node, Object data)
            throws InconsistentNodeException, IllegalArgumentException {
        if (node == null) {
            throw new IllegalArgumentException();
        }

        JModifierList resultNode = new JModifierList();

        resultNode.setPossibleModifiers(node.getPossibleModifiers());
        resultNode.addModifier(node.getModifiers());

        this.doCommonCloningWork(node, resultNode);

        return resultNode;
    }

    /**
     * Métodos visit para objetos do tipo <code>JName</code>.
     *
     * @param node O nó a ser clonado.
     * @param data Um objeto que pode conter informação adicional necessária ao
     * visitor.
     *
     * @return Um objeto qualquer.
     *
     * @exception java.lang.IllegalArgumentException Levantada quando o
     * parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     * apresenta uma estrutura interna inválida.
     */
    public Object visit(JName node, Object data)
            throws InconsistentNodeException, IllegalArgumentException {
        if (node == null) {
            throw new IllegalArgumentException();
        }

        JName resultNode = new JName();

        // desempilha o que veio  e adiciona em um name temporário
        // pois a ordem está invertida
        int i = node.size() - 1;
        NodeList names = new NodeList();

        while (i >= 0) {
            names.addElement(this.state.pop());
            i--;
        }

        // pega o name temporário e inverte seus elementos, para que o name
        // retornado seja igual ao name original
        int j = 0, max = names.size();
        while (j < max) {
            JaTSNode tempNode = names.elementAt(max - (j + 1));
            if (!(tempNode instanceof JIdentifier))
                throw new InconsistentNodeException(node);

            resultNode.addIdentifier((JIdentifier) tempNode);
            j++;
        }

        this.doCommonCloningWork(node, resultNode);

        return resultNode;
    }

    /**
     * Métodos visit para objetos do tipo <code>JNodeString</code>.
     *
     * @param node O nó a ser clonado.
     * @param data Um objeto que pode conter informação adicional necessária ao
     * visitor.
     *
     * @return Um objeto qualquer.
     *
     * @exception java.lang.IllegalArgumentException Levantada quando o
     * parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     * apresenta uma estrutura interna inválida.
     */
    public Object visit(JNodeString node, Object data)
            throws InconsistentNodeException, IllegalArgumentException {
        if (node == null) {
            throw new IllegalArgumentException();
        }

        JNodeString resultNode = new JNodeString(node.getNodeValue());

        this.doCommonCloningWork(node, resultNode);
        return resultNode;
    }

    /**
     * Métodos visit para objetos do tipo <code>JNameList</code>.
     *
     * @param node O nó a ser clonado.
     * @param data Um objeto que pode conter informação adicional necessária ao
     * visitor.
     *
     * @return Um objeto qualquer.
     *
     * @exception java.lang.IllegalArgumentException Levantada quando o
     * parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     * apresenta uma estrutura interna inválida.
     */
    public Object visit(JNameList node, Object data)
            throws InconsistentNodeException, IllegalArgumentException {

        if (node == null) {
            throw new IllegalArgumentException();
        }
        JNameList resultNode = new JNameList();
        if (node.isAJavaNode()) {
            int i = 0;
            int size = node.size();
            while (i < size) {
                if (this.state.top() instanceof JName) {
                    resultNode.add(this.state.pop());
                }
                i++;
            }
        }
        this.doCommonCloningWork(node, resultNode);
        return resultNode;
    }

    /**
     * Métodos visit para objetos do tipo <code>OtherDeclarationsSet</code>.
     *
     * @param node O nó a ser clonado.
     * @param data Um objeto que pode conter informação adicional necessária ao
     * visitor.
     *
     * @return Um objeto qualquer.
     *
     * @exception java.lang.IllegalArgumentException Levantada quando o
     * parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     * apresenta uma estrutura interna inválida.
     */
    public Object visit(OtherDeclarationsSet node, Object data)
            throws InconsistentNodeException, IllegalArgumentException {

        if (node == null) {
            throw new IllegalArgumentException();
        }

        OtherDeclarationsSet resultNode = new OtherDeclarationsSet();

        // Clona as "outras" declarações.
        int max3 = node.numberOfConditionalDeclarations();
        int k = 0;

        while (k < max3) {
            JaTSNode tempOtherDec = this.state.pop();
            resultNode.addConditionalDeclaration(tempOtherDec);
            if (tempOtherDec instanceof NestableExecutableDeclaration) {
                ((NestableExecutableDeclaration) tempOtherDec).setParentNode(resultNode);
            }
            k++;
        }

        // Clona as declarações resultantes.
        int max = node.numberOfResultDeclarations();
        int i = 0;

        while (i < max) {
            JaTSNode tempResDec = this.state.pop();
            resultNode.add(tempResDec);
            i++;
        }

        // Clona as declarações iterativas.
        int max2 = node.numberOfIterativeDeclarations();
        int j = 0;

        while (j < max2) {
            if (!(this.state.top() instanceof IterativeDeclaration)) {
                throw new InconsistentNodeException(node);
            }
            IterativeDeclaration tempIterDec =
                    (IterativeDeclaration) this.state.pop();
            resultNode.addIterativeDeclaration(tempIterDec);
            tempIterDec.setParentNode(resultNode);
            j++;
        }

        this.doCommonCloningWork(node, resultNode);

        return resultNode;
    }


    /**
     * Métodos visit para objetos do tipo <code>JPackageDeclaration</code>.
     *
     * @param node O nó a ser clonado.
     * @param data Um objeto que pode conter informação adicional necessária ao<br>
     * visitor.
     *
     * @return Um objeto qualquer.
     *
     * @exception java.lang.IllegalArgumentException Levantada quando o
     * parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     * apresenta uma estrutura interna inválida.
     */
    public Object visit(JPackageDeclaration node, Object data)
            throws InconsistentNodeException, IllegalArgumentException {

        if (node == null) {
            throw new IllegalArgumentException();
        }
        JPackageDeclaration resultNode = new JPackageDeclaration();
        if (node.isAJavaNode()) {
            resultNode = new JPackageDeclaration();
            JName tmp = new JName();
            if (this.state.top() instanceof JName) {
                tmp = (JName) this.state.pop();
                resultNode.setName(tmp);
            }

        }
        this.doCommonCloningWork(node, resultNode);
        return resultNode;
    }

    /**
     * Métodos visit para objetos do tipo <code>JParameterList</code>.
     *
     * @param node O nó a ser clonado.
     * @param data Um objeto que pode conter informação adicional necessária ao<br>
     * visitor.
     *
     * @return Um objeto qualquer.
     *
     * @exception java.lang.IllegalArgumentException Levantada quando o
     * parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     * apresenta uma estrutura interna inválida.
     */
    public Object visit(JParameterList node, Object data)
            throws InconsistentNodeException, IllegalArgumentException {

        if (node == null) {
            throw new IllegalArgumentException();
        }
        JParameterList resultNode = new JParameterList();
        JFormalParameter tmp = new JFormalParameter();
        if (node.isAJavaNode()) {
            int i = 0;
            int size = node.size();
            while (i < size) {
                if (this.state.top() instanceof JFormalParameter) {
                    tmp = (JFormalParameter) this.state.pop();
                    resultNode.add(tmp);
                }
                i++;
            }

        }
        this.doCommonCloningWork(node, resultNode);
        return resultNode;
    }

    /**
     * Métodos visit para objetos do tipo <code>JPreconditionDeclaration</code>.
     *
     * @param node O nó a ser impresso.
     * @param data Um objeto que pode conter informação adicional necessária ao<br>
     * visitor.
     *
     * @return Um objeto qualquer.
     *
     * @exception java.lang.IllegalArgumentException Levantada quando o
     * parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     * apresenta uma estrutura interna inválida.
     */
    public Object visit(JPreconditionDeclaration node, Object data)
            throws InconsistentNodeException, IllegalArgumentException {

        if (node == null) {
            throw new IllegalArgumentException();
        }
        JPreconditionDeclaration resultNode = new JPreconditionDeclaration();
        if (!(this.state.top() instanceof JLiteral)) {
            throw new InconsistentNodeException("Literal nulo");
        }
        JLiteral l = (JLiteral) this.state.pop();
        resultNode.setValue(l);

        this.doCommonCloningWork(node, resultNode);
        return resultNode;
    }

    /**
     * Métodos visit para objetos do tipo <code>JResultStatement</code>.
     *
     * @param node O nó a ser impresso.
     * @param data Um objeto que pode conter informação adicional necessária ao<br>
     * visitor.
     *
     * @return Um objeto qualquer.
     *
     * @exception java.lang.IllegalArgumentException Levantada quando o
     * parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     * apresenta uma estrutura interna inválida.
     */
    public Object visit(JResultStatement node, Object data)
            throws InconsistentNodeException, IllegalArgumentException,
            ExecutionException {

        if (node == null) {
            throw new IllegalArgumentException();
        }

        JResultStatement resultNode = new JResultStatement();
        if (node.isAJavaNode()) {

            // clona o comentário
            if (node.getComment() != null) {
                resultNode.setComment(node.getComment());
            }
            // clona o tipo
            resultNode.setType(node.getType());
            // clona a expressão, se houver
            if (node.getExpression() != null) {
                if (!(this.state.top() instanceof JExpression)) {
                    throw new InconsistentNodeException(node);
                }
                resultNode.setExpression((JExpression) this.state.pop());
            }
        }
        this.doCommonCloningWork(node, resultNode);
        return resultNode;

    }


    /**
     * Métodos visit para objetos do tipo <code>JStatementExpression</code>.
     *
     * @param node O nó a ser clonado.
     * @param data Um objeto que pode conter informação adicional necessária ao
     * visitor.
     *
     * @return Um objeto qualquer.
     *
     * @exception java.lang.IllegalArgumentException Levantada quando o
     * parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     * apresenta uma estrutura interna inválida.
     */
    public  Object visit( JStatementExpression node,  Object data) throws IllegalArgumentException, InconsistentNodeException{
        if (node == null) {
            throw new IllegalArgumentException("node is null");
        }
        JStatementExpression resultNode = new JStatementExpression();
        if (node.isAJavaNode()) {
            if (node.getExpression() == null
                    && !(this.state.top() instanceof JExpression)) {
                throw new InconsistentNodeException(node);
            }
            resultNode.setExpression((JExpression)this.state.pop());
        }
        this.doCommonCloningWork(node, resultNode);
        return resultNode;
    }

    /**
     * Métodos visit para objetos do tipo <code>JSwitchStatement</code>.
     *
     * @param node O nó a ser clonado.
     * @param data Um objeto que pode conter informação adicional necessária ao
     * visitor.
     *
     * @return Um objeto qualquer.
     *
     * @exception java.lang.IllegalArgumentException Levantada quando o
     * parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     * apresenta uma estrutura interna inválida.
     */
    public Object visit(JSwitchStatement node, Object data)
            throws InconsistentNodeException, IllegalArgumentException,
            ExecutionException {

        if (node == null) {
            throw new IllegalArgumentException();
        }

        JSwitchStatement resultNode = new JSwitchStatement();
        if (!node.isExecutable() && !node.isVariable()) {
            // clona a expressão do switch
            if (node.getExpression() != null) {
                if (!(this.state.top() instanceof JExpression)) {
                    throw new InconsistentNodeException(node);
                }
                resultNode.setExpression((JExpression) this.state.pop());
            }
            // clona os cases
            if (node.getCases() != null) {
                int max = node.getCases().size();
                NodeList cases = new NodeList();
                for (int i = 0; i < max; i++) {
                    if (!(this.state.top() instanceof JSwitchPair)) {
                        throw new InconsistentNodeException(node);
                    }
                    cases.addElement(this.state.pop());
                }
                resultNode.setCases(cases);
            }
        }
        this.doCommonCloningWork(node, resultNode);
        return resultNode;
    }

    /**
     * Métodos visit para objetos do tipo <code>JSwitchLabel</code>.
     *
     * @param node O nó a ser clonado.
     * @param data Um objeto que pode conter informação adicional necessária ao
     * visitor.
     *
     * @return Um objeto qualquer.
     *
     * @exception java.lang.IllegalArgumentException Levantada quando o
     * parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     * apresenta uma estrutura interna inválida.
     */
    public Object visit(JSwitchLabel node, Object data)
            throws InconsistentNodeException, IllegalArgumentException,
            ExecutionException {

        if (node == null) {
            throw new IllegalArgumentException();
        }
        JSwitchLabel resultNode = new JSwitchLabel();
        if (node.isAJavaNode()) {
            // clona o tipo
            resultNode.setType(node.getType());

            // clona a expressão, se houver
            if (node.getExpression() != null) {
                if (!(this.state.top() instanceof JExpression)) {
                    throw new InconsistentNodeException(node);
                }
                resultNode.setExpression((JExpression) this.state.pop());
            }
        }
        this.doCommonCloningWork(node, resultNode);
        return resultNode;
    }

    /**
     * Métodos visit para objetos do tipo <code>JSwitchPair</code>.
     *
     * @param node O nó a ser clonado.
     * @param data Um objeto que pode conter informação adicional necessária ao
     * visitor.
     *
     * @return Um objeto qualquer.
     *
     * @exception java.lang.IllegalArgumentException Levantada quando o
     * parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     * apresenta uma estrutura interna inválida.
     */
    public Object visit(JSwitchPair node, Object data)
            throws InconsistentNodeException, IllegalArgumentException,
            ExecutionException {

        if (node == null) {
            throw new IllegalArgumentException();
        }
        JSwitchPair resultNode = new JSwitchPair();
        if (node.isAJavaNode()) {
            // clona o label
            if (node.getLabel() != null) {
                if (!(this.state.top() instanceof JSwitchLabel)) {
                    throw new InconsistentNodeException(node);
                }
                resultNode.setLabel((JSwitchLabel) this.state.pop());
            }

            // clona a lista de statements associados
            if (node.getStatements() != null) {
                int max = node.getStatements().size();
                NodeList statements = new NodeList();

                for (int i = 0; i < max; i++) {
                    if (!(this.state.top() instanceof JStatement)) {
                        throw new InconsistentNodeException(node);
                    }
                    statements.addElement(this.state.pop());
                }
                resultNode.setStatements(statements);
            }

        }
        this.doCommonCloningWork(node, resultNode);
        return resultNode;
    }

    /**
     * Métodos visit para objetos do tipo <code>JTernaryExpression</code>.
     *
     * @param node O nó a ser clonado.
     * @param data Um objeto que pode conter informação adicional necessária ao
     * visitor.
     *
     * @return Um objeto qualquer.
     *
     * @exception java.lang.IllegalArgumentException Levantada quando o
     * parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     * apresenta uma estrutura interna inválida.
     */
    public Object visit(JTernaryExpression node, Object data)
            throws InconsistentNodeException, IllegalArgumentException {
        if (node == null) {
            throw new IllegalArgumentException();
        }

        JTernaryExpression resultNode = new JTernaryExpression();

        if (node.getThirdOperand() == null
                || !(this.state.top() instanceof JExpression))
            throw new InconsistentNodeException(node);
        JExpression third = (JExpression) this.state.pop();

        if (node.getSecondOperand() == null
                || !(this.state.top() instanceof JExpression))
            throw new InconsistentNodeException(node);
        JExpression second = (JExpression) this.state.pop();

        if (node.getFirstOperand() == null
                || !(this.state.top() instanceof JExpression))
            throw new InconsistentNodeException(node);
        JExpression first = (JExpression) this.state.pop();

        resultNode.setThirdOperand(third);
        resultNode.setSecondOperand(second);
        resultNode.setFirstOperand(first);

        resultNode.setOperator(node.getOperator());
        this.doCommonCloningWork(node, resultNode);

        return resultNode;
    }

    /**
     * Métodos visit para objetos do tipo <code>JTryStatement</code>.
     *
     * @param node O nó a ser clonado.
     * @param data Um objeto que pode conter informação adicional necessária ao
     * visitor.
     *
     * @return Um objeto qualquer.
     *
     * @exception java.lang.IllegalArgumentException Levantada quando o
     * parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     * apresenta uma estrutura interna inválida.
     */
    public Object visit(JTryStatement node, Object data)
            throws InconsistentNodeException, IllegalArgumentException,
            ExecutionException {
        if (node == null) {
            throw new IllegalArgumentException("JTryStatement nulo");
        }

        JTryStatement resultNode = new JTryStatement();

        if (node.isAJavaNode()) {
            // clona o bloco try
            if (node.getTryBlock() != null) {
                if (!(this.state.top() instanceof JBlock)) {
                    throw new InconsistentNodeException(node);
                }
                resultNode.setTryBlock((JBlock) this.state.pop());
            }

            // clona o statement
            if (node.getCatchlist() != null) {
                int max = node.getCatchlist().size();
                NodeList catchList = new NodeList();
                for (int i = 0; i < max; i++) {
                    if (!(this.state.top() instanceof JCatchClause)) {
                        throw new IllegalArgumentException("");
                    }
                    catchList.addElement(this.state.pop());
                }
                resultNode.setCatchlist(catchList);
            }

            // clona o bloco finally, se houver
            if (node.getFinallyBlock() != null) {
                if (!(this.state.top() instanceof JBlock)) {
                    throw new InconsistentNodeException(node);
                }
                resultNode.setFinallyBlock((JBlock) this.state.pop());
            }
        }

        this.doCommonCloningWork(node, resultNode);

        return resultNode;
    }


    /**
     * Métodos visit para objetos do tipo <code>JType</code>.
     *
     * @param node O nó a ser clonado.
     * @param data Um objeto que pode conter informação adicional necessária ao
     * visitor.
     *
     * @return Um objeto qualquer.
     *
     * @exception java.lang.IllegalArgumentException Levantada quando o
     * parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     * apresenta uma estrutura interna inválida.
     */
    public Object visit(JType node, Object data)
            throws InconsistentNodeException, IllegalArgumentException {

        if (node == null) {
            throw new IllegalArgumentException();
        }
        JType resultNode = new JType();
        if (node.isAJavaNode()) {
            if ((node.getTypeName() == null
                    || !(this.state.top() instanceof JName)))
                throw new InconsistentNodeException(node);

            JName typeName = (JName) this.state.pop();
            resultNode.setName(typeName);
        }

        resultNode.setNumberOfDimensions(node.getNumberOfDimensions());
        this.doCommonCloningWork(node, resultNode);
        return resultNode;
    }

    /**
     * Métodos visit para objetos do tipo <code>JMethodDeclarationSet</code>.
     * @param node O nó a ser clonado.
     * @param data Um objeto que pode conter informação adicional necessária ao<br>
     *        visitor.
     * @return Um objeto qualquer.
     * @exception java.lang.IllegalArgumentException Levantada quando o
     *            parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     *            apresenta uma estrutura interna inválida.
     */
    public Object visit(JTypeDeclarationSet node, Object data)
            throws InconsistentNodeException, IllegalArgumentException {

        if (node == null) {
            throw new IllegalArgumentException();
        }
        JTypeDeclarationSet resultNode = new JTypeDeclarationSet();
        JTypeDeclaration tmp = null;
        int i = 0;
        int size = node.size();
        while (i < size) {
            if (this.state.top() instanceof JTypeDeclaration) {
                tmp = (JTypeDeclaration) this.state.pop();
                resultNode.add(tmp);
            }
            i++;
        }
        this.doCommonCloningWork(node, resultNode);
        return resultNode;
    }


    /**
     * Métodos visit para objetos do tipo <code>JUnaryExpression</code>.
     *
     * @param node O nó a ser clonado.
     * @param data Um objeto que pode conter informação adicional necessária ao
     * visitor.
     *
     * @return Um objeto qualquer.
     *
     * @exception java.lang.IllegalArgumentException Levantada quando o
     * parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     * apresenta uma estrutura interna inválida.
     */
    public Object visit(JUnaryExpression node, Object data)
            throws InconsistentNodeException, IllegalArgumentException {
        if (node == null) {
            throw new IllegalArgumentException();
        }

        JUnaryExpression resultNode = new JUnaryExpression();

        if (node.getOperand() == null
                || !(this.state.top() instanceof JExpression))
            throw new InconsistentNodeException(node);
        JExpression operand = (JExpression) this.state.pop();

        resultNode.setOperand(operand);
        resultNode.setOperator(node.getOperator());

        this.doCommonCloningWork(node, resultNode);
        return resultNode;
    }


    /**
     * Métodos visit para objetos do tipo <code>JVariableDeclarator</code>.
     *
     * @param node O nó a ser clonado.
     * @param data Um objeto que pode conter informação adicional necessária
     * ao visitor.
     *
     * @return Um objeto qualquer.
     *
     * @exception java.lang.IllegalArgumentException Levantada quando o
     * parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     * apresenta uma estrutura interna inválida.
     */
    public Object visit(JVariableDeclarator node, Object data)
            throws InconsistentNodeException, IllegalArgumentException {

        if (node == null) {
            throw new IllegalArgumentException("JVariableDeclarator nulo");
        }
        JVariableDeclarator resultNode = new JVariableDeclarator();
        if (node.isAJavaNode()) {
            if ((node.getIdentifier() == null ||
                    !(this.state.top() instanceof JIdentifier))) {
                throw new InconsistentNodeException(node);
            }
            JIdentifier id = (JIdentifier) this.state.pop();
            resultNode.setIdentifier(id);
        }

        if (node.getInitializer() != null) {
            JaTSNode init = this.state.pop();
            resultNode.setInitializer(init);
        }
        resultNode.setDimensions(node.getDimensions());
        this.doCommonCloningWork(node, resultNode);
        return resultNode;
    }

    /**
     * Métodos visit para objetos do tipo <code>JWhileStatement</code>.
     *
     * @param node O nó a ser clonado.
     * @param data Um objeto que pode conter informação adicional necessária
     * ao visitor.
     *
     * @return Um objeto qualquer.
     *
     * @exception java.lang.IllegalArgumentException Levantada quando o
     * parâmetro <code>node</code> for <code>null</code>.
     * @exception java.lang.IllegalArgumentException Levantada quando um nó
     * apresenta uma estrutura interna inválida.
     */
    public Object visit(JWhileStatement node, Object data)
            throws InconsistentNodeException, IllegalArgumentException,
            ExecutionException {

        if (node == null) {
            throw new IllegalArgumentException("JWhileStatement nulo");
        }
        JWhileStatement resultNode = new JWhileStatement();
        if (node.isAJavaNode()) {
            // clona o tipo
            resultNode.setType(node.getType());

            // se tiver comentário, clona o comentário
            if (node.getComment() != null) {
                resultNode.setComment(node.getComment());
            }


            // clona a expressão
            if (node.getExpression() != null) {
                if (!(this.state.top() instanceof JExpression)) {
                    throw new InconsistentNodeException(node);
                }
                resultNode.setExpression((JExpression) this.state.pop());
            }
            // clona o statement
            if (node.getStatement() != null) {
                if (!(this.state.top() instanceof JStatement)) {
                    throw new InconsistentNodeException(node);
                }
                resultNode.setStatement(this.state.pop());
            }
        }
        this.doCommonCloningWork(node, resultNode);
        return resultNode;

    }
}