Class: Hocon::Impl::ConfigConcatenation

Inherits:
Object
  • Object
show all
Includes:
AbstractConfigValue, Container, Unmergeable
Defined in:
lib/hocon/impl/config_concatenation.rb

Constant Summary collapse

SimpleConfigList =
Hocon::Impl::SimpleConfigList
ConfigObject =
Hocon::ConfigObject
ConfigString =
Hocon::Impl::ConfigString
ResolveStatus =
Hocon::Impl::ResolveStatus
Unmergeable =
Hocon::Impl::Unmergeable
SimpleConfigOrigin =
Hocon::Impl::SimpleConfigOrigin
ConfigBugOrBrokenError =
Hocon::ConfigError::ConfigBugOrBrokenError
ConfigNotResolvedError =
Hocon::ConfigError::ConfigNotResolvedError
ConfigWrongTypeError =
Hocon::ConfigError::ConfigWrongTypeError

Constants included from AbstractConfigValue

AbstractConfigValue::ConfigImplUtil

Instance Attribute Summary collapse

Attributes included from AbstractConfigValue

#origin

Class Method Summary collapse

Instance Method Summary collapse

Methods included from AbstractConfigValue

#at_key, #at_key_with_origin, #at_path, #at_path_with_origin, #construct_delayed_merge, #delay_merge, has_descendant_in_list?, indent, #inspect, #merged_stack_with_non_object, #merged_stack_with_object, #merged_stack_with_the_unmergeable, #merged_with_non_object, #merged_with_object, #merged_with_the_unmergeable, #render, #render_to_sb, replace_child_in_list, #require_not_ignoring_fallbacks, #to_fallback_value, #to_s, #transform_to_string, #with_fallback, #with_fallbacks_ignored, #with_origin

Methods included from ConfigValue

#at_key, #at_path, #origin, #render, #with_fallback, #with_origin

Methods included from ConfigMergeable

#with_fallback

Constructor Details

#initialize(origin, pieces) ⇒ ConfigConcatenation

Returns a new instance of ConfigConcatenation.



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/hocon/impl/config_concatenation.rb', line 30

def initialize(origin, pieces)
  super(origin)
  @pieces = pieces

  if pieces.size < 2
    raise ConfigBugOrBrokenError, "Created concatenation with less than 2 items: #{self}"
  end

  had_unmergeable = false
  pieces.each do |p|
    if p.is_a?(Hocon::Impl::ConfigConcatenation)
      raise ConfigBugOrBrokenError, "ConfigConcatenation should never be nested: #{self}"
    end
    if p.is_a?(Unmergeable)
      had_unmergeable = true
    end
  end

  unless had_unmergeable
    raise ConfigBugOrBrokenError, "Created concatenation without an unmergeable in it: #{self}"
  end
end

Instance Attribute Details

#piecesObject (readonly)

Returns the value of attribute pieces.



28
29
30
# File 'lib/hocon/impl/config_concatenation.rb', line 28

def pieces
  @pieces
end

Class Method Details

.concatenate(pieces) ⇒ Object



156
157
158
159
160
161
162
163
164
165
166
# File 'lib/hocon/impl/config_concatenation.rb', line 156

def self.concatenate(pieces)
  consolidated = consolidate(pieces)
  if consolidated.empty?
    nil
  elsif consolidated.length == 1
    consolidated[0]
  else
    merged_origin = SimpleConfigOrigin.merge_value_origins(consolidated)
    Hocon::Impl::ConfigConcatenation.new(merged_origin, consolidated)
  end
end

.consolidate(pieces) ⇒ Object



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
# File 'lib/hocon/impl/config_concatenation.rb', line 130

def self.consolidate(pieces)
  if pieces.length < 2
    pieces
  else
    flattened = []
    pieces.each do |v|
      if v.is_a?(Hocon::Impl::ConfigConcatenation)
        flattened.concat(v.pieces)
      else
        flattened.push(v)
      end
    end

    consolidated = []
    flattened.each do |v|
      if consolidated.empty?
        consolidated.push(v)
      else
        join(consolidated, v)
      end
    end

    consolidated
  end
