protected virtual Dictionary<string, KeyValuePair<string, string>> GetEntityDataFromType(Type type) { bool addedDefaults = false; Dictionary<string, KeyValuePair<string, string>> res = new Dictionary<string, KeyValuePair<string, string>>(); foreach (var attr in type.GetCustomAttributes()) { if (attr is SemanticEntityAttribute) { var semantics = (SemanticEntityAttribute)attr; // we can only support mapping to a single semantic entity, the derived type is set first, so that is what we use if (!res.ContainsKey(semantics.Prefix)) { res.Add(semantics.Prefix, new KeyValuePair<string, string>(semantics.Vocab, semantics.EntityName)); } } if (attr is SemanticDefaultsAttribute) { var semantics = (SemanticDefaultsAttribute)attr; res.Add(semantics.Prefix, new KeyValuePair<string,string>(semantics.Vocab, String.Empty)); addedDefaults = true; } } //Add default vocab if none was specified on entity if (!addedDefaults) { var semantics = new SemanticDefaultsAttribute(); res.Add(semantics.Prefix, new KeyValuePair<string, string>(semantics.Vocab, String.Empty)); } return res; }
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); }