View Javadoc
1   /*
2    * Copyright (c) 2012-2022 Yegor Bugayenko
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met: 1) Redistributions of source code must retain the above
8    * copyright notice, this list of conditions and the following
9    * disclaimer. 2) Redistributions in binary form must reproduce the above
10   * copyright notice, this list of conditions and the following
11   * disclaimer in the documentation and/or other materials provided
12   * with the distribution. 3) Neither the name of the jcabi.com nor
13   * the names of its contributors may be used to endorse or promote
14   * products derived from this software without specific prior written
15   * permission.
16   *
17   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
19   * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21   * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
22   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
28   * OF THE POSSIBILITY OF SUCH DAMAGE.
29   */
30  package com.jcabi.maven.plugin;
31  
32  import com.jcabi.log.Logger;
33  import java.io.File;
34  import java.io.FileFilter;
35  import java.io.IOException;
36  import java.nio.charset.StandardCharsets;
37  import java.nio.file.Files;
38  import java.util.ArrayList;
39  import java.util.Collection;
40  import java.util.Date;
41  import org.apache.commons.io.FileUtils;
42  import org.apache.commons.io.filefilter.DirectoryFileFilter;
43  import org.apache.commons.io.filefilter.NotFileFilter;
44  import org.apache.commons.io.filefilter.TrueFileFilter;
45  import org.apache.commons.io.filefilter.WildcardFileFilter;
46  import org.apache.commons.lang3.StringUtils;
47  import org.apache.commons.lang3.time.DateFormatUtils;
48  import org.apache.maven.plugin.AbstractMojo;
49  import org.apache.maven.plugin.MojoFailureException;
50  import org.apache.maven.plugins.annotations.LifecyclePhase;
51  import org.apache.maven.plugins.annotations.Mojo;
52  import org.apache.maven.plugins.annotations.Parameter;
53  import org.apache.maven.project.MavenProject;
54  import org.slf4j.impl.StaticLoggerBinder;
55  
56  /**
57   * Versionalize Java packages.
58   *
59   * @since 0.7.16
60   */
61  @Mojo(
62      name = "versionalize",
63      defaultPhase = LifecyclePhase.PREPARE_PACKAGE,
64      threadSafe = true
65  )
66  @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
67  public final class VersionalizeMojo extends AbstractMojo {
68  
69      /**
70       * Maven project.
71       */
72      @Parameter(defaultValue = "${project}", readonly = true)
73      private transient MavenProject project;
74  
75      /**
76       * Build number.
77       * @checkstyle MemberNameCheck (10 lines)
78       */
79      @Parameter(property = "buildNumber")
80      private transient String buildNumber;
81  
82      @Override
83      public void execute() throws MojoFailureException {
84          StaticLoggerBinder.getSingleton().setMavenLog(this.getLog());
85          final File src = new File(this.project.getBuild().getSourceDirectory());
86          if (!src.exists()) {
87              Logger.info(this, "source directory '%s' is absent", src);
88              return;
89          }
90          final File dest =
91              new File(this.project.getBuild().getOutputDirectory());
92          if (dest.mkdirs()) {
93              Logger.info(this, "created directory %s", dest);
94          }
95          Logger.info(this, "Versionalizing %s directory", dest);
96          try {
97              this.versionalize(src, dest);
98          } catch (final IOException ex) {
99              throw new MojoFailureException("failed to versionalize", ex);
100         }
101     }
102 
103     /**
104      * Create and return a text of the version file.
105      * @param dir The destination directory
106      * @return The text
107      */
108     @SuppressWarnings("PMD.ConsecutiveLiteralAppends")
109     private String text(final File dir) {
110         final StringBuilder text = new StringBuilder(0)
111             .append(String.format("Build Number: %s%n", this.buildNumber))
112             .append(
113                 String.format(
114                     "Project Version: %s%n",
115                     this.project.getVersion()
116                 )
117             )
118             .append(
119                 String.format(
120                     "Build Date: %s%n%n",
121                     DateFormatUtils.ISO_8601_EXTENDED_DATETIME_TIME_ZONE_FORMAT
122                         .format(new Date())
123                 )
124             );
125         for (final String name : VersionalizeMojo.files(dir, "*")) {
126             final File file = new File(dir, name);
127             if (file.isFile()) {
128                 text.append(name)
129                     .append(": ")
130                     .append(file.length())
131                     .append('\n');
132             }
133         }
134         return text.toString();
135     }
136 
137     /**
138      * Versionalize packages from source to dest.
139      * @param src Source directory
140      * @param dest Destination
141      * @throws IOException If some IO problem
142      */
143     private void versionalize(final File src, final File dest)
144         throws IOException {
145         final Collection<File> dirs = FileUtils.listFilesAndDirs(
146             src,
147             new NotFileFilter(TrueFileFilter.INSTANCE),
148             DirectoryFileFilter.DIRECTORY
149         );
150         final String name = String.format(
151             "%s-%s-%s.txt",
152             VersionalizeMojo.cleanup(this.project.getGroupId()),
153             VersionalizeMojo.cleanup(this.project.getArtifactId()),
154             VersionalizeMojo.cleanup(this.project.getPackaging())
155         );
156         for (final File dir : dirs) {
157             if (VersionalizeMojo.files(dir, "*.java").isEmpty()) {
158                 continue;
159             }
160             final File ddir = new File(
161                 dest,
162                 StringUtils.substring(
163                     dir.getCanonicalPath(),
164                     src.getCanonicalPath().length() + 1
165                 )
166             );
167             final File version = new File(ddir, name);
168             if (version.getParentFile().mkdirs()) {
169                 Logger.info(this, "created dir %s", version.getParentFile());
170             }
171             Files.write(
172                 version.toPath(),
173                 this.text(ddir).getBytes(StandardCharsets.UTF_8)
174             );
175             Logger.info(this, "File %s added", version);
176         }
177     }
178 
179     /**
180      * Clean the text.
181      * @param text The text
182      * @return Clean version of it
183      */
184     private static String cleanup(final String text) {
185         return text.replaceAll("[^_a-z0-9\\-]", "-");
186     }
187 
188     /**
189      * All Java files in the directory.
190      * @param dir The directory
191      * @param mask Mask to use
192      * @return List of Java file names
193      */
194     private static Collection<String> files(final File dir, final String mask) {
195         final FileFilter filter = new WildcardFileFilter(mask);
196         final File[] files = dir.listFiles(filter);
197         final Collection<String> names = new ArrayList<>(files.length);
198         for (final File file : files) {
199             names.add(file.getName());
200         }
201         return names;
202     }
203 
204 }