end

.join(builder, orig_right) ⇒ Object

Add left and right, or their merger, to builder



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
# File 'lib/hocon/impl/config_concatenation.rb', line 79

def self.join(builder, orig_right)
  left = builder[builder.size - 1]
  right = orig_right

  # check for an object which can be converted to a list
  # (this will be an object with numeric keys, like foo.0, foo.1)
  if (left.is_a?(ConfigObject)) && (right.is_a?(SimpleConfigList))
    left = Hocon::Impl::DefaultTransformer.transform(left, Hocon::ConfigValueType::LIST)
  elsif (left.is_a?(SimpleConfigList)) && (right.is_a?(ConfigObject))
    right = Hocon::Impl::DefaultTransformer.transform(right, Hocon::ConfigValueType::LIST)
  end

  # Since this depends on the type of two instances, I couldn't think
  # of much alternative to an instanceof chain. Visitors are sometimes
  # used for multiple dispatch but seems like overkill.
  joined = nil
  if (left.is_a?(ConfigObject)) && (right.is_a?(ConfigObject))
    joined = right.with_fallback(left)
  elsif (left.is_a?(SimpleConfigList)) && (right.is_a?(SimpleConfigList))
    joined = left.concatenate(right)
  elsif (left.is_a?(SimpleConfigList) || left.is_a?(ConfigObject)) &&
         is_ignored_whitespace(right)
    joined = left
    # it should be impossible that left is whitespace and right is a list or object
  elsif (left.is_a?(Hocon::Impl::ConfigConcatenation)) ||
      (right.is_a?(Hocon::Impl::ConfigConcatenation))
    raise ConfigBugOrBrokenError, "unflattened ConfigConcatenation"
  elsif (left.is_a?(Unmergeable)) || (right.is_a?(Unmergeable))
    # leave joined=null, cannot join
  else
    # handle primitive type or primitive type mixed with object or list
    s1 = left.transform_to_string
    s2 = right.transform_to_string
    if s1.nil? || s2.nil?
      raise ConfigWrongTypeError.new(left.origin,
              "Cannot concatenate object or list with a non-object-or-list, #{left} " +
                  "and #{right} are not compatible", nil)
    else
      joined_origin = SimpleConfigOrigin.merge_origins([left.origin, right.origin])
      joined = Hocon::Impl::ConfigString::Quoted.new(joined_origin, s1 + s2)
    end
  end

  if joined.nil?
    builder.push(right)
  else
    builder.pop
    builder.push(joined)
  end
end

Instance Method Details

#==(other) ⇒ Object



257
258
259
260
261
262
263
# File 'lib/hocon/impl/config_concatenation.rb', line 257

def ==(other)
  if other.is_a? Hocon::Impl::ConfigConcatenation
    can_equal(other) && @pieces == other.pieces
  else
    false
  end
end

#can_equal(other) ⇒ Object



253
254
255
# File 'lib/hocon/impl/config_concatenation.rb', line 253

def can_equal(other)
  other.is_a? Hocon::Impl::ConfigConcatenation
end

#has_descendant?(descendant) ⇒ Boolean

Returns:

  • (Boolean)


236
237
238
# File 'lib/hocon/impl/config_concatenation.rb', line 236

def has_descendant?(descendant)
  has_descendant_in_list?(@pieces, descendant)
end

#hashObject



265
266
267
268
# File 'lib/hocon/impl/config_concatenation.rb', line 265

def hash
  # note that "origin" is deliberately NOT part of equality
  @pieces.hash
end

#ignores_fallbacks?Boolean

Returns:

  • (Boolean)


65
66
67
68
69
70
# File 'lib/hocon/impl/config_concatenation.rb', line 65

def ignores_fallbacks?
  # we can never ignore fallbacks because if a child ConfigReference
  # is self-referential we have to look lower in the merge stack
  # for its value.
  false
end

#new_copy(new_origin) ⇒ Object



