View Javadoc
1   package com.github.mikkoi.maven.plugins.enforcer.rule.propertyusage;
2   
3   import org.apache.maven.plugin.logging.Log;
4   
5   import javax.annotation.Nonnull;
6   import java.io.File;
7   import java.io.FileInputStream;
8   import java.io.IOException;
9   import java.io.InputStream;
10  import java.nio.charset.Charset;
11  import java.nio.file.Files;
12  import java.nio.file.Paths;
13  import java.util.Collection;
14  import java.util.HashMap;
15  import java.util.HashSet;
16  import java.util.List;
17  import java.util.Map;
18  import java.util.Properties;
19  import java.util.Set;
20  import java.util.regex.Matcher;
21  import java.util.regex.Pattern;
22  
23  /**
24   * Handle issues with .properties files.
25   */
26  class PropertyFiles {
27  
28      private final Log log;
29  
30      private final Charset charset;
31  
32      private final Pattern commentLineP = Pattern.compile("^[\\s]{0,}[\\#\\!]{1}.{0,}$");
33      private final Pattern simplePropertyLineP = Pattern.compile("^[\\s]{0,}([^=:]{1,})[=:]{1}(.{0,})$", Pattern.COMMENTS | Pattern.UNICODE_CHARACTER_CLASS);
34      private final Pattern notSimplePropertyLineP = Pattern.compile("^[\\s]{0,}([\\S]{1,})[\\s]{0,}(.{0,})$", Pattern.COMMENTS | Pattern.UNICODE_CHARACTER_CLASS);
35      private final Pattern multiLineP = Pattern.compile("\\\\{1}[\\s]{0,}$");
36  
37      PropertyFiles(@Nonnull final Log logger, @Nonnull final Charset cset) {
38          log = logger;
39          charset = cset;
40      }
41  
42      /**
43       * @param filenames Collection of file names to read properties from.
44       * @return Map of definitions and how many times they are defined.
45       */
46      @Nonnull
47      Map<String, Integer> readPropertiesFromFilesWithoutCount(@Nonnull final Collection<String> filenames)
48              throws IOException {
49          Map<String, Integer> results = new HashMap<>();
50          for (String filename : filenames) {
51              File file = new File(filename);
52              Properties properties = new Properties();
53              log.debug("PropertyFiles:readPropertiesFromFilesWithoutCount() Reading file " + filename + ".");
54              try (InputStream inputStream = new FileInputStream(file)) {
55                  properties.load(inputStream);
56                  for (String name : properties.stringPropertyNames()) {
57                      log.debug("    Reading property " + name + ".");
58                      results.put(name, 1);
59                  }
60              }
61          }
62          return results;
63      }
64  
65      /**
66       * Read properties with our own reading routine and count
67       * how many times they are used.
68       *
69       * @param filenames Collection of file names to read properties from.
70       * @return Map of definitions and how many times they are defined.
71       */
72      @Nonnull
73      Map<String, Integer> readPropertiesFromFilesWithCount(@Nonnull final Collection<String> filenames)
74              throws IOException {
75          final Map<String, Integer> results = new HashMap<>();
76          log.debug("commentLineP:" + commentLineP.pattern());
77          log.debug("simplePropertyLineP:" + simplePropertyLineP.pattern());
78          log.debug("simplePropertyLineP:" + simplePropertyLineP.pattern());
79          log.debug("multiLineP:" + multiLineP);
80          for (final String filename : filenames) {
81              log.debug("Reading property file '" + filename + "'.");
82              readPropertiesFromFileWithCount(filename).forEach((key, value) -> results.put(key, value));
83          }
84          return results;
85      }
86  
87      /**
88       * Read properties with our own reading routine and return
89       * a set of definition instances
90       *
91       * @param filenames Collection of file names to read properties from.
92       * @return Map of definitions and PropertyDefinitions
93       */
94      @Nonnull
95      Map<String, Set<PropertyDefinition>> readPropertiesFromFilesGetDefinitions(@Nonnull final Collection<String> filenames)
96              throws IOException {
97          final Map<String, Set<PropertyDefinition>> results = new HashMap<>();
98          log.debug("commentLineP:" + commentLineP.pattern());
99          log.debug("simplePropertyLineP:" + simplePropertyLineP.pattern());
100         log.debug("simplePropertyLineP:" + simplePropertyLineP.pattern());
101         log.debug("multiLineP:" + multiLineP);
102         for (final String filename : filenames) {
103             log.debug("Reading property file '" + filename + "'.");
104             readPropertiesFromFileGetDefinitions(filename).forEach((key, value) -> {
105                 log.debug("key:" + key);
106                 log.debug("value:" + value);
107                 if(results.containsKey(key)) {
108                     results.get(key).addAll(value);
109                 } else {
110                     results.put(key, value);
111                 }
112             });
113         }
114         return results;
115     }
116 
117     /**
118      * Read properties with our own reading routine and count
119      * how many times they are used.
120      *
121      * @param filename File name to read properties from.
122      * @return Map of definitions and how many times they are defined.
123      */
124     @SuppressWarnings({
125             "squid:S3776", // Cognitive Complexity of methods should not be too high
126             "squid:S134",  // Control flow statements "if", "for", "while", "switch" and "try" should not be nested too deeply
127             "squid:S135"   // Loops should not contain more than a single "break" or "continue" statement
128     })
129     @Nonnull
130     Map<String, Integer> readPropertiesFromFileWithCount(@Nonnull final String filename)
131             throws IOException {
132         final Map<String, Integer> results = new HashMap<>();
133         List<String> rows = Files.readAllLines(Paths.get(filename), charset);
134         boolean readingMultiLineDefinition = false;
135         int linenumber = 0;
136         for (String row : rows) {
137             linenumber++;
138             log.debug("    Reading property row '" + row + "' (" + linenumber + ").");
139             Matcher commentLineM = commentLineP.matcher(row);
140             if (commentLineM.find()) {
141                 log.debug("        This is comment line.");
142                 continue;
143             }
144             Matcher multiLineM = multiLineP.matcher(row);
145             if (multiLineM.find()) {
146                 if (readingMultiLineDefinition) {
147                     log.debug("        This is multirow (not first row)");
148                     continue;
149                 } else {
150                     log.debug("        This is multirow (first row).");
151                     readingMultiLineDefinition = true;
152                 }
153             } else {
154                 if (readingMultiLineDefinition) {
155                     log.debug("        This is multirow (last row).");
156                     readingMultiLineDefinition = false;
157                     continue;
158                 }
159             }
160             Matcher simplePropertyLineM = simplePropertyLineP.matcher(row);
161             if (simplePropertyLineM.find()) {
162                 log.debug("        This is simple property line.");
163                 final String key = simplePropertyLineM.group(1).trim();
164                 storePropertyName(key, results);
165                 continue;
166             }
167             Matcher notSimplePropertyLineM = notSimplePropertyLineP.matcher(row);
168             if (notSimplePropertyLineM.find()) {
169                 log.debug("        This is not simple property line.");
170                 final String key = notSimplePropertyLineM.group(1).trim();
171                 storePropertyName(key, results);
172                 continue;
173             }
174             log.debug("        This row matched nothing,  propably empty or multiline continuation.");
175         }
176         return results;
177     }
178 
179     /**
180      * Read properties with our own reading routine ...
181      *
182      * @param filename File name to read properties from.
183      * @return Map of definitions and ...
184      */
185     @SuppressWarnings({
186             "squid:S3776", // Cognitive Complexity of methods should not be too high
187             "squid:S134",  // Control flow statements "if", "for", "while", "switch" and "try" should not be nested too deeply
188             "squid:S135"   // Loops should not contain more than a single "break" or "continue" statement
189     })
190     @Nonnull
191     Map<String, Set<PropertyDefinition>> readPropertiesFromFileGetDefinitions(@Nonnull final String filename)
192             throws IOException {
193         final Map<String, Set<PropertyDefinition>> propertyDefinitions = new HashMap<>();
194         List<String> rows = Files.readAllLines(Paths.get(filename), charset);
195         boolean readingMultiLineDefinition = false;
196         int linenumber = 0;
197         for (String row : rows) {
198             linenumber++;
199             log.debug("    Reading property row '" + row + "' (" + linenumber + ").");
200             Matcher commentLineM = commentLineP.matcher(row);
201             if (commentLineM.find()) {
202                 log.debug("        This is comment line.");
203                 continue;
204             }
205             Matcher multiLineM = multiLineP.matcher(row);
206             if (multiLineM.find()) {
207                 if (readingMultiLineDefinition) {
208                     log.debug("        This is multirow (not first row)");
209                     continue;
210                 } else {
211                     log.debug("        This is multirow (first row).");
212                     readingMultiLineDefinition = true;
213                 }
214             } else {
215                 if (readingMultiLineDefinition) {
216                     log.debug("        This is multirow (last row).");
217                     readingMultiLineDefinition = false;
218                     continue;
219                 }
220             }
221             Matcher simplePropertyLineM = simplePropertyLineP.matcher(row);
222             if (simplePropertyLineM.find()) {
223                 log.debug("        This is simple property line.");
224                 final String key = simplePropertyLineM.group(1).trim();
225                 final String value = simplePropertyLineM.group(2).trim();
226                 storePropertyDefinition(key, value, filename, linenumber, propertyDefinitions);
227                 continue;
228             }
229             Matcher notSimplePropertyLineM = notSimplePropertyLineP.matcher(row);
230             if (notSimplePropertyLineM.find()) {
231                 log.debug("        This is not simple property line.");
232                 final String key = notSimplePropertyLineM.group(1).trim();
233                 final String value = notSimplePropertyLineM.group(2).trim();
234                 storePropertyDefinition(key, value, filename, linenumber, propertyDefinitions);
235                 continue;
236             }
237             log.debug("        This row matched nothing,  propably empty or multiline continuation.");
238         }
239         return propertyDefinitions;
240     }
241 
242     private void storePropertyName(@Nonnull final String key, @Nonnull final Map<String, Integer> properties) {
243         log.debug("            Reading property " + key + ".");
244         if (!properties.containsKey(key)) {
245             log.debug("            Not defined before.");
246             properties.put(key, 1);
247         } else {
248             log.debug("            Defined before. Update counter.");
249             properties.replace(key, properties.get(key) + 1);
250         }
251     }
252 
253     private void storePropertyDefinition(@Nonnull final String key, @Nonnull final String value, @Nonnull final String filename, final int linenumber, @Nonnull final Map<String, Set<PropertyDefinition>> propertyDefinitions) {
254         log.debug("            Reading property " + key + ".");
255         if (propertyDefinitions.containsKey(key)) {
256             log.debug("            Defined before.");
257             propertyDefinitions.get(key).add(new PropertyDefinition(key, value, filename, linenumber));
258         } else {
259             log.debug("            Not defined before.");
260             final Set<PropertyDefinition> defs = new HashSet<>();
261             defs.add(new PropertyDefinition(key, value, filename, linenumber));
262             propertyDefinitions.put(key, defs);
263         }
264     }
265 }