View Javadoc

1   /**
2    * This file Copyright (c) 2003-2014 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   * @author gjoseph
43   * @version $Revision: $ ($Author: $)
44   */
45  public class Version {
46      private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(Version.class);
47  
48      public static final Version UNDEFINED_FROM = new UndefinedEarlierVersion();
49      public static final Version UNDEFINED_TO = new UndefinedLaterVersion();
50      public static final Version UNDEFINED_DEVELOPMENT_VERSION = new UndefinedDevelopmentVersion();
51  
52      private static final Pattern classifierValidation = Pattern.compile("[A-Za-z0-9-_]+");
53      private final short major;
54      private final short minor;
55      private final short patch;
56      private final String classifier;
57  
58      /**
59       * Convenience constructor that could be used to register Deltas or update tasks.
60       */
61      protected Version(int major, int minor, int patch) {
62          this.major = (short) major;
63          this.minor = (short) minor;
64          this.patch = (short) patch;
65          this.classifier = null;
66      }
67  
68      private Version(String versionStr) {
69          final String numbers;
70          final int classifierIdx = versionStr.indexOf('-');
71          if (classifierIdx > 0) {
72              classifier = versionStr.substring(classifierIdx + 1);
73              if (!classifierValidation.matcher(classifier).matches()) {
74                  throw new IllegalArgumentException("Invalid classifier: \"" + classifier + "\" in version \"" + versionStr + "\"");
75              }
76              numbers = versionStr.substring(0, classifierIdx);
77          } else {
78              classifier = null;
79              numbers = versionStr;
80          }
81  
82          final String[] strings = numbers.split("\\.", -1);
83          if (strings.length > 0) {
84              major = getShortFor("major revision", versionStr, strings[0]);
85          } else {
86              major = getShortFor("major revision", versionStr, versionStr);
87          }
88          if (strings.length > 1) {
89              minor = getShortFor("minor revision", versionStr, strings[1]);
90          } else {
91              minor = 0;
92          }
93          if (strings.length > 2) {
94              patch = getShortFor("patch revision", versionStr, strings[2]);
95          } else {
96              patch = 0;
97          }
98      }
99  
100     /**
101      * Factory method that will parse a version string and return the correct Version implementation.
102      * @param versionStr version as string, for example <code>1.2.3-test</code>. The String
103      * <code>${project.version}</code> is interpreted as an undefined version during development ant it will always
104      * match version ranges
105      * @return a Version implementation, never null
106      */
107     public static Version parseVersion(String versionStr) {
108 
109         versionStr = versionStr.trim();
110 
111         log.debug("parsing version [{}]", versionStr);
112 
113         if (UndefinedDevelopmentVersion.isDevelopmentVersion(versionStr)) {
114             // development mode.
115             return UNDEFINED_DEVELOPMENT_VERSION;
116         }
117 
118         return new Version(versionStr);
119     }
120 
121     public static Version parseVersion(int major, int minor, int patch) {
122         return new Version(major, minor, patch);
123     }
124 
125     /**
126      * Compares major, minor and patch revisions of this Version against the given Version.
127      * Classifier is ignored.
128      */
129     public boolean isEquivalent(final Version other) {
130         if(other == UNDEFINED_DEVELOPMENT_VERSION){
131             return true;
132         }
133         return this.getMajor() == other.getMajor() &&
134                 this.getMinor() == other.getMinor() &&
135                 this.getPatch() == other.getPatch();
136     }
137 
138     public boolean isStrictlyAfter(final Version other) {
139         if(isEquivalent(other)){
140             return false;
141         }
142         if (this.getMajor() != other.getMajor()) {
143             return this.getMajor() > other.getMajor();
144         }
145         if (this.getMinor() != other.getMinor()) {
146             return this.getMinor() > other.getMinor();
147         }
148         if (this.getPatch() != other.getPatch()) {
149             return this.getPatch() > other.getPatch();
150         }
151         return false;
152     }
153 
154     public boolean isBeforeOrEquivalent(final Version other) {
155         return !isStrictlyAfter(other);
156     }
157 
158     public short getMajor() {
159         return major;
160     }
161 
162     public short getMinor() {
163         return minor;
164     }
165 
166     public short getPatch() {
167         return patch;
168     }
169 
170     public String getClassifier() {
171         return classifier;
172     }
173 
174     @Override
175     public String toString() {
176         return major + "." + minor + "." + patch + (classifier != null ? "-" + classifier : "");
177     }
178 
179     private short getShortFor(String message, String versionStr, String input) {
180         try {
181             return Short.parseShort(input);
182         } catch (NumberFormatException e) {
183             throw new RuntimeException("Invalid " + message + ": \"" + input + "\" in version \"" + versionStr + "\"");
184         }
185     }
186 
187     private static final class UndefinedLaterVersion extends Version {
188         public UndefinedLaterVersion() {
189             super(Short.MAX_VALUE, Short.MAX_VALUE, Short.MAX_VALUE);
190         }
191 
192         @Override
193         public String toString() {
194             return "*";
195         }
196     }
197 
198     private static final class UndefinedEarlierVersion extends Version {
199         public UndefinedEarlierVersion() {
200             super(Short.MIN_VALUE, Short.MIN_VALUE, Short.MIN_VALUE);
201         }
202 
203         @Override
204         public String toString() {
205             return "*";
206         }
207     }
208 
209     /**
210      * A undefined developer version being always equivalent to other versions. Any version in the module xml starting
211      * with <code>${</code> like <code>${project.version}</code> or <code>${buildNumber}</code>
212      */
213     static final class UndefinedDevelopmentVersion extends Version {
214 
215         @Deprecated
216         static final String KEY = "${project.version}";
217 
218         public UndefinedDevelopmentVersion() {
219             super(0, 0, 0);
220         }
221 
222         @Override
223         public boolean isEquivalent(Version other) {
224             return true;
225         }
226 
227         @Override
228         public String toString() {
229             return KEY;
230         }
231 
232         public static boolean isDevelopmentVersion(String version) {
233             return version != null && version.startsWith("${");
234         }
235 
236     }
237 
238     // generated methods:
239     @Override
240     public boolean equals(Object o) {
241         if (this == o) {
242             return true;
243         }
244         if (o == null || getClass() != o.getClass()) {
245             return false;
246         }
247 
248         Version version = (Version) o;
249 
250         if (major != version.major) {
251             return false;
252         }
253         if (minor != version.minor) {
254             return false;
255         }
256         if (patch != version.patch) {
257             return false;
258         }
259         if (classifier != null ? !classifier.equals(version.classifier) : version.classifier != null) {
260             return false;
261         }
262 
263         return true;
264     }
265 
266     @Override
267     public int hashCode() {
268         int result;
269         result = major;
270         result = 31 * result + minor;
271         result = 31 * result + patch;
272         result = 31 * result + (classifier != null ? classifier.hashCode() : 0);
273         return result;
274     }
275 }