openhab-scripting 5.10.0 → 5.12.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -36,12 +36,9 @@ module OpenHAB
36
36
  # Base class for all widgets
37
37
  # @see org.openhab.core.model.sitemap.sitemap.Widget
38
38
  class WidgetBuilder
39
- # rubocop:disable Layout/LineLength, Lint/MixedRegexpCaptureTypes
40
- # These are copied directly out of UIComponentSitemapProvider.java
41
- VISIBILITY_PATTERN = /(?<item>[A-Za-z]\w*)\s*(?<condition>==|!=|<=|>=|<|>)\s*(?<sign>\+|-)?(?<state>\S+)/.freeze
42
- COLOR_PATTERN = /((?<item>[A-Za-z]\w*)?\s*((?<condition>==|!=|<=|>=|<|>)\s*(?<sign>\+|-)?(?<state>\S+))?\s*=)?\s*(?<arg>\S+)/.freeze
43
- # rubocop:enable Layout/LineLength, Lint/MixedRegexpCaptureTypes
44
- private_constant :VISIBILITY_PATTERN, :COLOR_PATTERN
39
+ # This is copied directly out of UIComponentSitemapProvider.java
40
+ CONDITION_PATTERN = /(?<item>[A-Za-z]\w*)?\s*(?<condition>==|!=|<=|>=|<|>)?\s*(?<sign>\+|-)?(?<state>.+)/.freeze
41
+ private_constant :CONDITION_PATTERN
45
42
 
46
43
  # @return [String, nil]
47
44
  attr_accessor :label
@@ -52,15 +49,15 @@ module OpenHAB
52
49
  # @see https://www.openhab.org/docs/ui/sitemaps.html#icons
53
50
  attr_accessor :icon
54
51
  # Label color rules
55
- # @return [Array<String>]
52
+ # @return [Hash<String, String>]
56
53
  # @see https://www.openhab.org/docs/ui/sitemaps.html#label-value-and-icon-colors
57
54
  attr_reader :label_colors
58
55
  # Value color rules
59
- # @return [Array<String>]
56
+ # @return [Hash<String, String>]
60
57
  # @see https://www.openhab.org/docs/ui/sitemaps.html#label-value-and-icon-colors
61
58
  attr_reader :value_colors
62
59
  # Icon color rules
63
- # @return [Array<String>]
60
+ # @return [Hash<String, String>]
64
61
  # @see https://www.openhab.org/docs/ui/sitemaps.html#label-value-and-icon-colors
65
62
  attr_reader :icon_colors
66
63
  # Visibility rules
@@ -94,32 +91,32 @@ module OpenHAB
94
91
  @label = label
95
92
  @icon = icon
96
93
  @visibilities = []
97
- @label_colors = []
98
- @value_colors = []
99
- @icon_colors = []
94
+ @label_colors = {}
95
+ @value_colors = {}
96
+ @icon_colors = {}
100
97
 
101
- self.label_color(*label_color) if label_color
102
- self.value_color(*value_color) if value_color
103
- self.icon_color(*icon_color) if icon_color
98
+ self.label_color(label_color) if label_color
99
+ self.value_color(value_color) if value_color
100
+ self.icon_color(icon_color) if icon_color
104
101
  self.visibility(*visibility) if visibility
105
102
  end
106
103
 
107
104
  # Adds one or more new rules for setting the label color
108
- # @return [Array<String>] the current rules
109
- def label_color(*rules)
110
- @label_colors.concat(rules)
105
+ # @return [Hash<String, String>] the current rules
106
+ def label_color(rules)
107
+ @label_colors.merge!(rules)
111
108
  end
112
109
 
113
110
  # Adds one or more new rules for setting the value color
114
- # @return [Array<String>] the current rules
115
- def value_color(*rules)
116
- @value_colors.concat(rules)
111
+ # @return [Hash<String, String>] the current rules
112
+ def value_color(rules)
113
+ @value_colors.merge!(rules)
117
114
  end
118
115
 
119
116
  # Adds one or more new rules for setting the icon color
120
- # @return [Array<String>] the current rules
121
- def icon_color(*rules)
122
- @icon_colors.concat(rules)
117
+ # @return [Hash<String, String>] the current rules
118
+ def icon_color(rules)
119
+ @icon_colors.merge!(rules)
123
120
  end
124
121
 
125
122
  # Adds one or more new visibility rules
@@ -137,21 +134,28 @@ module OpenHAB
137
134
  widget.label = @label
138
135
  widget.icon = @icon
139
136
 
140
- add_color(widget.label_color, label_colors) unless label_colors.empty?
141
- add_color(widget.value_color, value_colors) unless value_colors.empty?
142
- add_color(widget.icon_color, icon_colors) unless icon_colors.empty?
137
+ add_colors(widget, :label_color, label_colors)
138
+ add_colors(widget, :value_color, value_colors)
139
+ add_colors(widget, :icon_color, icon_colors)
143
140
 
144
- visibilities.each do |v|
145
- unless (match = VISIBILITY_PATTERN.match(v))
146
- raise ArgumentError, "Syntax error in visibility rule #{v.inspect}"
141
+ # @deprecated OH 4.1
142
+ if SitemapBuilder.factory.respond_to?(:create_condition)
143
+ add_conditions(widget, :visibility, visibilities, :create_visibility_rule)
144
+ else
145
+ visibilities.each do |v|
146
+ raise ArgumentError, "AND conditions not supported prior to openHAB 4.1" if v.is_a?(Array)
147
+
148
+ unless (match = CONDITION_PATTERN.match(v))
149
+ raise ArgumentError, "Syntax error in visibility rule #{v.inspect}"
150
+ end
151
+
152
+ rule = SitemapBuilder.factory.create_visibility_rule
153
+ rule.item = match["item"]
154
+ rule.condition = match["condition"]
155
+ rule.sign = match["sign"]
156
+ rule.state = match["state"]
157
+ widget.visibility.add(rule)
147
158
  end
148
-
149
- rule = SitemapBuilder.factory.create_visibility_rule
150
- rule.item = match["item"]
151
- rule.condition = match["condition"]
152
- rule.sign = match["sign"]
153
- rule.state = match["state"]
154
- widget.visibility.add(rule)
155
159
  end
156
160
 
157
161
  widget
@@ -169,19 +173,51 @@ module OpenHAB
169
173
 
170
174
  private
171
175
 
172
- def add_color(widget_color, colors)
173
- colors.each do |c|
174
- unless (match = COLOR_PATTERN.match(c))
175
- raise ArgumentError, "Syntax error in color rule #{c.inspect}"
176
+ def add_colors(widget, method, conditions)
177
+ conditions.each do |condition, color|
178
+ condition = [condition] unless condition.is_a?(Array)
179
+ add_conditions(widget, method, condition, :create_color_array) do |color_array|
180
+ color_array.arg = color
181
+ end
182
+ end
183
+ end
184
+
185
+ def add_conditions(widget, method, conditions, container_method)
186
+ return if conditions.empty?
187
+
188
+ object = widget.send(method)
189
+ has_and_conditions = conditions.any?(Array)
190
+ # @deprecated OH 4.1
191
+ if !SitemapBuilder.factory.respond_to?(:create_condition) && has_and_conditions
192
+ raise ArgumentError, "AND conditions not supported prior to openHAB 4.1"
193
+ end
194
+
195
+ conditions = [conditions] unless has_and_conditions
196
+
197
+ conditions.each do |sub_conditions|
198
+ container = SitemapBuilder.factory.send(container_method)
199
+
200
+ add_conditions_to_container(container, sub_conditions)
201
+ yield container if block_given?
202
+ object.add(container)
203
+ end
204
+ end
205
+
206
+ def add_conditions_to_container(container, conditions)
207
+ # @deprecated OH 4.1
208
+ supports_and_conditions = SitemapBuilder.factory.respond_to?(:create_condition)
209
+
210
+ Array.wrap(conditions).each do |c|
211
+ unless (match = CONDITION_PATTERN.match(c))
212
+ raise ArgumentError, "Syntax error in condition #{c.inspect}"
176
213
  end
177
214
 
