forked from cloudfoundry/java-buildpack
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtokenized_version.rb
178 lines (139 loc) · 5.68 KB
/
tokenized_version.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
# frozen_string_literal: true
# Cloud Foundry Java Buildpack
# Copyright 2013-2020 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require 'java_buildpack/util'
module JavaBuildpack
module Util
# A utility for manipulating JRE version numbers.
class TokenizedVersion < Array
include Comparable
# The wildcard component.
WILDCARD = '+'
# Create a tokenized version based on the input string.
#
# @param [String] version a version string
# @param [Boolean] allow_wildcards whether or not to allow '+' as the last component to represent a wildcard
def initialize(version, allow_wildcards = true)
@version = version
@version = WILDCARD if !@version && allow_wildcards
major, tail = major_or_minor_and_tail @version
minor, tail = major_or_minor_and_tail tail
micro, qualifier = micro_and_qualifier tail
concat [major, minor, micro, qualifier]
validate allow_wildcards
end
# Compare this to another array
#
# @return [Integer] A numerical representation of the comparison between two instances
def <=>(other)
comparison = 0
i = 0
while comparison.zero? && i < 3
comparison = self[i].to_i <=> other[i].to_i
i += 1
end
comparison = qualifier_compare(non_nil_qualifier(self[3]), non_nil_qualifier(other[3])) if comparison.zero?
comparison
end
# Convert this to a string
#
# @return [String] a string representation of this tokenized version
def to_s
@version
end
# Check that this version has at most the given number of components.
#
# @param [Integer] maximum_components the maximum number of components this version is allowed to have
# @raise if this version has more than the given number of components
def check_size(maximum_components)
raise "Malformed version #{self}: too many version components" if self[maximum_components]
end
private
COLLATING_SEQUENCE = (['-', '.'] + ('a'..'z').to_a + ('A'..'Z').to_a + ('0'..'9').to_a).freeze
private_constant :COLLATING_SEQUENCE
def char_compare(c1, c2)
COLLATING_SEQUENCE.index(c1) <=> COLLATING_SEQUENCE.index(c2)
end
def major_or_minor_and_tail(s)
if s.nil? || s.empty?
major_or_minor = nil
tail = nil
else
raise "Invalid version '#{s}': must not end in '.'" if s[-1] == '.'
raise "Invalid version '#{s}': missing component" if s =~ /\.[._]/
tokens = s.match(/^([^.]+)(?:\.(.*))?/)
# disable as we still have to support Ruby 2.5, remove & update when we drop Bionic support
# rubocop:disable Style/SlicingWithRange
major_or_minor, tail = tokens[1..-1]
# rubocop:enable Style/SlicingWithRange
raise "Invalid major or minor version '#{major_or_minor}'" unless valid_major_minor_or_micro major_or_minor
end
[major_or_minor, tail]
end
def micro_and_qualifier(s)
if s.nil? || s.empty?
micro = nil
qualifier = nil
else
raise "Invalid version '#{s}': must not end in '_'" if s[-1] == '_'
tokens = s.match(/^([^_]+)(?:_(.*))?/)
# disable as we still have to support Ruby 2.5, remove & update when we drop Bionic support
# rubocop:disable Style/SlicingWithRange
micro, qualifier = tokens[1..-1]
# rubocop:enable Style/SlicingWithRange
raise "Invalid micro version '#{micro}'" unless valid_major_minor_or_micro micro
raise "Invalid qualifier '#{qualifier}'" unless valid_qualifier qualifier
end
[micro, qualifier]
end
def minimum_qualifier_length(a, b)
[a.length, b.length].min
end
def qualifier_compare(a, b)
comparison = a[/^\d+/].to_i <=> b[/^\d+/].to_i
i = 0
until comparison.nonzero? || i == minimum_qualifier_length(a, b)
comparison = char_compare(a[i], b[i])
i += 1
end
comparison = a.length <=> b.length if comparison.zero?
comparison
end
def non_nil_qualifier(qualifier)
qualifier.nil? ? '' : qualifier
end
def validate(allow_wildcards)
wildcarded = false
each do |value|
if ends_with_wildcard(value) && !allow_wildcards
raise "Invalid version '#{@version}': wildcards are not allowed this context"
end
raise "Invalid version '#{@version}': no characters are allowed after a wildcard" if wildcarded && value
wildcarded = true if ends_with_wildcard(value)
end
raise "Invalid version '#{@version}': missing component" if !wildcarded && compact.length < 3
end
def ends_with_wildcard(value)
!value.nil? && value.end_with?(WILDCARD)
end
def valid_major_minor_or_micro(major_minor_or_micro)
major_minor_or_micro =~ /^\d*$/ || major_minor_or_micro =~ /^\+$/
end
def valid_qualifier(qualifier)
qualifier.nil? || qualifier.empty? || qualifier =~ /^[-.a-zA-Z\d]*\+?$/
end
end
end
end