Code:
/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / cdf / src / NetFx40 / Tools / System.Activities.Presentation / System / Activities / Presentation / Model / ModelTreeManager.cs / 1305376 / ModelTreeManager.cs
//------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
namespace System.Activities.Presentation.Model
{
using System.Activities.Presentation;
using System.Activities.Presentation.Hosting;
using System.Activities.Presentation.Services;
using System.Activities.Presentation.View;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.ComponentModel;
using System.Runtime;
using System.Diagnostics.CodeAnalysis;
using System.Activities.Presentation.Internal.PropertyEditing;
// This class manages the model tree, provides the root model item and the modelservice
// This also provides syncing the model tree with the xaml text
// The model service is publishes on the editing context passed to the constructor.
[Fx.Tag.XamlVisible(false)]
public class ModelTreeManager
{
internal ModelServiceImpl modelService;
EditingContext context;
// The value of this dictionary is a WeakReference to ModelItem.
// This need to be a WeakReference because if the ModelItem has a strong reference, it
// will have a strong reference to the underlying object instance as well.
WeakKeyDictionary objectMap;
ModelItem rootItem;
Stack editingScopes;
bool trackChanges = true;
FeatureManager featureManager;
public ModelTreeManager(EditingContext context)
{
if (context == null)
{
throw FxTrace.Exception.AsError( new ArgumentNullException("context"));
}
this.context = context;
objectMap = new WeakKeyDictionary(new ObjectEqualityComparer());
editingScopes = new Stack();
}
public event EventHandler EditingScopeCompleted;
public ModelItem Root
{
get
{
return this.rootItem;
}
}
internal EditingContext Context
{
get
{
return this.context;
}
}
FeatureManager FeatureManager
{
get
{
if (this.featureManager == null)
{
this.featureManager = this.context.Services.GetService();
}
return this.featureManager;
}
}
internal bool RedoUndoInProgress
{
get
{
return !this.trackChanges;
}
}
internal void StartTracking()
{
trackChanges = true;
}
internal void StopTracking()
{
trackChanges = false;
}
public ModelItem CreateModelItem(ModelItem parent, object instance)
{
if (instance == null)
{
throw FxTrace.Exception.AsError( new ArgumentNullException("instance"));
}
ModelItem retval;
Type instanceType = instance.GetType();
object[] result = new object[2] { false, false };
Type[] interfaces = instanceType.FindInterfaces(ModelTreeManager.CheckInterface, result);
bool isList = (bool)result[0];
bool isDictionary = (bool)result[1];
if (isDictionary)
{
foreach (Type type in interfaces)
{
if (type.GetGenericTypeDefinition() == typeof(IDictionary<,>))
{
// To expose one more property, a collection of MutableKeyValuePairs, to the model tree.
TypeDescriptor.AddProvider(new DictionaryTypeDescriptionProvider(instanceType), instance);
break;
}
}
ModelItemDictionary modelItem = new ModelItemDictionaryImpl(this, instance.GetType(), instance, parent);
retval = modelItem;
}
else if (isList)
{
ModelItemCollectionImpl modelItem = new ModelItemCollectionImpl(this, instance.GetType(), instance, parent);
retval = modelItem;
}
else
{
retval = new ModelItemImpl(this, instance.GetType(), instance, parent);
}
if (!((instance is ValueType) || (instance is string)))
{
//
// ValueType do not have a concept of shared reference, they are always copied.
// strings are immutatable, therefore the risk of making all shared string references to different
// string ModelItems is low.
//
// To special case string is because underlying OM are sharing string objects for DisplayName across
// Different activity object instances. These shared references is causing memory leak because of bugs.
//
// We will need to fix these issues in Beta2.
//
objectMap[instance] = new WeakReference(retval);
}
if (this.FeatureManager != null)
{
this.FeatureManager.InitializeFeature(instance.GetType());
}
return retval;
}
static bool CheckInterface(Type type, object result)
{
object[] values = (object[])result;
if (typeof(IList).IsAssignableFrom(type))
{
values[0] = true;
return true;
}
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IList < > ))
{
values[0] = true;
return true;
}
if (typeof(IDictionary).IsAssignableFrom(type))
{
values[1] = true;
return true;
}
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IDictionary <, > ))
{
values[1] = true;
return true;
}
return false;
}
public void Load(object rootInstance)
{
if (rootInstance == null)
{
throw FxTrace.Exception.AsError( new ArgumentNullException("rootInstance"));
}
objectMap.Clear();
this.rootItem = WrapAsModelItem(null, rootInstance);
if (this.modelService == null)
{
this.modelService = new ModelServiceImpl(this);
this.context.Services.Publish(modelService);
}
}
// This methods clears the value of a property , if the property is
// a reference type then its set to null, if its a value type the
// property value is reset to the default. this also clears the sub modelitem corresponding
// to the old value from the parent modelitem's modelPropertyStore.
internal void ClearValue(ModelPropertyImpl modelProperty)
{
Fx.Assert(modelProperty != null, "modelProperty should not be null");
Fx.Assert(modelProperty.Parent is IModelTreeItem, "modelProperty.Parent should be an IModelTreeItem");
ModelItem newValueModelItem = null;
newValueModelItem = WrapAsModelItem(null, modelProperty.DefaultValue);
PropertyChange propertyChange = new PropertyChange()
{
Owner = modelProperty.Parent,
PropertyName = modelProperty.Name,
OldValue = modelProperty.Value,
NewValue = newValueModelItem,
ModelTreeManager = this
};
AddToCurrentEditingScope(propertyChange);
}
internal void CollectionAdd(ModelItemCollectionImpl dataModelItemCollection, ModelItem item)
{
CollectionInsert(dataModelItemCollection, -1, item);
}
internal void CollectionInsert(ModelItemCollectionImpl dataModelItemCollection, int index, ModelItem item)
{
Fx.Assert(dataModelItemCollection != null,"collection should not be null");
CollectionChange change = new CollectionChange()
{
Collection = dataModelItemCollection,
Item = item,
Index = index,
ModelTreeManager = this,
Operation = CollectionChange.OperationType.Insert
};
AddToCurrentEditingScope(change);
}
internal void CollectionClear(ModelItemCollectionImpl modelItemCollectionImpl)
{
Fx.Assert(modelItemCollectionImpl != null,"collection should not be null");
Fx.Assert(this.modelService != null, "modelService should not be null");
List removedItems = new List();
removedItems.AddRange(modelItemCollectionImpl);
using (ModelEditingScope editingScope = CreateEditingScope(SR.CollectionClearEditingScopeDescription))
{
foreach (ModelItem modelItem in removedItems)
{
this.CollectionRemove(modelItemCollectionImpl, modelItem);
}
editingScope.Complete();
}
this.modelService.OnModelItemsRemoved(removedItems);
}
internal void NotifyCollectionInsert(ModelItem item)
{
this.modelService.OnModelItemAdded(item);
}
internal void CollectionRemove(ModelItemCollectionImpl dataModelItemCollection, ModelItem item)
{
CollectionRemove(dataModelItemCollection, item, -1);
}
internal void CollectionRemoveAt(ModelItemCollectionImpl dataModelItemCollection, int index)
{
ModelItem item = dataModelItemCollection[index];
CollectionRemove(dataModelItemCollection, item, index);
}
void CollectionRemove(ModelItemCollectionImpl dataModelItemCollection, ModelItem item, int index)
{
Fx.Assert(dataModelItemCollection != null,"collection should not be null");
CollectionChange change = new CollectionChange()
{
Collection = dataModelItemCollection,
Item = item,
Index = index,
ModelTreeManager = this,
Operation = CollectionChange.OperationType.Delete
};
AddToCurrentEditingScope(change);
}
internal void NotifyCollectionRemove(ModelItem item)
{
this.modelService.OnModelItemRemoved(item);
}
internal void DictionaryClear(ModelItemDictionaryImpl modelDictionary)
{
Fx.Assert(modelDictionary != null,"dictionary should not be null");
Fx.Assert(this.modelService != null, "modelService should not be null");
ModelItem[] keys = modelDictionary.Keys.ToArray();
using (ModelEditingScope editingScope = CreateEditingScope(SR.DictionaryClearEditingScopeDescription))
{
foreach (ModelItem key in keys)
{
this.DictionaryRemove(modelDictionary, key);
}
editingScope.Complete();
}
}
internal void DictionaryEdit(ModelItemDictionaryImpl dataModelItemDictionary, ModelItem key, ModelItem newValue, ModelItem oldValue)
{
Fx.Assert(dataModelItemDictionary != null,"dictionary should not be null");
Fx.Assert(this.modelService != null, "modelService should not be null");
DictionaryEditChange change = new DictionaryEditChange()
{
Dictionary = dataModelItemDictionary,
Key = key,
NewValue = newValue,
OldValue = oldValue,
ModelTreeManager = this
};
AddToCurrentEditingScope(change);
}
internal void DictionaryAdd(ModelItemDictionaryImpl dataModelItemDictionary, ModelItem key, ModelItem value)
{
Fx.Assert(dataModelItemDictionary != null, "dictionary should not be null");
Fx.Assert(this.modelService != null, "modelService should not be null");
DictionaryChange change = new DictionaryChange()
{
Dictionary = dataModelItemDictionary,
Key = key,
Value = value,
Operation = DictionaryChange.OperationType.Insert,
ModelTreeManager = this
};
AddToCurrentEditingScope(change);
}
internal void DictionaryRemove(ModelItemDictionaryImpl dataModelItemDictionary, ModelItem key)
{
Fx.Assert(dataModelItemDictionary != null, "dictionary should not be null");
Fx.Assert(this.modelService != null, "modelService should not be null");
ModelItem value = dataModelItemDictionary[key];
DictionaryChange change = new DictionaryChange()
{
Dictionary = dataModelItemDictionary,
Key = key,
Value = value,
Operation = DictionaryChange.OperationType.Delete,
ModelTreeManager = this
};
AddToCurrentEditingScope(change);
}
internal IEnumerable Find(ModelItem startingItem, Predicate matcher, bool skipCollapsedAndUnrootable)
{
Fx.Assert(startingItem != null, "starting item should not be null");
Fx.Assert(matcher != null, "matching predicate should not be null");
WorkflowViewService viewService = this.Context.Services.GetService() as WorkflowViewService;
if (skipCollapsedAndUnrootable)
{
Fx.Assert(viewService != null, "ViewService must be available in order to skip exploring ModelItems whose views are collapsed.");
}
List foundItems = new List();
Queue modelItems = new Queue();
modelItems.Enqueue(startingItem);
HashSet alreadyVisited = new HashSet();
while (modelItems.Count > 0)
{
ModelItem currentModelItem = modelItems.Dequeue();
if (currentModelItem == null)
{
continue;
}
if (matcher(currentModelItem.ItemType))
{
foundItems.Add(currentModelItem);
}
ModelItemCollection collection = currentModelItem as ModelItemCollection;
if (collection != null)
{
foreach (ModelItem modelItem in collection)
{
if (modelItem != null && !alreadyVisited.Contains(modelItem))
{
alreadyVisited.Add(modelItem);
modelItems.Enqueue(modelItem);
}
}
}
else
{
ModelItemDictionary dictionary = currentModelItem as ModelItemDictionary;
if (dictionary != null)
{
foreach (KeyValuePair kvp in dictionary)
{
ModelItem miKey = kvp.Key;
if (miKey != null && !alreadyVisited.Contains(miKey))
{
alreadyVisited.Add(miKey);
modelItems.Enqueue(miKey);
}
ModelItem miValue = kvp.Value;
if (miValue != null && !alreadyVisited.Contains(miValue))
{
alreadyVisited.Add(miValue);
modelItems.Enqueue(miValue);
}
}
}
}
if (!skipCollapsedAndUnrootable || !typeof(WorkflowViewElement).IsAssignableFrom(viewService.GetDesignerType(currentModelItem.ItemType)) ||
ViewUtilities.IsViewExpanded(currentModelItem, this.Context) && viewService.ShouldAppearOnBreadCrumb(currentModelItem, true))
{
ModelPropertyCollection modelProperties = currentModelItem.Properties;
foreach (ModelProperty property in modelProperties)
{
// we dont want to even try to get the value for a value type property
// because that will create a new modelitem everytime.
// System.Type has properties that throw when we try to get value
// we dont want to expand system.type further during a search.
if (property.PropertyType.IsAssignableFrom(typeof(Type)) || property.PropertyType.IsValueType)
{
continue;
}
else
{
if (property.Value != null && !alreadyVisited.Contains(property.Value))
{
alreadyVisited.Add(property.Value);
modelItems.Enqueue(property.Value);
}
}
}
}
}
return foundItems;
}
internal ModelItem FindFirst(ModelItem startingItem, Predicate matcher)
{
Fx.Assert(startingItem != null, "starting item should not be null");
Fx.Assert(matcher != null, "matching predicate should not be null");
ModelItem foundItem = null;
Queue modelItems = new Queue();
modelItems.Enqueue(startingItem);
HashSet alreadyVisited = new HashSet();
while (modelItems.Count > 0)
{
ModelItem currentModelItem = modelItems.Dequeue();
if (currentModelItem == null)
{
continue;
}
if (matcher(currentModelItem))
{
foundItem = currentModelItem;
break;
}
ModelItemCollection collection = currentModelItem as ModelItemCollection;
if (collection != null)
{
foreach (ModelItem modelItem in collection)
{
if (modelItem != null && !alreadyVisited.Contains(modelItem))
{
alreadyVisited.Add(modelItem);
modelItems.Enqueue(modelItem);
}
}
}
else
{
ModelItemDictionary dictionary = currentModelItem as ModelItemDictionary;
if (dictionary != null)
{
foreach (KeyValuePair kvp in dictionary)
{
ModelItem miKey = kvp.Key;
if (miKey != null && !alreadyVisited.Contains(miKey))
{
alreadyVisited.Add(miKey);
modelItems.Enqueue(miKey);
}
ModelItem miValue = kvp.Value;
if (miValue != null && !alreadyVisited.Contains(miValue))
{
alreadyVisited.Add(miValue);
modelItems.Enqueue(miValue);
}
}
}
}
ModelPropertyCollection modelProperties = currentModelItem.Properties;
foreach (ModelProperty property in modelProperties)
{
// we dont want to even try to get the value for a value type property
// because that will create a new modelitem everytime.
// System.Type has properties that throw when we try to get value
// we dont want to expand system.type further during a search.
if (property.PropertyType.IsAssignableFrom(typeof(Type)) || property.PropertyType.IsValueType)
{
continue;
}
else
{
if (property.Value != null && !alreadyVisited.Contains(property.Value))
{
alreadyVisited.Add(property.Value);
modelItems.Enqueue(property.Value);
}
}
}
}
return foundItem;
}
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
Justification = "If the property getter threw here we dont want to crash, we just dont want to wrap that property value")]
[SuppressMessage("Reliability", "Reliability108:IsFatalRule",
Justification = "If the property getter threw here we dont want to crash, we just dont want to wrap that property value")]
internal ModelItem GetValue(ModelPropertyImpl dataModelProperty)
{
Fx.Assert(dataModelProperty != null,"modelproperty should not be null");
Fx.Assert(dataModelProperty.Parent is IModelTreeItem, "modelproperty.Parent should be an IModelTreeItem");
IModelTreeItem parent = (IModelTreeItem)dataModelProperty.Parent;
ModelItem value;
// always reevaluate attached properties. the cache in attached properties case is only to remember the old value.
if (!dataModelProperty.IsAttached && parent.ModelPropertyStore.ContainsKey(dataModelProperty.Name))
{
value = parent.ModelPropertyStore[dataModelProperty.Name];
}
// Create a ModelItem on demand for the value of the property.
else
{
try
{
value = WrapAsModelItem(null, dataModelProperty.PropertyDescriptor.GetValue(parent.ModelItem.GetCurrentValue()));
}
catch (System.Exception)
{
// GetValue throws an exception if Value is not available
value = null;
}
if (value != null)
{
if (!dataModelProperty.IsAttached)
{
parent.ModelPropertyStore.Add(dataModelProperty.Name, value);
}
((IModelTreeItem)value).SetSource(dataModelProperty);
}
}
return value;
}
internal ModelItem SetValue(ModelPropertyImpl modelProperty, object value)
{
Fx.Assert(modelProperty != null, "modelProperty should not be null");
ModelItem newValueModelItem = null;
RefreshPropertiesAttribute refreshPropertiesAttribute = ExtensibilityAccessor.GetAttribute(modelProperty.Attributes);
if (refreshPropertiesAttribute != null && refreshPropertiesAttribute.RefreshProperties == RefreshProperties.All)
{
((IModelTreeItem)modelProperty.Parent).ModelPropertyStore.Clear();
}
if (value is ModelItem)
{
newValueModelItem = (ModelItem)value;
}
else
{
newValueModelItem = WrapAsModelItem(null, value);
}
// dont do deferred updates for attached properties
if (modelProperty.IsAttached)
{
modelProperty.SetValueCore(newValueModelItem);
}
else
{
PropertyChange propertyChange = new PropertyChange()
{
Owner = modelProperty.Parent,
PropertyName = modelProperty.Name,
OldValue = modelProperty.Value,
NewValue = newValueModelItem,
ModelTreeManager = this
};
AddToCurrentEditingScope(propertyChange);
}
return newValueModelItem;
}
internal void AddToCurrentEditingScope(Change change)
{
EditingScope editingScope;
if (editingScopes.Count > 0)
{
editingScope = (EditingScope)editingScopes.Peek();
// Automatic generated change during apply changes of Redo/Undo should be ignored.
if (!RedoUndoInProgress)
{
editingScope.Changes.Add(change);
}
}
else
{
//edit operation without editingscope create an editing scope and complete it immediately.
editingScope = CreateEditingScope(change.Description);
editingScope.Changes.Add(change);
try
{
editingScope.Complete();
}
catch
{
editingScope.Revert();
throw;
}
}
}
internal EditingScope CreateEditingScope(string description)
{
EditingScope editingScope;
EditingScope outerScope = editingScopes.Count > 0 ? (EditingScope)editingScopes.Peek() : null;
editingScope = new EditingScope(this, outerScope);
editingScope.Description = description;
editingScopes.Push(editingScope);
return editingScope;
}
internal void NotifyPropertyChange(ModelPropertyImpl dataModelProperty)
{
modelService.OnModelPropertyChanged(dataModelProperty);
}
internal void SyncModelAndText()
{
// Place holder for xaml generation ModelTreeManager now is instance only.
}
internal ModelItem WrapAsModelItem(ModelItem parent, object instance)
{
ModelItem modelItem = GetModelItem(instance);
//first, check if model item exists - if it does, check if its parent needs to be set properly.
if (instance != null && modelItem !=null)
{
if (null != parent)
{
((IModelTreeItem)modelItem).SetParent(parent);
}
}
//if one doesn't exists - create new one
else if (null != instance && null == modelItem)
{
modelItem = CreateModelItem(parent, instance);
}
return modelItem;
}
internal ModelItem GetModelItem(object instance)
{
if (instance == null)
{
return null;
}
ModelItem modelItem = null;
WeakReference mappedModelItem = null;
objectMap.TryGetValue(instance, out mappedModelItem);
if (mappedModelItem != null)
{
modelItem = (ModelItem)mappedModelItem.Target;
}
return modelItem;
}
internal void ReAddModelItemToModelTree(ModelItem modelItem)
{
if (modelItem != null)
{
IList modelItemsThatNeedBackLinkUpdate = ((IModelTreeItem)modelItem).SubNodesThatNeedBackLinkUpdate;
if (modelItemsThatNeedBackLinkUpdate.Count > 0)
{
foreach (ModelItem modelItemThatNeedsBackLinkUpdate in modelItemsThatNeedBackLinkUpdate)
{
UpdateBackLinksForDependentsOf(modelItemThatNeedsBackLinkUpdate);
}
modelItemsThatNeedBackLinkUpdate.Clear();
}
}
}
private void UpdateBackLinksForDependentsOf(ModelItem modelItem)
{
//restore property backlinks
foreach (string propertyName in ((IModelTreeItem)modelItem).ModelPropertyStore.Keys)
{
ModelItem propertyValueModelItem = ((IModelTreeItem)modelItem).ModelPropertyStore[propertyName];
if(propertyValueModelItem != null)
{
((IModelTreeItem)propertyValueModelItem).SetSource(modelItem.Properties[propertyName]);
}
}
// restore collection backlinks
ModelItemCollection modelItemCollection = modelItem as ModelItemCollection;
if (modelItemCollection != null)
{
foreach(ModelItem collectionChild in modelItemCollection)
{
if(collectionChild != null)
{
((IModelTreeItem)collectionChild).SetParent(modelItemCollection);
}
}
}
// restore dictionary backlinks
ModelItemDictionary modelItemDictionary = modelItem as ModelItemDictionary;
if (modelItemDictionary != null)
{
foreach (ModelItem keyModelItem in modelItemDictionary.Keys)
{
if (keyModelItem != null)
{
((IModelTreeItem)keyModelItem).SetParent(modelItemDictionary);
}
}
foreach (ModelItem valueModelItem in modelItemDictionary.Values)
{
if (valueModelItem != null)
{
((IModelTreeItem)valueModelItem).SetParent(modelItemDictionary);
}
}
}
}
internal void ReleaseModelItem(ModelItem oldValueModelItem, ModelItem parent)
{
Fx.Assert(oldValueModelItem != null, "old value modelItem should not be null");
HashSet visited = new HashSet();
Queue nodes = new Queue();
nodes.Enqueue(new Dependency() { Parent = parent, Dependant = oldValueModelItem });
while (nodes.Count > 0)
{
Dependency dependency = nodes.Dequeue();
if (HasNoOtherParents(dependency))
{
// this node depends only on this parent, visit this.
ModelItem currentModelItem = dependency.Dependant;
if (!visited.Contains(currentModelItem))
{
visited.Add(currentModelItem);
foreach (ModelItem child in GetDependants(currentModelItem))
{
nodes.Enqueue(new Dependency() { Dependant = child, Parent = currentModelItem });
}
}
}
else // This node has other references, dont dig any deeper, but remove its immediate backpointer to this parent.
{
RemoveBackLink(dependency);
if (dependency.Dependant != oldValueModelItem)
{
((IModelTreeItem)oldValueModelItem).SubNodesThatNeedBackLinkUpdate.Add(dependency.Parent);
}
}
}
}
private void RemoveBackLink(Dependency dependency)
{
// remove sources that have source.Parent == dependency.Parent
var sourceBackLinks = from source in dependency.Dependant.Sources
where source.Parent == dependency.Parent
select source;
foreach (ModelProperty sourceBackLink in sourceBackLinks.ToList())
{
((IModelTreeItem)dependency.Dependant).RemoveSource(sourceBackLink);
}
// remove collectionParents that equal dependency.Parent.
((IModelTreeItem)dependency.Dependant).RemoveParent(dependency.Parent);
}
private IEnumerable GetDependants(ModelItem modelItem)
{
List dependants = new List();
// modelimpl- propertystore
dependants.AddRange(((IModelTreeItem)modelItem).ModelPropertyStore.Values);
// modelcolleciton - propertystore + items
if (modelItem is ModelItemCollection)
{
dependants.AddRange((ModelItemCollection)modelItem);
}
// modeldictionary - propertystore + keys + values;
else if (modelItem is ModelItemDictionary)
{
dependants.AddRange(((ModelItemDictionary)modelItem).Keys);
dependants.AddRange(((ModelItemDictionary)modelItem).Values);
}
return dependants.Where(e => e != null);
}
private bool HasNoOtherParents(Dependency dependency)
{
var otherParents = from parent in dependency.Dependant.Parents
where parent != dependency.Parent
select parent;
return otherParents.Count() == 0;
}
internal void OnEditingScopeCompleted(EditingScope modelEditingScopeImpl)
{
if (editingScopes.Contains(modelEditingScopeImpl))
{
editingScopes.Pop();
}
// if the outer most scope completed notify listeners
if (this.EditingScopeCompleted != null && editingScopes.Count == 0)
{
this.EditingScopeCompleted(this, new EditingScopeEventArgs() { EditingScope = modelEditingScopeImpl });
}
}
internal bool CanEditingScopeComplete(EditingScope modelEditingScopeImpl)
{
ReadOnlyState readOnlyState = this.Context.Items.GetValue();
return (modelEditingScopeImpl == editingScopes.Peek()) && (readOnlyState == null || !readOnlyState.IsReadOnly);
}
internal void OnEditingScopeReverted(EditingScope modelEditingScopeImpl)
{
if (editingScopes.Contains(modelEditingScopeImpl))
{
editingScopes.Pop();
}
}
class ObjectEqualityComparer : IEqualityComparer
{
public int GetHashCode(object obj)
{
//If two reference objects are the same we return equal.
//Object.GetHashCode() gurantees we return same hash code for same objects(equal references).
return obj.GetHashCode();
}
//We want to check reference equality for keys in ObjectMap.
//If the user overrides Equals method for their class we still want to use referential equality.
public new bool Equals(object x, object y)
{
return object.ReferenceEquals(x, y);
}
}
class Dependency
{
public ModelItem Parent { get; set; }
public ModelItem Dependant { get; set; }
}
class DictionaryTypeDescriptionProvider : TypeDescriptionProvider
{
Type type;
public DictionaryTypeDescriptionProvider(Type type)
: base(TypeDescriptor.GetProvider(type))
{
this.type = type;
}
public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)
{
ICustomTypeDescriptor defaultDescriptor = base.GetTypeDescriptor(objectType, instance);
return new DictionaryTypeDescriptor(defaultDescriptor, this.type);
}
}
class DictionaryTypeDescriptor : CustomTypeDescriptor
{
Type type;
public DictionaryTypeDescriptor(ICustomTypeDescriptor parent, Type type)
: base(parent)
{
this.type = type;
}
// Expose one more property, a collection of MutableKeyValuePairs, described by ItemsCollectionPropertyDescriptor
public override PropertyDescriptorCollection GetProperties()
{
return new PropertyDescriptorCollection(base.GetProperties().Cast()
.Union(new PropertyDescriptor[] {new ItemsCollectionPropertyDescriptor(type) }).ToArray());
}
// Expose one more property, a collection of MutableKeyValuePairs, described by ItemsCollectionPropertyDescriptor
public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
return new PropertyDescriptorCollection(base.GetProperties(attributes).Cast()
.Union(new PropertyDescriptor[] { new ItemsCollectionPropertyDescriptor(type) }).ToArray());
}
}
class ItemsCollectionPropertyDescriptor : PropertyDescriptor
{
Type dictionaryType;
Type[] genericArguments;
Type kvpairType;
Type itemType;
Type propertyType;
internal ItemsCollectionPropertyDescriptor(Type type)
: base("ItemsCollection", null)
{
this.dictionaryType = type;
}
Type[] GenericArguments
{
get
{
if (this.genericArguments == null)
{
object[] result = new object[2] { false, false };
Type[] interfaces = this.ComponentType.FindInterfaces(ModelTreeManager.CheckInterface, result);
foreach (Type type in interfaces)
{
if (type.GetGenericTypeDefinition() == typeof(IDictionary<,>))
{
this.genericArguments = type.GetGenericArguments();
Fx.Assert(this.genericArguments.Length == 2, "this.genericArguments.Length should be = 2");
return this.genericArguments;
}
}
Debug.Fail("Cannot find generic arguments for IDictionary<,>.");
}
return this.genericArguments;
}
}
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "This is intended for use through reflection")]
Type KVPairType
{
get
{
if (this.kvpairType == null)
{
this.kvpairType = typeof(KeyValuePair<,>).MakeGenericType(this.GenericArguments);
}
return this.kvpairType;
}
}
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "This is intended for use through reflection")]
Type ItemType
{
get
{
if (this.itemType == null)
{
this.itemType = typeof(ModelItemKeyValuePair<,>).MakeGenericType(this.GenericArguments);
}
return this.itemType;
}
}
public override Type ComponentType
{
get { return this.dictionaryType; }
}
public override bool IsReadOnly
{
get
{
return true;
}
}
public override Type PropertyType
{
get
{
if (this.propertyType == null)
{
this.propertyType = typeof(DictionaryItemsCollection<,>).MakeGenericType(this.GenericArguments);
}
return this.propertyType;
}
}
public override bool IsBrowsable
{
get
{
return false;
}
}
public override bool CanResetValue(object component)
{
return false;
}
public override object GetValue(object component)
{
return Activator.CreateInstance(this.PropertyType, new object[] { component });
}
public override void ResetValue(object component)
{
Debug.Fail("ResetValue is not implemented.");
throw FxTrace.Exception.AsError( new NotImplementedException());
}
public override void SetValue(object component, object value)
{
Debug.Fail("SetValue is not implemented.");
throw FxTrace.Exception.AsError( new NotImplementedException());
}
public override bool ShouldSerializeValue(object component)
{
return false;
}
}
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.