Example #1
0
        public bool IsInCircularInheritance()
        {
            List <ModelClass> classes    = new List <ModelClass>();
            ModelClass        modelClass = Subclass;

            while (modelClass != null)
            {
                if (classes.Contains(modelClass))
                {
                    return(true);
                }

                classes.Add(modelClass);
                modelClass = modelClass.Superclass;
            }

            return(false);
        }
        private static bool CanAcceptModelClassAsSource(ModelClass candidate)
        {
            // valid unidirectional associations:
            // EF6 - entity to entity, entity to dependent
            // EFCore - entity to entity, entity to dependent
            // EFCore5Plus - entity to entity, entity to dependent, dependent to dependent, keyless to entity

            ModelRoot modelRoot = candidate.ModelRoot;
            EFVersion entityFrameworkVersion = modelRoot.EntityFrameworkVersion;

            if (entityFrameworkVersion == EFVersion.EF6)
            {
                if (candidate.IsEntity())
                {
                    return(true);
                }
            }
            else if (entityFrameworkVersion == EFVersion.EFCore && !modelRoot.IsEFCore5Plus)
            {
                if (candidate.IsEntity())
                {
                    return(true);
                }
            }
            else if (entityFrameworkVersion == EFVersion.EFCore && modelRoot.IsEFCore5Plus)
            {
                if (candidate.IsEntity())
                {
                    return(true);
                }

                if (candidate.IsDependent())
                {
                    return(true);
                }

                if (candidate.IsKeyless())
                {
                    return(true);
                }
            }

            return(false);
        }
Example #3
0
        private void DerivedClassesShouldNotHaveIdentity(ValidationContext context)
        {
            if (Attributes.Any(x => x.IsIdentity))
            {
                ModelClass modelClass = Superclass;
                while (modelClass != null)
                {
                    if (modelClass.Attributes.Any(x => x.IsIdentity))
                    {
                        context.LogWarning($"{modelClass.Name}: Identity attribute in derived class {Name} becomes a composite key", "MCWDerivedIdentity", this);
                        hasWarning = true;
                        RedrawItem();
                        return;
                    }

                    modelClass = modelClass.Superclass;
                }
            }
        }
        public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
        {
            Store store = GetStore(context.Instance);

            ClassShape classShape      = context.Instance as ClassShape;
            ModelClass modelClass      = classShape?.Subject as ModelClass;
            string     targetClassName = modelClass?.Name;

            List <string> validNames = store.ElementDirectory
                                       .FindElements <ModelClass>()
                                       .Where(e => e.Name != targetClassName)
                                       .OrderBy(c => c.Name)
                                       .Select(c => c.Name)
                                       .ToList();

            validNames.Insert(0, null);

            return(new StandardValuesCollection(validNames));
        }
Example #5
0
      private void ProcessProperties(ModelClass modelClass, List<ModelProperty> properties)
      {
         foreach (ModelProperty data in properties)
         {
            ModelAttribute element = modelClass.Attributes.FirstOrDefault(x => x.Name == data.Name);

            if (element == null)
            {
               // we've never seen this one before. Add it.
               element = new ModelAttribute(Store,
                                            new PropertyAssignment(ModelAttribute.TypeDomainPropertyId, data.TypeName),
                                            new PropertyAssignment(ModelAttribute.NameDomainPropertyId, data.Name),
                                            new PropertyAssignment(ModelAttribute.CustomAttributesDomainPropertyId, data.CustomAttributes),
                                            new PropertyAssignment(ModelAttribute.IndexedDomainPropertyId, data.Indexed),
                                            new PropertyAssignment(ModelAttribute.RequiredDomainPropertyId, data.Required),
                                            new PropertyAssignment(ModelAttribute.MaxLengthDomainPropertyId, data.MaxStringLength),
                                            new PropertyAssignment(ModelAttribute.MinLengthDomainPropertyId, data.MinStringLength),
                                            new PropertyAssignment(ModelAttribute.IsIdentityDomainPropertyId, data.IsIdentity),
                                            new PropertyAssignment(ModelAttribute.IdentityTypeDomainPropertyId, data.IsIdentity 
                                                                                                                   ? data.IsIdentityGenerated ? IdentityType.AutoGenerated : IdentityType.Manual 
                                                                                                                   : IdentityType.None));
               modelClass.Attributes.Add(element);
            }
            else
            {
               // somehow, we have seen this before. Update it.
               element.Type = data.TypeName;
               element.Name = data.Name;
               element.CustomAttributes = data.CustomAttributes;
               element.Indexed = data.Indexed;
               element.Required = data.Required;
               element.MaxLength = data.MaxStringLength;
               element.MinLength = data.MinStringLength;
               element.IsIdentity = data.IsIdentity;

               element.IdentityType = data.IsIdentity
                                         ? data.IsIdentityGenerated ? IdentityType.AutoGenerated : IdentityType.Manual
                                         : IdentityType.None;
            }
         }
      }
Example #6
0
        //private static void SetConnectorWidth(AssociationConnector connector)
        //{
        //   if (!(connector?.ModelElement is Association element))
        //      return;

        //   BidirectionalAssociation bidirectionalElement = connector.ModelElement as BidirectionalAssociation;
        //   PenSettings settings = connector.StyleSet.GetOverriddenPenSettings(DiagramPens.ConnectionLine) ?? new PenSettings();

        //   bool hasAutoInclude = element.TargetAutoInclude || (bidirectionalElement?.SourceAutoInclude == true);
        //   settings.Width = hasAutoInclude ? 0.04f : 0.01f;

        //   connector.StyleSet.OverridePen(DiagramPens.ConnectionLine, settings);
        //}

        /// <summary>
        /// Redraws the class on every open diagram
        /// </summary>
        /// <param name="element"></param>
        public static void UpdateClassDisplay(ModelClass element)
        {
            if (element == null)
            {
                return;
            }

            // ensure foreign key attributes have the proper setting to surface the right glyph
            foreach (var data in element.Store.ElementDirectory.AllElements
                     .OfType <Association>()
                     .Where(a => a.Dependent == element && !string.IsNullOrEmpty(a.FKPropertyName))
                     .SelectMany(association => association.FKPropertyName.Split(',')
                                 .Where(propertyName => element.Attributes.Any(attr => attr.Name == propertyName))
                                 .Select(propertyName => new
            {
                Assoc = association
                , Attr = element.Attributes.FirstOrDefault(attr => attr.Name == propertyName)
            })))
            {
                data.Attr.IsForeignKeyFor = data.Assoc.Id;
            }

            // update on every diagram
            foreach (ClassShape classShape in PresentationViewsSubject
                     .GetPresentation(element)
                     .OfType <ClassShape>())
            {
                classShape.Invalidate();
            }

            // ensure any associations have the correct end for composition ownership
            foreach (AssociationConnector connector in element.Store.ElementDirectory
                     .AllElements.OfType <Association>()
                     .Where(a => a.Dependent == element)
                     .SelectMany(association => PresentationViewsSubject.GetPresentation(association)
                                 .OfType <AssociationConnector>()))
            {
                connector.Invalidate();
            }
        }
Example #7
0
      private void ProcessClasses(ModelRoot modelRoot, List<ParsingModels.ModelClass> classDataList)
      {
         foreach (ParsingModels.ModelClass data in classDataList)
         {
            StatusDisplay.Show($"Processing {data.FullName}");

            ModelClass element = modelRoot.Classes.FirstOrDefault(x => x.FullName == data.FullName);

            if (element == null)
            {
               element = new ModelClass(Store,
                                        new PropertyAssignment(ModelClass.NameDomainPropertyId, data.Name),
                                        new PropertyAssignment(ModelClass.NamespaceDomainPropertyId, data.Namespace),
                                        new PropertyAssignment(ModelClass.CustomAttributesDomainPropertyId, data.CustomAttributes),
                                        new PropertyAssignment(ModelClass.CustomInterfacesDomainPropertyId, data.CustomInterfaces),
                                        new PropertyAssignment(ModelClass.IsAbstractDomainPropertyId, data.IsAbstract),
                                        new PropertyAssignment(ModelClass.BaseClassDomainPropertyId, data.BaseClass),
                                        new PropertyAssignment(ModelClass.TableNameDomainPropertyId, data.TableName),
                                        new PropertyAssignment(ModelClass.IsDependentTypeDomainPropertyId, data.IsDependentType));
               
               modelRoot.Classes.Add(element);
            }
            else
            {
               element.Name = data.Name;
               element.Namespace = data.Namespace;
               element.CustomAttributes = data.CustomAttributes;
               element.CustomInterfaces = data.CustomInterfaces;
               element.IsAbstract = data.IsAbstract;
               element.BaseClass = data.BaseClass;
               element.TableName = data.TableName;
               element.IsDependentType = data.IsDependentType;
            }

            ProcessProperties(element, data.Properties);
            ProcessUnidirectionalAssociations(data.UnidirectionalAssociations);
            ProcessBidirectionalAssociations(data.BidirectionalAssociations);
         }
      }
