/*
 *  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 groovy.util

/**
     Testing BuilderSupport and reveal how calling
    methods on it result in implementation callbacks.
    Using the SpoofBuilder (see below) to call it in various ways
    and analyze the "spoofed" logs.
    This is especially useful when designing subclasses of BuilderSupport.
     @author Dierk Koenig
**/


class BuilderSupportTest extends GroovyTestCase{
    void testSimpleNode() {
        def b = new SpoofBuilder()
        assert b.log == []
        def node = b.foo()
        assert b.log == [  'create_with_name','foo',
                           'node_completed',null, node, 
                           'post_node_completion',null, node
                        ]
    }

    void testSimpleNodeWithValue() {
        def b = new SpoofBuilder()
        def node = b.foo('value')
        assert b.log == [  'create_with_name_and_value','foo',
                           'value', 'node_completed',null,node,
                           'post_node_completion',null, node
                        ]
    }

    void testSimpleNodeWithOneAttribute() {
        def b = new SpoofBuilder()
        def node = b.foo(name:'value')
        assert b.log == [
                           'create_with_name_and_map','foo', 
                           'name','value', 
                           'node_completed',null,'x',
                           'post_node_completion',null, 'x'
                        ]
    }

    void testSimpleNodeWithClosure() {
        def b = new SpoofBuilder()
        b.foo(){
            b.bar()
        }
        assert b.log == [
            'create_with_name','foo',
                'create_with_name','bar',
            'set_parent', 'x', 'x',
                'node_completed','x','x',
                'post_node_completion', 'x', 'x',
            'node_completed',null,'x',
            'post_node_completion',null, 'x'
            ]
    }

    void testSimpleNodeWithOneAttributeAndValue() {
        def b = new SpoofBuilder()
        def node = b.foo(bar:'baz', 'value')
        assert b.log == [
                          'create_with_name_map_and_value', 'foo', 'bar', 'baz','value', 
                          'node_completed',null,node,
                          'post_node_completion',null, node
                        ]
    }

    void testSimpleNodeWithValueAndOneAttribute() {
        def b = new SpoofBuilder()
        def node = b.foo('value', bar:'baz')
        assert b.log == [
                          'create_with_name_map_and_value', 'foo', 'bar', 'baz','value', 
                          'node_completed',null,node,
                          'post_node_completion',null, node
                        ]
    }

    void testSimpleNodeWithOneAttributeAndValueAndClosure() {
        def b = new SpoofBuilder()
        def node = b.foo(bar:'baz', 'value') { 1 }
        assert b.log == [
                          'create_with_name_map_and_value', 'foo', 'bar', 'baz','value', 
                          'node_completed',null,node,
                          'post_node_completion',null, node
                        ]
    }

    void testSimpleNodeWithValueAndOneAttributeAndClosure() {
        def b = new SpoofBuilder()
        def node = b.foo('value', bar:'baz') { 1 }
        assert b.log == [
                          'create_with_name_map_and_value', 'foo', 'bar', 'baz','value', 
                          'node_completed',null,node,
                          'post_node_completion',null, node
                        ]
    }

    void testSimpleNodeTwoValues() {
        def b = new SpoofBuilder()
        shouldFail(MissingMethodException, {def node = b.foo('arg1', 'arg2')})
    }

    void testSimpleNodeTwoValuesClosure() {
        def b = new SpoofBuilder()
        shouldFail(MissingMethodException, {def node = b.foo('arg1', 'arg2') { 1 } })
    }

    void testSimpleNodeThreeValues() {
        def b = new SpoofBuilder()
        shouldFail(MissingMethodException, {def node = b.foo('arg1', 'arg2', 'arg3') })
    }

    void testSimpleNodeFourValues() {
        def b = new SpoofBuilder()
        shouldFail(MissingMethodException, {def node = b.foo('arg1', 'arg2', 'arg3', 'arg4') })
    }

    void testNestedMethodCallsResolution() {
        def b = new SpoofBuilder()
        b.outest {
            b.outer {
                nestedBuilderCall(b)
            }
        }
        assert b.log.contains('inner') 
    }

    void nestedBuilderCall(builder) {
        builder.inner()
    }

    // GROOVY-3341
    void testSimpleNodeWithClosureThatThrowsAMissingMethodException() {
      def builder = new SpoofBuilder()
      String errorMessage = shouldFail(MissingMethodException, {
          builder.a {
              b {
                  error('xy'.foo())
              }
          }
      })
      assert errorMessage.contains('No signature of method: java.lang.String.foo()')
  }
}

/**
    The SpoofBuilder is a sample instance of the abstract BuilderSupport class
    that does nothing but logging how it was called, returning 'x' for each node.
**/
class SpoofBuilder extends BuilderSupport{
    def log = []
    
    protected void setParent(Object parent, Object child){
        log << "set_parent"
        log << parent
        log << child
    }
    protected Object createNode(Object name){
        log << 'create_with_name'
        log <<  name
        return 'x'
    }
    protected Object createNode(Object name, Object value){
        log << 'create_with_name_and_value'
        log << name
        log << value
        return 'x'
    }
    protected Object createNode(Object name, Map attributes){
        log << 'create_with_name_and_map'
        log << name
        attributes.each{entry -> log << entry.key; log << entry.value}
        return 'x'
    }
    protected Object createNode(Object name, Map attributes, Object value){
        log << 'create_with_name_map_and_value'
        log << name
        attributes.each{entry -> log << entry.key; log << entry.value}
        log << value
        return 'x'
    }
    protected void nodeCompleted(Object parent, Object node) {
        log << 'node_completed'
        log << parent
        log << node
    }
    
    protected Object postNodeCompletion(Object parent, Object node) {
        log << 'post_node_completion'
        log << parent
        log << node
        node
    }
}
