bovender framework
C# framework that implements MVVM and more
SemanticVersion.cs
1 /* SemanticVersion.cs
2  * part of Bovender
3  *
4  * Copyright 2014-2018 Daniel Kraus
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 using System;
19 using System.Collections.Generic;
20 using System.Linq;
21 using System.Text;
22 using System.IO;
23 using System.Reflection;
24 using System.Text.RegularExpressions;
25 
26 namespace Bovender.Versioning
27 {
28  #region Prerelease taxonomy enumeration
29 
30  public enum Prerelease
31  {
32  Alpha = -4,
33  Beta = -3,
34  RC = -2,
35  Numeric = -1,
36  None = 0,
37  }
38 
39  #endregion
40 
44  public class SemanticVersion : Object, IComparable
45  {
46  #region Public properties
47 
48  public int Major { get; set; }
49  public int Minor { get; set; }
50  public int Patch { get; set; }
51  public Prerelease Prerelease { get; set; }
52  public int PreMajor { get; set; }
53  public int PreMinor { get; set; }
54  public int PrePatch { get; set; }
55  public string Build { get; set; }
56 
57  #endregion
58 
59  #region Private fields
60 
61  private string _version;
62 
63  #endregion
64 
65  #region Constructor
66 
67  public SemanticVersion() { }
68 
73  public SemanticVersion(string version)
74  {
75  ParseString(version);
76  }
77 
87  public SemanticVersion(Assembly assembly)
88  {
89  var versionFile = from resources in assembly.GetManifestResourceNames()
90  where resources.EndsWith(".VERSION")
91  select resources;
92  Stream stream = assembly.GetManifestResourceStream(versionFile.First());
93  StreamReader text = new StreamReader(stream);
94  ParseString(text.ReadLine());
95  }
96 
97  #endregion
98 
99  #region Operators
100 
101  public static bool operator <(SemanticVersion lower, SemanticVersion higher)
102  {
103  return (lower.CompareTo(higher) < 0);
104  }
105 
106  public static bool operator >(SemanticVersion higher, SemanticVersion lower)
107  {
108  return (lower.CompareTo(higher) < 0);
109  }
110 
111  public static bool operator <=(SemanticVersion lower, SemanticVersion higher)
112  {
113  return (lower.CompareTo(higher) <= 0);
114  }
115 
116  public static bool operator >=(SemanticVersion higher, SemanticVersion lower)
117  {
118  return (lower.CompareTo(higher) <= 0);
119  }
120 
121  public static bool operator ==(SemanticVersion v1, SemanticVersion v2)
122  {
123  try
124  {
125  return (v1.Equals(v2));
126  }
127  catch (NullReferenceException)
128  {
129  return (object)v2 == null;
130  }
131  }
132 
133  public static bool operator !=(SemanticVersion v1, SemanticVersion v2)
134  {
135  try
136  {
137  return (!v1.Equals(v2));
138  }
139  catch (NullReferenceException)
140  {
141  return (object)v2 != null;
142  }
143  }
144 
145  #endregion
146 
147  #region Comparators
148 
149  public int CompareTo(object obj)
150  {
151  SemanticVersion other = obj as SemanticVersion;
152  if (this.Major < other.Major)
153  {
154  return -1;
155  }
156  else if (this.Major > other.Major)
157  {
158  return 1;
159  }
160  else // both major versions are the same, compare minor version
161  {
162  if (this.Minor < other.Minor)
163  {
164  return -1;
165  }
166  else if (this.Minor > other.Minor)
167  {
168  return 1;
169  }
170  else // major and minor are same, compare patch
171  {
172  if (this.Patch < other.Patch)
173  {
174  return -1;
175  }
176  else if (this.Patch > other.Patch)
177  {
178  return 1;
179  }
180  else // major, minor, and path are same, compare pre-release
181  {
182  if (this.Prerelease < other.Prerelease)
183  {
184  return -1;
185  }
186  else if (this.Prerelease > other.Prerelease)
187  {
188  return 1;
189  }
190  else // prerelease type is same (alpha/beta/etc.)
191  {
192  if (this.Prerelease == Versioning.Prerelease.Numeric)
193  {
194  if (this.PreMajor < other.PreMajor)
195  {
196  return -1;
197  }
198  else if (this.PreMajor > other.PreMajor)
199  {
200  return 1;
201  }
202  else
203  {
204  if (this.PreMinor < other.PreMinor)
205  {
206  return -1;
207  }
208  else if (this.PreMinor > other.PreMinor)
209  {
210  return 1;
211  }
212  else
213  {
214  if (this.PrePatch < other.PrePatch)
215  {
216  return -1;
217  }
218  else if (this.PrePatch> other.PrePatch)
219  {
220  return 1;
221  }
222  else
223  {
224  return 0;
225  }
226  }
227  }
228  }
229  else // prerelease type same, not numeric
230  {
231  if (this.PrePatch < other.PrePatch)
232  {
233  return -1;
234  }
235  else if (this.PrePatch > other.PrePatch)
236  {
237  return 1;
238  }
239  else
240  {
241  return 0;
242  }
243  }
244  }
245  }
246  }
247  }
248  }
249 
250  public override bool Equals(object obj)
251  {
252  if (obj != null)
253  {
254  return (this.CompareTo(obj) == 0);
255  }
256  else
257  {
258  return false;
259  }
260  }
261 
262 
263  #endregion
264 
265  #region Object overrides
266 
271  public override string ToString()
272  {
273  BuildString();
274  return _version;
275  }
276 
277  public override int GetHashCode()
278  {
279  BuildString();
280  return _version.GetHashCode();
281  }
282 
283  #endregion
284 
285  #region Internal logic
286 
291  protected void ParseString(string s)
292  {
293  Regex r = new Regex(
294  @"(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)" +
295  @"(-(?<pre>((?<preMajor>\d+)\.(?<preMinor>\d+)\.|"+
296  @"((?<alpha>alpha)|(?<beta>beta)|(?<rc>rc))\.)(?<prePatch>\d+)))?" +
297  @"(\+(?<build>[a-zA-Z0-9]+))?");
298  Match m = r.Match(s);
299 
300  if (!m.Success)
301  {
302  throw new InvalidVersionStringException(s);
303  };
304 
305  _version = s;
306  Major = Convert.ToInt32(m.Groups["major"].Value);
307  Minor = Convert.ToInt32(m.Groups["minor"].Value);
308  Patch = Convert.ToInt32(m.Groups["patch"].Value);
309 
310  if (m.Groups["pre"].Success)
311  {
312  if (m.Groups["alpha"].Success)
313  {
314  Prerelease = Versioning.Prerelease.Alpha;
315  }
316  else if (m.Groups["beta"].Success)
317  {
318  Prerelease = Versioning.Prerelease.Beta;
319  }
320  else if (m.Groups["rc"].Success)
321  {
322  Prerelease = Versioning.Prerelease.RC;
323  }
324  else
325  {
326  Prerelease = Versioning.Prerelease.Numeric;
327  PreMajor = Convert.ToInt32(m.Groups["preMajor"].Value);
328  PreMinor = Convert.ToInt32(m.Groups["preMinor"].Value);
329  }
330  }
331  else
332  {
333  Prerelease = Versioning.Prerelease.None;
334  }
335  if (m.Groups["prePatch"].Success)
336  {
337  PrePatch = Convert.ToInt32(m.Groups["prePatch"].Value);
338  };
339 
340  Build = m.Groups["build"].Value;
341  }
342 
343  protected void BuildString()
344  {
345  string s = String.Format("{0}.{1}.{2}", Major, Minor, Patch);
346  if (Prerelease != Prerelease.None)
347  {
348  if (Prerelease == Prerelease.Numeric)
349  {
350  s += String.Format("-{0}.{1}.{2}", PreMajor, PreMinor, PrePatch);
351  }
352  else
353  {
354  s += String.Format("-{0}.{1}", Prerelease.ToString().ToLower(), PrePatch);
355  }
356  }
357  if (!string.IsNullOrWhiteSpace(Build))
358  {
359  s += String.Format("+{0}", Build);
360  }
361  _version = s;
362  }
363 
364  #endregion
365  }
366 }
SemanticVersion(string version)
Instantiates the class from a given version string.
Class that handles semantic versioning.
SemanticVersion(Assembly assembly)
Creates an instance with the current version information, which must be contained in a file called "V...
override string ToString()
Returns the full version string.
void ParseString(string s)
Parses a string that complies with semantic versioning, V.