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

import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.apache.groovy.parser.antlr4.GroovyParser;
import org.apache.groovy.parser.antlr4.Pair;
import org.codehaus.groovy.ast.ASTNode;

import static org.codehaus.groovy.runtime.DefaultGroovyMethods.asBoolean;

/**
 * Utilities for configuring node positions
 */
public class PositionConfigureUtils {
    /**
     * Sets location(lineNumber, colNumber, lastLineNumber, lastColumnNumber) for node using standard context information.
     * Note: this method is implemented to be closed over ASTNode. It returns same node as it received in arguments.
     *
     * @param astNode Node to be modified.
     * @param ctx     Context from which information is obtained.
     * @return Modified astNode.
     */
    public static <T extends ASTNode> T configureAST(T astNode, GroovyParser.GroovyParserRuleContext ctx) {
        Token start = ctx.getStart();
        Token stop = ctx.getStop();

        astNode.setLineNumber(start.getLine());
        astNode.setColumnNumber(start.getCharPositionInLine() + 1);

        configureEndPosition(astNode, stop);

        return astNode;
    }

    public static Pair<Integer, Integer> endPosition(Token token) {
        String stopText = token.getText();
        int stopTextLength = 0;
        int newLineCnt = 0;
        if (null != stopText) {
            stopTextLength = stopText.length();
            newLineCnt = (int) StringUtils.countChar(stopText, '\n');
        }

        if (0 == newLineCnt) {
            return new Pair<>(token.getLine(), token.getCharPositionInLine() + 1 + token.getText().length());
        } else { // e.g. GStringEnd contains newlines, we should fix the location info
            return new Pair<>(token.getLine() + newLineCnt, stopTextLength - stopText.lastIndexOf('\n'));
        }
    }

    public static <T extends ASTNode> T configureAST(T astNode, TerminalNode terminalNode) {
        return configureAST(astNode, terminalNode.getSymbol());
    }

    public static <T extends ASTNode> T configureAST(T astNode, Token token) {
        astNode.setLineNumber(token.getLine());
        astNode.setColumnNumber(token.getCharPositionInLine() + 1);
        astNode.setLastLineNumber(token.getLine());
        astNode.setLastColumnNumber(token.getCharPositionInLine() + 1 + token.getText().length());

        return astNode;
    }

    public static <T extends ASTNode> T configureAST(T astNode, ASTNode source) {
        astNode.setLineNumber(source.getLineNumber());
        astNode.setColumnNumber(source.getColumnNumber());
        astNode.setLastLineNumber(source.getLastLineNumber());
        astNode.setLastColumnNumber(source.getLastColumnNumber());

        return astNode;
    }

    public static <T extends ASTNode> T configureAST(T astNode, GroovyParser.GroovyParserRuleContext ctx, ASTNode stop) {
        Token start = ctx.getStart();

        astNode.setLineNumber(start.getLine());
        astNode.setColumnNumber(start.getCharPositionInLine() + 1);

        if (asBoolean(stop)) {
            astNode.setLastLineNumber(stop.getLastLineNumber());
            astNode.setLastColumnNumber(stop.getLastColumnNumber());
        } else {
            configureEndPosition(astNode, start);
        }

        return astNode;
    }

    public static <T extends ASTNode> void configureEndPosition(T astNode, Token token) {
        Pair<Integer, Integer> endPosition = endPosition(token);
        astNode.setLastLineNumber(endPosition.getKey());
        astNode.setLastColumnNumber(endPosition.getValue());
    }

    public static <T extends ASTNode> T configureAST(T astNode, ASTNode start, ASTNode stop) {
        astNode.setLineNumber(start.getLineNumber());
        astNode.setColumnNumber(start.getColumnNumber());

        if (asBoolean(stop)) {
            astNode.setLastLineNumber(stop.getLastLineNumber());
            astNode.setLastColumnNumber(stop.getLastColumnNumber());
        } else {
            astNode.setLastLineNumber(start.getLastLineNumber());
            astNode.setLastColumnNumber(start.getLastColumnNumber());
        }

        return astNode;
    }
}

@@ -862,16 +862,43 @@
 .util;%0A%0A
+import groovy.lang.Tuple2;%0A
 import o
@@ -1028,53 +1028,8 @@
 er;%0A
-import org.apache.groovy.parser.antlr4.Pair;%0A
 impo
@@ -2030,20 +2030,22 @@
  static 
-Pair
+Tuple2
 %3CInteger
@@ -2392,36 +2392,38 @@
      return new 
-Pair
+Tuple2
 %3C%3E(token.getLine
@@ -2599,20 +2599,22 @@
 urn new 
-Pair
+Tuple2
 %3C%3E(token
@@ -4255,12 +4255,14 @@
     
-Pair
+Tuple2
 %3CInt
@@ -4363,11 +4363,13 @@
 .get
-Key
+First
 ());
@@ -4424,13 +4424,14 @@
 .get
-Value
+Second
 ());

Replace Pair with Tuple2 further