aboutsummaryrefslogtreecommitdiff
path: root/src/WixToolset.Data/Serialize/ElementCollection.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/WixToolset.Data/Serialize/ElementCollection.cs')
-rw-r--r--src/WixToolset.Data/Serialize/ElementCollection.cs617
1 files changed, 0 insertions, 617 deletions
diff --git a/src/WixToolset.Data/Serialize/ElementCollection.cs b/src/WixToolset.Data/Serialize/ElementCollection.cs
deleted file mode 100644
index c2236627..00000000
--- a/src/WixToolset.Data/Serialize/ElementCollection.cs
+++ /dev/null
@@ -1,617 +0,0 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Data.Serialize
4{
5 using System;
6 using System.Collections;
7 using System.Globalization;
8
9 /// <summary>
10 /// Collection used in the CodeDOM for the children of a given element. Provides type-checking
11 /// on the allowed children to ensure that only allowed types are added.
12 /// </summary>
13 public class ElementCollection : ICollection, IEnumerable
14 {
15 private CollectionType collectionType;
16 private int totalContainedItems;
17 private int containersUsed;
18 private ArrayList items;
19
20 /// <summary>
21 /// Creates a new element collection.
22 /// </summary>
23 /// <param name="collectionType">Type of the collection to create.</param>
24 public ElementCollection(CollectionType collectionType)
25 {
26 this.collectionType = collectionType;
27 this.items = new ArrayList();
28 }
29
30 /// <summary>
31 /// Enum representing types of XML collections.
32 /// </summary>
33 public enum CollectionType
34 {
35 /// <summary>
36 /// A choice type, corresponding to the XSD choice element.
37 /// </summary>
38 Choice,
39
40 /// <summary>
41 /// A sequence type, corresponding to the XSD sequence element.
42 /// </summary>
43 Sequence
44 }
45
46 /// <summary>
47 /// Gets the type of collection.
48 /// </summary>
49 /// <value>The type of collection.</value>
50 public CollectionType Type
51 {
52 get { return this.collectionType; }
53 }
54
55 /// <summary>
56 /// Gets the count of child elements in this collection (counts ISchemaElements, not nested collections).
57 /// </summary>
58 /// <value>The count of child elements in this collection (counts ISchemaElements, not nested collections).</value>
59 public int Count
60 {
61 get { return this.totalContainedItems; }
62 }
63
64 /// <summary>
65 /// Gets the flag specifying whether this collection is synchronized. Always returns false.
66 /// </summary>
67 /// <value>The flag specifying whether this collection is synchronized. Always returns false.</value>
68 public bool IsSynchronized
69 {
70 get { return false; }
71 }
72
73 /// <summary>
74 /// Gets an object external callers can synchronize on.
75 /// </summary>
76 /// <value>An object external callers can synchronize on.</value>
77 public object SyncRoot
78 {
79 get { return this; }
80 }
81
82 /// <summary>
83 /// Adds a child element to this collection.
84 /// </summary>
85 /// <param name="element">The element to add.</param>
86 /// <exception cref="ArgumentException">Thrown if the child is not of an allowed type.</exception>
87 public void AddElement(ISchemaElement element)
88 {
89 foreach (object obj in this.items)
90 {
91 bool containerUsed;
92
93 CollectionItem collectionItem = obj as CollectionItem;
94 if (collectionItem != null)
95 {
96 containerUsed = collectionItem.Elements.Count != 0;
97 if (collectionItem.ElementType.IsAssignableFrom(element.GetType()))
98 {
99 collectionItem.AddElement(element);
100
101 if (!containerUsed)
102 {
103 this.containersUsed++;
104 }
105
106 this.totalContainedItems++;
107 return;
108 }
109
110 continue;
111 }
112
113 ElementCollection collection = obj as ElementCollection;
114 if (collection != null)
115 {
116 containerUsed = collection.Count != 0;
117
118 try
119 {
120 collection.AddElement(element);
121
122 if (!containerUsed)
123 {
124 this.containersUsed++;
125 }
126
127 this.totalContainedItems++;
128 return;
129 }
130 catch (ArgumentException)
131 {
132 // Eat the exception and keep looking. We'll throw our own if we can't find its home.
133 }
134
135 continue;
136 }
137 }
138
139 throw new ArgumentException(String.Format(
140 CultureInfo.InvariantCulture,
141 WixDataStrings.EXP_ElementOfTypeIsNotValidForThisCollection,
142 element.GetType().Name));
143 }
144
145 /// <summary>
146 /// Removes a child element from this collection.
147 /// </summary>
148 /// <param name="element">The element to remove.</param>
149 /// <exception cref="ArgumentException">Thrown if the element is not of an allowed type.</exception>
150 public void RemoveElement(ISchemaElement element)
151 {
152 foreach (object obj in this.items)
153 {
154 CollectionItem collectionItem = obj as CollectionItem;
155 if (collectionItem != null)
156 {
157 if (collectionItem.ElementType.IsAssignableFrom(element.GetType()))
158 {
159 if (collectionItem.Elements.Count == 0)
160 {
161 return;
162 }
163
164 collectionItem.RemoveElement(element);
165
166 if (collectionItem.Elements.Count == 0)
167 {
168 this.containersUsed--;
169 }
170
171 this.totalContainedItems--;
172 return;
173 }
174
175 continue;
176 }
177
178 ElementCollection collection = obj as ElementCollection;
179 if (collection != null)
180 {
181 if (collection.Count == 0)
182 {
183 continue;
184 }
185
186 try
187 {
188 collection.RemoveElement(element);
189
190 if (collection.Count == 0)
191 {
192 this.containersUsed--;
193 }
194
195 this.totalContainedItems--;
196 return;
197 }
198 catch (ArgumentException)
199 {
200 // Eat the exception and keep looking. We'll throw our own if we can't find its home.
201 }
202
203 continue;
204 }
205 }
206
207 throw new ArgumentException(String.Format(
208 CultureInfo.InvariantCulture,
209 WixDataStrings.EXP_ElementOfTypeIsNotValidForThisCollection,
210 element.GetType().Name));
211 }
212
213 /// <summary>
214 /// Copies this collection to an array.
215 /// </summary>
216 /// <param name="array">Array to copy to.</param>
217 /// <param name="index">Offset into the array.</param>
218 public void CopyTo(Array array, int index)
219 {
220 int item = 0;
221 foreach (ISchemaElement element in this)
222 {
223 array.SetValue(element, (long)(item + index));
224 item++;
225 }
226 }
227
228 /// <summary>
229 /// Creates an enumerator for walking the elements in this collection.
230 /// </summary>
231 /// <returns>A newly created enumerator.</returns>
232 public IEnumerator GetEnumerator()
233 {
234 return new ElementCollectionEnumerator(this);
235 }
236
237 /// <summary>
238 /// Gets an enumerable collection of children of a given type.
239 /// </summary>
240 /// <param name="childType">Type of children to get.</param>
241 /// <returns>A collection of children.</returns>
242 /// <exception cref="ArgumentException">Thrown if the type isn't a valid child type.</exception>
243 public IEnumerable Filter(Type childType)
244 {
245 foreach (object container in this.items)
246 {
247 CollectionItem collectionItem = container as CollectionItem;
248 if (collectionItem != null)
249 {
250 if (collectionItem.ElementType.IsAssignableFrom(childType))
251 {
252 return collectionItem.Elements;
253 }
254
255 continue;
256 }
257
258 ElementCollection elementCollection = container as ElementCollection;
259 if (elementCollection != null)
260 {
261 IEnumerable nestedFilter = elementCollection.Filter(childType);
262 if (nestedFilter != null)
263 {
264 return nestedFilter;
265 }
266
267 continue;
268 }
269 }
270
271 throw new ArgumentException(String.Format(
272 CultureInfo.InvariantCulture,
273 WixDataStrings.EXP_TypeIsNotValidForThisCollection,
274 childType.Name));
275 }
276
277 /// <summary>
278 /// Adds a type to this collection.
279 /// </summary>
280 /// <param name="collectionItem">CollectionItem representing the type to add.</param>
281 public void AddItem(CollectionItem collectionItem)
282 {
283 this.items.Add(collectionItem);
284 }
285
286 /// <summary>
287 /// Adds a nested collection to this collection.
288 /// </summary>
289 /// <param name="collection">ElementCollection to add.</param>
290 public void AddCollection(ElementCollection collection)
291 {
292 this.items.Add(collection);
293 }
294
295 /// <summary>
296 /// Class used to represent a given type in the child collection of an element. Abstract,
297 /// has subclasses for choice and sequence (which can do cardinality checks).
298 /// </summary>
299 public abstract class CollectionItem
300 {
301 private Type elementType;
302 private ArrayList elements;
303
304 /// <summary>
305 /// Creates a new CollectionItem for the given element type.
306 /// </summary>
307 /// <param name="elementType">Type of the element for this collection item.</param>
308 protected CollectionItem(Type elementType)
309 {
310 this.elementType = elementType;
311 this.elements = new ArrayList();
312 }
313
314 /// <summary>
315 /// Gets the type of this collection's items.
316 /// </summary>
317 /// <value>The type of this collection's items.</value>
318 public Type ElementType
319 {
320 get { return this.elementType; }
321 }
322
323 /// <summary>
324 /// Gets the elements of this collection.
325 /// </summary>
326 /// <value>The elements of this collection.</value>
327 public ArrayList Elements
328 {
329 get { return this.elements; }
330 }
331
332 /// <summary>
333 /// Adds an element to this collection. Must be of an assignable type to the collection's
334 /// type.
335 /// </summary>
336 /// <param name="element">The element to add.</param>
337 /// <exception cref="ArgumentException">Thrown if the type isn't assignable to the collection's type.</exception>
338 public void AddElement(ISchemaElement element)
339 {
340 if (!this.elementType.IsAssignableFrom(element.GetType()))
341 {
342 throw new ArgumentException(
343 String.Format(
344 CultureInfo.InvariantCulture,
345 WixDataStrings.EXP_ElementIsSubclassOfDifferentType,
346 this.elementType.Name,
347 element.GetType().Name),
348 "element");
349 }
350
351 this.elements.Add(element);
352 }
353
354 /// <summary>
355 /// Removes an element from this collection.
356 /// </summary>
357 /// <param name="element">The element to remove.</param>
358 /// <exception cref="ArgumentException">Thrown if the element's type isn't assignable to the collection's type.</exception>
359 public void RemoveElement(ISchemaElement element)
360 {
361 if (!this.elementType.IsAssignableFrom(element.GetType()))
362 {
363 throw new ArgumentException(
364 String.Format(
365 CultureInfo.InvariantCulture,
366 WixDataStrings.EXP_ElementIsSubclassOfDifferentType,
367 this.elementType.Name,
368 element.GetType().Name),
369 "element");
370 }
371
372 this.elements.Remove(element);
373 }
374 }
375
376 /// <summary>
377 /// Class representing a choice item. Doesn't do cardinality checks.
378 /// </summary>
379 public class ChoiceItem : CollectionItem
380 {
381 /// <summary>
382 /// Creates a new choice item.
383 /// </summary>
384 /// <param name="elementType">Type of the created item.</param>
385 public ChoiceItem(Type elementType)
386 : base(elementType)
387 {
388 }
389 }
390
391 /// <summary>
392 /// Class representing a sequence item. Can do cardinality checks, if required.
393 /// </summary>
394 public class SequenceItem : CollectionItem
395 {
396 /// <summary>
397 /// Creates a new sequence item.
398 /// </summary>
399 /// <param name="elementType">Type of the created item.</param>
400 public SequenceItem(Type elementType)
401 : base(elementType)
402 {
403 }
404 }
405
406 /// <summary>
407 /// Enumerator for the ElementCollection.
408 /// </summary>
409 private class ElementCollectionEnumerator : IEnumerator
410 {
411 private ElementCollection collection;
412 private Stack collectionStack;
413
414 /// <summary>
415 /// Creates a new ElementCollectionEnumerator.
416 /// </summary>
417 /// <param name="collection">The collection to create an enumerator for.</param>
418 public ElementCollectionEnumerator(ElementCollection collection)
419 {
420 this.collection = collection;
421 }
422
423 /// <summary>
424 /// Gets the current object from the enumerator.
425 /// </summary>
426 public object Current
427 {
428 get
429 {
430 if (this.collectionStack != null && this.collectionStack.Count > 0)
431 {
432 CollectionSymbol symbol = (CollectionSymbol)this.collectionStack.Peek();
433 object container = symbol.Collection.items[symbol.ContainerIndex];
434
435 CollectionItem collectionItem = container as CollectionItem;
436 if (collectionItem != null)
437 {
438 return collectionItem.Elements[symbol.ItemIndex];
439 }
440
441 throw new InvalidOperationException(String.Format(
442 CultureInfo.InvariantCulture,
443 WixDataStrings.EXP_ElementMustBeChoiceItemOrSequenceItem,
444 container.GetType().Name));
445 }
446
447 return null;
448 }
449 }
450
451 /// <summary>
452 /// Resets the enumerator to the beginning.
453 /// </summary>
454 public void Reset()
455 {
456 if (this.collectionStack != null)
457 {
458 this.collectionStack.Clear();
459 this.collectionStack = null;
460 }
461 }
462
463 /// <summary>
464 /// Moves the enumerator to the next item.
465 /// </summary>
466 /// <returns>True if there is a next item, false otherwise.</returns>
467 public bool MoveNext()
468 {
469 if (this.collectionStack == null)
470 {
471 if (this.collection.Count == 0)
472 {
473 return false;
474 }
475
476 this.collectionStack = new Stack();
477 this.collectionStack.Push(new CollectionSymbol(this.collection));
478 }
479
480 CollectionSymbol symbol = (CollectionSymbol)this.collectionStack.Peek();
481
482 if (this.FindNext(symbol))
483 {
484 return true;
485 }
486
487 this.collectionStack.Pop();
488 if (this.collectionStack.Count == 0)
489 {
490 return false;
491 }
492
493 return this.MoveNext();
494 }
495
496 /// <summary>
497 /// Pushes a collection onto the stack.
498 /// </summary>
499 /// <param name="elementCollection">The collection to push.</param>
500 private void PushCollection(ElementCollection elementCollection)
501 {
502 if (elementCollection.Count <= 0)
503 {
504 throw new ArgumentException(String.Format(
505 CultureInfo.InvariantCulture,
506 WixDataStrings.EXP_CollectionMustHaveAtLeastOneElement,
507 elementCollection.Count));
508 }
509
510 CollectionSymbol symbol = new CollectionSymbol(elementCollection);
511 this.collectionStack.Push(symbol);
512 this.FindNext(symbol);
513 }
514
515 /// <summary>
516 /// Finds the next item from a given symbol.
517 /// </summary>
518 /// <param name="symbol">The symbol to start looking from.</param>
519 /// <returns>True if a next element is found, false otherwise.</returns>
520 private bool FindNext(CollectionSymbol symbol)
521 {
522 object container = symbol.Collection.items[symbol.ContainerIndex];
523
524 CollectionItem collectionItem = container as CollectionItem;
525 if (collectionItem != null)
526 {
527 if (symbol.ItemIndex + 1 < collectionItem.Elements.Count)
528 {
529 symbol.ItemIndex++;
530 return true;
531 }
532 }
533
534 ElementCollection elementCollection = container as ElementCollection;
535 if (elementCollection != null && elementCollection.Count > 0 && symbol.ItemIndex == -1)
536 {
537 symbol.ItemIndex++;
538 this.PushCollection(elementCollection);
539 return true;
540 }
541
542 symbol.ItemIndex = 0;
543
544 for (int i = symbol.ContainerIndex + 1; i < symbol.Collection.items.Count; ++i)
545 {
546 object nestedContainer = symbol.Collection.items[i];
547
548 CollectionItem nestedCollectionItem = nestedContainer as CollectionItem;
549 if (nestedCollectionItem != null)
550 {
551 if (nestedCollectionItem.Elements.Count > 0)
552 {
553 symbol.ContainerIndex = i;
554 return true;
555 }
556 }
557
558 ElementCollection nestedElementCollection = nestedContainer as ElementCollection;
559 if (nestedElementCollection != null && nestedElementCollection.Count > 0)
560 {
561 symbol.ContainerIndex = i;
562 this.PushCollection(nestedElementCollection);
563 return true;
564 }
565 }
566
567 return false;
568 }
569
570 /// <summary>
571 /// Class representing a single point in the collection. Consists of an ElementCollection,
572 /// a container index, and an index into the container.
573 /// </summary>
574 private class CollectionSymbol
575 {
576 private ElementCollection collection;
577 private int containerIndex;
578 private int itemIndex = -1;
579
580 /// <summary>
581 /// Creates a new CollectionSymbol.
582 /// </summary>
583 /// <param name="collection">The collection for the symbol.</param>
584 public CollectionSymbol(ElementCollection collection)
585 {
586 this.collection = collection;
587 }
588
589 /// <summary>
590 /// Gets the collection for the symbol.
591 /// </summary>
592 public ElementCollection Collection
593 {
594 get { return this.collection; }
595 }
596
597 /// <summary>
598 /// Gets and sets the index of the container in the collection.
599 /// </summary>
600 public int ContainerIndex
601 {
602 get { return this.containerIndex; }
603 set { this.containerIndex = value; }
604 }
605
606 /// <summary>
607 /// Gets and sets the index of the item in the container.
608 /// </summary>
609 public int ItemIndex
610 {
611 get { return this.itemIndex; }
612 set { this.itemIndex = value; }
613 }
614 }
615 }
616 }
617}