View Javadoc

1   /*
2    ProgramVersionUtils.java
3    Creation date : 18/11/2010
4    Copyright © Benjamin Croizet (graffity2199@yahoo.fr)
5   
6    This program is free software; you can redistribute it and/or
7    modify it under the terms of the GNU General Public License
8    or GNU Lesser General Public License as published by the
9    Free Software Foundation; either version 3 of the License,
10   or (at your option) any later version.
11  
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16  
17   You should have received copies of the GNU General Public License
18   and GNU Lesser General Public License along with this program;
19   if not, write to the Free Software Foundation, Inc.,
20   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21   http://www.fsf.org/licensing/licenses/gpl.html
22   http://www.gnu.org/licenses/lgpl.html
23   */
24  
25  package net.sourceforge.plantumldependency.commoncli.utils.version;
26  
27  import static java.lang.ClassLoader.getSystemClassLoader;
28  import static java.lang.Thread.currentThread;
29  import static java.util.logging.Level.FINE;
30  import static java.util.logging.Level.SEVERE;
31  import static java.util.logging.Logger.getLogger;
32  import static net.sourceforge.plantumldependency.common.constants.CommonConstants.MINUS_ONE_RETURN_CODE;
33  import static net.sourceforge.plantumldependency.common.utils.check.ParameterCheckerUtils.checkNull;
34  import static net.sourceforge.plantumldependency.common.utils.check.ParameterCheckerUtils.checkNullOrEmpty;
35  import static net.sourceforge.plantumldependency.common.utils.file.FileUtils.closeCloseable;
36  import static net.sourceforge.plantumldependency.common.utils.log.LogUtils.buildLogString;
37  import static net.sourceforge.plantumldependency.common.utils.string.StringUtils.isNotEmpty;
38  import static net.sourceforge.plantumldependency.commoncli.constants.CommandLineConstants.PROTECTED_DOT_REGEXP;
39  import static net.sourceforge.plantumldependency.commoncli.constants.log.ErrorConstants.DATE_FORMAT_ERROR;
40  import static net.sourceforge.plantumldependency.commoncli.constants.log.ErrorConstants.EMPTY_ARGUMENT_ERROR;
41  import static net.sourceforge.plantumldependency.commoncli.constants.log.ErrorConstants.MISSING_PROPERTY_ERROR;
42  import static net.sourceforge.plantumldependency.commoncli.constants.log.ErrorConstants.MISSING_PROPERTY_FILE_ERROR;
43  import static net.sourceforge.plantumldependency.commoncli.constants.log.ErrorConstants.PROGRAM_VERSION_PARSING2_ERROR;
44  import static net.sourceforge.plantumldependency.commoncli.constants.log.ErrorConstants.PROGRAM_VERSION_PARSING_ERROR;
45  import static net.sourceforge.plantumldependency.commoncli.constants.log.FineConstants.PROPERTY_SPECIFIED_FINE;
46  import static net.sourceforge.plantumldependency.commoncli.constants.log.FineConstants.SNAPSHOT_VERSION_SPECIFIED_FINE;
47  
48  import java.io.File;
49  import java.io.FileInputStream;
50  import java.io.IOException;
51  import java.io.InputStream;
52  import java.text.DateFormat;
53  import java.text.ParseException;
54  import java.text.SimpleDateFormat;
55  import java.util.Date;
56  import java.util.Properties;
57  import java.util.logging.Logger;
58  
59  import net.sourceforge.plantumldependency.commoncli.exception.MissingPropertyException;
60  import net.sourceforge.plantumldependency.commoncli.program.version.ProgramVersion;
61  import net.sourceforge.plantumldependency.commoncli.program.version.impl.ProgramVersionImpl;
62  
63  /**
64   * The class utilities simplifying some {@link ProgramVersion} tasks.
65   *
66   * @author Benjamin Croizet (<a href="mailto:graffity2199@yahoo.fr>graffity2199@yahoo.fr</a>)
67   * @since 1.3.0
68   * @version 1.3.0
69   */
70  public abstract class ProgramVersionUtils {
71  
72      /** The class logger. */
73      private static final transient Logger LOGGER = getLogger(ProgramVersionUtils.class.getName());
74  
75      /** Build time stamp property constant. */
76      public static final String BUILD_TIMESTAMP_PROPERTY = "build.timestamp";
77  
78      /** Build time stamp format property constant. */
79      public static final String BUILD_TIMESTAMP_FORMAT_PROPERTY = "maven.build.timestamp.format";
80  
81      /** Version property constant. */
82      public static final String VERSION_PROPERTY = "version";
83  
84      /** The string representing a snapshot version. */
85      public static final String SNAPSHOT_VERSION = "-SNAPSHOT";
86  
87      /**
88       * The integer representing the number of digit expected while parsing a program version string.
89       */
90      private static final int VERSION_NUMBER = 3;
91  
92      /**
93       * Creates a {@link ProgramVersion} instance from a properties {@link Properties}.
94       * <p>
95       * The following properties must exist in the properties in order to correctly create the
96       * {@link ProgramVersion} instance :<br>
97       * <i>{@link ProgramVersionUtils#BUILD_TIMESTAMP_PROPERTY}</i> where the value should have a
98       * form matching the build time stamp format property value.<br>
99       * <i>{@link ProgramVersionUtils#BUILD_TIMESTAMP_FORMAT_PROPERTY}</i> where the value should
100      * have a form matching the {@link java.text.DateFormat}.<br>
101      * <i>{@link ProgramVersionUtils#VERSION_PROPERTY} where the value should have a form matching
102      * "majorNumber.minorNumber.revisionNumber(-SNAPSHOT)" or
103      * "majorNumber-minorNumber-revisionNumber(-SNAPSHOT)", following the separator.</i>
104      * </p>
105      *
106      * @param properties
107      *            the properties {@link Properties} instance to read the program version from,
108      *            mustn't be <code>null</code>.
109      * @return the {@link ProgramVersion} instance created from the property file attributes.
110      * @throws ParseException
111      *             if a property value can't be parsed.
112      * @throws MissingPropertyException
113      *             if one of the three needed property is missing.
114      * @since 1.3.0
115      */
116     public static ProgramVersion createProgramVersionFromProperties(final Properties properties) throws ParseException,
117             MissingPropertyException {
118         return createProgramVersionFromProperties(properties, PROTECTED_DOT_REGEXP);
119     }
120 
121     /**
122      * Creates a {@link ProgramVersion} instance from a properties {@link Properties}.
123      * <p>
124      * The following properties must exist in the properties in order to correctly create the
125      * {@link ProgramVersion} instance :<br>
126      * <i>{@link ProgramVersionUtils#BUILD_TIMESTAMP_PROPERTY}</i> where the value should have a
127      * form matching the build time stamp format property value.<br>
128      * <i>{@link ProgramVersionUtils#BUILD_TIMESTAMP_FORMAT_PROPERTY}</i> where the value should
129      * have a form matching the {@link java.text.DateFormat}.<br>
130      * <i>{@link ProgramVersionUtils#VERSION_PROPERTY} where the value should have a form matching
131      * "majorNumber.minorNumber.revisionNumber(-SNAPSHOT)" or
132      * "majorNumber-minorNumber-revisionNumber(-SNAPSHOT)", following the separator.</i>
133      * </p>
134      *
135      * @param properties
136      *            the properties {@link Properties} instance to read the program version from,
137      *            mustn't be <code>null</code>.
138      * @param versionSeparatorRegexp
139      *            the version separator to be used for parsing the
140      *            {@link ProgramVersionUtils#VERSION_PROPERTY} property, mustn't be
141      *            <code>null</code>.
142      * @return the {@link ProgramVersion} instance created from the property file attributes.
143      * @throws ParseException
144      *             if a property value can't be parsed.
145      * @throws MissingPropertyException
146      *             if one of the three needed property is missing.
147      * @since 1.3.0
148      */
149     public static ProgramVersion createProgramVersionFromProperties(final Properties properties,
150             final String versionSeparatorRegexp) throws ParseException, MissingPropertyException {
151         checkNull(properties, buildLogString(EMPTY_ARGUMENT_ERROR, "properties"));
152         checkNullOrEmpty(versionSeparatorRegexp, buildLogString(EMPTY_ARGUMENT_ERROR, "versionSeparatorRegexp"));
153 
154         ProgramVersion programVersion = null;
155 
156         final String buildTimestamp = getPropertyValue(properties, BUILD_TIMESTAMP_PROPERTY);
157         final String buildTimestampFormat = getPropertyValue(properties, BUILD_TIMESTAMP_FORMAT_PROPERTY);
158 
159         try {
160             final DateFormat dateFormat = new SimpleDateFormat(buildTimestampFormat);
161             final Date buildDate = dateFormat.parse(buildTimestamp);
162 
163             final String version = getPropertyValue(properties, VERSION_PROPERTY);
164             programVersion = createProgramVersionFromStringAndDate(version, versionSeparatorRegexp, buildDate);
165         } catch (final IllegalArgumentException e) {
166             LOGGER.log(SEVERE, buildLogString(DATE_FORMAT_ERROR, buildTimestampFormat), e);
167             throw new ParseException(buildLogString(DATE_FORMAT_ERROR, buildTimestampFormat), MINUS_ONE_RETURN_CODE);
168         } catch (final ParseException e) {
169             LOGGER.log(SEVERE, buildLogString(DATE_FORMAT_ERROR, buildTimestampFormat), e);
170             throw new ParseException(buildLogString(DATE_FORMAT_ERROR, buildTimestampFormat), MINUS_ONE_RETURN_CODE);
171         }
172 
173         return programVersion;
174     }
175 
176     /**
177      * Creates a {@link ProgramVersion} instance from a file properties path.
178      * <p>
179      * The following properties must exist in the properties in order to correctly create the
180      * {@link ProgramVersion} instance :<br>
181      * <i>{@link ProgramVersionUtils#BUILD_TIMESTAMP_PROPERTY}</i> where the value should have a
182      * form matching the build time stamp format property value.<br>
183      * <i>{@link ProgramVersionUtils#BUILD_TIMESTAMP_FORMAT_PROPERTY}</i> where the value should
184      * have a form matching the {@link java.text.DateFormat}.<br>
185      * <i>{@link ProgramVersionUtils#VERSION_PROPERTY} where the value should have a form matching
186      * "majorNumber.minorNumber.revisionNumber(-SNAPSHOT)" or
187      * "majorNumber-minorNumber-revisionNumber(-SNAPSHOT)", following the separator.</i>
188      * </p>
189      *
190      * @param propertiesFullPath
191      *            the full path following the current OS of the property file, mustn't be
192      *            <code>null</code>.
193      * @return the {@link ProgramVersion} instance created from the property file attributes.
194      * @throws IOException
195      *             if any occurs when reading the property file.
196      * @throws ParseException
197      *             if a property value can't be parsed.
198      * @throws MissingPropertyException
199      *             if one of the three needed property is missing.
200      * @since 1.3.0
201      */
202     public static ProgramVersion createProgramVersionFromPropertiesFile(final String propertiesFullPath)
203             throws IOException, ParseException, MissingPropertyException {
204         return createProgramVersionFromPropertiesFile(propertiesFullPath, PROTECTED_DOT_REGEXP);
205     }
206 
207     /**
208      * Creates a {@link ProgramVersion} instance from a file properties path.
209      * <p>
210      * The following properties must exist in the properties in order to correctly create the
211      * {@link ProgramVersion} instance :<br>
212      * <i>{@link ProgramVersionUtils#BUILD_TIMESTAMP_PROPERTY}</i> where the value should have a
213      * form matching the build time stamp format property value.<br>
214      * <i>{@link ProgramVersionUtils#BUILD_TIMESTAMP_FORMAT_PROPERTY}</i> where the value should
215      * have a form matching the {@link java.text.DateFormat}.<br>
216      * <i>{@link ProgramVersionUtils#VERSION_PROPERTY} where the value should have a form matching
217      * "majorNumber.minorNumber.revisionNumber(-SNAPSHOT)" or
218      * "majorNumber-minorNumber-revisionNumber(-SNAPSHOT)", following the separator.</i>
219      * </p>
220      *
221      * @param propertiesFullPath
222      *            the full path following the current OS of the property file, mustn't be
223      *            <code>null</code>.
224      * @param versionSeparatorRegexp
225      *            the version separator to be used for parsing the
226      *            {@link ProgramVersionUtils#VERSION_PROPERTY} property, mustn't be
227      *            <code>null</code>.
228      * @return the {@link ProgramVersion} instance created from the property file attributes.
229      * @throws IOException
230      *             if any occurs when reading the property file.
231      * @throws ParseException
232      *             if a property value can't be parsed.
233      * @throws MissingPropertyException
234      *             if one of the three needed property is missing.
235      * @since 1.3.0
236      */
237     public static ProgramVersion createProgramVersionFromPropertiesFile(final String propertiesFullPath,
238             final String versionSeparatorRegexp) throws IOException, ParseException, MissingPropertyException {
239         checkNullOrEmpty(propertiesFullPath, buildLogString(EMPTY_ARGUMENT_ERROR, "propertiesFullPath"));
240         checkNullOrEmpty(versionSeparatorRegexp, buildLogString(EMPTY_ARGUMENT_ERROR, "versionSeparatorRegexp"));
241 
242         ProgramVersion programVersion = null;
243 
244         InputStream inputStream = null;
245         try {
246             final File propertyFile = new File(propertiesFullPath);
247             inputStream = new FileInputStream(propertyFile);
248             programVersion = createProgramVersionFromPropertiesFileInputStream(inputStream, versionSeparatorRegexp);
249         } finally {
250             closeCloseable(inputStream, propertiesFullPath);
251         }
252 
253         return programVersion;
254     }
255 
256     /**
257      * Creates a {@link ProgramVersion} instance from an {@link InputStream} which must be a
258      * property file.
259      * <p>
260      * The following properties must exist in the properties in order to correctly create the
261      * {@link ProgramVersion} instance :<br>
262      * <i>{@link ProgramVersionUtils#BUILD_TIMESTAMP_PROPERTY}</i> where the value should have a
263      * form matching the build time stamp format property value.<br>
264      * <i>{@link ProgramVersionUtils#BUILD_TIMESTAMP_FORMAT_PROPERTY}</i> where the value should
265      * have a form matching the {@link java.text.DateFormat}.<br>
266      * <i>{@link ProgramVersionUtils#VERSION_PROPERTY} where the value should have a form matching
267      * "majorNumber.minorNumber.revisionNumber(-SNAPSHOT)" or
268      * "majorNumber-minorNumber-revisionNumber(-SNAPSHOT)", following the separator.</i>
269      * </p>
270      *
271      * @param propertiesFileInputStream
272      *            the {@link InputStream} instance to read the program version properties from,
273      *            mustn't be <code>null</code>.
274      * @return the {@link ProgramVersion} instance created from the property file attributes.
275      * @throws IOException
276      *             if any occurs when reading the property file.
277      * @throws ParseException
278      *             if a property value can't be parsed.
279      * @throws MissingPropertyException
280      *             if one of the three needed property is missing.
281      * @since 1.3.0
282      */
283     public static ProgramVersion createProgramVersionFromPropertiesFileInputStream(
284             final InputStream propertiesFileInputStream) throws IOException, ParseException, MissingPropertyException {
285         return createProgramVersionFromPropertiesFileInputStream(propertiesFileInputStream, PROTECTED_DOT_REGEXP);
286     }
287 
288     /**
289      * Creates a {@link ProgramVersion} instance from an {@link InputStream} which must be a
290      * property file.
291      * <p>
292      * The following properties must exist in the properties in order to correctly create the
293      * {@link ProgramVersion} instance :<br>
294      * <i>{@link ProgramVersionUtils#BUILD_TIMESTAMP_PROPERTY}</i> where the value should have a
295      * form matching the build time stamp format property value.<br>
296      * <i>{@link ProgramVersionUtils#BUILD_TIMESTAMP_FORMAT_PROPERTY}</i> where the value should
297      * have a form matching the {@link java.text.DateFormat}.<br>
298      * <i>{@link ProgramVersionUtils#VERSION_PROPERTY} where the value should have a form matching
299      * "majorNumber.minorNumber.revisionNumber(-SNAPSHOT)" or
300      * "majorNumber-minorNumber-revisionNumber(-SNAPSHOT)", following the separator.</i>
301      * </p>
302      *
303      * @param propertiesFileInputStream
304      *            the {@link InputStream} instance to read the program version properties from,
305      *            mustn't be <code>null</code>.
306      * @param versionSeparatorRegexp
307      *            the version separator to be used for parsing the
308      *            {@link ProgramVersionUtils#VERSION_PROPERTY} property, mustn't be
309      *            <code>null</code>.
310      * @return the {@link ProgramVersion} instance created from the property file attributes.
311      * @throws IOException
312      *             if any occurs when reading the property file.
313      * @throws ParseException
314      *             if a property value can't be parsed.
315      * @throws MissingPropertyException
316      *             if one of the three needed property is missing.
317      * @since 1.3.0
318      */
319     public static ProgramVersion createProgramVersionFromPropertiesFileInputStream(
320             final InputStream propertiesFileInputStream, final String versionSeparatorRegexp) throws IOException,
321             ParseException, MissingPropertyException {
322         ProgramVersion programVersion = null;
323 
324         if (propertiesFileInputStream != null) {
325             final Properties properties = new Properties();
326             properties.load(propertiesFileInputStream);
327             programVersion = createProgramVersionFromProperties(properties, versionSeparatorRegexp);
328         } else {
329             throw new IllegalArgumentException(buildLogString(EMPTY_ARGUMENT_ERROR, "propertiesFileInputStream"));
330         }
331 
332         return programVersion;
333     }
334 
335     /**
336      * Creates a {@link ProgramVersion} instance from a properties path which can be read by the
337      * specified class classloader.
338      * <p>
339      * The following properties must exist in the properties in order to correctly create the
340      * {@link ProgramVersion} instance :<br>
341      * <i>{@link ProgramVersionUtils#BUILD_TIMESTAMP_PROPERTY}</i> where the value should have a
342      * form matching the build time stamp format property value.<br>
343      * <i>{@link ProgramVersionUtils#BUILD_TIMESTAMP_FORMAT_PROPERTY}</i> where the value should
344      * have a form matching the {@link java.text.DateFormat}.<br>
345      * <i>{@link ProgramVersionUtils#VERSION_PROPERTY} where the value should have a form matching
346      * "majorNumber.minorNumber.revisionNumber(-SNAPSHOT)" or
347      * "majorNumber-minorNumber-revisionNumber(-SNAPSHOT)", following the separator.</i>
348      * </p>
349      *
350      * @param propertiesFullPath
351      *            the full path of the property file within the specified class classloader.
352      * @param clazz
353      *            the {@link Class} to get the classloader from, mustn't be <code>null</code>.
354      * @return the {@link ProgramVersion} instance created from the property file attributes.
355      * @throws IOException
356      *             if any occurs when reading the property file.
357      * @throws ParseException
358      *             if a property value can't be parsed.
359      * @throws MissingPropertyException
360      *             if one of the three needed property is missing.
361      * @since 1.3.0
362      */
363     public static ProgramVersion createProgramVersionFromPropertiesFileWithinClassClassloader(
364             final String propertiesFullPath, final Class < ? > clazz) throws IOException, ParseException,
365             MissingPropertyException {
366         return createProgramVersionFromPropertiesFileWithinClassloader(propertiesFullPath, PROTECTED_DOT_REGEXP,
367                 clazz.getClassLoader());
368     }
369 
370     /**
371      * Creates a {@link ProgramVersion} instance from a properties path which can be read by the
372      * classloader.
373      * <p>
374      * The following properties must exist in the properties in order to correctly create the
375      * {@link ProgramVersion} instance :<br>
376      * <i>{@link ProgramVersionUtils#BUILD_TIMESTAMP_PROPERTY}</i> where the value should have a
377      * form matching the build time stamp format property value.<br>
378      * <i>{@link ProgramVersionUtils#BUILD_TIMESTAMP_FORMAT_PROPERTY}</i> where the value should
379      * have a form matching the {@link java.text.DateFormat}.<br>
380      * <i>{@link ProgramVersionUtils#VERSION_PROPERTY} where the value should have a form matching
381      * "majorNumber.minorNumber.revisionNumber(-SNAPSHOT)" or
382      * "majorNumber-minorNumber-revisionNumber(-SNAPSHOT)", following the separator.</i>
383      * </p>
384      *
385      * @param propertiesFullPath
386      *            the full path of the property file within the classloader.
387      * @param versionSeparatorRegexp
388      *            the version separator to be used for parsing the
389      *            {@link ProgramVersionUtils#VERSION_PROPERTY} property, mustn't be
390      *            <code>null</code>.
391      * @param classloader
392      *            the {@link ClassLoader} to load the resource from, mustn't be <code>null</code>.
393      * @return the {@link ProgramVersion} instance created from the property file attributes.
394      * @throws IOException
395      *             if any occurs when reading the property file.
396      * @throws ParseException
397      *             if a property value can't be parsed.
398      * @throws MissingPropertyException
399      *             if one of the three needed property is missing.
400      * @since 1.3.0
401      */
402     public static ProgramVersion createProgramVersionFromPropertiesFileWithinClassloader(
403             final String propertiesFullPath, final String versionSeparatorRegexp, final ClassLoader classloader)
404             throws IOException, ParseException, MissingPropertyException {
405         checkNullOrEmpty(propertiesFullPath, buildLogString(EMPTY_ARGUMENT_ERROR, "propertiesFullPath"));
406         checkNullOrEmpty(versionSeparatorRegexp, buildLogString(EMPTY_ARGUMENT_ERROR, "versionSeparatorRegexp"));
407         checkNull(classloader, buildLogString(EMPTY_ARGUMENT_ERROR, "classloader"));
408 
409         ProgramVersion programVersion = null;
410 
411         InputStream inputStream = null;
412         try {
413             inputStream = classloader.getResourceAsStream(propertiesFullPath);
414             if (inputStream != null) {
415                 programVersion = createProgramVersionFromPropertiesFileInputStream(inputStream, versionSeparatorRegexp);
416             } else {
417                 throw new IOException(buildLogString(MISSING_PROPERTY_FILE_ERROR, propertiesFullPath));
418             }
419         } finally {
420             closeCloseable(inputStream, propertiesFullPath);
421         }
422 
423         return programVersion;
424     }
425 
426     /**
427      * Creates a {@link ProgramVersion} instance from a properties path which can be read by the
428      * system classloader.
429      * <p>
430      * The following properties must exist in the properties in order to correctly create the
431      * {@link ProgramVersion} instance :<br>
432      * <i>{@link ProgramVersionUtils#BUILD_TIMESTAMP_PROPERTY}</i> where the value should have a
433      * form matching the build time stamp format property value.<br>
434      * <i>{@link ProgramVersionUtils#BUILD_TIMESTAMP_FORMAT_PROPERTY}</i> where the value should
435      * have a form matching the {@link java.text.DateFormat}.<br>
436      * <i>{@link ProgramVersionUtils#VERSION_PROPERTY} where the value should have a form matching
437      * "majorNumber.minorNumber.revisionNumber(-SNAPSHOT)" or
438      * "majorNumber-minorNumber-revisionNumber(-SNAPSHOT)", following the separator.</i>
439      * </p>
440      *
441      * @param propertiesFullPath
442      *            the full path of the property file within the system classloader.
443      * @return the {@link ProgramVersion} instance created from the property file attributes.
444      * @throws IOException
445      *             if any occurs when reading the property file.
446      * @throws ParseException
447      *             if a property value can't be parsed.
448      * @throws MissingPropertyException
449      *             if one of the three needed property is missing.
450      * @since 1.3.0
451      */
452     public static ProgramVersion createProgramVersionFromPropertiesFileWithinSystemClassloader(
453             final String propertiesFullPath) throws IOException, ParseException, MissingPropertyException {
454         return createProgramVersionFromPropertiesFileWithinClassloader(propertiesFullPath, PROTECTED_DOT_REGEXP,
455                 getSystemClassLoader());
456     }
457 
458     /**
459      * Creates a {@link ProgramVersion} instance from a properties path which can be read by the
460      * thread classloader.
461      * <p>
462      * The following properties must exist in the properties in order to correctly create the
463      * {@link ProgramVersion} instance :<br>
464      * <i>{@link ProgramVersionUtils#BUILD_TIMESTAMP_PROPERTY}</i> where the value should have a
465      * form matching the build time stamp format property value.<br>
466      * <i>{@link ProgramVersionUtils#BUILD_TIMESTAMP_FORMAT_PROPERTY}</i> where the value should
467      * have a form matching the {@link java.text.DateFormat}.<br>
468      * <i>{@link ProgramVersionUtils#VERSION_PROPERTY} where the value should have a form matching
469      * "majorNumber.minorNumber.revisionNumber(-SNAPSHOT)" or
470      * "majorNumber-minorNumber-revisionNumber(-SNAPSHOT)", following the separator.</i>
471      * </p>
472      *
473      * @param propertiesFullPath
474      *            the full path of the property file within the thread classloader.
475      * @return the {@link ProgramVersion} instance created from the property file attributes.
476      * @throws IOException
477      *             if any occurs when reading the property file.
478      * @throws ParseException
479      *             if a property value can't be parsed.
480      * @throws MissingPropertyException
481      *             if one of the three needed property is missing.
482      * @since 1.3.0
483      */
484     public static ProgramVersion createProgramVersionFromPropertiesFileWithinThreadClassloader(
485             final String propertiesFullPath) throws IOException, ParseException, MissingPropertyException {
486         return createProgramVersionFromPropertiesFileWithinClassloader(propertiesFullPath, PROTECTED_DOT_REGEXP,
487                 currentThread().getContextClassLoader());
488     }
489 
490     /**
491      * Creates a {@link ProgramVersion} instance from a {@link String}.
492      *
493      * @param programVersionString
494      *            the program version as a {@link String} to parse, mustn't be <code>null</code> nor
495      *            empty. Must have the form of "majorNumber.minorNumber.revisionNumber(-SNAPSHOT)"
496      *            or "majorNumber-minorNumber-revisionNumber(-SNAPSHOT)", following the separator.
497      * @param versionSeparatorRegexp
498      *            the version separator to be used for parsing the <code>programVersionString</code>
499      *            , mustn't be <code>null</code>.
500      * @param programVersionDate
501      *            the compilation date version to pass to the {@link ProgramVersion} instance as a
502      *            {@link String}.
503      * @param programVersionDatePattern
504      *            the program version date pattern to read the <code>programVersionDate</code>
505      *            parameter, mustn't be <code>null</code>.
506      * @return the {@link ProgramVersion} instance created from the string.
507      * @throws ParseException
508      *             if any exception occurs while reading or parsing the input string.
509      * @since 1.3.0
510      */
511     public static ProgramVersion createProgramVersionFromString(final String programVersionString,
512             final String versionSeparatorRegexp, final String programVersionDate, final String programVersionDatePattern)
513             throws ParseException {
514         checkNullOrEmpty(programVersionDate, buildLogString(EMPTY_ARGUMENT_ERROR, "programVersionDate"));
515         checkNullOrEmpty(programVersionDatePattern, buildLogString(EMPTY_ARGUMENT_ERROR, "programVersionDatePattern"));
516 
517         final DateFormat dateFormat = new SimpleDateFormat(programVersionDatePattern);
518         final Date buildDate = dateFormat.parse(programVersionDate);
519 
520         return createProgramVersionFromStringAndDate(programVersionString, versionSeparatorRegexp, buildDate);
521     }
522 
523     /**
524      * Creates a {@link ProgramVersion} instance from a {@link String}.
525      *
526      * @param programVersionString
527      *            the program version as a {@link String} to parse, mustn't be <code>null</code> nor
528      *            empty. Must have the form of "majorNumber.minorNumber.revisionNumber(-SNAPSHOT)"
529      *            or "majorNumber-minorNumber-revisionNumber(-SNAPSHOT)", following the separator.
530      * @param versionSeparatorRegexp
531      *            the version separator to be used for parsing the <code>programVersionString</code>
532      *            , mustn't be <code>null</code>.
533      * @param programVersionDate
534      *            the compilation date version to pass to the {@link ProgramVersion} instance.
535      * @return the {@link ProgramVersion} instance created from the string.
536      * @throws ParseException
537      *             if any exception occurs while reading or parsing the input string.
538      * @since 1.3.0
539      */
540     public static ProgramVersion createProgramVersionFromStringAndDate(final String programVersionString,
541             final String versionSeparatorRegexp, final Date programVersionDate) throws ParseException {
542         checkNullOrEmpty(programVersionString, buildLogString(EMPTY_ARGUMENT_ERROR, "programVersionString"));
543         checkNullOrEmpty(versionSeparatorRegexp, buildLogString(EMPTY_ARGUMENT_ERROR, "versionSeparatorRegexp"));
544 
545         final String version = removeSnapshot(programVersionString);
546 
547         final boolean isSnapshot = !version.equals(programVersionString);
548         return createProgramVersionFromStringDateAndSnapshot(programVersionDate, version, versionSeparatorRegexp,
549                 isSnapshot);
550     }
551 
552     /**
553      * Creates a {@link ProgramVersion} instance from a {@link String}, a {@link Date} and a
554      * snapshot parameter.
555      *
556      * @param programVersionDate
557      *            the program version {@link Date}, mustn't be <code>null</code>
558      * @param programVersionString
559      *            the program version as a {@link String} to parse, mustn't be <code>null</code> nor
560      *            empty. Must have the form of "majorNumber.minorNumber.revisionNumber" or
561      *            "majorNumber-minorNumber-revisionNumber", following the separator.
562      * @param versionSeparatorRegexp
563      *            the version separator to be used for parsing the <code>programVersionString</code>
564      *            , mustn't be <code>null</code>.
565      * @param isSnapshot
566      *            the <code>boolean</code> telling if the program version is a snapshot.
567      * @return the {@link ProgramVersion} instance created from the string and the passed parameter.
568      * @throws ParseException
569      *             if any exception occurs while reading or parsing the input string.
570      * @since 1.3.0
571      */
572     private static ProgramVersion createProgramVersionFromStringDateAndSnapshot(final Date programVersionDate,
573             final String programVersionString, final String versionSeparatorRegexp, final boolean isSnapshot)
574             throws ParseException {
575         ProgramVersion programVersion = null;
576 
577         final String[] versionNumbers = programVersionString.split(versionSeparatorRegexp);
578         if (versionNumbers.length == VERSION_NUMBER) {
579             try {
580                 final Integer majorVersionNumber = Integer.valueOf(versionNumbers[0]);
581                 final Integer minorVersionNumber = Integer.valueOf(versionNumbers[1]);
582                 final Integer revisionVersionNumber = Integer.valueOf(versionNumbers[2]);
583                 programVersion = new ProgramVersionImpl(majorVersionNumber.intValue(), minorVersionNumber.intValue(),
584                         revisionVersionNumber.intValue(), programVersionDate, isSnapshot);
585             } catch (final NumberFormatException e) {
586                 throw new ParseException(buildLogString(PROGRAM_VERSION_PARSING_ERROR, e.getMessage()), 0);
587             }
588         } else {
589             throw new ParseException(PROGRAM_VERSION_PARSING2_ERROR, 0);
590         }
591         return programVersion;
592     }
593 
594     /**
595      * Gets the value of the property key in the properties instance, and throw an exception if not
596      * found.
597      *
598      * @param properties
599      *            the {@link Properties} instance where to look into, mustn't be <code>null</code>.
600      * @param propertyKey
601      *            the property key to look for, mustn't be empty.
602      * @return the value matching the <code>propertyKey</code> if found.
603      * @throws MissingPropertyException
604      *             if the property value can't be found within the <code>properties</code> instance.
605      * @since 1.3.0
606      */
607     private static String getPropertyValue(final Properties properties, final String propertyKey)
608             throws MissingPropertyException {
609         final String propertyValue = (String) properties.get(propertyKey);
610         if (isNotEmpty(propertyValue)) {
611             LOGGER.log(FINE, buildLogString(PROPERTY_SPECIFIED_FINE, new String[] {propertyKey, propertyValue}));
612         } else {
613             throw new MissingPropertyException(buildLogString(MISSING_PROPERTY_ERROR, propertyKey));
614         }
615 
616         return propertyValue;
617     }
618 
619     /**
620      * Remove the {@link ProgramVersionUtils#SNAPSHOT_VERSION} from the program version string if
621      * found.
622      *
623      * @param programVersionString
624      *            the program version string to parse, with a form matching
625      *            "majorNumber.minorNumber.revisionNumber(-SNAPSHOT)" or
626      *            "majorNumber-minorNumber-revisionNumber(-SNAPSHOT)", following the separator, can
627      *            be <code>null</code> of empty.
628      * @return the program version string without the {@link ProgramVersionUtils#SNAPSHOT_VERSION}
629      *         substring, if found, or the same <code>programVersionString</code> if not found.
630      * @since 1.3.0
631      */
632     private static String removeSnapshot(final String programVersionString) {
633         String programVersion = programVersionString;
634         final int snapshotIndex = programVersionString.indexOf(SNAPSHOT_VERSION);
635         if (snapshotIndex != MINUS_ONE_RETURN_CODE) {
636             programVersion = programVersionString.substring(0, snapshotIndex);
637         } else {
638             LOGGER.log(FINE, buildLogString(SNAPSHOT_VERSION_SPECIFIED_FINE, programVersionString));
639         }
640 
641         return programVersion;
642     }
643 
644     /**
645      * Private constructor to prevent from instantiation.
646      *
647      * @since 1.3.0
648      */
649     private ProgramVersionUtils() {
650         super();
651     }
652 }