Parent

Included Modules

Class Index [+]

Quicksearch

TaskJuggler::KeywordDocumentation

The textual TaskJuggler Project description consists of many keywords. The parser has built-in support to document the meaning and usage of these keywords. Most keywords are unique, but there can be exceptions. To resolve ambiguoties the keywords can be prefixed by a scope. The scope is usually a keyword that describes the context that the ambiguous keyword is used in. This class stores the keyword, the corresponding TextParser::Pattern and the context that the keyword is used in. It also stores information such as the list of optional attributes (keywords used in the context of the current keyword) and whether the keyword is scenario specific or not.

Attributes

keyword[R]
names[R]
pattern[R]
references[R]
optionalAttributes[R]
contexts[RW]
scenarioSpecific[RW]
inheritedFromProject[RW]
inheritedFromParent[RW]
predecessor[RW]
successor[RW]

Public Class Methods

new(rule, pattern, syntax, args, optAttrPatterns, manual) click to toggle source

Construct a new KeywordDocumentation object. rule is the TextParser::Rule and pattern is the corresponding TextParser::Pattern. syntax is an expanded syntax representation of the pattern. args is a Array of ParserTokenDoc that describe the arguments of the pattern. optAttrPatterns is an Array with references to TextParser::Patterns that are optional attributes to this keyword.

    # File lib/taskjuggler/KeywordDocumentation.rb, line 46
46:     def initialize(rule, pattern, syntax, args, optAttrPatterns, manual)
47:       @messageHandler = MessageHandler.new(true)
48:       @rule = rule
49:       @pattern = pattern
50:       # The unique identifier. Usually the attribute or property name. To
51:       # disambiguate a .<scope> can be added.
52:       @keyword = pattern.keyword
53:       # Similar to @keyword, but without the scope. Since there could be
54:       # several, this is an Array of String objects.
55:       @names = []
56:       @syntax = syntax
57:       @args = args
58:       @manual = manual
59:       # Hash that maps patterns of optional attributes to a boolean value. It
60:       # is true if the pattern is a scenario specific attribute.
61:       @optAttrPatterns = optAttrPatterns
62:       # The above hash is later converted into a list that points to the
63:       # keyword documentation of the optional attribute.
64:       @optionalAttributes = []
65:       @scenarioSpecific = false
66:       @inheritedFromProject= false
67:       @inheritedFromParent = false
68:       @contexts = []
69:       @seeAlso = []
70:       # The following are references to the neighboring keyword in an
71:       # alphabetically sorted list.
72:       @predecessor = nil
73:       @successor = nil
74:       # Array to collect all references to other RichText objects.
75:       @references = []
76:     end

Public Instance Methods

computeInheritance(keywords, rules) click to toggle source
     # File lib/taskjuggler/KeywordDocumentation.rb, line 145
145:     def computeInheritance(keywords, rules)
146:       property = nil
147:       @contexts.each do |kwd|
148:         if %( task resource account report shift scenario).include?(kwd.keyword)
149:           property = kwd.keyword
150:           break
151:         end
152:       end
153:       if property
154:         project = Project.new('id', 'dummy', '1.0', nil)
155:         propertySet = case property
156:                       when 'task'
157:                         project.tasks
158:                       when 'resource'
159:                         project.resources
160:                       when 'account'
161:                         project.accounts
162:                       when 'report'
163:                         project.reports
164:                       when 'shift'
165:                         project.shifts
166:                       when 'scenario'
167:                         project.scenarios
168:                       end
169:         keyword = @keyword
170:         keyword = keyword.split('.')[0] if keyword.include?('.')
171:         @inheritedFromProject = propertySet.inheritedFromProject?(keyword)
172:         @inheritedFromParent = propertySet.inheritedFromParent?(keyword)
173:       end
174:     end
crossReference(keywords, rules) click to toggle source

Post process the class member to set cross references to other KeywordDocumentation items.

     # File lib/taskjuggler/KeywordDocumentation.rb, line 101
