001/*
002MIT License
003
004Copyright (c) 2020 FBSQL Team
005
006Permission is hereby granted, free of charge, to any person obtaining a copy
007of this software and associated documentation files (the "Software"), to deal
008in the Software without restriction, including without limitation the rights
009to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
010copies of the Software, and to permit persons to whom the Software is
011furnished to do so, subject to the following conditions:
012
013The above copyright notice and this permission notice shall be included in all
014copies or substantial portions of the Software.
015
016THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
017IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
018FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
019AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
020LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
021OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
022SOFTWARE.
023
024Home:   https://fbsql.github.io
025E-Mail: fbsql.team@gmail.com
026*/
027
028package org.fbsql.antlr4.parser;
029
030import java.util.ArrayList;
031import java.util.Collection;
032import java.util.List;
033import java.util.Locale;
034
035import org.antlr.v4.runtime.CharStreams;
036import org.antlr.v4.runtime.CommonTokenStream;
037import org.antlr.v4.runtime.Lexer;
038import org.antlr.v4.runtime.tree.ParseTree;
039import org.antlr.v4.runtime.tree.ParseTreeWalker;
040import org.fbsql.antlr4.generated.FbsqlBaseListener;
041import org.fbsql.antlr4.generated.FbsqlLexer;
042import org.fbsql.antlr4.generated.FbsqlParser;
043import org.fbsql.antlr4.generated.FbsqlParser.ProcedureContext;
044
045public class ParseNativeStmt {
046
047        /**
048         * Procedure transfer object
049         */
050        public class Procedure {
051                public String       name;
052                public boolean      hasNestedProcedures;
053                public int          startIndex;
054                public int          stopIndex;
055                public List<String> parameters = new ArrayList<>();
056
057                @Override
058                public String toString() {
059                        return "Procedure [name=" + name + ", hasNestedProcedures=" + hasNestedProcedures + ", startIndex=" + startIndex + ", stopIndex=" + stopIndex + ", parameters=" + parameters + "]";
060                }
061
062        }
063
064        private Collection<String /* stored procedure name */> nonNativeProceduresNames;
065        private Procedure                                      procedure;
066
067        public ParseNativeStmt(Collection<String /* stored procedure name */> nonNativeProceduresNames) {
068                this.nonNativeProceduresNames = nonNativeProceduresNames;
069        }
070
071        /**
072         * Native statement parser
073         *
074         * @param sql
075         * @return
076         */
077        public Procedure parse(String sql) {
078                Lexer       lexer  = new FbsqlLexer(CharStreams.fromString(sql));
079                FbsqlParser parser = new FbsqlParser(new CommonTokenStream(lexer));
080                ParseTree   tree   = parser.native_stmt();
081
082                List<Procedure> procedures = new ArrayList<>();
083                ParseTreeWalker.DEFAULT.walk(new FbsqlBaseListener() {
084
085                        @Override
086                        public void enterProcedure(ProcedureContext ctx) {
087                                String procedureName = ctx.children.get(0).getText().toUpperCase(Locale.ENGLISH);
088                                if (!nonNativeProceduresNames.contains(procedureName))
089                                        return;
090                                procedure = new Procedure();
091                                procedures.add(procedure);
092                                procedure.name = procedureName;
093                                for (int i = 1; i < ctx.children.size(); i++) {
094                                        String s = ctx.children.get(i).getText();
095                                        char   c = s.charAt(0);
096                                        if (c == ',' || c == '(' || c == ')')
097                                                continue;
098                                        if (s.endsWith(")"))
099                                                procedure.hasNestedProcedures = true;
100                                        procedure.parameters.add(s);
101                                }
102                                procedure.startIndex = ctx.start.getStartIndex();
103                                procedure.stopIndex  = ctx.stop.getStopIndex();
104                        }
105                }, tree);
106                return getFirstUnNestedProcedure(procedures);
107        }
108
109        /**
110         * 
111         * @return first un-nested procedure
112         */
113        private static Procedure getFirstUnNestedProcedure(List<Procedure> procedures) {
114                for (Procedure procedure : procedures) {
115                        if (procedure.hasNestedProcedures)
116                                continue;
117                        return procedure;
118                }
119                return null;
120        }
121}
122
123/*
124Please contact FBSQL Team by E-Mail fbsql.team@gmail.com
125or visit https://fbsql.github.io if you need additional
126information or have any questions.
127*/
128
129/* EOF */