Class: TaskJuggler::PropertyList

Inherits:
Object
  • Object
show all
Includes:
MessageHandler
Defined in:
lib/taskjuggler/PropertyList.rb

Overview

The PropertyList is a utility class that can be used to hold a list of properties. It’s derived from an Array, so it can hold the properties in a well defined order. The order can be determined by an arbitrary number of sorting levels. A sorting level specifies an attribute who’s value should be used for sorting, a scenario index if necessary and the sorting direction (up/down). All nodes in the PropertyList must belong to the same PropertySet.

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from MessageHandler

#critical, #debug, #error, #fatal, #info, #warning

Constructor Details

#initialize(arg, copyItems = true) ⇒ PropertyList

A PropertyList is always bound to a certain PropertySet. All properties in the list must be of that set.



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/taskjuggler/PropertyList.rb', line 37

def initialize(arg, copyItems = true)
  @items = copyItems ? arg.to_ary : []
  if arg.is_a?(PropertySet)
    # Create a PropertyList from the given PropertySet.
    @propertySet = arg
    # To keep the list sorted, we may have to access Property attributes.
    # Pre-scheduling, we can only use static attributes. Post-scheduling,
    # we can include dynamic attributes as well. This query template will
    # be used to query attributes when it has been set. Otherwise the list
    # can only be sorted by static attributes.
    @query = nil
    resetSorting
    addSortingCriteria('seqno', true, -1)
    sort!
  else
    # Create a PropertyList from a given other PropertyList.
    @propertySet = arg.propertySet
    @query = arg.query ? arg.query.dup : nil
    @sortingLevels = arg.sortingLevels
    @sortingCriteria = arg.sortingCriteria.dup
    @sortingUp = arg.sortingUp.dup
    @scenarioIdx = arg.scenarioIdx.dup
  end
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(func, *args, &block) ⇒ Object

This class should be a derived class of Array. But since it re-defines sort!() and still needs to call Array::sort!() I took a different route. All missing methods will be propagated to the @items Array.



65
66
67
# File 'lib/taskjuggler/PropertyList.rb', line 65

def method_missing(func, *args, &block)
  @items.method(func).call(*args, &block)
end

Instance Attribute Details

#propertySetObject (readonly)

Returns the value of attribute propertySet.



32
33
34
# File 'lib/taskjuggler/PropertyList.rb', line 32

def propertySet
  @propertySet
end

#queryObject

Returns the value of attribute query.



32
33
34
# File 'lib/taskjuggler/PropertyList.rb', line 32

def query
  @query
end

#scenarioIdxObject (readonly)

Returns the value of attribute scenarioIdx.



32
33
34
# File 'lib/taskjuggler/PropertyList.rb', line 32

def scenarioIdx
  @scenarioIdx
end

#sortingCriteriaObject (readonly)

Returns the value of attribute sortingCriteria.



32
33
34
# File 'lib/taskjuggler/PropertyList.rb', line 32

def sortingCriteria
  @sortingCriteria
end

#sortingLevelsObject (readonly)

Returns the value of attribute sortingLevels.



32
33
34
# File 'lib/taskjuggler/PropertyList.rb', line 32

def sortingLevels
  @sortingLevels
end

#sortingUpObject (readonly)

Returns the value of attribute sortingUp.



32
33
34
# File 'lib/taskjuggler/PropertyList.rb', line 32

def sortingUp
  @sortingUp
end

Instance Method Details

#[](node) ⇒ Object



103
104
105
# File 'lib/taskjuggler/PropertyList.rb', line 103

def [](node)
  @items.find { |n| n.ptn == node.ptn }
end

#append(list) ⇒ Object

Append another Array of PropertyTreeNodes or a PropertyList to this. The list will be sorted again.



129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/taskjuggler/PropertyList.rb', line 129

def append(list)
  if $DEBUG
    list.each do |node|
      unless node.propertySet == @propertySet
        raise "Fatal Error: All nodes must belong to the same PropertySet."
      end
    end
  end

  @items.concat(list)
  raise "Duplicate items" if @items != @items.uniq
  sort!
end

#checkForDuplicates(sourceFileInfo) ⇒ Object

Make sure that the list does not contain the same PropertyTreeNode more than once. This could happen for adopted tasks. If you use includeAdopted(), you should call this method after filtering to see if the filter was strict enough.



83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/taskjuggler/PropertyList.rb', line 83