101:     def crossReference(keywords, rules)
102:       # Get the attribute or property name of the Keyword. This is not unique
103:       # like @keyword since it's got no scope.
104:       @pattern.terminalTokens(rules).each do |tok|
105:         # Ignore patterns that don't have a real name.
106:         break if tok[0] == '{'
107: 
108:         @names << tok[0]
109:       end
110: 
111:       # Some arguments are references to other patterns. The current keyword
112:       # is added as context to such patterns.
113:       @args.each do |arg|
114:         if arg.pattern && checkReference(arg.pattern)
115:           kwd = keywords[arg.pattern.keyword]
116:           kwd.contexts << self unless kwd.contexts.include?(self)
117:         end
118:       end
119: 
120:       # Optional attributes are treated similarly. In addition we add them to
121:       # the @optionalAttributes list of this keyword.
122:       @optAttrPatterns.each do |pattern, scenarioSpecific|
123:         next unless checkReference(pattern)
124: 
125:         if (kwd = keywords[pattern.keyword]).nil?
126:           token = pattern.terminalToken(rules)
127:           $stderr.puts "Keyword #{keyword} has undocumented optional " +
128:                        "attribute #{token[0]}"
129:         else
130:           @optionalAttributes << kwd
131:           kwd.contexts << self unless kwd.contexts.include?(self)
132:           kwd.scenarioSpecific = true if scenarioSpecific
133:         end
134:       end
135: 
136:       # Resolve the seeAlso patterns to keyword references.
137:       @pattern.seeAlso.sort.each do |also|
138:         if keywords[also].nil?
139:           raise "See also reference #{also} of #{@pattern} is unknown"
140:         end
141:         @seeAlso << keywords[also]
142:       end
143:     end
generateHTML(directory) click to toggle source

Return a String that represents the keyword documentation in an XML formatted form.

     # File lib/taskjuggler/KeywordDocumentation.rb, line 309
309:     def generateHTML(directory)
310:       html = HTMLDocument.new(:strict)
311:       head = html.generateHead(keyword,
312:                                { 'description' => 'The TaskJuggler Manual',
313:                                  'keywords' =>
314:                                  'taskjuggler, project, management' })
315:       head << @manual.generateStyleSheet
316: 
317:       html.html << BODY.new do
318:         [
319:           @manual.generateHTMLHeader,
320:           generateHTMLNavigationBar,
321: 
322:           DIV.new('style' => 'margin-left:5%; margin-right:5%') do
323:             [
324:               generateHTMLKeywordBox,
325:               generateHTMLDescriptionBox,
326:               generateHTMLOptionalAttributesBox,
327:               generateHTMLExampleBox
328:             ]
329:           end,
330:           generateHTMLNavigationBar,
331:           @manual.generateHTMLFooter
332:         ]
333:       end
334: 
335:       if directory
336:         html.write(directory + "#{keyword}.html")
337:       else
338:         puts html.to_s
339:       end
340:     end
globalScope?() click to toggle source

Returns true of the keyword can be used outside of any other keyword context.

    # File lib/taskjuggler/KeywordDocumentation.rb, line 91
91:     def globalScope?
92:       return true if @contexts.empty?
93:       @contexts.each do |context|
94:         return true if context.keyword == 'properties'
95:       end
96:       false
97:     end
isProperty?() click to toggle source

Returns true of the KeywordDocumentation is documenting a TJP property (task, resources, etc.). A TJP property can be nested.

    # File lib/taskjuggler/KeywordDocumentation.rb, line 80
80:     def isProperty?
81:       # I haven't found a good way to automatically detect all the various
82:       # report types as properties. The non-nestable ones need to be added
83:       # manually here.
84:       return true if %( export nikureport timesheetreport statussheetreport).
85:                      include?(keyword)
86:       @optionalAttributes.include?(self)
87:     end
title() click to toggle source

Return the keyword name in a more readable form. E.g. ‘foo.bar’ is returned as ‘foo (bar)’. ‘foo’ will remain ‘foo’.

     # File lib/taskjuggler/KeywordDocumentation.rb, line 178
178:     def title
179:       kwTokens = @keyword.split('.')
180:       if kwTokens.size == 1
181:         title = @keyword
182:       else
183:         title = "#{kwTokens[0]} (#{kwTokens[1]})"
184:       end
185:       title
186:     end
to_s() click to toggle source

