Entitas  0.35.0
Entitas is a super fast Entity Component System (ECS) Framework specifically made for C# and Unity
ComponentExtensionsGenerator.cs
1 using System.Collections.Generic;
2 using System.Linq;
4 
5 namespace Entitas.CodeGenerator {
6 
8 
9  public CodeGenFile[] Generate(ComponentInfo[] componentInfos) {
10  var generatorName = GetType().FullName;
11  return componentInfos
12  .Where(info => info.generateMethods)
13  .Select(info => new CodeGenFile(
14  info.fullTypeName + "GeneratedExtension",
15  generateComponentExtension(info),
16  generatorName
17  )).ToArray();
18  }
19 
20  static string generateComponentExtension(ComponentInfo componentInfo) {
21  var code = addNamespace();
22  code += addEntityMethods(componentInfo);
23  if(componentInfo.isSingleEntity) {
24  code += addPoolMethods(componentInfo);
25  }
26 
27  if(componentInfo.generateComponent) {
28  // Add default matcher
29  code += addMatcher(componentInfo, true);
30  code += closeNamespace();
31  // Add custom matchers
32  code += addMatcher(componentInfo);
33  return addUsings("Entitas")
34  + generateComponent(componentInfo)
35  + code;
36  }
37 
38  code += addMatcher(componentInfo, true);
39  code += closeNamespace();
40 
41  var hasCustomPools = componentInfo.pools.Length > 1 || !componentInfo.pools[0].IsDefaultPoolName();
42  if(hasCustomPools) {
43  code += addMatcher(componentInfo);
44  code = addUsings("Entitas") + code;
45  }
46 
47  return code;
48  }
49 
50  static string generateComponent(ComponentInfo componentInfo) {
51  const string hideInBlueprintInspector = "[Entitas.Serialization.Blueprints.HideInBlueprintInspectorAttribute]\n";
52  const string componentFormat = @"public class {0} : IComponent {{
53 
54  public {1} {2};
55 }}
56 
57 ";
58  var memberInfo = componentInfo.memberInfos[0];
59  var code = string.Format(componentFormat, componentInfo.fullTypeName, memberInfo.type, memberInfo.name);
60  return componentInfo.hideInBlueprintInspector
61  ? hideInBlueprintInspector + code
62  : code;
63  }
64 
65  static string addUsings(params string[] usings) {
66  return string.Join("\n", usings
67  .Select(name => "using " + name + ";")
68  .ToArray()) + "\n\n";
69  }
70 
71  static string addNamespace() {
72  return "namespace Entitas {\n";
73  }
74 
75  static string closeNamespace() {
76  return "}\n";
77  }
78 
79  /*
80  *
81  * ENTITY METHODS
82  *
83  */
84 
85  static string addEntityMethods(ComponentInfo componentInfo) {
86  return addEntityClassHeader()
87  + addGetMethods(componentInfo)
88  + addHasMethods(componentInfo)
89  + addAddMethods(componentInfo)
90  + addReplaceMethods(componentInfo)
91  + addRemoveMethods(componentInfo)
92  + addCloseClass();
93  }
94 
95  static string addEntityClassHeader() {
96  return "\n public partial class Entity {\n";
97  }
98 
99  static string addGetMethods(ComponentInfo componentInfo) {
100  var getMethod = componentInfo.isSingletonComponent
101  ? "\n static readonly $Type $nameComponent = new $Type();\n"
102  : "\n public $Type $name { get { return ($Type)GetComponent($Ids.$Name); } }\n";
103 
104  return buildString(componentInfo, getMethod);
105  }
106 
107  static string addHasMethods(ComponentInfo componentInfo) {
108  var hasMethod = componentInfo.isSingletonComponent ? @"
109  public bool $prefix$Name {
110  get { return HasComponent($Ids.$Name); }
111  set {
112  if(value != $prefix$Name) {
113  if(value) {
114  AddComponent($Ids.$Name, $nameComponent);
115  } else {
116  RemoveComponent($Ids.$Name);
117  }
118  }
119  }
120  }
121 
122  public Entity $Prefix$Name(bool value) {
123  $prefix$Name = value;
124  return this;
125  }
126 " : @" public bool has$Name { get { return HasComponent($Ids.$Name); } }
127 ";
128  return buildString(componentInfo, hasMethod);
129  }
130 
131  static string addAddMethods(ComponentInfo componentInfo) {
132  return componentInfo.isSingletonComponent ? string.Empty : buildString(componentInfo, @"
133  public Entity Add$Name($typedArgs) {
134  var component = CreateComponent<$Type>($Ids.$Name);
135 $assign
136  return AddComponent($Ids.$Name, component);
137  }
138 ");
139  }
140 
141  static string addReplaceMethods(ComponentInfo componentInfo) {
142  return componentInfo.isSingletonComponent ? string.Empty : buildString(componentInfo, @"
143  public Entity Replace$Name($typedArgs) {
144  var component = CreateComponent<$Type>($Ids.$Name);
145 $assign
146  ReplaceComponent($Ids.$Name, component);
147  return this;
148  }
149 ");
150  }
151 
152  static string addRemoveMethods(ComponentInfo componentInfo) {
153  return componentInfo.isSingletonComponent ? string.Empty : buildString(componentInfo, @"
154  public Entity Remove$Name() {
155  return RemoveComponent($Ids.$Name);
156  }
157 ");
158  }
159 
160  /*
161  *
162  * POOL METHODS
163  *
164  */
165 
166  static string addPoolMethods(ComponentInfo componentInfo) {
167  return addPoolClassHeader()
168  + addPoolGetMethods(componentInfo)
169  + addPoolHasMethods(componentInfo)
170  + addPoolAddMethods(componentInfo)
171  + addPoolReplaceMethods(componentInfo)
172  + addPoolRemoveMethods(componentInfo)
173  + addCloseClass();
174  }
175 
176  static string addPoolClassHeader() {
177  return "\n public partial class Pool {\n";
178  }
179 
180  static string addPoolGetMethods(ComponentInfo componentInfo) {
181  var getMehod = componentInfo.isSingletonComponent ? @"
182  public Entity $nameEntity { get { return GetGroup($TagMatcher.$Name).GetSingleEntity(); } }
183 " : @"
184  public Entity $nameEntity { get { return GetGroup($TagMatcher.$Name).GetSingleEntity(); } }
185  public $Type $name { get { return $nameEntity.$name; } }
186 ";
187  return buildString(componentInfo, getMehod);
188  }
189 
190  static string addPoolHasMethods(ComponentInfo componentInfo) {
191  var hasMethod = componentInfo.isSingletonComponent ? @"
192  public bool $prefix$Name {
193  get { return $nameEntity != null; }
194  set {
195  var entity = $nameEntity;
196  if(value != (entity != null)) {
197  if(value) {
198  CreateEntity().$prefix$Name = true;
199  } else {
200  DestroyEntity(entity);
201  }
202  }
203  }
204  }
205 " : @" public bool has$Name { get { return $nameEntity != null; } }
206 ";
207  return buildString(componentInfo, hasMethod);
208  }
209 
210  static object addPoolAddMethods(ComponentInfo componentInfo) {
211  return componentInfo.isSingletonComponent ? string.Empty : buildString(componentInfo, @"
212  public Entity Set$Name($typedArgs) {
213  if(has$Name) {
214  throw new EntitasException(""Could not set $name!\n"" + this + "" already has an entity with $Type!"",
215  ""You should check if the pool already has a $nameEntity before setting it or use pool.Replace$Name()."");
216  }
217  var entity = CreateEntity();
218  entity.Add$Name($args);
219  return entity;
220  }
221 ");
222  }
223 
224  static string addPoolReplaceMethods(ComponentInfo componentInfo) {
225  return componentInfo.isSingletonComponent ? string.Empty : buildString(componentInfo, @"
226  public Entity Replace$Name($typedArgs) {
227  var entity = $nameEntity;
228  if(entity == null) {
229  entity = Set$Name($args);
230  } else {
231  entity.Replace$Name($args);
232  }
233 
234  return entity;
235  }
236 ");
237  }
238 
239  static string addPoolRemoveMethods(ComponentInfo componentInfo) {
240  return componentInfo.isSingletonComponent ? string.Empty : buildString(componentInfo, @"
241  public void Remove$Name() {
242  DestroyEntity($nameEntity);
243  }
244 ");
245  }
246 
247  /*
248  *
249  * MATCHER
250  *
251  */
252 
253  static string addMatcher(ComponentInfo componentInfo, bool onlyDefault = false) {
254  const string matcherFormat = @"
255  public partial class $TagMatcher {
256 
257  static IMatcher _matcher$Name;
258 
259  public static IMatcher $Name {
260  get {
261  if(_matcher$Name == null) {
262  var matcher = (Matcher)Matcher.AllOf($Ids.$Name);
263  matcher.componentNames = $Ids.componentNames;
264  _matcher$Name = matcher;
265  }
266 
267  return _matcher$Name;
268  }
269  }
270  }
271 ";
272 
273  if(onlyDefault) {
274  if(componentInfo.pools.Contains(CodeGenerator.DEFAULT_POOL_NAME)) {
275  return buildString(componentInfo, matcherFormat);
276  } else {
277  return string.Empty;
278  }
279  } else {
280  var poolIndex = 0;
281  var matchers = componentInfo.pools.Aggregate(string.Empty, (acc, poolName) => {
282  if(!poolName.IsDefaultPoolName()) {
283  return acc + buildString(componentInfo, matcherFormat, poolIndex++);
284  } else {
285  poolIndex += 1;
286  return acc;
287  }
288  });
289 
290  return buildString(componentInfo, matchers);
291  }
292  }
293 
294  /*
295  *
296  * HELPERS
297  *
298  */
299 
300  static string buildString(ComponentInfo componentInfo, string format, int poolIndex = 0) {
301  format = createFormatString(format);
302  var a0_type = componentInfo.fullTypeName;
303  var a1_name = componentInfo.typeName.RemoveComponentSuffix();
304  var a2_lowercaseName = a1_name.LowercaseFirst();
305  var poolNames = componentInfo.pools;
306  var a3_tag = poolNames[poolIndex].PoolPrefix();
307  var lookupTags = componentInfo.ComponentLookupTags();
308  var a4_ids = lookupTags.Length == 0 ? string.Empty : lookupTags[poolIndex];
309  var memberInfos = componentInfo.memberInfos;
310  var a5_memberNamesWithType = memberNamesWithType(memberInfos);
311  var a6_memberAssigns = memberAssignments(memberInfos);
312  var a7_memberNames = memberNames(memberInfos);
313  var prefix = componentInfo.singleComponentPrefix;
314  var a8_prefix = prefix.UppercaseFirst();
315  var a9_lowercasePrefix = prefix.LowercaseFirst();
316 
317  return string.Format(format, a0_type, a1_name, a2_lowercaseName,
318  a3_tag, a4_ids, a5_memberNamesWithType, a6_memberAssigns, a7_memberNames,
319  a8_prefix, a9_lowercasePrefix);
320  }
321 
322  static string createFormatString(string format) {
323  return format.Replace("{", "{{")
324  .Replace("}", "}}")
325  .Replace("$Type", "{0}")
326  .Replace("$Name", "{1}")
327  .Replace("$name", "{2}")
328  .Replace("$Tag", "{3}")
329  .Replace("$Ids", "{4}")
330  .Replace("$typedArgs", "{5}")
331  .Replace("$assign", "{6}")
332  .Replace("$args", "{7}")
333  .Replace("$Prefix", "{8}")
334  .Replace("$prefix", "{9}");
335  }
336 
337  static string memberNamesWithType(List<PublicMemberInfo> memberInfos) {
338  var typedArgs = memberInfos
339  .Select(info => info.type.ToCompilableString() + " new" + info.name.UppercaseFirst())
340  .ToArray();
341 
342  return string.Join(", ", typedArgs);
343  }
344 
345  static string memberAssignments(List<PublicMemberInfo> memberInfos) {
346  const string format = " component.{0} = {1};";
347  var assignments = memberInfos.Select(info => {
348  var newArg = "new" + info.name.UppercaseFirst();
349  return string.Format(format, info.name, newArg);
350  }).ToArray();
351 
352  return string.Join("\n", assignments);
353  }
354 
355  static string memberNames(List<PublicMemberInfo> memberInfos) {
356  var args = memberInfos.Select(info => "new" + info.name.UppercaseFirst()).ToArray();
357  return string.Join(", ", args);
358  }
359 
360  static string addCloseClass() {
361  return " }\n";
362  }
363  }
364 }