178
- rule = SitemapBuilder.factory.create_color_array
179
- rule.item = match["item"]
180
- rule.condition = match["condition"]
181
- rule.sign = match["sign"]
182
- rule.state = match["state"]
183
- rule.arg = match["arg"]
184
- widget_color.add(color)
215
+ condition = supports_and_conditions ? SitemapBuilder.factory.create_condition : container
216
+ condition.item = match["item"]
217
+ condition.condition = match["condition"]
218
+ condition.sign = match["sign"]
219
+ condition.state = match["state"]
220
+ container.conditions.add(condition) if supports_and_conditions
185
221
  end
186
222
  end
187
223
  end
@@ -284,6 +320,7 @@ module OpenHAB
284
320
 
285
321
  @switch = switch
286
322
  @frequency = frequency
323
+ @switch_enabled = nil
287
324
  end
288
325
 
289
326
  # (see #switch=)
@@ -210,7 +210,18 @@ module OpenHAB
210
210
  thing_type,
211
211
  self.class.config_description_registry
212
212
  )
213
+
214
+ predefined_channels = self.class.thing_factory_helper
215
+ .create_channels(thing_type, uid, self.class.config_description_registry)
216
+ .to_h { |channel| [channel.uid, channel] }
217
+ new_channels = channels.to_h { |channel| [channel.uid, channel] }
218
+ merged_channels = predefined_channels.merge(new_channels) do |_key, predefined_channel, new_channel|
219
+ predefined_channel.configuration.merge!(new_channel.configuration)
220
+ predefined_channel
221
+ end
222
+ @channels = merged_channels.values
213
223
  end
224
+
214
225
  builder = org.openhab.core.thing.binding.builder.ThingBuilder
215
226
  .create(thing_type_uid, uid)
216
227
  .with_label(label)
@@ -219,15 +230,7 @@ module OpenHAB
219
230
  .with_bridge(bridge_uid)
220
231
  .with_channels(channels)
221
232
 
222
- if thing_type
223
- # can't use with_channels, or it will wipe out custom channels from above
224
- self.class.thing_factory_helper.create_channels(thing_type,
225
- uid,
226
- self.class.config_description_registry).each do |channel|
227
- builder.with_channel(channel)
228
- end
229
- builder.with_properties(thing_type.properties)
230
- end
233
+ builder.with_properties(thing_type.properties) if thing_type
231
234
 
232
235
  builder.build
233
236
  end
@@ -263,8 +266,14 @@ module OpenHAB
263
266
  :default_tags,
264
267
  :properties,
265
268
  :description,
266
- :auto_update_policy,
267
- :accepted_item_type
269
+ :auto_update_policy
270
+
271
+ class << self
272
+ # @!visibility private
273
+ def channel_type_registry
274
+ @channel_type_registry ||= OSGi.service("org.openhab.core.thing.type.ChannelTypeRegistry")
275
+ end
276
+ end
268
277
 
269
278
  #
270
279
  # Constructor for ChannelBuilder
@@ -284,7 +293,7 @@ module OpenHAB
284
293
  # The default tags for this channel.
285
294
  # @param [:default, :recommend, :veto, org.openhab.core.thing.type.AutoUpdatePolicy] auto_update_policy
286
295
  # The channel's auto update policy.
287
- # @param [String] accepted_item_type The accepted item type.
296
+ # @param [String] accepted_item_type The accepted item type. If nil, infer the item type from the channel type.
288
297
  #
