-
Notifications
You must be signed in to change notification settings - Fork 1.9k
/
AnnotationInspector.cs
159 lines (143 loc) · 7.5 KB
/
AnnotationInspector.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="AnnotationInspector.cs">
// Copyright (c) by respective owners including Yahoo!, Microsoft, and
// individual contributors. All rights reserved. Released under a BSD
// license as described in the file LICENSE.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using VW.Labels;
using VW.Serializer.Attributes;
using VW.Serializer.Intermediate;
namespace VW.Serializer
{
/// <summary>
/// Utility class analyzing compile-time <see cref="FeatureAttribute"/> annotation.
/// </summary>
public static class TypeInspector
{
/// <summary>
/// All properties are used as features.
/// </summary>
public static readonly ITypeInspector All;
/// <summary>
/// Only properties annotated using Feature attribute are considered.
/// </summary>
public static readonly ITypeInspector Default;
static TypeInspector()
{
All = new AnnotationInspectorAll();
Default = new AnnotationInspectorDefault();
}
private sealed class AnnotationInspectorDefault : ITypeInspector
{
public Schema CreateSchema(VowpalWabbitSettings settings, Type type)
{
return TypeInspector.CreateSchema(type,
featurePropertyPredicate: (_, attr) => attr != null,
labelPropertyPredicate: (_, attr) => attr != null);
}
}
private sealed class AnnotationInspectorAll : ITypeInspector
{
public Schema CreateSchema(VowpalWabbitSettings settings, Type type)
{
return TypeInspector.CreateSchema(type,
featurePropertyPredicate: (_, __) => true,
labelPropertyPredicate: (_, __) => true);
}
}
private static Schema CreateSchema(Type type,
Func<PropertyInfo, FeatureAttribute, bool> featurePropertyPredicate,
Func<PropertyInfo, LabelAttribute, bool> labelPropertyPredicate)
{
Contract.Requires(type != null);
Contract.Requires(featurePropertyPredicate != null);
Contract.Requires(labelPropertyPredicate != null);
var validExpressions = new Stack<Func<Expression,Expression>>();
// CODE example != null
validExpressions.Push(valueExpression => Expression.NotEqual(valueExpression, Expression.Constant(null)));
return CreateSchema(
null,
type,
null,
null,
null,
// CODE example
valueExpression => valueExpression,
validExpressions,
featurePropertyPredicate,
labelPropertyPredicate);
}
private static Schema CreateSchema(
FeatureExpression parent,
Type type,
string parentNamespace,
char? parentFeatureGroup,
bool? parentDictify,
Func<Expression, Expression> valueExpressionFactory,
Stack<Func<Expression, Expression>> valueValidExpressionFactories,
Func<PropertyInfo, FeatureAttribute, bool> featurePropertyPredicate,
Func<PropertyInfo, LabelAttribute, bool> labelPropertyPredicate)
{
var props = type.GetProperties(BindingFlags.Instance | BindingFlags.GetProperty | BindingFlags.Public);
var localFeatures = (from p in props
let declaredAttr = (FeatureAttribute)p.GetCustomAttributes(typeof(FeatureAttribute), true).FirstOrDefault()
where featurePropertyPredicate(p, declaredAttr)
let attr = declaredAttr ?? new FeatureAttribute()
select new FeatureExpression(
featureType: p.PropertyType,
name: attr.Name ?? p.Name,
// CODE example.Property
valueExpressionFactory: valueExpression => Expression.Property(valueExpressionFactory(valueExpression), p),
// @Reverse: make sure conditions are specified in the right order
valueValidExpressionFactories: valueValidExpressionFactories.Reverse().ToList(),
@namespace: attr.Namespace ?? parentNamespace,
featureGroup: attr.InternalFeatureGroup ?? parentFeatureGroup,
enumerize: attr.Enumerize,
variableName: p.Name,
order: attr.Order,
addAnchor: attr.AddAnchor,
stringProcessing: attr.StringProcessing,
dictify: attr.InternalDictify ?? parentDictify,
parent: parent)
).ToList();
var localLabels = from p in props
let declaredAttr = (LabelAttribute)p.GetCustomAttributes(typeof(LabelAttribute), true).FirstOrDefault()
where labelPropertyPredicate(p, declaredAttr) || typeof(ILabel).IsAssignableFrom(p.PropertyType)
let attr = declaredAttr ?? new LabelAttribute()
let labelType = p.PropertyType
where typeof(ILabel).IsAssignableFrom(labelType) || p.PropertyType == typeof(string)
select new LabelExpression
{
Name = p.Name,
LabelType = p.PropertyType,
// CODE example.Property
ValueExpressionFactory = valueExpression => Expression.Property(valueExpressionFactory(valueExpression), p),
// @Reverse: make sure conditions are specified in the right order
ValueValidExpressionFactories = valueValidExpressionFactories.Reverse().ToList()
};
// Recurse
var schemas = localFeatures
.Select(f =>
{
// CODE example.Prop1.Prop2 != null
valueValidExpressionFactories.Push(valueExpression => Expression.NotEqual(f.ValueExpressionFactory(valueExpression), Expression.Constant(null)));
var subSchema = CreateSchema(f, f.FeatureType, f.Namespace, f.FeatureGroup, f.Dictify, f.ValueExpressionFactory, valueValidExpressionFactories, featurePropertyPredicate, labelPropertyPredicate);
valueValidExpressionFactories.Pop();
return subSchema;
})
.ToList();
return new Schema
{
Features = localFeatures.Union(schemas.SelectMany(s => s.Features)).ToList(),
Label = localLabels.Union(schemas.Select(s => s.Label)).FirstOrDefault(l => l != null)
};
}
}
}