All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.icthh.xm.commons.lep.groovy.GroovyFileParser Maven / Gradle / Ivy

There is a newer version: 4.0.20
Show newest version
package com.icthh.xm.commons.lep.groovy;

import groovyjarjarantlr.RecognitionException;
import groovyjarjarantlr.TokenStreamException;
import groovyjarjarantlr.collections.AST;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.codehaus.groovy.antlr.AntlrASTProcessor;
import org.codehaus.groovy.antlr.GroovySourceAST;
import org.codehaus.groovy.antlr.SourceBuffer;
import org.codehaus.groovy.antlr.UnicodeEscapingReader;
import org.codehaus.groovy.antlr.parser.GroovyLexer;
import org.codehaus.groovy.antlr.parser.GroovyRecognizer;
import org.codehaus.groovy.antlr.treewalker.SourceCodeTraversal;
import org.codehaus.groovy.antlr.treewalker.Visitor;
import org.codehaus.groovy.antlr.treewalker.VisitorAdapter;

import java.io.StringReader;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Optional;
import java.util.Set;

import static org.codehaus.groovy.antlr.parser.GroovyTokenTypes.IDENT;
import static org.codehaus.groovy.antlr.parser.GroovyTokenTypes.LITERAL_static;
import static org.codehaus.groovy.antlr.parser.GroovyTokenTypes.MODIFIERS;

@Slf4j
public class GroovyFileParser {

    @SneakyThrows
    public GroovyFileMetadata getFileMetaData(String source) {
        try {
            return getGroovyFileMetadata(source);
        } catch (Throwable e) {
            log.error("Error parse groovy: {}", e.getMessage(), e);
        }
        return new GroovyFileMetadata();
    }

    public GroovyFileMetadata getGroovyFileMetadata(String source) throws TokenStreamException, RecognitionException {
        GroovyFileMetadata metadata = new GroovyFileMetadata();
        parseGroovy(source, new GroovyParseVisitor(metadata));
        return metadata;
    }

    private void parseGroovy(String source, Visitor visitor) throws TokenStreamException, RecognitionException {
        SourceBuffer sourceBuffer = new SourceBuffer();
        GroovyRecognizer parser = getGroovyParser(source, sourceBuffer);
        parser.compilationUnit();
        AST ast = parser.getAST();
        AntlrASTProcessor traverser = new SourceCodeTraversal(visitor);
        traverser.process(ast);
    }

    private static GroovyRecognizer getGroovyParser(String input, SourceBuffer sourceBuffer) {
        UnicodeEscapingReader unicodeReader = new UnicodeEscapingReader(new StringReader(input), sourceBuffer);
        GroovyLexer lexer = new GroovyLexer(unicodeReader);
        unicodeReader.setLexer(lexer);
        GroovyRecognizer parser = GroovyRecognizer.make(lexer);
        parser.setSourceBuffer(sourceBuffer);
        return parser;
    }

    @ToString
    @Getter
    @RequiredArgsConstructor
    public static class GroovyFileMetadata {

        private boolean isScript = false;
        private final Set classes = new HashSet<>();
        private final Set staticFields = new HashSet<>();
        private final Set staticMethods = new HashSet<>();

        public boolean canImport(String importValue) {
            return classes.contains(importValue)
                || staticFields.contains(importValue)
                || staticMethods.contains(importValue);
        }
    }

    private static class GroovyParseVisitor extends VisitorAdapter {
        private final LinkedList classNames;
        private final LinkedList levels;
        private final GroovyFileMetadata metadata;
        private int level;

        private boolean isInAnnotation;
        private boolean isInAnnotationArray;

        public GroovyParseVisitor(GroovyFileMetadata metadata) {
            this.metadata = metadata;
            classNames = new LinkedList<>();
            levels = new LinkedList<>();
            level = 0;
            isInAnnotation = false;
            isInAnnotationArray = false;
        }

        @Override
        public void visitVariableDef(GroovySourceAST t, int visit) {
            if (isStatic(t)) {
                metadata.staticFields.add(StringUtils.join(classNames, "$") + "." + getIdent(t));
            }
            super.visitVariableDef(t, visit); // to detect outside code
        }

