View Javadoc
1   /*
2    * Copyright (c) 2011-2024 Qulice.com
3    *
4    * All rights reserved.
5    *
6    * Redistribution and use in source and binary forms, with or without
7    * modification, are permitted provided that the following conditions
8    * are met: 1) Redistributions of source code must retain the above
9    * copyright notice, this list of conditions and the following
10   * disclaimer. 2) Redistributions in binary form must reproduce the above
11   * copyright notice, this list of conditions and the following
12   * disclaimer in the documentation and/or other materials provided
13   * with the distribution. 3) Neither the name of the Qulice.com nor
14   * the names of its contributors may be used to endorse or promote
15   * products derived from this software without specific prior written
16   * permission.
17   *
18   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
20   * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
21   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
22   * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
23   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
29   * OF THE POSSIBILITY OF SUCH DAMAGE.
30   */
31  package com.qulice.maven;
32  
33  import com.jcabi.log.Logger;
34  import com.qulice.spi.ValidationException;
35  import java.io.File;
36  import java.util.Collection;
37  import org.apache.commons.io.FileUtils;
38  import org.apache.maven.project.MavenProject;
39  import org.cactoos.text.TextOf;
40  import org.cactoos.text.Trimmed;
41  import org.cactoos.text.UncheckedText;
42  
43  /**
44   * Check for required svn properties in all text files.
45   *
46   * <p>Every text file should have two SVN properties:
47   *
48   * <pre>
49   * svn:keywords=Id
50   * svn:eol-style=native
51   * </pre>
52   *
53   * <p>Read SVN documentation about how you can set them.
54   *
55   * @see <a href="http://svnbook.red-bean.com/en/1.5/svn.ref.properties.html">Properties in Subversion</a>
56   * @since 0.3
57   */
58  public final class SvnPropertiesValidator implements MavenValidator {
59  
60      @Override
61      public void validate(final MavenEnvironment env)
62          throws ValidationException {
63          if (SvnPropertiesValidator.isSvn(env.project())) {
64              final File dir = new File(env.project().getBasedir(), "src");
65              if (dir.exists()) {
66                  this.validate(dir);
67              } else {
68                  Logger.info(
69                      this,
70                      "%s directory is absent, no need to check SVN properties",
71                      dir
72                  );
73              }
74          } else {
75              Logger.info(this, "This is not an SVN project");
76          }
77      }
78  
79      /**
80       * Validate directory.
81       * @param dir The directory
82       * @throws ValidationException If fails
83       */
84      private void validate(final File dir) throws ValidationException {
85          final Collection<File> files = FileUtils.listFiles(
86              dir,
87              new String[] {
88                  "java", "txt", "xsl", "xml", "html", "js", "css", "vm",
89                  "php", "py", "groovy", "ini", "properties", "bsh", "xsd", "sql",
90              },
91              true
92          );
93          int errors = 0;
94          for (final File file : files) {
95              if (!this.valid(file)) {
96                  ++errors;
97              }
98          }
99          if (errors == 0) {
100             Logger.info(
101                 this,
102                 "%d text files have all required SVN properties",
103                 files.size()
104             );
105         } else {
106             Logger.info(
107                 this,
108                 "%d of %d files don't have required SVN properties",
109                 errors,
110                 files.size()
111             );
112             throw new ValidationException(
113                 "%d files with invalid SVN properties",
114                 errors
115             );
116         }
117     }
118 
119     /**
120      * Check whether this project uses SVN.
121      * @param project The Maven project
122      * @return TRUE if yes
123      */
124     private static boolean isSvn(final MavenProject project) {
125         return project.getScm() != null
126             && project.getScm().getConnection() != null
127             && project.getScm().getConnection().startsWith("scm:svn");
128     }
129 
130     /**
131      * Check one file.
132      * @param file The file to check
133      * @return TRUE if valid
134      */
135     private boolean valid(final File file) {
136         boolean valid = true;
137         final String style = SvnPropertiesValidator.propget(
138             file, "svn:eol-style"
139         );
140         if (!"native".equals(style)) {
141             Logger.error(
142                 this,
143                 "File %s doesn't have 'svn:eol-style' set to 'native': '%s'",
144                 file,
145                 style
146             );
147             valid = false;
148         }
149         final String keywords = SvnPropertiesValidator.propget(
150             file, "svn:keywords"
151         );
152         if (!keywords.contains("Id")) {
153             Logger.error(
154                 this,
155                 "File %s doesn't have 'svn:keywords' with 'Id': '%s'",
156                 file,
157                 keywords
158             );
159             valid = false;
160         }
161         return valid;
162     }
163 
164     /**
165      * Get SVN property from the file.
166      * @param file The file to check
167      * @param name Property name
168      * @return Property value
169      */
170     private static String propget(final File file, final String name) {
171         final ProcessBuilder builder = new ProcessBuilder(
172             "svn",
173             "propget",
174             name,
175             file.getAbsolutePath()
176         );
177         builder.redirectErrorStream(true);
178         try {
179             final Process process = builder.start();
180             process.waitFor();
181             return new UncheckedText(
182                 new Trimmed(
183                     new TextOf(
184                         process.getInputStream()
185                     )
186                 )
187             ).asString();
188         } catch (final java.io.IOException ex) {
189             throw new IllegalArgumentException(ex);
190         } catch (final InterruptedException ex) {
191             Thread.currentThread().interrupt();
192             throw new IllegalStateException(ex);
193         }
194     }
195 
196 }