Skip to content

Commit 963efa4

Browse files
committed
Cleanup
1 parent 9b54431 commit 963efa4

File tree

5 files changed

+518
-891
lines changed

5 files changed

+518
-891
lines changed

Attribute.cs

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Numerics;
4+
5+
using AttrKVP = System.Collections.Generic.KeyValuePair<string, object>;
6+
7+
namespace Datamodel
8+
{
9+
/// <summary>
10+
/// A name/value pair associated with an <see cref="Element"/>.
11+
/// </summary>
12+
class Attribute
13+
{
14+
/// <summary>
15+
/// Creates a new Attribute with a specified name and value.
16+
/// </summary>
17+
/// <param name="name">The name of the Attribute, which must be unique to its owner.</param>
18+
/// <param name="value">The value of the Attribute, which must be of a supported Datamodel type.</param>
19+
public Attribute(string name, AttributeList owner, object value)
20+
{
21+
ArgumentNullException.ThrowIfNull(name);
22+
23+
Name = name;
24+
_Owner = owner;
25+
Value = value;
26+
}
27+
28+
/// <summary>
29+
/// Creates a new Attribute with deferred loading.
30+
/// </summary>
31+
/// <param name="name">The name of the Attribute, which must be unique to its owner.</param>
32+
/// <param name="owner">The AttributeList which owns this Attribute.</param>
33+
/// <param name="defer_offset">The location in the encoded DMX stream at which this Attribute's value can be found.</param>
34+
public Attribute(string name, AttributeList owner, long defer_offset)
35+
: this(name, owner, null)
36+
{
37+
ArgumentNullException.ThrowIfNull(owner);
38+
39+
Offset = defer_offset;
40+
}
41+
42+
#region Properties
43+
/// <summary>
44+
/// Gets the name of this Attribute.
45+
/// </summary>
46+
public string Name { get; private set; }
47+
48+
/// <summary>
49+
/// Gets the Type of this Attribute's Value.
50+
/// </summary>
51+
public Type ValueType { get; private set; }
52+
53+
/// <summary>
54+
/// Gets or sets the OverrideType of this Attributes.
55+
/// </summary>
56+
public AttributeList.OverrideType? OverrideType
57+
{
58+
get
59+
{
60+
return _OverrideType;
61+
}
62+
set
63+
{
64+
switch (value)
65+
{
66+
case null:
67+
break;
68+
case AttributeList.OverrideType.Angle:
69+
if (ValueType != typeof(Vector3))
70+
throw new AttributeTypeException("OverrideType.Angle can only be applied to Vector3 attributes");
71+
break;
72+
case AttributeList.OverrideType.Binary:
73+
if (ValueType != typeof(byte[]))
74+
throw new AttributeTypeException("OverrideType.Binary can only be applied to byte[] attributes");
75+
break;
76+
default:
77+
throw new NotImplementedException();
78+
}
79+
_OverrideType = value;
80+
}
81+
}
82+
AttributeList.OverrideType? _OverrideType;
83+
84+
/// <summary>
85+
/// Gets the <see cref="AttributeList"/> which this Attribute is a member of.
86+
/// </summary>
87+
public AttributeList Owner
88+
{
89+
get { return _Owner; }
90+
internal set
91+
{
92+
if (_Owner == value) return;
93+
94+
if (Deferred && _Owner != null) DeferredLoad();
95+
_Owner = value;
96+
}
97+
}
98+
AttributeList _Owner;
99+
100+
Datamodel OwnerDatamodel { get { return Owner?.Owner; } }
101+
102+
/// <summary>
103+
/// Gets whether the value of this Attribute has yet to be decoded.
104+
/// </summary>
105+
public bool Deferred { get { return Offset != 0; } }
106+
107+
/// <summary>
108+
/// Loads the value of this Attribute from the encoded source Datamodel.
109+
/// </summary>
110+
/// <exception cref="InvalidOperationException">Thrown when the Attribute has already been loaded.</exception>
111+
/// <exception cref="CodecException">Thrown when the deferred load fails.</exception>
112+
public void DeferredLoad()
113+
{
114+
if (Offset == 0) throw new InvalidOperationException("Attribute already loaded.");
115+
116+
if (OwnerDatamodel == null || OwnerDatamodel.Codec == null)
117+
throw new CodecException("Trying to load a deferred Attribute, but could not find codec.");
118+
119+
try
120+
{
121+
lock (OwnerDatamodel.Codec)
122+
{
123+
_Value = OwnerDatamodel.Codec.DeferredDecodeAttribute(OwnerDatamodel, Offset);
124+
}
125+
}
126+
catch (Exception err)
127+
{
128+
throw new CodecException($"Deferred loading of attribute \"{Name}\" on element {((Element)Owner).ID} using {OwnerDatamodel.Codec} codec threw an exception.", err);
129+
}
130+
Offset = 0;
131+
132+
if (_Value is ElementArray elem_array)
133+
elem_array.Owner = Owner;
134+
}
135+
136+
/// <summary>
137+
/// Gets or sets the value held by this Attribute.
138+
/// </summary>
139+
/// <exception cref="CodecException">Thrown when deferred value loading fails.</exception>
140+
/// <exception cref="DestubException">Thrown when Element destubbing fails.</exception>
141+
public object Value
142+
{
143+
get
144+
{
145+
if (Offset > 0)
146+
DeferredLoad();
147+
148+
if (OwnerDatamodel != null)
149+
{
150+
// expand stubs
151+
if (_Value is Element elem && elem.Stub)
152+
{
153+
try { _Value = OwnerDatamodel.OnStubRequest(elem.ID) ?? _Value; }
154+
catch (Exception err) { throw new DestubException(this, err); }
155+
}
156+
}
157+
158+
return _Value;
159+
}
160+
set
161+
{
162+
ValueType = value == null ? typeof(Element) : value.GetType();
163+
164+
if (!Datamodel.IsDatamodelType(ValueType))
165+
throw new AttributeTypeException(String.Format("{0} is not a valid Datamodel attribute type. (If this is an array, it must implement IList<T>).", ValueType.FullName));
166+
167+
if (value is Element elem)
168+
{
169+
if (elem.Owner == null)
170+
elem.Owner = OwnerDatamodel;
171+
else if (elem.Owner != OwnerDatamodel)
172+
throw new ElementOwnershipException();
173+
}
174+
175+
if (value is IEnumerable<Element> elem_enumerable)
176+
{
177+
if (elem_enumerable is not ElementArray)
178+
throw new InvalidOperationException("Element array objects must derive from Datamodel.ElementArray");
179+
180+
var elem_array = (ElementArray)value;
181+
if (elem_array.Owner == null)
182+
elem_array.Owner = Owner;
183+
else if (elem_array.Owner != Owner)
184+
throw new InvalidOperationException("ElementArray is already owned by a different Datamodel.");
185+
186+
foreach (var arr_elem in elem_array)
187+
{
188+
if (arr_elem == null) continue;
189+
else if (arr_elem.Owner == null)
190+
arr_elem.Owner = OwnerDatamodel;
191+
else if (arr_elem.Owner != OwnerDatamodel)
192+
throw new ElementOwnershipException("One or more Elements in the assigned collection are from a different Datamodel. Use ImportElement() to copy them to this one before assigning.");
193+
}
194+
}
195+
196+
_Value = value;
197+
Offset = 0;
198+
}
199+
}
200+
object _Value;
201+
202+
/// <summary>
203+
/// Gets the Attribute's Value without attempting deferred loading or destubbing.
204+
/// </summary>
205+
public object RawValue { get { return _Value; } }
206+
207+
#endregion
208+
209+
long Offset;
210+
211+
public override string ToString()
212+
{
213+
var type = Value != null ? Value.GetType() : typeof(Element);
214+
var inner_type = Datamodel.GetArrayInnerType(type);
215+
return String.Format("{0} <{1}>", Name, inner_type != null ? inner_type.FullName + "[]" : type.FullName);
216+
}
217+
218+
public AttrKVP ToKeyValuePair()
219+
{
220+
return new AttrKVP(Name, Value);
221+
}
222+
}
223+
}

0 commit comments

Comments
 (0)