        @Override
        public void visitMethodDef(GroovySourceAST t, int visit) {
            if (isStatic(t)) {
                metadata.staticMethods.add(StringUtils.join(classNames, "$") + "." + getIdent(t));
            }
            super.visitMethodDef(t, visit); // to detect outside code
        }

        @Override
        public void visitEnumConstantDef(GroovySourceAST t, int visit) {
            metadata.staticFields.add(StringUtils.join(classNames, "$") + "." + getIdent(t));
        }

        @Override
        public void visitClassDef(GroovySourceAST t, int visit) {
            if (visit != OPENING_VISIT) {
                return;
            }

            String currentClassName = getIdent(t);
            classNames.addLast(currentClassName);
            levels.addLast(level);
            level = 0;
            metadata.classes.add(StringUtils.join(classNames, "$"));
        }

        @Override
        public void visitObjblock(GroovySourceAST t, int visit) {
            if (visit == OPENING_VISIT) {
                level++;
            } else if (visit == CLOSING_VISIT) {
                level--;
                if (level == 0) {
                    if (!classNames.isEmpty()) {
                        classNames.removeLast();
                    }
                    if (!levels.isEmpty()) {
                        levels.removeLast();
                    }
                    if (!levels.isEmpty()) {
                        level = levels.getLast();
                    }
                }
            }
        }

        @Override
        public void visitInterfaceDef(GroovySourceAST t, int visit) {
            visitClassDef(t, visit);
        }

        @Override
        public void visitEnumDef(GroovySourceAST t, int visit) {
            visitClassDef(t, visit);
        }

        @Override
        public void visitAnnotationDef(GroovySourceAST t, int visit) {
            visitClassDef(t, visit);
        }

        @Override
        public void visitPackageDef(GroovySourceAST t, int visit) {
            // it's not are outside code
        }

        @Override
        public void visitStaticImport(GroovySourceAST t, int visit) {
            // it's not are outside code
        }

        @Override
        public void visitImport(GroovySourceAST t, int visit) {
            // it's not are outside code
        }

        @Override
        public void visitMlComment(GroovySourceAST t, int visit) {
            // it's not are outside code
        }

        @Override
        public void visitShComment(GroovySourceAST t, int visit) {
            // it's not are outside code
        }

        @Override
        public void visitSlComment(GroovySourceAST t, int visit) {
            // it's not are outside code
        }

        @Override
        public void visitAnnotation(GroovySourceAST t, int visit) {
            // it's not are outside code
            if (visit == OPENING_VISIT) {
                isInAnnotation = true;
            } else if (visit == CLOSING_VISIT) {
                isInAnnotation = false;
            }
        }

        @Override
        public void visitAnnotations(GroovySourceAST t, int visit) {
            // it's not are outside code
            if (visit == OPENING_VISIT) {
                isInAnnotationArray = true;
            } else if (visit == CLOSING_VISIT) {
                isInAnnotationArray = false;
            }
        }

        @Override
        public void visitIdent(GroovySourceAST t, int visit) {
            // it's not are outside code
        }

        @Override
        public void visitDot(GroovySourceAST t, int visit) {
            // it's not are outside code
        }

        @Override
        public void visitModifiers(GroovySourceAST t, int visit) {
            // it's not are outside code
        }

        @Override
        public void visitExtendsClause(GroovySourceAST t, int visit) {
            // it's not are outside code
        }

        @Override
        public void visitImplementsClause(GroovySourceAST t, int visit) {
            // it's not are outside code
        }

        @Override
        public void visitAnnotationMemberValuePair(GroovySourceAST t, int visit) {
            // it's not are outside code
        }

        @Override
        public void visitAnnotationArrayInit(GroovySourceAST t, int visit) {
            // it's not are outside code
        }

        @Override
        public void visitDefault(GroovySourceAST t, int visit) {
            if (level == 0 && !isInAnnotation && !isInAnnotationArray) {
                super.visitDefault(t, visit);
                metadata.isScript = true;
            }
        }

        private boolean isStatic(GroovySourceAST t) {
            return Optional.ofNullable(t.childOfType(MODIFIERS))
                .map(it -> it.childOfType(LITERAL_static)).isPresent();
        }

        private String getIdent(GroovySourceAST t) {
            return t.childOfType(IDENT).getText();
        }

    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy