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

com.technophobia.substeps.glossary.SubstepsGlossaryMojo Maven / Gradle / Ivy

The newest version!
/*
 *  Copyright Technophobia Ltd 2012
 *
 *   This file is part of Substeps.
 *
 *    Substeps is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU Lesser General Public License as published by
 *    the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    Substeps is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public License
 *    along with Substeps.  If not, see .
 */
package com.technophobia.substeps.glossary;

import com.google.common.io.Files;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import com.technophobia.substeps.model.SubSteps;
import com.technophobia.substeps.runner.BaseSubstepsMojo;
import com.technophobia.substeps.runner.ExecutionConfig;
import com.typesafe.config.Config;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.DependencyResolutionRequiredException;
import org.apache.maven.plugin.descriptor.PluginDescriptor;
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.codehaus.plexus.classworlds.realm.ClassRealm;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.substeps.config.SubstepsConfigLoader;
import org.substeps.glossary.DocletWrapper;
import org.substeps.runner.NewSubstepsExecutionConfig;

import java.io.*;
import java.net.MalformedURLException;
import java.net.URI;
import java.nio.charset.Charset;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;



/**
 * A Maven plugin to generate a json representation of the step implementations in this library.  That json is then used in verious plugins and IDE's etc.
 */
@Mojo(name = "generate-docs",
        defaultPhase = LifecyclePhase.GENERATE_RESOURCES,
        requiresDependencyResolution = ResolutionScope.TEST,
        requiresProject = true,
        configurator = "include-project-dependencies")
public class SubstepsGlossaryMojo extends BaseSubstepsMojo {

    private final Logger log = LoggerFactory.getLogger(SubstepsGlossaryMojo.class);



    /**
     * @parameter
     */
    @Parameter
    private GlossaryPublisher glossaryPublisher = null;

    private List runJavaDoclet(final String classToDocument) {

        final List classStepTagList = new ArrayList();

        project.getBasedir();

        String sourceRoot = null;
        // what's the path to this class ?
        String path = resolveClassToPath(classToDocument, project.getBuild().getSourceDirectory());
        if (path != null) {
            // file is in the source path
            sourceRoot = project.getBuild().getSourceDirectory();
        } else {
            path = resolveClassToPath(classToDocument, project.getBuild().getTestSourceDirectory());
            if (path != null) {
                // file is in the test tree
                sourceRoot = project.getBuild().getTestSourceDirectory();
            }
        }

        if (sourceRoot == null || path == null) {

            log.error("unabled to locate source file");
            // TODO exception ?
            return Collections.emptyList();
        } else {

            final String[] args = {"-doclet", //"com.technophobia.substeps.glossary.CustomDoclet",
                    "-sourcepath", sourceRoot
                    //,path
            };

            try {
                List  testClasspathElements = this.project.getTestClasspathElements();
                return DocletWrapper.invoke(sourceRoot, path, testClasspathElements);

            } catch (DependencyResolutionRequiredException e) {
                getLog().error("DependencyResolutionRequiredException", e);
                return null;
            }
        }
    }


    /**
     * @param classToDocument = fqn of a class, dotted syntax
     * @param dir             the directory of the source
     * @return
     */
    private String resolveClassToPath(final String classToDocument, final String dir) {
        String fullpath = null;
        log.debug("resolving class to path: " + classToDocument + " in " + dir);

        final String filepath = dir + File.separator
                + classToDocument.replace('.', File.separator.charAt(0)) + ".java";

        log.debug("looking for file: " + filepath);

        final File f = new File(filepath);

        if (f.exists()) {
            fullpath = f.getAbsolutePath();
        }
        return fullpath;
    }


    @Override
    public void executeConfig(Config cfg)  {
        // no op
    }

    @Override
    public void executeBeforeAllConfigs(Config masterConfig) {
        // no op
    }

    @Override
    public void executeAfterAllConfigs(Config masterConfig) {

        setupBuildEnvironmentInfo();

        final HashSet loadedClasses = new HashSet();

        final List classStepTags = new ArrayList();

        final PluginDescriptor pluginDescriptor = (PluginDescriptor) getPluginContext().get("pluginDescriptor");
        final ClassRealm classRealm = pluginDescriptor.getClassRealm();
        final File classes = new File(project.getBuild().getOutputDirectory());
        try
        {

            classRealm.addURL(classes.toURI().toURL());
            classRealm.addURL(new File(project.getBuild().getTestOutputDirectory()).toURI().toURL());
        }
        catch (MalformedURLException e)
        {
            log.error("MalformedURLException adding outputdirs", e);
        }

        if (masterConfig != null){
            List configs = SubstepsConfigLoader.splitMasterConfig(masterConfig);

            Set stepImplementationClassNames = new LinkedHashSet<>();
            Set stepImplsToExclude = new LinkedHashSet<>();

            for (Config executionConfig : configs) {

                stepImplementationClassNames.addAll(NewSubstepsExecutionConfig.getStepImplementationClassNames(executionConfig));

                List excluded = NewSubstepsExecutionConfig.getStepImplementationClassNamesGlossaryExcluded(executionConfig);

                if (excluded != null) {
                    stepImplsToExclude.addAll(excluded);
                }
            }

            if (stepImplsToExclude != null ) {
                stepImplementationClassNames.removeAll(stepImplsToExclude);
            }

            for (final String classToDocument : stepImplementationClassNames) {

                classStepTags.addAll(getStepTags(loadedClasses, classRealm, classToDocument));
            }
        }
        else {

            for (ExecutionConfig cfg : executionConfigs) {

                for (final String classToDocument : cfg.getStepImplementationClassNames()) {
                    classStepTags.addAll(getStepTags(loadedClasses, classRealm, classToDocument));
                }
            }
        }


        if (!classStepTags.isEmpty()) {

            if (glossaryPublisher != null) {

                glossaryPublisher.publish(classStepTags);

            }

            saveJsonFile(classStepTags);
        } else {
            log.error("no results to write out");
        }

    }


