Exemplo n.º 1
0
        private static SemanticInfo ExtractSemanticInfo(Type modelType)
        {
            SemanticInfo semanticInfo = new SemanticInfo();

            // Built-in semantic type mapping
            string bareTypeName = modelType.Name.Split('`')[0]; // Type name without generic type parameters (if any)

            semanticInfo.MappedSemanticTypes.Add(SemanticMapping.GetQualifiedTypeName(bareTypeName));

            // Extract semantic info from SemanticEntity attributes on the Model Type.
            foreach (SemanticEntityAttribute attribute in modelType.GetCustomAttributes(true).Where(a => a is SemanticEntityAttribute))
            {
                semanticInfo.MappedSemanticTypes.Add(SemanticMapping.GetQualifiedTypeName(attribute.EntityName, attribute.Vocab));

                if (!attribute.Public || string.IsNullOrEmpty(attribute.Prefix))
                {
                    continue;
                }

                string prefix = attribute.Prefix;
                string registeredVocab;
                if (semanticInfo.PrefixMappings.TryGetValue(prefix, out registeredVocab))
                {
                    // Prefix mapping already exists; must match.
                    if (attribute.Vocab != registeredVocab)
                    {
                        throw new DxaException(
                                  string.Format("Attempt to use semantic prefix '{0}' for vocabulary '{1}', but is is already used for vocabulary '{2}",
                                                prefix, attribute.Vocab, registeredVocab)
                                  );
                    }
                }
                else
                {
                    semanticInfo.PrefixMappings.Add(prefix, attribute.Vocab);
                }

                semanticInfo.PublicSemanticTypes.Add(String.Format("{0}:{1}", prefix, attribute.EntityName));
            }

            // Extract semantic info from SemanticEntity attributes on the Model Type's properties
            foreach (MemberInfo memberInfo in modelType.GetMembers(BindingFlags.Public | BindingFlags.Instance))
            {
                foreach (SemanticPropertyAttribute attribute in memberInfo.GetCustomAttributes(true).Where(a => a is SemanticPropertyAttribute))
                {
                    if (string.IsNullOrEmpty(attribute.PropertyName))
                    {
                        // Skip properties without name.
                        continue;
                    }
                    string[] semanticPropertyNameParts = attribute.PropertyName.Split(':');
                    if (semanticPropertyNameParts.Length < 2)
                    {
                        // Skip property names without prefix.
                        continue;
                    }
                    string prefix = semanticPropertyNameParts[0];
                    if (!semanticInfo.PrefixMappings.ContainsKey(prefix))
                    {
                        // Skip property names with prefix which is not declared as public prefix on the type.
                        continue;
                    }

                    IList <string> semanticPropertyNames;
                    if (!semanticInfo.SemanticProperties.TryGetValue(memberInfo.Name, out semanticPropertyNames))
                    {
                        semanticPropertyNames = new List <string>();
                        semanticInfo.SemanticProperties.Add(memberInfo.Name, semanticPropertyNames);
                    }
                    semanticPropertyNames.Add(attribute.PropertyName);
                }
            }

            return(semanticInfo);
        }