def checkForDuplicates(sourceFileInfo)
  ptns = {}
  @items.each do |i|
    if ptns.include?(i.ptn)
      error('proplist_duplicate',
            "An adopted property is included as #{i.logicalId} and " +
            "as #{ptns[i.ptn].logicalId}. Please use stronger filtering " +
            'to avoid including the property more than once!',
            sourceFileInfo)
    end
    ptns[i.ptn] = i
  end
end

#include?(node) ⇒ Boolean

Specialized version of Array::include? that also matches adopted tasks.

Returns:

  • (Boolean)


99
100
101
# File 'lib/taskjuggler/PropertyList.rb', line 99

def include?(node)
  !@items.find { |p| p.ptn == node.ptn }.nil?
end

#includeAdoptedObject



69
70
71
72
73
74
75
76
77
# File 'lib/taskjuggler/PropertyList.rb', line 69

def includeAdopted
  adopted = []
  @items.each do |p|
    p.adoptees.each do |ap|
      adopted += includeAdoptedR(ap, p)
    end
  end
  append(adopted)
end

#indexObject

This function sets the index attribute of all the properties in the list. The index starts with 0 and increases for each property.



192
193
194
195
196
197
# File 'lib/taskjuggler/PropertyList.rb', line 192

def index
  i = 0
  @items.each do |p|
    p.force('index', i += 1)
  end
end

#itemIndex(item) ⇒ Object

Return the Array index of item or nil.



186
187
188
# File 'lib/taskjuggler/PropertyList.rb', line 186

def itemIndex(item)
  @items.index(item)
end

#resetSortingObject

Clear all sorting levels.



120
121
122
123
124
125
# File 'lib/taskjuggler/PropertyList.rb', line 120

def resetSorting
  @sortingLevels = 0
  @sortingCriteria = []
  @sortingUp = []
  @scenarioIdx = []
end

#setSorting(modes) ⇒ Object

Set all sorting levels as Array of triplets.



112
113
114
115
116
117
# File 'lib/taskjuggler/PropertyList.rb', line 112

def setSorting(modes)
  resetSorting
  modes.each do |mode|
    addSortingCriteria(*mode)
  end
end

#sort!Object

Sort the properties according to the currently defined sorting criteria.



151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/taskjuggler/PropertyList.rb', line 151

def sort!
  if treeMode?
    # Tree sorting is somewhat complex. It will be based on the 'tree'
    # attribute of the PropertyTreeNodes but we have to update them first
    # based on the other sorting criteria.

    # Remove the tree sorting mode first.
    sc = @sortingCriteria.delete_at(0)
    su = @sortingUp.delete_at(0)
    si = @scenarioIdx.delete_at(0)
    @sortingLevels -= 1

    # Sort the list based on the rest of the modes.
    sortInternal
    # The update the 'index' attributes of the PropertyTreeNodes.
    index
    # An then the 'tree' attributes.
    indexTree

    # Restore the 'tree' sorting mode again.
    @sortingCriteria.insert(0, sc)
    @sortingUp.insert(0, su)
    @scenarioIdx.insert(0, si)
    @sortingLevels += 1

    # Sort again, now based on the updated 'tree' attributes.
    sortInternal
  else
    sortInternal
  end
  # Update indexes.
  index
end

#to_aryObject



107
108
109
# File 'lib/taskjuggler/PropertyList.rb', line 107

def to_ary
  @items.dup
end

#to_sObject

Turn the list into a String. This is only used for debugging.



200
201
202
203
204
205
206
207
208
209
# File 'lib/taskjuggler/PropertyList.rb', line 200

def to_s # :nodoc:
  res = "Sorting: "
  @sortingLevels.times do |i|
    res += "#{@sortingCriteria[i]}/#{@sortingUp[i] ? 'up' : 'down'}/" +
           "#{@scenarioIdx[i]}, "
  end
  res += "\n#{@items.length} properties:"
  @items.each { |i| res += "#{i.get('id')}: #{i.get('name')}\n" }
  res
end

#treeMode?Boolean

If the first sorting level is ‘tree’ the breakdown structure of the list is preserved. This is a somewhat special mode and this function returns true if the mode is set.

Returns:

  • (Boolean)


146
147
148
# File 'lib/taskjuggler/PropertyList.rb', line 146

def treeMode?
  @sortingLevels > 0 && @sortingCriteria[0] == 'tree'
end