Class TaskJuggler::KeywordDocumentation
In: lib/taskjuggler/KeywordDocumentation.rb
Parent: Object

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.

Methods

Included Modules

HTMLElements Term::ANSIColor

Attributes

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

Public Class methods

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 an Array of TextParser::TokenDoc that describe the arguments of the pattern. optAttrPatterns is an Array with references to TextParser::Patterns that are optional attributes to this keyword.

[Source]

# File lib/taskjuggler/KeywordDocumentation.rb, line 48
    def initialize(rule, pattern, syntax, args, optAttrPatterns, manual)
      @messageHandler = MessageHandler.new(true)
      @rule = rule
      @pattern = pattern
      # The unique identifier. Usually the attribute or property name. To
      # disambiguate a .<scope> can be added.
      @keyword = pattern.keyword
      # Similar to @keyword, but without the scope. Since there could be
      # several, this is an Array of String objects.
      @names = []
      @syntax = syntax
      @args = args
      @manual = manual
      # Hash that maps patterns of optional attributes to a boolean value. It
      # is true if the pattern is a scenario specific attribute.
      @optAttrPatterns = optAttrPatterns
      # The above hash is later converted into a list that points to the
      # keyword documentation of the optional attribute.
      @optionalAttributes = []
      @scenarioSpecific = false
      @inheritedFromProject= false
      @inheritedFromParent = false
      @contexts = []
      @seeAlso = []
      # The following are references to the neighboring keyword in an
      # alphabetically sorted list.
      @predecessor = nil
      @successor = nil
      # Array to collect all references to other RichText objects.
      @references = []
    end

Public Instance methods

[Source]

# File lib/taskjuggler/KeywordDocumentation.rb, line 152
    def computeInheritance(keywords, rules)
      property = nil
      @contexts.each do |kwd|
        if %w( task resource account shift scenario
               accountreport resourcereport taskreport textreport ).
               include?(kwd.keyword)
          property = kwd.keyword
          break
        end
      end
      if property
        project = Project.new('id', 'dummy', '1.0', nil)
        propertySet = case property
                      when 'task'
                        project.tasks
                      when 'resource'
                        project.resources
                      when 'account'
                        project.accounts
                      when 'shift'
                        project.shifts
                      when 'scenario'
                        project.scenarios
                      else
                        project.reports
                      end
        keyword = @keyword
        keyword = keyword.split('.')[0] if keyword.include?('.')
        @inheritedFromProject = propertySet.inheritedFromProject?(keyword)
        @inheritedFromParent = propertySet.inheritedFromParent?(keyword)
      end
    end

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

[Source]

# File lib/taskjuggler/KeywordDocumentation.rb, line 104
    def crossReference(keywords, rules)
      # Get the attribute or property name of the Keyword. This is not unique
      # like @keyword since it's got no scope.
      @pattern.terminalTokens(rules).each do |tok|
        # Ignore patterns that don't have a real name.
        break if tok[0] == '{'

        @names << tok[0]
      end

      # Some arguments are references to other patterns. The current keyword
      # is added as context to such patterns.
      @args.each do |arg|
        if arg.pattern && checkReference(arg.pattern)
          kwd = keywords[arg.pattern.keyword]
          kwd.contexts << self unless kwd.contexts.include?(self)
        end
      end

      # Optional attributes are treated similarly. In addition we add them to
      # the @optionalAttributes list of this keyword.
      @optAttrPatterns.each do |pattern, scenarioSpecific|
        next unless checkReference(pattern)

        # Check if all the attributes are documented. We ignore undocumented
        # keywords that are deprecated or removed.
        if (kwd = keywords[pattern.keyword]).nil?
          unless [ :deprecated, :removed ].include?(pattern.supportLevel)
            token = pattern.terminalTokens(rules)
            $stderr.puts "Keyword #{keyword} has undocumented optional " +
              "attribute #{token[0]}"
          end
        else
          @optionalAttributes << kwd
          kwd.contexts << self unless kwd.contexts.include?(self)
          kwd.scenarioSpecific = true if scenarioSpecific
        end
      end

      # Resolve the seeAlso patterns to keyword references.
      @pattern.seeAlso.sort.each do |also|
        if keywords[also].nil?
          raise "See also reference #{also} of #{@pattern} is unknown"
        end
        @seeAlso << keywords[also]
      end
    end

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

[Source]

# File lib/taskjuggler/KeywordDocumentation.rb, line 341
    def generateHTML(directory)
      html = HTMLDocument.new
      head = html.generateHead(keyword,
                               { 'description' => 'The TaskJuggler Manual',
                                 'keywords' =>
                                 'taskjuggler, project, management' })
      head << @manual.generateStyleSheet

      html.html << BODY.new do
        [
          @manual.generateHTMLHeader,
          generateHTMLNavigationBar,

          DIV.new('style' => 'margin-left:5%; margin-right:5%') do
            [
              generateHTMLKeywordBox,
              generateHTMLSupportLevel,
              generateHTMLDescriptionBox,
              generateHTMLOptionalAttributesBox,
              generateHTMLExampleBox
            ]
          end,
          generateHTMLNavigationBar,
          @manual.generateHTMLFooter
        ]
      end

      if directory
        html.write(directory + "#{keyword}.html")
      else
        puts html.to_s
      end
    end

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

[Source]

# File lib/taskjuggler/KeywordDocumentation.rb, line 94
    def globalScope?
      return true if @contexts.empty?
      @contexts.each do |context|
        return true if context.keyword == 'properties'
      end
      false
    end

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

[Source]

# File lib/taskjuggler/KeywordDocumentation.rb, line 82
    def isProperty?
      # I haven't found a good way to automatically detect all the various
      # report types as properties. They don't directly include themselves as
      # attributes.
      return true if %w( accountreport export nikureport resourcereport
                         taskreport textreport timesheetreport
                         statussheetreport).include?(keyword)
      @optionalAttributes.include?(self)
    end

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

[Source]

# File lib/taskjuggler/KeywordDocumentation.rb, line 187
    def title
      kwTokens = @keyword.split('.')
      if kwTokens.size == 1
        title = @keyword
      else
        title = "#{kwTokens[0]} (#{kwTokens[1]})"
      end
      title
    end

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

[Source]

# File lib/taskjuggler/KeywordDocumentation.rb, line 199
    def to_s
      tagW = 13
      textW = 79

      # Top line with multiple elements
      str = "#{blue('Keyword:')}     #{bold(@keyword)}\n\n"

      if @pattern.supportLevel != :supported
        msg = supportLevelMessage

        if [ :deprecated, :removed ].include?(@pattern.supportLevel) &&
           @seeAlso.length > 0
          msg += "\n\nPlease use "
          alsoStr = ''
          @seeAlso.each do |also|
            unless alsoStr.empty?
              alsoStr += ', '
            end
            alsoStr += also.keyword
          end
          msg += "#{alsoStr} instead!"
        end

        str += red("Warning:     #{format(tagW, msg, textW)}\n")
      end

      # Don't show further details if the keyword has been removed.
      return str if @pattern.supportLevel == :removed

      str += blue('Purpose:') +
             "     #{format(tagW, newRichText(@pattern.doc).to_s,
                                    textW)}\n"
      if @syntax != '[{ <attributes> }]'
        str += blue('Syntax:') + "      #{format(tagW, @syntax, textW)}\n"

        str += blue('Arguments:') + "   "
        if @args.empty?
          str += format(tagW, "none\n", textW)
        else
          argStr = ''
          @args.each do |arg|
            argText = newRichText(arg.text ||
              "See '#{arg.name}' for details.").to_s
            if arg.typeSpec.nil? || ("<#{arg.name}>") == arg.typeSpec
              indent = arg.name.length + 2
              argStr += "#{arg.name}: " +
                        "#{format(indent, argText, textW - tagW)}\n"
            else
              typeSpec = arg.typeSpec
              typeSpec[0] = '['
              typeSpec[-1] = ']'
              indent = arg.name.length + typeSpec.size + 3
              argStr += "#{arg.name} #{typeSpec}: " +
                        "#{format(indent, argText, textW - tagW)}\n"
            end
          end
          str += indent(tagW, argStr)
        end
        str += "\n"
      end

      str += blue('Context:') + '     '
      if @contexts.empty?
        str += format(tagW, 'Global scope', textW)
      else
        cxtStr = ''
        @contexts.each do |context|
          unless cxtStr.empty?
            cxtStr += ', '
          end
          cxtStr += context.keyword
        end
        str += format(tagW, cxtStr, textW)
      end

      str += "\n#{blue('Attributes:')}  "
      if @optionalAttributes.empty?
        str += "none\n\n"
      else
        attrStr = ''
        @optionalAttributes.sort! do |a, b|
          a.keyword <=> b.keyword
        end
        showLegend = false
        @optionalAttributes.each do |attr|
          unless attrStr.empty?
            attrStr += ', '
          end
          attrStr += attr.keyword
          if attr.scenarioSpecific || attr.inheritedFromProject ||
             attr.inheritedFromParent
            first = true
            showLegend = true
            tag = '['
            if attr.scenarioSpecific
              tag += 'sc'
              first = false
            end
            if attr.inheritedFromProject
              tag += ':' unless first
              tag += 'ig'
              first = false
            end
            if attr.inheritedFromParent
              tag += ':' unless first
              tag += 'ip'
            end
            tag += ']'
            attrStr += cyan(tag)
          end
        end
        if showLegend
          attrStr += "\n\n#{cyan('[sc]')} : Attribute is scenario specific" +
                     "\r#{cyan('[ig]')} : " +
                     "Value can be inherited from global setting" +
                     "\r#{cyan('[ip]')} : " +
                     "Value can be inherited from parent property"
        end
        str += format(tagW, attrStr, textW)
        str += "\n"
      end

      unless @seeAlso.empty?
        str += blue('See also:') + "    "
        alsoStr = ''
        @seeAlso.each do |also|
          unless alsoStr.empty?
            alsoStr += ', '
          end
          alsoStr += also.keyword
        end
        str += format(tagW, alsoStr, textW)
        str += "\n"
      end

  #    str += "Rule:    #{@rule.name}\n" if @rule
  #    str += "Pattern: #{@pattern.tokens.join(' ')}\n" if @pattern
      str
    end

[Validate]