/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.struts2.views.jsp;

import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.util.IteratorGenerator.Converter;
import org.apache.struts2.views.jsp.iterator.IteratorGeneratorTag;

import java.util.Iterator;

/**
 * Test case for IteratorGeneratorTag.
 *
 */
public class IteratorGeneratorTagTest extends AbstractTagTest {

    public void testGeneratorBasic() throws Exception {
        IteratorGeneratorTag tag = new IteratorGeneratorTag();

        tag.setPageContext(pageContext);
        tag.setVal("%{'aaa,bbb,ccc,ddd,eee'}");
        tag.doStartTag();
        Object topOfStack = stack.findValue("top");


        assertTrue(topOfStack instanceof Iterator);
        // 1
        assertTrue(((Iterator)topOfStack).hasNext());
        assertEquals(((Iterator)topOfStack).next(), "aaa");
        // 2
        assertTrue(((Iterator)topOfStack).hasNext());
        assertEquals(((Iterator)topOfStack).next(), "bbb");
        // 3
        assertTrue(((Iterator)topOfStack).hasNext());
        assertEquals(((Iterator)topOfStack).next(), "ccc");
        // 4
        assertTrue(((Iterator)topOfStack).hasNext());
        assertEquals(((Iterator)topOfStack).next(), "ddd");
        // 5
        assertTrue(((Iterator)topOfStack).hasNext());
        assertEquals(((Iterator)topOfStack).next(),"eee");

        assertFalse(((Iterator)topOfStack).hasNext());

        tag.doEndTag();
        Object afterTopOfStack = stack.findValue("top");


        assertNotSame(afterTopOfStack, topOfStack);

        // Basic sanity check of clearTagStateForTagPoolingServers() behaviour for Struts Tags after doEndTag().
        IteratorGeneratorTag freshTag = new IteratorGeneratorTag();
        freshTag.setPageContext(pageContext);
        assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set.  " +
                "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
                strutsBodyTagsAreReflectionEqual(tag, freshTag));
    }

    public void testGeneratorBasic_clearTagStateSet() throws Exception {
        IteratorGeneratorTag tag = new IteratorGeneratorTag();

        tag.setPerformClearTagStateForTagPoolingServers(true);  // Explicitly request tag state clearing.
        tag.setPageContext(pageContext);
        tag.setVal("%{'aaa,bbb,ccc,ddd,eee'}");
        tag.doStartTag();
        Object topOfStack = stack.findValue("top");


        assertTrue(topOfStack instanceof Iterator);
        // 1
        assertTrue(((Iterator)topOfStack).hasNext());
        assertEquals(((Iterator)topOfStack).next(), "aaa");
        // 2
        assertTrue(((Iterator)topOfStack).hasNext());
        assertEquals(((Iterator)topOfStack).next(), "bbb");
        // 3
        assertTrue(((Iterator)topOfStack).hasNext());
        assertEquals(((Iterator)topOfStack).next(), "ccc");
        // 4
        assertTrue(((Iterator)topOfStack).hasNext());
        assertEquals(((Iterator)topOfStack).next(), "ddd");
        // 5
        assertTrue(((Iterator)topOfStack).hasNext());
        assertEquals(((Iterator)topOfStack).next(),"eee");

        assertFalse(((Iterator)topOfStack).hasNext());

        tag.doEndTag();
        Object afterTopOfStack = stack.findValue("top");


        assertNotSame(afterTopOfStack, topOfStack);

        // Basic sanity check of clearTagStateForTagPoolingServers() behaviour for Struts Tags after doEndTag().
        IteratorGeneratorTag freshTag = new IteratorGeneratorTag();
        freshTag.setPerformClearTagStateForTagPoolingServers(true);
        freshTag.setPageContext(pageContext);
        assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set.  " +
                "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
                strutsBodyTagsAreReflectionEqual(tag, freshTag));
    }

    public void testGeneratorWithSeparator() throws Exception {
        IteratorGeneratorTag tag = new IteratorGeneratorTag();

        tag.setPageContext(pageContext);
        tag.setVal("%{'aaa|bbb|ccc|ddd|eee'}");
        tag.setSeparator("|");
        tag.doStartTag();
        Object topOfStack = stack.findValue("top");
        tag.doEndTag();
        Object afterTopOfStack = stack.findValue("top");

        assertTrue(topOfStack instanceof Iterator);
        // 1
        assertTrue(((Iterator)topOfStack).hasNext());
        assertEquals(((Iterator)topOfStack).next(), "aaa");
        // 2
        assertTrue(((Iterator)topOfStack).hasNext());
        assertEquals(((Iterator)topOfStack).next(), "bbb");
        // 3
        assertTrue(((Iterator)topOfStack).hasNext());
        assertEquals(((Iterator)topOfStack).next(), "ccc");
        // 4
        assertTrue(((Iterator)topOfStack).hasNext());
        assertEquals(((Iterator)topOfStack).next(), "ddd");
        // 5
        assertTrue(((Iterator)topOfStack).hasNext());
        assertEquals(((Iterator)topOfStack).next(), "eee");

        assertFalse(((Iterator)topOfStack).hasNext());
        assertNotSame(afterTopOfStack, topOfStack);

        // Basic sanity check of clearTagStateForTagPoolingServers() behaviour for Struts Tags after doEndTag().
        IteratorGeneratorTag freshTag = new IteratorGeneratorTag();
        freshTag.setPageContext(pageContext);
        assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set.  " +
                "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
                strutsBodyTagsAreReflectionEqual(tag, freshTag));
    }

    public void testGeneratorWithSeparator_clearTagStateSet() throws Exception {
        IteratorGeneratorTag tag = new IteratorGeneratorTag();

        tag.setPerformClearTagStateForTagPoolingServers(true);  // Explicitly request tag state clearing.
        tag.setPageContext(pageContext);
        tag.setVal("%{'aaa|bbb|ccc|ddd|eee'}");
        tag.setSeparator("|");
        tag.doStartTag();
        Object topOfStack = stack.findValue("top");
        tag.doEndTag();
        Object afterTopOfStack = stack.findValue("top");

        assertTrue(topOfStack instanceof Iterator);
        // 1
        assertTrue(((Iterator)topOfStack).hasNext());
        assertEquals(((Iterator)topOfStack).next(), "aaa");
        // 2
        assertTrue(((Iterator)topOfStack).hasNext());
        assertEquals(((Iterator)topOfStack).next(), "bbb");
        // 3
        assertTrue(((Iterator)topOfStack).hasNext());
        assertEquals(((Iterator)topOfStack).next(), "ccc");
        // 4
        assertTrue(((Iterator)topOfStack).hasNext());
        assertEquals(((Iterator)topOfStack).next(), "ddd");
        // 5
        assertTrue(((Iterator)topOfStack).hasNext());
        assertEquals(((Iterator)topOfStack).next(), "eee");

        assertFalse(((Iterator)topOfStack).hasNext());
        assertNotSame(afterTopOfStack, topOfStack);

        // Basic sanity check of clearTagStateForTagPoolingServers() behaviour for Struts Tags after doEndTag().
        IteratorGeneratorTag freshTag = new IteratorGeneratorTag();
        freshTag.setPerformClearTagStateForTagPoolingServers(true);
        freshTag.setPageContext(pageContext);
        assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set.  " +
                "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
                strutsBodyTagsAreReflectionEqual(tag, freshTag));
    }

    public void testGeneratorWithConverter() throws Exception {
        IteratorGeneratorTag tag = new IteratorGeneratorTag();

        tag.setPageContext(pageContext);
        tag.setVal("%{'aaa, bbb, ccc, ddd, eee'}");
        tag.setConverter("myConverter");
        tag.doStartTag();
        Object topOfStack = stack.findValue("top");
        tag.doEndTag();
        Object afterTopOfStack = stack.findValue("top");

        assertTrue(topOfStack instanceof Iterator);
        // 1.
        assertTrue(((Iterator)topOfStack).hasNext());
        assertEquals(((Iterator)topOfStack).next(), "myConverter-aaa");
        // 2
        assertTrue(((Iterator)topOfStack).hasNext());
        assertEquals(((Iterator)topOfStack).next(), "myConverter-bbb");
        // 3
        assertTrue(((Iterator)topOfStack).hasNext());
        assertEquals(((Iterator)topOfStack).next(), "myConverter-ccc");
        // 4.
        assertTrue(((Iterator)topOfStack).hasNext());
        assertEquals(((Iterator)topOfStack).next(), "myConverter-ddd");
        // 5.
        assertTrue(((Iterator)topOfStack).hasNext());
        assertEquals(((Iterator)topOfStack).next(), "myConverter-eee");

        assertFalse(((Iterator)topOfStack).hasNext());
        assertNotSame(afterTopOfStack, topOfStack);

        // Basic sanity check of clearTagStateForTagPoolingServers() behaviour for Struts Tags after doEndTag().
        IteratorGeneratorTag freshTag = new IteratorGeneratorTag();
        freshTag.setPageContext(pageContext);
        assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set.  " +
                "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
                strutsBodyTagsAreReflectionEqual(tag, freshTag));
    }

    public void testGeneratorWithConverter_clearTagStateSet() throws Exception {
        IteratorGeneratorTag tag = new IteratorGeneratorTag();

        tag.setPerformClearTagStateForTagPoolingServers(true);  // Explicitly request tag state clearing.
        tag.setPageContext(pageContext);
        tag.setVal("%{'aaa, bbb, ccc, ddd, eee'}");
        tag.setConverter("myConverter");
        tag.doStartTag();
        Object topOfStack = stack.findValue("top");
        tag.doEndTag();
        Object afterTopOfStack = stack.findValue("top");

        assertTrue(topOfStack instanceof Iterator);
        // 1.
        assertTrue(((Iterator)topOfStack).hasNext());
        assertEquals(((Iterator)topOfStack).next(), "myConverter-aaa");
        // 2
        assertTrue(((Iterator)topOfStack).hasNext());
        assertEquals(((Iterator)topOfStack).next(), "myConverter-bbb");
        // 3
        assertTrue(((Iterator)topOfStack).hasNext());
        assertEquals(((Iterator)topOfStack).next(), "myConverter-ccc");
        // 4.
        assertTrue(((Iterator)topOfStack).hasNext());
        assertEquals(((Iterator)topOfStack).next(), "myConverter-ddd");
        // 5.
        assertTrue(((Iterator)topOfStack).hasNext());
        assertEquals(((Iterator)topOfStack).next(), "myConverter-eee");

        assertFalse(((Iterator)topOfStack).hasNext());
        assertNotSame(afterTopOfStack, topOfStack);

        // Basic sanity check of clearTagStateForTagPoolingServers() behaviour for Struts Tags after doEndTag().
        IteratorGeneratorTag freshTag = new IteratorGeneratorTag();
        freshTag.setPerformClearTagStateForTagPoolingServers(true);
        freshTag.setPageContext(pageContext);
        assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set.  " +
                "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
                strutsBodyTagsAreReflectionEqual(tag, freshTag));
    }

    public void testGeneratorWithId() throws Exception {
        IteratorGeneratorTag tag = new IteratorGeneratorTag();
        tag.setPageContext(pageContext);
        tag.setVal("%{'aaa,bbb,ccc,ddd,eee'}");
        tag.setVar("myPageContextAttId");
        tag.doStartTag();
        tag.doEndTag();

        Object pageContextIterator = stack.findValue("myPageContextAttId");

        assertTrue(pageContextIterator instanceof Iterator);
        // 1
        assertTrue(((Iterator)pageContextIterator).hasNext());
        assertEquals(((Iterator)pageContextIterator).next(), "aaa");
        // 2.
        assertTrue(((Iterator)pageContextIterator).hasNext());
        assertEquals(((Iterator)pageContextIterator).next(), "bbb");
        // 3.
        assertTrue(((Iterator)pageContextIterator).hasNext());
        assertEquals(((Iterator)pageContextIterator).next(), "ccc");
        // 4
        assertTrue(((Iterator)pageContextIterator).hasNext());
        assertEquals(((Iterator)pageContextIterator).next(), "ddd");
        // 5
        assertTrue(((Iterator)pageContextIterator).hasNext());
        assertEquals(((Iterator)pageContextIterator).next(), "eee");

        assertFalse(((Iterator)pageContextIterator).hasNext());

        // Basic sanity check of clearTagStateForTagPoolingServers() behaviour for Struts Tags after doEndTag().
        IteratorGeneratorTag freshTag = new IteratorGeneratorTag();
        freshTag.setPageContext(pageContext);
        assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set.  " +
                "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
                strutsBodyTagsAreReflectionEqual(tag, freshTag));
    }

    public void testGeneratorWithId_clearTagStateSet() throws Exception {
        IteratorGeneratorTag tag = new IteratorGeneratorTag();
        tag.setPerformClearTagStateForTagPoolingServers(true);  // Explicitly request tag state clearing.
        tag.setPageContext(pageContext);
        tag.setVal("%{'aaa,bbb,ccc,ddd,eee'}");
        tag.setVar("myPageContextAttId");
        tag.doStartTag();
        tag.doEndTag();

        Object pageContextIterator = stack.findValue("myPageContextAttId");

        assertTrue(pageContextIterator instanceof Iterator);
        // 1
        assertTrue(((Iterator)pageContextIterator).hasNext());
        assertEquals(((Iterator)pageContextIterator).next(), "aaa");
        // 2.
        assertTrue(((Iterator)pageContextIterator).hasNext());
        assertEquals(((Iterator)pageContextIterator).next(), "bbb");
        // 3.
        assertTrue(((Iterator)pageContextIterator).hasNext());
        assertEquals(((Iterator)pageContextIterator).next(), "ccc");
        // 4
        assertTrue(((Iterator)pageContextIterator).hasNext());
        assertEquals(((Iterator)pageContextIterator).next(), "ddd");
        // 5
        assertTrue(((Iterator)pageContextIterator).hasNext());
        assertEquals(((Iterator)pageContextIterator).next(), "eee");

        assertFalse(((Iterator)pageContextIterator).hasNext());

        // Basic sanity check of clearTagStateForTagPoolingServers() behaviour for Struts Tags after doEndTag().
        IteratorGeneratorTag freshTag = new IteratorGeneratorTag();
        freshTag.setPerformClearTagStateForTagPoolingServers(true);
        freshTag.setPageContext(pageContext);
        assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set.  " +
                "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
                strutsBodyTagsAreReflectionEqual(tag, freshTag));
    }

    public void testGeneratorWithCount() throws Exception {
        IteratorGeneratorTag tag = new IteratorGeneratorTag();

        tag.setPageContext(pageContext);
        tag.setVal("%{'aaa,bbb,ccc,ddd,eee'}");
        tag.setCount("myCount");
        tag.doStartTag();
        Object topOfStack = stack.findValue("top");
        tag.doEndTag();
        Object afterTopOfStack = stack.findValue("top");


        assertTrue(topOfStack instanceof Iterator);
        // 1
        assertTrue(((Iterator)topOfStack).hasNext());
        assertEquals(((Iterator)topOfStack).next(), "aaa");
        // 2
        assertTrue(((Iterator)topOfStack).hasNext());
        assertEquals(((Iterator)topOfStack).next(), "bbb");
        // 3.
        assertTrue(((Iterator)topOfStack).hasNext());
        assertEquals(((Iterator)topOfStack).next(), "ccc");

        assertFalse(((Iterator)topOfStack).hasNext());
        assertNotSame(topOfStack, afterTopOfStack);

        // Basic sanity check of clearTagStateForTagPoolingServers() behaviour for Struts Tags after doEndTag().
        IteratorGeneratorTag freshTag = new IteratorGeneratorTag();
        freshTag.setPageContext(pageContext);
        assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set.  " +
                "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
                strutsBodyTagsAreReflectionEqual(tag, freshTag));
    }

    public void testGeneratorWithCount_clearTagStateSet() throws Exception {
        IteratorGeneratorTag tag = new IteratorGeneratorTag();

        tag.setPerformClearTagStateForTagPoolingServers(true);  // Explicitly request tag state clearing.
        tag.setPageContext(pageContext);
        tag.setVal("%{'aaa,bbb,ccc,ddd,eee'}");
        tag.setCount("myCount");
        tag.doStartTag();
        Object topOfStack = stack.findValue("top");
        tag.doEndTag();
        Object afterTopOfStack = stack.findValue("top");


        assertTrue(topOfStack instanceof Iterator);
        // 1
        assertTrue(((Iterator)topOfStack).hasNext());
        assertEquals(((Iterator)topOfStack).next(), "aaa");
        // 2
        assertTrue(((Iterator)topOfStack).hasNext());
        assertEquals(((Iterator)topOfStack).next(), "bbb");
        // 3.
        assertTrue(((Iterator)topOfStack).hasNext());
        assertEquals(((Iterator)topOfStack).next(), "ccc");

        assertFalse(((Iterator)topOfStack).hasNext());
        assertNotSame(topOfStack, afterTopOfStack);

        // Basic sanity check of clearTagStateForTagPoolingServers() behaviour for Struts Tags after doEndTag().
        IteratorGeneratorTag freshTag = new IteratorGeneratorTag();
        freshTag.setPerformClearTagStateForTagPoolingServers(true);
        freshTag.setPageContext(pageContext);
        assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set.  " +
                "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
                strutsBodyTagsAreReflectionEqual(tag, freshTag));
    }

    @Override
    public Action getAction() {
        return new ActionSupport() {
            public Converter getMyConverter() {
                return value -> "myConverter-"+value;
            }

            public int getMyCount() {
                return 3;
            }
        };
    }
}

