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

org.finos.legend.engine.shared.javaCompiler.JavaCompileException Maven / Gradle / Ivy

// Copyright 2020 Goldman Sachs
//
// Licensed 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.finos.legend.engine.shared.javaCompiler;

import org.eclipse.collections.api.RichIterable;
import org.eclipse.collections.api.factory.Lists;
import org.eclipse.collections.api.factory.Sets;
import org.eclipse.collections.api.list.ImmutableList;
import org.eclipse.collections.api.list.ListIterable;
import org.eclipse.collections.api.list.MutableList;
import org.eclipse.collections.impl.utility.ArrayIterate;
import org.eclipse.collections.impl.utility.Iterate;
import org.eclipse.collections.impl.utility.LazyIterate;

import java.util.regex.Pattern;
import javax.tools.Diagnostic;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaFileObject;

public class JavaCompileException extends Exception
{
    private static final int MAX_SOURCES_FOR_MESSAGE = 5;
    private static final int MAX_LINES_FOR_MESSAGE = 250;
    private static final long SOURCE_CONTEXT_LINES = 20;

    private final ImmutableList> diagnostics;
    private final ImmutableList options;

    public JavaCompileException(Iterable> diagnostics, Iterable options)
    {
        super(createMessage(diagnostics));
        this.diagnostics = Lists.immutable.withAll(diagnostics);
        this.options = Lists.immutable.withAll(options);
    }

    public JavaCompileException(DiagnosticCollector diagnosticCollector, Iterable options)
    {
        this(diagnosticCollector.getDiagnostics(), options);
    }

    public JavaCompileException(Iterable> diagnostics)
    {
        this(diagnostics, null);
    }

    public JavaCompileException(DiagnosticCollector diagnosticCollector)
    {
        this(diagnosticCollector, null);
    }

    public RichIterable> getDiagnostics()
    {
        return this.diagnostics;
    }

    public RichIterable> getErrorDiagnostics()
    {
        return LazyIterate.select(this.diagnostics, JavaCompileException::isErrorDiagnostic);
    }

    public RichIterable getCompileOptions()
    {
        return this.options;
    }

    private static String createMessage(Iterable> diagnostics)
    {
        MutableList> errorDiagnostics = Iterate.select(diagnostics, JavaCompileException::isErrorDiagnostic, Lists.mutable.empty());
        return errorDiagnostics.isEmpty() ? "Unknown error compiling generated Java code" : new MessageBuilder(errorDiagnostics).getMessage();
    }

    private static boolean isErrorDiagnostic(Diagnostic diagnostic)
    {
        return (diagnostic != null) && (Diagnostic.Kind.ERROR == diagnostic.getKind());
    }

    private static String getSourceName(Diagnostic diagnostic)
    {
        JavaFileObject source = diagnostic.getSource();
        return (source == null) ? null : source.getName();
    }

    private static class MessageBuilder
    {
        private final ListIterable> diagnostics;
        private final Pattern lineSplitter = Pattern.compile("\\R");
        private final MutableList lines = Lists.mutable.empty();

        private MessageBuilder(MutableList> diagnostics)
        {
            this.diagnostics = diagnostics;
        }

        String getMessage()
        {
            addLine(createSummaryMessage());
            this.diagnostics.forEach(diagnostic -> printLines(diagnostic.toString()));
            this.diagnostics.groupBy(JavaCompileException::getSourceName).forEachKeyMultiValues(this::printDiagnosticSources);
            return this.lines.makeString("\n");
        }

        private void addLine(String line)
        {
            if (this.lines.size() < MAX_LINES_FOR_MESSAGE)
            {
                this.lines.add(line);
            }
            else if (this.lines.size() == MAX_LINES_FOR_MESSAGE)
            {
                this.lines.add("... (max message size exceeded)");
            }
        }

        private boolean maxMessageSizeReached()
        {
            return this.lines.size() >= MAX_LINES_FOR_MESSAGE;
        }

        private void printLines(String text)
        {
            String[] lines = this.lineSplitter.split(text);
            ArrayIterate.forEach(lines, this::addLine);
        }

        private String createSummaryMessage()
        {
            StringBuilder builder = new StringBuilder();
            builder.append(this.diagnostics.size()).append((this.diagnostics.size() == 1) ? " error compiling " : " errors compiling ");
            MutableList sourceNames = this.diagnostics.collect(JavaCompileException::getSourceName, Sets.mutable.empty()).without(null).toSortedList();
            if (sourceNames.size() <= MAX_SOURCES_FOR_MESSAGE)
            {
                sourceNames.appendString(builder, ", ");
            }
            else
            {
                sourceNames.subList(0, MAX_SOURCES_FOR_MESSAGE).appendString(builder, "", ", ", ", ... (and ");
                builder.append(sourceNames.size() - MAX_SOURCES_FOR_MESSAGE).append(" more)");
            }
            return builder.toString();
        }

        private void printDiagnosticSources(String sourceName, Iterable> diagnostics)
        {
            if (maxMessageSizeReached())
            {
                return;
            }

            Diagnostic firstDiagnostic = Iterate.getFirst(diagnostics);
            if (firstDiagnostic == null)
            {
                return;
            }

            JavaFileObject source = firstDiagnostic.getSource();
            String[] sourceLines;
            if (source instanceof StringJavaSource)
            {
                sourceLines = this.lineSplitter.split(((StringJavaSource) source).getCode());
                addLine("");
                addLine(sourceName);
            }
            else
            {
                sourceLines = new String[0];
            }

            long sourceLinesHighwater = 0;
            for (Diagnostic diagnostic : diagnostics)
            {
                if (maxMessageSizeReached())
                {
                    return;
                }

                long lineNo = Math.max(diagnostic.getLineNumber() - SOURCE_CONTEXT_LINES, sourceLinesHighwater + 1);
                long end = Math.min(diagnostic.getLineNumber() + SOURCE_CONTEXT_LINES, sourceLines.length);
                if (lineNo - sourceLinesHighwater > 2)
                {
                    addLine(String.format("%04d:%04d ...", sourceLinesHighwater + 1, lineNo - 1));
                    sourceLinesHighwater = lineNo - 1;
                }
                else
                {
                    lineNo = sourceLinesHighwater + 1;
                }

                while (lineNo < end)
                {
                    if (sourceLinesHighwater < lineNo)
                    {
                        addLine(String.format("%04d %s", lineNo, sourceLines[(int) (lineNo - 1)]));
                        sourceLinesHighwater = lineNo;
                    }
                    lineNo++;
                }
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy