[corlib] Avoid unnecessary ephemeron array resizes
[mono.git] / mcs / class / referencesource / System.Activities.Presentation / System.Activities.Presentation / System / Activities / Presentation / Model / ModelItemImpl.cs
1 //-----------------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //-----------------------------------------------------------------------------
4
5 namespace System.Activities.Presentation.Model
6 {
7     using System.Activities.Presentation.Services;
8     using System.Activities.Presentation.View;
9     using System.Collections.Generic;
10     using System.Collections.ObjectModel;
11     using System.ComponentModel;
12     using System.Diagnostics;
13     using System.Dynamic;
14     using System.Linq;
15     using System.Reflection;
16     using System.Runtime;
17     using System.Windows;
18     using System.Windows.Markup;
19
20
21
22     // This class provides the View facing ModelItem implementation, this works with the ModelTreeManager
23     // and keeps the xaml up to date by intercepting every change to the model properties.
24
25     class ModelItemImpl : ModelItem, IModelTreeItem, ICustomTypeDescriptor, IDynamicMetaObjectProvider
26     {
27         ModelProperty contentProperty;
28         object instance;
29         Type itemType;
30         Dictionary<string, ModelItem> modelPropertyStore;
31         ModelTreeManager modelTreeManager;
32         ModelProperty nameProperty;
33         internal ObservableCollection<ModelItem> parents;
34         ReadOnlyObservableCollection<ModelItem> internalParents;
35         ModelPropertyCollectionImpl properties;
36         ObservableCollection<ModelProperty> sources;
37         ModelTreeItemHelper helper;
38         ReadOnlyObservableCollection<ModelProperty> internalSources;
39         List<ModelItem> subTreeNodesThatNeedBackLinkPatching;
40         DependencyObject view;
41         ModelItem manuallySetParent;
42
43
44         public ModelItemImpl(ModelTreeManager modelTreeManager, Type itemType, object instance, ModelItem parent)
45         {
46             this.itemType = itemType;
47             this.instance = instance;
48             this.modelTreeManager = modelTreeManager;
49             this.parents = new ObservableCollection<ModelItem>();
50             this.internalParents = new ReadOnlyObservableCollection<ModelItem>(parents);
51             this.sources = new ObservableCollection<ModelProperty>();
52             this.helper = new ModelTreeItemHelper();
53             this.internalSources = new ReadOnlyObservableCollection<ModelProperty>(sources);
54             if (parent != null)
55             {
56                 this.manuallySetParent = parent;
57             }
58             this.modelPropertyStore = new Dictionary<string, ModelItem>();
59             this.subTreeNodesThatNeedBackLinkPatching = new List<ModelItem>();
60         }
61
62
63
64         public override event PropertyChangedEventHandler PropertyChanged;
65
66         public override global::System.ComponentModel.AttributeCollection Attributes
67         {
68             get
69             {
70                 return TypeDescriptor.GetAttributes(itemType);
71             }
72         }
73
74         public override ModelProperty Content
75         {
76             get
77             {
78
79                 if (this.contentProperty == null)
80                 {
81                     Fx.Assert(this.instance != null, "instance should not be null");
82
83                     ContentPropertyAttribute contentAttribute = TypeDescriptor.GetAttributes(this.instance)[typeof(ContentPropertyAttribute)] as ContentPropertyAttribute;
84                     if (contentAttribute != null && !String.IsNullOrEmpty(contentAttribute.Name))
85                     {
86                         this.contentProperty = this.Properties.Find(contentAttribute.Name);
87                     }
88                 }
89                 return contentProperty;
90             }
91         }
92
93         public override Type ItemType
94         {
95             get
96             {
97                 return this.itemType;
98             }
99         }
100
101         public ModelItem ModelItem
102         {
103             get
104             {
105                 return this;
106             }
107         }
108
109         public override string Name
110         {
111             get
112             {
113                 string name = null;
114                 if ((this.NameProperty != null) && (this.NameProperty.Value != null))
115                 {
116                     name = (string)this.NameProperty.Value.GetCurrentValue();
117                 }
118                 return name;
119             }
120             set
121             {
122                 if (this.NameProperty != null)
123                 {
124                     this.NameProperty.SetValue(value);
125                 }
126             }
127         }
128
129         public override ModelItem Parent
130         {
131             get
132             {
133                 return (this.Parents.Count() > 0) ? this.Parents.First() : null;
134             }
135
136         }
137
138         public override ModelPropertyCollection Properties
139         {
140             get
141             {
142                 if (this.properties == null)
143                 {
144                     properties = new ModelPropertyCollectionImpl(this);
145                 }
146                 return properties;
147             }
148         }
149
150         public override ModelItem Root
151         {
152             get
153             {
154                 return this.modelTreeManager.Root;
155             }
156         }
157
158
159         // This holds a reference to the modelproperty that is currently holding this ModelItem.
160         public override ModelProperty Source
161         {
162             get
163             {
164                 return (this.sources.Count > 0) ? this.sources.First() : null;
165             }
166         }
167
168         public override DependencyObject View
169         {
170             get
171             {
172                 return this.view;
173             }
174         }
175
176         public override IEnumerable<ModelItem> Parents
177         {
178             get
179             {
180                 if (this.manuallySetParent != null)
181                 {
182                     List<ModelItem> list = new List<ModelItem>();
183                     list.Add(this.manuallySetParent);
184                     return list.Concat(this.parents).Concat(
185                         from source in this.sources
186                         select source.Parent);
187                 }
188
189                 return this.parents.Concat(
190                     from source in this.sources
191                     select source.Parent);
192             }
193         }
194
195         internal ReadOnlyObservableCollection<ModelItem> InternalParents
196         {
197             get
198             {
199                 return internalParents;
200             }
201         }
202
203         public override IEnumerable<ModelProperty> Sources
204         {
205             get
206             {
207                 return this.sources;
208             }
209         }
210
211         internal ReadOnlyObservableCollection<ModelProperty> InternalSources
212         {
213             get
214             {
215                 return internalSources;
216             }
217         }
218
219         protected ModelProperty NameProperty
220         {
221             get
222             {
223                 if (this.nameProperty == null)
224                 {
225                     Fx.Assert(this.instance != null, "instance should not be null");
226
227                     RuntimeNamePropertyAttribute runtimeNamePropertyAttribute = TypeDescriptor.GetAttributes(this.instance)[typeof(RuntimeNamePropertyAttribute)] as RuntimeNamePropertyAttribute;
228                     if (runtimeNamePropertyAttribute != null && !String.IsNullOrEmpty(runtimeNamePropertyAttribute.Name))
229                     {
230                         this.nameProperty = this.Properties.Find(runtimeNamePropertyAttribute.Name);
231                     }
232                 }
233                 return nameProperty;
234             }
235         }
236
237
238         Dictionary<string, ModelItem> IModelTreeItem.ModelPropertyStore
239         {
240             get
241             {
242                 return modelPropertyStore;
243             }
244         }
245
246         ModelTreeManager IModelTreeItem.ModelTreeManager
247         {
248             get
249             {
250                 return modelTreeManager;
251             }
252         }
253
254         void IModelTreeItem.SetCurrentView(DependencyObject view)
255         {
256             this.view = view;
257         }
258
259         public override ModelEditingScope BeginEdit(string description, bool shouldApplyChangesImmediately)
260         {
261             return ModelItemHelper.ModelItemBeginEdit(this.modelTreeManager, description, shouldApplyChangesImmediately);
262         }
263
264         public override ModelEditingScope BeginEdit(bool shouldApplyChangesImmediately)
265         {
266             return this.BeginEdit(null, shouldApplyChangesImmediately);
267         }
268
269         public override ModelEditingScope BeginEdit(string description)
270         {
271             return this.BeginEdit(description, false);
272         }
273
274         public override ModelEditingScope BeginEdit()
275         {
276             return this.BeginEdit(null);
277         }
278
279         public override object GetCurrentValue()
280         {
281             return this.instance;
282         }
283
284         void IModelTreeItem.OnPropertyChanged(string propertyName)
285         {
286             this.OnPropertyChanged(propertyName);
287         }
288
289
290         void IModelTreeItem.SetParent(ModelItem dataModelItem)
291         {
292             if (this.manuallySetParent == dataModelItem)
293             {
294                 this.manuallySetParent = null;
295             }
296
297             if (dataModelItem != null && !this.parents.Contains(dataModelItem))
298             {
299                 this.parents.Add(dataModelItem);
300             }
301         }
302
303         IEnumerable<ModelItem> IModelTreeItem.ItemBackPointers
304         {
305             get { return this.parents; }
306         }
307
308         List<BackPointer> IModelTreeItem.ExtraPropertyBackPointers
309         {
310             get { return this.helper.ExtraPropertyBackPointers; }
311         }
312
313         void IModelTreeItem.SetSource(ModelProperty property)
314         {
315             if (!this.sources.Contains(property))
316             {
317                 // also check if the same parent.property is in the list as a different instance of oldModelProperty
318                 ModelProperty foundProperty = sources.FirstOrDefault<ModelProperty>((modelProperty) =>
319                     modelProperty.Name.Equals(property.Name) && property.Parent == modelProperty.Parent);
320                 if (foundProperty == null)
321                 {
322                     this.sources.Add(property);
323                 }
324             }
325         }
326
327         #region ICustomTypeDescriptor Members
328
329         AttributeCollection ICustomTypeDescriptor.GetAttributes()
330         {
331             return this.Attributes;
332         }
333
334         string ICustomTypeDescriptor.GetClassName()
335         {
336             return TypeDescriptor.GetClassName(this);
337         }
338
339         string ICustomTypeDescriptor.GetComponentName()
340         {
341             return TypeDescriptor.GetComponentName(this);
342         }
343
344         TypeConverter ICustomTypeDescriptor.GetConverter()
345         {
346             return ModelUtilities.GetConverter(this);
347         }
348
349         EventDescriptor ICustomTypeDescriptor.GetDefaultEvent()
350         {
351             // we dont support events;
352             return null;
353         }
354
355         PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty()
356         {
357             return ModelUtilities.GetDefaultProperty(this);
358         }
359
360         object ICustomTypeDescriptor.GetEditor(Type editorBaseType)
361         {
362             // we dont support editors
363             return null;
364         }
365
366         EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes)
367         {
368             // we dont support events;
369             return null;
370         }
371
372         EventDescriptorCollection ICustomTypeDescriptor.GetEvents()
373         {
374             // we dont support events;
375             return null;
376         }
377
378         PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes)
379         {
380             return ModelUtilities.WrapProperties(this);
381         }
382
383         PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties()
384         {
385             // get model properties
386             List<PropertyDescriptor> properties = new List<PropertyDescriptor>();
387
388
389             foreach (PropertyDescriptor modelPropertyDescriptor in ModelUtilities.WrapProperties(this))
390             {
391                 properties.Add(modelPropertyDescriptor);
392             }
393
394             // try to see if there are pseudo builtin properties for this type.
395             AttachedPropertiesService AttachedPropertiesService = this.modelTreeManager.Context.Services.GetService<AttachedPropertiesService>();
396             if (AttachedPropertiesService != null)
397             {
398                 var nonBrowsableAttachedProperties = from attachedProperty in AttachedPropertiesService.GetAttachedProperties(this.itemType)
399                                                      where (!attachedProperty.IsBrowsable && !attachedProperty.IsVisibleToModelItem)
400                                                      select attachedProperty;
401
402                 foreach (AttachedProperty AttachedProperty in nonBrowsableAttachedProperties)
403                 {
404                     properties.Add(new AttachedPropertyDescriptor(AttachedProperty, this));
405                 }
406             }
407             return new PropertyDescriptorCollection(properties.ToArray(), true);
408         }
409
410         object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd)
411         {
412             return this;
413         }
414
415         #endregion
416
417
418         void IModelTreeItem.RemoveParent(ModelItem oldParent)
419         {
420             if (this.manuallySetParent == oldParent)
421             {
422                 this.manuallySetParent = null;
423             }
424
425             if (this.parents.Contains(oldParent))
426             {
427                 this.parents.Remove(oldParent);
428             }
429         }
430
431         void IModelTreeItem.RemoveSource(ModelProperty oldModelProperty)
432         {
433             if (this.sources.Contains(oldModelProperty))
434             {
435                 this.sources.Remove(oldModelProperty);
436             }
437             else
438             {
439                 ((IModelTreeItem)this).RemoveSource(oldModelProperty.Parent, oldModelProperty.Name);
440             }
441         }
442
443         void IModelTreeItem.RemoveSource(ModelItem parent, string propertyName)
444         {
445             // also check if the same parent.property is in the list as a different instance of oldModelProperty
446             ModelProperty foundProperty = this.sources.FirstOrDefault<ModelProperty>((modelProperty) => modelProperty.Name.Equals(propertyName) && modelProperty.Parent == parent);
447             if (foundProperty != null)
448             {
449                 this.sources.Remove(foundProperty);
450             }
451             else
452             {
453                 this.helper.RemoveExtraPropertyBackPointer(parent, propertyName);
454             }
455         }
456
457         protected virtual void OnPropertyChanged(string propertyName)
458         {
459             if (PropertyChanged != null)
460             {
461                 PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
462             }
463         }
464
465         DynamicMetaObject IDynamicMetaObjectProvider.GetMetaObject(System.Linq.Expressions.Expression parameter)
466         {
467             return new ModelItemMetaObject(parameter, this);
468         }
469
470         public object SetPropertyValue(string propertyName, object val)
471         {
472             ModelProperty modelProperty = this.Properties.Find(propertyName);
473             if (modelProperty != null)
474             {
475                 modelProperty.SetValue(val);
476             }
477             else
478             {
479                 PropertyDescriptor descriptor = TypeDescriptor.GetProperties(this)[propertyName];
480                 if (descriptor != null)
481                 {
482                     descriptor.SetValue(this, val);
483                 }
484             }
485             return GetPropertyValue(propertyName);
486         }
487
488         public object GetPropertyValue(string propertyName)
489         {
490             ModelProperty modelProperty = this.Properties.Find(propertyName);
491             object value = null;
492             if (modelProperty != null)
493             {
494                 ModelItem valueModelitem = modelProperty.Value;
495                 if (valueModelitem == null)
496                 {
497                     value = null;
498                 }
499                 else
500                 {
501                     Type itemType = valueModelitem.ItemType;
502                     if (itemType.IsPrimitive || itemType.IsEnum || itemType.Equals(typeof(String)))
503                     {
504                         value = valueModelitem.GetCurrentValue();
505                     }
506                     else
507                     {
508                         value = valueModelitem;
509                     }
510                 }
511
512             }
513             else
514             {
515                 PropertyDescriptor descriptor = TypeDescriptor.GetProperties(this)[propertyName];
516                 if (descriptor != null)
517                 {
518                     value = descriptor.GetValue(this);
519                 }
520             }
521             return value;
522         }
523
524         class ModelItemMetaObject : System.Dynamic.DynamicMetaObject
525         {
526             MethodInfo getPropertyValueMethodInfo = typeof(ModelItemImpl).GetMethod("GetPropertyValue");
527             MethodInfo setPropertyValueMethodInfo = typeof(ModelItemImpl).GetMethod("SetPropertyValue");
528
529             public ModelItemMetaObject(System.Linq.Expressions.Expression parameter, ModelItemImpl target)
530                 : base(parameter, BindingRestrictions.Empty, target)
531             {
532             }
533
534             public override DynamicMetaObject BindGetMember(GetMemberBinder binder)
535             {
536                 System.Linq.Expressions.Expression s = System.Linq.Expressions.Expression.Convert(this.Expression, typeof(ModelItemImpl));
537                 System.Linq.Expressions.Expression value = System.Linq.Expressions.Expression.Call(s, getPropertyValueMethodInfo, System.Linq.Expressions.Expression.Constant(binder.Name));
538                 return new DynamicMetaObject(value, BindingRestrictions.GetTypeRestriction(this.Expression, this.LimitType));
539             }
540
541             public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value)
542             {
543                 System.Linq.Expressions.Expression s = System.Linq.Expressions.Expression.Convert(this.Expression, typeof(ModelItemImpl));
544                 System.Linq.Expressions.Expression objectValue = System.Linq.Expressions.Expression.Convert(value.Expression, typeof(object));
545                 System.Linq.Expressions.Expression valueExp = System.Linq.Expressions.Expression.Call(s, setPropertyValueMethodInfo, System.Linq.Expressions.Expression.Constant(binder.Name), objectValue);
546                 return new DynamicMetaObject(valueExp, BindingRestrictions.GetTypeRestriction(this.Expression, this.LimitType));
547             }
548         }
549
550
551
552     }
553
554
555
556
557
558
559
560 }