Return the complete documentation of this keyword as formatted text string.

     # File lib/taskjuggler/KeywordDocumentation.rb, line 190
190:     def to_s
191:       tagW = 13
192:       textW = 79
193: 
194:       # Top line with multiple elements
195:       str = "Keyword:     #{@keyword}     " +
196:             "Scenario Specific: #{@scenarioSpecific ? 'Yes' : 'No'}    " +
197:             "Inherited: #{@inheritedFromParent ? 'Yes' : 'No'}\n\n"
198: 
199:       str += "Purpose:     #{format(tagW, newRichText(@pattern.doc).to_s,
200:                                     textW)}\n\n"
201: 
202:       if @syntax != '[{ <attributes> }]'
203:         str += "Syntax:      #{format(tagW, @syntax, textW)}\n\n"
204: 
205:         str += "Arguments:   "
206:         if @args.empty?
207:           str += format(tagW, "none\n\n", textW)
208:         else
209:           argStr = ''
210:           @args.each do |arg|
211:             argText = newRichText(arg.text ||
212:               "See '#{arg.name}' for details.").to_s
213:             if arg.typeSpec.nil? || ("<#{arg.name}>") == arg.typeSpec
214:               indent = arg.name.length + 2
215:               argStr += "#{arg.name}: " +
216:                         "#{format(indent, argText, textW - tagW)}\n"
217:             else
218:               typeSpec = arg.typeSpec
219:               typeSpec[0] = '['
220:               typeSpec[1] = ']'
221:               indent = arg.name.length + typeSpec.size + 3
222:               argStr += "#{arg.name} #{typeSpec}: " +
223:                         "#{format(indent, argText, textW - tagW)}\n"
224:             end
225:           end
226:           str += indent(tagW, argStr)
227:         end
228:         str += "\n"
229:       end
230: 
231:       str += 'Context:     '
232:       if @contexts.empty?
233:         str += format(tagW, 'Global scope', textW)
234:       else
235:         cxtStr = ''
236:         @contexts.each do |context|
237:           unless cxtStr.empty?
238:             cxtStr += ', '
239:           end
240:           cxtStr += context.keyword
241:         end
242:         str += format(tagW, cxtStr, textW)
243:       end
244: 
245:       str += "\n\nAttributes:  "
246:       if @optionalAttributes.empty?
247:         str += "none\n\n"
248:       else
249:         attrStr = ''
250:         @optionalAttributes.sort! do |a, b|
251:           a.keyword <=> b.keyword
252:         end
253:         showLegend = false
254:         @optionalAttributes.each do |attr|
255:           unless attrStr.empty?
256:             attrStr += ', '
257:           end
258:           attrStr += attr.keyword
259:           if attr.scenarioSpecific || attr.inheritedFromProject ||
260:              attr.inheritedFromParent
261:             first = true
262:             showLegend = true
263:             attrStr += '['
264:             if attr.scenarioSpecific
265:               attrStr += 'sc'
266:               first = false
267:             end
268:             if attr.inheritedFromProject
269:               attrStr += ':' unless first
270:               attrStr += 'ig'
271:               first = false
272:             end
273:             if attr.inheritedFromParent
274:               attrStr += ':' unless first
275:               attrStr += 'ip'
276:             end
277:             attrStr += ']'
278:           end
279:         end
280:         if showLegend
281:           attrStr += "\n\n[sc] : Attribute is scenario specific" +
282:                      "\n[ig] : Attribute is inherited from global attribute" +
283:                      "\n[ip] : Attribute is inherited from parent property"
284:         end
285:         str += format(tagW, attrStr, textW)
286:         str += "\n"
287:       end
288: 
289:       unless @seeAlso.empty?
290:         str += "See also:    "
291:         alsoStr = ''
292:         @seeAlso.each do |also|
293:           unless alsoStr.empty?
294:             alsoStr += ', '
295:           end
296:           alsoStr += also.keyword
297:         end
298:         str += format(tagW, alsoStr, textW)
299:         str += "\n"
300:       end
301: 
302:   #    str += "Rule:    #{@rule.name}\n" if @rule
303:   #    str += "Pattern: #{@pattern.tokens.join(' ')}\n" if @pattern
304:       str
305:     end