61
62
63
# File 'lib/hocon/impl/config_concatenation.rb', line 61

def new_copy(new_origin)
  self.class.new(new_origin, @pieces)
end

#relativized(prefix) ⇒ Object

when you graft a substitution into another object, you have to prefix it with the location in that object where you grafted it; but save prefixLength so system property and env variable lookups don ‘t get broken.



245
246
247
248
249
250
251
# File 'lib/hocon/impl/config_concatenation.rb', line 245

def relativized(prefix)
  new_pieces = []
  @pieces.each { |p|
    new_pieces << p.relativized(prefix)
  }
  self.class.new(origin, new_pieces)
end

#render_value_to_sb(sb, indent, at_root, options) ⇒ Object



270
271
272
273
274
# File 'lib/hocon/impl/config_concatenation.rb', line 270

def render_value_to_sb(sb, indent, at_root, options)
  @pieces.each do |piece|
    piece.render_value_to_sb(sb, indent, at_root, options)
  end
end

#replace_child(child, replacement) ⇒ Object



227
228
229
230
231
232
233
234
# File 'lib/hocon/impl/config_concatenation.rb', line 227

def replace_child(child, replacement)
  new_pieces = replace_child_in_list(@pieces, child, replacement)
  if new_pieces == nil
    nil
  else
    self.class.new(origin, new_pieces)
  end
end

#resolve_statusObject



223
224
225
# File 'lib/hocon/impl/config_concatenation.rb', line 223

def resolve_status
  ResolveStatus::UNRESOLVED
end

#resolve_substitutions(context, source) ⇒ Object



168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
# File 'lib/hocon/impl/config_concatenation.rb', line 168

def resolve_substitutions(context, source)
  if Hocon::Impl::ConfigImpl.trace_substitution_enabled
    indent = context.depth + 2
    Hocon::Impl::ConfigImpl.trace("concatenation has #{@pieces.size} pieces",
                                  indent - 1)
    count = 0
    @pieces.each { |v|
      Hocon::Impl::ConfigImpl.trace("#{count}: #{v}", count)
      count += 1
    }
  end

  # Right now there's no reason to pushParent here because the
  # content of ConfigConcatenation should not need to replaceChild,
  # but if it did we'd have to do this.
  source_with_parent = source
  new_context = context

  resolved = []
  @pieces.each { |p|
    # to concat into a string we have to do a full resolve,
    # so unrestrict the context, then put restriction back afterward
    restriction = new_context.restrict_to_child
    result = new_context.unrestricted
                 .resolve(p, source_with_parent)
    r = result.value
    new_context = result.context.restrict(restriction)
    if Hocon::Impl::ConfigImpl.trace_substitution_enabled
      Hocon::Impl::ConfigImpl.trace("resolved concat piece to #{r}",
                                    context.depth)
    end

    if r
      resolved << r
    end
    # otherwise, it was optional ... omit
  }

  # now need to concat everything
  joined = self.class.consolidate(resolved)
  # if unresolved is allowed we can just become another
  # ConfigConcatenation
  if joined.size > 1 and context.options.allow_unresolved
    Hocon::Impl::ResolveResult.make(new_context, Hocon::Impl::ConfigConcatenation.new(origin, joined))
  elsif joined.empty?
    # we had just a list of optional references using ${?}
    Hocon::Impl::ResolveResult.make(new_context, nil)
  elsif joined.size == 1
    Hocon::Impl::ResolveResult.make(new_context, joined[0])
  else
    raise ConfigBugOrBrokenError.new(
              "Bug in the library; resolved list was joined to too many values: #{joined}")
  end
end

#unmerged_valuesObject



72
73
74
# File 'lib/hocon/impl/config_concatenation.rb', line 72

def unmerged_values
  [self]
end

#unwrappedObject



57
58
59
# File 'lib/hocon/impl/config_concatenation.rb', line 57

def unwrapped
  raise not_resolved
end

#value_typeObject



53
54
55
# File 'lib/hocon/impl/config_concatenation.rb', line 53

def value_type
  raise not_resolved
end