Exemplo n.º 2
0
        private static SemanticInfo ExtractSemanticInfo(Type modelType)
        {
            SemanticInfo semanticInfo = new SemanticInfo();

            // Get model type name
            string modelTypeName = modelType.BareTypeName();

            // Built-in semantic type mapping
            semanticInfo.MappedSemanticTypes.Add(SemanticMapping.GetQualifiedTypeName(modelTypeName));

            // Extract semantic info from SemanticEntity attributes on the Model Type.
            foreach (SemanticEntityAttribute attribute in modelType.GetCustomAttributes <SemanticEntityAttribute>(inherit: true))
            {
                semanticInfo.MappedSemanticTypes.Add(SemanticMapping.GetQualifiedTypeName(attribute.EntityName, attribute.Vocab));

                string prefix = attribute.Prefix ?? string.Empty;
                if (attribute.Public && !String.IsNullOrEmpty(attribute.Prefix))
                {
                    string registeredVocab;
                    if (semanticInfo.PrefixMappings.TryGetValue(prefix, out registeredVocab))
                    {
                        // Prefix mapping already exists; must match.
                        if (attribute.Vocab != registeredVocab)
                        {
                            throw new DxaException(
                                      String.Format("Attempt to use semantic prefix '{0}' for vocabulary '{1}', but is is already used for vocabulary '{2}",
                                                    prefix, attribute.Vocab, registeredVocab)
                                      );
                        }
                    }
                    else
                    {
                        semanticInfo.PrefixMappings.Add(prefix, attribute.Vocab);
                    }
                    semanticInfo.PublicSemanticTypes.Add(String.Format("{0}:{1}", prefix, attribute.EntityName));
                }

                // There may be multiple Semantic Entity attributes for the same prefix. The first one will be used.
                if (semanticInfo.PrefixToSemanticTypeMap.ContainsKey(prefix))
                {
                    if (Log.IsDebugEnabled)
                    {
                        Log.Debug($"Type '{modelType.FullName}' has multiple SemanticEntity attributes for prefix '{prefix}'. Ignoring '{attribute.EntityName}'.");
                    }
                }
                else
                {
                    semanticInfo.PrefixToSemanticTypeMap.Add(prefix, new SemanticType(attribute.EntityName, attribute.Vocab));
                }
            }

            if (!semanticInfo.PrefixToSemanticTypeMap.ContainsKey(string.Empty))
            {
                // If there is no SemanticEntity attribute without prefix, we add an implicit one:
                semanticInfo.PrefixToSemanticTypeMap.Add(string.Empty, new SemanticType(modelTypeName, SemanticMapping.DefaultVocabulary));
            }

            string defaultPrefix;
            bool   mapAllProperties;
            SemanticDefaultsAttribute semanticDefaultsAttr = modelType.GetCustomAttribute <SemanticDefaultsAttribute>();

            if (semanticDefaultsAttr == null)
            {
                defaultPrefix    = string.Empty;
                mapAllProperties = true;
            }
            else
            {
                defaultPrefix    = semanticDefaultsAttr.Prefix;
                mapAllProperties = semanticDefaultsAttr.MapAllProperties;
            }

            foreach (PropertyInfo propertyInfo in modelType.GetProperties())
            {
                // Extract semantic info from SemanticEntity attributes on the Model Type's properties
                bool useImplicitMapping = mapAllProperties;

                List <SemanticPropertyAttribute> attributes = propertyInfo.GetCustomAttributes <SemanticPropertyAttribute>(inherit: true).ToList();

                // check if we should be ignoring this mapping completely
                if (attributes.Where(x => x.IgnoreMapping).Select(x => x).FirstOrDefault() != null)
                {
                    continue;
                }

                List <SemanticProperty> semanticProperties = new List <SemanticProperty>();
                foreach (SemanticPropertyAttribute attribute in attributes)
                {
                    if (string.IsNullOrEmpty(attribute.PropertyName))
                    {
                        continue;
                    }

                    // check for known property names
                    switch (attribute.PropertyName)
                    {
                    // To do : we need to make this more generic to collect all fields of a given type (e.g. [SemtanticProperty("_all", typeof(Keyword)])
                    case SemanticProperty.AllFields:
                        if (!typeof(IDictionary <string, string>).IsAssignableFrom(propertyInfo.PropertyType) &&
                            !typeof(IDictionary <string, KeywordModel>).IsAssignableFrom(propertyInfo.PropertyType))
                        {
                            throw new DxaException(
                                      $"Invalid semantics for property {modelType.Name}.{propertyInfo.Name}. Properties with [SemanticProperty(\"_all\")] annotation must be of type Dictionary<string, string> or Dictionary<string, KeywordModel>."
                                      );
                        }
                        break;

                    case SemanticProperty.Self:
                        Type elementType = GetElementType(propertyInfo.PropertyType);
                        if (!typeof(MediaItem).IsAssignableFrom(elementType) && !typeof(Link).IsAssignableFrom(elementType) && (elementType != typeof(string)) && (elementType != typeof(RichText)))
                        {
                            throw new DxaException(
                                      $"Invalid semantics for property {modelType.Name}.{propertyInfo.Name}. Properties with [SemanticProperty(\"_self\")] annotation must be of type MediaItem, Link, String or RichText.");
                        }
                        break;
                    }

                    string prefix = attribute.Prefix;
                    string name   = attribute.PropertyName;
                    if (prefix != null)
                    {
                        if (semanticInfo.PrefixMappings.ContainsKey(prefix))
                        {
                            IList <string> semanticPropertyNames;
                            if (!semanticInfo.SemanticProperties.TryGetValue(propertyInfo.Name, out semanticPropertyNames))
                            {
                                semanticPropertyNames = new List <string>();
                                semanticInfo.SemanticProperties.Add(propertyInfo.Name, semanticPropertyNames);
                            }
                            semanticPropertyNames.Add(attribute.PropertyName);
                        }
                    }
                    else
                    {
                        // Skip property names without prefix.
                        prefix             = defaultPrefix;
                        useImplicitMapping = false;
                    }

                    SemanticType semanticType;
                    if (!semanticInfo.PrefixToSemanticTypeMap.TryGetValue(prefix, out semanticType))
                    {
                        throw new DxaException($"Use of undeclared prefix '{prefix}' in property '{propertyInfo.Name}' in type '{modelType.FullName}'.");
                    }
                    semanticProperties.Add(new SemanticProperty(prefix, name, semanticType));
                }

                if (useImplicitMapping)
                {
                    SemanticType semanticType;
                    if (!semanticInfo.PrefixToSemanticTypeMap.TryGetValue(defaultPrefix, out semanticType))
                    {
                        throw new DxaException($"Use of undeclared prefix '{defaultPrefix}' in property '{propertyInfo.Name}' in type '{modelType.FullName}'.");
                    }
                    SemanticProperty implicitSemanticProperty = new SemanticProperty(
                        String.Empty, GetDefaultSemanticPropertyName(propertyInfo),
                        semanticType
                        );

                    semanticProperties.Add(implicitSemanticProperty);
                }

                if (semanticProperties.Count > 0)
                {
                    if (semanticInfo.PropertySemantics.ContainsKey(propertyInfo.Name))
                    {
                        // Properties with same name can exist is a property is reintroduced with a different signature in a subclass.
                        if (Log.IsDebugEnabled)
                        {
                            Log.Debug("Property with name '{0}' is declared multiple times in type {1}.", propertyInfo.Name, modelType.FullName);
                        }
                    }
                    else
                    {
                        semanticInfo.PropertySemantics.Add(propertyInfo.Name, semanticProperties);
                    }
                }
            }

            return(semanticInfo);
        }