openhab-scripting 5.10.0 → 5.12.0

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.
@@ -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