docgenerator 0.1.1

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.
@@ -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