Added LwesBinaryInstaller0.1.486.19070.msi
[lwes-dotnet.git] / Org.Lwes / Event.cs
blob09ed15d4520698607f336a98ae200386cbbca584
1 //
2 // This file is part of the LWES .NET Binding (LWES.net)
3 //
4 // COPYRIGHT© 2009, Phillip Clark (phillip[at*flitbit[dot*org)
5 // original .NET implementation
6 //
7 // LWES.net is free software: you can redistribute it and/or modify
8 // it under the terms of the Lesser GNU General Public License as published by
9 // the Free Software Foundation, either version 3 of the License, or
10 // (at your option) any later version.
12 // LWES.net is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // Lesser GNU General Public License for more details.
17 // You should have received a copy of the Lesser GNU General Public License
18 // along with LWES.net. If not, see <http://www.gnu.org/licenses/>.
20 namespace Org.Lwes
22 using System;
23 using System.Collections.Generic;
24 using System.Net;
25 using System.Text;
27 using Org.Lwes.DB;
28 using Org.Lwes.ESF;
29 using Org.Lwes.Properties;
31 /// <summary>
32 /// Event base class.
33 /// </summary>
34 [Serializable]
35 public class Event
37 #region Fields
39 List<IEventAttribute> _attributes;
40 SupportedEncoding _encoding;
41 Object _sync = new Object();
42 EventTemplate _template;
43 bool _validating;
45 #endregion Fields
47 #region Constructors
49 /// <summary>
50 /// Creates a new event with the given name.
51 /// </summary>
52 public Event(string evName)
53 : this(new EventTemplate(false, evName), false, Constants.CDefaultSupportedEncoding)
57 /// <summary>
58 /// Creates a new instance with an encoding.
59 /// </summary>
60 /// <param name="enc">An encoding for the event.</param>
61 public Event(SupportedEncoding enc)
62 : this(EventTemplate.Empty, false, enc)
66 /// <summary>
67 /// Creates a new event.
68 /// </summary>
69 /// <param name="evTemplate">the event's template</param>
70 /// <param name="validating"><em>true</em> if validating should be performed on the event;
71 /// otherwise <em>false</em></param>
72 /// <param name="enc">indicates the encoding to use for the event's character data</param>
73 public Event(EventTemplate evTemplate, bool validating, SupportedEncoding enc)
75 _template = evTemplate;
76 _validating = validating;
77 _encoding = enc;
78 _attributes = new List<IEventAttribute>(evTemplate.Count);
81 #endregion Constructors
83 #region Properties
85 /// <summary>
86 /// Gets the attribute count.
87 /// </summary>
88 public int AttributeCount
90 get { return _template.Count; }
93 /// <summary>
94 /// Gets the encoding used when the event is encoded for binary transmission.
95 /// </summary>
96 public SupportedEncoding Encoding
98 get { return _encoding; }
101 /// <summary>
102 /// The event's name.
103 /// </summary>
104 public string Name
106 get { return _template.Name; }
109 #endregion Properties
111 #region Indexers
113 /// <summary>
114 /// Gets an attribute by name.
115 /// </summary>
116 /// <param name="name">attribute name</param>
117 /// <returns>the attribute if it exists; otherwise an exception is thrown</returns>
118 /// <exception cref="ArgumentOutOfRangeException">thrown if an attribute with the given <paramref name="name"/> doesn't exist</exception>
119 public IEventAttribute this[string name]
123 int ord;
124 if (!_template.TryGetOrdinal(name, out ord))
125 throw new ArgumentOutOfRangeException("name", String.Format(Resources.Error_AttributeNotDefined, name));
126 return _attributes[ord];
130 /// <summary>
131 /// Gets an attribute by ordinal position.
132 /// </summary>
133 /// <param name="ord">ordinal position of the attribute</param>
134 /// <returns>an attribute at the <paramref name="ordinal"/> position given</returns>
135 /// <exception cref="ArgumentOutOfRangeException">thrown if the <paramref name="ordinal"/> is out of range</exception>
136 public IEventAttribute this[int ord]
140 if (0 > ord || ord >= _attributes.Count)
141 throw new ArgumentOutOfRangeException("ordinal");
142 return _attributes[ord];
146 #endregion Indexers
148 #region Methods
150 /// <summary>
151 /// Gets an attribute's value.
152 /// </summary>
153 /// <typeparam name="V">value type V</typeparam>
154 /// <param name="name">attribute's name.</param>
155 /// <returns>The value of the attribute, as type V</returns>
156 /// <exception cref="InvalidCastException">if the attribute's value cannot be
157 /// coerced into value type V</exception>
158 /// <exception cref="NoSuchAttributeException">thrown if an attribute with the given name
159 /// does not exist.</exception>
160 public V GetValue<V>(string name)
162 int ord;
163 if (_template.TryGetOrdinal(name, out ord))
165 return _attributes[ord].GetValue<V>();
167 throw new ArgumentOutOfRangeException("name", String.Format(Resources.Error_AttributeNotDefined, name));
170 /// <summary>
171 /// Sets an attribute's value.
172 /// </summary>
173 /// <param name="name">the attribute's name</param>
174 /// <param name="value">the attribute's UInt16 value</param>
175 public void SetValue(string name, UInt16 value)
177 int ord;
178 AttributeTemplate attr;
179 if (_template.TryGetOrdinal(name, out ord))
181 attr = _template[ord];
183 // Validate if necessary
184 if (_validating && !attr.IsAssignable(value))
185 throw new AttributeNotSetException("name");
187 _attributes[ord] = _attributes[ord].Mutate(_template, value);
190 if (_validating)
192 // Allow for SiteID or SenderPort inherited from MetaInfoEvent
193 if (!String.Equals(Constants.MetaEventInfoAttributes.SiteID.Name, name)
194 && !String.Equals(Constants.MetaEventInfoAttributes.SenderPort.Name, name))
195 throw new ArgumentOutOfRangeException("name", String.Format(Resources.Error_AttributeNotDefined, name));
198 // Adding a new attribute
199 AppendAttribute(AttributeTemplate.CreateTemplateForVariable(name, 0, value), value);
202 /// <summary>
203 /// Sets an attribute's value.
204 /// </summary>
205 /// <param name="name">the attribute's name</param>
206 /// <param name="value">the attribute's Int16 value</param>
207 public void SetValue(string name, Int16 value)
209 int ord;
210 AttributeTemplate attr;
211 if (_template.TryGetOrdinal(name, out ord))
213 attr = _template[ord];
215 // Validate if necessary
216 if (_validating && !attr.IsAssignable(value))
217 throw new AttributeNotSetException("name");
219 _attributes[ord] = _attributes[ord].Mutate(_template, value);
222 // Allow for Encoding inherited from MetaInfoEvent
223 if (String.Equals(Constants.MetaEventInfoAttributes.Encoding.Name, name))
225 // encoding must be the first attribute (when encoded), make it so
226 PrependAttribute(Constants.MetaEventInfoAttributes.Encoding, value);
229 if (_validating)
230 throw new ArgumentOutOfRangeException("name", String.Format(Resources.Error_AttributeNotDefined, name));
232 // Adding a new attribute
233 AppendAttribute(AttributeTemplate.CreateTemplateForVariable(name, 0, value), value);
236 /// <summary>
237 /// Sets an attribute's value.
238 /// </summary>
239 /// <param name="name">the attribute's name</param>
240 /// <param name="value">the attribute's UInt32 value</param>
241 public void SetValue(string name, UInt32 value)
243 int ord;
244 AttributeTemplate attr;
245 if (_template.TryGetOrdinal(name, out ord))
247 attr = _template[ord];
249 // Validate if necessary
250 if (_validating && !attr.IsAssignable(value))
251 throw new AttributeNotSetException("name");
253 _attributes[ord] = _attributes[ord].Mutate(_template, value);
256 if (_validating)
257 throw new ArgumentOutOfRangeException("name", String.Format(Resources.Error_AttributeNotDefined, name));
259 // Adding a new attribute
260 AppendAttribute(AttributeTemplate.CreateTemplateForVariable(name, 0, value), value);
263 /// <summary>
264 /// Sets an attribute's value.
265 /// </summary>
266 /// <param name="name">the attribute's name</param>
267 /// <param name="value">the attribute's Int32 value</param>
268 public void SetValue(string name, Int32 value)
270 int ord;
271 AttributeTemplate attr;
272 if (_template.TryGetOrdinal(name, out ord))
274 attr = _template[ord];
276 // Validate if necessary
277 if (_validating && !attr.IsAssignable(value))
278 throw new AttributeNotSetException("name");
280 _attributes[ord] = _attributes[ord].Mutate(_template, value);
283 if (_validating)
284 throw new ArgumentOutOfRangeException("name", String.Format(Resources.Error_AttributeNotDefined, name));
286 // Adding a new attribute
287 AppendAttribute(AttributeTemplate.CreateTemplateForVariable(name, 0, value), value);
290 /// <summary>
291 /// Sets an attribute's value.
292 /// </summary>
293 /// <param name="name">the attribute's name</param>
294 /// <param name="value">the attribute's String value</param>
295 public void SetValue(string name, String value)
297 int ord;
298 AttributeTemplate attr;
299 if (_template.TryGetOrdinal(name, out ord))
301 attr = _template[ord];
303 // Validate if necessary
304 if (_validating && !attr.IsAssignable(value))
305 throw new AttributeNotSetException("name");
307 _attributes[ord] = _attributes[ord].Mutate(_template, value);
310 if (_validating)
311 throw new ArgumentOutOfRangeException("name", String.Format(Resources.Error_AttributeNotDefined, name));
313 // Adding a new attribute
314 AppendAttribute( AttributeTemplate.CreateTemplateForVariable(name, 0, value), value);
317 /// <summary>
318 /// Sets an attribute's value.
319 /// </summary>
320 /// <param name="name">the attribute's name</param>
321 /// <param name="value">the attribute's IPAddress value</param>
322 public void SetValue(string name, IPAddress value)
324 int ord;
325 AttributeTemplate attr;
326 if (_template.TryGetOrdinal(name, out ord))
328 attr = _template[ord];
330 // Validate if necessary
331 if (_validating && !attr.IsAssignable(value))
332 throw new AttributeNotSetException("name");
334 _attributes[ord] = _attributes[ord].Mutate(_template, value);
337 if (_validating)
339 // Allow for SenderIP inherited from MetaInfoEvent
340 if (!String.Equals(Constants.MetaEventInfoAttributes.SenderIP.Name, name))
341 throw new ArgumentOutOfRangeException("name", String.Format(Resources.Error_AttributeNotDefined, name));
344 // Adding a new attribute
345 AppendAttribute(AttributeTemplate.CreateTemplateForVariable(name, 0, value), value);
348 /// <summary>
349 /// Sets an attribute's value.
350 /// </summary>
351 /// <param name="name">the attribute's name</param>
352 /// <param name="value">the attribute's Int64 value</param>
353 public void SetValue(string name, Int64 value)
355 int ord;
356 AttributeTemplate attr;
357 if (_template.TryGetOrdinal(name, out ord))
359 attr = _template[ord];
361 // Validate if necessary
362 if (_validating && !attr.IsAssignable(value))
363 throw new AttributeNotSetException("name");
365 _attributes[ord] = _attributes[ord].Mutate(_template, value);
368 if (_validating)
370 // Allow for ReceiptTime inherited from MetaInfoEvent
371 if (!String.Equals(Constants.MetaEventInfoAttributes.ReceiptTime.Name, name))
372 throw new ArgumentOutOfRangeException("name", String.Format(Resources.Error_AttributeNotDefined, name));
375 // Adding a new attribute
376 AppendAttribute(AttributeTemplate.CreateTemplateForVariable(name, 0, value), value);
379 /// <summary>
380 /// Sets an attribute's value.
381 /// </summary>
382 /// <param name="name">the attribute's name</param>
383 /// <param name="value">the attribute's UInt64 value</param>
384 public void SetValue(string name, UInt64 value)
386 int ord;
387 AttributeTemplate attr;
388 if (_template.TryGetOrdinal(name, out ord))
390 attr = _template[ord];
392 // Validate if necessary
393 if (_validating && !attr.IsAssignable(value))
394 throw new AttributeNotSetException("name");
396 _attributes[ord] = _attributes[ord].Mutate(_template, value);
399 if (_validating)
400 throw new ArgumentOutOfRangeException("name", String.Format(Resources.Error_AttributeNotDefined, name));
402 // Adding a new attribute
403 AppendAttribute(AttributeTemplate.CreateTemplateForVariable(name, 0, value), value);
406 /// <summary>
407 /// Sets an attribute's value.
408 /// </summary>
409 /// <param name="name">the attribute's name</param>
410 /// <param name="value">the attribute's Boolean value</param>
411 public void SetValue(string name, Boolean value)
413 int ord;
414 AttributeTemplate attr;
415 if (_template.TryGetOrdinal(name, out ord))
417 attr = _template[ord];
419 // Validate if necessary
420 if (_validating && !attr.IsAssignable(value))
421 throw new AttributeNotSetException("name");
423 _attributes[ord] = _attributes[ord].Mutate(_template, value);
426 if (_validating)
427 throw new ArgumentOutOfRangeException("name", String.Format(Resources.Error_AttributeNotDefined, name));
429 // Adding a new attribute
430 AppendAttribute( AttributeTemplate.CreateTemplateForVariable(name, 0, value), value);
433 /// <summary>
434 /// Converts the event to a string.
435 /// </summary>
436 /// <returns>String containing the event.</returns>
437 public override string ToString()
439 StringBuilder s = new StringBuilder(400);
440 s.Append(_template.Name).Append(": { Name: '").Append(_template.Name)
441 .Append("', EsfPresent: ").Append(_template.FromEsf)
442 .Append(", Attributes: {");
443 if (_attributes != null)
445 int i = 0;
446 foreach (var a in _attributes)
448 if (i++ > 0) s.Append(", ").Append(a);
449 else s.Append(a);
452 return s.Append("}}").ToString();
455 /// <summary>
456 /// Converts the event to a string.
457 /// </summary>
458 /// <param name="humanReadable">Whether the output should be formatted for human readability</param>
459 /// <returns>string contianing the event</returns>
460 public string ToString(bool humanReadable)
462 if (!humanReadable) return ToString();
464 string nl = Environment.NewLine;
466 StringBuilder s = new StringBuilder(400);
467 s.Append(_template.Name).Append(":").Append(nl).Append(" { Name: '").Append(_template.Name)
468 .Append("'").Append(nl).Append(" , EsfPresent: ").Append(_template.FromEsf)
469 .Append(nl).Append(" , Attributes: {");
470 if (_attributes != null)
472 int i = 0;
473 foreach (var a in _attributes)
475 if (i++ > 0) s.Append(nl).Append(" , ").Append(a);
476 else s.Append(a);
479 return s.Append(nl).Append(" }").Append(nl).Append(" }").ToString();
482 /// <summary>
483 /// Tries to get an attribute.
484 /// </summary>
485 /// <param name="name">attribute's name</param>
486 /// <param name="value">reference to a variable where the attribute will be stored upon success</param>
487 /// <returns><em>true</em> if the attribute exists; otherwise <em>false</em></returns>
488 public bool TryGetAttribute(string name, out IEventAttribute value)
490 int ord;
491 if (_template.TryGetOrdinal(name, out ord))
493 value = _attributes[ord];
494 return true;
496 value = default(IEventAttribute);
497 return false;
500 /// <summary>
501 /// Tries to get an attribute's value as a UInt16.
502 /// </summary>
503 /// <param name="attr">the attribute's name</param>
504 /// <param name="value">reference to a variable to hold the value</param>
505 /// <returns><em>true</em> if the value can be retrieved as a UInt16, otherwise <em>false</em></returns>
506 public bool TryGetValue(string attr, out ushort value)
508 int ord;
509 if (_template.TryGetOrdinal(attr, out ord))
511 return _attributes[ord].TryGetValue(out value);
513 value = default(ushort);
514 return false;
517 /// <summary>
518 /// Tries to get an attribute's value as a Int16.
519 /// </summary>
520 /// <param name="attr">the attribute's name</param>
521 /// <param name="value">reference to a variable to hold the value</param>
522 /// <returns><em>true</em> if the value can be retrieved as a Int16, otherwise <em>false</em></returns>
523 public bool TryGetValue(string attr, out short value)
525 int ord;
526 if (_template.TryGetOrdinal(attr, out ord))
528 return _attributes[ord].TryGetValue(out value);
530 value = default(short);
531 return false;
534 /// <summary>
535 /// Tries to get an attribute's value as a UInt32.
536 /// </summary>
537 /// <param name="attr">the attribute's name</param>
538 /// <param name="value">reference to a variable to hold the value</param>
539 /// <returns><em>true</em> if the value can be retrieved as a UInt32, otherwise <em>false</em></returns>
540 public bool TryGetValue(string attr, out uint value)
542 int ord;
543 if (_template.TryGetOrdinal(attr, out ord))
545 return _attributes[ord].TryGetValue(out value);
547 value = default(uint);
548 return false;
551 /// <summary>
552 /// Tries to get an attribute's value as a Int32.
553 /// </summary>
554 /// <param name="attr">the attribute's name</param>
555 /// <param name="value">reference to a variable to hold the value</param>
556 /// <returns><em>true</em> if the value can be retrieved as a Int32, otherwise <em>false</em></returns>
557 public bool TryGetValue(string attr, out int value)
559 int ord;
560 if (_template.TryGetOrdinal(attr, out ord))
562 return _attributes[ord].TryGetValue(out value);
564 value = default(int);
565 return false;
568 /// <summary>
569 /// Tries to get an attribute's value as a string.
570 /// </summary>
571 /// <param name="attr">the attribute's name</param>
572 /// <param name="value">reference to a variable to hold the value</param>
573 /// <returns><em>true</em> if the value can be retrieved as a string, otherwise <em>false</em></returns>
574 public bool TryGetValue(string attr, out string value)
576 int ord;
577 if (_template.TryGetOrdinal(attr, out ord))
579 return _attributes[ord].TryGetValue(out value);
581 value = default(string);
582 return false;
585 /// <summary>
586 /// Tries to get an attribute's value as a IPAddress.
587 /// </summary>
588 /// <param name="attr">the attribute's name</param>
589 /// <param name="value">reference to a variable to hold the value</param>
590 /// <returns><em>true</em> if the value can be retrieved as a IPAddress, otherwise <em>false</em></returns>
591 public bool TryGetValue(string attr, out IPAddress value)
593 int ord;
594 if (_template.TryGetOrdinal(attr, out ord))
596 return _attributes[ord].TryGetValue(out value);
598 value = default(IPAddress);
599 return false;
602 /// <summary>
603 /// Tries to get an attribute's value as a UInt16.
604 /// </summary>
605 /// <param name="attr">the attribute's name</param>
606 /// <param name="value">reference to a variable to hold the value</param>
607 /// <returns><em>true</em> if the value can be retrieved as a UInt16, otherwise <em>false</em></returns>
608 public bool TryGetValue(string attr, out long value)
610 int ord;
611 if (_template.TryGetOrdinal(attr, out ord))
613 return _attributes[ord].TryGetValue(out value);
615 value = default(long);
616 return false;
619 /// <summary>
620 /// Tries to get an attribute's value as a UInt64.
621 /// </summary>
622 /// <param name="attr">the attribute's name</param>
623 /// <param name="value">reference to a variable to hold the value</param>
624 /// <returns><em>true</em> if the value can be retrieved as a UInt64, otherwise <em>false</em></returns>
625 public bool TryGetValue(string attr, out ulong value)
627 int ord;
628 if (_template.TryGetOrdinal(attr, out ord))
630 return _attributes[ord].TryGetValue(out value);
632 value = default(ulong);
633 return false;
636 /// <summary>
637 /// Tries to get an attribute's value as a Boolean.
638 /// </summary>
639 /// <param name="attr">the attribute's name</param>
640 /// <param name="value">reference to a variable to hold the value</param>
641 /// <returns><em>true</em> if the value can be retrieved as a Boolean, otherwise <em>false</em></returns>
642 public bool TryGetValue(string attr, out bool value)
644 int ord;
645 if (_template.TryGetOrdinal(attr, out ord))
647 return _attributes[ord].TryGetValue(out value);
649 value = default(bool);
650 return false;
653 internal static Event BinaryDecode(IEventTemplateDB db, byte[] buffer, int offset, int count)
655 Decoder dec = Constants.DefaultEncoding.GetDecoder();
656 Decoder bodyDecoder = dec;
657 int ofs = offset, end_of_input = offset + count;
659 string eventName = LwesSerializer.ReadEVENTWORD(buffer, ref ofs, dec);
660 UInt16 attributeCount = LwesSerializer.ReadUInt16(buffer, ref ofs);
662 Event ev;
663 if (!db.TryCreateEvent(eventName, out ev, false, SupportedEncoding.Default))
665 ev = new Event(new EventTemplate(false, eventName), false, SupportedEncoding.Default);
668 for (int i = 0; i < attributeCount && ofs < end_of_input; i++)
670 string attributeName = LwesSerializer.ReadATTRIBUTEWORD(buffer, ref ofs, dec);
671 TypeToken attributeTokenType = (TypeToken)LwesSerializer.ReadByte(buffer, ref ofs);
673 // Encoding is in the first attribute position if it is present
674 if (i == 0
675 && String.Equals(Constants.MetaEventInfoAttributes.Encoding, attributeName)
676 && attributeTokenType == TypeToken.UINT16)
678 Int16 changeEncoding = LwesSerializer.ReadInt16(buffer, ref ofs);
679 bodyDecoder = Constants.GetEncoding(changeEncoding).GetDecoder();
680 ev.SetValue(Constants.MetaEventInfoAttributes.Encoding.Name, changeEncoding);
682 else
684 switch (attributeTokenType)
686 case TypeToken.UINT16:
687 ev.SetValue(attributeName, LwesSerializer.ReadUInt16(buffer, ref ofs));
688 break;
689 case TypeToken.INT16:
690 ev.SetValue(attributeName, LwesSerializer.ReadInt16(buffer, ref ofs));
691 break;
692 case TypeToken.UINT32:
693 ev.SetValue(attributeName, LwesSerializer.ReadUInt32(buffer, ref ofs));
694 break;
695 case TypeToken.INT32:
696 ev.SetValue(attributeName, LwesSerializer.ReadInt32(buffer, ref ofs));
697 break;
698 case TypeToken.STRING:
699 ev.SetValue(attributeName, LwesSerializer.ReadString(buffer, ref ofs, bodyDecoder));
700 break;
701 case TypeToken.IP_ADDR:
702 ev.SetValue(attributeName, LwesSerializer.ReadIPAddress(buffer, ref ofs));
703 break;
704 case TypeToken.INT64:
705 ev.SetValue(attributeName, LwesSerializer.ReadInt64(buffer, ref ofs));
706 break;
707 case TypeToken.UINT64:
708 ev.SetValue(attributeName, LwesSerializer.ReadUInt64(buffer, ref ofs));
709 break;
710 case TypeToken.BOOLEAN:
711 ev.SetValue(attributeName, LwesSerializer.ReadBoolean(buffer, ref ofs));
712 break;
717 return ev;
720 internal int BinaryEncode(byte[] buffer, int offset)
722 /* Encoded using the following protocol:
724 * EVENTWORD,<number of elements>,ATTRIBUTEWORD,TYPETOKEN,
725 * (UINT16|INT16|UINT32|INT32|UINT64|INT64|BOOLEAN|STRING)
726 * ...ATTRIBUTEWORD,TYPETOKEN(UINT16|INT16|UINT32|INT32|
727 * UINT64|INT64|BOOLEAN|STRING)
729 * The EVENTWORD and ATTRIBUTEWORD(s) are encoded with
730 * the default encoding, the first attribute will be the
731 * encoding for strings if present (may differ from the
732 * encoding used for EVENTWORD and ATTRIBUTEWORD(s)
734 int count = 0, ofs = offset;
735 Encoder utf8 = Constants.DefaultEncoding.GetEncoder();
737 lock (_sync)
739 Encoder bodyEncoder = Constants.GetEncoding((short)_encoding).GetEncoder();
741 // Encode the EVENTWORD + attribute-count
742 _template.BinaryEncode(buffer, ref ofs);
744 // All of the template and dynamic attributes are encoded...
745 for (int i = 0; i < _attributes.Count; i++)
747 count += _attributes[i].BinaryEncode(buffer, ref ofs, bodyEncoder);
751 offset = ofs;
753 return count;
756 internal int CalculateEncodedByteCount()
758 lock (_sync)
760 Encoding enc = Constants.GetEncoding((short)_encoding);
761 int count = _template.GetByteCount();
763 // All of the template and dynamic attributes are encoded...
764 foreach (var a in _attributes)
766 count += a.GetByteCount(enc);
768 return count;
772 private void AppendAttribute<T>(AttributeTemplate attr, T value)
774 lock (_sync)
776 _template = _template.AppendAttributes(attr);
777 _attributes.Add(new EventAttribute<T>(attr, value));
781 private void PrependAttribute<T>(AttributeTemplate attr, T value)
783 lock (_sync)
785 EventTemplate et = _template.PrependAttributes(attr);
786 _attributes.Insert(0, new EventAttribute<T>(attr, value));
790 #endregion Methods