class 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

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

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

# File lib/taskjuggler/KeywordDocumentation.rb, line 49
def initialize(rule, pattern, syntax, args, optAttrPatterns, manual)
  @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

computeInheritance() click to toggle source
# File lib/taskjuggler/KeywordDocumentation.rb, line 162
def computeInheritance
  if (propertySet = findPropertySet)
    keyword = @keyword
    keyword = keyword.split('.')[0] if keyword.include?('.')
    @inheritedFromProject = propertySet.inheritedFromProject?(keyword)
    @inheritedFromParent = propertySet.inheritedFromParent?(keyword)
  end
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 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
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 328
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
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 94
def globalScope?
  return true if @contexts.empty?
  @contexts.each do |context|
    return true if context.keyword == 'properties'
  end
  false
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 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
listAttribute?() click to toggle source
# File lib/taskjuggler/KeywordDocumentation.rb, line 152
def listAttribute?
  if (propertySet = findPropertySet)
    keyword = @keyword
    keyword = keyword.split('.')[0] if keyword.include?('.')
    return propertySet.listAttribute?(keyword)
  end

  false
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 173
def title
  kwTokens = @keyword.split('.')
  if kwTokens.size == 1
    title = @keyword
  else
    title = "#{kwTokens[0]} (#{kwTokens[1]})"
  end
  title
end
to_s() click to toggle source

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

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

    # Top line with multiple elements
    str = "#{blue('Keyword:')}     #{bold(@keyword)}" +
          "#{listAttribute? ? ' (List Attribute)' : '' }\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