/*
 * 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.jasper.compiler;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import jakarta.servlet.jsp.tagext.FunctionInfo;

import org.apache.jasper.JasperException;

/**
 * This class defines internal representation for an EL Expression
 *
 * It currently only defines functions.  It can be expanded to define
 * all the components of an EL expression, if need to.
 *
 * @author Kin-man Chung
 */

abstract class ELNode {

    public abstract void accept(Visitor v) throws JasperException;


    /**
     * Represents an EL expression: anything in ${ and }.
     */
    public static class Root extends ELNode {

        private final ELNode.Nodes expr;
        private final char type;

        Root(ELNode.Nodes expr, char type) {
            this.expr = expr;
        this.type = type;
        }

        @Override
        public void accept(Visitor v) throws JasperException {
            v.visit(this);
        }

        public ELNode.Nodes getExpression() {
            return expr;
        }

        public char getType() {
            return type;
        }
    }

    /**
     * Represents text outside of EL expression.
     */
    public static class Text extends ELNode {

        private final String text;

        Text(String text) {
            this.text = text;
        }

        @Override
        public void accept(Visitor v) throws JasperException {
            v.visit(this);
        }

        public String getText() {
            return text;
        }
    }

    /**
     * Represents anything in EL expression, other than functions, including
     * function arguments etc
     */
    public static class ELText extends ELNode {

        private final String text;

        ELText(String text) {
            this.text = text;
        }

        @Override
        public void accept(Visitor v) throws JasperException {
            v.visit(this);
        }

        public String getText() {
            return text;
        }
    }

    /**
     * Represents a function
     * Currently only include the prefix and function name, but not its
     * arguments.
     */
    public static class Function extends ELNode {

        private final String prefix;
        private final String name;
        private final String originalText;
        private String uri;
        private FunctionInfo functionInfo;
        private String methodName;
        private String[] parameters;

        Function(String prefix, String name, String originalText) {
            this.prefix = prefix;
            this.name = name;
            this.originalText = originalText;
        }

        @Override
        public void accept(Visitor v) throws JasperException {
            v.visit(this);
        }

        public String getPrefix() {
            return prefix;
        }

        public String getName() {
            return name;
        }

        public String getOriginalText() {
            return originalText;
        }

        public void setUri(String uri) {
            this.uri = uri;
        }

        public String getUri() {
            return uri;
        }

        public void setFunctionInfo(FunctionInfo f) {
            this.functionInfo = f;
        }

        public FunctionInfo getFunctionInfo() {
            return functionInfo;
        }

        public void setMethodName(String methodName) {
            this.methodName = methodName;
        }

        public String getMethodName() {
            return methodName;
        }

        public void setParameters(String[] parameters) {
            this.parameters = parameters;
        }

        public String[] getParameters() {
            return parameters;
        }
    }

    /**
     * An ordered list of ELNode.
     */
    public static class Nodes {

        /* Name used for creating a map for the functions in this
           EL expression, for communication to Generator.
         */
        private String mapName = null;    // The function map associated this EL
        private final List<ELNode> list;

        Nodes() {
            list = new ArrayList<>();
        }

        public void add(ELNode en) {
            list.add(en);
        }

        /**
         * Visit the nodes in the list with the supplied visitor.
         *
         * @param v The visitor used
         *
         * @throws JasperException if an error occurs while visiting a node
         */
        public void visit(Visitor v) throws JasperException {
            for (ELNode n : list) {
                n.accept(v);
            }
        }

        public Iterator<ELNode> iterator() {
            return list.iterator();
        }

        public boolean isEmpty() {
            return list.size() == 0;
        }

        /**
         * @return true if the expression contains a ${...}
         */
        public boolean containsEL() {
            for (ELNode n : list) {
                if (n instanceof Root) {
                    return true;
                }
            }
            return false;
        }

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

        public String getMapName() {
            return mapName;
        }

    }

    /*
     * A visitor class for traversing ELNodes
     */
    public static class Visitor {

        public void visit(Root n) throws JasperException {
            n.getExpression().visit(this);
        }

        @SuppressWarnings("unused")
        public void visit(Function n) throws JasperException {
            // NOOP by default
        }

        @SuppressWarnings("unused")
        public void visit(Text n) throws JasperException {
            // NOOP by default
        }

        @SuppressWarnings("unused")
        public void visit(ELText n) throws JasperException {
            // NOOP by default
        }
    }
}