Example #8
0
            /// <summary>Performs the reset operation for the IsOutputDirectoryTracking property for a model element.</summary>
            /// <param name="element">The model element that has the property to reset.</param>
            internal void ResetValue(ModelClass element)
            {
                object    calculatedValue = null;
                ModelRoot modelRoot       = element.Store.ModelRoot();

                try
                {
                    calculatedValue = element.IsDependentType ? modelRoot?.StructOutputDirectory : element.ModelRoot?.EntityOutputDirectory;
                }
                catch (NullReferenceException) { }
                catch (Exception e)
                {
                    if (CriticalException.IsCriticalException(e))
                    {
                        throw;
                    }
                }

                if (calculatedValue != null && element.OutputDirectory == (string)calculatedValue)
                {
                    element.isOutputDirectoryTrackingPropertyStorage = true;
                }
            }
Example #9
0
            /// <summary>Performs the reset operation for the IsDefaultConstructorVisibilityTracking property for a model element.</summary>
            /// <param name="element">The model element that has the property to reset.</param>
            internal void ResetValue(ModelClass element)
            {
                object    calculatedValue = null;
                ModelRoot modelRoot       = element.Store.ModelRoot();

                try
                {
                    calculatedValue = modelRoot?.EntityDefaultConstructorVisibilityDefault;
                }
                catch (NullReferenceException) { }
                catch (Exception e)
                {
                    if (CriticalException.IsCriticalException(e))
                    {
                        throw;
                    }
                }

                if (calculatedValue != null && element.DefaultConstructorVisibility == (TypeAccessModifierExt)calculatedValue)
                {
                    element.isDefaultConstructorVisibilityTrackingPropertyStorage = true;
                }
            }
        private void SetTargetPropertyName(Association element)
        {
            if (string.IsNullOrEmpty(element.TargetPropertyName))
            {
                string rootName = element.TargetMultiplicity == Multiplicity.ZeroMany && ModelRoot.PluralizationService?.IsSingular(element.Target.Name) == true
                                 ? ModelRoot.PluralizationService.Pluralize(element.Target.Name)
                                 : element.Target.Name;

                string identifierName = rootName;
                int    index          = 0;

                ModelClass modelClass = element.Source;

                while (modelClass.HasPropertyNamed(identifierName) ||
                       modelClass.AllSubclasses.Any(c => c.HasPropertyNamed(identifierName)) ||
                       modelClass.AllSuperclasses.Any(c => c.HasPropertyNamed(identifierName)))
                {
                    identifierName = $"{rootName}_{++index}";
                }

                element.TargetPropertyName = identifierName;
            }
        }
Example #11
0
            /// <summary>Performs the reset operation for the IsDatabaseSchemaTracking property for a model element.</summary>
            /// <param name="element">The model element that has the property to reset.</param>
            internal void ResetValue(ModelClass element)
            {
                object    calculatedValue = null;
                ModelRoot modelRoot       = element.Store.ModelRoot();

                try
                {
                    calculatedValue = modelRoot?.DatabaseSchema;
                }
                catch (NullReferenceException) { }
                catch (Exception e)
                {
                    if (CriticalException.IsCriticalException(e))
                    {
                        throw;
                    }
                }

                if (calculatedValue != null && element.DatabaseSchema == (string)calculatedValue)
                {
                    element.isDatabaseSchemaTrackingPropertyStorage = true;
                }
            }
Example #12
0
        public static void UpdateClassDisplay(ModelClass element)
        {
            if (element == null)
            {
                return;
            }

            foreach (ClassShape classShape in PresentationViewsSubject
                     .GetPresentation(element)
                     .OfType <ClassShape>())
            {
                if (element.IsAbstract)
                {
                    classShape.OutlineColor     = Color.OrangeRed;
                    classShape.OutlineThickness = 0.03f;
                    classShape.OutlineDashStyle = DashStyle.Dot;
                }
                else if (element.IsDependentType)
                {
                    classShape.OutlineColor     = Color.ForestGreen;
                    classShape.OutlineThickness = 0.03f;
                    classShape.OutlineDashStyle = DashStyle.Dot;
                }
                else if (element.ImplementNotify)
                {
                    classShape.OutlineColor     = Color.CornflowerBlue;
                    classShape.OutlineThickness = 0.03f;
                    classShape.OutlineDashStyle = DashStyle.Dot;
                }
                else
                {
                    classShape.OutlineColor     = Color.Black;
                    classShape.OutlineThickness = 0.01f;
                    classShape.OutlineDashStyle = DashStyle.Solid;
                }
            }
        }
Example #13
0
      private void ProcessUnidirectionalAssociations(List<ModelUnidirectionalAssociation> unidirectionalAssociations)
      {
         foreach (ModelUnidirectionalAssociation data in unidirectionalAssociations)
         {
            if (Store.Get<UnidirectionalAssociation>()
                     .Any(x => x.Target.FullName == data.TargetClassFullName &&
                               x.Source.FullName == data.SourceClassFullName &&
                               x.TargetPropertyName == data.TargetPropertyName))
               continue;

            ModelClass source = Store.Get<ModelClass>().FirstOrDefault(c => c.FullName == data.SourceClassFullName);

            if (source == null)
               continue;

            ModelClass target = Store.Get<ModelClass>().FirstOrDefault(c => c.FullName == data.TargetClassFullName);

            if (target == null)
               continue;

            // ReSharper disable once UnusedVariable
            UnidirectionalAssociation element = new UnidirectionalAssociation(Store,
                                                    new[]
                                                    {
                                                       new RoleAssignment(UnidirectionalAssociation.UnidirectionalSourceDomainRoleId, source), 
                                                       new RoleAssignment(UnidirectionalAssociation.UnidirectionalTargetDomainRoleId, target)
                                                    },
                                                    new[]
                                                    {
                                                       new PropertyAssignment(Association.SourceMultiplicityDomainPropertyId, ConvertMultiplicity(data.SourceMultiplicity)), 
                                                       new PropertyAssignment(Association.TargetMultiplicityDomainPropertyId, ConvertMultiplicity(data.TargetMultiplicity)), 
                                                       new PropertyAssignment(Association.TargetPropertyNameDomainPropertyId, data.TargetPropertyName), 
                                                       new PropertyAssignment(Association.TargetSummaryDomainPropertyId, data.TargetSummary), 
                                                       new PropertyAssignment(Association.TargetDescriptionDomainPropertyId, data.TargetDescription)
                                                    });
         }
      }
Example #14
0
        /// <inheritdoc />
        public override void ElementDeleting(ElementDeletingEventArgs e)
        {
            base.ElementDeleting(e);

            Generalization element = (Generalization)e.ModelElement;
            Store          store   = element.Store;
            Transaction    current = store.TransactionManager.CurrentTransaction;

            if (current.IsSerializing || ModelRoot.BatchUpdating)
            {
                return;
            }

            if (element.Superclass.IsDeleting)
            {
                return;
            }

            ModelClass subclass   = element.Subclass;
            ModelClass superclass = element.Superclass;

            List <Association> associations = superclass.AllNavigationProperties()
                                              .Select(n => n.AssociationObject)
                                              .Distinct()
                                              .ToList();

            if (!superclass.AllAttributes.Any() && !associations.Any())
            {
                return;
            }

            if (!subclass.IsDeleting && BooleanQuestionDisplay.Show(store, $"Push {superclass.Name} attributes and associations down to {subclass.Name}?") == true)
            {
                superclass.PushDown(subclass);
            }
        }