289
298
  def initialize(uid,
290
299
  type,
@@ -343,6 +352,12 @@ module OpenHAB
343
352
  .build
344
353
  end
345
354
 
355
+ # @!attribute [r] accepted_item_type
356
+ # @return [String] The accepted item type.
357
+ def accepted_item_type
358
+ @accepted_item_type ||= self.class.channel_type_registry.get_channel_type(type)&.item_type
359
+ end
360
+
346
361
  private
347
362
 
348
363
  def kind
@@ -4,6 +4,6 @@ module OpenHAB
4
4
  module DSL
5
5
  # Version of openHAB helper libraries
6
6
  # @return [String]
7
- VERSION = "5.10.0"
7
+ VERSION = "5.12.0"
8
8
  end
9
9
  end
data/lib/openhab/dsl.rb CHANGED
@@ -77,7 +77,13 @@ module OpenHAB
77
77
  #
78
78
  # Defines a new profile that can be applied to item channel links.
79
79
  #
80
+ # To create a profile that can be used in the UI, provide a label and optionally a {config_description},
81
+ # otherwise the profile will not be visible in the UI.
82
+ #
80
83
  # @param [String, Symbol] id The id for the profile.
84
+ # @param [String, nil] label The label for the profile. When nil, the profile will not be visible in the UI.
85
+ # @param [org.openhab.core.config.core.ConfigDescription, nil] config_description
86
+ # The configuration description for the profile so that it can be configured in the UI.
81
87
  # @yield [event, command: nil, state: nil, callback:, link:, item:, channel_uid:, configuration:, context:]
82
88
  # All keyword params are optional. Any that aren't defined won't be passed.
83
89
  # @yieldparam [:command_from_item, :state_from_item, :command_from_handler, :state_from_handler] event
@@ -97,8 +103,8 @@ module OpenHAB
97
103
  # @yieldreturn [Boolean] Return true from the block in order to have default processing.
98
104
  # @return [void]
99
105
  #
100
- # @see org.openhab.thing.Profile
101
- # @see org.openhab.thing.StateProfile
106
+ # @see org.openhab.core.thing.profiles.Profile
107
+ # @see org.openhab.core.thing.profiles.StateProfile
102
108
  #
103
109
  # @example Vetoing a command
104
110
  # profile(:veto_closing_shades) do |event, item:, command:|
@@ -142,17 +148,54 @@ module OpenHAB
142
148
  # # can also be referenced from an `.items` file:
143
149
  # # Number:Temperature MyTempWithNonUnitValueFromBinding "I prefer Celsius [%d °C]" { channel="something_that_returns_F"[profile="ruby:set_uom", unit="°F"] }
144
150
  #
145
- def profile(id, &block)
151
+ # @example Create a profile that is usable in the UI
152
+ # config_description = config_description do
153
+ # parameter :min, :decimal, label: "Minimum", description: "Minimum value"
154
+ # parameter :max, :decimal, label: "Maximum", description: "Maximum value"
155
+ # end
156
+ #
157
+ # profile(:range_filter, label: "Range Filter", config_description: config_description) do |event, state:, configuration:|
158
+ # return true unless event == :state_from_handler
159
+ #
160
+ # (configuration["min"]..configuration["max"]).cover?(state)
161
+ # end
162
+ #
163
+ def profile(id, label: nil, config_description: nil, &block)
146
164
  raise ArgumentError, "Block is required" unless block
147
165
 
148
166
  id = id.to_s
149
- uid = org.openhab.core.thing.profiles.ProfileTypeUID.new("ruby", id)
150
167
 
151
168
  ThreadLocal.thread_local(openhab_rule_type: "profile", openhab_rule_uid: id) do
152
- Core::ProfileFactory.instance.register(uid, block)
169
+ Core::ProfileFactory.instance.register(id, block, label: label, config_description: config_description)
153
170
  end
154
171
  end
155
172
 
173
+ #
174
+ # Create a {org.openhab.core.config.core.ConfigDescription ConfigDescription} object.
175
+ #
176
+ # @param [String, java.net.URI] uri The URI for the ConfigDescription. When nil, a dummy URI is used which will
177
+ # be replaced by the profile with the correct URI for that profile.
178
+ # @yield Block that consists of {ConfigDescription::Builder#parameter} and {ConfigDescription::Builder#group} calls.
179
+ #
180
+ # @return [org.openhab.core.config.core.ConfigDescription]
181
+ # The created {org.openhab.core.config.core.ConfigDescription ConfigDescription} object
182
+ #
183
+ # @example
184
+ # config_description = config_description do
185
+ # parameter :ungrouped_parameter, :decimal, label: "Ungrouped Parameter", min: 1, max: 5
186
+ #
187
+ # group "Config Group", label: "Grouped parameters", advanced: true do
188
+ # parameter :my_parameter, :string, label: "My Parameter", description: "My Parameter Description"
189
+ # parameter :other_parameter, :integer, label: "Other Parameter", description: "Other Parameter Description"
190
+ # end
191
+ # end
192
+ #
193
+ def config_description(uri = nil, &block)
194
+ raise ArgumentError, "Block is required" unless block
195
+
196
+ ConfigDescription::Builder.new.build(uri, &block)
197
+ end
198
+
156
199
  # rubocop:enable Layout/LineLength
157
200
 
158
201
  # @!group Object Access
@@ -557,10 +600,49 @@ module OpenHAB
557
600
  # to reduce repetitions
558
601
  #
559
602
 
603
+ #
604
+ # Permanently enable conditional execution of commands and updates for the current thread.
605
+ #
606
+ # When conditional executions are enabled, commands and updates will only be sent if the
607
+ # item's current state is not the same as the command or updated state.
608
+ # This eliminates the need to chain the command and update calls through
609
+ # {DSL::Items::Ensure::Ensurable#ensure ensure}.
610
+ #
611
+ # When conditional executions are enabled either by this method or within a block of {ensure_states},
612
+ # commands and updates can still be forcefully executed using the corresponding bang methods, e.g.
613
+ # `Item1.on!`, `Item1.command!(50)`, or `Item1.update!(ON)`.
614
+ #
615
+ # @note This method is only intended for use at the top level of rule
616
+ # scripts. If it's used within library methods, or hap-hazardly within
617
+ # rules, things can get very confusing because the prior state won't be
618
+ # properly restored.
619
+ #
620
+ # @param [Boolean] active Whether to enable or disable conditional executions.
621
+ # @return [Boolean] The previous ensure_states setting.
622
+ #
623
+ # @example Make ensure_states the default for the rest of the script
624
+ # ensure_states!
625
+ #
626
+ # # From now, all commands are "ensured", i.e. only sent when the current state is different
627
+ # Item1.on
628
+ # Item2.command(ON)
629
+ #
630
+ # # While ensure_states! is active, we can still forcibly send a command
631
+ # # regardless of the item's current state
632
+ # Item2.on!
633
+ #
634
+ # @see ensure_states
635
+ #
636
+ def ensure_states!(active: true)
637
+ old = Thread.current[:openhab_ensure_states]
638
+ Thread.current[:openhab_ensure_states] = active
639
+ old
640
+ end
641
+
560
642
  #
561
643
  # Global method that takes a block and for the duration of the block
562
644
  # all commands sent will check if the item is in the command's state
563
- # before sending the command.
645
+ # before sending the command. This also applies to updates.
564
646
  #
565
647
  # @yield
566
648
  # @return [Object] The result of the block.
@@ -603,11 +685,10 @@ module OpenHAB
603
685
  # end
604
686
  #
605
687
  def ensure_states
606
- old = Thread.current[:openhab_ensure_states]
607
- Thread.current[:openhab_ensure_states] = true
688
+ old = ensure_states!
608
689
  yield
609
690
  ensure
610
- Thread.current[:openhab_ensure_states] = old
691
+ ensure_states!(active: old)
611
692
  end
612
693
 
613
694
  #
@@ -841,7 +922,7 @@ module OpenHAB
841
922
  # elements, the {Core::Items::Metadata::Hash} will be passed as an argument. Therefore it's
842
923
  # recommended that you use a Proc, not a Lambda, for permissive argument matching.
843
924
  #
844
- # @return [void]
925
+ # @return [Hash] the prior provider configuration.
845
926
  #
846
927
  # @see provider
847
928
  # @see OpenHAB::Core::Provider.current Provider.current for how the current provider is calculated
@@ -5,26 +5,36 @@ module OpenHAB
5
5
  # based on https://stackoverflow.com/questions/1197224/source-shell-script-into-environment-within-a-ruby-script#19826329
6
6
  # @!visibility private
7
7
  module Shell
8
- module_function
8
+ # @!visibility private
9
+ DEFAULT_PRINTENV_COMMAND = "printenv -0"
10
+ @printenv_command = DEFAULT_PRINTENV_COMMAND
11
+ @printenv_separator = "\0"
9
12
 
10
- # Read in the bash environment, after an optional command.
11
- # Returns Array of key/value pairs.
12
- def shell_env(cmd = nil)
13
- cmd = "#{cmd} > /dev/null; " if cmd
14
- env = `#{cmd}printenv -0`
15
- env.split("\0").map { |l| l.split("=", 2) }
16
- end
13
+ class << self
14
+ # Read in the bash environment, after an optional command.
15
+ # Returns Array of key/value pairs.
16
+ def shell_env(cmd = nil)
17
+ cmd = "#{cmd} > #{IO::NULL}; " if cmd
18
+ env = `#{cmd}#{@printenv_command} 2> #{IO::NULL}`
19
+ if !$?.success? && @printenv_command.equal?(DEFAULT_PRINTENV_COMMAND)
20
+ @printenv_command = "printenv"
21
+ @printenv_separator = "\n"
22
+ env = `#{cmd}#{@printenv_command}`
23
+ end
24
+ env.split(@printenv_separator).map { |l| l.split("=", 2) }
25
+ end
17
26
 
18
- # Source a given file, and compare environment before and after.
19
- # Returns Hash of any keys that have changed.
20
- def shell_source(file)
21
- (shell_env(". #{File.realpath(file)}") - shell_env).to_h
22
- end
27
+ # Source a given file, and compare environment before and after.
28
+ # Returns Hash of any keys that have changed.
29
+ def shell_source(file)
30
+ (shell_env(". #{File.realpath(file)}") - shell_env).to_h
31
+ end
23
32
 
24
- # Find variables changed as a result of sourcing the given file,
25
- # and update in ENV.
26
- def source_env_from(file)
27
- shell_source(file).each { |k, v| ENV[k] = v }
33
+ # Find variables changed as a result of sourcing the given file,
34
+ # and update in ENV.
35
+ def source_env_from(file)
36
+ shell_source(file).each { |k, v| ENV[k] = v }
37
+ end
28
38
  end
29
39
  end
30
40
  end
@@ -9,7 +9,9 @@ module YARD
9
9
  # Which might be a class, an enum, or an interface
10
10
  module Base
11
11
  module ClassMethods
12
- def new(name)
12
+ def new(name, _suffix = nil)
13
+ # _suffix is given when it encounters a class with ::, e.g. org.openhab.core.OpenHAB::DEFAULT_CONFIG_FOLDER
14
+
13
15
  super(:root, name)
14
16
  end
15
17
  end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module YARD
4
+ module CodeObjects
5
+ module Java
6
+ class MethodObject < CodeObjects::MethodObject
7
+ include Base
8
+
9
+ def type
10
+ :method
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -5,27 +5,76 @@ module YARD
5
5
  module JRuby
6
6
  module Base
7
7
  class << self
8
+ #
9
+ # Creates an external link to java documentation for a java class.
10
+ #
11
+ # The supported classes are defined in the `javadocs` configuration option.
12
+ #
13
+ # Supported syntaxes:
14
+ # Package:
15
+ # - org.openhab.core => href_base/org/openhab/core/package-summary.html
16
+ #
17
+ # Class:
18
+ # - org.openhab.core.OpenHAB => href_base/org/openhab/core/OpenHAB.html
19
+ # This doesn't get mistaken as a constant:
20
+ # - java.net.URI => href_base/java/net/URI.html
21
+ #
22
+ # Constant: (To specify a constant, use Ruby's `::` syntax)
23
+ # - org.openhab.core.OpenHAB::DEFAULT_CONFIG_FOLDER =>
24
+ # href_base/org/openhab/core/OpenHAB.html#DEFAULT_CONFIG_FOLDER
25
+ #
26
+ # Method:
27
+ # - org.openhab.core.OpenHAB#getVersion() => href_base/org/openhab/core/OpenHAB.html#getVersion()
28
+ # But can also work with constants, albeit not a valid Ruby syntax:
29
+ # - org.openhab.core.OpenHAB#version => href_base/org/openhab/core/OpenHAB.html#version
30
+ # - org.openhab.core.OpenHAB#DEFAULT_CONFIG_FOLDER =>
31
+ # href_base/org/openhab/core/OpenHAB.html#DEFAULT_CONFIG_FOLDER
32
+ #
33
+ # Inner class:
34
+ # - org.openhab.core.config.core.ConfigDescriptionParameter::Type =>
35
+ # href_base/org/openhab/core/config/core/ConfigDescriptionParameter.Type.html
36
+ # - org.openhab.core.config.core.ConfigDescriptionParameter.Type =>
37
+ # href_base/org/openhab/core/config/core/ConfigDescriptionParameter.Type.html
38
+ #
39
+ # Constant in inner class:
40
+ # - org.openhab.core.config.core.ConfigDescriptionParameter::Type::TEXT =>
41
+ # href_base/org/openhab/core/config/core/ConfigDescriptionParameter.Type.html#TEXT
42
+ # - org.openhab.core.config.core.ConfigDescriptionParameter.Type::TEXT =>
43
+ # href_base/org/openhab/core/config/core/ConfigDescriptionParameter.Type.html#TEXT
44
+ #
8
45
  def infer_java_class(klass, inferred_type = nil, comments = nil, statement = nil)
9
- components = klass.split(".")
10
- components.pop if components.last == "freeze"
11
-
12
- class_first_char = components.last[0]
13
- is_field = components.last == components.last.upcase
14
- container_first_char = components[-2]&.[](0)
15
- is_method = container_first_char &&
16
- class_first_char != class_first_char.upcase &&
17
- container_first_char == container_first_char.upcase
18
- is_package = !is_method && !is_field && class_first_char != class_first_char.upcase
19
-
20
- # methods aren't supported right now
21
- return if is_method
22
-
23
46
  javadocs = YARD::Config.options.dig(:jruby, "javadocs") || {}
24
47
 
25
48
  href_base = javadocs.find { |package, _href| klass == package || klass.start_with?("#{package}.") }&.last
26
49
  return unless href_base
27
50
 
51
+ components = klass.split(/\.(?=[A-Z])/, 2)
52
+ components.unshift(*components.shift.split("."))
53
+ components.push(components.pop.delete_suffix(".freeze"))
54
+
55
+ class_first_char = components.last[0]
56
+ if /#|::/.match?(components.last)
57
+ parts = components.pop.rpartition(/#|::/)
58
+ is_field = parts.last == parts.last.upcase
59
+ # explicit method is fine, e.g. `org.openhab.core.OpenHAB#getVersion()`
60
+ is_method = !is_field
61
+ components.push(parts.first.gsub("::", "."), parts.last)
62
+ else
63
+ is_field = is_method = false
64
+ if components.last.include?(".")
65
+ parts = components.last.split(".")
66
+ if (is_method = parts.last[0] == parts.last[0].downcase)
67
+ # implicit method is not supported, e.g. `org.openhab.core.OpenHAB.version`
68
+ # because we're not sure whether it should be #version() or #getVersion()
69
+ return
70
+ end
71
+ end
72
+ end
73
+
74
+ is_package = !is_method && !is_field && class_first_char != class_first_char.upcase
75
+
28
76
  inferred_type = CodeObjects::Java::FieldObject if is_field
77
+ inferred_type = CodeObjects::Java::MethodObject if is_method
29
78
  inferred_type = CodeObjects::Java::PackageObject if is_package
30
79
  if inferred_type.nil?
31
80
  docstring = Docstring.parser.parse(comments || statement&.comments).to_docstring
@@ -36,12 +85,18 @@ module YARD
36
85
  end
37
86
  end
38
87
 
88
+ orig_klass = klass.dup
89
+
90
+ # purposely calling gsub! to modify the caller's string
91
+ # YARD doesn't handle java inner classes well, so we convert them to ruby
92
+ klass.gsub!("::", ".")
93
+
39
94
  inferred_type.new(klass) do |o|
40
95
  o.source = statement if statement
41
96
  suffix = "/package-summary" if is_package
42
- field = "##{components.pop}" if is_field
97
+ field = "##{components.pop}" if is_field || is_method
43
98
  link = "#{href_base}#{components.join("/")}#{suffix}.html#{field}"
44
- o.docstring.add_tag(Tags::Tag.new(:see, klass, nil, link)) unless o.docstring.has_tag?(:see)
99
+ o.docstring.add_tag(Tags::Tag.new(:see, orig_klass, nil, link)) unless o.docstring.has_tag?(:see)
45
100
  end
46
101
  end
47
102
  end