PageRenderTime 77ms CodeModel.GetById 27ms RepoModel.GetById 1ms app.codeStats 0ms

/Modules/Shared/Sources/Versions/ProjectVersion.cs

http://vcb.codeplex.com
C# | 1220 lines | 667 code | 94 blank | 459 comment | 234 complexity | 186246e172280ac33625b5d5929542a5 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. /*
  2. * Filename: ProjectVersion.cs
  3. * Product: Versioning Controlled Build
  4. * Solution: BuildAutoIncrement
  5. * Project: Shared
  6. * Description: Wrapper arround version.
  7. * Copyright: Julijan Šribar, 2004-2007
  8. *
  9. * This software is provided 'as-is', without any express or implied
  10. * warranty. In no event will the author(s) be held liable for any damages
  11. * arising from the use of this software.
  12. *
  13. * Permission is granted to anyone to use this software for any purpose,
  14. * including commercial applications, and to alter it and redistribute it
  15. * freely, subject to the following restrictions:
  16. *
  17. * 1. The origin of this software must not be misrepresented; you must not
  18. * claim that you wrote the original software. If you use this software
  19. * in a product, an acknowledgment in the product documentation would be
  20. * appreciated but is not required.
  21. * 2. Altered source versions must be plainly marked as such, and must not be
  22. * misrepresented as being the original software.
  23. * 3. This notice may not be removed or altered from any source distribution.
  24. */
  25. using System;
  26. using System.Collections;
  27. using System.Collections.Specialized;
  28. using System.Diagnostics;
  29. using System.Resources;
  30. using System.Text;
  31. using System.Text.RegularExpressions;
  32. namespace BuildAutoIncrement {
  33. /// <summary>
  34. /// <c>ProjectVersion</c> is a wrapper arround <c>Version</c> class
  35. /// adding increment functionality and supporting <c>Empty</c> (i.e.
  36. /// not defined) version.
  37. /// </summary>
  38. public class ProjectVersion : IComparable, ICloneable {
  39. public enum VersionComponent {
  40. Major,
  41. Minor,
  42. Build,
  43. Revision
  44. }
  45. #region Constructors
  46. /// <summary>
  47. /// Initializes a new instance of the <c>ProjectVersion</c> class
  48. /// with a non-defined (empty) version.
  49. /// </summary>
  50. private ProjectVersion() {
  51. m_version = new ListDictionary();
  52. Valid = true;
  53. }
  54. private ProjectVersion(bool valid) : this() {
  55. Valid = valid;
  56. }
  57. /// <summary>
  58. /// Initializes a new instance of the <c>ProjectVersion</c> class using
  59. /// the value represented by the specified <c>String</c>.
  60. /// </summary>
  61. /// <param name="version">
  62. /// Version string of the form: [Major].[Minor].[Build].[Revision].
  63. /// Major and Minor components are obligatory, while Build and
  64. /// Revison may be omitted or substituted by a single or two
  65. /// asterisks.
  66. /// </param>
  67. protected ProjectVersion(string version) : this() {
  68. m_originalString = version;
  69. SplitComponents(version);
  70. }
  71. public ProjectVersion(string version, AssemblyVersionType versionType) : this() {
  72. m_originalString = version;
  73. if (version.IndexOf(',') != -1) {
  74. version = version.Replace(',', '.');
  75. }
  76. Valid = IsValidVersionString(version, versionType, ProjectType.CSharpProject);
  77. if (Valid)
  78. SplitComponents(version);
  79. }
  80. /// <summary>
  81. /// Initializes a new instance of the <c>ProjectVersion</c> class
  82. /// with the specified major, minor, build, and revision numbers.
  83. /// </summary>
  84. /// <param name="major">
  85. /// The major version number.
  86. /// </param>
  87. /// <param name="minor">
  88. /// The minor version number.
  89. /// </param>
  90. /// <param name="build">
  91. /// The build number.
  92. /// </param>
  93. /// <param name="revision">
  94. /// The revision number.
  95. /// </param>
  96. private ProjectVersion(int major, int minor, int build, int revision) : this(major, minor, build) {
  97. m_version[VersionComponent.Revision] = revision;
  98. }
  99. /// <summary>
  100. /// Initializes a new instance of the <c>ProjectVersion</c> class
  101. /// with the specified major, minor and build numbers.
  102. /// </summary>
  103. /// <param name="major">
  104. /// The major version number.
  105. /// </param>
  106. /// <param name="minor">
  107. /// The minor version number.
  108. /// </param>
  109. /// <param name="build">
  110. /// The build number.
  111. /// </param>
  112. private ProjectVersion(int major, int minor, int build) : this(major, minor) {
  113. m_version[VersionComponent.Build] = build;
  114. }
  115. /// <summary>
  116. /// Initializes a new instance of the <c>ProjectVersion</c> class
  117. /// with the specified major and minor numbers.
  118. /// </summary>
  119. /// <param name="major">
  120. /// The major version number.
  121. /// </param>
  122. /// <param name="minor">
  123. /// The minor version number.
  124. /// </param>
  125. private ProjectVersion(int major, int minor) : this() {
  126. m_version[VersionComponent.Major] = major;
  127. m_version[VersionComponent.Minor] = minor;
  128. }
  129. /// <summary>
  130. /// Copy constructor.
  131. /// </summary>
  132. /// <param name="version">
  133. /// <c>ProjectVersion</c> to copy.
  134. /// </param>
  135. private ProjectVersion(ProjectVersion version) : this() {
  136. foreach (VersionComponent vc in version.Version.Keys) {
  137. m_version[vc] = (int)version.Version[vc];
  138. }
  139. Valid = version.Valid;
  140. m_originalString = version.m_originalString;
  141. }
  142. #endregion // Constructors
  143. #region IComparable interface implementation
  144. /// <summary>
  145. /// IComparable interface implementation.
  146. /// </summary>
  147. /// <param name="obj"></param>
  148. /// <returns></returns>
  149. int IComparable.CompareTo(object obj) {
  150. return CompareTo((ProjectVersion)obj);
  151. }
  152. /// <summary>
  153. /// Compares this instance with another one.
  154. /// </summary>
  155. /// <param name="pattern">
  156. /// <c>ProjectVersion</c> to compare to.
  157. /// </param>
  158. /// <returns>
  159. /// 0 if versions are equal, negative number if this instance
  160. /// is a lower version or positive number if this instance is a
  161. /// higher version than the version provided.
  162. /// </returns>
  163. public int CompareTo(ProjectVersion other) {
  164. int[] vc1 = ComparableComponents;
  165. int[] vc2 = other.ComparableComponents;
  166. for (int i = 0; i < 4; i++) {
  167. if (vc1[i] < vc2[i]) {
  168. return -1;
  169. }
  170. if (vc1[i] > vc2[i]) {
  171. return +1;
  172. }
  173. }
  174. return 0;
  175. }
  176. /// <summary>
  177. /// Compares this version with a string presentation of another
  178. /// instance.
  179. /// </summary>
  180. /// <param name="other">
  181. /// String presentation of the <c>ProjectVersion</c> to compare to.
  182. /// </param>
  183. /// <returns>
  184. /// 0 if versions are equal, negative number if this instance
  185. /// is a lower version or positive number if this instance is a
  186. /// higher version than the version provided.
  187. /// </returns>
  188. public int CompareTo(string other) {
  189. ProjectVersion otherVersion = new ProjectVersion(other);
  190. return CompareTo(otherVersion);
  191. }
  192. /// <summary>
  193. /// Checks if version provided is higher than the crrent instance. In
  194. /// contrast to <c>CompareTo</c> methods, for a version component
  195. /// containing asterisk, the pattern provided is assumed to be higher.
  196. /// </summary>
  197. /// <param name="pattern">
  198. /// String presentation of a version.
  199. /// </param>
  200. /// <returns>
  201. /// <c>true</c> if pattern provided is higher version.
  202. /// </returns>
  203. public bool IsStringPatternHigher(string pattern) {
  204. ProjectVersion other = new ProjectVersion(pattern);
  205. int[] vc1 = ComparableComponents;
  206. int[] vc2 = other.ComparableComponents;
  207. for (int i = 0; i < 4; i++) {
  208. if (vc1[i] != vc2[i])
  209. return vc2[i] > vc1[i];
  210. // if component is asterisk, then string pattern will be higher
  211. if (vc1[i] == MaxVersion && vc2[i] == MaxVersion) {
  212. return true;
  213. }
  214. }
  215. return false;
  216. }
  217. /// <summary>
  218. /// Compares this version with a pattern provided.
  219. /// </summary>
  220. /// <param name="pattern">
  221. /// String pattern to compare to. Pattern may contain asterisk ('*')
  222. /// and '+' characters which are treated as wildcards. For '*'
  223. /// wildcard corresponding version component is assumed to be equal.
  224. /// For '+' version component is assumed to be lower.
  225. /// </param>
  226. /// <returns>
  227. /// 0 if versions are equal, negative non-zero integer if this version
  228. /// is lower, positive non-zero integer if this version is higher.
  229. /// </returns>
  230. public int CompareToPattern(string pattern) {
  231. Debug.Assert(pattern != null && pattern.Length > 0);
  232. string[] splitPattern = pattern.Split('.');
  233. Debug.Assert(splitPattern.Length == 4);
  234. int[] components = ComparableComponents;
  235. for (int i = 0; i < 4; i++) {
  236. if (splitPattern[i] == "+")
  237. return -1;
  238. if (splitPattern[i] != "*") {
  239. int patternComponent = int.Parse(splitPattern[i]);
  240. if (components[i] < patternComponent)
  241. return -1;
  242. if (components[i] > patternComponent)
  243. return +1;
  244. }
  245. }
  246. return 0;
  247. }
  248. #endregion // IComparable interface implementation
  249. #region ICloneable interface implementation
  250. object ICloneable.Clone() {
  251. return Clone();
  252. }
  253. public ProjectVersion Clone() {
  254. return new ProjectVersion(this);
  255. }
  256. /// <summary>
  257. /// Clones this version assigning provided Build and Revision values.
  258. /// </summary>
  259. /// <param name="build">
  260. /// Build to assign.
  261. /// </param>
  262. /// <param name="revision">
  263. /// Revision to assign.
  264. /// </param>
  265. /// <returns>
  266. /// New <c>ProjectVersion</c> object.
  267. /// </returns>
  268. public ProjectVersion Clone(int build, int revision, bool createRevision) {
  269. Debug.Assert(m_version.Count >= 2);
  270. if (m_version.Count == 2)
  271. return new ProjectVersion(this[VersionComponent.Major], this[VersionComponent.Minor]);
  272. if (m_version.Count == 4 || (this[VersionComponent.Build] == MaxVersion && createRevision))
  273. return new ProjectVersion(this[VersionComponent.Major], this[VersionComponent.Minor], build, revision);
  274. return new ProjectVersion(this[VersionComponent.Major], this[VersionComponent.Minor], build);
  275. }
  276. #endregion // ICloneable interface implementation
  277. /// <summary>
  278. /// Maximum version for a component.
  279. /// </summary>
  280. public const int MaxVersion = UInt16.MaxValue;
  281. #region Public methods
  282. /// <summary>
  283. /// Increments the version according to numbering scheme defined in
  284. /// configuration.
  285. /// </summary>
  286. /// <param name="numberingOptions">
  287. /// <c>NumberingOptions</c> that define which component of the version
  288. /// should be incremented.
  289. /// </param>
  290. public void Increment(NumberingOptions numberingOptions) {
  291. switch (numberingOptions.IncrementScheme) {
  292. case IncrementScheme.IncrementMajorVersion:
  293. IncrementComponent(VersionComponent.Major, numberingOptions);
  294. break;
  295. case IncrementScheme.IncrementMinorVersion:
  296. IncrementComponent(VersionComponent.Minor, numberingOptions);
  297. break;
  298. case IncrementScheme.IncrementBuild:
  299. IncrementComponent(VersionComponent.Build, numberingOptions);
  300. break;
  301. case IncrementScheme.IncrementRevision:
  302. IncrementComponent(VersionComponent.Revision, numberingOptions);
  303. break;
  304. }
  305. }
  306. /// <summary>
  307. /// Increments the version using configuration settings. Numerical
  308. /// overflow increments the higher component.
  309. /// </summary>
  310. /// <param name="toIncrement">
  311. /// <c>VersionComponent</c> to increment.
  312. /// </param>
  313. /// <param name="numberingOptions">
  314. /// <c>NumberingOptions</c> which defines the scheme for increment.
  315. /// </param>
  316. public void IncrementComponent(VersionComponent toIncrement, NumberingOptions numberingOptions) {
  317. if (numberingOptions.UseDateTimeBasedBuildAndRevisionNumbering)
  318. IncrementUsingDateTimeBasedBuildAndRevisionNumbering(toIncrement, numberingOptions);
  319. else
  320. IncrementVersionStandard(toIncrement, numberingOptions);
  321. }
  322. /// <summary>
  323. /// Checks if version contains a wildcard ('*') character.
  324. /// </summary>
  325. /// <returns>
  326. /// Returns <c>true</c> if there is a wildcard character.
  327. /// </returns>
  328. public bool ContainsWildCard() {
  329. if (Version[VersionComponent.Build] != null && this[VersionComponent.Build] == MaxVersion)
  330. return true;
  331. if (Version[VersionComponent.Revision] != null && this[VersionComponent.Revision] == MaxVersion)
  332. return true;
  333. return false;
  334. }
  335. #endregion // Public methods
  336. #region Public overrides
  337. /// <summary>
  338. /// Converts the value of this instance to its equivalent <c>String</c>
  339. /// representation.
  340. /// </summary>
  341. /// <returns>
  342. /// The string representation of the values of the major, minor,
  343. /// build, and revision components of this instance.
  344. /// </returns>
  345. public override string ToString() {
  346. return ToString(false);
  347. }
  348. /// <summary>
  349. /// Converts the value of this instance to its equivalent <c>String</c>
  350. /// representation.
  351. /// </summary>
  352. /// <param name="displayAllComponents">
  353. /// Flag indicating if all versions (including Build and Revision)
  354. /// should be included. If this flag is set to <c>true</c> and Build
  355. /// and/or Revision are missing, they are substituted by asterisk.
  356. /// </param>
  357. /// <returns>
  358. /// The string representation of the values of the major, minor,
  359. /// build, and revision components of this instance.
  360. /// </returns>
  361. public string ToString(bool displayAllComponents) {
  362. if (this == ProjectVersion.Empty)
  363. return m_originalString;
  364. StringBuilder result = new StringBuilder();
  365. result.AppendFormat("{0}.{1}", (int)Version[VersionComponent.Major], (int)Version[VersionComponent.Minor]);
  366. bool asteriskInput = false;
  367. if (Version[VersionComponent.Build] != null) {
  368. int build = (int)Version[VersionComponent.Build];
  369. if (build < MaxVersion) {
  370. Debug.Assert(build >= 0);
  371. result.AppendFormat(".{0}", build);
  372. }
  373. else {
  374. result.Append(".*");
  375. asteriskInput = true;
  376. }
  377. }
  378. else if (displayAllComponents) {
  379. result.Append(".*");
  380. }
  381. if (Version[VersionComponent.Revision] != null) {
  382. int revision = (int)Version[VersionComponent.Revision];
  383. if (revision < MaxVersion) {
  384. Debug.Assert(revision >= 0);
  385. result.AppendFormat(".{0}", revision);
  386. }
  387. else if (!asteriskInput) {
  388. result.Append(".*");
  389. }
  390. }
  391. else if (displayAllComponents) {
  392. result.Append(".*");
  393. }
  394. return result.ToString();
  395. }
  396. /// <summary>
  397. /// Returns a value indicating whether this instance is equal to a
  398. /// specified object.
  399. /// </summary>
  400. /// <param name="obj">
  401. /// An object to compare with this instance, or a null reference.
  402. /// </param>
  403. /// <returns>
  404. /// <c>true</c> if this instance and <c>obj</c> are both
  405. /// <c>ProjectVersion</c> objects, and every component of this
  406. /// instance matches the corresponding component of <c>obj</c>;
  407. /// otherwise, <c>false</c>.
  408. /// </returns>
  409. public override bool Equals(object obj) {
  410. if (obj == null || GetType() != obj.GetType())
  411. return false;
  412. ProjectVersion otherPv = (ProjectVersion)obj;
  413. return CompareTo(otherPv) == 0;
  414. }
  415. /// <summary>
  416. /// Returns a hash code for this instance.
  417. /// </summary>
  418. /// <returns>
  419. /// A 32-bit signed integer hash code.
  420. /// </returns>
  421. public override int GetHashCode() {
  422. return 0;
  423. }
  424. #endregion Public overrides
  425. #region Private properties
  426. /// <summary>
  427. /// Gets the internal <c>Version</c> list.
  428. /// </summary>
  429. private ListDictionary Version {
  430. get { return m_version; }
  431. }
  432. private int this[VersionComponent component] {
  433. get {
  434. return (int)Version[component];
  435. }
  436. }
  437. #endregion // Private properties
  438. #region Private methods
  439. /// <summary>
  440. /// Splits version components.
  441. /// </summary>
  442. /// <param name="version"></param>
  443. private void SplitComponents(string version) {
  444. string[] splitVersion = version.Split('.');
  445. Debug.Assert(splitVersion.Length > 1);
  446. m_version[VersionComponent.Major] = int.Parse(splitVersion[0]);
  447. if (splitVersion.Length > 1)
  448. m_version[VersionComponent.Minor] = int.Parse(splitVersion[1]);
  449. if (splitVersion.Length > 2)
  450. m_version[VersionComponent.Build] = splitVersion[2] == "*" ? MaxVersion : int.Parse(splitVersion[2]);
  451. if (splitVersion.Length > 3)
  452. m_version[VersionComponent.Revision] = splitVersion[3] == "*" ? MaxVersion : int.Parse(splitVersion[3]);
  453. }
  454. /// <summary>
  455. /// Increments version.
  456. /// </summary>
  457. /// <param name="toIncrement">
  458. /// Version component to increment.
  459. /// </param>
  460. /// <param name="numberingOptions">
  461. /// Numbering options from the configuration.
  462. /// </param>
  463. private void IncrementVersionStandard(VersionComponent toIncrement, NumberingOptions numberingOptions) {
  464. Debug.Assert(!numberingOptions.UseDateTimeBasedBuildAndRevisionNumbering);
  465. CreateMissingBuildAndRevision(numberingOptions);
  466. int incrementStep = numberingOptions.IncrementBy;
  467. bool overflow = false;
  468. if (toIncrement == VersionComponent.Revision) {
  469. // if Revision exists and is not asterisk then increment it
  470. if (Version[VersionComponent.Revision] != null && (int)Version[VersionComponent.Revision] != MaxVersion) {
  471. overflow = true;
  472. Version[VersionComponent.Revision] = IncrementIntWithOverflow(this[VersionComponent.Revision], incrementStep, ref overflow, (int)numberingOptions.ResetBuildAndRevisionTo);
  473. }
  474. }
  475. if (toIncrement == VersionComponent.Build) {
  476. overflow = true;
  477. if (numberingOptions.ResetRevisionOnBuildIncrement && Version[VersionComponent.Revision] != null)
  478. Version[VersionComponent.Revision] = (int)numberingOptions.ResetBuildAndRevisionTo;
  479. }
  480. if (overflow) {
  481. // if Build exists and is not asterisk, then increment it
  482. if ((Version[VersionComponent.Build] != null) && (int)Version[VersionComponent.Build] != MaxVersion) {
  483. Version[VersionComponent.Build] = IncrementIntWithOverflow((int)Version[VersionComponent.Build], incrementStep, ref overflow, (int)numberingOptions.ResetBuildAndRevisionTo);
  484. }
  485. else {
  486. overflow = false;
  487. }
  488. }
  489. if (toIncrement == VersionComponent.Minor) {
  490. overflow = true;
  491. if (numberingOptions.ResetBuildOnMinorIncrement && Version[VersionComponent.Build] != null)
  492. Version[VersionComponent.Build] = (int)numberingOptions.ResetBuildAndRevisionTo;
  493. if (numberingOptions.ResetRevisionOnMinorIncrement && Version[VersionComponent.Revision] != null)
  494. Version[VersionComponent.Revision] = (int)numberingOptions.ResetBuildAndRevisionTo;
  495. }
  496. Version[VersionComponent.Minor] = IncrementIntWithOverflow((int)Version[VersionComponent.Minor], incrementStep, ref overflow, 0);
  497. if (toIncrement == VersionComponent.Major) {
  498. overflow = true;
  499. // incrementing Major will automatically reset Minor
  500. Version[VersionComponent.Minor] = 0;
  501. if (numberingOptions.ResetBuildOnMinorIncrement && Version[VersionComponent.Build] != null)
  502. Version[VersionComponent.Build] = (int)numberingOptions.ResetBuildAndRevisionTo;
  503. if (numberingOptions.ResetRevisionOnMinorIncrement && Version[VersionComponent.Revision] != null)
  504. Version[VersionComponent.Revision] = (int)numberingOptions.ResetBuildAndRevisionTo;
  505. }
  506. Version[VersionComponent.Major] = IncrementIntWithOverflow((int)Version[VersionComponent.Major], incrementStep, ref overflow, 0);
  507. if (overflow) {
  508. throw new VersionOverflowException(VersionComponent.Major);
  509. }
  510. }
  511. /// <summary>
  512. /// Creates missing Build and Revision components. Called before
  513. /// version increment in order to supply missing components.
  514. /// </summary>
  515. /// <param name="toIncrement">
  516. /// <c>VersionComponent</c> that is going to be incremented.
  517. /// </param>
  518. /// <param name="numberingOptions">
  519. /// Numbering options.
  520. /// </param>
  521. private void CreateMissingBuildAndRevision(NumberingOptions numberingOptions) {
  522. if (numberingOptions.ReplaceAsteriskWithVersionComponents) {
  523. if ((Version[VersionComponent.Revision] == null && Version[VersionComponent.Build] != null && (int)Version[VersionComponent.Build] == MaxVersion)
  524. || (Version[VersionComponent.Revision] != null && (int)Version[VersionComponent.Revision] == MaxVersion)) {
  525. Version[VersionComponent.Revision] = (int)numberingOptions.ResetBuildAndRevisionTo;
  526. }
  527. if (Version[VersionComponent.Build] != null && (int)Version[VersionComponent.Build] == MaxVersion) {
  528. Version[VersionComponent.Build] = (int)numberingOptions.ResetBuildAndRevisionTo;
  529. }
  530. }
  531. }
  532. /// <summary>
  533. /// Increments version using date &amp; time based build/revision
  534. /// schema.
  535. /// </summary>
  536. /// <param name="toIncrement">
  537. /// Version component to increment.
  538. /// </param>
  539. /// <param name="numberingOptions">
  540. /// Numbering options from the configuration.
  541. /// </param>
  542. private void IncrementUsingDateTimeBasedBuildAndRevisionNumbering(VersionComponent toIncrement, NumberingOptions numberingOptions) {
  543. Debug.Assert(numberingOptions.UseDateTimeBasedBuildAndRevisionNumbering);
  544. int incrementStep = numberingOptions.IncrementBy;
  545. bool overflow = false;
  546. if (toIncrement == VersionComponent.Minor)
  547. overflow = true;
  548. Version[VersionComponent.Minor] = IncrementIntWithOverflow((int)Version[VersionComponent.Minor], incrementStep, ref overflow, 0);
  549. if (toIncrement == VersionComponent.Major) {
  550. overflow = true;
  551. // incrementing Major automatically resets Minor
  552. Version[VersionComponent.Minor] = 0;
  553. }
  554. Version[VersionComponent.Major] = IncrementIntWithOverflow((int)Version[VersionComponent.Major], incrementStep, ref overflow, 0);
  555. if (overflow) {
  556. throw new VersionOverflowException(VersionComponent.Major);
  557. }
  558. }
  559. /// <summary>
  560. /// Increments an integer if overflow flag is set. If incremented
  561. /// integer exceeds largest integer value, resulting value is reset
  562. /// to <c>resetValue</c> and overflow flag is set. If integer is
  563. /// negative, it is set to <c>resetValue</c>.
  564. /// </summary>
  565. /// <param name="toIncrement">
  566. /// Integer to increment.
  567. /// </param>
  568. /// <param name="overflow">
  569. /// Overflow flag.
  570. /// </param>
  571. /// <param name="resetValue">
  572. /// Value to start numbering from. Usually 0, but for Build and
  573. /// Revision it may be 1, depending on configuration settings.
  574. /// </param>
  575. /// <returns>
  576. /// Incremented value.
  577. /// </returns>
  578. private int IncrementIntWithOverflow(int toIncrement, int incrementStep, ref bool overflow, int resetValue) {
  579. if (overflow) {
  580. if (toIncrement < 0) {
  581. overflow = false;
  582. return resetValue;
  583. }
  584. toIncrement += incrementStep;
  585. // if value to increment has the largest value, then it
  586. // should be reset and overflow is forwarded to higher version
  587. if (toIncrement >= (MaxVersion)) {
  588. return resetValue;
  589. }
  590. overflow = false;
  591. }
  592. return toIncrement;
  593. }
  594. /// <summary>
  595. /// Gets an array of integers representing components, that may be
  596. /// used for version comparisons.
  597. /// </summary>
  598. private int[] ComparableComponents {
  599. get {
  600. if (Version.Count == 0)
  601. return new int[] { -1, -1, -1, -1 };
  602. int[] components = { 0, 0, 0, 0 };
  603. Debug.Assert(Version.Count >= 2 && Version.Count <= 4);
  604. components[0] = (int)Version[VersionComponent.Major];
  605. components[1] = (int)Version[VersionComponent.Minor];
  606. if (Version.Count > 2) {
  607. components[2] = (int)Version[VersionComponent.Build];
  608. if (Version.Count > 3) {
  609. components[3] = (int)Version[VersionComponent.Revision];
  610. }
  611. else if (components[2] == MaxVersion) {
  612. components[3] = MaxVersion;
  613. }
  614. else
  615. components[3] = 0;
  616. }
  617. return components;
  618. }
  619. }
  620. #endregion // Private methods
  621. #region Private fields
  622. private ListDictionary m_version;
  623. private string m_originalString = "";
  624. private static readonly string s_txtInvalidVersionString;
  625. private static readonly string s_txtInvalidVersionPattern;
  626. private static readonly string s_txtVersionMustConsistOfAtLeastNComponents;
  627. private static readonly string s_txtVersionMustConsistOfAtMostNComponents;
  628. private static readonly string s_txtMustNotContainNegativeIntegers;
  629. private static readonly string s_txtMustBeNonNegativeIntegersOrAsterisk;
  630. private static readonly string s_txtMustBeNonNegativeIntegers;
  631. private static readonly string s_txtAsteriskMustBeLast;
  632. private static readonly string s_txtNoAsteriskAllowed;
  633. private static readonly string s_txtMustBeIntegerSmallerThanMaxValue;
  634. private static readonly string s_txtVersionMustNotEndWithDot;
  635. #endregion Private fields
  636. #region Type constructor
  637. static ProjectVersion() {
  638. ResourceManager resources = new System.Resources.ResourceManager("BuildAutoIncrement.Resources.Shared", typeof(ResourceAccessor).Assembly);
  639. Debug.Assert(resources != null);
  640. s_txtInvalidVersionString = resources.GetString("Invalid version string");
  641. s_txtInvalidVersionPattern = resources.GetString("Invalid version pattern");
  642. s_txtVersionMustConsistOfAtLeastNComponents = resources.GetString("Version must consist of at least N components");
  643. s_txtVersionMustConsistOfAtMostNComponents = resources.GetString("Version must consist of at most N components");
  644. s_txtMustNotContainNegativeIntegers = resources.GetString("Version must not contain negative integers");
  645. s_txtMustBeNonNegativeIntegersOrAsterisk = resources.GetString("Version may consist of non-negative integers or a single asterisk character");
  646. s_txtMustBeNonNegativeIntegers = resources.GetString("Version may consist of non-negative integers");
  647. s_txtAsteriskMustBeLast = resources.GetString("Asterisk may appear only at the end of version string");
  648. s_txtVersionMustNotEndWithDot = resources.GetString("Version must not end with dot");
  649. s_txtNoAsteriskAllowed = resources.GetString("Asterisk not allowed");
  650. s_txtMustBeIntegerSmallerThanMaxValue = string.Format(resources.GetString("Version must be smaller than"), MaxVersion + 1);
  651. Debug.Assert(s_txtInvalidVersionString != null);
  652. Debug.Assert(s_txtInvalidVersionPattern != null);
  653. Debug.Assert(s_txtVersionMustConsistOfAtLeastNComponents != null);
  654. Debug.Assert(s_txtVersionMustConsistOfAtMostNComponents != null);
  655. Debug.Assert(s_txtMustNotContainNegativeIntegers != null);
  656. Debug.Assert(s_txtMustBeNonNegativeIntegersOrAsterisk != null);
  657. Debug.Assert(s_txtMustBeNonNegativeIntegers != null);
  658. Debug.Assert(s_txtAsteriskMustBeLast != null);
  659. Debug.Assert(s_txtVersionMustNotEndWithDot != null);
  660. Debug.Assert(s_txtNoAsteriskAllowed != null);
  661. Debug.Assert(s_txtMustBeIntegerSmallerThanMaxValue != null);
  662. }
  663. #endregion // Type constructor
  664. #region Public static properties
  665. public readonly bool Valid;
  666. public static readonly ProjectVersion Empty = new ProjectVersion();
  667. public static readonly ProjectVersion Invalid = new ProjectVersion(false);
  668. public static readonly ProjectVersion MinValue = new ProjectVersion(0, 0, 0, 0);
  669. #endregion // Public static properties
  670. #region Public static methods
  671. /// <summary>
  672. /// Applies a pattern to the version.
  673. /// </summary>
  674. /// <param name="pattern">
  675. /// Pattern to apply. Pattern may contain '*' and '+' wildcards.
  676. /// </param>
  677. /// <param name="version">
  678. /// Version onto which pattern has to be applied.
  679. /// </param>
  680. /// <param name="buildAndRevisionResetValue">
  681. /// Reset value of Build and Revision.
  682. /// </param>
  683. /// <returns>
  684. /// String with new version.
  685. /// </returns>
  686. public static string ApplyVersionPattern(string pattern, string version, int buildAndRevisionResetValue) {
  687. Debug.Assert(IsValidPattern(pattern));
  688. string[] patternSections = pattern.Split('.');
  689. // replace "+" with "+1" for consistency
  690. for (int l = 0; l < patternSections.Length; l++) {
  691. if (patternSections[l] == "+")
  692. patternSections[l] = "+1";
  693. }
  694. string[] versionSections = version.Split('.');
  695. Debug.Assert(patternSections.Length >= versionSections.Length);
  696. StringCollection result = new StringCollection();
  697. int versionLength = versionSections.Length;
  698. int i = 0;
  699. bool moreNumbersInPattern = MoreNumbersInPattern(patternSections, 0);
  700. // first apply pattern sections to existent version sections
  701. while (i < versionLength) {
  702. if (patternSections[i].StartsWith("+")) {
  703. int increment = int.Parse(patternSections[i]);
  704. try {
  705. result.Add(IncrementStringInteger(versionSections[i], increment));
  706. }
  707. catch (OverflowException) {
  708. VersionComponent vc = (VersionComponent)Enum.GetValues(typeof(VersionComponent)).GetValue(i);
  709. throw new VersionOverflowException(vc);
  710. }
  711. }
  712. else if (patternSections[i] != "*") {
  713. result.Add(patternSections[i]);
  714. }
  715. else {
  716. // update moreNumbersInPatern (only if true)
  717. if (moreNumbersInPattern) {
  718. Debug.Assert(patternSections[i] == "*");
  719. moreNumbersInPattern = MoreNumbersInPattern(patternSections, i + 1);
  720. }
  721. // if this character is asterisk and pattern contains more numbers, skip to next loop
  722. if (moreNumbersInPattern && versionSections[i] == "*") {
  723. int resetValue = i >= (int)VersionComponent.Build ? buildAndRevisionResetValue : 0;
  724. result.Add(resetValue.ToString());
  725. i++;
  726. break;
  727. }
  728. result.Add(versionSections[i]);
  729. }
  730. i++;
  731. }
  732. // extend version if ends with asterisk and pattern contains more numerical versions
  733. if (versionSections[i - 1] == "*" && moreNumbersInPattern) {
  734. int patternLength = patternSections.Length;
  735. while (i < patternLength) {
  736. if (!MoreNumbersInPattern(patternSections, i))
  737. break;
  738. int resetValue = i >= (int)VersionComponent.Build ? buildAndRevisionResetValue : 0;
  739. if (patternSections[i].StartsWith("+")) {
  740. int newValue = int.Parse(patternSections[i]) + resetValue;
  741. result.Add(newValue.ToString());
  742. }
  743. else if (patternSections[i] == "*") {
  744. result.Add(resetValue.ToString());
  745. }
  746. else {
  747. result.Add(patternSections[i]);
  748. }
  749. i++;
  750. }
  751. }
  752. // setup the output string
  753. StringBuilder sb = new StringBuilder();
  754. foreach (string s in result) {
  755. sb.Append(s);
  756. sb.Append(".");
  757. }
  758. return sb.ToString(0, sb.Length - 1);
  759. }
  760. /// <summary>
  761. /// Converts comma delimited string into dot delimited.
  762. /// </summary>
  763. /// <param name="version">String to convert</param>
  764. /// <returns>Converted string.</returns>
  765. public static string ConvertFromCommaDelimited(string version) {
  766. string[] versionSections = version.Split(',');
  767. StringBuilder result = new StringBuilder();
  768. for (int i = 0; i < versionSections.Length; i++) {
  769. result.Append(versionSections[i].Trim() + ".");
  770. }
  771. return result.ToString(0, result.Length - 1);
  772. }
  773. /// <summary>
  774. /// Class used for checking version formats.
  775. /// </summary>
  776. private class AssemblyVersionFormatProvider {
  777. public AssemblyVersionFormatProvider(AssemblyVersionType assemblyVersionType, ProjectType projectType) {
  778. m_assemblyVersionType = assemblyVersionType;
  779. m_projectType = projectType;
  780. }
  781. public int MinLength {
  782. get {
  783. switch (m_assemblyVersionType) {
  784. case AssemblyVersionType.AssemblyVersion:
  785. return 2;
  786. case AssemblyVersionType.AssemblyFileVersion:
  787. return 2;
  788. case AssemblyVersionType.AssemblyInformationalVersion:
  789. return 2;
  790. }
  791. return 2;
  792. }
  793. }
  794. public int MaxLength {
  795. get {
  796. if (m_projectType == ProjectType.SetupProject)
  797. return 3;
  798. return 4;
  799. }
  800. }
  801. public bool IsWildcardAllowed {
  802. get {
  803. switch (m_assemblyVersionType) {
  804. case AssemblyVersionType.AssemblyVersion:
  805. return true;
  806. case AssemblyVersionType.AssemblyFileVersion:
  807. return false;
  808. case AssemblyVersionType.AssemblyInformationalVersion:
  809. return false;
  810. }
  811. return false;
  812. }
  813. }
  814. AssemblyVersionType m_assemblyVersionType;
  815. ProjectType m_projectType;
  816. }
  817. /// <summary>
  818. /// Validates version string and returns description string in the
  819. /// case of invalid format. If version string is valid, returns empty
  820. /// string.
  821. /// </summary>
  822. /// <param name="version">
  823. /// Version string to validate.
  824. /// </param>
  825. /// <param name="assemblyVersionType">
  826. /// <c>AssemblyVersionType</c> to which this string corresponds.
  827. /// </param>
  828. /// <param name="projectType">
  829. /// <c>ProjectType</c> to which this string corresponds.
  830. /// </param>
  831. /// <returns>
  832. /// String with error description or empty string if version is valid.
  833. /// </returns>
  834. public static string ValidateVersionString(string version, AssemblyVersionType assemblyVersionType, ProjectType projectType) {
  835. Debug.Assert(version != null);
  836. AssemblyVersionFormatProvider avfp = new AssemblyVersionFormatProvider(assemblyVersionType, projectType);
  837. if (version.EndsWith("."))
  838. return s_txtVersionMustNotEndWithDot;
  839. string[] versionParts = version.Split('.');
  840. if (versionParts.Length < avfp.MinLength)
  841. return string.Format(s_txtVersionMustConsistOfAtLeastNComponents, avfp.MinLength);
  842. if (versionParts.Length > avfp.MaxLength)
  843. return string.Format(s_txtVersionMustConsistOfAtMostNComponents, avfp.MaxLength);
  844. for (int i = 0; i < avfp.MinLength; i++) {
  845. try {
  846. int val = int.Parse(versionParts[i]);
  847. if (val < 0)
  848. return s_txtMustNotContainNegativeIntegers;
  849. }
  850. catch {
  851. return s_txtMustBeNonNegativeIntegers;
  852. }
  853. }
  854. // if there is asterisk in Build or Revision it must be the last component in version
  855. if (avfp.IsWildcardAllowed) {
  856. if (versionParts.Length > 3 && versionParts[2] == "*")
  857. return s_txtAsteriskMustBeLast;
  858. }
  859. // flag controlling if number appears after an asterisk (asterisk
  860. // must not be followed by integer version components)
  861. for (int i = avfp.MinLength; i < versionParts.Length; i++) {
  862. if (versionParts[i] == "*") {
  863. if (!avfp.IsWildcardAllowed)
  864. return s_txtNoAsteriskAllowed;
  865. }
  866. else {
  867. int val = 0;
  868. try {
  869. val = int.Parse(versionParts[i]);
  870. }
  871. catch (FormatException) {
  872. return avfp.IsWildcardAllowed ? s_txtMustBeNonNegativeIntegersOrAsterisk : s_txtMustBeNonNegativeIntegers;
  873. }
  874. catch (OverflowException) {
  875. return s_txtMustBeIntegerSmallerThanMaxValue;
  876. }
  877. if (val < 0)
  878. return s_txtMustNotContainNegativeIntegers;
  879. if (val > MaxVersion)
  880. return s_txtMustBeIntegerSmallerThanMaxValue;
  881. }
  882. }
  883. return string.Empty;
  884. }
  885. /// <summary>
  886. /// Returns higher of two <c>ProjectVersion</c> objects provided.
  887. /// </summary>
  888. /// <param name="v1">
  889. /// First <c>ProjectVersion</c> to compare.
  890. /// </param>
  891. /// <param name="v2">
  892. /// Second <c>ProjectVersion</c> to compare.
  893. /// </param>
  894. /// <returns>
  895. /// Reference two higher <c>ProjectVersion</c> objects.
  896. /// </returns>
  897. public static ProjectVersion Max(ProjectVersion v1, ProjectVersion v2) {
  898. int[] vc1 = v1.ComparableComponents;
  899. int[] vc2 = v2.ComparableComponents;
  900. for (int i = 0; i < 4 ; i++) {
  901. if (vc1[i] != vc2[i]) {
  902. // if both aren't wildcards, return the larger
  903. if (vc1[i] != MaxVersion && vc2[i] != MaxVersion) {
  904. return vc1[i] > vc2[i] ? v1 : v2;
  905. }
  906. else {
  907. return vc1[i] != MaxVersion ? v1 : v2;
  908. }
  909. }
  910. }
  911. return v1;
  912. }
  913. /// <summary>
  914. /// Validates version string.
  915. /// </summary>
  916. /// <param name="version">
  917. /// Version string to validate.
  918. /// </param>
  919. /// <param name="assemblyVersionType">
  920. /// <c>AssemblyVersionType</c> to which this string corresponds.
  921. /// </param>
  922. /// <param name="projectType">
  923. /// <c>ProjectType</c> to which this string corresponds.
  924. /// </param>
  925. /// <returns>
  926. /// <c>true</c> if version is valid, else returns <c>false</c>.
  927. /// </returns>
  928. public static bool IsValidVersionString(string version, AssemblyVersionType assemblyVersionType, ProjectType projectType) {
  929. return ValidateVersionString(version, assemblyVersionType, projectType) == string.Empty;
  930. }
  931. /// <summary>
  932. /// Checks if pattern to be applied is valid. Pattern must consist of
  933. /// exactly four dot separated sections (Major, Minor, Build and
  934. /// Revision). Each section may consist of an integer, an integer
  935. /// preceeded by '+' character, '*' or '+' character.
  936. /// </summary>
  937. /// <param name="pattern">
  938. /// Pattern to validate.
  939. /// </param>
  940. /// <returns>
  941. /// <c>true</c> if pattern is a valid one, else returns <c>false</c>.
  942. /// </returns>
  943. public static bool IsValidPattern(string pattern) {
  944. string[] patternSections = pattern.Split('.');
  945. if (patternSections.Length != 4)
  946. return false;
  947. foreach (string section in patternSections) {
  948. switch (section) {
  949. case "*":
  950. case "+":
  951. break;
  952. default:
  953. try {
  954. long val = long.Parse(section);
  955. if (val < 0 || val >= MaxVersion)
  956. return false;
  957. }
  958. catch (Exception) {
  959. return false;
  960. }
  961. break;
  962. }
  963. }
  964. return true;
  965. }
  966. #endregion // Public static methods
  967. #region Private static methods
  968. private static string IncrementStringInteger(string integerValue, int increment) {
  969. if (integerValue == "*")
  970. return integerValue;
  971. // although Parse method may throw an exception, in this case
  972. // integerValue should be a valid string representation of integer
  973. long n = long.Parse(integerValue);
  974. n += increment;
  975. if (n >= MaxVersion)
  976. throw new OverflowException();
  977. return n.ToString();
  978. }
  979. private static bool MoreNumbersInPattern(string[] sections, int index) {
  980. for (int i = index; i < sections.Length; i++) {
  981. if (sections[i] != "*" && !sections[i].StartsWith("+")) {
  982. return true;
  983. }
  984. }
  985. return false;
  986. }
  987. #endregion Private static methods
  988. #region Operators
  989. /// <summary>
  990. /// Determines whether two specified instances of <c>ProjectVersion</c>
  991. /// are equal.
  992. /// </summary>
  993. /// <param name="v1">
  994. /// The first instance of <c>ProjectVersion</c>.
  995. /// </param>
  996. /// <param name="v2">
  997. /// The second instance of <c>ProjectVersion</c>.
  998. /// </param>
  999. /// <returns>
  1000. /// <c>true</c> if v1 equals v2; otherwise, <c>false</c>.
  1001. /// </returns>
  1002. public static bool operator ==(ProjectVersion v1, ProjectVersion v2) {
  1003. if ((object)v1 == null)
  1004. return (object)v1 == (object)v2;
  1005. return v1.Equals(v2);
  1006. }
  1007. /// <summary>
  1008. /// Determines whether two specified instances of <c>ProjectVersion</c>
  1009. /// are not equal.
  1010. /// </summary>
  1011. /// <param name="v1">
  1012. /// The first instance of <c>ProjectVersion</c>.
  1013. /// </param>
  1014. /// <param name="v2">
  1015. /// The second instance of <c>ProjectVersion</c>.
  1016. /// </param>
  1017. /// <returns>
  1018. /// <c>true</c> if v1 does not equal v2; otherwise, <c>false</c>.
  1019. /// </returns>
  1020. public static bool operator !=(ProjectVersion v1, ProjectVersion v2) {
  1021. return !(v1 == v2);
  1022. }
  1023. /// <summary>
  1024. /// Determines whether the first specified instance of <c>ProjectVersion</c>
  1025. /// is greater than the second specified instance of <c>ProjectVersion</c>.
  1026. /// </summary>
  1027. /// <param name="v1">
  1028. /// The first instance of <c>ProjectVersion</c>.
  1029. /// </param>
  1030. /// <param name="v2">
  1031. /// The second instance of <c>ProjectVersion</c>.
  1032. /// </param>
  1033. /// <returns>
  1034. /// <c>true</c> if v1 is greater than v2; otherwise, <c>false</c>.
  1035. /// </returns>
  1036. public static bool operator >(ProjectVersion v1, ProjectVersion v2) {
  1037. Debug.Assert(v1 != null);
  1038. Debug.Assert(v2 != null);
  1039. int[] vc1 = v1.ComparableComponents;
  1040. int[] vc2 = v2.ComparableComponents;
  1041. for (int i = 0; i < 4; i++) {
  1042. if (vc1[i] != vc2[i])
  1043. return vc1[i] > vc2[i];
  1044. }
  1045. return false;
  1046. }
  1047. /// <summary>
  1048. /// Determines whether the first specified instance of <c>ProjectVersion</c>
  1049. /// is greater than or equal to the second specified instance of
  1050. /// <c>ProjectVersion</c>.
  1051. /// </summary>
  1052. /// <param name="v1">
  1053. /// The first instance of <c>ProjectVersion</c>.
  1054. /// </param>
  1055. /// <param name="v2">
  1056. /// The second instance of <c>ProjectVersion</c>.
  1057. /// </param>
  1058. /// <returns>
  1059. /// <c>true</c> if v1 is greater than or equal to v2;
  1060. /// otherwise, <c>false</c>.
  1061. /// <…

Large files files are truncated, but you can click here to view the full file