Example #15
0
        private void ProcessAssociation([NotNull] ModelClass source, [NotNull] ModelClass target, [NotNull] PropertyDeclarationSyntax propertyDecl, bool toMany = false)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }

            if (target == null)
            {
                throw new ArgumentNullException(nameof(target));
            }

            if (propertyDecl == null)
            {
                throw new ArgumentNullException(nameof(propertyDecl));
            }

            Transaction tx = Store.TransactionManager.CurrentTransaction == null
                             ? Store.TransactionManager.BeginTransaction()
                             : null;

            try
            {
                string propertyName = propertyDecl.Identifier.ToString();

                // since we don't have enough information from the code, we'll create unidirectional associations
                // cardinality 1 on the source end, 0..1 or 0..* on the target, depending on the parameter

                XMLDocumentation xmlDocumentation = new XMLDocumentation(propertyDecl);

                // if the association doesn't yet exist, create it
                if (!Store.ElementDirectory
                    .AllElements
                    .OfType <UnidirectionalAssociation>()
                    .Any(a => a.Source == source &&
                         a.Target == target &&
                         a.TargetPropertyName == propertyName))
                {
                    // if there's a unidirectional going the other direction, we'll whack that one and make a bidirectional
                    // otherwise, proceed as planned
                    UnidirectionalAssociation compliment = Store.ElementDirectory
                                                           .AllElements
                                                           .OfType <UnidirectionalAssociation>()
                                                           .FirstOrDefault(a => a.Source == target &&
                                                                           a.Target == source);

                    if (compliment == null)
                    {
                        UnidirectionalAssociation _ =
                            new UnidirectionalAssociation(Store,
                                                          new[]
                        {
                            new RoleAssignment(UnidirectionalAssociation.UnidirectionalSourceDomainRoleId, source),
                            new RoleAssignment(UnidirectionalAssociation.UnidirectionalTargetDomainRoleId, target)
                        },
                                                          new[]
                        {
                            new PropertyAssignment(Association.SourceMultiplicityDomainPropertyId, Multiplicity.One),

                            new PropertyAssignment(Association.TargetMultiplicityDomainPropertyId, toMany ? Multiplicity.ZeroMany : Multiplicity.ZeroOne),
                            new PropertyAssignment(Association.TargetPropertyNameDomainPropertyId, propertyName),
                            new PropertyAssignment(Association.TargetSummaryDomainPropertyId, xmlDocumentation.Summary),
                            new PropertyAssignment(Association.TargetDescriptionDomainPropertyId, xmlDocumentation.Description)
                        });
                    }
                    else
                    {
                        compliment.Delete();

                        BidirectionalAssociation _ =
                            new BidirectionalAssociation(Store,
                                                         new[]
                        {
                            new RoleAssignment(BidirectionalAssociation.BidirectionalSourceDomainRoleId, source),
                            new RoleAssignment(BidirectionalAssociation.BidirectionalTargetDomainRoleId, target)
                        },
                                                         new[]
                        {
                            new PropertyAssignment(Association.SourceMultiplicityDomainPropertyId, compliment.TargetMultiplicity),
                            new PropertyAssignment(BidirectionalAssociation.SourcePropertyNameDomainPropertyId, compliment.TargetPropertyName),
                            new PropertyAssignment(BidirectionalAssociation.SourceSummaryDomainPropertyId, compliment.TargetSummary),
                            new PropertyAssignment(BidirectionalAssociation.SourceDescriptionDomainPropertyId, compliment.TargetDescription),

                            new PropertyAssignment(Association.TargetMultiplicityDomainPropertyId, toMany ? Multiplicity.ZeroMany : Multiplicity.ZeroOne),
                            new PropertyAssignment(Association.TargetPropertyNameDomainPropertyId, propertyName),
                            new PropertyAssignment(Association.TargetSummaryDomainPropertyId, xmlDocumentation.Summary),
                            new PropertyAssignment(Association.TargetDescriptionDomainPropertyId, xmlDocumentation.Description)
                        });
                    }
                }
            }
            catch
            {
                tx.Rollback();
                tx = null;

                throw;
            }
            finally
            {
                tx?.Commit();
            }
        }
