View Javadoc
1   /**
2    * This file Copyright (c) 2003-2018 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
32   *
33   */
34  package info.magnolia.module.model;
35  
36  import java.util.regex.Pattern;
37  
38  /**
39   * Represents a module version. Format is x.y.z-classifier. y,z and classifier are
40   * optional. The classifier string is ignored in version comparisons.
41   */
42  public class Version {
43      private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(Version.class);
44  
45      public static final Version UNDEFINED_FROM = new UndefinedEarlierVersion();
46      public static final Version UNDEFINED_TO = new UndefinedLaterVersion();
47      public static final Version UNDEFINED_DEVELOPMENT_VERSION = new UndefinedDevelopmentVersion();
48  
49      private static final Pattern classifierValidation = Pattern.compile("[A-Za-z0-9-_]+");
50      private final short major;
51      private final short minor;
52      private final short patch;
53      private final String classifier;
54  
55      /**
56       * Convenience constructor that could be used to register Deltas or update tasks.
57       */
58      protected Version(int major, int minor, int patch) {
59          this.major = (short) major;
60          this.minor = (short) minor;
61          this.patch = (short) patch;
62          this.classifier = null;
63      }
64  
65      private Version(String versionStr) {
66          final String numbers;
67          final int classifierIdx = versionStr.indexOf('-');
68          if (classifierIdx > 0) {
69              classifier = versionStr.substring(classifierIdx + 1);
70              if (!classifierValidation.matcher(classifier).matches()) {
71                  throw new IllegalArgumentException("Invalid classifier: \"" + classifier + "\" in version \"" + versionStr + "\"");
72              }
73              numbers = versionStr.substring(0, classifierIdx);
74          } else {
75              classifier = null;
76              numbers = versionStr;
77          }
78  
79          final String[] strings = numbers.split("\\.", -1);
80          if (strings.length > 0) {
81              major = getShortFor("major revision", versionStr, strings[0]);
82          } else {
83              major = getShortFor("major revision", versionStr, versionStr);
84          }
85          if (strings.length > 1) {
86              minor = getShortFor("minor revision", versionStr, strings[1]);
87          } else {
88              minor = 0;
89          }
90          if (strings.length > 2) {
91              patch = getShortFor("patch revision", versionStr, strings[2]);
92          } else {
93              patch = 0;
94          }
95      }
96  
97      /**
98       * Factory method that will parse a version string and return the correct Version implementation.
99       *
100      * @param versionStr version as string, for example <code>1.2.3-test</code>. The String
101      * <code>${project.version}</code> is interpreted as an undefined version during development ant it will always
102      * match version ranges
103      * @return a Version implementation, never null
104      */
105     public static Version parseVersion(String versionStr) {
106 
107         versionStr = versionStr.trim();
108 
109         log.debug("parsing version [{}]", versionStr);
110 
111         if (UndefinedDevelopmentVersion.isDevelopmentVersion(versionStr)) {
112             // development mode.
113             return UNDEFINED_DEVELOPMENT_VERSION;
114         }
115 
116         return new Version(versionStr);
117     }
118 
119     public static Version parseVersion(int major, int minor, int patch) {
120         return new Version(major, minor, patch);
121     }
122 
123     /**
124      * Compares major, minor and patch revisions of this Version against the given Version.
125      * Classifier is ignored.
126      */
127     public boolean isEquivalent(final Version other) {
128         if (other == UNDEFINED_DEVELOPMENT_VERSION) {
129             return true;
130         }
131         return this.getMajor() == other.getMajor() &&
132                 this.getMinor() == other.getMinor() &&
133                 this.getPatch() == other.getPatch();
134     }
135 
136     public boolean isStrictlyAfter(final Version other) {
137         if (isEquivalent(other)) {
138             return false;
139         }
140         if (this.getMajor() != other.getMajor()) {
141             return this.getMajor() > other.getMajor();
142         }
143         if (this.getMinor() != other.getMinor()) {
144             return this.getMinor() > other.getMinor();
145         }
146         if (this.getPatch() != other.getPatch()) {
147             return this.getPatch() > other.getPatch();
148         }
149         return false;
150     }
151 
152     public boolean isBeforeOrEquivalent(final Version other) {
153         return !isStrictlyAfter(other);
154     }
155 
156     public short getMajor() {
157         return major;
158     }
159 
160     public short getMinor() {
161         return minor;
162     }
163 
164     public short getPatch() {
165         return patch;
166     }
167 
168     public String getClassifier() {
169         return classifier;
170     }
171 
172     @Override
173     public String toString() {
174         return major + "." + minor + "." + patch + (classifier != null ? "-" + classifier : "");
175     }
176 
177     private short getShortFor(String message, String versionStr, String input) {
178         try {
179             return Short.parseShort(input);
180         } catch (NumberFormatException e) {
181             throw new RuntimeException("Invalid " + message + ": \"" + input + "\" in version \"" + versionStr + "\"");
182         }
183     }
184 
185     private static final class UndefinedLaterVersion extends Version {
186         public UndefinedLaterVersion() {
187             super(Short.MAX_VALUE, Short.MAX_VALUE, Short.MAX_VALUE);
188         }
189 
190         @Override
191         public String toString() {
192             return "*";
193         }
194     }
195 
196     private static final class UndefinedEarlierVersion extends Version {
197         public UndefinedEarlierVersion() {
198             super(Short.MIN_VALUE, Short.MIN_VALUE, Short.MIN_VALUE);
199         }
200 
201         @Override
202         public String toString() {
203             return "*";
204         }
205     }
206 
207     /**
208      * A undefined developer version being always equivalent to other versions. Any version in the module xml starting
209      * with <code>${</code> like <code>${project.version}</code> or <code>${buildNumber}</code>
210      */
211     static final class UndefinedDevelopmentVersion extends Version {
212 
213         @Deprecated
214         static final String KEY = "${project.version}";
215 
216         public UndefinedDevelopmentVersion() {
217             super(0, 0, 0);
218         }
219 
220         @Override
221         public boolean isEquivalent(Version other) {
222             return true;
223         }
224 
225         @Override
226         public String toString() {
227             return KEY;
228         }
229 
230         public static boolean isDevelopmentVersion(String version) {
231             return version != null && version.startsWith("${");
232         }
233 
234     }
235 
236     // generated methods:
237     @Override
238     public boolean equals(Object o) {
239         if (this == o) {
240             return true;
241         }
242         if (o == null || getClass() != o.getClass()) {
243             return false;
244         }
245 
246         Version/../../../info/magnolia/module/model/Version.html#Version">Version version = (Version) o;
247 
248         if (major != version.major) {
249             return false;
250         }
251         if (minor != version.minor) {
252             return false;
253         }
254         if (patch != version.patch) {
255             return false;
256         }
257         if (classifier != null ? !classifier.equals(version.classifier) : version.classifier != null) {
258             return false;
259         }
260 
261         return true;
262     }
263 
264     @Override
265     public int hashCode() {
266         int result;
267         result = major;
268         result = 31 * result + minor;
269         result = 31 * result + patch;
270         result = 31 * result + (classifier != null ? classifier.hashCode() : 0);
271         return result;
272     }
273 }