Private Instance Methods

checkReference(pattern) click to toggle source
     # File lib/taskjuggler/KeywordDocumentation.rb, line 344
344:     def checkReference(pattern)
345:       if pattern.keyword.nil?
346:         $stderr.puts "Pattern #{pattern} is undocumented but referenced by " +
347:                      "#{@keyword}."
348:         false
349:       end
350:       true
351:     end
format(indent, str, width) click to toggle source
     # File lib/taskjuggler/KeywordDocumentation.rb, line 405
405:     def format(indent, str, width)
406:       TextFormatter.new(width, indent).format(str)[indent..1]
407:     end
generateHTMLAlsoLine() click to toggle source
     # File lib/taskjuggler/KeywordDocumentation.rb, line 512
512:     def generateHTMLAlsoLine
513:       unless @seeAlso.empty?
514:         descr = []
515:         @seeAlso.each do |a|
516:           descr << ', ' unless descr.empty?
517:           descr << A.new('href' => "#{a.keyword}.html") { a.title }
518:         end
519:         generateHTMLTableLine('See also', descr)
520:       end
521:     end
generateHTMLArgumentsLine() click to toggle source
     # File lib/taskjuggler/KeywordDocumentation.rb, line 457
457:     def generateHTMLArgumentsLine
458:       return nil unless @syntax != '[{ <attributes> }]'
459: 
460:       if @args.empty?
461:         generateHTMLTableLine('Arguments', 'none')
462:       else
463:         rows = []
464:         first = true
465:         @args.each do |arg|
466:           if first
467:             col1 = 'Arguments'
468:             col1rows = @args.length
469:             first = false
470:           else
471:             col1 = col1rows = nil
472:           end
473:           if arg.typeSpec.nil? || ('<' + arg.name + '>') == arg.typeSpec
474:             col2 = "#{arg.name}"
475:           else
476:             typeSpec = arg.typeSpec
477:             typeName = typeSpec[1..2]
478:             typeSpec[0] = '['
479:             typeSpec[1] = ']'
480:             col2 = [
481:               "#{arg.name} [",
482:               A.new('href' =>
483:                     "The_TaskJuggler_Syntax.html" +
484:                     "\##{typeName}") { typeName },
485:               ']'
486:             ]
487:           end
488:           col3 = newRichText(arg.text ||
489:                              "See [[#{arg.name}]] for details.").to_html
490:           rows << generateHTMLTableLine(col1, col2, col3, col1rows)
491:         end
492:         rows
493:       end
494:     end
generateHTMLContextLine() click to toggle source
     # File lib/taskjuggler/KeywordDocumentation.rb, line 496
496:     def generateHTMLContextLine
497:       if @contexts.empty?
498:         descr = A.new('href' =>
499:                       'Getting_Started.html#Structure_of_a_TJP_File') do
500:                         'Global scope'
501:                       end
502:       else
503:         descr = []
504:         @contexts.each do |c|
505:           descr << ', ' unless descr.empty?
506:           descr << A.new('href' => "#{c.keyword}.html") { c.title }
507:         end
508:       end
509:       generateHTMLTableLine('Context', descr)
510:     end
generateHTMLDescriptionBox() click to toggle source
     # File lib/taskjuggler/KeywordDocumentation.rb, line 425
425:     def generateHTMLDescriptionBox
426:       # Box with purpose, syntax, arguments and context.
427:       P.new do
428:         TABLE.new({ 'align' => 'center', 'class' => 'table' }) do
429:           [
430:             COLGROUP.new do
431:               [
432:                 COL.new('width' => '16%'),
433:                 COL.new('width' => '24%'),
434:                 COL.new('width' => '60%')
435:               ]
436:             end,
437:             generateHTMLPurposeLine,
438:             generateHTMLSyntaxLine,
439:             generateHTMLArgumentsLine,
440:             generateHTMLContextLine,
441:             generateHTMLAlsoLine
442:           ]
443:         end
444:       end
445:     end
generateHTMLExampleBox() click to toggle source
     # File lib/taskjuggler/KeywordDocumentation.rb, line 620
620:     def generateHTMLExampleBox
621:       if @pattern.exampleFile
622:         exampleDir = AppConfig.dataDirs('test')[0] + "TestSuite/Syntax/Correct/"
623:         example = TjpExample.new
624:         fileName = "#{exampleDir}/#{@pattern.exampleFile}.tjp"
625:         example.open(fileName)
626:         unless (text = example.to_s(@pattern.exampleTag))
627:           raise "There is no tag '#{@pattern.exampleTag}' in file " +
628:             "#{fileName}."
629:         end
630: 
631:         DIV.new('class' => 'codeframe') do
632:           PRE.new('class' => 'code') { text }
633:         end
634:       end
635:     end
generateHTMLKeywordBox() click to toggle source
     # File lib/taskjuggler/KeywordDocumentation.rb, line 409
409:     def generateHTMLKeywordBox
410:       # Box with keyword name.
411:       P.new do
412:         TABLE.new('align' => 'center', 'class' => 'table') do
413:           TR.new('align' => 'left') do
414:             [
415:               TD.new({ 'class' => 'tag',
416:                        'style' => 'width:16%'}) { 'Keyword' },
417:               TD.new({ 'class' => 'descr',
418:                        'style' => 'width:84%; font-weight:bold' }) { title }
419:             ]
420:           end
421:         end
422:       end
423:     end
generateHTMLNavigationBar() click to toggle source

Generate the navigation bar.

     # File lib/taskjuggler/KeywordDocumentation.rb, line 358
358:     def generateHTMLNavigationBar
359:       @manual.generateHTMLNavigationBar(
360:         @predecessor ? @predecessor.title : nil,
361:         @predecessor ? "#{@predecessor.keyword}.html" : nil,
362:         @successor ? @successor.title : nil,
363:         @successor ? "#{@successor.keyword}.html" : nil)
364:     end
generateHTMLOptionalAttributesBox() click to toggle source
     # File lib/taskjuggler/KeywordDocumentation.rb, line 537
537:     def generateHTMLOptionalAttributesBox
538:       # Box with attributes.
539:       unless @optionalAttributes.empty?
540:         @optionalAttributes.sort! do |a, b|
541:           a.keyword <=> b.keyword
542:         end
543: 
544:         showDetails = false
545:         @optionalAttributes.each do |attr|
546:           if attr.scenarioSpecific || attr.inheritedFromProject ||
547:              attr.inheritedFromParent
548:             showDetails = true
549:             break
550:           end
551:         end
552: 
553:         P.new do
554:           TABLE.new('align' => 'center', 'class' => 'table') do
555:             if showDetails
556:               # Table of all attributes with checkmarks for being scenario
557:               # specific, inherited from parent and inherited from global
558:               # scope.
559:               rows = []
560:               rows << COLGROUP.new do
561:                 [ 16, 24, 20, 20, 20 ].map { |p| COL.new('width' => "#{p}%") }
562:               end
563:               rows <<  TR.new('align' => 'left') do
564:                   [
565:                     TD.new('class' => 'tag',
566:                            'rowspan' => "#{@optionalAttributes.length + 1}") do
567:                       'Attributes'
568:                     end,
569:                     TD.new('class' => 'tag') { 'Name' },
570:                     TD.new('class' => 'tag') { 'Scen. spec.' },
571:                     TD.new('class' => 'tag') { 'Inh. fm. Global' },
572:                     TD.new('class' => 'tag') { 'Inh. fm. Parent' }
573:                   ]
574:               end
575: 
576:               @optionalAttributes.each do |attr|
577:                 rows << TR.new('align' => 'left') do
578:                   [
579:                     TD.new('align' => 'left', 'class' => 'descr') do
580:                       A.new('href' => "#{attr.keyword}.html") { attr.title }
581:                     end,
582:                     TD.new('align' => 'center', 'class' => 'descr') do
583:                       'x' if attr.scenarioSpecific
584:                     end,
585:                     TD.new('align' => 'center', 'class' => 'descr') do
586:                       'x' if attr.inheritedFromProject
587:                     end,
588:                     TD.new('align' => 'center', 'class' => 'descr') do
589:                       'x' if attr.inheritedFromParent
590:                     end
591:                   ]
592:                 end
593:               end
594:               rows
595:             else
596:               # Comma separated list of all attributes.
597:               TR.new('align' => 'left') do
598:                 [
599:                   TD.new('class' => 'tag', 'style' => 'width:16%') do
600:                     'Attributes'
601:                   end,
602:                   TD.new('class' => 'descr', 'style' => 'width:84%') do
603:                     list = []
604:                     @optionalAttributes.each do |attr|
605:                       list << ', ' unless list.empty?
606:                       list << A.new('href' => "#{attr.keyword}.html") do
607:                         attr.title
608:                       end
609:                     end
610:                     list
611:                   end
612:                 ]
613:               end
614:             end
615:           end
616:         end
617:       end
618:     end
generateHTMLPurposeLine() click to toggle source
     # File lib/taskjuggler/KeywordDocumentation.rb, line 447
447:     def generateHTMLPurposeLine
448:       generateHTMLTableLine('Purpose', newRichText(@pattern.doc).to_html)
449:     end
generateHTMLSyntaxLine() click to toggle source
     # File lib/taskjuggler/KeywordDocumentation.rb, line 451
451:     def generateHTMLSyntaxLine
452:       if @syntax != '[{ <attributes> }]'
453:         generateHTMLTableLine('Syntax', CODE.new { @syntax })
454:       end
455:     end
generateHTMLTableLine(col1, col2, col3 = nil, col1rows = nil) click to toggle source
     # File lib/taskjuggler/KeywordDocumentation.rb, line 523
523:     def generateHTMLTableLine(col1, col2, col3 = nil, col1rows = nil)
524:       TR.new('align' => 'left') do
525:         columns = []
526:         attrs = { 'class' => 'tag' }
527:         attrs['rowspan'] = col1rows.to_s if col1rows
528:         columns << TD.new(attrs) { col1 } if col1
529:         attrs = { 'class' => 'descr' }
530:         attrs['colspan'] = '2' unless col3
531:         columns << TD.new(attrs) { col2 }
532:         columns << TD.new('class' => 'descr') { col3 } if col3
533:         columns
534:       end
535:     end
indent(width, str) click to toggle source
     # File lib/taskjuggler/KeywordDocumentation.rb, line 353
353:     def indent(width, str)
354:       TextFormatter.new(80, width).indent(str)[width..1]
355:     end
keywordHTMLRef(parent, keyword) click to toggle source

Return a HTML object with a link to the manual page for the keyword.

     # File lib/taskjuggler/KeywordDocumentation.rb, line 367
367:     def keywordHTMLRef(parent, keyword)
368:       parent << XMLNamedText.new(keyword.title,
369:                                  'a', 'href' => "#{keyword.keyword}.html")
370:     end
listHTMLAttributes(list, width) click to toggle source

Utility function to turn a list of keywords into a comma separated list of HTML references to the files of these keywords. All embedded in a table cell element. list is the KeywordDocumentation list. width is the percentage width of the cell.

     # File lib/taskjuggler/KeywordDocumentation.rb, line 389
389:     def listHTMLAttributes(list, width)
390:       td = XMLElement.new('td', 'class' => 'descr',
391:                           'style' => "width:#{width}%")
392:       first = true
393:       list.each do |attr|
394:         if first
395:           first = false
396:         else
397:           td << XMLText.new(', ')
398:         end
399:         keywordHTMLRef(td, attr)
400:       end
401: 
402:       td
403:     end
newRichText(text) click to toggle source

This function is primarily a wrapper around the RichText constructor. It catches all RichTextScanner processing problems and converts the exception data into an error message.

     # File lib/taskjuggler/KeywordDocumentation.rb, line 375
375:     def newRichText(text)
376:       rText = RichText.new(text, [], @messageHandler)
377:       unless (rti = rText.generateIntermediateFormat)
378:         @messageHandler.error('rich_text',
379:                               "Error in RichText of rule #{@keyword}")
380:       end
381:       @references += rti.internalReferences
382:       rti
383:     end

Disabled; run with --debug to generate this.

[Validate]

Generated with the Darkfish Rdoc Generator 1.1.6.