    private List getStepTags(HashSet loadedClasses, ClassRealm classRealm, String classToDocument) {

        final List classStepTags = new ArrayList();


        log.debug("documenting: " + classToDocument);

        // have we loaded info for this class already ?
        if (!loadedClasses.contains(classToDocument)) {

            // where is this class ?
            final JarFile jarFileForClass = getJarFileForClass(classToDocument);

            try {
                if (jarFileForClass != null) {

                    log.debug("loading info from jar");

                    // look for the xml file in the jar, load up from
                    // there
                    loadStepTagsFromJar(jarFileForClass, classStepTags, loadedClasses);
                } else {
                    log.debug("loading step info from paths");
                    // if it's in the project, run the javadoc and collect the
                    // details

                    // TODO - if this class is annotated with AdditionalStepImplementations, lookup those instead..

                    try {
                        Class stepImplClass = classRealm.loadClass(classToDocument);

                        SubSteps.AdditionalStepImplementations additionalStepImpls = stepImplClass.getDeclaredAnnotation(SubSteps.AdditionalStepImplementations.class);

                        if (additionalStepImpls != null) {
                            for (Class c : additionalStepImpls.value()) {

                                classStepTags.addAll(runJavaDoclet(c.getCanonicalName()));
                            }
                        }

                    } catch (ClassNotFoundException e) {
                        log.error("failed to load class: " + classToDocument, e);

                    }

                    classStepTags.addAll(runJavaDoclet(classToDocument));
                }
            }
            finally {
                if (jarFileForClass != null){
                    try {
                        jarFileForClass.close();
                    } catch (IOException e) {
                        log.debug("ioexcception closing jar file", e);
                    }
                }
            }
        }
        return classStepTags;
    }

    /**
     * @param classStepTags
     */
    private void saveJsonFile(final List classStepTags) {

        Gson gson = new GsonBuilder().create();

        final String json = gson.toJson(classStepTags);

        writeOutputFile(json, STEP_IMPLS_JSON_FILENAME);
    }

    private void writeOutputFile(String xml, String filename) {
        final File output = new File(outputDirectory, filename);

        if (!outputDirectory.exists() && !outputDirectory.mkdirs()) {
            throw new IllegalStateException("unable to create output directory");
        }

        try {

            Files.asCharSink(output, Charset.forName("UTF-8")).write(xml);

        } catch (final IOException e) {
            log.error("error writing file", e);
        }
    }


    /**
     * @param jarFileForClass
     * @param classStepTags
     * @param loadedClasses
     */
    private void loadStepTagsFromJar(final JarFile jarFileForClass,
                                     final List classStepTags, final Set loadedClasses) {

        // TODO - change this to load from the json version

        final ZipEntry entry = jarFileForClass
                .getEntry("stepimplementations.json");

        if (entry != null) {

            try {
                final InputStream is = jarFileForClass.getInputStream(entry);
                InputStreamReader isr = new InputStreamReader(is);

                Gson gson = new GsonBuilder().create();
                ListstepDescriptors = gson.fromJson(isr, new TypeToken>() {
                }.getType());

                classStepTags.addAll(stepDescriptors);


            } catch (final IOException e) {
                log.error("Error loading from jarfile: ", e);
            }

            for (final StepImplementationsDescriptor descriptor : classStepTags) {
                loadedClasses.add(descriptor.getClassName());
            }
        } else {
            log.error("couldn't locate file in jar: stepimplementations.json");
        }
    }


    private static String convertClassNameToPath(final String className) {
        return className.replace('.', '/') + ".class";
    }


    private JarFile getJarFileForClass(final String className) {

        JarFile jarFile = null;
        final Set artifacts = project.getArtifacts();
        if (artifacts != null) {
            for (final Artifact a : artifacts) {
                // does this jar contain this class?
                JarFile tempJarFile = null;
                try {
                    tempJarFile = new JarFile(a.getFile());

                    final JarEntry jarEntry = tempJarFile
                            .getJarEntry(convertClassNameToPath(className));

                    if (jarEntry != null) {
                        jarFile = tempJarFile;
                        break;
                    }
                } catch (final IOException e) {
                    log.error("IO Exception opening jar file", e);
                }
            }
        }
        return jarFile;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy