taskjuggler 0.0.2
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.
- data/COPYING +280 -0
- data/README +31 -0
- data/Rakefile +20 -0
- data/benchmarks/UTF-8-Strings.rb +58 -0
- data/benchmarks/allocate.tjp +30 -0
- data/benchmarks/booking.tjp +62 -0
- data/benchmarks/depends.tjp +112 -0
- data/benchmarks/htmltaskreport.tjp +45 -0
- data/benchmarks/runbench.rb +24 -0
- data/bin/tj3 +3 -0
- data/bin/tj3man +3 -0
- data/doc/classes/AppConfig.html +808 -0
- data/doc/classes/Arguments.html +226 -0
- data/doc/classes/String.html +395 -0
- data/doc/classes/TaskJuggler.html +1358 -0
- data/doc/classes/TaskJuggler/Account.html +257 -0
- data/doc/classes/TaskJuggler/AccountScenario.html +218 -0
- data/doc/classes/TaskJuggler/Allocation.html +419 -0
- data/doc/classes/TaskJuggler/AllocationAttribute.html +291 -0
- data/doc/classes/TaskJuggler/AttributeBase.html +608 -0
- data/doc/classes/TaskJuggler/AttributeDefinition.html +259 -0
- data/doc/classes/TaskJuggler/Booking.html +307 -0
- data/doc/classes/TaskJuggler/BookingListAttribute.html +263 -0
- data/doc/classes/TaskJuggler/BooleanAttribute.html +261 -0
- data/doc/classes/TaskJuggler/CSVFile.html +325 -0
- data/doc/classes/TaskJuggler/Charge.html +279 -0
- data/doc/classes/TaskJuggler/ChargeListAttribute.html +229 -0
- data/doc/classes/TaskJuggler/ChargeSet.html +440 -0
- data/doc/classes/TaskJuggler/ChargeSetListAttribute.html +276 -0
- data/doc/classes/TaskJuggler/ColumnTable.html +260 -0
- data/doc/classes/TaskJuggler/DateAttribute.html +194 -0
- data/doc/classes/TaskJuggler/DependencyListAttribute.html +267 -0
- data/doc/classes/TaskJuggler/DurationAttribute.html +229 -0
- data/doc/classes/TaskJuggler/FixnumAttribute.html +194 -0
- data/doc/classes/TaskJuggler/FlagListAttribute.html +263 -0
- data/doc/classes/TaskJuggler/FloatAttribute.html +229 -0
- data/doc/classes/TaskJuggler/GanttChart.html +667 -0
- data/doc/classes/TaskJuggler/GanttContainer.html +441 -0
- data/doc/classes/TaskJuggler/GanttHeader.html +280 -0
- data/doc/classes/TaskJuggler/GanttHeaderScaleItem.html +245 -0
- data/doc/classes/TaskJuggler/GanttLine.html +398 -0
- data/doc/classes/TaskJuggler/GanttLoadStack.html +327 -0
- data/doc/classes/TaskJuggler/GanttMilestone.html +415 -0
- data/doc/classes/TaskJuggler/GanttRouter.html +425 -0
- data/doc/classes/TaskJuggler/GanttTaskBar.html +429 -0
- data/doc/classes/TaskJuggler/HTMLDocument.html +240 -0
- data/doc/classes/TaskJuggler/HTMLGraphics.html +231 -0
- data/doc/classes/TaskJuggler/Interval.html +552 -0
- data/doc/classes/TaskJuggler/IntervalListAttribute.html +267 -0
- data/doc/classes/TaskJuggler/KeywordDocumentation.html +796 -0
- data/doc/classes/TaskJuggler/Limits.html +416 -0
- data/doc/classes/TaskJuggler/Limits/Limit.html +381 -0
- data/doc/classes/TaskJuggler/LimitsAttribute.html +261 -0
- data/doc/classes/TaskJuggler/Log.html +613 -0
- data/doc/classes/TaskJuggler/LogicalAttribute.html +226 -0
- data/doc/classes/TaskJuggler/LogicalExpression.html +251 -0
- data/doc/classes/TaskJuggler/LogicalFlag.html +229 -0
- data/doc/classes/TaskJuggler/LogicalFunction.html +324 -0
- data/doc/classes/TaskJuggler/LogicalOperation.html +299 -0
- data/doc/classes/TaskJuggler/Macro.html +194 -0
- data/doc/classes/TaskJuggler/MacroParser.html +360 -0
- data/doc/classes/TaskJuggler/MacroTable.html +366 -0
- data/doc/classes/TaskJuggler/Message.html +281 -0
- data/doc/classes/TaskJuggler/MessageHandler.html +215 -0
- data/doc/classes/TaskJuggler/Project.html +1606 -0
- data/doc/classes/TaskJuggler/ProjectFileParser.html +412 -0
- data/doc/classes/TaskJuggler/PropertyList.html +597 -0
- data/doc/classes/TaskJuggler/PropertySet.html +1200 -0
- data/doc/classes/TaskJuggler/PropertyTreeNode.html +1449 -0
- data/doc/classes/TaskJuggler/Query.html +600 -0
- data/doc/classes/TaskJuggler/RealFormat.html +252 -0
- data/doc/classes/TaskJuggler/ReferenceAttribute.html +194 -0
- data/doc/classes/TaskJuggler/Report.html +528 -0
- data/doc/classes/TaskJuggler/ReportElement.html +1070 -0
- data/doc/classes/TaskJuggler/ReportTable.html +497 -0
- data/doc/classes/TaskJuggler/ReportTableCell.html +518 -0
- data/doc/classes/TaskJuggler/ReportTableColumn.html +364 -0
- data/doc/classes/TaskJuggler/ReportTableElement.html +644 -0
- data/doc/classes/TaskJuggler/ReportTableLegend.html +343 -0
- data/doc/classes/TaskJuggler/ReportTableLine.html +431 -0
- data/doc/classes/TaskJuggler/Resource.html +211 -0
- data/doc/classes/TaskJuggler/ResourceListAttribute.html +267 -0
- data/doc/classes/TaskJuggler/ResourceListRE.html +249 -0
- data/doc/classes/TaskJuggler/ResourceScenario.html +1137 -0
- data/doc/classes/TaskJuggler/RichText.html +537 -0
- data/doc/classes/TaskJuggler/RichTextAttribute.html +229 -0
- data/doc/classes/TaskJuggler/RichTextDocument.html +418 -0
- data/doc/classes/TaskJuggler/RichTextElement.html +829 -0
- data/doc/classes/TaskJuggler/RichTextException.html +212 -0
- data/doc/classes/TaskJuggler/RichTextParser.html +317 -0
- data/doc/classes/TaskJuggler/RichTextProtocolExample.html +303 -0
- data/doc/classes/TaskJuggler/RichTextProtocolHandler.html +194 -0
- data/doc/classes/TaskJuggler/RichTextScanner.html +561 -0
- data/doc/classes/TaskJuggler/RichTextSnip.html +364 -0
- data/doc/classes/TaskJuggler/RichTextSyntaxRules.html +883 -0
- data/doc/classes/TaskJuggler/Scenario.html +163 -0
- data/doc/classes/TaskJuggler/ScenarioData.html +354 -0
- data/doc/classes/TaskJuggler/Scoreboard.html +638 -0
- data/doc/classes/TaskJuggler/Shift.html +255 -0
- data/doc/classes/TaskJuggler/ShiftAssignment.html +488 -0
- data/doc/classes/TaskJuggler/ShiftAssignments.html +715 -0
- data/doc/classes/TaskJuggler/ShiftAssignmentsAttribute.html +261 -0
- data/doc/classes/TaskJuggler/ShiftScenario.html +282 -0
- data/doc/classes/TaskJuggler/SourceFileInfo.html +247 -0
- data/doc/classes/TaskJuggler/StringAttribute.html +229 -0
- data/doc/classes/TaskJuggler/SymbolAttribute.html +194 -0
- data/doc/classes/TaskJuggler/SyntaxReference.html +516 -0
- data/doc/classes/TaskJuggler/TOCEntry.html +242 -0
- data/doc/classes/TaskJuggler/TableColumnDefinition.html +273 -0
- data/doc/classes/TaskJuggler/TableOfContents.html +256 -0
- data/doc/classes/TaskJuggler/Task.html +203 -0
- data/doc/classes/TaskJuggler/TaskDependency.html +251 -0
- data/doc/classes/TaskJuggler/TaskListAttribute.html +267 -0
- data/doc/classes/TaskJuggler/TaskListRE.html +250 -0
- data/doc/classes/TaskJuggler/TaskScenario.html +2206 -0
- data/doc/classes/TaskJuggler/TextParser.html +670 -0
- data/doc/classes/TaskJuggler/TextParser/Pattern.html +923 -0
- data/doc/classes/TaskJuggler/TextParser/Rule.html +779 -0
- data/doc/classes/TaskJuggler/TextParser/StackElement.html +267 -0
- data/doc/classes/TaskJuggler/TextParser/TextParserResultArray.html +212 -0
- data/doc/classes/TaskJuggler/TextParser/TokenDoc.html +221 -0
- data/doc/classes/TaskJuggler/TextScanner.html +708 -0
- data/doc/classes/TaskJuggler/TextScanner/BufferStreamHandle.html +355 -0
- data/doc/classes/TaskJuggler/TextScanner/FileStreamHandle.html +341 -0
- data/doc/classes/TaskJuggler/TextScanner/StreamHandle.html +260 -0
- data/doc/classes/TaskJuggler/TjException.html +185 -0
- data/doc/classes/TaskJuggler/TjTime.html +1845 -0
- data/doc/classes/TaskJuggler/TjpExample.html +310 -0
- data/doc/classes/TaskJuggler/TjpExportRE.html +329 -0
- data/doc/classes/TaskJuggler/TjpSyntaxRules.html +8928 -0
- data/doc/classes/TaskJuggler/UserManual.html +606 -0
- data/doc/classes/TaskJuggler/WorkingHours.html +582 -0
- data/doc/classes/TaskJuggler/WorkingHoursAttribute.html +284 -0
- data/doc/classes/TaskJuggler/XMLBlob.html +207 -0
- data/doc/classes/TaskJuggler/XMLComment.html +206 -0
- data/doc/classes/TaskJuggler/XMLDocument.html +293 -0
- data/doc/classes/TaskJuggler/XMLElement.html +341 -0
- data/doc/classes/TaskJuggler/XMLNamedText.html +174 -0
- data/doc/classes/TaskJuggler/XMLText.html +221 -0
- data/doc/files/COPYING.html +448 -0
- data/doc/files/README.html +134 -0
- data/doc/files/lib/AccountScenario_rb.html +116 -0
- data/doc/files/lib/Account_rb.html +118 -0
- data/doc/files/lib/Allocation_rb.html +118 -0
- data/doc/files/lib/AppConfig_rb.html +116 -0
- data/doc/files/lib/AttributeBase_rb.html +106 -0
- data/doc/files/lib/AttributeDefinition_rb.html +106 -0
- data/doc/files/lib/Attributes_rb.html +130 -0
- data/doc/files/lib/Booking_rb.html +106 -0
- data/doc/files/lib/ChargeSet_rb.html +116 -0
- data/doc/files/lib/Charge_rb.html +116 -0
- data/doc/files/lib/HTMLDocument_rb.html +116 -0
- data/doc/files/lib/Interval_rb.html +116 -0
- data/doc/files/lib/KeywordDocumentation_rb.html +122 -0
- data/doc/files/lib/Limits_rb.html +116 -0
- data/doc/files/lib/Log_rb.html +116 -0
- data/doc/files/lib/LogicalExpression_rb.html +122 -0
- data/doc/files/lib/LogicalFlag_rb.html +116 -0
- data/doc/files/lib/LogicalFunction_rb.html +116 -0
- data/doc/files/lib/LogicalOperation_rb.html +116 -0
- data/doc/files/lib/MacroParser_rb.html +118 -0
- data/doc/files/lib/MacroTable_rb.html +122 -0
- data/doc/files/lib/MessageHandler_rb.html +106 -0
- data/doc/files/lib/Message_rb.html +116 -0
- data/doc/files/lib/ProjectFileParser_rb.html +122 -0
- data/doc/files/lib/Project_rb.html +148 -0
- data/doc/files/lib/PropertyList_rb.html +106 -0
- data/doc/files/lib/PropertySet_rb.html +118 -0
- data/doc/files/lib/PropertyTreeNode_rb.html +106 -0
- data/doc/files/lib/Query_rb.html +116 -0
- data/doc/files/lib/RealFormat_rb.html +106 -0
- data/doc/files/lib/ResourceScenario_rb.html +116 -0
- data/doc/files/lib/Resource_rb.html +118 -0
- data/doc/files/lib/RichTextDocument_rb.html +120 -0
- data/doc/files/lib/RichTextElement_rb.html +120 -0
- data/doc/files/lib/RichTextParser_rb.html +120 -0
- data/doc/files/lib/RichTextProtocolExample_rb.html +120 -0
- data/doc/files/lib/RichTextProtocolHandler_rb.html +106 -0
- data/doc/files/lib/RichTextScanner_rb.html +116 -0
- data/doc/files/lib/RichTextSnip_rb.html +118 -0
- data/doc/files/lib/RichTextSyntaxRules_rb.html +106 -0
- data/doc/files/lib/RichText_rb.html +118 -0
- data/doc/files/lib/ScenarioData_rb.html +118 -0
- data/doc/files/lib/Scenario_rb.html +116 -0
- data/doc/files/lib/Scoreboard_rb.html +106 -0
- data/doc/files/lib/ShiftAssignments_rb.html +116 -0
- data/doc/files/lib/ShiftScenario_rb.html +116 -0
- data/doc/files/lib/Shift_rb.html +118 -0
- data/doc/files/lib/SourceFileInfo_rb.html +106 -0
- data/doc/files/lib/SyntaxReference_rb.html +122 -0
- data/doc/files/lib/TOCEntry_rb.html +118 -0
- data/doc/files/lib/TableColumnDefinition_rb.html +106 -0
- data/doc/files/lib/TableOfContents_rb.html +118 -0
- data/doc/files/lib/TaskDependency_rb.html +106 -0
- data/doc/files/lib/TaskJuggler_rb.html +120 -0
- data/doc/files/lib/TaskScenario_rb.html +116 -0
- data/doc/files/lib/Task_rb.html +118 -0
- data/doc/files/lib/TextParser/Pattern_rb.html +116 -0
- data/doc/files/lib/TextParser/Rule_rb.html +106 -0
- data/doc/files/lib/TextParser/StackElement_rb.html +106 -0
- data/doc/files/lib/TextParser/TokenDoc_rb.html +106 -0
- data/doc/files/lib/TextParser_rb.html +124 -0
- data/doc/files/lib/TextScanner_rb.html +128 -0
- data/doc/files/lib/Tj3Config_rb.html +118 -0
- data/doc/files/lib/TjException_rb.html +106 -0
- data/doc/files/lib/TjTime_rb.html +118 -0
- data/doc/files/lib/TjpExample_rb.html +116 -0
- data/doc/files/lib/TjpSyntaxRules_rb.html +106 -0
- data/doc/files/lib/UTF8String_rb.html +132 -0
- data/doc/files/lib/UserManual_rb.html +124 -0
- data/doc/files/lib/WorkingHours_rb.html +116 -0
- data/doc/files/lib/XMLDocument_rb.html +116 -0
- data/doc/files/lib/XMLElement_rb.html +116 -0
- data/doc/files/lib/reports/CSVFile_rb.html +116 -0
- data/doc/files/lib/reports/ColumnTable_rb.html +116 -0
- data/doc/files/lib/reports/GanttChart_rb.html +122 -0
- data/doc/files/lib/reports/GanttContainer_rb.html +116 -0
- data/doc/files/lib/reports/GanttHeaderScaleItem_rb.html +106 -0
- data/doc/files/lib/reports/GanttHeader_rb.html +116 -0
- data/doc/files/lib/reports/GanttLine_rb.html +126 -0
- data/doc/files/lib/reports/GanttLoadStack_rb.html +116 -0
- data/doc/files/lib/reports/GanttMilestone_rb.html +116 -0
- data/doc/files/lib/reports/GanttRouter_rb.html +106 -0
- data/doc/files/lib/reports/GanttTaskBar_rb.html +116 -0
- data/doc/files/lib/reports/HTMLGraphics_rb.html +106 -0
- data/doc/files/lib/reports/ReportElement_rb.html +118 -0
- data/doc/files/lib/reports/ReportTableCell_rb.html +106 -0
- data/doc/files/lib/reports/ReportTableColumn_rb.html +106 -0
- data/doc/files/lib/reports/ReportTableElement_rb.html +122 -0
- data/doc/files/lib/reports/ReportTableLegend_rb.html +106 -0
- data/doc/files/lib/reports/ReportTableLine_rb.html +116 -0
- data/doc/files/lib/reports/ReportTable_rb.html +118 -0
- data/doc/files/lib/reports/Report_rb.html +126 -0
- data/doc/files/lib/reports/ResourceListRE_rb.html +122 -0
- data/doc/files/lib/reports/TaskListRE_rb.html +122 -0
- data/doc/files/lib/reports/TjpExportRE_rb.html +116 -0
- data/doc/files/lib/taskjuggler3_rb.html +276 -0
- data/doc/files/lib/tj3man_rb.html +189 -0
- data/doc/fr_class_index.html +285 -0
- data/doc/fr_file_index.html +223 -0
- data/doc/fr_method_index.html +1953 -0
- data/doc/index.html +21 -0
- data/doc/rdoc-style.css +299 -0
- data/examples/tutorial.tjp +361 -0
- data/gem_spec.rb +30 -0
- data/lib/Account.rb +50 -0
- data/lib/AccountScenario.rb +39 -0
- data/lib/Allocation.rb +102 -0
- data/lib/AppConfig.rb +134 -0
- data/lib/AttributeBase.rb +131 -0
- data/lib/AttributeDefinition.rb +47 -0
- data/lib/Attributes.rb +478 -0
- data/lib/BatchProcessor.rb +209 -0
- data/lib/Booking.rb +59 -0
- data/lib/Charge.rb +71 -0
- data/lib/ChargeSet.rb +126 -0
- data/lib/HTMLDocument.rb +59 -0
- data/lib/Interval.rb +127 -0
- data/lib/KeywordDocumentation.rb +560 -0
- data/lib/Limits.rb +219 -0
- data/lib/Log.rb +160 -0
- data/lib/LogicalExpression.rb +71 -0
- data/lib/LogicalFlag.rb +34 -0
- data/lib/LogicalFunction.rb +102 -0
- data/lib/LogicalOperation.rb +118 -0
- data/lib/MacroParser.rb +77 -0
- data/lib/MacroTable.rb +84 -0
- data/lib/Message.rb +56 -0
- data/lib/MessageHandler.rb +34 -0
- data/lib/Project.rb +662 -0
- data/lib/ProjectFileParser.rb +333 -0
- data/lib/PropertyList.rb +181 -0
- data/lib/PropertySet.rb +304 -0
- data/lib/PropertyTreeNode.rb +461 -0
- data/lib/Query.rb +227 -0
- data/lib/RealFormat.rb +73 -0
- data/lib/Resource.rb +42 -0
- data/lib/ResourceScenario.rb +511 -0
- data/lib/RichText.rb +147 -0
- data/lib/RichTextDocument.rb +139 -0
- data/lib/RichTextElement.rb +391 -0
- data/lib/RichTextParser.rb +66 -0
- data/lib/RichTextProtocolExample.rb +65 -0
- data/lib/RichTextProtocolHandler.rb +35 -0
- data/lib/RichTextScanner.rb +390 -0
- data/lib/RichTextSnip.rb +104 -0
- data/lib/RichTextSyntaxRules.rb +265 -0
- data/lib/Scenario.rb +27 -0
- data/lib/ScenarioData.rb +65 -0
- data/lib/Scoreboard.rb +141 -0
- data/lib/Shift.rb +48 -0
- data/lib/ShiftAssignments.rb +291 -0
- data/lib/ShiftScenario.rb +46 -0
- data/lib/SourceFileInfo.rb +37 -0
- data/lib/SyntaxReference.rb +284 -0
- data/lib/TOCEntry.rb +76 -0
- data/lib/TableColumnDefinition.rb +54 -0
- data/lib/TableOfContents.rb +46 -0
- data/lib/Task.rb +37 -0
- data/lib/TaskDependency.rb +39 -0
- data/lib/TaskJuggler.rb +84 -0
- data/lib/TaskScenario.rb +1622 -0
- data/lib/TextParser.rb +416 -0
- data/lib/TextParser/Pattern.rb +263 -0
- data/lib/TextParser/Rule.rb +171 -0
- data/lib/TextParser/StackElement.rb +45 -0
- data/lib/TextParser/TokenDoc.rb +38 -0
- data/lib/TextScanner.rb +682 -0
- data/lib/Tj3Config.rb +27 -0
- data/lib/TjException.rb +27 -0
- data/lib/TjTime.rb +395 -0
- data/lib/TjpExample.rb +119 -0
- data/lib/TjpSyntaxRules.rb +4022 -0
- data/lib/UTF8String.rb +100 -0
- data/lib/UserManual.rb +282 -0
- data/lib/WorkingHours.rb +323 -0
- data/lib/XMLDocument.rb +54 -0
- data/lib/XMLElement.rb +175 -0
- data/lib/reports/CSVFile.rb +146 -0
- data/lib/reports/ColumnTable.rb +66 -0
- data/lib/reports/GanttChart.rb +308 -0
- data/lib/reports/GanttContainer.rb +107 -0
- data/lib/reports/GanttHeader.rb +141 -0
- data/lib/reports/GanttHeaderScaleItem.rb +42 -0
- data/lib/reports/GanttLine.rb +329 -0
- data/lib/reports/GanttLoadStack.rb +113 -0
- data/lib/reports/GanttMilestone.rb +80 -0
- data/lib/reports/GanttRouter.rb +375 -0
- data/lib/reports/GanttTaskBar.rb +95 -0
- data/lib/reports/HTMLGraphics.rb +65 -0
- data/lib/reports/Report.rb +344 -0
- data/lib/reports/ReportElement.rb +427 -0
- data/lib/reports/ReportTable.rb +144 -0
- data/lib/reports/ReportTableCell.rb +142 -0
- data/lib/reports/ReportTableColumn.rb +82 -0
- data/lib/reports/ReportTableElement.rb +852 -0
- data/lib/reports/ReportTableLegend.rb +167 -0
- data/lib/reports/ReportTableLine.rb +87 -0
- data/lib/reports/ResourceListRE.rb +72 -0
- data/lib/reports/TaskListRE.rb +73 -0
- data/lib/reports/TjpExportRE.rb +394 -0
- data/lib/taskjuggler3.rb +106 -0
- data/lib/tj3man.rb +88 -0
- data/manual/Day_To_Day_Juggling +168 -0
- data/manual/Getting_Started +61 -0
- data/manual/How_To_Contribute +185 -0
- data/manual/Installation +68 -0
- data/manual/Intro +102 -0
- data/manual/Reporting_Bugs +26 -0
- data/manual/Rich_Text_Attributes +90 -0
- data/manual/TaskJuggler_2x_Migration +40 -0
- data/manual/Tutorial +579 -0
- data/manual/fdl +450 -0
- data/prj_cfg.rb +43 -0
- data/setup.rb +1585 -0
- data/tasks/csts.rake +72 -0
- data/tasks/gem.rake +14 -0
- data/tasks/manual.rake +10 -0
- data/tasks/missing.rake +21 -0
- data/tasks/rcov.rake +14 -0
- data/tasks/rdoc.rake +17 -0
- data/tasks/rexml_fix.rb +16 -0
- data/tasks/rexml_fix_19.rb +49 -0
- data/tasks/show.rake +21 -0
- data/tasks/stats.rake +25 -0
- data/tasks/test.rake +11 -0
- data/test/MessageChecker.rb +53 -0
- data/test/TestSuite/CSV-Reports/celltext-Reference.csv +14 -0
- data/test/TestSuite/CSV-Reports/celltext.tjp +7 -0
- data/test/TestSuite/CSV-Reports/genrefs +6 -0
- data/test/TestSuite/CSV-Reports/project-1.tji +57 -0
- data/test/TestSuite/CSV-Reports/resourcereport-Reference.csv +4 -0
- data/test/TestSuite/CSV-Reports/resourcereport.tjp +10 -0
- data/test/TestSuite/CSV-Reports/resourcereport_with_tasks-Reference.csv +22 -0
- data/test/TestSuite/CSV-Reports/resourcereport_with_tasks.tjp +11 -0
- data/test/TestSuite/CSV-Reports/sortByTree-Reference.csv +14 -0
- data/test/TestSuite/CSV-Reports/sortByTree.tjp +8 -0
- data/test/TestSuite/CSV-Reports/sortBy_duration.down-Reference.csv +14 -0
- data/test/TestSuite/CSV-Reports/sortBy_duration.down.tjp +10 -0
- data/test/TestSuite/CSV-Reports/sortBy_effort.up-Reference.csv +14 -0
- data/test/TestSuite/CSV-Reports/sortBy_effort.up.tjp +10 -0
- data/test/TestSuite/CSV-Reports/sortBy_plan.start.down-Reference.csv +14 -0
- data/test/TestSuite/CSV-Reports/sortBy_plan.start.down.tjp +10 -0
- data/test/TestSuite/CSV-Reports/taskreport-Reference.csv +14 -0
- data/test/TestSuite/CSV-Reports/taskreport.tjp +10 -0
- data/test/TestSuite/CSV-Reports/taskreport_with_resources-Reference.csv +24 -0
- data/test/TestSuite/CSV-Reports/taskreport_with_resources.tjp +11 -0
- data/test/TestSuite/Scheduler/Correct/Allocate.tjp +86 -0
- data/test/TestSuite/Scheduler/Correct/AutomaticMilestones.tjp +63 -0
- data/test/TestSuite/Scheduler/Correct/Booking.tjp +161 -0
- data/test/TestSuite/Scheduler/Correct/Depends.tjp +50 -0
- data/test/TestSuite/Scheduler/Correct/Duration.tjp +34 -0
- data/test/TestSuite/Scheduler/Correct/InheritStartEnd.tjp +129 -0
- data/test/TestSuite/Scheduler/Correct/Limits.tjp +81 -0
- data/test/TestSuite/Scheduler/Correct/MultipleMandatories.tjp +43 -0
- data/test/TestSuite/Scheduler/Correct/Optimize-1.tjp +28 -0
- data/test/TestSuite/Scheduler/Correct/Optimize-2.tjp +33 -0
- data/test/TestSuite/Scheduler/Correct/Optimize-3.tjp +33 -0
- data/test/TestSuite/Scheduler/Correct/Optimize-4.tjp +34 -0
- data/test/TestSuite/Scheduler/Correct/Optimize-5.tjp +62 -0
- data/test/TestSuite/Scheduler/Correct/Precedes.tjp +50 -0
- data/test/TestSuite/Scheduler/Correct/Shift.tjp +102 -0
- data/test/TestSuite/Scheduler/Errors/account_no_leaf.tjp +13 -0
- data/test/TestSuite/Scheduler/Errors/booking_conflict.tjp +10 -0
- data/test/TestSuite/Scheduler/Errors/booking_no_duty.tjp +9 -0
- data/test/TestSuite/Scheduler/Errors/booking_on_vacation.tjp +9 -0
- data/test/TestSuite/Scheduler/Errors/container_booking.tjp +14 -0
- data/test/TestSuite/Scheduler/Errors/container_duration.tjp +11 -0
- data/test/TestSuite/Scheduler/Errors/effort_no_allocations.tjp +7 -0
- data/test/TestSuite/Scheduler/Errors/loop_detected_1.tjp +19 -0
- data/test/TestSuite/Scheduler/Errors/loop_detected_10.tjp +36 -0
- data/test/TestSuite/Scheduler/Errors/loop_detected_11.tjp +27 -0
- data/test/TestSuite/Scheduler/Errors/loop_detected_12.tjp +20 -0
- data/test/TestSuite/Scheduler/Errors/loop_detected_13.tjp +27 -0
- data/test/TestSuite/Scheduler/Errors/loop_detected_14.tjp +26 -0
- data/test/TestSuite/Scheduler/Errors/loop_detected_2.tjp +24 -0
- data/test/TestSuite/Scheduler/Errors/loop_detected_3.tjp +18 -0
- data/test/TestSuite/Scheduler/Errors/loop_detected_4.tjp +36 -0
- data/test/TestSuite/Scheduler/Errors/loop_detected_5.tjp +37 -0
- data/test/TestSuite/Scheduler/Errors/loop_detected_6.tjp +35 -0
- data/test/TestSuite/Scheduler/Errors/loop_detected_7.tjp +46 -0
- data/test/TestSuite/Scheduler/Errors/loop_detected_8.tjp +51 -0
- data/test/TestSuite/Scheduler/Errors/loop_detected_9.tjp +20 -0
- data/test/TestSuite/Scheduler/Errors/maxend.tjp +8 -0
- data/test/TestSuite/Scheduler/Errors/maxstart.tjp +8 -0
- data/test/TestSuite/Scheduler/Errors/milestone_booking.tjp +10 -0
- data/test/TestSuite/Scheduler/Errors/milestone_duration.tjp +8 -0
- data/test/TestSuite/Scheduler/Errors/milestone_start_end.tjp +8 -0
- data/test/TestSuite/Scheduler/Errors/minend.tjp +8 -0
- data/test/TestSuite/Scheduler/Errors/minstart.tjp +8 -0
- data/test/TestSuite/Scheduler/Errors/multiple_durations.tjp +11 -0
- data/test/TestSuite/Scheduler/Errors/no_tasks.tjp +6 -0
- data/test/TestSuite/Scheduler/Errors/not_scheduled.tjp +8 -0
- data/test/TestSuite/Scheduler/Errors/task_depend_child.tjp +10 -0
- data/test/TestSuite/Scheduler/Errors/task_depend_multi.tjp +13 -0
- data/test/TestSuite/Scheduler/Errors/task_depend_parent.tjp +11 -0
- data/test/TestSuite/Scheduler/Errors/task_depend_self.tjp +10 -0
- data/test/TestSuite/Scheduler/Errors/task_depend_unknown.tjp +10 -0
- data/test/TestSuite/Scheduler/Errors/task_overspecified_1.tjp +9 -0
- data/test/TestSuite/Scheduler/Errors/task_overspecified_2.tjp +14 -0
- data/test/TestSuite/Scheduler/Errors/task_overspecified_3.tjp +14 -0
- data/test/TestSuite/Scheduler/Errors/task_underspecified_1.tjp +8 -0
- data/test/TestSuite/Scheduler/Errors/task_underspecified_2.tjp +8 -0
- data/test/TestSuite/Scheduler/Errors/task_underspecified_3.tjp +9 -0
- data/test/TestSuite/Syntax/Correct/Account.tjp +53 -0
- data/test/TestSuite/Syntax/Correct/Allocate-1.tjp +24 -0
- data/test/TestSuite/Syntax/Correct/Alternative.tjp +13 -0
- data/test/TestSuite/Syntax/Correct/AutoMacros.tjp +14 -0
- data/test/TestSuite/Syntax/Correct/Booking.tjp +26 -0
- data/test/TestSuite/Syntax/Correct/Caption.tjp +33 -0
- data/test/TestSuite/Syntax/Correct/Celltext.tjp +28 -0
- data/test/TestSuite/Syntax/Correct/Comments.tjp +29 -0
- data/test/TestSuite/Syntax/Correct/Complete.tjp +16 -0
- data/test/TestSuite/Syntax/Correct/CompletedWork.tji +20 -0
- data/test/TestSuite/Syntax/Correct/CriticalPath.tjp +31 -0
- data/test/TestSuite/Syntax/Correct/Currencyformat.tjp +12 -0
- data/test/TestSuite/Syntax/Correct/CustomAttributes.tjp +14 -0
- data/test/TestSuite/Syntax/Correct/Depends1.tjp +22 -0
- data/test/TestSuite/Syntax/Correct/Durations.tjp +29 -0
- data/test/TestSuite/Syntax/Correct/Efficiency.tjp +19 -0
- data/test/TestSuite/Syntax/Correct/Export.tjp +40 -0
- data/test/TestSuite/Syntax/Correct/Flags.tjp +32 -0
- data/test/TestSuite/Syntax/Correct/Freeze.tjp +28 -0
- data/test/TestSuite/Syntax/Correct/Gap.tjp +15 -0
- data/test/TestSuite/Syntax/Correct/HtmlTaskReport.tjp +33 -0
- data/test/TestSuite/Syntax/Correct/Limits-1.tjp +71 -0
- data/test/TestSuite/Syntax/Correct/LoadUnits.tjp +31 -0
- data/test/TestSuite/Syntax/Correct/Macro-1.tjp +19 -0
- data/test/TestSuite/Syntax/Correct/Mandatory.tjp +17 -0
- data/test/TestSuite/Syntax/Correct/Milestone.tjp +12 -0
- data/test/TestSuite/Syntax/Correct/MinMax.tjp +17 -0
- data/test/TestSuite/Syntax/Correct/Numberformat.tjp +12 -0
- data/test/TestSuite/Syntax/Correct/Period.tjp +16 -0
- data/test/TestSuite/Syntax/Correct/Persistent.tjp +11 -0
- data/test/TestSuite/Syntax/Correct/Precedes1.tjp +17 -0
- data/test/TestSuite/Syntax/Correct/Priority.tjp +30 -0
- data/test/TestSuite/Syntax/Correct/Project.tjp +21 -0
- data/test/TestSuite/Syntax/Correct/ProjectIDs.tjp +23 -0
- data/test/TestSuite/Syntax/Correct/RawHTML.tjp +29 -0
- data/test/TestSuite/Syntax/Correct/Reports.tjp +54 -0
- data/test/TestSuite/Syntax/Correct/Resource.tjp +20 -0
- data/test/TestSuite/Syntax/Correct/Responsible.tjp +16 -0
- data/test/TestSuite/Syntax/Correct/Scenario.tjp +15 -0
- data/test/TestSuite/Syntax/Correct/Scheduling.tjp +26 -0
- data/test/TestSuite/Syntax/Correct/Select.tjp +27 -0
- data/test/TestSuite/Syntax/Correct/Shift.tjp +55 -0
- data/test/TestSuite/Syntax/Correct/Simple.tjp +25 -0
- data/test/TestSuite/Syntax/Correct/String.tjp +12 -0
- data/test/TestSuite/Syntax/Correct/Supplement.tjp +24 -0
- data/test/TestSuite/Syntax/Correct/TaskRoot.tjp +34 -0
- data/test/TestSuite/Syntax/Correct/TimeFrame.tjp +19 -0
- data/test/TestSuite/Syntax/Correct/Timezone.tjp +8 -0
- data/test/TestSuite/Syntax/Correct/Vacation.tjp +33 -0
- data/test/TestSuite/Syntax/Correct/csvtest +16 -0
- data/test/TestSuite/Syntax/Correct/manual2example.rb +24 -0
- data/test/TestSuite/Syntax/Correct/tutorial.tjp +485 -0
- data/test/TestSuite/Syntax/Errors/bad_include.tjp +11 -0
- data/test/TestSuite/Syntax/Errors/booking_group.tjp +14 -0
- data/test/TestSuite/Syntax/Errors/booking_milestone.tjp +13 -0
- data/test/TestSuite/Syntax/Errors/booking_no_leaf.tjp +13 -0
- data/test/TestSuite/Syntax/Errors/chargeset.tjp +14 -0
- data/test/TestSuite/Syntax/Errors/chargeset_master.tjp +15 -0
- data/test/TestSuite/Syntax/Errors/container_attribute.tjp +13 -0
- data/test/TestSuite/Syntax/Errors/cost_acct_no_top.tjp +24 -0
- data/test/TestSuite/Syntax/Errors/cost_rev_same.tjp +24 -0
- data/test/TestSuite/Syntax/Errors/date_in_range.tjp +7 -0
- data/test/TestSuite/Syntax/Errors/effort_zero.tjp +8 -0
- data/test/TestSuite/Syntax/Errors/empty.tjp +1 -0
- data/test/TestSuite/Syntax/Errors/export_bad_extn.tjp +9 -0
- data/test/TestSuite/Syntax/Errors/extend_id_cap.tjp +7 -0
- data/test/TestSuite/Syntax/Errors/interval_end_in_range.tjp +7 -0
- data/test/TestSuite/Syntax/Errors/interval_start_in_range.tjp +7 -0
- data/test/TestSuite/Syntax/Errors/leaf_resource_id_expected.tjp +12 -0
- data/test/TestSuite/Syntax/Errors/misaligned_date.tjp +7 -0
- data/test/TestSuite/Syntax/Errors/no_csv_suffix.tjp +10 -0
- data/test/TestSuite/Syntax/Errors/no_html_suffix.tjp +10 -0
- data/test/TestSuite/Syntax/Errors/operand_attribute.tjp +11 -0
- data/test/TestSuite/Syntax/Errors/operand_unkn_flag.tjp +11 -0
- data/test/TestSuite/Syntax/Errors/operand_unkn_scen.tjp +11 -0
- data/test/TestSuite/Syntax/Errors/overtime_range.tjp +13 -0
- data/test/TestSuite/Syntax/Errors/purge_no_list.tjp +8 -0
- data/test/TestSuite/Syntax/Errors/purge_unknown_id.tjp +8 -0
- data/test/TestSuite/Syntax/Errors/report_end.tjp +10 -0
- data/test/TestSuite/Syntax/Errors/report_redifinition.tjp +10 -0
- data/test/TestSuite/Syntax/Errors/report_start.tjp +10 -0
- data/test/TestSuite/Syntax/Errors/resource_exists.tjp +7 -0
- data/test/TestSuite/Syntax/Errors/resource_id_expected.tjp +8 -0
- data/test/TestSuite/Syntax/Errors/rev_acct_no_top.tjp +24 -0
- data/test/TestSuite/Syntax/Errors/scenario_exists.tjp +7 -0
- data/test/TestSuite/Syntax/Errors/shift_assignment_overlap.tjp +15 -0
- data/test/TestSuite/Syntax/Errors/shift_exists.tjp +7 -0
- data/test/TestSuite/Syntax/Errors/shift_id_expected.tjp +7 -0
- data/test/TestSuite/Syntax/Errors/sloppy_range.tjp +13 -0
- data/test/TestSuite/Syntax/Errors/sort_direction.tjp +11 -0
- data/test/TestSuite/Syntax/Errors/sort_unknown_scen.tjp +11 -0
- data/test/TestSuite/Syntax/Errors/sorting_crit_exptd1.tjp +11 -0
- data/test/TestSuite/Syntax/Errors/sorting_crit_exptd2.tjp +11 -0
- data/test/TestSuite/Syntax/Errors/sorting_wbs.tjp +11 -0
- data/test/TestSuite/Syntax/Errors/start_before_end1.tjp +7 -0
- data/test/TestSuite/Syntax/Errors/start_before_end2.tjp +6 -0
- data/test/TestSuite/Syntax/Errors/task_complete.tjp +8 -0
- data/test/TestSuite/Syntax/Errors/task_exists.tjp +7 -0
- data/test/TestSuite/Syntax/Errors/task_priority.tjp +8 -0
- data/test/TestSuite/Syntax/Errors/task_without_chargeset.tjp +9 -0
- data/test/TestSuite/Syntax/Errors/time_interval.tjp +12 -0
- data/test/TestSuite/Syntax/Errors/too_many_bangs.tjp +10 -0
- data/test/TestSuite/Syntax/Errors/undecl_flag.tjp +6 -0
- data/test/TestSuite/Syntax/Errors/unknown_projectid.tjp +7 -0
- data/test/TestSuite/Syntax/Errors/unknown_scenario_id.tjp +6 -0
- data/test/TestSuite/Syntax/Errors/unknown_scenario_idx.tjp +11 -0
- data/test/TestSuite/Syntax/Errors/unknown_task.tjp +10 -0
- data/test/all.rb +31 -0
- data/test/test_BatchProcessor.rb +54 -0
- data/test/test_CSV-Reports.rb +101 -0
- data/test/test_Limits.rb +104 -0
- data/test/test_LogicalExpression.rb +110 -0
- data/test/test_MacroTable.rb +51 -0
- data/test/test_Project.rb +57 -0
- data/test/test_PropertySet.rb +71 -0
- data/test/test_Query.rb +83 -0
- data/test/test_RealFormat.rb +83 -0
- data/test/test_RichText.rb +869 -0
- data/test/test_Scheduler.rb +42 -0
- data/test/test_ShiftAssignments.rb +77 -0
- data/test/test_Syntax.rb +41 -0
- data/test/test_TextScanner.rb +95 -0
- data/test/test_TjTime.rb +114 -0
- data/test/test_TjpExample.rb +169 -0
- data/test/test_UTF8String.rb +84 -0
- data/test/test_WorkingHours.rb +56 -0
- metadata +649 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
#!/usr/bin/env ruby -w
|
|
2
|
+
# encoding: UTF-8
|
|
3
|
+
#
|
|
4
|
+
# = ReportTableCell.rb -- The TaskJuggler III Project Management Software
|
|
5
|
+
#
|
|
6
|
+
# Copyright (c) 2006, 2007, 2008, 2009 by Chris Schlaeger <cs@kde.org>
|
|
7
|
+
#
|
|
8
|
+
# This program is free software; you can redistribute it and/or modify
|
|
9
|
+
# it under the terms of version 2 of the GNU General Public License as
|
|
10
|
+
# published by the Free Software Foundation.
|
|
11
|
+
#
|
|
12
|
+
|
|
13
|
+
class TaskJuggler
|
|
14
|
+
|
|
15
|
+
# This class models the output format independent version of a cell in a
|
|
16
|
+
# ReportTableElement. It belongs to a certain ReportTableLine and
|
|
17
|
+
# ReportTableColumn. Normally a cell contains text on a colored background.
|
|
18
|
+
# By help of the @special variable it can alternatively contain any object the
|
|
19
|
+
# provides the necessary output methods such as to_html.
|
|
20
|
+
class ReportTableCell
|
|
21
|
+
|
|
22
|
+
attr_reader :line
|
|
23
|
+
attr_accessor :data, :text, :url, :category, :hidden, :alignment,
|
|
24
|
+
:padding, :indent, :fontSize, :fontColor, :bold, :width,
|
|
25
|
+
:rows, :columns, :special
|
|
26
|
+
|
|
27
|
+
# Create the ReportTableCell object and initialize the attributes to some
|
|
28
|
+
# default values. _line_ is the ReportTableLine this cell belongs to. _text_
|
|
29
|
+
# is the text that should appear in the cell. _headerCell_ is a flag that
|
|
30
|
+
# must be true only for table header cells.
|
|
31
|
+
def initialize(line, text = '', headerCell = false)
|
|
32
|
+
@line = line
|
|
33
|
+
@line.addCell(self) if line
|
|
34
|
+
|
|
35
|
+
@headerCell = headerCell
|
|
36
|
+
# The printable form of the cell content
|
|
37
|
+
@text = text
|
|
38
|
+
# A URL that is associated with the content of the cell.
|
|
39
|
+
@url = nil
|
|
40
|
+
# The original data of the cell content (optional, nil if not provided)
|
|
41
|
+
@data = nil
|
|
42
|
+
@category = nil
|
|
43
|
+
@hidden = false
|
|
44
|
+
# How to horizontally align the cell
|
|
45
|
+
@alignment = :center
|
|
46
|
+
# Horizontal padding between frame and cell content
|
|
47
|
+
@padding = 3
|
|
48
|
+
# Whether or not to indent the cell. If not nil, it is a Fixnum
|
|
49
|
+
# indicating the indentation level.
|
|
50
|
+
@indent = nil
|
|
51
|
+
@fontSize = nil
|
|
52
|
+
@fontColor = 0x000000
|
|
53
|
+
@bold = false
|
|
54
|
+
@width = nil
|
|
55
|
+
@rows = 1
|
|
56
|
+
@columns = 1
|
|
57
|
+
# Ignore everything and use this reference to generate the output.
|
|
58
|
+
@special = nil
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Return true if two cells are similar enough so that they can be merged in
|
|
62
|
+
# the report to a single, wider cell. _c_ is the cell to compare this cell
|
|
63
|
+
# with.
|
|
64
|
+
def ==(c)
|
|
65
|
+
@text == c.text &&
|
|
66
|
+
@alignment == c.alignment &&
|
|
67
|
+
@padding == c.padding &&
|
|
68
|
+
@indent == c.indent &&
|
|
69
|
+
@category == c.category
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Turn the abstract cell representation into an HTML element tree.
|
|
73
|
+
def to_html
|
|
74
|
+
return nil if @hidden
|
|
75
|
+
return @special.to_html if @special
|
|
76
|
+
|
|
77
|
+
# Determine cell attributes
|
|
78
|
+
attribs = { }
|
|
79
|
+
attribs['rowspan'] = "#{@rows}" if @rows > 1
|
|
80
|
+
attribs['colspan'] = "#{@columns}" if @columns > 1
|
|
81
|
+
attribs['class'] = @category ? @category : 'tabcell'
|
|
82
|
+
cell = XMLElement.new('td', attribs)
|
|
83
|
+
|
|
84
|
+
# Determine cell style
|
|
85
|
+
alignSymbols = [ :left, :center, :right ]
|
|
86
|
+
aligns = %w( left center right)
|
|
87
|
+
style = "text-align:#{aligns[alignSymbols.index(@alignment)]}; "
|
|
88
|
+
if @indent && @alignment != :center
|
|
89
|
+
if @alignment == :left
|
|
90
|
+
style += "padding-left:#{@padding + @indent * 8}px; "
|
|
91
|
+
style += "padding-right:#{@padding}px; " unless @padding == 3
|
|
92
|
+
elsif @alignment == :right
|
|
93
|
+
style += "padding-left:#{@padding}px; " unless @padding == 3
|
|
94
|
+
style += "padding-right:#{@padding +
|
|
95
|
+
(@line.table.maxIndent - @indent) * 8}px; "
|
|
96
|
+
end
|
|
97
|
+
elsif @padding != 3
|
|
98
|
+
style += "padding-left:#{@padding}px; padding-right:#{@padding}px; "
|
|
99
|
+
end
|
|
100
|
+
unless @text.is_a?(TaskJuggler::RichText)
|
|
101
|
+
style += 'font-weight:bold; ' if @bold
|
|
102
|
+
style += "font-size: #{@fontSize}px; " if fontSize
|
|
103
|
+
end
|
|
104
|
+
unless @fontColor == 0
|
|
105
|
+
style += "color:#{'#%06X' % @fontColor}; "
|
|
106
|
+
end
|
|
107
|
+
style += "width: #{@width}px; " if @width
|
|
108
|
+
if @text.is_a?(TaskJuggler::RichText) && @line && @line.table.equiLines
|
|
109
|
+
style += "height:#{@line.height - 3}px; "
|
|
110
|
+
end
|
|
111
|
+
cell << (div = XMLElement.new('div',
|
|
112
|
+
'class' => @category ? 'celldiv' : 'headercelldiv', 'style' => style))
|
|
113
|
+
|
|
114
|
+
if url
|
|
115
|
+
div << (a = XMLElement.new('a', 'href' => @url))
|
|
116
|
+
a << XMLText.new(@text.is_a?(TaskJuggler::RichText) ? @text.to_s : text)
|
|
117
|
+
else
|
|
118
|
+
div << (@text.is_a?(TaskJuggler::RichText) ?
|
|
119
|
+
@text.to_html : XMLText.new(@text))
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
cell
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Add the text content of the cell to an Array of Arrays form of the table.
|
|
126
|
+
def to_csv(csv)
|
|
127
|
+
# We only support left indentation in CSV files as the spaces for right
|
|
128
|
+
# indentation will be disregarded by most applications.
|
|
129
|
+
indent = @indent && @alignment == :left ? ' ' * @indent : ''
|
|
130
|
+
if @special
|
|
131
|
+
csv[-1] << @special.to_csv
|
|
132
|
+
elsif @data && @data.is_a?(String)
|
|
133
|
+
csv[-1] << indent + @data
|
|
134
|
+
elsif @text
|
|
135
|
+
csv[-1] << indent + @text
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
end
|
|
142
|
+
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#!/usr/bin/env ruby -w
|
|
2
|
+
# encoding: UTF-8
|
|
3
|
+
#
|
|
4
|
+
# = ReportTableColumn.rb -- The TaskJuggler III Project Management Software
|
|
5
|
+
#
|
|
6
|
+
# Copyright (c) 2006, 2007, 2008, 2009 by Chris Schlaeger <cs@kde.org>
|
|
7
|
+
#
|
|
8
|
+
# This program is free software; you can redistribute it and/or modify
|
|
9
|
+
# it under the terms of version 2 of the GNU General Public License as
|
|
10
|
+
# published by the Free Software Foundation.
|
|
11
|
+
#
|
|
12
|
+
|
|
13
|
+
class TaskJuggler
|
|
14
|
+
|
|
15
|
+
# The ReportTableColumn class models the output format independend column of a
|
|
16
|
+
# ReportTable. It usually just contains the table header description. The
|
|
17
|
+
# table header comprises of one or two lines per column. So each column header
|
|
18
|
+
# consists of 2 cells. @cell1 is the top cell and must be present. @cell2 is
|
|
19
|
+
# the optional bottom cell. If @cell2 is hidden, @cell1 takes all the vertical
|
|
20
|
+
# space.
|
|
21
|
+
#
|
|
22
|
+
# For some columns, the table does not contain the usual grid lines but
|
|
23
|
+
# another abstract object that responds to the usual generator methods such as
|
|
24
|
+
# to_html(). In such a case, @cell1 references the embedded object via its
|
|
25
|
+
# special variable. The embedded object then replaced the complete column
|
|
26
|
+
# content.
|
|
27
|
+
class ReportTableColumn
|
|
28
|
+
|
|
29
|
+
attr_reader :definition, :cell1, :cell2
|
|
30
|
+
attr_accessor :scrollbar
|
|
31
|
+
|
|
32
|
+
# Create a new column. _table_ is a reference to the ReportTable this column
|
|
33
|
+
# belongs to. _definition_ is the TableColumnDefinition of the column from
|
|
34
|
+
# the project definition. _title_ is the text that is used for the column
|
|
35
|
+
# header.
|
|
36
|
+
def initialize(table, definition, title)
|
|
37
|
+
@table = table
|
|
38
|
+
# Register this new column with the ReportTable.
|
|
39
|
+
@table.addColumn(self)
|
|
40
|
+
@definition = definition
|
|
41
|
+
# Register this new column with the TableColumnDefinition.
|
|
42
|
+
definition.column = self if definition
|
|
43
|
+
|
|
44
|
+
# Create the 2 cells of the header.
|
|
45
|
+
@cell1 = ReportTableCell.new(nil, title, true)
|
|
46
|
+
@cell1.padding = 5
|
|
47
|
+
@cell2 = ReportTableCell.new(nil, '', true)
|
|
48
|
+
# Header text is always bold.
|
|
49
|
+
@cell1.bold = @cell2.bold = true
|
|
50
|
+
# This variable is set to true if the column requires a scrollbar later
|
|
51
|
+
# on.
|
|
52
|
+
@scrollbar = false
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Return the mininum required width for the column.
|
|
56
|
+
def minWidth
|
|
57
|
+
width = @cell1.width
|
|
58
|
+
width = @cell2.width if width.nil? || @cell2.width > width
|
|
59
|
+
width
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Convert the abstract representation into HTML elements.
|
|
63
|
+
def to_html(row)
|
|
64
|
+
if row == 1
|
|
65
|
+
@cell1.to_html
|
|
66
|
+
else
|
|
67
|
+
@cell2.to_html
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Put the abstract representation into an Array. _csv_ is an Array of Arrays
|
|
72
|
+
# of Strings. We have an Array with Strings for every cell. The outer Array
|
|
73
|
+
# holds the Arrays representing the lines.
|
|
74
|
+
def to_csv(csv)
|
|
75
|
+
# For CSV reports we can only include the first header line.
|
|
76
|
+
@cell1.to_csv(csv)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
end
|
|
82
|
+
|
|
@@ -0,0 +1,852 @@
|
|
|
1
|
+
#!/usr/bin/env ruby -w
|
|
2
|
+
# encoding: UTF-8
|
|
3
|
+
#
|
|
4
|
+
# = ReportTableElement.rb -- The TaskJuggler III Project Management Software
|
|
5
|
+
#
|
|
6
|
+
# Copyright (c) 2006, 2007, 2008, 2009 by Chris Schlaeger <cs@kde.org>
|
|
7
|
+
#
|
|
8
|
+
# This program is free software; you can redistribute it and/or modify
|
|
9
|
+
# it under the terms of version 2 of the GNU General Public License as
|
|
10
|
+
# published by the Free Software Foundation.
|
|
11
|
+
#
|
|
12
|
+
|
|
13
|
+
require 'reports/GanttChart'
|
|
14
|
+
require 'reports/ReportTableLegend'
|
|
15
|
+
require 'reports/ColumnTable'
|
|
16
|
+
require 'Query'
|
|
17
|
+
|
|
18
|
+
class TaskJuggler
|
|
19
|
+
|
|
20
|
+
# This is base class for all types of tabular report elements. All tabular
|
|
21
|
+
# report elements are converted to an abstract (output independent)
|
|
22
|
+
# intermediate form first, before the are turned into the requested output
|
|
23
|
+
# format.
|
|
24
|
+
class ReportTableElement < ReportElement
|
|
25
|
+
|
|
26
|
+
attr_reader :legend
|
|
27
|
+
|
|
28
|
+
# Generate a new ReportTableElement object.
|
|
29
|
+
def initialize(report)
|
|
30
|
+
super
|
|
31
|
+
|
|
32
|
+
# Reference to the intermediate representation.
|
|
33
|
+
@table = nil
|
|
34
|
+
|
|
35
|
+
@legend = ReportTableLegend.new
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# This is an abstract member that all sub classes must re-implement. It may
|
|
39
|
+
# or may not do something though.
|
|
40
|
+
def generateIntermediateFormat
|
|
41
|
+
raise 'This function must be overriden by derived classes.'
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Turn the ReportTableElement into an equivalent HTML element tree.
|
|
45
|
+
def to_html
|
|
46
|
+
html = []
|
|
47
|
+
|
|
48
|
+
# Make sure we have some margins around the report.
|
|
49
|
+
html << (frame = XMLElement.new('div',
|
|
50
|
+
'style' => 'margin: 35px 5% 25px 5%; '))
|
|
51
|
+
|
|
52
|
+
if @prolog
|
|
53
|
+
@prolog.sectionNumbers = false
|
|
54
|
+
frame << @prolog.to_html
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
frame << (table = XMLElement.new('table', 'summary' => 'Report Table',
|
|
58
|
+
'cellspacing' => '2', 'border' => '0',
|
|
59
|
+
'cellpadding' => '0', 'align' => 'center',
|
|
60
|
+
'class' => 'tabback'))
|
|
61
|
+
|
|
62
|
+
# The headline is put in a sub-table to appear bigger.
|
|
63
|
+
if @headline
|
|
64
|
+
table << (thead = XMLElement.new('thead'))
|
|
65
|
+
thead << (tr = XMLElement.new('tr'))
|
|
66
|
+
tr << (td = XMLElement.new('td'))
|
|
67
|
+
td << (table1 = XMLElement.new('table', 'summary' => 'headline',
|
|
68
|
+
'cellspacing' => '1', 'border' => '0',
|
|
69
|
+
'cellpadding' => '5',
|
|
70
|
+
'align' => 'center', 'width' => '100%'))
|
|
71
|
+
table1 << (tr1 = XMLElement.new('tr'))
|
|
72
|
+
tr1 << (td1 = XMLElement.new('td', 'align' => 'center',
|
|
73
|
+
'style' => 'font-size:16px; ' +
|
|
74
|
+
'font-weight:bold',
|
|
75
|
+
'class' => 'tabfront'))
|
|
76
|
+
td1 << XMLNamedText.new(@headline, 'p')
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Now generate the actual table with the data.
|
|
80
|
+
table << (tbody = XMLElement.new('tbody'))
|
|
81
|
+
tbody << (tr = XMLElement.new('tr'))
|
|
82
|
+
tr << (td = XMLElement.new('td'))
|
|
83
|
+
td << @table.to_html
|
|
84
|
+
|
|
85
|
+
# Embedd the caption as RichText into the table footer.
|
|
86
|
+
if @caption
|
|
87
|
+
tbody << (tr = XMLElement.new('tr'))
|
|
88
|
+
tr << (td = XMLElement.new('td', 'class' => 'tabback'))
|
|
89
|
+
td << (div = XMLElement.new('div', 'class' => 'caption',
|
|
90
|
+
'style' => 'margin:1px'))
|
|
91
|
+
@caption.sectionNumbers = false
|
|
92
|
+
div << @caption.to_html
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# A sub-table with the legend.
|
|
96
|
+
tbody << (tr = XMLElement.new('tr', 'style' => 'font-size:10px;'))
|
|
97
|
+
tr << (td = XMLElement.new('td', 'style' =>
|
|
98
|
+
'padding-left:1px; padding-right:1px;'))
|
|
99
|
+
td << @legend.to_html
|
|
100
|
+
|
|
101
|
+
# The footer with some administrative information.
|
|
102
|
+
tbody << (tr = XMLElement.new('tr', 'style' => 'font-size:9px'))
|
|
103
|
+
tr << (td = XMLElement.new('td', 'class' => 'tabfooter'))
|
|
104
|
+
td << XMLText.new(@project['copyright'] + " - ") if @project['copyright']
|
|
105
|
+
td << XMLText.new("Project: #{@project['name']} " +
|
|
106
|
+
"Version: #{@project['version']} - " +
|
|
107
|
+
"Created on #{TjTime.now.to_s("%Y-%m-%d %H:%M:%S")} " +
|
|
108
|
+
"with ")
|
|
109
|
+
td << XMLNamedText.new("#{AppConfig.packageName}", 'a',
|
|
110
|
+
'href' => "#{AppConfig.contact}")
|
|
111
|
+
td << XMLText.new(" v#{AppConfig.version}")
|
|
112
|
+
|
|
113
|
+
if @epilog
|
|
114
|
+
@epilog.sectionNumbers = false
|
|
115
|
+
frame << @epilog.to_html
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
html
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Convert the ReportElement into an Array of Arrays. It has one Array for
|
|
122
|
+
# each line. The nested Arrays have one String for each column.
|
|
123
|
+
def to_csv
|
|
124
|
+
@table.to_csv
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
protected
|
|
128
|
+
|
|
129
|
+
# Generates cells for the table header. _columnDef_ is the
|
|
130
|
+
# TableColumnDefinition object that describes the column. Based on the id of
|
|
131
|
+
# the column different actions need to be taken to generate the header text.
|
|
132
|
+
def generateHeaderCell(columnDef)
|
|
133
|
+
case columnDef.id
|
|
134
|
+
when 'chart'
|
|
135
|
+
# For the 'chart' column we generate a GanttChart object. The sizes are
|
|
136
|
+
# set so that the lines of the Gantt chart line up with the lines of the
|
|
137
|
+
# table.
|
|
138
|
+
gantt = GanttChart.new(@now, @weekStartsMonday, self)
|
|
139
|
+
gantt.generateByScale(@start, @end, columnDef.scale)
|
|
140
|
+
# The header consists of 2 lines separated by a 1 pixel boundary.
|
|
141
|
+
gantt.header.height = @table.headerLineHeight * 2 + 1
|
|
142
|
+
# The maximum width of the chart. In case it needs more space, a
|
|
143
|
+
# scrollbar is shown or the chart gets truncated depending on the output
|
|
144
|
+
# format.
|
|
145
|
+
gantt.viewWidth = columnDef.width
|
|
146
|
+
column = ReportTableColumn.new(@table, columnDef, '')
|
|
147
|
+
column.cell1.special = gantt
|
|
148
|
+
column.cell2.hidden = true
|
|
149
|
+
column.scrollbar = gantt.hasScrollbar?
|
|
150
|
+
@table.equiLines = true
|
|
151
|
+
when 'hourly'
|
|
152
|
+
genCalChartHeader(columnDef, @start.midnight, :sameTimeNextHour,
|
|
153
|
+
:weekdayAndDate, :hour)
|
|
154
|
+
when 'daily'
|
|
155
|
+
genCalChartHeader(columnDef, @start.midnight, :sameTimeNextDay,
|
|
156
|
+
:shortMonthName, :day)
|
|
157
|
+
when 'weekly'
|
|
158
|
+
genCalChartHeader(columnDef, @start.beginOfWeek(@weekStartsMonday),
|
|
159
|
+
:sameTimeNextWeek, :shortMonthName, :day)
|
|
160
|
+
when 'monthly'
|
|
161
|
+
genCalChartHeader(columnDef, @start.beginOfMonth, :sameTimeNextMonth,
|
|
162
|
+
:year, :shortMonthName)
|
|
163
|
+
when 'quarterly'
|
|
164
|
+
genCalChartHeader(columnDef, @start.beginOfQuarter,
|
|
165
|
+
:sameTimeNextQuarter, :year, :quarterName)
|
|
166
|
+
when 'yearly'
|
|
167
|
+
genCalChartHeader(columnDef, @start.beginOfYear, :sameTimeNextYear,
|
|
168
|
+
nil, :year)
|
|
169
|
+
else
|
|
170
|
+
# This is the most common case. It does not need any special treatment.
|
|
171
|
+
# We just set the pre-defined or user-defined column title in the first
|
|
172
|
+
# row of the header. The 2nd row is not visible.
|
|
173
|
+
column = ReportTableColumn.new(@table, columnDef, columnDef.title)
|
|
174
|
+
column.cell1.rows = 2
|
|
175
|
+
column.cell2.hidden = true
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# Generate a ReportTableLine for each of the tasks in _taskList_. In case
|
|
180
|
+
# _resourceList_ is not nil, it also generates the nested resource lines for
|
|
181
|
+
# each resource that is assigned to the particular task. If _scopeLine_
|
|
182
|
+
# is defined, the generated task lines will be within the scope this resource
|
|
183
|
+
# line.
|
|
184
|
+
def generateTaskList(taskList, resourceList, scopeLine)
|
|
185
|
+
queryAttrs = { 'scopeProperty' => scopeLine ? scopeLine.property : nil,
|
|
186
|
+
'loadUnit' => @loadUnit,
|
|
187
|
+
'numberFormat' => @numberFormat,
|
|
188
|
+
'currencyFormat' => @currencyFormat,
|
|
189
|
+
'start' => @start, 'end' => @end,
|
|
190
|
+
'costAccount' => @costAccount,
|
|
191
|
+
'revenueAccount' => @revenueAccount }
|
|
192
|
+
taskList.query = Query.new(queryAttrs)
|
|
193
|
+
taskList.sort!
|
|
194
|
+
|
|
195
|
+
# The primary line counter. Is not used for enclosed lines.
|
|
196
|
+
no = 0
|
|
197
|
+
# The scope line counter. It's reset for each new scope.
|
|
198
|
+
lineNo = scopeLine ? scopeLine.lineNo : 0
|
|
199
|
+
# Init the variable to get a larger scope
|
|
200
|
+
line = nil
|
|
201
|
+
taskList.each do |task|
|
|
202
|
+
no += 1
|
|
203
|
+
Log.activity if lineNo % 10 == 0
|
|
204
|
+
lineNo += 1
|
|
205
|
+
@scenarios.each do |scenarioIdx|
|
|
206
|
+
# Generate line for each task.
|
|
207
|
+
line = ReportTableLine.new(@table, task, scopeLine)
|
|
208
|
+
|
|
209
|
+
line.no = no unless scopeLine
|
|
210
|
+
line.lineNo = lineNo
|
|
211
|
+
line.subLineNo = @table.lines
|
|
212
|
+
setIndent(line, @taskRoot, taskList.treeMode?)
|
|
213
|
+
|
|
214
|
+
# Generate a cell for each column in this line.
|
|
215
|
+
@columns.each do |columnDef|
|
|
216
|
+
next unless generateTableCell(line, task, columnDef, scenarioIdx)
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
if resourceList
|
|
221
|
+
# If we have a resourceList we generate nested lines for each of the
|
|
222
|
+
# resources that are assigned to this task and pass the user-defined
|
|
223
|
+
# filter.
|
|
224
|
+
resourceList.setSorting(@sortResources)
|
|
225
|
+
assignedResourceList = filterResourceList(resourceList, task,
|
|
226
|
+
@hideResource, @rollupResource)
|
|
227
|
+
assignedResourceList.sort!
|
|
228
|
+
lineNo = generateResourceList(assignedResourceList, nil, line)
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
lineNo
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# Generate a ReportTableLine for each of the resources in _resourceList_. In
|
|
235
|
+
# case _taskList_ is not nil, it also generates the nested task lines for
|
|
236
|
+
# each task that the resource is assigned to. If _scopeLine_ is defined, the
|
|
237
|
+
# generated resource lines will be within the scope this task line.
|
|
238
|
+
def generateResourceList(resourceList, taskList, scopeLine)
|
|
239
|
+
queryAttrs = { 'scopeProperty' => scopeLine ? scopeLine.property : nil,
|
|
240
|
+
'loadUnit' => @loadUnit,
|
|
241
|
+
'numberFormat' => @numberFormat,
|
|
242
|
+
'currencyFormat' => @currencyFormat,
|
|
243
|
+
'start' => @start, 'end' => @end,
|
|
244
|
+
'costAccount' => @costAccount,
|
|
245
|
+
'revenueAccount' => @revenueAccount }
|
|
246
|
+
resourceList.query = Query.new(queryAttrs)
|
|
247
|
+
resourceList.sort!
|
|
248
|
+
|
|
249
|
+
# The primary line counter. Is not used for enclosed lines.
|
|
250
|
+
no = 0
|
|
251
|
+
# The scope line counter. It's reset for each new scope.
|
|
252
|
+
lineNo = scopeLine ? scopeLine.lineNo : 0
|
|
253
|
+
# Init the variable to get a larger scope
|
|
254
|
+
line = nil
|
|
255
|
+
resourceList.each do |resource|
|
|
256
|
+
no += 1
|
|
257
|
+
Log.activity if lineNo % 10 == 0
|
|
258
|
+
lineNo += 1
|
|
259
|
+
@scenarios.each do |scenarioIdx|
|
|
260
|
+
# Generate line for each resource.
|
|
261
|
+
line = ReportTableLine.new(@table, resource, scopeLine)
|
|
262
|
+
|
|
263
|
+
line.no = no unless scopeLine
|
|
264
|
+
line.lineNo = lineNo
|
|
265
|
+
line.subLineNo = @table.lines
|
|
266
|
+
setIndent(line, @resourceRoot, resourceList.treeMode?)
|
|
267
|
+
|
|
268
|
+
# Generate a cell for each column in this line.
|
|
269
|
+
@columns.each do |column|
|
|
270
|
+
next unless generateTableCell(line, resource, column, scenarioIdx)
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
if taskList
|
|
275
|
+
# If we have a taskList we generate nested lines for each of the
|
|
276
|
+
# tasks that the resource is assigned to and pass the user-defined
|
|
277
|
+
# filter.
|
|
278
|
+
taskList.setSorting(@sortTasks)
|
|
279
|
+
assignedTaskList = filterTaskList(taskList, resource,
|
|
280
|
+
@hideTask, @rollupTask)
|
|
281
|
+
assignedTaskList.sort!
|
|
282
|
+
lineNo = generateTaskList(assignedTaskList, nil, line)
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
lineNo
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
private
|
|
289
|
+
|
|
290
|
+
# Generate the header data for calendar tables. They consists of columns for
|
|
291
|
+
# each hour, day, week, etc. _columnDef_ is the definition of the columns.
|
|
292
|
+
# _t_ is the start time for the calendar. _sameTimeNextFunc_ is a function
|
|
293
|
+
# that is called to advance _t_ to the next table column interval.
|
|
294
|
+
# _name1Func_ and _name2Func_ are functions that return the upper and lower
|
|
295
|
+
# title of the particular column.
|
|
296
|
+
def genCalChartHeader(columnDef, t, sameTimeNextFunc, name1Func, name2Func)
|
|
297
|
+
tableColumn = ReportTableColumn.new(@table, columnDef, '')
|
|
298
|
+
|
|
299
|
+
# Calendar chars only work when all lines have same height.
|
|
300
|
+
@table.equiLines = true
|
|
301
|
+
|
|
302
|
+
# Embedded tables have unpredictable width. So we always need to make room
|
|
303
|
+
# for a potential scrollbar.
|
|
304
|
+
tableColumn.scrollbar = true
|
|
305
|
+
|
|
306
|
+
# Create the table that is embedded in this column.
|
|
307
|
+
tableColumn.cell1.special = table = ColumnTable.new
|
|
308
|
+
table.equiLines = true
|
|
309
|
+
tableColumn.cell2.hidden = true
|
|
310
|
+
table.maxWidth = columnDef.width
|
|
311
|
+
|
|
312
|
+
# Iterate over the report interval until we hit the end date. The
|
|
313
|
+
# iteration is done with 2 nested loops. The outer loops generates the
|
|
314
|
+
# intervals for the upper (larger) scale. The inner loop generates the
|
|
315
|
+
# lower (smaller) scale.
|
|
316
|
+
while t < @end
|
|
317
|
+
cellsInInterval = 0
|
|
318
|
+
# Label for upper scale. The yearly calendar only has a lower scale.
|
|
319
|
+
currentInterval = t.send(name1Func) if name1Func
|
|
320
|
+
firstColumn = nil
|
|
321
|
+
# The innter loops terminates when the label for the upper scale has
|
|
322
|
+
# changed to the next scale cell.
|
|
323
|
+
while t < @end && (name1Func.nil? || t.send(name1Func) == currentInterval)
|
|
324
|
+
# call TjTime::sameTimeNext... function to get the end of the column.
|
|
325
|
+
nextT = t.send(sameTimeNextFunc)
|
|
326
|
+
iv = Interval.new(t, nextT)
|
|
327
|
+
# Create the new column object.
|
|
328
|
+
column = ReportTableColumn.new(table, nil, '')
|
|
329
|
+
# Store the date of the column in the original form.
|
|
330
|
+
column.cell1.data = t.to_s(@timeFormat)
|
|
331
|
+
# The upper scale cells will be merged into one large cell that spans
|
|
332
|
+
# all lower scale cells that belong to this upper cell.
|
|
333
|
+
if firstColumn.nil?
|
|
334
|
+
firstColumn = column
|
|
335
|
+
column.cell1.text = currentInterval.to_s
|
|
336
|
+
else
|
|
337
|
+
column.cell1.hidden = true
|
|
338
|
+
end
|
|
339
|
+
column.cell2.text = t.send(name2Func).to_s
|
|
340
|
+
# TODO: The width should be taken from some data structure.
|
|
341
|
+
column.cell2.width = 20
|
|
342
|
+
# Off-duty cells will have a different color than working time cells.
|
|
343
|
+
unless @project.isWorkingTime(iv)
|
|
344
|
+
column.cell2.category = 'tabhead_offduty'
|
|
345
|
+
end
|
|
346
|
+
cellsInInterval += 1
|
|
347
|
+
|
|
348
|
+
t = nextT
|
|
349
|
+
end
|
|
350
|
+
# The the first upper scale cell how many trailing hidden cells are
|
|
351
|
+
# following.
|
|
352
|
+
firstColumn.cell1.columns = cellsInInterval
|
|
353
|
+
end
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
# Generate a cell of the table. _line_ is the ReportTableLine that this cell
|
|
357
|
+
# should belong to. _property_ is the PropertyTreeNode that is reported in
|
|
358
|
+
# this _line_. _columnDef_ is the TableColumnDefinition of the column this
|
|
359
|
+
# cell should belong to. _scenarioIdx_ is the index of the scenario that is
|
|
360
|
+
# reported in this _line_.
|
|
361
|
+
#
|
|
362
|
+
# There are 4 kinds of cells. The most simple one is the standard cell. It
|
|
363
|
+
# literally reports the value of a property attribute. Calculated cells are
|
|
364
|
+
# more flexible. They contain computed values. The values are computed at
|
|
365
|
+
# cell generation time. The calendar columns consist of multiple sub
|
|
366
|
+
# columns. In such a case many cells are generated with a single call of
|
|
367
|
+
# this method. The last kind of cell is actually not a cell. It just
|
|
368
|
+
# generates the chart objects that belong to the property in this line.
|
|
369
|
+
def generateTableCell(line, property, columnDef, scenarioIdx)
|
|
370
|
+
case columnDef.id
|
|
371
|
+
when 'chart'
|
|
372
|
+
# Generate a hidden cell. The real meat is in the actual chart object,
|
|
373
|
+
# not in this cell.
|
|
374
|
+
cell = ReportTableCell.new(line)
|
|
375
|
+
cell.hidden = true
|
|
376
|
+
cell.text = nil
|
|
377
|
+
# The GanttChart can be reached via the special variable of the column
|
|
378
|
+
# header.
|
|
379
|
+
chart = columnDef.column.cell1.special
|
|
380
|
+
GanttLine.new(chart, property,
|
|
381
|
+
line.scopeLine ? line.scopeLine.property : nil,
|
|
382
|
+
scenarioIdx, (line.subLineNo - 1) * (line.height + 1),
|
|
383
|
+
line.height)
|
|
384
|
+
return true
|
|
385
|
+
# The calendar cells can be all generated by the same function. But we
|
|
386
|
+
# need to use different parameters.
|
|
387
|
+
when 'hourly'
|
|
388
|
+
start = @start.midnight
|
|
389
|
+
sameTimeNextFunc = :sameTimeNextHour
|
|
390
|
+
when 'daily'
|
|
391
|
+
start = @start.midnight
|
|
392
|
+
sameTimeNextFunc = :sameTimeNextDay
|
|
393
|
+
when 'weekly'
|
|
394
|
+
start = @start.beginOfWeek(@weekStartsMonday)
|
|
395
|
+
sameTimeNextFunc = :sameTimeNextWeek
|
|
396
|
+
when 'monthly'
|
|
397
|
+
start = @start.beginOfMonth
|
|
398
|
+
sameTimeNextFunc = :sameTimeNextMonth
|
|
399
|
+
when 'quarterly'
|
|
400
|
+
start = @start.beginOfQuarter
|
|
401
|
+
sameTimeNextFunc = :sameTimeNextQuarter
|
|
402
|
+
when 'yearly'
|
|
403
|
+
start = @start.beginOfYear
|
|
404
|
+
sameTimeNextFunc = :sameTimeNextYear
|
|
405
|
+
else
|
|
406
|
+
if calculated?(columnDef.id)
|
|
407
|
+
genCalculatedCell(scenarioIdx, line, columnDef, property)
|
|
408
|
+
return true
|
|
409
|
+
else
|
|
410
|
+
return genStandardCell(scenarioIdx, line, columnDef)
|
|
411
|
+
end
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
# The calendar cells don't live in this ReportTable but in an embedded
|
|
415
|
+
# ReportTable that can be reached via the column header special variable.
|
|
416
|
+
# For embedded column tables we need to create a new line.
|
|
417
|
+
tcLine = ReportTableLine.new(columnDef.column.cell1.special,
|
|
418
|
+
line.property, line.scopeLine)
|
|
419
|
+
|
|
420
|
+
# Depending on the property type we use different generator functions.
|
|
421
|
+
if property.is_a?(Task)
|
|
422
|
+
genCalChartTaskCell(scenarioIdx, tcLine, columnDef, start,
|
|
423
|
+
sameTimeNextFunc)
|
|
424
|
+
elsif property.is_a?(Resource)
|
|
425
|
+
genCalChartResourceCell(scenarioIdx, tcLine, columnDef, start,
|
|
426
|
+
sameTimeNextFunc)
|
|
427
|
+
else
|
|
428
|
+
raise "Unknown property type #{property.class}"
|
|
429
|
+
end
|
|
430
|
+
true
|
|
431
|
+
end
|
|
432
|
+
|
|
433
|
+
# Generate a ReportTableCell filled the value of an attribute of the
|
|
434
|
+
# property that line is for. It returns true if the cell exists, false for a
|
|
435
|
+
# hidden cell.
|
|
436
|
+
def genStandardCell(scenarioIdx, line, columnDef)
|
|
437
|
+
property = line.property
|
|
438
|
+
# Create a new cell
|
|
439
|
+
cell = newCell(line, cellText(property, scenarioIdx, columnDef.id))
|
|
440
|
+
|
|
441
|
+
if property.is_a?(Task)
|
|
442
|
+
properties = @project.tasks
|
|
443
|
+
elsif property.is_a?(Resource)
|
|
444
|
+
properties = @project.resources
|
|
445
|
+
else
|
|
446
|
+
raise "Unknown property type #{property.class}"
|
|
447
|
+
end
|
|
448
|
+
|
|
449
|
+
# Check if we are dealing with multiple scenarios.
|
|
450
|
+
if @scenarios.length > 1
|
|
451
|
+
# Check if the attribute is not scenario specific
|
|
452
|
+
unless properties.scenarioSpecific?(columnDef.id)
|
|
453
|
+
if scenarioIdx == @scenarios.first
|
|
454
|
+
# Use a somewhat bigger font.
|
|
455
|
+
cell.fontSize = 15
|
|
456
|
+
else
|
|
457
|
+
# And hide the cells for all but the first scenario.
|
|
458
|
+
cell.hidden = true
|
|
459
|
+
return false
|
|
460
|
+
end
|
|
461
|
+
cell.rows = @scenarios.length
|
|
462
|
+
end
|
|
463
|
+
end
|
|
464
|
+
|
|
465
|
+
setStandardCellAttributes(cell, columnDef,
|
|
466
|
+
properties.attributeType(columnDef.id), line)
|
|
467
|
+
|
|
468
|
+
scopeProperty = line.scopeLine ? line.scopeLine.property : nil
|
|
469
|
+
query = Query.new('property' => property,
|
|
470
|
+
'scopeProperty' => scopeProperty,
|
|
471
|
+
'attributeId' => columnDef.id,
|
|
472
|
+
'scenarioIdx' => scenarioIdx, 'loadUnit' => @loadUnit,
|
|
473
|
+
'numberFormat' => @numberFormat,
|
|
474
|
+
'currencyFormat' => @currencyFormat,
|
|
475
|
+
'start' => @start, 'end' => @end,
|
|
476
|
+
'costAccount' => @costAccount,
|
|
477
|
+
'revenueAccount' => @revenueAccount)
|
|
478
|
+
if cell.text
|
|
479
|
+
if columnDef.cellText
|
|
480
|
+
cell.text = expandMacros(columnDef.cellText, cell.text, query)
|
|
481
|
+
end
|
|
482
|
+
else
|
|
483
|
+
cell.text = '<Error>'
|
|
484
|
+
cell.fontColor = 0xFF0000
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
setCellURL(cell, columnDef, query)
|
|
488
|
+
true
|
|
489
|
+
end
|
|
490
|
+
|
|
491
|
+
# Generate a ReportTableCell filled with a calculted value from the property
|
|
492
|
+
# or other sources of information. It returns true if the cell exists, false
|
|
493
|
+
# for a hidden cell. _scenarioIdx_ is the index of the reported scenario.
|
|
494
|
+
# _line_ is the ReportTableLine of the cell. _columnDef_ is the
|
|
495
|
+
# TableColumnDefinition of the column. _property_ is the PropertyTreeNode
|
|
496
|
+
# that is reported in this cell.
|
|
497
|
+
def genCalculatedCell(scenarioIdx, line, columnDef, property)
|
|
498
|
+
# Create a new cell
|
|
499
|
+
cell = newCell(line)
|
|
500
|
+
|
|
501
|
+
unless scenarioSpecific?(columnDef.id)
|
|
502
|
+
if scenarioIdx != @scenarios.first
|
|
503
|
+
cell.hidden = true
|
|
504
|
+
return false
|
|
505
|
+
end
|
|
506
|
+
cell.rows = @scenarios.length
|
|
507
|
+
end
|
|
508
|
+
|
|
509
|
+
setStandardCellAttributes(cell, columnDef, nil, line)
|
|
510
|
+
|
|
511
|
+
return if columnDef.hideCellText &&
|
|
512
|
+
endcolumnDef.hideCellText.eval(property, scopeProperty)
|
|
513
|
+
|
|
514
|
+
scopeProperty = line.scopeLine ? line.scopeLine.property : nil
|
|
515
|
+
query = Query.new('property' => property,
|
|
516
|
+
'scopeProperty' => scopeProperty,
|
|
517
|
+
'attributeId' => columnDef.id,
|
|
518
|
+
'scenarioIdx' => scenarioIdx, 'loadUnit' => @loadUnit,
|
|
519
|
+
'numberFormat' => @numberFormat,
|
|
520
|
+
'currencyFormat' => @currencyFormat,
|
|
521
|
+
'start' => @start, 'end' => @end,
|
|
522
|
+
'costAccount' => @costAccount,
|
|
523
|
+
'revenueAccount' => @revenueAccount)
|
|
524
|
+
query.process
|
|
525
|
+
cell.text = query.result
|
|
526
|
+
|
|
527
|
+
# Some columns need some extra care.
|
|
528
|
+
case columnDef.id
|
|
529
|
+
when 'line'
|
|
530
|
+
cell.text = line.lineNo.to_s
|
|
531
|
+
when 'no'
|
|
532
|
+
cell.text = line.no.to_s
|
|
533
|
+
when 'wbs'
|
|
534
|
+
cell.indent = 2 if line.scopeLine
|
|
535
|
+
end
|
|
536
|
+
|
|
537
|
+
if columnDef.cellText
|
|
538
|
+
cell.text = expandMacros(columnDef.cellText, cell.text, query)
|
|
539
|
+
end
|
|
540
|
+
setCellURL(cell, columnDef, query)
|
|
541
|
+
end
|
|
542
|
+
|
|
543
|
+
# Generate the cells for the task lines of a calendar column. These lines do
|
|
544
|
+
# not directly belong to the @table object but to an embedded ColumnTable
|
|
545
|
+
# object. Therefor a single @table column usually has many cells on each
|
|
546
|
+
# single line. _scenarioIdx_ is the index of the scenario that is reported
|
|
547
|
+
# in this line. _line_ is the @table line. _t_ is the start date for the
|
|
548
|
+
# calendar. _sameTimeNextFunc_ is the function that will move the date to
|
|
549
|
+
# the next cell.
|
|
550
|
+
def genCalChartTaskCell(scenarioIdx, line, columnDef, t, sameTimeNextFunc)
|
|
551
|
+
task = line.property
|
|
552
|
+
# Find out if we have an enclosing resource scope.
|
|
553
|
+
if line.scopeLine && line.scopeLine.property.is_a?(Resource)
|
|
554
|
+
resource = line.scopeLine.property
|
|
555
|
+
else
|
|
556
|
+
resource = nil
|
|
557
|
+
end
|
|
558
|
+
|
|
559
|
+
# Get the interval of the task. In case a date is invalid due to a
|
|
560
|
+
# scheduling problem, we use the full project interval.
|
|
561
|
+
taskIv = Interval.new(task['start', scenarioIdx].nil? ?
|
|
562
|
+
@project['start'] : task['start', scenarioIdx],
|
|
563
|
+
task['end', scenarioIdx].nil? ?
|
|
564
|
+
@project['end'] : task['end', scenarioIdx])
|
|
565
|
+
|
|
566
|
+
query = Query.new('property' => task, 'scopeProperty' => resource,
|
|
567
|
+
'scenarioIdx' => scenarioIdx, 'loadUnit' => @loadUnit,
|
|
568
|
+
'numberFormat' => @numberFormat,
|
|
569
|
+
'currencyFormat' => @currencyFormat,
|
|
570
|
+
'costAccount' => @costAccount,
|
|
571
|
+
'revenueAccount' => @revenueAccount)
|
|
572
|
+
|
|
573
|
+
firstCell = nil
|
|
574
|
+
while t < @end
|
|
575
|
+
# Create a new cell
|
|
576
|
+
cell = newCell(line)
|
|
577
|
+
|
|
578
|
+
# call TjTime::sameTimeNext... function
|
|
579
|
+
nextT = t.send(sameTimeNextFunc)
|
|
580
|
+
cellIv = Interval.new(t, nextT)
|
|
581
|
+
case columnDef.content
|
|
582
|
+
when 'empty'
|
|
583
|
+
# We only generate cells will different background colors.
|
|
584
|
+
when 'load'
|
|
585
|
+
query.attributeId = 'effort'
|
|
586
|
+
query.startIdx = t
|
|
587
|
+
query.endIdx = nextT
|
|
588
|
+
query.process
|
|
589
|
+
# To increase readability, we don't show 0.0 values.
|
|
590
|
+
cell.text = query.result if query.numericalResult > 0.0
|
|
591
|
+
else
|
|
592
|
+
raise "Unknown column content #{column.content}"
|
|
593
|
+
end
|
|
594
|
+
|
|
595
|
+
# Determine cell category (mostly the background color)
|
|
596
|
+
if cellIv.overlaps?(taskIv)
|
|
597
|
+
cell.category = task.container? ? 'calconttask' : 'caltask'
|
|
598
|
+
elsif !@project.isWorkingTime(cellIv)
|
|
599
|
+
cell.category = 'offduty'
|
|
600
|
+
else
|
|
601
|
+
cell.category = 'taskcell'
|
|
602
|
+
end
|
|
603
|
+
cell.category += line.property.get('index') % 2 == 1 ? '1' : '2'
|
|
604
|
+
|
|
605
|
+
tryCellMerging(cell, line, firstCell)
|
|
606
|
+
|
|
607
|
+
t = nextT
|
|
608
|
+
firstCell = cell unless firstCell
|
|
609
|
+
end
|
|
610
|
+
|
|
611
|
+
legend.addCalendarItem('Container Task', 'calconttask1')
|
|
612
|
+
legend.addCalendarItem('Task', 'caltask1')
|
|
613
|
+
legend.addCalendarItem('Off duty time', 'offduty')
|
|
614
|
+
end
|
|
615
|
+
|
|
616
|
+
# Generate the cells for the resource lines of a calendar column. These
|
|
617
|
+
# lines do not directly belong to the @table object but to an embedded
|
|
618
|
+
# ColumnTable object. Therefor a single @table column usually has many cells
|
|
619
|
+
# on each single line. _scenarioIdx_ is the index of the scenario that is
|
|
620
|
+
# reported in this line. _line_ is the @table line. _t_ is the start date
|
|
621
|
+
# for the calendar. _sameTimeNextFunc_ is the function that will move the
|
|
622
|
+
# date to the next cell.
|
|
623
|
+
def genCalChartResourceCell(scenarioIdx, line, columnDef, t,
|
|
624
|
+
sameTimeNextFunc)
|
|
625
|
+
resource = line.property
|
|
626
|
+
# Find out if we have an enclosing task scope.
|
|
627
|
+
if line.scopeLine && line.scopeLine.property.is_a?(Task)
|
|
628
|
+
task = line.scopeLine.property
|
|
629
|
+
# Get the interval of the task. In case a date is invalid due to a
|
|
630
|
+
# scheduling problem, we use the full project interval.
|
|
631
|
+
taskIv = Interval.new(task['start', scenarioIdx].nil? ?
|
|
632
|
+
@project['start'] : task['start', scenarioIdx],
|
|
633
|
+
task['end', scenarioIdx].nil? ?
|
|
634
|
+
@project['end'] : task['end', scenarioIdx])
|
|
635
|
+
else
|
|
636
|
+
task = nil
|
|
637
|
+
end
|
|
638
|
+
|
|
639
|
+
query = Query.new('property' => resource,
|
|
640
|
+
'scenarioIdx' => scenarioIdx, 'loadUnit' => @loadUnit,
|
|
641
|
+
'numberFormat' => @numberFormat,
|
|
642
|
+
'currencyFormat' => @currencyFormat,
|
|
643
|
+
'costAccount' => @costAccount,
|
|
644
|
+
'revenueAccount' => @revenueAccount)
|
|
645
|
+
|
|
646
|
+
firstCell = nil
|
|
647
|
+
while t < @end
|
|
648
|
+
# Create a new cell
|
|
649
|
+
cell = newCell(line)
|
|
650
|
+
|
|
651
|
+
# call TjTime::sameTimeNext... function
|
|
652
|
+
nextT = t.send(sameTimeNextFunc)
|
|
653
|
+
cellIv = Interval.new(t, nextT)
|
|
654
|
+
# Get work load for all tasks.
|
|
655
|
+
query.scopeProperty = nil
|
|
656
|
+
query.attributeId = 'effort'
|
|
657
|
+
query.startIdx = @project.dateToIdx(t, true)
|
|
658
|
+
query.endIdx = @project.dateToIdx(nextT, true) - 1
|
|
659
|
+
query.process
|
|
660
|
+
workLoad = query.numericalResult
|
|
661
|
+
scaledWorkLoad = query.result
|
|
662
|
+
if task
|
|
663
|
+
# Get work load for the particular task.
|
|
664
|
+
query.scopeProperty = task
|
|
665
|
+
query.process
|
|
666
|
+
workLoadTask = query.numericalResult
|
|
667
|
+
scaledWorkLoad = query.result
|
|
668
|
+
else
|
|
669
|
+
workLoadTask = 0.0
|
|
670
|
+
end
|
|
671
|
+
# Get unassigned work load.
|
|
672
|
+
query.attributeId = 'freework'
|
|
673
|
+
query.process
|
|
674
|
+
freeLoad = query.numericalResult
|
|
675
|
+
case columnDef.content
|
|
676
|
+
when 'empty'
|
|
677
|
+
# We only generate cells will different background colors.
|
|
678
|
+
when 'load'
|
|
679
|
+
# Report the workload of the resource in this time interval.
|
|
680
|
+
# To increase readability, we don't show 0.0 values.
|
|
681
|
+
wLoad = task ? workLoadTask : workLoad
|
|
682
|
+
if wLoad > 0.0
|
|
683
|
+
cell.text = scaledWorkLoad
|
|
684
|
+
end
|
|
685
|
+
else
|
|
686
|
+
raise "Unknown column content #{column.content}"
|
|
687
|
+
end
|
|
688
|
+
|
|
689
|
+
# Determine cell category (mostly the background color)
|
|
690
|
+
cell.category = if task
|
|
691
|
+
if cellIv.overlaps?(taskIv)
|
|
692
|
+
if workLoadTask > 0.0 && freeLoad == 0.0
|
|
693
|
+
'busy'
|
|
694
|
+
elsif workLoad == 0.0 && freeLoad == 0.0
|
|
695
|
+
'offduty'
|
|
696
|
+
else
|
|
697
|
+
'loaded'
|
|
698
|
+
end
|
|
699
|
+
else
|
|
700
|
+
if freeLoad > 0.0
|
|
701
|
+
'free'
|
|
702
|
+
elsif workLoad == 0.0 && freeLoad == 0.0
|
|
703
|
+
'offduty'
|
|
704
|
+
else
|
|
705
|
+
'resourcecell'
|
|
706
|
+
end
|
|
707
|
+
end
|
|
708
|
+
else
|
|
709
|
+
if workLoad > 0.0 && freeLoad == 0.0
|
|
710
|
+
'busy'
|
|
711
|
+
elsif workLoad > 0.0 && freeLoad > 0.0
|
|
712
|
+
'loaded'
|
|
713
|
+
elsif workLoad == 0.0 && freeLoad > 0.0
|
|
714
|
+
'free'
|
|
715
|
+
else
|
|
716
|
+
'offduty'
|
|
717
|
+
end
|
|
718
|
+
end
|
|
719
|
+
cell.category += line.property.get('index') % 2 == 1 ? '1' : '2'
|
|
720
|
+
|
|
721
|
+
tryCellMerging(cell, line, firstCell)
|
|
722
|
+
|
|
723
|
+
t = nextT
|
|
724
|
+
firstCell = cell unless firstCell
|
|
725
|
+
end
|
|
726
|
+
|
|
727
|
+
legend.addCalendarItem('Resource is fully loaded', 'busy1')
|
|
728
|
+
legend.addCalendarItem('Resource is partially loaded', 'loaded1')
|
|
729
|
+
legend.addCalendarItem('Resource is available', 'free')
|
|
730
|
+
legend.addCalendarItem('Off duty time', 'offduty')
|
|
731
|
+
end
|
|
732
|
+
|
|
733
|
+
def setStandardCellAttributes(cell, columnDef, attributeType, line)
|
|
734
|
+
# Determine whether it should be indented
|
|
735
|
+
if indent(columnDef.id, attributeType)
|
|
736
|
+
cell.indent = line.indentation
|
|
737
|
+
end
|
|
738
|
+
|
|
739
|
+
# Determine the cell alignment
|
|
740
|
+
cell.alignment = alignment(columnDef.id, attributeType)
|
|
741
|
+
|
|
742
|
+
# Set background color
|
|
743
|
+
if line.property.is_a?(Task)
|
|
744
|
+
cell.category = line.property.get('index') % 2 == 1 ?
|
|
745
|
+
'taskcell1' : 'taskcell2'
|
|
746
|
+
else
|
|
747
|
+
cell.category = line.property.get('index') % 2 == 1 ?
|
|
748
|
+
'resourcecell1' : 'resourcecell2'
|
|
749
|
+
end
|
|
750
|
+
end
|
|
751
|
+
|
|
752
|
+
# Create a new ReportTableCell object and initialize some common values.
|
|
753
|
+
def newCell(line, text = '')
|
|
754
|
+
property = line.property
|
|
755
|
+
cell = ReportTableCell.new(line, text)
|
|
756
|
+
|
|
757
|
+
# Cells for containers should be using bold font face.
|
|
758
|
+
cell.bold = true if property.container?
|
|
759
|
+
|
|
760
|
+
cell
|
|
761
|
+
end
|
|
762
|
+
|
|
763
|
+
# Determine the indentation for this line.
|
|
764
|
+
def setIndent(line, propertyRoot, treeMode)
|
|
765
|
+
property = line.property
|
|
766
|
+
scopeLine = line.scopeLine
|
|
767
|
+
level = property.level - (propertyRoot ? propertyRoot.level : 0)
|
|
768
|
+
# We indent at least as much as the scopeline + 1, if we have a scope.
|
|
769
|
+
line.indentation = scopeLine.indentation + 1 if scopeLine
|
|
770
|
+
# In tree mode we indent according to the level.
|
|
771
|
+
line.indentation += level if treeMode
|
|
772
|
+
end
|
|
773
|
+
|
|
774
|
+
# Set the URL associated with the cell text. _cell_ is the ReportTableCell.
|
|
775
|
+
# _columnDef_ is the user specified definition for the cell content and
|
|
776
|
+
# look. _query_ is the query used to resolve dynamic macros in the cellURL.
|
|
777
|
+
def setCellURL(cell, columnDef, query)
|
|
778
|
+
return unless columnDef.cellURL
|
|
779
|
+
|
|
780
|
+
url = expandMacros(columnDef.cellURL, cell.text, query)
|
|
781
|
+
cell.url = url unless url.empty?
|
|
782
|
+
end
|
|
783
|
+
|
|
784
|
+
# Try to merge equal cells without text to multi-column cells.
|
|
785
|
+
def tryCellMerging(cell, line, firstCell)
|
|
786
|
+
if cell.text == '' && firstCell && (c = line.last(1)) && c == cell
|
|
787
|
+
cell.hidden = true
|
|
788
|
+
c.columns += 1
|
|
789
|
+
end
|
|
790
|
+
end
|
|
791
|
+
|
|
792
|
+
# Expand the run-time macros in _pattern_. ${0} is a special case and will
|
|
793
|
+
# be replaced with the _originalText_. For all other macros the _query_ will
|
|
794
|
+
# be used with the macro name used for the attributeId of the query. The
|
|
795
|
+
# method returns the expanded pattern.
|
|
796
|
+
def expandMacros(pattern, originalText, query)
|
|
797
|
+
return pattern unless pattern.include?('${')
|
|
798
|
+
|
|
799
|
+
out = ''
|
|
800
|
+
# Scan the pattern for macros ${...}
|
|
801
|
+
i = 0
|
|
802
|
+
while i < pattern.length
|
|
803
|
+
c = pattern[i]
|
|
804
|
+
if c == ?$
|
|
805
|
+
# This could be a macro
|
|
806
|
+
if pattern[i + 1] != ?{
|
|
807
|
+
# It's not. Just append the '$'
|
|
808
|
+
out << c
|
|
809
|
+
else
|
|
810
|
+
# It is a macro.
|
|
811
|
+
i += 2
|
|
812
|
+
macro = ''
|
|
813
|
+
# Scan for the end '}' and get the macro name.
|
|
814
|
+
while i < pattern.length && pattern[i] != ?}
|
|
815
|
+
macro << pattern[i]
|
|
816
|
+
i += 1
|
|
817
|
+
end
|
|
818
|
+
if macro == '0'
|
|
819
|
+
# This turns RichText into plain ASCII!
|
|
820
|
+
out += originalText
|
|
821
|
+
else
|
|
822
|
+
# resolve by query
|
|
823
|
+
# If the macro is prefixed by a '?' it may be undefined.
|
|
824
|
+
if macro[0] == ??
|
|
825
|
+
macro = macro[1..-1]
|
|
826
|
+
ignoreErrors = true
|
|
827
|
+
else
|
|
828
|
+
ignoreErrors = false
|
|
829
|
+
end
|
|
830
|
+
query.attributeId = macro
|
|
831
|
+
query.process
|
|
832
|
+
unless query.ok || ignoreErrors
|
|
833
|
+
raise TjException.new, query.errorMessage
|
|
834
|
+
end
|
|
835
|
+
# This turns RichText into plain ASCII!
|
|
836
|
+
out += query.result
|
|
837
|
+
end
|
|
838
|
+
end
|
|
839
|
+
else
|
|
840
|
+
# Just append the character to the output.
|
|
841
|
+
out << c
|
|
842
|
+
end
|
|
843
|
+
i += 1
|
|
844
|
+
end
|
|
845
|
+
|
|
846
|
+
out
|
|
847
|
+
end
|
|
848
|
+
|
|
849
|
+
end
|
|
850
|
+
|
|
851
|
+
end
|
|
852
|
+
|