/*
 * * Copyright (C) 2015 Matt Baxter http://kitteh.org
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use, copy,
 * modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package org.kitteh.maven.plugin;

import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.reflections.Reflections;
import org.reflections.scanners.ResourcesScanner;
import org.reflections.scanners.SubTypesScanner;
import org.reflections.util.ClasspathHelper;
import org.reflections.util.ConfigurationBuilder;
import org.reflections.util.FilterBuilder;

import java.io.File;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

@Mojo(name = "tostring", defaultPhase = LifecyclePhase.TEST, requiresDependencyResolution = ResolutionScope.COMPILE)
public class ToString extends AbstractMojo {
    @Parameter(property = "project.compileClasspathElements", required = true, readonly = true)
    private List<String> classpath;

    @Parameter(required = true)
    private String packageName;

    @Parameter(defaultValue = "false")
    private boolean toStringRequired;

    @Override
    public void execute() throws MojoExecutionException, MojoFailureException {
        try {
            Set<URL> urlSet = new HashSet<>();
            for (String classPath : this.classpath) {
                urlSet.add(new File(classPath).toURI().toURL());
            }
            ClassLoader contextClassLoader = URLClassLoader.newInstance(urlSet.toArray(new URL[urlSet.size()]), Thread.currentThread().getContextClassLoader());
            Thread.currentThread().setContextClassLoader(contextClassLoader);
        } catch (MalformedURLException e) {
            throw new MojoExecutionException("Classpath failure! Malformed URL.", e);
        }

        ConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
        configurationBuilder.setScanners(new SubTypesScanner(false), new ResourcesScanner());
        configurationBuilder.setUrls(ClasspathHelper.forClassLoader(new ClassLoader[]{ClasspathHelper.contextClassLoader(), ClasspathHelper.staticClassLoader()}));
        configurationBuilder.filterInputsBy(new FilterBuilder().include(FilterBuilder.prefix(this.packageName)));
        Reflections reflections = new Reflections(configurationBuilder);

        Set<Class<?>> allClasses = reflections.getSubTypesOf(Object.class);

        List<String> problematic = new LinkedList<>();
        for (Class<?> clazz : allClasses) {
            if ((clazz.getModifiers() & Modifier.INTERFACE) != 0) {
                continue;
            }
            try {
                Method toString = clazz.getMethod("toString");
                if (toString.getDeclaringClass().equals(Object.class)) {
                    problematic.add(clazz.getName());
                } else {
                    this.getLog().info("Good work " + clazz.getSimpleName());
                }
            } catch (NoSuchMethodException e) {
                throw new MojoExecutionException("Could not find a toString at all on " + clazz.getName());
            }
        }

        if (!problematic.isEmpty()) {
            Collections.sort(problematic);
            this.getLog().warn("Found " + problematic.size() + " classes with Object's toString()");
            for (String name : problematic) {
                this.getLog().info(name);
            }
            if (this.toStringRequired) {
                throw new MojoFailureException("All classes must have a toString not from Object");
            }
        }
    }
}
@@ -1843,16 +1843,48 @@
 o.File;%0A
+import java.lang.reflect.Field;%0A
 import j
@@ -2604,16 +2604,101 @@
 uired;%0A%0A
+    @Parameter(defaultValue = %22false%22)%0A    private boolean toStringIgnoreUtilities;%0A%0A
     @Ove
@@ -4345,121 +4345,161 @@
     
-problematic.add(clazz.getName());%0A                %7D else %7B%0A                    this.getLog().info(%22Good work %22 + 
+if (this.toStringIgnoreUtilities && isUtilityClass(clazz)) %7B%0A                        continue;%0A                    %7D%0A                    problematic.add(
 claz
@@ -4507,14 +4507,8 @@
 .get
-Simple
 Name
@@ -5165,10 +5165,604 @@
  %7D%0A    %7D
+%0A%0A    private boolean isUtilityClass(Class%3C?%3E clazz) %7B%0A        if (!(clazz.getSuperclass() == null %7C%7C clazz.getSuperclass() == Object.class)) %7B%0A            return false;%0A        %7D%0A        for (Method method : clazz.getDeclaredMethods()) %7B%0A            if (!Modifier.isStatic(method.getModifiers())) %7B%0A                return false;%0A            %7D%0A        %7D%0A        for (Field field : clazz.getDeclaredFields()) %7B%0A            if (!(Modifier.isStatic(field.getModifiers()) && Modifier.isFinal(field.getModifiers()))) %7B%0A                return false;%0A            %7D%0A        %7D%0A        return true;%0A    %7D
 %0A%7D

Allow ignoring static utility classes