sws 0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (158) hide show
  1. data/doc/DOC.otl +34 -0
  2. data/doc/Makefile +13 -0
  3. data/doc/architecture.dia +0 -0
  4. data/doc/docbook/architecture.png +0 -0
  5. data/doc/docbook/concepts.docbook +474 -0
  6. data/doc/docbook/installation.docbook +57 -0
  7. data/doc/docbook/introduction.docbook +130 -0
  8. data/doc/docbook/sws_manual.docbook +35 -0
  9. data/doc/docbook/todo.docbook +38 -0
  10. data/doc/docbook/tutorial.docbook +594 -0
  11. data/examples/README +1 -0
  12. data/examples/addressbook/CardBrowse/CardBrowse.html +43 -0
  13. data/examples/addressbook/CardBrowse/CardBrowse.rb +65 -0
  14. data/examples/addressbook/CardBrowse/CardBrowse.sws +92 -0
  15. data/examples/addressbook/Login/LoginPage.html +12 -0
  16. data/examples/addressbook/Login/LoginPage.rb +19 -0
  17. data/examples/addressbook/Login/LoginPage.sws +15 -0
  18. data/examples/addressbook/README +1 -0
  19. data/examples/addressbook/addressbook.rb +70 -0
  20. data/examples/addressbook/application.yaml +8 -0
  21. data/examples/addressbook/db.yaml +7 -0
  22. data/examples/component_demo/CheckBoxDemo/CheckBoxDemo.html +11 -0
  23. data/examples/component_demo/CheckBoxDemo/CheckBoxDemo.rb +21 -0
  24. data/examples/component_demo/CheckBoxDemo/CheckBoxDemo.sws +25 -0
  25. data/examples/component_demo/ComponentDemo.rb +21 -0
  26. data/examples/component_demo/ConditionalDemo/ConditionalDemo.html +18 -0
  27. data/examples/component_demo/ConditionalDemo/ConditionalDemo.rb +2 -0
  28. data/examples/component_demo/ConditionalDemo/ConditionalDemo.sws +22 -0
  29. data/examples/component_demo/FileUploadDemo/FileUploadDemo.html +10 -0
  30. data/examples/component_demo/FileUploadDemo/FileUploadDemo.rb +9 -0
  31. data/examples/component_demo/FileUploadDemo/FileUploadDemo.sws +33 -0
  32. data/examples/component_demo/FormFieldsDemo/FormFieldsDemo.html +12 -0
  33. data/examples/component_demo/FormFieldsDemo/FormFieldsDemo.rb +21 -0
  34. data/examples/component_demo/FormFieldsDemo/FormFieldsDemo.sws +40 -0
  35. data/examples/component_demo/FormListsDemo/FormListsDemo.html +11 -0
  36. data/examples/component_demo/FormListsDemo/FormListsDemo.rb +37 -0
  37. data/examples/component_demo/FormListsDemo/FormListsDemo.sws +47 -0
  38. data/examples/component_demo/GenericDemo/GenericDemo.html +4 -0
  39. data/examples/component_demo/GenericDemo/GenericDemo.rb +2 -0
  40. data/examples/component_demo/GenericDemo/GenericDemo.sws +10 -0
  41. data/examples/component_demo/HyperlinkDemo/HyperlinkDemo.html +8 -0
  42. data/examples/component_demo/HyperlinkDemo/HyperlinkDemo.rb +20 -0
  43. data/examples/component_demo/HyperlinkDemo/HyperlinkDemo.sws +19 -0
  44. data/examples/component_demo/ImageLinkDemo/ImageLinkDemo.html +11 -0
  45. data/examples/component_demo/ImageLinkDemo/ImageLinkDemo.rb +2 -0
  46. data/examples/component_demo/ImageLinkDemo/ImageLinkDemo.sws +14 -0
  47. data/examples/component_demo/PageWrapper/PageWrapper.html +23 -0
  48. data/examples/component_demo/PageWrapper/PageWrapper.rb +2 -0
  49. data/examples/component_demo/PageWrapper/PageWrapper.sws +42 -0
  50. data/examples/component_demo/README +1 -0
  51. data/examples/component_demo/RepetitionDemo/RepetitionDemo.html +13 -0
  52. data/examples/component_demo/RepetitionDemo/RepetitionDemo.rb +19 -0
  53. data/examples/component_demo/RepetitionDemo/RepetitionDemo.sws +20 -0
  54. data/examples/component_demo/StringDemo/StringDemo.html +5 -0
  55. data/examples/component_demo/StringDemo/StringDemo.rb +16 -0
  56. data/examples/component_demo/StringDemo/StringDemo.sws +14 -0
  57. data/examples/component_demo/application.yaml +28 -0
  58. data/examples/component_demo/poweredby.jpg +0 -0
  59. data/examples/component_demo/style.css +1 -0
  60. data/examples/movies/Menu/Menu.html +3 -0
  61. data/examples/movies/Menu/Menu.rb +7 -0
  62. data/examples/movies/Menu/Menu.sws +7 -0
  63. data/examples/movies/MovieBrowse/MovieBrowse.html +68 -0
  64. data/examples/movies/MovieBrowse/MovieBrowse.rb +178 -0
  65. data/examples/movies/MovieBrowse/MovieBrowse.sws +127 -0
  66. data/examples/movies/README +1 -0
  67. data/examples/movies/UserBrowse/UserBrowse.html +50 -0
  68. data/examples/movies/UserBrowse/UserBrowse.rb +69 -0
  69. data/examples/movies/UserBrowse/UserBrowse.sws +49 -0
  70. data/examples/movies/application.yaml +11 -0
  71. data/examples/movies/da.rb +36 -0
  72. data/examples/movies/dbmovies.rb +44 -0
  73. data/examples/movies/frameworks/TestFramework/framework.yaml +4 -0
  74. data/examples/movies/frameworks/TestFramework/resources/im1.jpg +0 -0
  75. data/examples/movies/images/pbr1b.jpg +0 -0
  76. data/examples/movies/movies.rb +28 -0
  77. data/examples/movies/movies.sds +119 -0
  78. data/examples/movies/movies.sqlite +0 -0
  79. data/examples/movies/movies_mysql.sql +28 -0
  80. data/examples/movies/movies_postgres.sql +27 -0
  81. data/examples/movies/movies_sqlite.sql +28 -0
  82. data/lib/sws.rb +89 -0
  83. data/lib/sws/Core/components/CheckBox/CheckBox.api +5 -0
  84. data/lib/sws/Core/components/CheckBox/CheckBox.rb +45 -0
  85. data/lib/sws/Core/components/CheckBoxList/CheckBoxList.api +13 -0
  86. data/lib/sws/Core/components/CheckBoxList/CheckBoxList.rb +54 -0
  87. data/lib/sws/Core/components/Conditional/Conditional.api +3 -0
  88. data/lib/sws/Core/components/Conditional/Conditional.html +1 -0
  89. data/lib/sws/Core/components/Conditional/Conditional.rb +39 -0
  90. data/lib/sws/Core/components/Conditional/Conditional.sws +2 -0
  91. data/lib/sws/Core/components/Content/Content.rb +18 -0
  92. data/lib/sws/Core/components/ExceptionPage/ExceptionPage.html +13 -0
  93. data/lib/sws/Core/components/ExceptionPage/ExceptionPage.rb +18 -0
  94. data/lib/sws/Core/components/ExceptionPage/ExceptionPage.sws +16 -0
  95. data/lib/sws/Core/components/FileUpload/FileUpload.api +16 -0
  96. data/lib/sws/Core/components/FileUpload/FileUpload.rb +62 -0
  97. data/lib/sws/Core/components/Form/Form.api +9 -0
  98. data/lib/sws/Core/components/Form/Form.html +3 -0
  99. data/lib/sws/Core/components/Form/Form.rb +55 -0
  100. data/lib/sws/Core/components/Form/Form.sws +12 -0
  101. data/lib/sws/Core/components/GenericContainer/GenericContainer.api +10 -0
  102. data/lib/sws/Core/components/GenericContainer/GenericContainer.html +1 -0
  103. data/lib/sws/Core/components/GenericContainer/GenericContainer.rb +39 -0
  104. data/lib/sws/Core/components/GenericContainer/GenericContainer.sws +12 -0
  105. data/lib/sws/Core/components/GenericElement/GenericElement.api +10 -0
  106. data/lib/sws/Core/components/GenericElement/GenericElement.rb +34 -0
  107. data/lib/sws/Core/components/HiddenField/HiddenField.api +7 -0
  108. data/lib/sws/Core/components/HiddenField/HiddenField.rb +37 -0
  109. data/lib/sws/Core/components/Hyperlink/Hyperlink.api +13 -0
  110. data/lib/sws/Core/components/Hyperlink/Hyperlink.html +1 -0
  111. data/lib/sws/Core/components/Hyperlink/Hyperlink.rb +102 -0
  112. data/lib/sws/Core/components/Hyperlink/Hyperlink.sws +12 -0
  113. data/lib/sws/Core/components/Image/Image.api +11 -0
  114. data/lib/sws/Core/components/Image/Image.rb +49 -0
  115. data/lib/sws/Core/components/ImageButton/ImageButton.api +16 -0
  116. data/lib/sws/Core/components/ImageButton/ImageButton.rb +76 -0
  117. data/lib/sws/Core/components/Link/Link.api +11 -0
  118. data/lib/sws/Core/components/Link/Link.rb +39 -0
  119. data/lib/sws/Core/components/PasswordField/PasswordField.api +7 -0
  120. data/lib/sws/Core/components/PasswordField/PasswordField.rb +41 -0
  121. data/lib/sws/Core/components/RadioButton/RadioButton.api +7 -0
  122. data/lib/sws/Core/components/RadioButton/RadioButton.rb +44 -0
  123. data/lib/sws/Core/components/RadioButtonList/RadioButtonList.api +20 -0
  124. data/lib/sws/Core/components/RadioButtonList/RadioButtonList.rb +76 -0
  125. data/lib/sws/Core/components/Repetition/Repetition.api +10 -0
  126. data/lib/sws/Core/components/Repetition/Repetition.html +1 -0
  127. data/lib/sws/Core/components/Repetition/Repetition.rb +137 -0
  128. data/lib/sws/Core/components/Repetition/Repetition.sws +2 -0
  129. data/lib/sws/Core/components/ResetButton/ResetButton.api +5 -0
  130. data/lib/sws/Core/components/ResetButton/ResetButton.rb +28 -0
  131. data/lib/sws/Core/components/Select/Select.api +24 -0
  132. data/lib/sws/Core/components/Select/Select.rb +57 -0
  133. data/lib/sws/Core/components/String/String.api +5 -0
  134. data/lib/sws/Core/components/String/String.rb +28 -0
  135. data/lib/sws/Core/components/SubmitButton/SubmitButton.api +6 -0
  136. data/lib/sws/Core/components/SubmitButton/SubmitButton.rb +53 -0
  137. data/lib/sws/Core/components/TextArea/TextArea.api +9 -0
  138. data/lib/sws/Core/components/TextArea/TextArea.rb +28 -0
  139. data/lib/sws/Core/components/TextField/TextField.api +9 -0
  140. data/lib/sws/Core/components/TextField/TextField.rb +46 -0
  141. data/lib/sws/Core/framework.yaml +25 -0
  142. data/lib/sws/JSComponents/components/JSMenu/JSMenu.api +5 -0
  143. data/lib/sws/JSComponents/components/JSMenu/JSMenu.html +58 -0
  144. data/lib/sws/JSComponents/components/JSMenu/JSMenu.rb +34 -0
  145. data/lib/sws/JSComponents/components/JSMenu/JSMenu.sws +37 -0
  146. data/lib/sws/JSComponents/framework.yaml +3 -0
  147. data/lib/sws/adaptor.rb +334 -0
  148. data/lib/sws/application.rb +604 -0
  149. data/lib/sws/component.rb +656 -0
  150. data/lib/sws/cookie.rb +27 -0
  151. data/lib/sws/direct_action.rb +38 -0
  152. data/lib/sws/extensions.rb +49 -0
  153. data/lib/sws/parsers.rb +374 -0
  154. data/lib/sws/request.rb +308 -0
  155. data/lib/sws/response.rb +70 -0
  156. data/lib/sws/session.rb +195 -0
  157. data/lib/sws/slot.rb +198 -0
  158. metadata +263 -0
