View Javadoc

1   /**
2    * This file Copyright (c) 2003-2010 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     public String toString() {
175         return major + "." + minor + "." + patch + (classifier != null ? "-" + classifier : "");
176     }
177 
178     private short getShortFor(String message, String versionStr, String input) {
179         try {
180             return Short.parseShort(input);
181         } catch (NumberFormatException e) {
182             throw new RuntimeException("Invalid " + message + ": \"" + input + "\" in version \"" + versionStr + "\"");
183         }
184     }
185 
186     private static final class UndefinedLaterVersion extends Version {
187         public UndefinedLaterVersion() {
188             super(Short.MAX_VALUE, Short.MAX_VALUE, Short.MAX_VALUE);
189         }
190 
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         public String toString() {
202             return "*";
203         }
204     }
205 
206     /**
207      * A undefined developer version being always equivalent to other versions. Any version in the module xml starting
208      * with <code>${</code> like <code>${project.version}</code> or <code>${buildNumber}</code>
209      */
210     static final class UndefinedDevelopmentVersion extends Version {
211 
212         @Deprecated
213         static final String KEY = "${project.version}";
214 
215         public UndefinedDevelopmentVersion() {
216             super(0, 0, 0);
217         }
218 
219         public boolean isEquivalent(Version other) {
220             return true;
221         }
222 
223         public String toString() {
224             return KEY;
225         }
226 
227         public static boolean isDevelopmentVersion(String version) {
228             return version != null && version.startsWith("${");
229         }
230 
231     }
232 
233     // generated methods:
234     public boolean equals(Object o) {
235         if (this == o) {
236             return true;
237         }
238         if (o == null || getClass() != o.getClass()) {
239             return false;
240         }
241 
242         Version version = (Version) o;
243 
244         if (major != version.major) {
245             return false;
246         }
247         if (minor != version.minor) {
248             return false;
249         }
250         if (patch != version.patch) {
251             return false;
252         }
253         if (classifier != null ? !classifier.equals(version.classifier) : version.classifier != null) {
254             return false;
255         }
256 
257         return true;
258     }
259 
260     public int hashCode() {
261         int result;
262         result = (int) major;
263         result = 31 * result + (int) minor;
264         result = 31 * result + (int) patch;
265         result = 31 * result + (classifier != null ? classifier.hashCode() : 0);
266         return result;
267     }
268 }