docgenerator 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,570 @@
1
+ class Object
2
+ alias :old_is_a? :is_a?
3
+ #Redefine the standard is_a?
4
+ #Returns also true, if one of the ids is in testvalu
5
+ def is_a?( testvalue)
6
+ if testvalue.old_is_a?(Symbol)
7
+ if old_is_a?(Element)
8
+ return ids.include?(testvalue)
9
+ else
10
+ return false
11
+ end
12
+ else
13
+ return old_is_a?(testvalue)
14
+ end
15
+ end
16
+ end
17
+ #This class defines possible elements of a document.
18
+ #For each type of elements a class is defined.
19
+ #The definition can be done explicite or generic with Element.create.
20
+ #
21
+ #All types of elements are stored in a hash,
22
+ #the elements can be created via the method Element.get (or element() ).
23
+ #
24
+ #Tracing
25
+ #Often there is the problem, that you get an error when you write the element.
26
+ #But the error is done, when the element is created.
27
+ #When you redefine ELEMENTS_TRACE, some tracing information are stored during creation.
28
+ class Element
29
+ include Backlinks
30
+ #Hash with all ids and their corresponding classes.
31
+ @@ids = Hash.new( )
32
+ #All attributes are stored in this hash.
33
+ #The key is the corresponding class.
34
+ @@attr = { Element => {} }
35
+ #All output routines are stored in this hash.
36
+ #The key is the corresponding class.
37
+ @@log = false
38
+ def Element.log=( log )
39
+ @@log = log
40
+ end
41
+ #Flag, if the calling stack should be analysed for each Element
42
+ #Only for document debbuging
43
+ @@trace = nil
44
+ #Set Element tracing on/off
45
+ def Element.trace=( val )
46
+ @@trace = val
47
+ end
48
+
49
+ SUPPORTED_TARGETS = [ :latex, :html, :wiki, :text, :debug ]
50
+
51
+ #Return a element class.
52
+ def Element.get( name )
53
+ return @@ids[name]
54
+ end
55
+ #This method is called, when a subclass of this class is defined.
56
+ #This can happen via Element.create or in another programm.
57
+ #
58
+ #Add subclasses to the list of all element classes and initialize some internal hashes.
59
+ def Element.inherited( subclass )
60
+ puts "New subclass for 'Element' created: '#{subclass}'" if @@log
61
+ #All attributes of a class are collected in a hash.
62
+ @@attr[subclass] = {}
63
+ #Fill @@ids
64
+ #There is no possibility to give a list of id's or names to a 'inherit'-method.
65
+ #So a special method Element.add is used.
66
+ Element.add( [subclass], subclass )
67
+ end
68
+ #Add a new element class or assign another name (synonym) for a class.
69
+ #The initial call is made by Element.inherited.
70
+ def Element.add( idlist, elementClass )
71
+ if ! elementClass.new.kind_of?( Element )
72
+ raise "Element.add get no Element-class #{elementClass}"
73
+ end
74
+ if ! idlist.kind_of?( Array )
75
+ idlist = [ idlist ]
76
+ end
77
+ idlist.each{|id|
78
+ if @@ids[ id ] == nil
79
+ puts "Element class '#{id}' is new: #{elementClass}" if @@log
80
+ #All new ids are collected
81
+ @@ids[ id ] = elementClass
82
+ elsif @@ids[ id ] == elementClass
83
+ puts "Element class '#{id}' is defined double: #{elementClass}" if @@log
84
+ else
85
+ puts "Error: ID '#{id}' is redefined #{@@ids[id]} -> #{elementClass}"
86
+ @@ids[id] = elementClass
87
+ end
88
+ }
89
+ end
90
+
91
+ #Generic creation of a new class to define a new element.
92
+ #- name must be defined.
93
+ #- attr is a hash with all attributes.
94
+ # For details see Element.add_attributes
95
+ #- content is a flag, which defines if the element contains "content".
96
+ # Valid values are:
97
+ # - true: content available and necessary (e.g. <p>)
98
+ # - :empty_ok: content available, but not necessary (e.g. <td> or iframe)
99
+ # - false: no content, no endtag, but tag closed with / (example: <br />)
100
+ # - nil: no content, no endtag, no closing (example: <br>)
101
+ #- output contains a hash with logic, how to handle the output.
102
+ # -:htmltag Tag for html-output.
103
+ # -:latex Template for LaTeX-output
104
+ # -:html Template for HTML-output
105
+ def Element.create( name, attr = {}, content = false, output = {} )
106
+ #First some checks
107
+ if ! attr.kind_of?( Hash )
108
+ raise "Type error Element.create: Expected Hash, get #{attr.class}"
109
+ end
110
+ #Generic class creation
111
+ elementclass = Class.new( Element )
112
+ #Add the id of the new class to central collection.
113
+ Element.add( name, elementclass )
114
+
115
+ #Set a flag, if the class can contain 'content'.
116
+ #-true: content available and necessary (e.g. <p>)
117
+ #-:empty_ok: content available, but not necessary (e.g. <td>)
118
+ #-false: no content, no endtag, but tag closed with / (example: <br />)
119
+ #-nil: no content, no endtag, no closing (example: <br>)
120
+ elementclass.class_eval( %Q|
121
+ def content?()
122
+ return #{content.inspect}
123
+ end|)
124
+
125
+ output.each{ |k,v|
126
+ case k
127
+ when :latex
128
+ elementclass.add_output( :latex, v )
129
+ when :html
130
+ elementclass.add_output( :html, v )
131
+ when :text
132
+ elementclass.add_output( :text, v )
133
+ when :wiki
134
+ elementclass.add_output( :wiki, v )
135
+ when :htmltag
136
+ if v
137
+ elementclass.class_eval( "def htmltag()\n'#{v}'\nend" )
138
+ else
139
+ elementclass.class_eval( "def htmltag()\nnil\nend" )
140
+ end
141
+ else
142
+ puts "Unknown format #{k} for #{name}"
143
+ end
144
+ }
145
+ elementclass.add_attributes( attr )
146
+ #Return the class.
147
+ return elementclass
148
+ end
149
+ #Return all id's of the class.
150
+ #This method is expanded in Element.add.
151
+ def ids( )
152
+ myids = []
153
+ @@ids.each{|k,v| myids << k if v == self.class }
154
+ return myids
155
+ end
156
+ #Prepares an overview on all Elements.
157
+ #Can be used for some debugging.
158
+ def Element.overview( list = @@ids.values.uniq )
159
+ if ! list.kind_of?(Array)
160
+ list = [ list ]
161
+ end
162
+ result = '=' * 10 + "\nElement overview:"
163
+ list.each{ |k|
164
+ result += "\n#{k}\n"
165
+ result += "\tId's:\t\t"
166
+ result += k.new.ids.join("\n\t\t\t")
167
+ result += "\n\tAttributes:\t"
168
+ result += "can contain "
169
+ result += "no " if ! k.new.content?
170
+ result += "content\n\t\t\t"
171
+ @@attr[k].each{ |k2,v|
172
+ result += "#{k2}:\t#{v.inspect}\n\t\t\t"
173
+ }
174
+ }
175
+ return result
176
+ end
177
+ #Add possible attributes to an element class.
178
+ #Attributes are defined in a hash, each key get a array with values.
179
+ #The values can be:
180
+ #- OBLIGATORY/OPTIONAL
181
+ #- Alias (this allows the usage of a position and a name for a parameter).
182
+ #- allowed elements ( e.g. list content only list items)
183
+ #Problem: Attributes are not inherited. See Element.get_attribute_list
184
+ #
185
+ #This method is called from a subclass.
186
+ #Fixme: add_attributes overwrites everything.
187
+ def Element.add_attributes( attr )
188
+ elattr = @@attr[self] #self is the class
189
+ attr.each{ |k,a |
190
+ elattr[k] = a
191
+ }
192
+ end
193
+ #Returns list of attributes.
194
+ #Can be used for copying attributes from one class to another class.
195
+ def Element.get_attribute_list( elementclass )
196
+ return @@attr[elementclass]
197
+ end
198
+ #Add an output routine.
199
+ def Element.add_output( target, string )
200
+ case target
201
+ when :latex
202
+ cmd = "def to_latex()\n"
203
+ when :html
204
+ cmd = "def to_html()\n"
205
+ when :text
206
+ cmd = "def to_text()\n"
207
+ when :wiki
208
+ cmd = "def to_wiki()\n"
209
+ else
210
+ puts "#{self}: Undefined target format #{target}"
211
+ end
212
+ if ! string.kind_of?( String )
213
+ puts "Error element <#{self.new.ids}>: #{string.inspect} is no String"
214
+ end
215
+ template = string.gsub(/\\/, '\\\\\\')
216
+ template.gsub!(/"/, '\"')
217
+ cmd += "\"#{template}\"\n"
218
+ cmd += "end\n"
219
+ class_eval( cmd )
220
+ end
221
+ #Defines an element.
222
+ #
223
+ #There are two ways to add values:
224
+ #- "named" values in an hash.
225
+ # This named values can be attributes of a HTML-Tag or Parameters of a TeX-Makro.
226
+ # There is a check, if the attribute exist.
227
+ #- Content. Values "inside" the element.
228
+ # The content can be the content of a HTML-Tag or the content of a TeX-Environment.
229
+ # There is a check, if this class allows content.
230
+ def initialize( attr={}, content = nil)
231
+ #@attr is a hash containing the values for each attribute.
232
+ @attr = Hash.new( )
233
+ if self.content?()
234
+ @content = []
235
+ else
236
+ @content = nil
237
+ end
238
+ @part_of = [] #list of elements, where this element is part of (normaly only one element)
239
+ @part_of_doc = [] #list of documents, where this element is part of (normaly only one element)
240
+ @crbefore = false #make \n before element
241
+ @crmid = false #make \n before and after opening/closing html-tag
242
+ @crafter = false #make \n after element
243
+ #List of targets, where the element can be used.
244
+ #Details see method restrict_to()
245
+ #Default: allsupported targets
246
+ @targets = SUPPORTED_TARGETS.dup
247
+ @suppressed_targets = []
248
+
249
+ #Initialize the attribute hash.
250
+ @@attr[self.class].each{ |k,a|
251
+ #type check on Attribute does not work .
252
+ if a.kind_of?( Symbol )
253
+ #Filled in the second run
254
+ #~ @attr[k] = @attr[a]
255
+ elsif a #.kind_of?( Attribute )
256
+ @attr[k] = a.new( k, self )
257
+ else
258
+ @attr[k] = Attribute.create().new( k, self )
259
+ end
260
+ }
261
+ #Assign Alias-Attributes
262
+ @@attr[self.class].each{ |k,a|
263
+ next if !a.kind_of?( Symbol )
264
+ if @attr[a]
265
+ @attr[k] = @attr[a]
266
+ else
267
+ puts "Undefined Alias-Attribute #{k}"
268
+ @attr[k] = Attribute.create().new( k, self )
269
+ end
270
+ }
271
+ attr.each{ |k,v|
272
+ if @attr[k]
273
+ @attr[k] << v
274
+ else
275
+ puts "Usage of unknown attribute '#{k}' in '#{self.ids.join(',')}'"
276
+ end
277
+ }
278
+ @called_by = prepare_tracing_info( @@trace )
279
+ self << content if content
280
+ end
281
+ #List (array) of targets, where the element can be used.
282
+ #Default are all supported targets.
283
+ #
284
+ #With restrictions of this attribute, elements can be restricted on special documents.
285
+ def restrict_to( *argv )
286
+ @targets = []
287
+ @suppressed_targets = SUPPORTED_TARGETS.dup
288
+ puts "Element#restrict_to: #{self.inspect} restrict to #{argv.inspect}" if @@log
289
+ argv.each{ |arg|
290
+ #~ if ! SUPPORTED_TARGETS.include?( arg )
291
+ if ! @suppressed_targets.delete( arg )
292
+ puts "Restriction for unsupported target #{arg}" if @@log
293
+ end
294
+ @targets << arg
295
+ }
296
+ self
297
+ end
298
+ #Only for debbuging reasons.
299
+ #Should contain the first "no docgenerator.rb-place, where part is called
300
+ #if @@trace is on, some tracing information is stored during creation.
301
+ def prepare_tracing_info( doit = @@trace )
302
+ return nil if ! doit
303
+ caller().each{|c|
304
+ next if ( /DocumentGenerator/ =~ c )
305
+ return c
306
+ }
307
+ #--> No calling stack found --> Error
308
+ puts '----'*10
309
+ puts 'Element#prepare_tracing_info: Found no starting place in caller stack'
310
+ caller().each{|c| puts c }
311
+ puts '----'*10
312
+ return caller()
313
+ end
314
+ def inspect()
315
+ if @@trace
316
+ called_by = ", Created in #{@called_by}"
317
+ else
318
+ called_by = nil #'unknown. Please set Elements.trace if needed'
319
+ end
320
+ return "<Class Element(#{self.ids.join(',')})#{called_by}>"
321
+ #~ if @content
322
+ #~ return "<Class Element(#{self.ids.join(',')}) @content=#{@content.inspect}>"
323
+ #~ else
324
+ #~ return "<Class Element(#{self.ids.join(',')}) @attr=#{@attr.inspect}>"
325
+ #~ end
326
+ end
327
+ #Accessor on content
328
+ attr_reader :content
329
+ #Add something to the content.
330
+ #Only possible on elements, which supports "content".
331
+ def << ( content )
332
+ if @content
333
+ @content << content
334
+ else
335
+ puts "Add content to an element without this feature (#{self.ids}, #{content.inspect}"
336
+ #~ @site.add2log( :error, "Add content to an element without this feature (#{self.ids}, #{content.inspect}" )
337
+ end
338
+ set_backlink( content )
339
+ end
340
+
341
+ #Add content after a target (other content)
342
+ # insertafter(target,*obj)
343
+ #Requires the ability of "content".
344
+ def insertafter(target,*obj)
345
+ self.insert(target, 1, obj)
346
+ end
347
+ #Add content before a target (other content)
348
+ # insertbefore(target,*obj)
349
+ #Requires the ability of "content".
350
+ def insertbefore(target,*obj)
351
+ self.insert(target, 0, obj)
352
+ end
353
+ #Insert content relative to a given target.
354
+ #The target must exist, pos defines the relative position (0=before, 1=one after the element).
355
+ #Method called by Element#insertbefore and Element#insertafter.
356
+ def insert(target,pos,*obj )
357
+ if @content
358
+ if @content.include?( target )
359
+ @content[@content.index(target)+pos ,0] = obj
360
+ else
361
+ puts "Insert content not possible. Reference object does not exist (#{target.inspect})"
362
+ end
363
+ else
364
+ puts "Add content to an element without this feature"
365
+ end
366
+ self
367
+ end
368
+ #Delete the given object.
369
+ def delete(target )
370
+ if @content
371
+ @content.delete( target )
372
+ else
373
+ puts "Delete content of an element without this feature"
374
+ end
375
+ self
376
+ end
377
+ #Insert an empty line after the last entry
378
+ def cr()
379
+ @crafter = true
380
+ self
381
+ end
382
+ #Insert an empty line before and after the last entry
383
+ def Cr()
384
+ @crbefore = true
385
+ @crafter = true
386
+ self
387
+ end
388
+ #Insert an empty line after the last entry and after the opening of the tag.
389
+ #This feature has it reason for lists, when each :li-tag gets a cr.
390
+ #The first item gets also a cr.
391
+ def cR()
392
+ @crbefore = false
393
+ @crmid = true
394
+ @crafter = true
395
+ self
396
+ end
397
+ #Insert an empty line before and after the last entry and after the opening of the tag.
398
+ def CR()
399
+ @crbefore = true
400
+ @crmid = true
401
+ @crafter = true
402
+ self
403
+ end
404
+ #Accessor on attribute list
405
+ attr_reader :attr
406
+ #Return an attribute to add content.
407
+ def [] (key)
408
+ attr = @attr[key]
409
+ if !attr
410
+ puts "Request unknown attribute '#{key}', return a dummy"
411
+ attr = Attribute.create( ).new(key, self)
412
+ end
413
+ return attr
414
+ end
415
+ #Default setting, each element class may contain contents.
416
+ #Can be redefined by Elements.create.
417
+ #-true: content available and necessary (e.g. <p>)
418
+ #-:empty_ok: content available, but not necessary (e.g. <td>)
419
+ #-false: no content, no endtag, but tag closed with / (example: <br />)
420
+ #-nil: no content, no endtag, no closing (example: <br>)
421
+ def content?()
422
+ return true
423
+ end
424
+ #check if there is content assigned.
425
+ def empty?()
426
+ return @content.empty?
427
+ end
428
+ @@level = 0
429
+ #Build a String to be used for the target document.
430
+ #Calls Element#to_latex and Element#to_html.
431
+ def to_s( target = Document.target( self ) )
432
+ #Return empty string, if target is not requested.
433
+ if ! @targets.include?( target )
434
+ if @suppressed_targets.include?(target)
435
+ puts "Element#to_s: Content of #{self.inspect} suppressed for Target #{target.inspect}." if @@log
436
+ else
437
+ puts "Element#to_s: Target #{target.inspect} not supported for #{self.inspect}." if @@log
438
+ end
439
+ return ''
440
+ end
441
+ #Some checks
442
+ @attr.each{|k,v|
443
+ if v.required? and v.to_s == '' and v.settings.include?(target)
444
+ puts "#{self.ids}: Attribut '#{k}' without required value"
445
+ end
446
+ }
447
+ #Build the string.
448
+ result = ''
449
+ case target
450
+ when :latex
451
+ result = to_latex()
452
+ when :html
453
+ result = to_html()
454
+ when :wiki
455
+ result = to_wiki()
456
+ when :text
457
+ result = to_text()
458
+ when :debug
459
+ @@level += 1
460
+ result = "\n<element #{self.ids.join(',')} begin>"
461
+ @attr.each{|k,v|
462
+ #result += "\n\t#{k}: #{v.inspect}"
463
+ if v != []
464
+ result += "\n\t#{k}:"
465
+ v.each{|v2|
466
+ result += v2.to_s
467
+ }
468
+ end
469
+ }
470
+ result += @content.to_s
471
+ result += "\n<element #{self.ids.join(',')} end>"
472
+ @@level -= 1
473
+ result.gsub!(/^/, "\t" * @@level )
474
+ else
475
+ puts "Undefined target format '#{target}'"
476
+ end
477
+ #Already added in submethods
478
+ #~ result = "\n#{result}" if @crbefore
479
+ #~ result << "\n" if @crafter
480
+ return result
481
+ end
482
+ #This is a dummy method, called from Element#to_s for the target format LaTeX.
483
+ #This method must be overwritten from the element class.
484
+ #
485
+ #By default, the concatenation of all ids and the content is taken.
486
+ def to_latex()
487
+ puts "Missing output routine for LaTeX (#{self.ids.join(',')}) [#{@called_by}]" if @@log
488
+ cmd = ''
489
+ cmd << "\n" if @crbefore
490
+ cmd << "\\#{self.ids}{#{@content}}"
491
+ cmd << "\n" if @crafter
492
+ return cmd
493
+ end
494
+ #Method for definition inside Element.create.
495
+ def linebreak( flag = true )
496
+ flag ? "\n" : ''
497
+ end
498
+ #Prints the optional attribut key inside []. If empty, nil is returned
499
+ def texoptional(key)
500
+ return nil if ! @attr[key].filled?
501
+ return "[#{@attr[key]}]"
502
+ end
503
+ #Build key-val options from attributs.
504
+ #Used e.g. by includegraphics.
505
+ def texkeyval()
506
+ keyval = []
507
+ @attr.sort_by{|k,v| v.sortkey }.each{|k,v|
508
+ keyval << "#{k}={#{v}}" if v.to_s != '' and v.texkeyval?
509
+ }
510
+ return keyval.join(', ')
511
+ end
512
+ #Method for html-output.
513
+ #If the method is redefined, the result is taken as a tag.
514
+ def htmltag()
515
+ ''
516
+ end
517
+ #This is a dummy method, called from Element#to_s for the target format HTML.
518
+ #The tag from Element.htmltag is taken and all attributes and the content are used.
519
+ #
520
+ #This method can be overwriten from the element class.
521
+ def to_html()
522
+ tag = htmltag()
523
+ if ! tag
524
+ puts "No HTML element available (#{self.ids.join(',')})" if @@log
525
+ return ''
526
+ elsif tag == ''
527
+ tag = 'span'
528
+ puts "Missing output routine for HTML (#{self.ids.join(',')}) [#{@called_by}]" if @@log
529
+ end
530
+ #Test if the HTML-Tag should contain something, but there is nothing.
531
+ #If :empty_ok it is ok, that there is no content (e.g <td>)
532
+ #May make problems, if an empty tag is used to set a id-mark.
533
+ if content?() and content? != :empty_ok and
534
+ ( ! @content or @content == [] or @content == [nil] )
535
+ puts "HTML-Tag #{tag} without content -> ignored" if @@log
536
+ return ''
537
+ end
538
+ html = String.new()
539
+ html << "\n" if @crbefore
540
+ html << "<#{tag} "
541
+ @attr.sort_by{|k,v| v.sortkey }.each{|k,v|
542
+ html << "#{k} = \"#{v}\" " if v.to_s != '' and v.html?
543
+ }
544
+ if @content
545
+ html << ">"
546
+ html <<"\n" if @crmid
547
+ html << "#{@content}"
548
+ html << "\n" if @crmid and @content.size > 0 and html[-1,1] != "\n"
549
+ html << "</#{tag}>"
550
+ elsif content?() == nil
551
+ html << '>'
552
+ else
553
+ html << '/>'
554
+ end
555
+ html << "\n" if @crafter
556
+ return html
557
+ end #to_html
558
+ def to_wiki()
559
+ puts "Missing output routine for Wiki (#{self.ids.join(',')}) [#{@called_by}]" if @@log
560
+ return "#{@content}\n"
561
+ end
562
+ def to_text()
563
+ puts "Missing output routine for Text (#{self.ids.join(',')}) [#{@called_by}]" if @@log
564
+ text = String.new()
565
+ text << "\n" if @crbefore
566
+ text = "#{@content}".strip
567
+ text << "\n" if @crafter
568
+ return text
569
+ end
570
+ end