@@ -0,0 +1,27 @@
1
+ module SWS
2
+
3
+
4
+ # Represents HTML cookie
5
+ class Cookie
6
+
7
+
8
+ attr_reader :key
9
+ attr_reader :value
10
+ attr_reader :path
11
+
12
+ #TODO: add domain?
13
+
14
+ # Creates new Cookie with given key=value pair and optionally path
15
+ def initialize ( key, value, path="/" )
16
+ @key,@value,@path = key,value,path
17
+ end
18
+
19
+
20
+ # Returns cookie representation in HTTP header form
21
+ def to_s
22
+ return "Set-Cookie: #{@key}=#{@value}; path=#{@path}"
23
+ end
24
+
25
+ end
26
+
27
+ end
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ module SWS
4
+
5
+ # DirectAction is a way to define multiple entry point to the application.
6
+ # Default entry point is a default_component defined in application config
7
+ # file. But sometimes you may need more entry points. In such a situation
8
+ # create appropiate ..._action methods in your DirectAction class and then you
9
+ # may access them by using
10
+ # http://application_url/direct_action_key/method_name_without_action_word
11
+ # URL.
12
+ # DirectAction requests are served by instances of this class by default. You
13
+ # can also define your own classes for this purpose.
14
+ class DirectAction
15
+
16
+ # The Request object the receiver is associated to
17
+ attr_reader :request
18
+
19
+ def initialize ( request )
20
+ @request = request
21
+ end
22
+
23
+ # This methods returns default_component instance
24
+ def default_action ()
25
+
26
+ component = Component.create( Application.instance.default_component_class_name, nil, @request )
27
+ return component.process_request( @request )
28
+
29
+ end
30
+
31
+ def action_for_name ( name )
32
+ # TODO: handle non-existing action?
33
+ return method( name + "_action" ).call()
34
+ end
35
+
36
+ end
37
+
38
+ end
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Extensions to String class - encoding and decoding of URL-encoded string
4
+ class String
5
+
6
+
7
+ # Decode URL-encoded string - returns decoded string.
8
+ def url_decode ()
9
+ tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n) do
10
+ [$1.delete('%')].pack('H*')
11
+ end
12
+
13
+ end
14
+
15
+
16
+ # Decode URL-encoded string - modifies the receiver. Returns decoded string.
17
+ def url_decode! ()
18
+
19
+ tr!('+', ' ')
20
+ gsub!(/((?:%[0-9a-fA-F]{2})+)/n) do
21
+ [$1.delete('%')].pack('H*')
22
+ end
23
+ return self
24
+
25
+ end
26
+
27
+
28
+ # Encode string with URL-encoding - returns encoded string.
29
+ def url_encode ()
30
+
31
+ gsub(/([^ a-zA-Z0-9_.-]+)/n) do
32
+ '%' + $1.unpack('H2' * $1.size).join('%').upcase
33
+ end.tr(' ', '+')
34
+
35
+ end
36
+
37
+
38
+ # Encode string with URL-encoding - modifies the receiver. Returns encoded
39
+ # string.
40
+ def url_encode! ()
41
+
42
+ gsub!(/([^ a-zA-Z0-9_.-]+)/n) do
43
+ '%' + $1.unpack('H2' * $1.size).join('%').upcase
44
+ end.tr(' ', '+')
45
+
46
+ end
47
+
48
+ end
49
+
@@ -0,0 +1,374 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Definition and template parser and helper classes
4
+ module SWS
5
+
6
+ # Class that retrieves definition of the component from .sws and/or .api files.
7
+ # Parsed definitions are cached to avoid unnecessary overhead.
8
+ class DefinitionParser
9
+
10
+ include Singleton
11
+
12
+ def initialize ()
13
+
14
+ # .sws file cache
15
+ @sws_cache = Hash.new do |hash,filename|
16
+ $log_sws_parser.debug( "Parsing .sws #{filename}" )
17
+ hash[filename] = YAML::load( File.open( filename ) ) #sws_parse( filename )
18
+ end
19
+
20
+ # .api file cache
21
+ @api_cache = Hash.new do |hash,filename|
22
+ $log_sws_parser.debug( "Parsing .api #{filename}" )
23
+ hash[filename] = YAML::load( File.open( filename ) )
24
+ end
25
+
26
+ end
27
+
28
+
29
+ # Most important method of the class - returns Component object for given
30
+ # token in given parent component. Does it by parsing parent definition file
31
+ # and looking for definition of Component with given token name. When the
32
+ # definition is found, new Component object with proper slots and definition
33
+ # (again retrieved from file and parsed) is created.
34
+ def component_for_token_with_parent ( token, parent )
35
+
36
+
37
+ # Element to find - component definition
38
+ component_definition = nil
39
+
40
+ # Definition is looked for in parent component recursively - up to the
41
+ # topmost component
42
+ next_parent = parent
43
+
44
+ name = token.sws_name
45
+
46
+ loop do
47
+
48
+ $log_sws_parser.debug( "Looking for #{name} in #{next_parent}" )
49
+
50
+ # Because this is the parent (so it has children) we don't have to check
51
+ # if the .sws file is present
52
+ parent_definition = @sws_cache[ next_parent.definition_filename ]
53
+
54
+ # Definition of component looked for in parent definition - if not
55
+ # found, the parent's parent will be searched
56
+ # FIXME: this will surely work incorrectly when 2 nested components have
57
+ # subcomponents of the same name!
58
+ component_definition = parent_definition[name]
59
+
60
+ if ( component_definition != nil )
61
+ break
62
+ elsif ( next_parent.parent != nil )
63
+ next_parent = next_parent.parent
64
+ else
65
+ raise( "Definiton for component #{name} not found!" )
66
+ end
67
+
68
+ end
69
+
70
+ $log_sws_parser.debug( "Definition found" )
71
+
72
+ # Create Component object using component_definition hash
73
+ class_name = component_definition[ "_class" ]
74
+ unless( class_name )
75
+ raise( "Cannot find _class attribute for component #{token.sws_name}" )
76
+ end
77
+ component = Component.create( class_name, parent.request, name.dup(), parent )
78
+ component.definition_component = next_parent
79
+ component.html_attrs = token.attributes
80
+
81
+ slots = Hash.new
82
+
83
+ if ( component.api_filename != nil )
84
+ @api_cache[ component.api_filename ].each_pair do |slot_name, slot_attrs|
85
+
86
+ slot = Slot.new( component, slot_name )
87
+ slot.bind( component_definition[slot_name] )
88
+
89
+ if ( slot_attrs )
90
+ slot.default = slot_attrs["default"]
91
+ slot.required = slot_attrs["required"]
92
+ slot.settable = slot_attrs["settable"]
93
+ end
94
+
95
+ slots[slot_name] = slot
96
+ end
97
+ end
98
+
99
+ component.slots = slots
100
+ component.synchronize_slots()
101
+ return component
102
+
103
+ end
104
+
105
+ end
106
+
107
+
108
+ # Parser for HTML templates of the components.
109
+ class TemplateParser
110
+
111
+ @@file_data = {}
112
+
113
+ # Creates new TemplateParser for given filename.
114
+ def initialize ( filename )
115
+
116
+ $log_sws_parser.debug( "Creating template parser for name #{filename}" )
117
+ @template_filename = filename
118
+
119
+ end
120
+
121
+
122
+ # Main parsing method - parses definition file into the tree of
123
+ # TemplateToken objects
124
+ def parse ()
125
+
126
+ begin
127
+ #TODO: implement debug mode which forces sws to reload
128
+ # template file every time parse() is called
129
+ unless( @@file_data[ @template_filename ] )
130
+ @@file_data[ @template_filename ] = File.read( @template_filename )
131
+ end
132
+ template_data = @@file_data[ @template_filename ]
133
+
134
+ rescue Exception
135
+ raise "Cannot read template data from file \"#{@template_filename}\""
136
+ end
137
+
138
+ tokenized_data = template_data.scan( /<!--.*?-->|<[^<>]+>|[^<>]+/ )
139
+ root_token = create_token_tree( tokenized_data )
140
+
141
+ return root_token
142
+
143
+ end
144
+
145
+
146
+ private
147
+
148
+ # Parses attributes of the tag - used only for SWS tags, so returns null if "sws"
149
+ # attribute is not found
150
+ def parse_attributes ( tag )
151
+
152
+ attrs = Hash.new
153
+
154
+ # Scans tag attrs - matches also attrs with no value, like "selected"
155
+ # parameter of checkbox or "disabled"
156
+ tag.scan( /\s+(\w+)\s*=?\s*(['"])(.*?)\2|\s+(\w+)\s*=?\s*([^'"\s>]*)/ ).each do |name,quote,value,second_name,second_value|
157
+ unless ( name )
158
+ name = second_name
159
+ value = second_value
160
+ end
161
+ value ||= quote
162
+ attrs[name.downcase] = value
163
+ end
164
+
165
+ return attrs["sws"] ? attrs : nil
166
+
167
+ end
168
+
169
+
170
+ # Creates token tree from the array of lexems
171
+ def create_token_tree ( lexems )
172
+
173
+ nesting_level = 0
174
+ root_tokens = Array.new
175
+ current_token = nil
176
+
177
+ lexems.reverse.each do |lexem|
178
+
179
+ if ( comment?( lexem ) )
180
+
181
+ token = TemplateToken.new( current_token,nil,true,true )
182
+ token.data = lexem
183
+ if ( current_token )
184
+ current_token.children << token
185
+ else
186
+ root_tokens << token
187
+ end
188
+
189
+ elsif ( closing?( lexem ) )
190
+
191
+ tag_name = lexem.scan( /^<\/(\w+)>$/ )[0][0].downcase
192
+ token = TemplateToken.new( current_token,tag_name )
193
+ if ( current_token )
194
+ current_token.children << token
195
+ else
196
+ root_tokens << token
197
+ end
198
+ current_token = token
199
+ nesting_level += 1
200
+
201
+ elsif ( opening_or_standalone?( lexem ) )
202
+
203
+ tag_name = lexem.scan( /^<(\w+)/ )[0][0].downcase
204
+ if ( current_token && current_token.name == tag_name ) # tag should be closed
205
+ attrs = parse_attributes( lexem )
206
+ if ( attrs )
207
+ current_token.attributes = attrs
208
+ else
209
+ # Why should we parse an ordinal tag (attributes etc) and then
210
+ # generate it again, if we can just put the original lexem?
211
+ current_token.data = lexem
212
+ end
213
+ current_token = current_token.parent
214
+ nesting_level -= 1
215
+ else # standalone tag
216
+ token = TemplateToken.new( current_token,tag_name,true )
217
+ attrs = parse_attributes( lexem )
218
+ if ( attrs )
219
+ token.attributes = attrs
220
+ else
221
+ token.data = lexem
222
+ end
223
+ if ( current_token )
224
+ current_token.children << token
225
+ else
226
+ root_tokens << token
227
+ end
228
+ end
229
+
230
+ else #text token
231
+
232
+ token = TemplateToken.new( current_token,nil,true )
233
+ token.data = lexem
234
+ if ( current_token )
235
+ current_token.children << token
236
+ else
237
+ root_tokens << token
238
+ end
239
+
240
+ end
241
+
242
+ end
243
+
244
+ root_tokens.each { |token| token.reverse() }
245
+ root_tokens.reverse!
246
+
247
+ unless ( nesting_level == 0 )
248
+ raise( "Invalid .html template file - probably some tags are not closed" )
249
+ end
250
+ #root_tokens.each { |token| token.dump ( "" ) }
251
+
252
+ return root_tokens
253
+
254
+ end
255
+
256
+
257
+ # Returns true if the tag is an opening or standalone tag
258
+ def opening_or_standalone? ( tag )
259
+ return ( tag =~ /^<\w+/ )
260
+ end
261
+
262
+
263
+ # Returns true if the tag is a HTML comment
264
+ def comment? ( tag )
265
+ return tag =~ /^<!--.*-->$/
266
+ end
267
+
268
+
269
+ # Returns true if the tag is a closing tag
270
+ def closing? (tag)
271
+ return ( tag =~ /^<\/\w+>/ )
272
+ end
273
+
274
+ end
275
+
276
+
277
+ # Internal class - used only by TemplateParser. Represents a HTML tag or text
278
+ # token
279
+ class TemplateToken
280
+
281
+ attr_reader :parent, :name, :children
282
+ attr_accessor :attributes, :data
283
+
284
+
285
+ # Creates new Token with given parent
286
+ def initialize ( parent,name,standalone=false,comment=false )
287
+
288
+ @parent = parent
289
+ @name = name
290
+ @children = Array.new
291
+ @comment = comment
292
+ @standalone = standalone
293
+ @data = nil
294
+
295
+ end
296
+
297
+ # Reverses receiver's children array. Used at the end of parsing process, as
298
+ # parsing is performed backwards (otherwise it is hard to distinguish
299
+ # open/close and standalone tags)
300
+ def reverse ()
301
+ @children.reverse!
302
+ @children.each { |child| child.reverse() }
303
+ end
304
+
305
+ # Returns true if the receiver represents a Component
306
+ def sws? ()
307
+ return @name != nil && @attributes != nil
308
+ end
309
+
310
+ # Returns name of the Component
311
+ def sws_name ()
312
+ return @attributes["sws"]
313
+ end
314
+
315
+
316
+ # Returns true if the receiver represents a HTML tag
317
+ def tag? ()
318
+ return @name != nil && @data != nil
319
+ end
320
+
321
+
322
+ # Returns true if the receiver is a purely text one
323
+ def text? ()
324
+ return @name == nil
325
+ end
326
+
327
+
328
+ # Returns true if the receiver is a HTML comment
329
+ def comment? ()
330
+ return @comment
331
+ end
332
+
333
+
334
+ # Returns true if the receiver is a standalone tag
335
+ def standalone? ()
336
+ return @standalone
337
+ end
338
+
339
+
340
+ # Returns string representation of the receiver
341
+ def to_s ()
342
+
343
+ if( comment? )
344
+ return "comment"
345
+ elsif ( text? )
346
+ return "text"
347
+ elsif( tag? )
348
+ return "#{data}"
349
+ else
350
+ attrs = @attributes.to_a.join( "| " )
351
+ return "#{@name} #{attrs}"
352
+ end
353
+
354
+ end
355
+
356
+ # Dumps the token recursively in a tree form.
357
+ def dump ( prefix )
358
+ if( comment? )
359
+ puts "#{prefix}comment"
360
+ elsif ( text? )
361
+ puts "#{prefix}text"
362
+ elsif( tag? )
363
+ puts "#{prefix}#{data}"
364
+ @children.each { |child| child.dump( prefix + " " ) }
365
+ else
366
+ attrs = @attributes.to_a.join( "| " )
367
+ puts "#{prefix}#{@name} #{attrs}"
368
+ @children.each { |child| child.dump( prefix + " " ) }
369
+ end
370
+ end
371
+
372
+ end
373
+
374
+ end