Skip to content

Commit ef70a15

Browse files
ArkatufusAaronontheweb
authored andcommitted
Fix for #89 and #92, Object fields containing objects may not return the proper object value in certain circumstances. (#93)
1 parent e718f3c commit ef70a15

File tree

4 files changed

+138
-13
lines changed

4 files changed

+138
-13
lines changed

src/Hocon.Tests/ArrayAndObjectConcatenation.cs

+27
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,33 @@ public void CanConcatenateObjectsWhenMerged()
191191
Assert.Equal(2, config.GetInt("a.c"));
192192
}
193193

194+
[Fact]
195+
public void CanConcatenateObjectsWhenMerged_Issue89()
196+
{
197+
var hocon = @"
198+
a {
199+
aa: 1
200+
bb: ""2""
201+
}
202+
203+
a {
204+
cc: 3.3
205+
}
206+
";
207+
var config = Parser.Parse(hocon);
208+
Assert.Equal(1, config.GetInt("a.aa"));
209+
Assert.Equal("2", config.GetString("a.bb"));
210+
Assert.Equal(3.3, config.GetDouble("a.cc"));
211+
_output.WriteLine(config.PrettyPrint(2));
212+
Assert.Equal(@"{
213+
a : {
214+
aa : 1,
215+
bb : ""2"",
216+
cc : 3.3
217+
}
218+
}", config.PrettyPrint(2));
219+
}
220+
194221
// Undefined behavior in spec.
195222
// In this implementation, mixed value types in an array will throw an exception.
196223
[Theory]

src/Hocon.Tests/DuplicateKeysAndObjectMerging.cs

+42
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,19 @@
1111
using System.Text;
1212
using System.Threading.Tasks;
1313
using Xunit;
14+
using Xunit.Abstractions;
1415

1516
namespace Hocon.Tests
1617
{
1718
public class DuplicateKeysAndObjectMerging
1819
{
20+
private readonly ITestOutputHelper _output;
21+
22+
public DuplicateKeysAndObjectMerging(ITestOutputHelper output)
23+
{
24+
_output = output;
25+
}
26+
1927
/*
2028
* FACT:
2129
* duplicate keys that appear later override those that appear earlier, unless both values are objects
@@ -138,5 +146,39 @@ public void ObjectCanMixBraceAndDotSyntax()
138146
Assert.Equal(2, config.GetInt("foo.y"));
139147
Assert.Equal(32, config.GetInt("foo.z"));
140148
}
149+
150+
[Fact]
151+
public void CanMixObjectMergeAndSubstitutions_Issue92()
152+
{
153+
var hocon =
154+
@"x={ q : 10 }
155+
y=5
156+
157+
a=1
158+
a.q.r.s=${b}
159+
a=${y}
160+
a=${x}
161+
a={ c : 3 }
162+
163+
b=${x}
164+
b=${y}
165+
166+
// nesting ConfigDelayed inside another one
167+
c=${x}
168+
c={ d : 600, e : ${a}, f : ${b} }";
169+
170+
var config = Parser.Parse(hocon);
171+
_output.WriteLine(config.PrettyPrint(2));
172+
Assert.Equal(3, config.GetInt("a.c"));
173+
Assert.Equal(10, config.GetInt("a.q"));
174+
Assert.Equal(5, config.GetInt("b"));
175+
Assert.Equal(600, config.GetInt("c.d"));
176+
Assert.Equal(3, config.GetInt("c.e.c"));
177+
Assert.Equal(10, config.GetInt("c.e.q"));
178+
Assert.Equal(5, config.GetInt("c.f"));
179+
Assert.Equal(10, config.GetInt("c.q"));
180+
Assert.Equal(10, config.GetInt("x.q"));
181+
Assert.Equal(5, config.GetInt("y"));
182+
}
141183
}
142184
}

src/Hocon/Impl/HoconField.cs

+67-11
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,44 @@ internal HoconField ParentField
5757

5858
public HoconValue Value
5959
{
60-
get => _internalValues.Count > 0 ? _internalValues.Last() : HoconValue.Undefined;
61-
//set => _internalValues.Add(value);
60+
get
61+
{
62+
switch (_internalValues.Count)
63+
{
64+
case 0:
65+
return HoconValue.Undefined;
66+
case 1:
67+
return _internalValues[0];
68+
default:
69+
var lastValue = _internalValues.Last();
70+
if (lastValue.Type != HoconType.Object)
71+
return lastValue;
72+
73+
var objects = new List<HoconObject>();
74+
foreach (var val in _internalValues)
75+
{
76+
if (val.Type != HoconType.Object)
77+
objects.Clear();
78+
else
79+
objects.Add(val.GetObject());
80+
}
81+
82+
var value = new HoconValue(this);
83+
switch (objects.Count)
84+
{
85+
case 0:
86+
throw new HoconException("Should never reach this line.");
87+
case 1:
88+
value.Add(objects[0]);
89+
break;
90+
default:
91+
var resultObject = new HoconMergedObject(value, objects);
92+
value.Add(resultObject);
93+
break;
94+
}
95+
return value;
96+
}
97+
}
6298
}
6399

64100
public HoconField(HoconPath path, HoconObject parent)
@@ -84,19 +120,39 @@ internal void EnsureFieldIsObject()
84120
internal List<HoconSubstitution> SetValue(HoconValue value)
85121
{
86122
var removedSubs = new List<HoconSubstitution>();
87-
if (value.Type == HoconType.Array || value.Type == HoconType.Literal)
123+
124+
if (_internalValues.Count == 0)
88125
{
89-
var subs = value.GetSubstitutions();
90-
if (subs.All(sub => sub.Path != Path))
91-
{
92-
foreach (var item in _internalValues)
126+
_internalValues.Add(value);
127+
return removedSubs;
128+
}
129+
130+
switch (value.Type)
131+
{
132+
case HoconType.Array:
133+
case HoconType.Literal:
134+
var subs = value.GetSubstitutions();
135+
if (subs.All(sub => sub.Path != Path))
93136
{
94-
removedSubs.AddRange(item.GetSubstitutions());
137+
foreach (var item in _internalValues)
138+
{
139+
removedSubs.AddRange(item.GetSubstitutions());
140+
}
141+
_internalValues.Clear();
95142
}
96-
_internalValues.Clear();
97-
}
143+
_internalValues.Add(value);
144+
break;
145+
case HoconType.Object:
146+
var lastValue = _internalValues.Last();
147+
if (lastValue.Type != HoconType.Object)
148+
_internalValues.Add(value);
149+
else
150+
lastValue.GetObject().Merge(value.GetObject());
151+
break;
152+
default:
153+
_internalValues.Add(value);
154+
break;
98155
}
99-
_internalValues.Add(value);
100156
return removedSubs;
101157
}
102158

src/Hocon/Impl/HoconSubstitution.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -99,10 +99,10 @@ internal HoconSubstitution(IHoconElement parent, HoconPath path, IHoconLineInfo
9999
public string Raw => ResolvedValue?.Raw;
100100

101101
/// <inheritdoc />
102-
public List<HoconValue> GetArray() => ResolvedValue?.GetArray() ?? new List<HoconValue>();
102+
public List<HoconValue> GetArray() => ResolvedValue?.GetArray();
103103

104104
/// <inheritdoc />
105-
public HoconObject GetObject() => ResolvedValue?.GetObject() ?? new HoconObject(Parent);
105+
public HoconObject GetObject() => ResolvedValue?.GetObject();
106106

107107
/// <summary>
108108
/// Returns the string representation of this element.

0 commit comments

Comments
 (0)