Merge "Added Control::SetSubState handling" into devel/master
[platform/core/uifw/dali-toolkit.git] / plugins / dali-swig / property-wrapper.rb
1 #!/usr/bin/env ruby
2 require 'pathname'
3 require 'scanf'
4
5 # Script does the following:
6 # - greps dali-core for DALI_PROPERTY macro which holds all the information about a property ( type, read only etc)
7 # - uses the filename of the macro to detect the class the properties belong to. E.g. actor-impl.cpp  = Actor
8 # - Scans each property macro and builds a list of DALi classes with an array of properties
9 # - Generates the csharp get/set  code for each property
10 # - Pastes the property get / set code into the DALi csharp files
11
12 # Given a DALi C++ property type this table stores the
13 # information needed to produce a csharp getter / setter
14 $typeTable =  [
15         ["BOOLEAN",     "bool",             "ref",  "bool temp = false;"],
16         ["FLOAT",       "float",            "ref",  "float temp = 0.0f;"],
17         ["INTEGER",     "int",              "ref",  "int temp = 0;"],
18         ["VECTOR2",     "Vector2",          "",     "Vector2 temp = new Vector2(0.0f,0.0f);"],
19         ["VECTOR3",     "Vector3",          "",     "Vector3 temp = new Vector3(0.0f,0.0f,0.0f);"],
20         ["VECTOR4",     "Vector4",          "",     "Vector4 temp = new Vector4(0.0f,0.0f,0.0f,0.0f);"],
21         ["MATRIX3",     "Matrix3",          "",     "Matrix3 temp = new Matrix3();"],
22         ["MATRIX",      "Matrix",           "",     "Matrix temp = new Matrix();"  ],
23         ["RECTANGLE",   "Rectangle",        "",     "Rectangle temp = new Rectangle(0,0,0,0);"],
24         ["ROTATION",    "Rotation",       "",     "Rotation temp = new Rotation();"],
25         ["STRING",      "string",           "out",  "string temp;"],
26         ["ARRAY",       "Dali.Property.Array",   "",     "Dali.Property.Array temp = new Dali.Property.Array();"],
27         ["MAP",         "Dali.Property.Map",     "",     "Dali.Property.Map temp = new Dali.Property.Map();"],
28     ]
29
30 # Some csharp classes are renamed ( e.g. C++ Control is called View in C#)
31 $renameMap = [
32               ["Control", "View"]
33              ]
34
35 $daliSwigPath = String.new;
36
37 def getCSharpName( cppClassName )
38
39     entry = $renameMap.select{ |a| a.first == cppClassName }
40     if( entry.empty?)
41       return cppClassName
42     end
43     return entry[0][1]
44 end
45
46 # use the table above to get information for a specific type
47 def getCSharpType( type )
48
49     entry = $typeTable.select{ |a| a.first == type }
50     if( entry.empty? )
51       return nil
52     end
53     return entry[0]
54 end
55
56
57 # Property struct stores the information about a property after parsing the C++ DALI_PROPERTY macro
58 $propertyStruct = Struct.new("Property", :name, :type, :writable, :animatable,:constrainInput, :enum, :shortenum, :develAPI, :csharpGetter, :csharpSetter, :childProperty,)
59
60 # daliClass struct stores a class name and an array of properties
61 $daliClassStruct = Struct.new("DaliClass", :name, :properties )
62
63 # class array stores all the dali classes ( actor, image etc)
64 $daliClassArray = Array.new
65
66 # list of files not generated by swig that we have tried to inject properties into
67 $filesNotWrapped= Array.new
68
69 # stats
70 $totalProperties = 0
71 $totalDaliClasses = 0
72
73 # global paths
74 $rootPath = ""
75 $daliCorePath = ""
76 $daliSwigPath = ""
77
78 # Extracts data  DALI__PROPERTY( "points", ARRAY,true,false,false,Dali::Path::Property::POINTS )
79 def extractPropertyInfo( propertyMacro )
80
81     # want to extract the property name, type + read only status
82     # split the DALI_PROPERTY macro definition by comma and quotes, and delete any empty segments
83     data = propertyMacro.split(/[\s,"]/).reject { |s| s.empty? }
84
85     propertyName = data[1]
86
87     # e.g. turn viewMatrix into ViewMatrix
88     propertyName[0] = propertyName[0].capitalize
89
90     # extract the property enum name Dali::Path::Property::POINTS -> POINTS
91     shortenum =  data[6].split(":").last
92
93     develAPI = false;
94     # Check if the property uses devel API
95     # Currently we ignore devel properties for now
96     if data[6].include? "Devel"
97       develAPI = true;
98       puts("Ignoring DEVEL API property: " + shortenum);
99     end
100
101
102
103     # store the :name, :type, :writable, :animatable, :constrainInput, :enum
104     property = $propertyStruct.new;
105
106     property.name = propertyName
107     property.type = data[2]
108     property.writable = (data[3]=="true")
109     property.animatable = (data[4] == "true")
110     property.constrainInput = (data[5]=="true")
111     property.enum = shortenum
112     property.develAPI = develAPI;
113     return property;
114 end
115
116 # Extracts data from Toolkit property definition
117 def extractToolkitPropertyInfo( propertyMacro )
118
119     # Extract the property name, type
120     property = $propertyStruct.new;
121
122     #First strip out any comments at the end of the macro, some have text like // deprecated
123     commentIndex = propertyMacro.index("//");
124
125     if( commentIndex )
126         propertyMacro = propertyMacro.slice(0..commentIndex-1)
127     end
128
129
130     # Split the macro definition by comma and quotes, close bracket and delete any empty segments
131     data = propertyMacro.split(/[\s,")]/).reject { |s| s.empty? }
132
133     if(data[1] == "PropertyRegistration")
134
135       # Properties defined in Control using PropertyRegistration
136       # const PropertyRegistration Control::Impl::PROPERTY_1(typeRegistration, "styleName", Toolkit::Control::Property::STYLE_NAME, Property::STRING, &Control::Impl::SetProperty, &Control::Impl::GetProperty);
137
138       # Creates an array of strings that looks like this:
139       # const 0
140       # PropertyRegistration 1
141       # Control::Impl::PROPERTY_1 2
142       # typeRegistration 3
143       # styleName 4
144       # Toolkit::Control::Property::STYLE_NAME 5
145       # Property::STRING 6
146       # &Control::Impl::SetProperty 7
147       # &Control::Impl::GetProperty 8
148       #
149
150       property.name = data[4]
151
152       propertyType = data[6].rpartition("::")
153       property.type = propertyType[2]
154
155       propertyEnum = data[5].rpartition("::")
156       property.enum = propertyEnum[2]
157
158     else
159
160       # Properties defined in macro DALI_PROPERTY_REGISTRATION or DALI_ANIMATABLE_PROPERTY_REGISTRATION or DALI_CHILD_PROPERTY_REGISTRATION
161       # or DALI_ANIMATABLE_PROPERTY_REGISTRATION_WITH_DEFAULT:
162       # DALI_PROPERTY_REGISTRATION(Toolkit, TextLabel, "multiLine", BOOLEAN, MULTI_LINE)
163       # DALI_ANIMATABLE_PROPERTY_REGISTRATION_WITH_DEFAULT(Toolkit, ImageView, "pixelArea", Vector4(0.f, 0.f, 1.f, 1.f), PIXEL_AREA)
164
165       # Creates an array of strings that looks like this:
166       # DALI_PROPERTY_REGISTRATION( 0
167       # Toolkit 1
168       # PageTurnView 2
169       # pageSize 3
170       # VECTOR2 4
171       # PAGE_SIZE 5
172       #
173
174       property.name = data[3]
175
176       #puts property.name
177       if property.name == "image"
178         property.name = "imageMap"
179       end
180
181       if( data[0] == "DALI_ANIMATABLE_PROPERTY_REGISTRATION_WITH_DEFAULT(" )
182         # TODO: Need to work out the property type from the value
183         property.type = "VECTOR4"
184       else
185         property.type = data[4]
186       end
187
188       # last item should be property enum, e.g. INPUT_POINT_SIZE
189       property.enum = data[data.length-1]
190
191     end
192
193     # e.g. turn styleName into StyleName
194     property.name[0] = property.name[0].capitalize
195
196     property.writable = true
197     property.animatable = false
198     property.constrainInput = false
199     property.childProperty = false;
200
201     # check to see if it's a child property
202     if( data[0] == "DALI_CHILD_PROPERTY_REGISTRATION(" )
203       #puts(" #{property.name} is child property ")
204       property.childProperty = true;
205     end
206     if( data[0] == "DALI_ANIMATABLE_PROPERTY_REGISTRATION(" )
207       #puts("  #{property.name} is animatable")
208       property.animatable = true;
209     end
210
211     return property;
212 end
213
214 def writePropertiesToCSharpFile( daliClass )
215
216    # open the CSharp file autogenerated by SWIG
217   swigFiles =  $daliSwigPath + "/automatic/csharp/"
218
219   # some C++ classes are renamed for C#
220   className = getCSharpName( daliClass.name )
221
222   fileName =(swigFiles + className ) + ".cs"
223
224
225   # it's possible some classes in dali-core aren't being wrapped by swig, so if the swig generated file
226   # doesn't exist just return
227   if( ! File.exist?(fileName) )
228     $filesNotWrapped.push("#{daliClass.name}.cs ")
229     return
230   end
231
232   File.open(fileName, 'r+') do |file|
233
234   last_line  =0
235   file.each { last_line = file.pos unless file.eof? }
236
237   # we seek to the end of the file... minus 3 characters which lets us overwrite the 2 closing brackets
238   # so we can insert the getter/setter stuff into the file.
239   file.seek( last_line-3, IO::SEEK_SET)
240
241   for property in daliClass.properties
242
243     if( (!property.childProperty) && (!property.develAPI))
244       file.write( property.csharpGetter );
245       file.write( property.csharpSetter );
246     end
247
248   end
249
250   file.write("\n}\n\n}");  # re-insert the closing brackets we over-wrote
251   end
252
253   puts("Injected #{daliClass.properties.length} C# Properties from #{daliClass.name} into #{className}.cs".blueBackground)
254
255 end
256
257 def writeChildPropertiesToCSharpFile( daliClass )
258
259   # open the CSharp file autogenerated by SWIG
260   swigFiles =  $daliSwigPath + "/automatic/csharp/"
261
262   # Add all the child properties to Control
263   fileName = (swigFiles+"View") + ".cs"
264
265   if( ! File.exist?(fileName) )
266     return
267   end
268
269   File.open(fileName, 'r+') do |file|
270
271   last_line  =0
272   file.each { last_line = file.pos unless file.eof? }
273
274   # we seek to the end of the file... minus 3 characters which lets us overwrite the 2 closing brackets
275   # so we can insert the getter/setter stuff into the file.
276   file.seek( last_line-3, IO::SEEK_SET)
277
278   $childPropertyCount = 0
279
280   for property in daliClass.properties
281
282     if (property.childProperty)
283       file.write( property.csharpGetter );
284       file.write( property.csharpSetter );
285       $childPropertyCount += 1
286     end
287
288   end
289
290   file.write("\n}\n\n}");  # re-insert the closing brackets we over-wrote
291   end
292
293   puts("Injected #{$childPropertyCount} C# Child Properties into #{"View"}.cs".blueBackground)
294
295 end
296
297 # Write the CSharp data to the generated .cs file
298 def writeCSharpData
299
300     for daliClass in  $daliClassArray
301
302         #puts ( daliClass.name )
303
304
305         hasChildProperties = false
306
307         for property in daliClass.properties
308             propertyInfo = getCSharpType( property.type )
309
310             if( propertyInfo.length() < 2 )
311                 # some types aren't supported yet like Rotation
312                 next
313             end
314
315             #exception case <<<
316             #Tooltip gives swig build error
317             if( property.name == "Tooltip" )
318                 next
319             end
320             #exception case >>>
321
322             $totalProperties+=1  #  keep track of total
323
324             propertyType = propertyInfo[1]    # e.g. bool or int
325             propertyArg =  propertyInfo[2]  # e.g. ref or out
326             tempDeclaration = propertyInfo[3] # e.g. bool temp;
327
328             csharpClassName = getCSharpName(daliClass.name);
329
330
331             propertyName = "#{csharpClassName}.Property.#{property.enum}"
332
333             if property.childProperty
334               propertyName = "#{csharpClassName}.ChildProperty.#{property.enum}"
335               hasChildProperties = true
336             end
337
338             property.csharpGetter ="  public #{propertyType} #{property.name}\n"\
339                      "  {\n"\
340                      "    get\n" \
341                      "    {\n"\
342                      "      #{tempDeclaration}\n"\
343                      "      GetProperty( #{propertyName}).Get( #{propertyArg} temp );\n"\
344                      "      return temp;\n"\
345                      "    }\n"
346
347             if property.writable
348                   #text.SetProperty(TextLabel.Property.HORIZONTAL_ALIGNMENT, new Property.Value("CENTER"));
349                   property.csharpSetter = "    set\n" \
350                          "    {\n"\
351                          "      SetProperty( #{propertyName}, new Dali.Property.Value( value ) );\n" \
352                          "    }\n"\
353                          "  }\n"
354             else
355                    property.csharpSetter = "}"  # close the opening property declaration
356             end
357
358             #exception case <<<
359             if( property.name == "Behavior" )
360                 property.csharpGetter ="  public Layer.LayerBehavior #{property.name} \n"\
361                      "  { \n"\
362                      "    get \n" \
363                      "    {\n"\
364                      "      return GetBehavior();\n"\
365                      "    }\n"
366
367                 property.csharpSetter = "    set \n" \
368                      "    { \n"\
369                      "      SetBehavior( value );\n" \
370                      "    }\n"\
371                      "  }\n"
372             end
373             #exception case >>>
374         end
375         # write normal properties to the class's own csharp file
376         writePropertiesToCSharpFile( daliClass )
377         # write child properties to View.cs ( on Control has child properties)
378         if (hasChildProperties)
379           writeChildPropertiesToCSharpFile( daliClass )
380         end
381     end
382
383 end
384
385 def getDaliClassItem( className )
386
387    # puts( "getDaliClassItem  "+ className )
388     index = $daliClassArray.index{ |a| a.name == className }
389
390     if( index == nil)
391         # create a new item along with a array for it's properites
392         classItem = $daliClassStruct.new( className, Array.new )
393         $daliClassArray.push( classItem )
394         $totalDaliClasses+=1  # for stats
395     else
396         # puts("class found " + className )
397         classItem = $daliClassArray[ index ]
398     end
399
400     return classItem;
401
402 end
403
404
405
406 def init
407
408     pn = Pathname.new(Dir.pwd)
409     fullPath = pn.to_s
410
411     $rootPath = fullPath.slice(0..( fullPath.index('/dali-toolkit')))
412     $daliCorePath = $rootPath + "dali-core/dali"   # source code path
413     $daliSwigPath = $rootPath + "dali-toolkit/plugins/dali-swig"
414     $daliToolkitPath = $rootPath + "dali-toolkit/dali-toolkit"  # source code path
415
416     puts("--------------------------------------------")
417     puts("Injecting DALi properties into SWIG generated C# files ")
418     puts("")
419
420
421 end
422
423 def writeDaliCoreProperties
424
425     puts("Scanning for DALI_PROPERTY macro in dali-core");
426     puts("Scanning folder: #{$daliCorePath}\n\n");
427
428     # Executed a recursive grep over dali-core for the DALI_PROPERTY macro
429     result =`grep --include \\*.cpp  -r "DALI_PROPERTY( \" #{$daliCorePath}`
430
431
432     # We now have a list of lines that look like this:
433     # dali/internal/event/animation/path-impl.cpp:DALI__PROPERTY( "points", ARRAY,true,false,false,Dali::Path::Property::POINTS )
434
435     lines = result.split(/\n+/);
436     for line in lines
437
438
439       # Split the line into file name and property macro, splt 2 means just create two strings as we don't want to split
440       # property Dali::Path::Property::POINTS string as well
441
442       data  = line.split(":",2)
443       fileName = data[0];
444       macro = data[1];
445
446       # Get the class name from the filename  ( e.g. image-actor-impl.cpp => image-actor)
447       className = File.basename(fileName,"-impl.cpp").capitalize
448
449       # convert it from image-actor to ImageActor
450       className = className.split(/ |\_|\-/).map(&:capitalize).join
451
452       # Get the property information ( name, type, read/writeable)
453       propertyInfo = extractPropertyInfo( macro );
454
455       # get or create a new DALi class item which stores the property information
456       classItem  = getDaliClassItem( className )
457
458       classItem.properties.push( propertyInfo )
459
460     end
461
462     writeCSharpData()
463 end
464
465 def writeDaliToolkitProperties
466
467
468     puts("\nScanning for PROPERTY_REGISTRATION macros in dali-toolkit");
469     puts("Scanning folder: #{$daliToolkitPath}\n\n");
470
471     $daliClassArray.clear;
472
473     # Executed a recursive grep over dali-toolkit for following macros
474     # DALI_PROPERTY_REGISTRATION
475     # DALI_ANIMATABLE_PROPERTY_REGISTRATION
476     # DALI_CHILD_PROPERTY_REGISTRATION
477     result =`grep --include \\*.cpp  -w 'Control::Impl::SetProperty\\|DALI_ANIMATABLE_PROPERTY_REGISTRATION_WITH_DEFAULT(\\|DALI_CHILD_PROPERTY_REGISTRATION(\\|DALI_ANIMATABLE_PROPERTY_REGISTRATION(\\|DALI_PROPERTY_REGISTRATION' -r #{$daliToolkitPath}`
478
479     if( result == "" )
480       puts("Error parsing #{$daliToolkitPath} no properties found")
481       return
482     end
483     # create an array to store each DALi class and it's assoc
484     classArray = Array.new
485
486     # We now have a list of lines that look like this:
487     # text-controls/text-label-impl.cpp:DALI_PROPERTY_REGISTRATION( Toolkit, TextLabel, "multiLine", BOOLEAN, MULTI_LINE )
488     lines = result.split(/\n+/);
489     for line in lines
490
491
492       # Split the line into file name and property macro, split 2 means just create two strings
493       data  = line.split(":",2)
494       fileName = data[0];
495       macro = data[1];
496
497       # Get the class name from the filename  ( e.g. image-actor-impl.cpp => image-actor)
498       className = File.basename(fileName,"-impl.cpp").capitalize
499
500       # convert it from image-actor to ImageActor
501       className = className.split(/ |\_|\-/).map(&:capitalize).join
502
503       #puts(className);
504       #puts(fileName);
505
506         # Get the property information ( name, type, read/writeable)
507       propertyInfo = extractToolkitPropertyInfo( macro );
508
509       # get or create a new DALi class item which stores the property information
510       classItem  = getDaliClassItem( className )
511
512       classItem.properties.push( propertyInfo )
513
514     end
515
516     writeCSharpData()
517
518 end
519
520 # helper class to color the background
521 class String
522 def blueBackground;   "\e[44m#{self}\e[0m" end
523 end
524
525 def writeStats
526
527   puts("\nFiles that have not been wrapped file by SWIG ( not included in dali.i file):")
528   for i in $filesNotWrapped
529     puts(i)
530   end
531
532   puts("Done. Injected #{$totalProperties} properties into #{$totalDaliClasses} DALi C# classes".blueBackground)
533
534 end
535
536 init()
537
538 writeDaliCoreProperties()
539
540 writeDaliToolkitProperties()
541
542 writeStats()
543