001/*
002 * JDrupes Builder
003 * Copyright (C) 2025 Michael N. Lipp
004 * 
005 * This program is free software: you can redistribute it and/or modify
006 * it under the terms of the GNU Affero General Public License as
007 * published by the Free Software Foundation, either version 3 of the
008 * License, or (at your option) any later version.
009 *
010 * This program is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
013 * GNU Affero General Public License for more details.
014 *
015 * You should have received a copy of the GNU Affero General Public License
016 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
017 */
018
019package org.jdrupes.builder.java;
020
021import com.google.common.flogger.FluentLogger;
022import java.nio.file.Path;
023import java.util.ArrayList;
024import java.util.Arrays;
025import java.util.List;
026import java.util.Locale;
027import java.util.Optional;
028import java.util.stream.Stream;
029import javax.tools.Diagnostic;
030import javax.tools.DiagnosticCollector;
031import javax.tools.JavaFileObject;
032import org.jdrupes.builder.api.Project;
033import org.jdrupes.builder.core.AbstractGenerator;
034
035/// A base class for generators that invoke java tools.
036///
037public abstract class JavaTool extends AbstractGenerator {
038
039    private static final FluentLogger logger = FluentLogger.forEnclosingClass();
040    private final List<String> options = new ArrayList<>();
041
042    /// Instantiates a new java tool.
043    ///
044    /// @param project the project
045    ///
046    public JavaTool(Project project) {
047        super(project);
048    }
049
050    /// Adds the given options.
051    ///
052    /// @param options the options
053    /// @return the javadoc
054    ///
055    public JavaTool options(Stream<String> options) {
056        this.options.addAll(options.toList());
057        return this;
058    }
059
060    /// Adds the given options.
061    ///
062    /// @param options the options
063    /// @return the javadoc
064    ///
065    public JavaTool options(String... options) {
066        this.options.addAll(Arrays.asList(options));
067        return this;
068    }
069
070    /// Return the options.
071    ///
072    /// @return the stream
073    ///
074    public List<String> options() {
075        return options;
076    }
077
078    /// Find the argument for the given option. As some options are
079    /// allows in different styles, several names can be specified. 
080    ///
081    /// @param names the names
082    /// @return the optional
083    ///
084    public Optional<String> optionArgument(String... names) {
085        var itr = options.iterator();
086        if (itr.hasNext()) {
087            String opt = itr.next();
088            if (Arrays.stream(names).anyMatch(opt::equals) && itr.hasNext()) {
089                return Optional.of(itr.next());
090            }
091        }
092        return Optional.empty();
093    }
094
095    /// Log diagnostic.
096    ///
097    /// @param diagnostic the diagnostic
098    ///
099    protected void logDiagnostic(
100            Diagnostic<? extends JavaFileObject> diagnostic) {
101        String level = switch (diagnostic.getKind()) {
102        case ERROR -> "error";
103        case WARNING -> "warning";
104        case MANDATORY_WARNING -> "warning";
105        default -> "info";
106        };
107        String msg;
108        if (diagnostic.getSource() == null) {
109            msg = level + ": " + diagnostic.getMessage(Locale.ENGLISH);
110        } else {
111            msg = String.format("%s:%d:%d: %s: %s",
112                project().rootProject().directory().relativize(
113                    Path.of(diagnostic.getSource().toUri().getPath())),
114                diagnostic.getLineNumber(), diagnostic.getColumnNumber(),
115                level, diagnostic.getMessage(null));
116        }
117        switch (diagnostic.getKind()) {
118        case ERROR -> logger.atSevere().log(msg);
119        case WARNING -> logger.atWarning().log(msg);
120        case MANDATORY_WARNING -> logger.atWarning().log(msg);
121        default -> logger.atInfo().log(msg);
122        }
123        if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
124            context().error().println(msg);
125        } else {
126            context().out().println(msg);
127        }
128    }
129
130    /// Log diagnostics.
131    ///
132    /// @param diagnostics the diagnostics
133    ///
134    protected void
135            logDiagnostics(DiagnosticCollector<JavaFileObject> diagnostics) {
136        if (diagnostics.getDiagnostics().isEmpty()) {
137            return;
138        }
139        context().out().println("Problems found by " + this + ":");
140        for (var diagnostic : diagnostics.getDiagnostics()) {
141            logDiagnostic(diagnostic);
142        }
143    }
144}