pepijnve-ivy4r 0.12.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.autotest +14 -0
- data/.gitignore +7 -0
- data/.project +17 -0
- data/.rspec +2 -0
- data/CHANGELOG +280 -0
- data/Gemfile +5 -0
- data/README.txt +124 -0
- data/Rakefile +36 -0
- data/autotest/discover.rb +1 -0
- data/ivy4r.gemspec +30 -0
- data/lib/buildr/ivy_extension.rb +808 -0
- data/lib/ivy/artifactproperty.rb +34 -0
- data/lib/ivy/artifactreport.rb +24 -0
- data/lib/ivy/buildlist.rb +37 -0
- data/lib/ivy/buildnumber.rb +34 -0
- data/lib/ivy/cachepath.rb +30 -0
- data/lib/ivy/cleancache.rb +20 -0
- data/lib/ivy/configure.rb +29 -0
- data/lib/ivy/findrevision.rb +34 -0
- data/lib/ivy/info.rb +36 -0
- data/lib/ivy/install.rb +33 -0
- data/lib/ivy/java/all_version_matcher.rb +31 -0
- data/lib/ivy/java/java_object_wrapper.rb +160 -0
- data/lib/ivy/listmodules.rb +35 -0
- data/lib/ivy/makepom.rb +30 -0
- data/lib/ivy/publish.rb +37 -0
- data/lib/ivy/report.rb +33 -0
- data/lib/ivy/resolve.rb +44 -0
- data/lib/ivy/retrieve.rb +27 -0
- data/lib/ivy/settings.rb +15 -0
- data/lib/ivy/target.rb +162 -0
- data/lib/ivy/targets.rb +50 -0
- data/lib/ivy4r.rb +252 -0
- data/lib/ivy4r/version.rb +3 -0
- data/lib/ivy4r_java_extensions.rb +42 -0
- data/lib/rake/ivy_extension.rb +349 -0
- data/spec/ivy/target_spec.rb +86 -0
- data/spec/ivy/targets_spec.rb +27 -0
- data/spec/ivy4r_spec.rb +50 -0
- data/spec/spec_helper.rb +10 -0
- data/spec_files/buildlist/p1/buildfile +0 -0
- data/spec_files/buildlist/p1/ivy.xml +7 -0
- data/spec_files/buildlist/sub/p2/buildfile +0 -0
- data/spec_files/buildlist/sub/p2/ivy.xml +11 -0
- data/spec_files/buildlist/sub/p3/buildfile +0 -0
- data/spec_files/buildlist/sub/p3/ivy.xml +11 -0
- data/spec_files/ivy.xml +18 -0
- data/spec_files/ivysettings.xml +11 -0
- data/spec_functional/ivy4r_spec.rb +131 -0
- metadata +214 -0
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'ivy/target'
|
2
|
+
|
3
|
+
module Ivy
|
4
|
+
class Artifactproperty < Ivy::Target
|
5
|
+
def parameter
|
6
|
+
[
|
7
|
+
Parameter.new(:name, true),
|
8
|
+
Parameter.new(:value, true),
|
9
|
+
Parameter.new(:conf, false),
|
10
|
+
Parameter.new(:haltonfailure, false),
|
11
|
+
Parameter.new(:validate, false),
|
12
|
+
Parameter.new(:overwrite, false),
|
13
|
+
Parameter.new(:settingsRef, false),
|
14
|
+
]
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
def before_hook
|
19
|
+
@cached_property_names = ant_properties.map {|key, value| key }
|
20
|
+
end
|
21
|
+
|
22
|
+
def after_hook
|
23
|
+
@cached_property_names = nil
|
24
|
+
end
|
25
|
+
|
26
|
+
def execute_ivy
|
27
|
+
call_nested :ivy_artifactproperty => params
|
28
|
+
end
|
29
|
+
|
30
|
+
def create_return_values
|
31
|
+
ant_properties.reject { |key, value| @cached_property_names.member? key }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'ivy/target'
|
2
|
+
|
3
|
+
module Ivy
|
4
|
+
class Artifactreport < Ivy::Target
|
5
|
+
def parameter
|
6
|
+
[
|
7
|
+
Parameter.new(:tofile, true),
|
8
|
+
Parameter.new(:pattern, false),
|
9
|
+
Parameter.new(:conf, false),
|
10
|
+
Parameter.new(:haltonfailure, false),
|
11
|
+
Parameter.new(:settingsRef, false)
|
12
|
+
]
|
13
|
+
end
|
14
|
+
|
15
|
+
protected
|
16
|
+
def execute_ivy
|
17
|
+
call_nested :ivy_artifactreport => params
|
18
|
+
end
|
19
|
+
|
20
|
+
def create_return_values
|
21
|
+
IO.read(params[:tofile])
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'ivy/target'
|
2
|
+
|
3
|
+
module Ivy
|
4
|
+
class Buildlist < Ivy::Target
|
5
|
+
def parameter
|
6
|
+
[
|
7
|
+
Parameter.new(:reference, false),
|
8
|
+
Parameter.new(:nested, true),
|
9
|
+
Parameter.new(:ivyfilepath, false),
|
10
|
+
Parameter.new(:root, false),
|
11
|
+
Parameter.new(:excluderoot, false),
|
12
|
+
Parameter.new(:leaf, false),
|
13
|
+
Parameter.new(:onlydirectdep, false),
|
14
|
+
Parameter.new(:delimiter, false),
|
15
|
+
Parameter.new(:excludeleaf, false),
|
16
|
+
Parameter.new(:haltonerror, false),
|
17
|
+
Parameter.new(:skipbuildwithoutivy, false),
|
18
|
+
Parameter.new(:onMissingDescriptor, false),
|
19
|
+
Parameter.new(:reverse, false),
|
20
|
+
Parameter.new(:restartFrom, false),
|
21
|
+
Parameter.new(:settingsRef, false)
|
22
|
+
]
|
23
|
+
end
|
24
|
+
|
25
|
+
protected
|
26
|
+
def execute_ivy
|
27
|
+
params[:reference] = "path-#{rand.to_s}" unless params[:reference]
|
28
|
+
call_nested :ivy_buildlist => params
|
29
|
+
end
|
30
|
+
|
31
|
+
def create_return_values
|
32
|
+
path = ant_references.find { |current| params[:reference] == current[0] }[1]
|
33
|
+
raise "Could not get path for params #{params.inspect}" unless path && path.respond_to?(:list)
|
34
|
+
path.list.map {|a| a.to_s }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'ivy/target'
|
2
|
+
|
3
|
+
module Ivy
|
4
|
+
class Buildnumber < Ivy::Target
|
5
|
+
def parameter
|
6
|
+
[
|
7
|
+
Parameter.new(:organisation, true),
|
8
|
+
Parameter.new(:module, true),
|
9
|
+
Parameter.new(:branch, false),
|
10
|
+
Parameter.new(:revision, false),
|
11
|
+
Parameter.new(:default, false),
|
12
|
+
Parameter.new(:defaultBuildNumber, false),
|
13
|
+
Parameter.new(:revSep, false),
|
14
|
+
Parameter.new(:prefix, false),
|
15
|
+
Parameter.new(:settingsRef, false),
|
16
|
+
Parameter.new(:resolver, false)
|
17
|
+
]
|
18
|
+
end
|
19
|
+
|
20
|
+
def result_property_values
|
21
|
+
[
|
22
|
+
ResultValue.new("ivy.revision", nil),
|
23
|
+
ResultValue.new("ivy.new.revision", nil),
|
24
|
+
ResultValue.new("ivy.build.number", nil),
|
25
|
+
ResultValue.new("ivy.new.build.number", nil)
|
26
|
+
]
|
27
|
+
end
|
28
|
+
|
29
|
+
protected
|
30
|
+
def execute_ivy
|
31
|
+
call_nested :ivy_buildnumber => params
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'ivy/target'
|
2
|
+
|
3
|
+
module Ivy
|
4
|
+
class Cachepath < Ivy::Target
|
5
|
+
def parameter
|
6
|
+
[
|
7
|
+
Parameter.new(:pathid, true),
|
8
|
+
Parameter.new(:conf, false),
|
9
|
+
Parameter.new(:inline, false),
|
10
|
+
Parameter.new(:organisation, false),
|
11
|
+
Parameter.new(:module, false),
|
12
|
+
Parameter.new(:revision, false),
|
13
|
+
Parameter.new(:branch, false),
|
14
|
+
Parameter.new(:type, false),
|
15
|
+
Parameter.new(:settingsRef, false),
|
16
|
+
]
|
17
|
+
end
|
18
|
+
|
19
|
+
def create_return_values
|
20
|
+
path = ant_references.find { |current| current[0] == params[:pathid] }[1]
|
21
|
+
raise "Could not get path for confs: #{params.inspect}" unless path && path.respond_to?(:list)
|
22
|
+
path.list.map {|a| a.to_s }
|
23
|
+
end
|
24
|
+
|
25
|
+
protected
|
26
|
+
def execute_ivy
|
27
|
+
call_nested :ivy_cachepath => params
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'ivy/target'
|
2
|
+
|
3
|
+
module Ivy
|
4
|
+
class Cleancache < Ivy::Target
|
5
|
+
def parameter
|
6
|
+
[
|
7
|
+
Parameter.new(:settingsRef, false)
|
8
|
+
]
|
9
|
+
end
|
10
|
+
|
11
|
+
protected
|
12
|
+
def execute_ivy
|
13
|
+
call_nested :ivy_cleancache => params
|
14
|
+
end
|
15
|
+
|
16
|
+
def create_return_values
|
17
|
+
nil
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'ivy/target'
|
2
|
+
|
3
|
+
module Ivy
|
4
|
+
class Configure < Ivy::Target
|
5
|
+
def parameter
|
6
|
+
[
|
7
|
+
Parameter.new(:id, false),
|
8
|
+
Parameter.new(:file, false),
|
9
|
+
Parameter.new(:url, false),
|
10
|
+
Parameter.new(:host, false),
|
11
|
+
Parameter.new(:realm,false),
|
12
|
+
Parameter.new(:username, false),
|
13
|
+
Parameter.new(:passwd, false)
|
14
|
+
]
|
15
|
+
end
|
16
|
+
|
17
|
+
def result_property_values
|
18
|
+
property = params[:id] || 'ivy.instance'
|
19
|
+
[
|
20
|
+
ResultValue.new(/.*\.#{property}/, nil)
|
21
|
+
]
|
22
|
+
end
|
23
|
+
|
24
|
+
protected
|
25
|
+
def execute_ivy
|
26
|
+
call_nested :ivy_configure => params
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'ivy/target'
|
2
|
+
|
3
|
+
module Ivy
|
4
|
+
class Findrevision < Ivy::Target
|
5
|
+
def parameter
|
6
|
+
[
|
7
|
+
Parameter.new(:organisation, true),
|
8
|
+
Parameter.new(:module, true),
|
9
|
+
Parameter.new(:branch, false),
|
10
|
+
Parameter.new(:revision, true),
|
11
|
+
Parameter.new(:property, false),
|
12
|
+
Parameter.new(:settingsRef, false)
|
13
|
+
]
|
14
|
+
end
|
15
|
+
|
16
|
+
def result_property_values
|
17
|
+
property = params[:property] || 'ivy.revision'
|
18
|
+
[
|
19
|
+
ResultValue.new("#{property}", nil)
|
20
|
+
]
|
21
|
+
end
|
22
|
+
|
23
|
+
protected
|
24
|
+
def execute_ivy
|
25
|
+
call_nested :ivy_findrevision => params
|
26
|
+
end
|
27
|
+
|
28
|
+
def create_return_values
|
29
|
+
values = result_properties.values
|
30
|
+
raise "Could not retrieve revision for '#{params.inspect}'" if values.size > 1
|
31
|
+
values.size == 1 ? values[0] : nil
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/ivy/info.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'ivy/target'
|
2
|
+
|
3
|
+
module Ivy
|
4
|
+
class Info < Ivy::Target
|
5
|
+
def parameter
|
6
|
+
[
|
7
|
+
Parameter.new(:file, true),
|
8
|
+
Parameter.new(:organisation, false),
|
9
|
+
Parameter.new(:module, false),
|
10
|
+
Parameter.new(:branch, false),
|
11
|
+
Parameter.new(:revision, false),
|
12
|
+
Parameter.new(:property, false),
|
13
|
+
Parameter.new(:settingsRef, false)
|
14
|
+
]
|
15
|
+
end
|
16
|
+
|
17
|
+
def result_property_values
|
18
|
+
property = params[:property] || 'ivy'
|
19
|
+
[
|
20
|
+
ResultValue.new("#{property}.organisation", nil),
|
21
|
+
ResultValue.new("#{property}.module", nil),
|
22
|
+
ResultValue.new("#{property}.branch", nil),
|
23
|
+
ResultValue.new("#{property}.revision", nil),
|
24
|
+
ResultValue.new("#{property}.status", nil),
|
25
|
+
ResultValue.new(/#{property}.extra\..*/, nil),
|
26
|
+
ResultValue.new("#{property}.configurations", Ivy::COMMA_SPLITTER),
|
27
|
+
ResultValue.new("#{property}.public.configurations", Ivy::COMMA_SPLITTER)
|
28
|
+
]
|
29
|
+
end
|
30
|
+
|
31
|
+
protected
|
32
|
+
def execute_ivy
|
33
|
+
call_nested :ivy_info => params
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/ivy/install.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'ivy/target'
|
2
|
+
|
3
|
+
module Ivy
|
4
|
+
class Install < Ivy::Target
|
5
|
+
def parameter
|
6
|
+
[
|
7
|
+
Parameter.new(:from, true),
|
8
|
+
Parameter.new(:to, true),
|
9
|
+
Parameter.new(:organisation, true),
|
10
|
+
Parameter.new(:module, true),
|
11
|
+
Parameter.new(:branch, false),
|
12
|
+
Parameter.new(:revision, true),
|
13
|
+
Parameter.new(:type, false),
|
14
|
+
Parameter.new(:validate, false),
|
15
|
+
Parameter.new(:overwrite, false),
|
16
|
+
Parameter.new(:transitive, false),
|
17
|
+
Parameter.new(:matcher, false),
|
18
|
+
Parameter.new(:settingsRef, false),
|
19
|
+
Parameter.new(:haltonfailure, false)
|
20
|
+
]
|
21
|
+
end
|
22
|
+
|
23
|
+
protected
|
24
|
+
def execute_ivy
|
25
|
+
call_nested :ivy_install => params
|
26
|
+
end
|
27
|
+
|
28
|
+
def create_return_values
|
29
|
+
nil
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Ivy
|
2
|
+
# Matcher that accepts all versions of the same module as a match, i.e.
|
3
|
+
# only checking that the module is the same.
|
4
|
+
class IvyAllVersionMatcher
|
5
|
+
include Java::OrgApacheIvyPluginsVersion::VersionMatcher
|
6
|
+
|
7
|
+
def is_dynamic(askedMrid)
|
8
|
+
false
|
9
|
+
end
|
10
|
+
|
11
|
+
def accept(askedMrid, foundMrid)
|
12
|
+
true
|
13
|
+
end
|
14
|
+
|
15
|
+
def need_module_descriptor(askedMrid, foundMrid)
|
16
|
+
false
|
17
|
+
end
|
18
|
+
|
19
|
+
def accept(askedMrid, foundMD)
|
20
|
+
true
|
21
|
+
end
|
22
|
+
|
23
|
+
def compare(askedMrid, foundMrid, staticComparator)
|
24
|
+
0
|
25
|
+
end
|
26
|
+
|
27
|
+
def getName
|
28
|
+
self.class
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,160 @@
|
|
1
|
+
# Extenions to the {Ruby-Java Bridge}[http://rjb.rubyforge.org/] module that
|
2
|
+
# add a generic Java object wrapper class for transparent access of java objects
|
3
|
+
# from ruby using RJB.
|
4
|
+
#
|
5
|
+
# This file was part of the Stanford Parser Ruby Wrapper by William Patrick McNeill.
|
6
|
+
# Only a few modifications have been done to make it compatible with ivy4r and the
|
7
|
+
# Java Ant objects that are accessed.
|
8
|
+
module Rjb
|
9
|
+
class JavaObjectWrapper
|
10
|
+
include Enumerable
|
11
|
+
|
12
|
+
# The underlying Java object.
|
13
|
+
attr_reader :java_object
|
14
|
+
|
15
|
+
# Initialize with a Java object <em>obj</em>. If <em>obj</em> is a
|
16
|
+
# String, treat it as a Java class name and instantiate it. Otherwise,
|
17
|
+
# treat <em>obj</em> as an instance of a Java object.
|
18
|
+
def initialize(obj, *args)
|
19
|
+
@java_object = obj.class == String ? Rjb::import(obj).send(:new, *args) : obj
|
20
|
+
end
|
21
|
+
|
22
|
+
# Enumerate all the items in the object using its iterator. If the object
|
23
|
+
# has no iterator, this function yields nothing.
|
24
|
+
def each
|
25
|
+
if @java_object.getClass.getMethods.any? {|m| m.getName == "iterator"}
|
26
|
+
i = @java_object.iterator
|
27
|
+
while i.hasNext
|
28
|
+
yield wrap_java_object(i.next)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end # each
|
32
|
+
|
33
|
+
# Reflect unhandled method calls to the underlying Java object and wrap
|
34
|
+
# the return value in the appropriate Ruby object.
|
35
|
+
def method_missing(m, *args)
|
36
|
+
begin
|
37
|
+
JavaObjectWrapper.wrap_java_object(@java_object.send(m, *args))
|
38
|
+
rescue RuntimeError => e
|
39
|
+
# The instance method failed. See if this is a static method.
|
40
|
+
if not e.message.match(/^Fail: unknown method name/).nil?
|
41
|
+
getClass.send(m, *args)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Checks if underlying java object responds to method prior using standard respond_to? method.
|
47
|
+
def respond_to?(sym)
|
48
|
+
java = @java_object.getClass.getMethods.any? {|m| m.getName == sym.to_s} || super.respond_to?(sym)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Show the classname of the underlying Java object.
|
52
|
+
def inspect
|
53
|
+
"<#{@java_object._classname}>"
|
54
|
+
end
|
55
|
+
|
56
|
+
# Use the underlying Java object's stringification.
|
57
|
+
def to_s
|
58
|
+
toString
|
59
|
+
end
|
60
|
+
|
61
|
+
# All wrapping is done at class level and not at instance level this deviates from the base
|
62
|
+
# implementation
|
63
|
+
class << self
|
64
|
+
# Convert a value returned by a call to the underlying Java object to the
|
65
|
+
# appropriate Ruby object.
|
66
|
+
#
|
67
|
+
# If the value is a JavaObjectWrapper, convert it using a protected
|
68
|
+
# function with the name wrap_ followed by the underlying object's
|
69
|
+
# classname with the Java path delimiters converted to underscores. For
|
70
|
+
# example, a <tt>java.util.ArrayList</tt> would be converted by a function
|
71
|
+
# called wrap_java_util_ArrayList.
|
72
|
+
#
|
73
|
+
# If the value lacks the appropriate converter function, wrap it in a
|
74
|
+
# generic JavaObjectWrapper.
|
75
|
+
#
|
76
|
+
# If the value is not a JavaObjectWrapper, return it unchanged.
|
77
|
+
#
|
78
|
+
# This function is called recursively for every element in an Array.
|
79
|
+
def wrap_java_object(object)
|
80
|
+
if object.kind_of?(Array)
|
81
|
+
object.collect {|item| wrap_java_object(item)}
|
82
|
+
elsif object.respond_to?(:_classname)
|
83
|
+
# Ruby-Java Bridge Java objects all have a _classname member
|
84
|
+
find_converter(object) || JavaObjectWrapper.new(object)
|
85
|
+
else
|
86
|
+
object
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
protected
|
91
|
+
# Checks if any class in objects class hierachy or interface of class has a converter method defined
|
92
|
+
# if converter exists returns converted object.
|
93
|
+
def find_converter(object)
|
94
|
+
classnames_to_check(object).each do |name|
|
95
|
+
wrapper = wrapper_name(name)
|
96
|
+
if respond_to?(wrapper, true)
|
97
|
+
return send(wrapper, object)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
nil
|
102
|
+
end
|
103
|
+
|
104
|
+
# Returns all java classnames that should be checked for an adequate mapper for given object.
|
105
|
+
def classnames_to_check(object)
|
106
|
+
names = []
|
107
|
+
clazz = object.getClass
|
108
|
+
while clazz
|
109
|
+
names << clazz.getName
|
110
|
+
clazz.getInterfaces.each {|i| names << i.getName}
|
111
|
+
clazz = clazz.getSuperclass
|
112
|
+
end
|
113
|
+
|
114
|
+
names.uniq
|
115
|
+
end
|
116
|
+
|
117
|
+
# Returns the wrapper name for given java classname.
|
118
|
+
def wrapper_name(java_classname)
|
119
|
+
("convert_" + java_classname.gsub(/\./, "_")).downcase.to_sym
|
120
|
+
end
|
121
|
+
|
122
|
+
# Convert <tt>java.util.List</tt> objects to Ruby Array objects.
|
123
|
+
def convert_java_util_list(object)
|
124
|
+
array_list = []
|
125
|
+
object.size.times do
|
126
|
+
|i| array_list << wrap_java_object(object.get(i))
|
127
|
+
end
|
128
|
+
array_list
|
129
|
+
end
|
130
|
+
|
131
|
+
# Convert <tt>java.util.Set</tt> objects to Ruby array object with no duplicates.
|
132
|
+
def convert_java_util_set(object)
|
133
|
+
set = []
|
134
|
+
i = object.iterator
|
135
|
+
while i.hasNext
|
136
|
+
set << wrap_java_object(i.next)
|
137
|
+
end
|
138
|
+
set.uniq
|
139
|
+
end
|
140
|
+
|
141
|
+
# Convert <tt>java.util.Map</tt> objects to Ruby Hash object.
|
142
|
+
def convert_java_util_map(object)
|
143
|
+
hash = {}
|
144
|
+
i = object.entrySet.iterator
|
145
|
+
while i.hasNext
|
146
|
+
entry = i.next
|
147
|
+
hash[wrap_java_object(entry.getKey)] = wrap_java_object(entry.getValue)
|
148
|
+
end
|
149
|
+
hash
|
150
|
+
end
|
151
|
+
|
152
|
+
# Convert <tt>java.lang.String</tt> objects to Ruby String.
|
153
|
+
def convert_java_lang_string(object)
|
154
|
+
object.toString
|
155
|
+
end
|
156
|
+
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
end
|