|
@@ -88,145 +88,145 @@ namespace InABox.Core
|
|
|
.Select(x => new SubObject(objectType, x.Item1, x.Item2)));
|
|
|
}
|
|
|
|
|
|
- private static void RegisterProperties(Type master, Type type, string prefix, StandardProperty? parent)
|
|
|
+ public static void Clear()
|
|
|
+ {
|
|
|
+ _properties = new ConcurrentDictionary<Type, ImmutableDictionary<string, IProperty>>();
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void RegisterProperties(Type master, Type type, string prefix, StandardProperty? parent, Dictionary<string, IProperty> newProperties)
|
|
|
{
|
|
|
try
|
|
|
{
|
|
|
- var classname = master.EntityName();
|
|
|
var properties = CoreUtils.PropertyList(
|
|
|
type,
|
|
|
x => !x.PropertyType.IsInterface &&
|
|
|
- x.GetGetMethod()?.IsPublic == true &&
|
|
|
- (x.DeclaringType.IsSubclassOf(typeof(BaseObject))
|
|
|
+ (x.DeclaringType.IsSubclassOf(typeof(BaseObject))
|
|
|
|| x.DeclaringType.IsSubclassOf(typeof(BaseEditor)))
|
|
|
);
|
|
|
- var classProps = _properties.GetValueOrDefault(master);
|
|
|
|
|
|
var subObjects = new List<Tuple<Type, string>>();
|
|
|
- var newProperties = new List<IProperty>();
|
|
|
|
|
|
foreach (var prop in properties)
|
|
|
{
|
|
|
- var name = prefix.IsNullOrWhiteSpace() ? prop.Name : $"{prefix}.{prop.Name}";
|
|
|
- var p = classProps?.GetValueOrDefault(name);
|
|
|
- if (p == null && !prop.GetAccessors(true)[0].IsStatic)
|
|
|
+ var name = prefix + prop.Name;
|
|
|
+ if (newProperties.ContainsKey(name)) continue;
|
|
|
+
|
|
|
+ var getMethod = prop.GetGetMethod();
|
|
|
+ if (getMethod is null || !getMethod.IsPublic || getMethod.IsStatic) continue;
|
|
|
+
|
|
|
+ BaseEditor? editor;
|
|
|
+ if (parent != null && parent.HasEditor && parent.Editor is NullEditor)
|
|
|
+ {
|
|
|
+ editor = parent.Editor;
|
|
|
+ }
|
|
|
+ else
|
|
|
{
|
|
|
- BaseEditor? editor;
|
|
|
- if (parent != null && parent.HasEditor && parent.Editor is NullEditor)
|
|
|
+ editor = prop.GetEditor();
|
|
|
+ }
|
|
|
+
|
|
|
+ var captionAttr = prop.GetCustomAttribute<Caption>();
|
|
|
+ var subCaption = captionAttr != null ? captionAttr.Text : prop.Name;
|
|
|
+ var path = captionAttr == null || captionAttr.IncludePath; // If no caption attribute, we should always include the path
|
|
|
+ var caption = parent?.Caption ?? ""; // We default to the parent caption if subCaption doesn't exist
|
|
|
+ if (!string.IsNullOrWhiteSpace(subCaption))
|
|
|
+ {
|
|
|
+ if (!string.IsNullOrWhiteSpace(caption) && path)
|
|
|
{
|
|
|
- editor = parent.Editor;
|
|
|
+ caption = $"{caption} {subCaption}";
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- editor = prop.GetEditor();
|
|
|
- }
|
|
|
-
|
|
|
- var captionAttr = prop.GetCustomAttribute<Caption>();
|
|
|
- var subCaption = captionAttr != null ? captionAttr.Text : prop.Name;
|
|
|
- var path = captionAttr == null || captionAttr.IncludePath; // If no caption attribute, we should always include the path
|
|
|
- var caption = parent?.Caption ?? ""; // We default to the parent caption if subCaption doesn't exist
|
|
|
- if (!string.IsNullOrWhiteSpace(subCaption))
|
|
|
- {
|
|
|
- if (!string.IsNullOrWhiteSpace(caption) && path)
|
|
|
- {
|
|
|
- caption = $"{caption} {subCaption}";
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- caption = subCaption;
|
|
|
- }
|
|
|
+ caption = subCaption;
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- // Once the parent page has been found, this property is cemented to that page - it cannot change page to its parent
|
|
|
- // The same goes for sequence
|
|
|
- var page = parent?.Page;
|
|
|
- var sequence = parent?.Sequence;
|
|
|
- if (string.IsNullOrWhiteSpace(page))
|
|
|
+ // Once the parent page has been found, this property is cemented to that page - it cannot change page to its parent
|
|
|
+ // The same goes for sequence
|
|
|
+ var page = parent?.Page;
|
|
|
+ var sequence = parent?.Sequence;
|
|
|
+ if (string.IsNullOrWhiteSpace(page))
|
|
|
+ {
|
|
|
+ var sequenceAttribute = prop.GetCustomAttribute<EditorSequence>();
|
|
|
+ if (sequenceAttribute != null)
|
|
|
{
|
|
|
- var sequenceAttribute = prop.GetCustomAttribute<EditorSequence>();
|
|
|
- if (sequenceAttribute != null)
|
|
|
- {
|
|
|
- page = sequenceAttribute.Page;
|
|
|
- sequence = sequenceAttribute.Sequence;
|
|
|
- }
|
|
|
+ page = sequenceAttribute.Page;
|
|
|
+ sequence = sequenceAttribute.Sequence;
|
|
|
}
|
|
|
- editor = editor?.Clone() as BaseEditor;
|
|
|
- if (editor != null)
|
|
|
- {
|
|
|
- editor.Page = page;
|
|
|
- editor.Caption = caption;
|
|
|
- editor.EditorSequence = (int)(sequence ?? 999);
|
|
|
+ }
|
|
|
+ editor = editor?.Clone() as BaseEditor;
|
|
|
+ if (editor != null)
|
|
|
+ {
|
|
|
+ editor.Page = page;
|
|
|
+ editor.Caption = caption;
|
|
|
+ editor.EditorSequence = (int)(sequence ?? 999);
|
|
|
|
|
|
- editor.Security = prop.GetCustomAttributes<SecurityAttribute>().ToArray();
|
|
|
- }
|
|
|
+ editor.Security = prop.GetCustomAttributes<SecurityAttribute>().ToArray();
|
|
|
+ }
|
|
|
|
|
|
- bool required = false;
|
|
|
- if (parent == null || parent.Required)
|
|
|
- {
|
|
|
- required = prop.GetCustomAttribute<RequiredColumnAttribute>() != null;
|
|
|
- }
|
|
|
+ bool required = false;
|
|
|
+ if (parent == null || parent.Required)
|
|
|
+ {
|
|
|
+ required = prop.GetCustomAttribute<RequiredColumnAttribute>() != null;
|
|
|
+ }
|
|
|
|
|
|
- LoggablePropertyAttribute? loggable = null;
|
|
|
- if (parent == null || parent.Loggable != null)
|
|
|
- {
|
|
|
- loggable = prop.GetCustomAttribute<LoggablePropertyAttribute>();
|
|
|
- }
|
|
|
+ LoggablePropertyAttribute? loggable = null;
|
|
|
+ if (parent == null || parent.Loggable != null)
|
|
|
+ {
|
|
|
+ loggable = prop.GetCustomAttribute<LoggablePropertyAttribute>();
|
|
|
+ }
|
|
|
|
|
|
- var newProperty = new StandardProperty
|
|
|
- {
|
|
|
- _class = master,
|
|
|
- //Class = classname,
|
|
|
- Name = name,
|
|
|
- PropertyType = prop.PropertyType,
|
|
|
- Editor = editor ?? new NullEditor(),
|
|
|
- HasEditor = editor != null,
|
|
|
- Caption = caption,
|
|
|
- Sequence = sequence ?? 999,
|
|
|
- Page = page ?? "",
|
|
|
- Required = required,
|
|
|
- Loggable = loggable,
|
|
|
- Parent = parent,
|
|
|
- Property = prop
|
|
|
- };
|
|
|
-
|
|
|
- var parentWithEditable = newProperty.GetOuterParent(x =>
|
|
|
- x is StandardProperty st
|
|
|
- && st.Property.GetCustomAttribute<EditableAttribute>() != null);
|
|
|
- if(parentWithEditable != null)
|
|
|
- {
|
|
|
- var attr = (parentWithEditable as StandardProperty)!.Property.GetCustomAttribute<EditableAttribute>()!;
|
|
|
- newProperty.Editor.Editable = newProperty.Editor.Editable.Combine(attr.Editable);
|
|
|
- }
|
|
|
- else if(prop.GetCustomAttribute<EditableAttribute>() is EditableAttribute attr)
|
|
|
- {
|
|
|
- newProperty.Editor.Editable = newProperty.Editor.Editable.Combine(attr.Editable);
|
|
|
- }
|
|
|
+ var newProperty = new StandardProperty
|
|
|
+ {
|
|
|
+ _class = master,
|
|
|
+ Name = name,
|
|
|
+ PropertyType = prop.PropertyType,
|
|
|
+ Editor = editor ?? new NullEditor(),
|
|
|
+ HasEditor = editor != null,
|
|
|
+ Caption = caption,
|
|
|
+ Sequence = sequence ?? 999,
|
|
|
+ Page = page ?? "",
|
|
|
+ Required = required,
|
|
|
+ Loggable = loggable,
|
|
|
+ Parent = parent,
|
|
|
+ Property = prop
|
|
|
+ };
|
|
|
+
|
|
|
+ var parentWithEditable = newProperty.GetOuterParent(x =>
|
|
|
+ x is StandardProperty st
|
|
|
+ && st.Property.GetCustomAttribute<EditableAttribute>() != null);
|
|
|
+ if(parentWithEditable != null)
|
|
|
+ {
|
|
|
+ var attr = (parentWithEditable as StandardProperty)!.Property.GetCustomAttribute<EditableAttribute>()!;
|
|
|
+ newProperty.Editor.Editable = newProperty.Editor.Editable.Combine(attr.Editable);
|
|
|
+ }
|
|
|
+ else if(prop.GetCustomAttribute<EditableAttribute>() is EditableAttribute attr)
|
|
|
+ {
|
|
|
+ newProperty.Editor.Editable = newProperty.Editor.Editable.Combine(attr.Editable);
|
|
|
+ }
|
|
|
|
|
|
- var isLink = typeof(IEntityLink).IsAssignableFrom(prop.PropertyType);
|
|
|
- var isEnclosedEntity = typeof(IEnclosedEntity).IsAssignableFrom(prop.PropertyType);
|
|
|
- var isBaseEditor = prop.PropertyType.Equals(typeof(BaseEditor)) ||
|
|
|
- prop.PropertyType.IsSubclassOf(typeof(BaseEditor));
|
|
|
- if ((isLink || isEnclosedEntity) && !isBaseEditor)
|
|
|
- {
|
|
|
- subObjects.Add(new Tuple<Type, string>(prop.PropertyType, prop.Name));
|
|
|
- }
|
|
|
+ var isLink = prop.PropertyType.HasInterface<IEntityLink>();
|
|
|
+ var isEnclosedEntity = prop.PropertyType.HasInterface<IEnclosedEntity>();
|
|
|
+ var isBaseEditor = prop.PropertyType.HasInterface<IBaseEditor>();
|
|
|
+ if ((isLink || isEnclosedEntity) && !isBaseEditor)
|
|
|
+ {
|
|
|
+ subObjects.Add(new Tuple<Type, string>(prop.PropertyType, prop.Name));
|
|
|
+ }
|
|
|
|
|
|
- if (isLink || isEnclosedEntity || isBaseEditor)
|
|
|
- {
|
|
|
- RegisterProperties(master, prop.PropertyType, name, newProperty);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- newProperties.Add(newProperty);
|
|
|
- }
|
|
|
+ if (isLink || isEnclosedEntity || isBaseEditor)
|
|
|
+ {
|
|
|
+ RegisterProperties(master, prop.PropertyType, name + ".", newProperty, newProperties);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ newProperties.Add(newProperty.Name, newProperty);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- RegisterProperties(master, newProperties);
|
|
|
RegisterSubObjects(type, subObjects);
|
|
|
|
|
|
- if (type.IsSubclassOf(typeof(BaseObject)))
|
|
|
- RegisterProperties(master, type.BaseType, prefix, parent);
|
|
|
+ // I don't actually think we need this, since PropertyList gives us properties of our parent.
|
|
|
+ //if (type.IsSubclassOf(typeof(BaseObject)) && type.BaseType != typeof(BaseObject))
|
|
|
+ // RegisterProperties(master, type.BaseType, prefix, parent, newProperties);
|
|
|
}
|
|
|
catch (Exception e)
|
|
|
{
|
|
@@ -236,7 +236,12 @@ namespace InABox.Core
|
|
|
|
|
|
private static void RegisterProperties(Type type)
|
|
|
{
|
|
|
- RegisterProperties(type, type, "", null);
|
|
|
+ var properties = new Dictionary<string, IProperty>();
|
|
|
+ RegisterProperties(type, type, "", null, properties);
|
|
|
+ if(properties.Count > 0)
|
|
|
+ {
|
|
|
+ RegisterProperties(type, properties.Values);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
public static object? DefaultValue(Type type)
|
|
@@ -318,6 +323,7 @@ namespace InABox.Core
|
|
|
{
|
|
|
return CheckProperties(type)?.Select(x => x.Value) ?? Enumerable.Empty<IProperty>();
|
|
|
}
|
|
|
+ public static IEnumerable<IProperty> Properties<T>() => Properties(typeof(T));
|
|
|
|
|
|
public static IProperty? Property(Type type, string name)
|
|
|
{
|