Example #16
0
        private void ProcessProperties([NotNull] ClassDeclarationSyntax classDecl)
        {
            if (classDecl == null)
            {
                throw new ArgumentNullException(nameof(classDecl));
            }

            Transaction tx = Store.TransactionManager.CurrentTransaction == null
                             ? Store.TransactionManager.BeginTransaction()
                             : null;

            try
            {
                string     className  = classDecl.Identifier.Text;
                ModelRoot  modelRoot  = Store.ModelRoot();
                ModelClass modelClass = Store.Get <ModelClass>().FirstOrDefault(c => c.Name == className);
                modelClass.Attributes.Clear();

                foreach (PropertyDeclarationSyntax propertyDecl in classDecl.DescendantNodes().OfType <PropertyDeclarationSyntax>())
                {
                    // if the property has a fat arrow expression as its direct descendent, it's a readonly calculated property
                    // TODO: we should handle this
                    // but for this release, ignore it
                    if (propertyDecl.ChildNodes().OfType <ArrowExpressionClauseSyntax>().Any())
                    {
                        continue;
                    }

                    AccessorDeclarationSyntax getAccessor = (AccessorDeclarationSyntax)propertyDecl.DescendantNodes().FirstOrDefault(node => node.IsKind(SyntaxKind.GetAccessorDeclaration));
                    AccessorDeclarationSyntax setAccessor = (AccessorDeclarationSyntax)propertyDecl.DescendantNodes().FirstOrDefault(node => node.IsKind(SyntaxKind.SetAccessorDeclaration));

                    // if there's no getAccessor, why are we bothering?
                    if (getAccessor == null)
                    {
                        continue;
                    }

                    string     propertyName = propertyDecl.Identifier.ToString();
                    string     propertyType = propertyDecl.Type.ToString();
                    ModelClass target       = modelRoot.Classes.FirstOrDefault(t => t.Name == propertyType);

                    // is the property type a generic?
                    // assume it's a list
                    // TODO: this really isn't a good assumption. Fix later
                    if (propertyDecl.ChildNodes().OfType <GenericNameSyntax>().Any())
                    {
                        GenericNameSyntax genericDecl  = propertyDecl.ChildNodes().OfType <GenericNameSyntax>().FirstOrDefault();
                        List <string>     contentTypes = genericDecl.DescendantNodes().OfType <IdentifierNameSyntax>().Select(i => i.Identifier.ToString()).ToList();

                        // there can only be one generic argument
                        if (contentTypes.Count != 1)
                        {
                            WarningDisplay.Show($"Found {className}.{propertyName}, but its type ({genericDecl.Identifier}<{String.Join(", ", contentTypes)}>) isn't anything expected. Ignoring...");

                            continue;
                        }

                        propertyType = contentTypes[0];
                        target       = modelRoot.Classes.FirstOrDefault(t => t.Name == propertyType);

                        if (target == null)
                        {
                            target = new ModelClass(Store, new PropertyAssignment(ModelClass.NameDomainPropertyId, propertyType));
                            modelRoot.Classes.Add(target);
                        }

                        ProcessAssociation(modelClass, target, propertyDecl, true);

                        continue;
                    }


                    // is the property type an existing ModelClass?
                    if (target != null)
                    {
                        ProcessAssociation(modelClass, target, propertyDecl);

                        continue;
                    }

                    bool propertyShowsNullable = propertyDecl.DescendantNodes().OfType <NullableTypeSyntax>().Any();

                    // is the property type something we don't know about?
                    if (!modelRoot.IsValidCLRType(propertyType))
                    {
                        // might be an enum. If so, we'll handle it like a CLR type
                        // if it's nullable, it's definitely an enum, but if we don't know about it, it could be an enum or a class
                        if (!KnownEnums.Contains(propertyType) && !propertyShowsNullable)
                        {
                            // assume it's a class and create the class
                            target = new ModelClass(Store, new PropertyAssignment(ModelClass.NameDomainPropertyId, propertyType));
                            modelRoot.Classes.Add(target);

                            ProcessAssociation(modelClass, target, propertyDecl);

                            continue;
                        }
                    }

                    // if we're here, it's just a property (CLR or enum)
                    try
                    {
                        // ReSharper disable once UseObjectOrCollectionInitializer
                        ModelAttribute modelAttribute = new ModelAttribute(Store, new PropertyAssignment(ModelAttribute.NameDomainPropertyId, propertyName))
                        {
                            Type       = ModelAttribute.ToCLRType(propertyDecl.Type.ToString()).Trim('?'),
                            Required   = propertyDecl.HasAttribute("RequiredAttribute") || !propertyShowsNullable,
                            Indexed    = propertyDecl.HasAttribute("IndexedAttribute"),
                            IsIdentity = propertyDecl.HasAttribute("KeyAttribute"),
                            Virtual    = propertyDecl.DescendantTokens().Any(t => t.IsKind(SyntaxKind.VirtualKeyword))
                        };

                        if (modelAttribute.Type.ToLower() == "string")
                        {
                            AttributeSyntax         maxLengthAttribute = propertyDecl.GetAttribute("MaxLengthAttribute");
                            AttributeArgumentSyntax maxLength          = maxLengthAttribute?.GetAttributeArguments()?.FirstOrDefault();

                            if (maxLength != null)
                            {
                                modelAttribute.MaxLength = TryParse(maxLength.Expression.ToString(), out int _max) ? _max : -1;
                            }

                            AttributeSyntax         minLengthAttribute = propertyDecl.GetAttribute("MinLengthAttribute");
                            AttributeArgumentSyntax minLength          = minLengthAttribute?.GetAttributeArguments()?.FirstOrDefault();

                            if (minLength != null)
                            {
                                modelAttribute.MinLength = TryParse(minLength.Expression.ToString(), out int _min) ? _min : 0;
                            }
                        }
                        else
                        {
                            modelAttribute.MaxLength = -1;
                            modelAttribute.MinLength = 0;
                        }

                        // if no setAccessor, it's a calculated readonly property
                        if (setAccessor == null)
                        {
                            modelAttribute.Persistent = false;
                            modelAttribute.ReadOnly   = true;
                        }

                        modelAttribute.AutoProperty = !getAccessor.DescendantNodes().Any(node => node.IsKind(SyntaxKind.Block)) && !setAccessor.DescendantNodes().Any(node => node.IsKind(SyntaxKind.Block));

                        modelAttribute.SetterVisibility = setAccessor.Modifiers.Any(m => m.ToString() == "protected")
                                                       ? SetterAccessModifier.Protected
                                                       : setAccessor.Modifiers.Any(m => m.ToString() == "internal")
                                                          ? SetterAccessModifier.Internal
                                                          : SetterAccessModifier.Public;

                        XMLDocumentation xmlDocumentation = new XMLDocumentation(propertyDecl);
                        modelAttribute.Summary     = xmlDocumentation.Summary;
                        modelAttribute.Description = xmlDocumentation.Description;
                        modelClass.Attributes.Add(modelAttribute);
                    }
                    catch
                    {
                        WarningDisplay.Show($"Could not parse '{className}.{propertyDecl.Identifier}'.");
                    }
                }
            }
            catch
            {
                tx = null;

                throw;
            }
            finally
            {
                tx?.Commit();
            }
        }
        public override void ElementPropertyChanged(ElementPropertyChangedEventArgs e)
        {
            base.ElementPropertyChanged(e);

            ModelAttribute element = (ModelAttribute)e.ModelElement;

            if (element.IsDeleted)
            {
                return;
            }

            ModelClass modelClass = element.ModelClass;
            ModelRoot  modelRoot  = element.Store.ModelRoot();

            Store       store   = element.Store;
            Transaction current = store.TransactionManager.CurrentTransaction;

            if (current.IsSerializing || ModelRoot.BatchUpdating)
            {
                return;
            }

            if (Equals(e.NewValue, e.OldValue))
            {
                return;
            }

            List <string> errorMessages = EFCoreValidator.GetErrors(element).ToList();

            switch (e.DomainProperty.Name)
            {
            case "AutoProperty":
            {
                if (element.AutoProperty)
                {
                    element.PersistencePoint = PersistencePointType.Property;
                    element.ImplementNotify  = false;
                }
            }

            break;

            case "IdentityType":
            {
                if (element.IsIdentity)
                {
                    if (element.IdentityType == IdentityType.None)
                    {
                        errorMessages.Add($"{modelClass.Name}.{element.Name}: Identity properties must have an identity type defined");
                    }
                }
                else
                {
                    element.IdentityType = IdentityType.None;
                }
            }

            break;

            case "ImplementNotify":
            {
                if (element.IsIdentity)
                {
                    element.ImplementNotify = false;
                }

                if (element.ImplementNotify)
                {
                    element.AutoProperty = false;
                }
            }

            break;

            case "Indexed":
            {
                if (element.IsIdentity)
                {
                    element.Indexed = true;
                }

                if (element.IsConcurrencyToken)
                {
                    element.Indexed = false;
                }

                if (element.Indexed)
                {
                    element.Persistent = true;
                }
            }

            break;

            case "InitialValue":
            {
                string newInitialValue = (string)e.NewValue;

                // if the property is an Enum and the user just typed the name of the Enum value without the Enum type name, help them out
                if (element.ModelClass.ModelRoot.Enums.Any(x => x.Name == element.Type) && !newInitialValue.Contains("."))
                {
                    newInitialValue = element.InitialValue = $"{element.Type}.{newInitialValue}";
                }

                if (!element.IsValidInitialValue(null, newInitialValue))
                {
                    errorMessages.Add($"{modelClass.Name}.{element.Name}: {newInitialValue} isn't a valid value for {element.Type}");
                }
            }

            break;

            case "IsConcurrencyToken":
            {
                bool newIsConcurrencyToken = (bool)e.NewValue;

                if (newIsConcurrencyToken)
                {
                    element.IsIdentity = false;
                    element.Persistent = true;
                    element.Required   = true;
                    element.Type       = "Binary";
                }
            }

            break;

            case "IsIdentity":
            {
                bool newIsIdentity = (bool)e.NewValue;

                if (newIsIdentity)
                {
                    if (element.ModelClass.IsDependentType)
                    {
                        errorMessages.Add($"{modelClass.Name}.{element.Name}: Can't make {element.Name} an identity because {modelClass.Name} is a dependent type and can't have an identity property.");
                    }
                    else
                    {
                        if (!modelRoot.ValidIdentityAttributeTypes.Contains(element.Type))
                        {
                            errorMessages.Add($"{modelClass.Name}.{element.Name}: Properties of type {element.Type} can't be used as identity properties.");
                        }
                        else
                        {
                            element.IsConcurrencyToken = false;
                            element.Indexed            = true;
                            element.IndexedUnique      = true;
                            element.Persistent         = true;
                            element.Required           = true;

                            if (element.IdentityType == IdentityType.None)
                            {
                                element.IdentityType = IdentityType.AutoGenerated;
                            }
                        }
                    }
                }
                else
                {
                    element.IdentityType = IdentityType.None;
                }
            }

            break;

            case "MinLength":
            {
                int minLengthValue = (int)e.NewValue;

                if (element.Type != "String")
                {
                    element.MinLength = 0;
                }
                else if (minLengthValue < 0)
                {
                    errorMessages.Add($"{modelClass.Name}.{element.Name}: MinLength must be zero or a positive number");
                }
                else if (element.MaxLength > 0 && minLengthValue > element.MaxLength)
                {
                    errorMessages.Add($"{modelClass.Name}.{element.Name}: MinLength cannot be greater than MaxLength");
                }
            }

            break;

            case "MaxLength":
            {
                if (element.Type != "String")
                {
                    element.MaxLength = null;
                }
                else
                {
                    int?maxLengthValue = (int?)e.NewValue;

                    if (maxLengthValue > 0 && element.MinLength > maxLengthValue)
                    {
                        errorMessages.Add($"{modelClass.Name}.{element.Name}: MinLength cannot be greater than MaxLength");
                    }
                }
            }

            break;

            case "Name":
            {
                string newName = (string)e.NewValue;

                if (string.IsNullOrEmpty(newName))
                {
                    errorMessages.Add("Name must be a valid .NET identifier");
                }
                else
                {
                    ParseResult fragment;

                    try
                    {
                        fragment = ModelAttribute.Parse(modelRoot, newName);

                        if (fragment == null)
                        {
                            errorMessages.Add($"{modelClass.Name}: Could not parse entry '{newName}'");
                        }
                        else
                        {
                            if (string.IsNullOrEmpty(fragment.Name) || !CodeGenerator.IsValidLanguageIndependentIdentifier(fragment.Name))
                            {
                                errorMessages.Add($"{modelClass.Name}: Property name '{fragment.Name}' isn't a valid .NET identifier");
                            }
                            else if (modelClass.AllAttributes.Except(new[] { element }).Any(x => x.Name == fragment.Name))
                            {
                                errorMessages.Add($"{modelClass.Name}: Property name '{fragment.Name}' already in use");
                            }
                            else if (modelClass.AllNavigationProperties().Any(p => p.PropertyName == fragment.Name))
                            {
                                errorMessages.Add($"{modelClass.Name}: Property name '{fragment.Name}' already in use");
                            }
                            else
                            {
                                element.Name = fragment.Name;

                                if (fragment.Type != null)
                                {
                                    element.Type = fragment.Type;
                                }

                                if (fragment.Required != null)
                                {
                                    element.Required = fragment.Required.Value;
                                }

                                element.MaxLength = fragment.MaxLength;
                                element.MinLength = fragment.MinLength ?? 0;

                                if (fragment.InitialValue != null)
                                {
                                    element.InitialValue = fragment.InitialValue;
                                }

                                if (fragment.IsIdentity)
                                {
                                    element.IsIdentity = true; // don't reset to false if not entered as part of name
                                }
                            }
                        }
                    }
                    catch (Exception exception)
                    {
                        errorMessages.Add($"{modelClass.Name}: Could not parse entry '{newName}': {exception.Message}");
                    }
                }
            }

            break;

            case "PersistencePoint":
            {
                if ((PersistencePointType)e.NewValue == PersistencePointType.Field)
                {
                    element.AutoProperty = false;
                }
            }

            break;

            case "Persistent":
            {
                bool newPersistent = (bool)e.NewValue;

                if (!newPersistent)
                {
                    element.IsIdentity         = false;
                    element.Indexed            = false;
                    element.IndexedUnique      = false;
                    element.IdentityType       = IdentityType.None;
                    element.IsConcurrencyToken = false;
                    element.Virtual            = false;
                }
            }

            break;

            case "ReadOnly":
            {
                if (!element.Persistent || element.SetterVisibility != SetterAccessModifier.Public)
                {
                    element.ReadOnly = false;
                }
            }

            break;

            case "Required":
            {
                bool newRequired = (bool)e.NewValue;

                if (!newRequired)
                {
                    if (element.IsIdentity || element.IsConcurrencyToken)
                    {
                        element.Required = true;
                    }
                }
            }

            break;

            case "Type":
            {
                string newType = (string)e.NewValue;

                if (element.IsIdentity)
                {
                    if (!modelRoot.ValidIdentityAttributeTypes.Contains(ModelAttribute.ToCLRType(newType)))
                    {
                        errorMessages.Add($"{modelClass.Name}.{element.Name}: Properties of type {newType} can't be used as identity properties.");
                    }
                    else
                    {
                        element.Required   = true;
                        element.Persistent = true;
                    }
                }

                if (newType != "String")
                {
                    element.MaxLength  = null;
                    element.MinLength  = 0;
                    element.StringType = HTML5Type.None;
                }
                else
                {
                    if (!element.IsValidInitialValue(newType))
                    {
                        element.InitialValue = null;
                    }
                }

                if (element.IsConcurrencyToken)
                {
                    element.Type = "Binary";
                }

                if (!element.SupportsInitialValue)
                {
                    element.InitialValue = null;
                }
            }

            break;
            }

            errorMessages = errorMessages.Where(m => m != null).ToList();

            if (errorMessages.Any())
            {
                current.Rollback();
                ErrorDisplay.Show(string.Join("\n", errorMessages));
            }
        }
Example #18
0
      private void ProcessBidirectionalAssociations(ParsingModels.ModelClass modelClass)
      {
         List<ModelBidirectionalAssociation> bidirectionalAssociations = modelClass.BidirectionalAssociations;

         foreach (ModelBidirectionalAssociation data in bidirectionalAssociations)
         {
            if (Store.ModelRoot().EntityFrameworkVersion == EFVersion.EF6 && data.SourceMultiplicity != ParsingModels.Multiplicity.ZeroMany && data.TargetMultiplicity != ParsingModels.Multiplicity.ZeroMany)
               data.ForeignKey = null;

            BidirectionalAssociation existing = Store.GetAll<BidirectionalAssociation>()
                                                     .FirstOrDefault(x => x.Target.Name == data.TargetClassName
                                                                       && x.Source.Name == data.SourceClassName
                                                                       && x.Source.Name == modelClass.Name // just to be sure
                                                                       && x.TargetPropertyName == data.TargetPropertyName
                                                                       && x.SourcePropertyName == data.SourcePropertyName)
                                             ?? Store.GetAll<BidirectionalAssociation>()
                                                     .FirstOrDefault(x => x.Source.Name == data.TargetClassName
                                                                       && x.Target.Name == data.SourceClassName
                                                                       && x.Target.Name == modelClass.Name // just to be sure
                                                                       && x.SourcePropertyName == data.TargetPropertyName
                                                                       && x.TargetPropertyName == data.SourcePropertyName);

            if (existing != null)
            {
               if (string.IsNullOrWhiteSpace(existing.FKPropertyName) && !string.IsNullOrWhiteSpace(data.ForeignKey))
               {
                  existing.FKPropertyName = data.ForeignKey;
                  existing.Source.ModelRoot.ExposeForeignKeys = true;
               }

               continue;
            }

            ModelClass source = Store.GetAll<ModelClass>().FirstOrDefault(c => c.Name == data.SourceClassName);
            ModelClass target = Store.GetAll<ModelClass>().FirstOrDefault(c => c.Name == data.TargetClassName);

            if (source == null || target == null || source.FullName != modelClass.FullName)
               continue;

            // ReSharper disable once UnusedVariable
            BidirectionalAssociation element = new BidirectionalAssociation(Store,
                                                   new[]
                                                   {
                                                      new RoleAssignment(BidirectionalAssociation.BidirectionalSourceDomainRoleId, source),
                                                      new RoleAssignment(BidirectionalAssociation.BidirectionalTargetDomainRoleId, target)
                                                   },
                                                   new[]
                                                   {
                                                      new PropertyAssignment(Association.SourceMultiplicityDomainPropertyId, ConvertMultiplicity(data.SourceMultiplicity)),
                                                      new PropertyAssignment(Association.TargetMultiplicityDomainPropertyId, ConvertMultiplicity(data.TargetMultiplicity)),
                                                      new PropertyAssignment(Association.TargetPropertyNameDomainPropertyId, data.TargetPropertyName),
                                                      new PropertyAssignment(Association.TargetSummaryDomainPropertyId, data.TargetSummary),
                                                      new PropertyAssignment(Association.TargetDescriptionDomainPropertyId, data.TargetDescription),
                                                      new PropertyAssignment(Association.FKPropertyNameDomainPropertyId, data.ForeignKey),
                                                      new PropertyAssignment(Association.SourceRoleDomainPropertyId, ConvertRole(data.SourceRole)), 
                                                      new PropertyAssignment(Association.TargetRoleDomainPropertyId, ConvertRole(data.TargetRole)), 
                                                      new PropertyAssignment(BidirectionalAssociation.SourcePropertyNameDomainPropertyId, data.SourcePropertyName),
                                                      new PropertyAssignment(BidirectionalAssociation.SourceSummaryDomainPropertyId, data.SourceSummary),
                                                      new PropertyAssignment(BidirectionalAssociation.SourceDescriptionDomainPropertyId, data.SourceDescription),
                                                   });
            AssociationChangedRules.SetEndpointRoles(element);
         }
      }
Example #19
0
      private List<ModelElement> ProcessClasses(ModelRoot modelRoot, List<ParsingModels.ModelClass> classDataList)
      {
         List<ModelElement> result = new List<ModelElement>();
         RemoveDuplicateBidirectionalAssociations(classDataList);
         Dictionary<string, List<ModelClass>> baseClasses = new Dictionary<string, List<ModelClass>>();

         foreach (ParsingModels.ModelClass data in classDataList)
         {
            StatusDisplay.Show($"Processing {data.FullName}");

            ModelClass element = modelRoot.Classes.FirstOrDefault(x => x.FullName == data.FullName);

            if (element == null)
            {
               element = new ModelClass(Store,
                                        new PropertyAssignment(ModelClass.NameDomainPropertyId, data.Name),
                                        new PropertyAssignment(ModelClass.NamespaceDomainPropertyId, data.Namespace),
                                        new PropertyAssignment(ModelClass.CustomAttributesDomainPropertyId, data.CustomAttributes),
                                        new PropertyAssignment(ModelClass.CustomInterfacesDomainPropertyId, data.CustomInterfaces),
                                        new PropertyAssignment(ModelClass.IsAbstractDomainPropertyId, data.IsAbstract),
                                        new PropertyAssignment(ModelClass.TableNameDomainPropertyId, data.TableName),
                                        new PropertyAssignment(ModelClass.IsDependentTypeDomainPropertyId, data.IsDependentType));

               modelRoot.Classes.Add(element);
               result.Add(element);
            }
            else
            {
               element.Name = data.Name;
               element.Namespace = data.Namespace;
               element.CustomAttributes = data.CustomAttributes;
               element.CustomInterfaces = data.CustomInterfaces;
               element.IsAbstract = data.IsAbstract;
               element.TableName = data.TableName;
               element.IsDependentType = data.IsDependentType;
            }

            // if base class exists and isn't in the list yet, we can't hook it up to this class
            // so we'll defer base class linkage for all classes until we're sure they're all in the model
            if (!string.IsNullOrEmpty(data.BaseClass))
            {
               if (!baseClasses.ContainsKey(data.BaseClass))
                  baseClasses.Add(data.BaseClass, new List<ModelClass>());
               baseClasses[data.BaseClass].Add(element);
            }

            ProcessProperties(element, data.Properties);
         }

         // now we can fixup the generalization links
         foreach (string baseClassName in baseClasses.Keys)
         {
            foreach (ModelClass subClass in baseClasses[baseClassName])
            {
               ModelClass superClass = modelRoot.Classes.FirstOrDefault(s => s.Name == baseClassName);

               if (superClass == null)
               {
                  // we couldn't find the superclass, so we'll assume it's external to this assembly
                  superClass = new ModelClass(Store,
                                              new PropertyAssignment(ModelClass.NameDomainPropertyId, subClass.BaseClass),
                                              new PropertyAssignment(ModelClass.NamespaceDomainPropertyId, subClass.Namespace),
                                              new PropertyAssignment(ModelClass.GenerateCodeDomainPropertyId, false));
                  modelRoot.Classes.Add(superClass);
               }
               else
                  subClass.Superclass = superClass;

               subClass.BaseClass = baseClassName;
            }
         }

         // classes are all created, so we can work the associations
         foreach (ParsingModels.ModelClass data in classDataList)
         {
            ProcessUnidirectionalAssociations(data);
            ProcessBidirectionalAssociations(data);
         }

         return result;
      }
Example #20
0
        public bool Process(string filename)
        {
            if (string.IsNullOrEmpty(filename))
            {
                throw new ArgumentNullException(nameof(filename));
            }

            try
            {
                // read the file
                string fileContents = File.ReadAllText(filename);

                // parse the contents
                SyntaxTree tree = CSharpSyntaxTree.ParseText(fileContents);

                if (tree.GetRoot() is CompilationUnitSyntax root)
                {
                    List <ClassDeclarationSyntax> classDecls = root.DescendantNodes().OfType <ClassDeclarationSyntax>().Where(classDecl => classDecl.BaseList == null || classDecl.BaseList.Types.FirstOrDefault()?.ToString() != "DbContext").ToList();
                    List <EnumDeclarationSyntax>  enumDecls  = root.DescendantNodes().OfType <EnumDeclarationSyntax>().ToList();

                    if (!classDecls.Any() && !enumDecls.Any())
                    {
                        WarningDisplay.Show($"Couldn't find any classes or enums to add to the model in {filename}");

                        return(false);
                    }

                    // keep this order: enums, classes, class properties

                    foreach (EnumDeclarationSyntax enumDecl in enumDecls)
                    {
                        ProcessEnum(enumDecl);
                    }

                    List <ModelClass>             processedClasses = new List <ModelClass>();
                    List <ClassDeclarationSyntax> badClasses       = new List <ClassDeclarationSyntax>();

                    foreach (ClassDeclarationSyntax classDecl in classDecls)
                    {
                        ModelClass modelClass = ProcessClass(classDecl);
                        if (modelClass == null)
                        {
                            badClasses.Add(classDecl);
                        }
                        else
                        {
                            processedClasses.Add(modelClass);
                        }
                    }

                    // process last so all classes and enums are already in the model
                    foreach (ClassDeclarationSyntax classDecl in classDecls.Except(badClasses))
                    {
                        ProcessProperties(classDecl);
                    }

                    // now that all the properties are in, go through the classes again and ensure identities are present based on convention
                    // ReSharper disable once LoopCanBePartlyConvertedToQuery
                    foreach (ModelClass modelClass in processedClasses.Where(c => !c.AllIdentityAttributes.Any()))
                    {
                        // no identity attribute. Only look in current class for attributes that could be identity by convention
                        List <ModelAttribute> identitiesByConvention = modelClass.Attributes.Where(a => a.Name == "Id" || a.Name == $"{modelClass.Name}Id").ToList();

                        // if both 'Id' and '[ClassName]Id' are present, don't do anything since we don't know which to make the identity
                        if (identitiesByConvention.Count == 1)
                        {
                            using (Transaction transaction = Store.TransactionManager.BeginTransaction("Add identity"))
                            {
                                identitiesByConvention[0].IsIdentity = true;
                                transaction.Commit();
                            }
                        }
                    }
                }
            }
            catch
            {
                ErrorDisplay.Show("Error interpreting " + filename);

                return(false);
            }

            return(true);
        }
Example #21
0
        private ModelClass ProcessClass([NotNull] ClassDeclarationSyntax classDecl, NamespaceDeclarationSyntax namespaceDecl = null)
        {
            ModelClass result;

            if (classDecl == null)
            {
                throw new ArgumentNullException(nameof(classDecl));
            }

            ModelRoot modelRoot = Store.ModelRoot();
            string    className = classDecl.Identifier.Text.Split(':').LastOrDefault();

            if (!ValidateInput())
            {
                return(null);
            }

            Transaction tx = Store.TransactionManager.CurrentTransaction == null
                             ? Store.TransactionManager.BeginTransaction()
                             : null;

            List <string> customInterfaces = new List <string>();

            try
            {
                result = Store.Get <ModelClass>().FirstOrDefault(c => c.Name == className);

                if (result == null)
                {
                    result = new ModelClass(Store
                                            , new PropertyAssignment(ModelClass.NameDomainPropertyId, className)
                                            , new PropertyAssignment(ModelClass.NamespaceDomainPropertyId, namespaceDecl?.Name?.ToString() ?? modelRoot.Namespace)
                                            , new PropertyAssignment(ModelClass.IsAbstractDomainPropertyId, classDecl.DescendantNodes().Any(n => n.Kind() == SyntaxKind.AbstractKeyword)));

                    modelRoot.Classes.Add(result);
                }

                ModelClass superClass = FindSuperClass();

                if (superClass != null)
                {
                    result.Superclass = superClass;
                }

                if (result.CustomInterfaces != null)
                {
                    customInterfaces.AddRange(result.CustomInterfaces
                                              .Split(',')
                                              .Where(i => !string.IsNullOrEmpty(i))
                                              .Select(i => i.Trim()));
                }

                if (customInterfaces.Contains("INotifyPropertyChanged"))
                {
                    result.ImplementNotify = true;
                    customInterfaces.Remove("INotifyPropertyChanged");
                }

                if (result.Superclass != null && customInterfaces.Contains(result.Superclass.Name))
                {
                    customInterfaces.Remove(result.Superclass.Name);
                }

                result.CustomInterfaces = customInterfaces.Any()
                                         ? string.Join(",", customInterfaces.Distinct())
                                         : null;

                AttributeSyntax tableAttribute = classDecl.GetAttribute("Table");

                if (tableAttribute != null)
                {
                    result.TableName = tableAttribute.GetAttributeArguments().First().Expression.ToString().Trim('"');

                    string schemaName = tableAttribute.GetNamedArgumentValue("Schema");
                    if (schemaName != null)
                    {
                        result.DatabaseSchema = schemaName;
                    }
                }

                XMLDocumentation xmlDocumentation = new XMLDocumentation(classDecl);
                result.Summary     = xmlDocumentation.Summary;
                result.Description = xmlDocumentation.Description;
                tx?.Commit();
            }
            catch
            {
                tx?.Rollback();
                throw;
            }

            return(result);

            ModelClass FindSuperClass()
            {
                ModelClass superClass = null;

                // Base classes and interfaces
                // Check these first. If we need to add new models, we want the base class already in the store
                IEnumerable <BaseTypeSyntax> baseTypes = (classDecl.BaseList?.Types ?? Enumerable.Empty <BaseTypeSyntax>());

                foreach (string baseName in baseTypes.Select(type => type.ToString().Split(':').Last()))
                {
                    // Do we know this is an interface?
                    if (KnownInterfaces.Contains(baseName) || superClass != null || result.Superclass != null)
                    {
                        customInterfaces.Add(baseName);

                        if (!KnownInterfaces.Contains(baseName))
                        {
                            KnownInterfaces.Add(baseName);
                        }

                        continue;
                    }

                    // is it inheritance or an interface?
                    superClass = modelRoot.Classes.FirstOrDefault(c => c.Name == baseName);

                    // if it's not in the model, we just don't know. Ask the user
                    if (superClass == null && (KnownClasses.Contains(baseName) || QuestionDisplay.Show($"For class {className}, is {baseName} the base class?") == true))
                    {
                        string[] nameparts = baseName.Split('.');

                        superClass = nameparts.Length == 1
                                  ? new ModelClass(Store, new PropertyAssignment(ModelClass.NameDomainPropertyId, nameparts.Last()))
                                  : new ModelClass(Store
                                                   , new PropertyAssignment(ModelClass.NameDomainPropertyId, nameparts.Last())
                                                   , new PropertyAssignment(ModelClass.NamespaceDomainPropertyId, string.Join(".", nameparts.Take(nameparts.Length - 1))));

                        modelRoot.Classes.Add(superClass);
                    }
                    else
                    {
                        customInterfaces.Add(baseName);
                        KnownInterfaces.Add(baseName);
                    }
                }

                return(superClass);
            }

            bool ValidateInput()
            {
                if (className == null)
                {
                    ErrorDisplay.Show("Can't find class name");

                    return(false);
                }

                if (namespaceDecl == null && classDecl.Parent is NamespaceDeclarationSyntax classDeclParent)
                {
                    namespaceDecl = classDeclParent;
                }

                if (Store.Get <ModelEnum>().Any(c => c.Name == className))
                {
                    ErrorDisplay.Show($"'{className}' already exists in model as an Enum.");

                    return(false);
                }

                if (classDecl.TypeParameterList != null)
                {
                    ErrorDisplay.Show($"Can't add generic class '{className}'.");

                    return(false);
                }

                return(true);
            }
        }
Example #22
0
 /// <summary>
 ///    Method to set IsNamespaceTracking to false so that this instance of this tracking property is not
 ///    storage-based.
 /// </summary>
 /// <param name="element">
 ///    The element on which to reset the property
 ///    value.
 /// </param>
 internal void PreResetValue(ModelClass element) =>
 // Force the IsNamespaceTracking property to false so that the value
 // of the Namespace property is retrieved from storage.
 element.isNamespaceTrackingPropertyStorage = false;
Example #23
0
        protected override void OnSelectionChanged(EventArgs e)
        {
            base.OnSelectionChanged(e);

            // select element in tree
            if (PrimarySelection != null && PrimarySelection is ModelElement element)
            {
                using (Transaction t = element.Store.TransactionManager.BeginTransaction("TreeSelectionChanged"))
                {
                    Diagram diagram = element.GetActiveDiagramView()?.Diagram;

                    switch (PrimarySelection)
                    {
                    case ModelDiagramData modelDiagramData:
                        // user selected a diagram. Open it.
                        EFModelDocData docData = (EFModelDocData)TreeContainer.ModelingDocData;
                        docData.OpenView(Constants.LogicalView, new Mexedge.VisualStudio.Modeling.ViewContext(modelDiagramData.Name, typeof(EFModelDiagram), docData.RootElement));

                        break;

                    case ModelClass modelClass:
                        // user selected a class. If it's in the current diagram, find it, center it and make it visible
                        ShapeElement primaryShapeElement = PresentationViewsSubject.GetPresentation(modelClass)
                                                           .OfType <ShapeElement>()
                                                           .FirstOrDefault(s => s.Diagram == diagram);

                        //if (primaryShapeElement == null || !primaryShapeElement.IsVisible)
                        //   break;

                        modelClass.LocateInDiagram(true);

                        // then fix up the compartments since they might need it
                        ModelElement[] classElements = { modelClass };
                        CompartmentItemAddRule.UpdateCompartments(classElements, typeof(ClassShape), "AttributesCompartment", false);
                        CompartmentItemAddRule.UpdateCompartments(classElements, typeof(ClassShape), "AssociationsCompartment", false);
                        CompartmentItemAddRule.UpdateCompartments(classElements, typeof(ClassShape), "SourcesCompartment", false);

                        // any associations to visible classes on this diagram need to be visible as well
                        foreach (NavigationProperty navigationProperty in modelClass.LocalNavigationProperties())
                        {
                            ModelClass other = navigationProperty.AssociationObject.Source == modelClass
                                                 ? navigationProperty.AssociationObject.Target
                                                 : navigationProperty.AssociationObject.Source;

                            // should never happen
                            if (other == null)
                            {
                                continue;
                            }

                            ShapeElement shapeElement = PresentationViewsSubject.GetPresentation(other)
                                                        .OfType <ShapeElement>()
                                                        .FirstOrDefault(s => s.Diagram == diagram);

                            if (shapeElement != null && shapeElement.IsVisible)
                            {
                                ShapeElement connectorElement = PresentationViewsSubject.GetPresentation(navigationProperty.AssociationObject)
                                                                .OfType <AssociationConnector>()
                                                                .FirstOrDefault(s => s.Diagram == diagram);
                                connectorElement?.Show();
                            }
                        }

                        // so do generalizations, as long as both classes are available
                        foreach (Generalization generalization in modelClass.Store.ElementDirectory.AllElements.OfType <Generalization>().Where(g => g.Superclass == modelClass || g.Subclass == modelClass))
                        {
                            ModelClass other = generalization.Superclass == modelClass
                                              ? generalization.Subclass
                                              : generalization.Superclass;

                            // should never happen
                            if (other == null)
                            {
                                continue;
                            }

                            ShapeElement shapeElement = PresentationViewsSubject.GetPresentation(other)
                                                        .OfType <ShapeElement>()
                                                        .FirstOrDefault(s => s.Diagram == diagram);

                            if (shapeElement != null && shapeElement.IsVisible)
                            {
                                ShapeElement connectorElement = PresentationViewsSubject.GetPresentation(generalization)
                                                                .OfType <GeneralizationConnector>()
                                                                .FirstOrDefault(s => s.Diagram == diagram);
                                connectorElement?.Show();
                            }
                        }

                        FixUpAllDiagrams.FixUp(diagram, modelClass.ModelRoot, modelClass);

                        break;

                    case ModelEnum modelEnum:
                        // user selected an enum. Find it in the current diagram, center it and make it visible
                        modelEnum.LocateInDiagram(true);

                        // then fix up the compartment since it might need it
                        ModelElement[] enumElements = { modelEnum };
                        CompartmentItemAddRule.UpdateCompartments(enumElements, typeof(EnumShape), "ValuesCompartment", false);
                        FixUpAllDiagrams.FixUp(diagram, modelEnum.ModelRoot, modelEnum);

                        break;
                    }

                    t.Commit();
                }
            }
        }
Example #24
0
        private ModelClass ProcessClass([NotNull] ClassDeclarationSyntax classDecl, NamespaceDeclarationSyntax namespaceDecl = null)
        {
            ModelClass result = null;

            if (classDecl == null)
            {
                throw new ArgumentNullException(nameof(classDecl));
            }

            ModelRoot modelRoot = Store.ModelRoot();
            string    className = classDecl.Identifier.Text;

            if (namespaceDecl == null && classDecl.Parent is NamespaceDeclarationSyntax classDeclParent)
            {
                namespaceDecl = classDeclParent;
            }

            if (Store.Get <ModelEnum>().Any(c => c.Name == className))
            {
                ErrorDisplay.Show($"'{className}' already exists in model as an Enum.");

                // ReSharper disable once ExpressionIsAlwaysNull
                return(result);
            }

            if (classDecl.TypeParameterList != null)
            {
                ErrorDisplay.Show($"Can't add generic class '{className}'.");

                // ReSharper disable once ExpressionIsAlwaysNull
                return(result);
            }

            Transaction tx = Store.TransactionManager.CurrentTransaction == null
                             ? Store.TransactionManager.BeginTransaction()
                             : null;

            List <string> customInterfaces = new List <string>();

            try
            {
                ModelClass superClass = null;
                result = Store.Get <ModelClass>().FirstOrDefault(c => c.Name == className);

                // Base classes and interfaces
                // Check these first. If we need to add new models, we want the base class already in the store
                if (classDecl.BaseList != null)
                {
                    foreach (BaseTypeSyntax type in classDecl.BaseList.Types)
                    {
                        string baseName = type.ToString();

                        // Do we know this is an interface?
                        if (KnownInterfaces.Contains(baseName) || superClass != null || result?.Superclass != null)
                        {
                            customInterfaces.Add(baseName);
                            if (!KnownInterfaces.Contains(baseName))
                            {
                                KnownInterfaces.Add(baseName);
                            }

                            continue;
                        }

                        // is it inheritance or an interface?
                        superClass = modelRoot.Classes.FirstOrDefault(c => c.Name == baseName);

                        // if it's not in the model, we just don't know. Ask the user
                        if (superClass == null && (KnownClasses.Contains(baseName) || QuestionDisplay.Show($"For class {className}, is {baseName} the base class?") == true))
                        {
                            superClass = new ModelClass(Store, new PropertyAssignment(ModelClass.NameDomainPropertyId, baseName));
                            modelRoot.Classes.Add(superClass);
                        }
                        else
                        {
                            customInterfaces.Add(baseName);
                            KnownInterfaces.Add(baseName);
                        }
                    }
                }

                if (result == null)
                {
                    result = new ModelClass(Store, new PropertyAssignment(ModelClass.NameDomainPropertyId, className))
                    {
                        Namespace  = namespaceDecl?.Name?.ToString() ?? modelRoot.Namespace,
                        IsAbstract = classDecl.DescendantNodes().Any(n => n.Kind() == SyntaxKind.AbstractKeyword)
                    };

                    modelRoot.Classes.Add(result);
                }

                if (superClass != null)
                {
                    result.Superclass = superClass;
                }

                if (result.CustomInterfaces != null)
                {
                    customInterfaces.AddRange(result.CustomInterfaces
                                              .Split(',')
                                              .Where(i => !String.IsNullOrEmpty(i))
                                              .Select(i => i.Trim()));
                }

                if (customInterfaces.Contains("INotifyPropertyChanged"))
                {
                    result.ImplementNotify = true;
                    customInterfaces.Remove("INotifyPropertyChanged");
                }

                if (result.Superclass != null && customInterfaces.Contains(result.Superclass.Name))
                {
                    customInterfaces.Remove(result.Superclass.Name);
                }

                result.CustomInterfaces = customInterfaces.Any()
                                         ? String.Join(",", customInterfaces.Distinct())
                                         : null;


                XMLDocumentation xmlDocumentation = new XMLDocumentation(classDecl);
                result.Summary     = xmlDocumentation.Summary;
                result.Description = xmlDocumentation.Description;
            }
            catch
            {
                tx = null;

                throw;
            }
            finally
            {
                tx?.Commit();
            }

            return(result);
        }
Example #25
0
 /// <summary>
 ///    Method to set IsDatabaseSchemaTracking to false so that this instance of this tracking property is not
 ///    storage-based.
 /// </summary>
 /// <param name="element">
 ///    The element on which to reset the property
 ///    value.
 /// </param>
 internal void PreResetValue(ModelClass element) =>
 // Force the IsDatabaseSchemaTracking property to false so that the value
 // of the DatabaseSchema property is retrieved from storage.
 element.isDatabaseSchemaTrackingPropertyStorage = false;
Example #26
0
 internal void MoveAttribute(ModelAttribute attribute, ModelClass destination)
 {
     MergeDisconnect(attribute);
     destination.MergeRelate(attribute, null);
 }
        public override void ElementPropertyChanged(ElementPropertyChangedEventArgs e)
        {
            base.ElementPropertyChanged(e);

            ModelClass element = (ModelClass)e.ModelElement;

            if (element.IsDeleted)
            {
                return;
            }

            Store       store   = element.Store;
            Transaction current = store.TransactionManager.CurrentTransaction;

            if (current.IsSerializing || ModelRoot.BatchUpdating)
            {
                return;
            }

            if (Equals(e.NewValue, e.OldValue))
            {
                return;
            }

            List <string> errorMessages = EFCoreValidator.GetErrors(element).ToList();

            switch (e.DomainProperty.Name)
            {
            case "DbSetName":
            {
                string newDbSetName = (string)e.NewValue;

                if (element.IsDependentType)
                {
                    if (!string.IsNullOrEmpty(newDbSetName))
                    {
                        element.DbSetName = string.Empty;
                    }
                }
                else
                {
                    if (string.IsNullOrEmpty(newDbSetName))
                    {
                        element.DbSetName = MakeDefaultName(element.Name);
                    }

                    if (current.Name.ToLowerInvariant() != "paste" &&
                        (string.IsNullOrWhiteSpace(newDbSetName) || !CodeGenerator.IsValidLanguageIndependentIdentifier(newDbSetName)))
                    {
                        errorMessages.Add($"DbSet name '{newDbSetName}' isn't a valid .NET identifier.");
                    }
                    else if (store.GetAll <ModelClass>()
                             .Except(new[] { element })
                             .Any(x => x.DbSetName == newDbSetName))
                    {
                        errorMessages.Add($"DbSet name '{newDbSetName}' already in use");
                    }
                }

                break;
            }

            case "ImplementNotify":
            {
                bool newImplementNotify = (bool)e.NewValue;

                if (newImplementNotify)
                {
                    List <string> nameList = element.Attributes.Where(x => x.AutoProperty).Select(x => x.Name).ToList();
                    if (nameList.Any())
                    {
                        string names = nameList.Count > 1
                                       ? string.Join(", ", nameList.Take(nameList.Count - 1)) + " and " + nameList.Last()
                                       : nameList.First();

                        string verb = nameList.Count > 1
                                      ? "is an autoproperty"
                                      : "are autoproperties";

                        WarningDisplay.Show($"{names} {verb}, so will not participate in INotifyPropertyChanged messages");
                    }
                }

                PresentationHelper.UpdateClassDisplay(element);

                break;
            }

            case "IsAbstract":
            {
                bool newIsAbstract = (bool)e.NewValue;

                if (newIsAbstract && element.IsDependentType)
                {
                    errorMessages.Add($"Can't make {element.Name} abstract since it's a dependent type");

                    break;
                }

                PresentationHelper.UpdateClassDisplay(element);

                break;
            }

            case "IsDependentType":
            {
                bool newIsDependentType = (bool)e.NewValue;

                if (newIsDependentType)
                {
                    if (element.IsAbstract)
                    {
                        errorMessages.Add($"Can't make {element.Name} a dependent class since it's abstract");

                        break;
                    }

                    // dependent type can't be source in an association
                    if (store.GetAll <UnidirectionalAssociation>()
                        .Any(a => a.Source == element))
                    {
                        errorMessages.Add($"Can't make {element.Name} a dependent class since it references other classes");

                        break;
                    }

                    if (store.GetAll <BidirectionalAssociation>()
                        .Any(a => a.Source == element || a.Target == element))
                    {
                        errorMessages.Add($"Can't make {element.Name} a dependent class since it's in a bidirectional association");

                        break;
                    }

                    if (store.GetAll <Association>()
                        .Any(a => a.Target == element && a.TargetMultiplicity == Multiplicity.ZeroMany))
                    {
                        errorMessages.Add($"Can't make {element.Name} a dependent class since it's the target of a 0..* association");

                        break;
                    }

                    foreach (ModelAttribute modelAttribute in element.AllAttributes.Where(a => a.IsIdentity))
                    {
                        modelAttribute.IsIdentity = false;
                    }

                    foreach (UnidirectionalAssociation association in Association.GetLinksToTargets(element).OfType <UnidirectionalAssociation>())
                    {
                        if (association.SourceMultiplicity == Multiplicity.ZeroMany)
                        {
                            association.SourceMultiplicity = Multiplicity.ZeroOne;
                        }

                        if (association.TargetMultiplicity == Multiplicity.ZeroMany)
                        {
                            association.TargetMultiplicity = Multiplicity.ZeroOne;
                        }

                        association.TargetRole = EndpointRole.Dependent;
                    }

                    element.TableName = string.Empty;
                    element.DbSetName = string.Empty;
                }
                else
                {
                    element.DbSetName = MakeDefaultName(element.Name);
                    element.TableName = MakeDefaultName(element.Name);
                }

                PresentationHelper.UpdateClassDisplay(element);

                break;
            }

            case "Name":
            {
                string newName = (string)e.NewValue;

                if (current.Name.ToLowerInvariant() != "paste" &&
                    (string.IsNullOrWhiteSpace(newName) || !CodeGenerator.IsValidLanguageIndependentIdentifier(newName)))
                {
                    errorMessages.Add($"Class name '{newName}' isn't a valid .NET identifier.");
                }

                else if (store.ElementDirectory
                         .AllElements
                         .OfType <ModelClass>()
                         .Except(new[] { element })
                         .Any(x => x.Name == newName))
                {
                    errorMessages.Add($"Class name '{newName}' already in use by another class");
                }

                else if (store.ElementDirectory
                         .AllElements
                         .OfType <ModelEnum>()
                         .Any(x => x.Name == newName))
                {
                    errorMessages.Add($"Class name '{newName}' already in use by an enum");
                }

                else if (!string.IsNullOrEmpty((string)e.OldValue))
                {
                    string oldDefaultName = MakeDefaultName((string)e.OldValue);
                    string newDefaultName = MakeDefaultName(newName);

                    if (element.DbSetName == oldDefaultName)
                    {
                        element.DbSetName = newDefaultName;
                    }

                    if (element.TableName == oldDefaultName)
                    {
                        element.TableName = newDefaultName;
                    }
                }

                break;
            }

            case "Namespace":
            {
                string newNamespace = (string)e.NewValue;

                if (current.Name.ToLowerInvariant() != "paste")
                {
                    errorMessages.Add(CommonRules.ValidateNamespace(newNamespace, CodeGenerator.IsValidLanguageIndependentIdentifier));
                }

                break;
            }

            case "TableName":
            {
                string newTableName = (string)e.NewValue;

                if (element.IsDependentType)
                {
                    if (!string.IsNullOrEmpty(newTableName))
                    {
                        element.TableName = string.Empty;
                    }
                }
                else
                {
                    if (string.IsNullOrEmpty(newTableName))
                    {
                        element.TableName = MakeDefaultName(element.Name);
                    }

                    if (store.GetAll <ModelClass>()
                        .Except(new[] { element })
                        .Any(x => x.TableName == newTableName))
                    {
                        errorMessages.Add($"Table name '{newTableName}' already in use");
                    }
                }

                break;
            }
            }

            errorMessages = errorMessages.Where(m => m != null).ToList();

            if (errorMessages.Any())
            {
                current.Rollback();
                ErrorDisplay.Show(store, string.Join("\n", errorMessages));
            }
        }
Example #28
0
 /// <summary>
 ///    Method to set IsDefaultConstructorVisibilityTracking to false so that this instance of this tracking property is not
 ///    storage-based.
 /// </summary>
 /// <param name="element">
 ///    The element on which to reset the property value.
 /// </param>
 internal void PreResetValue(ModelClass element) =>
 // Force the IsDefaultConstructorVisibilityTracking property to false so that the value
 // of the DefaultConstructorVisibility property is retrieved from storage.
 element.isDefaultConstructorVisibilityTrackingPropertyStorage = false;
Example #29
0
        public static void RemoveHiddenProperties(PropertyDescriptorCollection propertyDescriptors, ModelClass element)
        {
            //ModelRoot modelRoot = element.ModelRoot;
            //for (int index = 0; index < propertyDescriptors.Count; index++)
            //{
            //   bool shouldRemove = false;
            //   switch (propertyDescriptors[index].Name)
            //   {
            //   }

            //   if (shouldRemove)
            //      propertyDescriptors.Remove(propertyDescriptors[index--]);
            //}
        }
Example #30
0
 /// <summary>Performs the reset operation for the IsNamespaceTracking property for a model element.</summary>
 /// <param name="element">The model element that has the property to reset.</param>
 internal void ResetValue(ModelClass element)
 {
     element.isNamespaceTrackingPropertyStorage = string.IsNullOrWhiteSpace(element.namespaceStorage);
 }