taskjuggler 0.0.5 → 0.0.6

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.
Files changed (544) hide show
  1. data/CHANGELOG +2086 -0
  2. data/benchmarks/666tasks.tjp +3183 -0
  3. data/benchmarks/booking.tjp +14 -10
  4. data/doc/AppConfig.html +95 -73
  5. data/doc/Arguments.html +22 -2
  6. data/doc/CHANGELOG.html +2587 -0
  7. data/doc/COPYING.html +21 -1
  8. data/doc/Object.html +161 -122
  9. data/doc/README.html +21 -1
  10. data/doc/RuntimeConfig.html +26 -6
  11. data/doc/String.html +38 -18
  12. data/doc/StringIO.html +579 -0
  13. data/doc/TaskJuggler.html +251 -143
  14. data/doc/TaskJuggler/Account.html +26 -6
  15. data/doc/TaskJuggler/AccountAttribute.html +28 -8
  16. data/doc/TaskJuggler/AccountScenario.html +24 -4
  17. data/doc/TaskJuggler/Allocation.html +30 -10
  18. data/doc/TaskJuggler/AllocationAttribute.html +28 -8
  19. data/doc/TaskJuggler/AttributeBase.html +46 -26
  20. data/doc/TaskJuggler/AttributeDefinition.html +22 -2
  21. data/doc/TaskJuggler/BatchProcessor.html +40 -20
  22. data/doc/TaskJuggler/Booking.html +26 -6
  23. data/doc/TaskJuggler/BookingListAttribute.html +28 -8
  24. data/doc/TaskJuggler/BooleanAttribute.html +28 -8
  25. data/doc/TaskJuggler/CSVFile.html +308 -146
  26. data/doc/TaskJuggler/CellSettingPattern.html +22 -2
  27. data/doc/TaskJuggler/CellSettingPatternList.html +26 -6
  28. data/doc/TaskJuggler/Charge.html +26 -6
  29. data/doc/TaskJuggler/ChargeListAttribute.html +26 -6
  30. data/doc/TaskJuggler/ChargeSet.html +33 -13
  31. data/doc/TaskJuggler/ChargeSetListAttribute.html +28 -8
  32. data/doc/TaskJuggler/ColumnListAttribute.html +26 -6
  33. data/doc/TaskJuggler/ColumnTable.html +24 -4
  34. data/doc/TaskJuggler/Daemon.html +26 -6
  35. data/doc/TaskJuggler/DateAttribute.html +26 -6
  36. data/doc/TaskJuggler/DefinitionListAttribute.html +22 -2
  37. data/doc/TaskJuggler/DependencyListAttribute.html +28 -8
  38. data/doc/TaskJuggler/DurationAttribute.html +28 -8
  39. data/doc/TaskJuggler/FixnumAttribute.html +24 -4
  40. data/doc/TaskJuggler/FlagListAttribute.html +28 -8
  41. data/doc/TaskJuggler/FloatAttribute.html +26 -6
  42. data/doc/TaskJuggler/FormatListAttribute.html +24 -4
  43. data/doc/TaskJuggler/GanttChart.html +147 -126
  44. data/doc/TaskJuggler/GanttContainer.html +34 -14
  45. data/doc/TaskJuggler/GanttHeader.html +28 -8
  46. data/doc/TaskJuggler/GanttHeaderScaleItem.html +24 -4
  47. data/doc/TaskJuggler/GanttLine.html +38 -18
  48. data/doc/TaskJuggler/GanttLoadStack.html +26 -6
  49. data/doc/TaskJuggler/GanttMilestone.html +34 -14
  50. data/doc/TaskJuggler/GanttRouter.html +50 -30
  51. data/doc/TaskJuggler/GanttTaskBar.html +34 -14
  52. data/doc/TaskJuggler/HTMLDocument.html +26 -6
  53. data/doc/TaskJuggler/HTMLGraphics.html +30 -10
  54. data/doc/TaskJuggler/Interval.html +40 -22
  55. data/doc/TaskJuggler/IntervalListAttribute.html +28 -8
  56. data/doc/TaskJuggler/JobInfo.html +24 -4
  57. data/doc/TaskJuggler/Journal.html +226 -147
  58. data/doc/TaskJuggler/JournalEntry.html +22 -2
  59. data/doc/TaskJuggler/JournalEntryList.html +129 -112
  60. data/doc/TaskJuggler/KeywordArray.html +26 -6
  61. data/doc/TaskJuggler/KeywordDocumentation.html +46 -26
  62. data/doc/TaskJuggler/Limits.html +152 -123
  63. data/doc/TaskJuggler/Limits/Limit.html +149 -70
  64. data/doc/TaskJuggler/LimitsAttribute.html +36 -51
  65. data/doc/TaskJuggler/ListAttributeBase.html +24 -4
  66. data/doc/TaskJuggler/Log.html +50 -32
  67. data/doc/TaskJuggler/LogFile.html +37 -17
  68. data/doc/TaskJuggler/LogicalAttribute.html +53 -33
  69. data/doc/TaskJuggler/LogicalExpression.html +26 -6
  70. data/doc/TaskJuggler/LogicalExpressionAttribute.html +32 -12
  71. data/doc/TaskJuggler/LogicalFlag.html +42 -22
  72. data/doc/TaskJuggler/LogicalFunction.html +204 -140
  73. data/doc/TaskJuggler/LogicalOperation.html +135 -111
  74. data/doc/TaskJuggler/Macro.html +22 -2
  75. data/doc/TaskJuggler/MacroParser.html +32 -12
  76. data/doc/TaskJuggler/MacroTable.html +32 -12
  77. data/doc/TaskJuggler/ManagerResponsibilities.html +24 -4
  78. data/doc/TaskJuggler/ManagerStatusRecord.html +24 -4
  79. data/doc/TaskJuggler/Message.html +24 -4
  80. data/doc/TaskJuggler/MessageHandler.html +24 -4
  81. data/doc/TaskJuggler/Navigator.html +104 -71
  82. data/doc/TaskJuggler/NavigatorElement.html +32 -12
  83. data/doc/TaskJuggler/NikuProject.html +22 -2
  84. data/doc/TaskJuggler/NikuReport.html +310 -228
  85. data/doc/TaskJuggler/NikuResource.html +22 -2
  86. data/doc/TaskJuggler/NodeListAttribute.html +615 -0
  87. data/doc/TaskJuggler/OnShiftCache.html +32 -12
  88. data/doc/TaskJuggler/ProcessIntercom.html +205 -78
  89. data/doc/TaskJuggler/ProcessIntercomIface.html +26 -6
  90. data/doc/TaskJuggler/Project.html +708 -660
  91. data/doc/TaskJuggler/ProjectBroker.html +506 -304
  92. data/doc/TaskJuggler/ProjectBrokerIface.html +61 -41
  93. data/doc/TaskJuggler/ProjectFileParser.html +429 -373
  94. data/doc/TaskJuggler/ProjectFileScanner.html +1790 -0
  95. data/doc/TaskJuggler/ProjectRecord.html +80 -60
  96. data/doc/TaskJuggler/ProjectServer.html +312 -237
  97. data/doc/TaskJuggler/ProjectServerIface.html +101 -43
  98. data/doc/TaskJuggler/PropertyAttribute.html +32 -12
  99. data/doc/TaskJuggler/PropertyList.html +166 -145
  100. data/doc/TaskJuggler/PropertySet.html +254 -224
  101. data/doc/TaskJuggler/PropertyTreeNode.html +670 -536
  102. data/doc/TaskJuggler/Query.html +169 -148
  103. data/doc/TaskJuggler/RTFHandlers.html +622 -0
  104. data/doc/TaskJuggler/RTFNavigator.html +28 -8
  105. data/doc/TaskJuggler/RTFQuery.html +40 -20
  106. data/doc/TaskJuggler/RTFReport.html +62 -25
  107. data/doc/TaskJuggler/RTFReportLink.html +765 -0
  108. data/doc/TaskJuggler/RealFormat.html +26 -6
  109. data/doc/TaskJuggler/RealFormatAttribute.html +26 -6
  110. data/doc/TaskJuggler/ReferenceAttribute.html +59 -39
  111. data/doc/TaskJuggler/Report.html +402 -251
  112. data/doc/TaskJuggler/ReportBase.html +162 -137
  113. data/doc/TaskJuggler/ReportContext.html +112 -29
  114. data/doc/TaskJuggler/ReportServer.html +89 -64
  115. data/doc/TaskJuggler/ReportServerIface.html +75 -55
  116. data/doc/TaskJuggler/ReportServerRecord.html +54 -31
  117. data/doc/TaskJuggler/ReportServlet.html +980 -0
  118. data/doc/TaskJuggler/ReportTable.html +41 -21
  119. data/doc/TaskJuggler/ReportTableCell.html +214 -170
  120. data/doc/TaskJuggler/ReportTableColumn.html +30 -10
  121. data/doc/TaskJuggler/ReportTableLegend.html +36 -16
  122. data/doc/TaskJuggler/ReportTableLine.html +32 -12
  123. data/doc/TaskJuggler/Resource.html +99 -87
  124. data/doc/TaskJuggler/ResourceListAttribute.html +59 -39
  125. data/doc/TaskJuggler/ResourceListRE.html +47 -26
  126. data/doc/TaskJuggler/ResourceScenario.html +403 -437
  127. data/doc/TaskJuggler/RichText.html +26 -6
  128. data/doc/TaskJuggler/RichTextAttribute.html +50 -30
  129. data/doc/TaskJuggler/RichTextDocument.html +37 -17
  130. data/doc/TaskJuggler/RichTextElement.html +475 -413
  131. data/doc/TaskJuggler/RichTextException.html +22 -2
  132. data/doc/TaskJuggler/RichTextFunctionExample.html +28 -8
  133. data/doc/TaskJuggler/RichTextFunctionHandler.html +24 -4
  134. data/doc/TaskJuggler/RichTextImage.html +22 -2
  135. data/doc/TaskJuggler/RichTextIntermediate.html +38 -18
  136. data/doc/TaskJuggler/RichTextParser.html +56 -34
  137. data/doc/TaskJuggler/RichTextScanner.html +82 -61
  138. data/doc/TaskJuggler/RichTextSnip.html +34 -14
  139. data/doc/TaskJuggler/RichTextSyntaxRules.html +507 -353
  140. data/doc/TaskJuggler/Scenario.html +22 -2
  141. data/doc/TaskJuggler/ScenarioData.html +30 -46
  142. data/doc/TaskJuggler/ScenarioListAttribute.html +38 -18
  143. data/doc/TaskJuggler/Scoreboard.html +42 -22
  144. data/doc/TaskJuggler/SheetHandlerBase.html +40 -20
  145. data/doc/TaskJuggler/SheetReceiver.html +333 -295
  146. data/doc/TaskJuggler/SheetSender.html +253 -230
  147. data/doc/TaskJuggler/Shift.html +26 -6
  148. data/doc/TaskJuggler/ShiftAssignment.html +89 -73
  149. data/doc/TaskJuggler/ShiftAssignments.html +226 -234
  150. data/doc/TaskJuggler/ShiftAssignmentsAttribute.html +39 -54
  151. data/doc/TaskJuggler/ShiftScenario.html +28 -8
  152. data/doc/TaskJuggler/SortListAttribute.html +34 -14
  153. data/doc/TaskJuggler/SourceFileInfo.html +24 -4
  154. data/doc/TaskJuggler/StatusSheetReceiver.html +24 -3
  155. data/doc/TaskJuggler/StatusSheetReport.html +168 -153
  156. data/doc/TaskJuggler/StatusSheetSender.html +24 -3
  157. data/doc/TaskJuggler/StringAttribute.html +38 -18
  158. data/doc/TaskJuggler/SymbolAttribute.html +32 -12
  159. data/doc/TaskJuggler/SyntaxReference.html +40 -20
  160. data/doc/TaskJuggler/TOCEntry.html +26 -6
  161. data/doc/TaskJuggler/TSResourceRecord.html +22 -2
  162. data/doc/TaskJuggler/TSTaskRecord.html +22 -2
  163. data/doc/TaskJuggler/TableColumnDefinition.html +59 -22
  164. data/doc/TaskJuggler/TableOfContents.html +26 -6
  165. data/doc/TaskJuggler/TableReport.html +937 -904
  166. data/doc/TaskJuggler/Task.html +55 -36
  167. data/doc/TaskJuggler/TaskDependency.html +24 -4
  168. data/doc/TaskJuggler/TaskListAttribute.html +50 -30
  169. data/doc/TaskJuggler/TaskListRE.html +27 -7
  170. data/doc/TaskJuggler/TaskScenario.html +1273 -1153
  171. data/doc/TaskJuggler/TextFormatter.html +28 -8
  172. data/doc/TaskJuggler/TextParser.html +585 -338
  173. data/doc/TaskJuggler/TextParser/Pattern.html +54 -34
  174. data/doc/TaskJuggler/TextParser/Rule.html +95 -73
  175. data/doc/TaskJuggler/TextParser/StackElement.html +39 -17
  176. data/doc/TaskJuggler/TextParser/TextParserResultArray.html +24 -4
  177. data/doc/TaskJuggler/TextParser/TokenDoc.html +22 -2
  178. data/doc/TaskJuggler/TextReport.html +28 -8
  179. data/doc/TaskJuggler/TextScanner.html +400 -1404
  180. data/doc/TaskJuggler/TextScanner/BufferStreamHandle.html +28 -240
  181. data/doc/TaskJuggler/TextScanner/FileStreamHandle.html +37 -184
  182. data/doc/TaskJuggler/TextScanner/MacroStackEntry.html +682 -0
  183. data/doc/TaskJuggler/TextScanner/StreamHandle.html +342 -67
  184. data/doc/TaskJuggler/TimeSheet.html +48 -28
  185. data/doc/TaskJuggler/TimeSheetReceiver.html +24 -3
  186. data/doc/TaskJuggler/TimeSheetRecord.html +47 -27
  187. data/doc/TaskJuggler/TimeSheetReport.html +154 -133
  188. data/doc/TaskJuggler/TimeSheetSender.html +24 -3
  189. data/doc/TaskJuggler/TimeSheetSummary.html +137 -91
  190. data/doc/TaskJuggler/TimeSheets.html +26 -6
  191. data/doc/TaskJuggler/Tj3AppBase.html +85 -58
  192. data/doc/TaskJuggler/Tj3Client.html +292 -238
  193. data/doc/TaskJuggler/Tj3Daemon.html +159 -74
  194. data/doc/TaskJuggler/Tj3SheetAppBase.html +26 -6
  195. data/doc/TaskJuggler/Tj3SsReceiver.html +26 -6
  196. data/doc/TaskJuggler/Tj3SsSender.html +53 -26
  197. data/doc/TaskJuggler/Tj3TsReceiver.html +28 -7
  198. data/doc/TaskJuggler/Tj3TsSender.html +26 -6
  199. data/doc/TaskJuggler/Tj3TsSummary.html +26 -6
  200. data/doc/TaskJuggler/TjException.html +22 -2
  201. data/doc/TaskJuggler/TjTime.html +216 -160
  202. data/doc/TaskJuggler/TjpExample.html +34 -14
  203. data/doc/TaskJuggler/TjpExportRE.html +403 -407
  204. data/doc/TaskJuggler/TjpSyntaxRules.html +4805 -4408
  205. data/doc/TaskJuggler/URLParameter.html +649 -0
  206. data/doc/TaskJuggler/UserManual.html +42 -22
  207. data/doc/TaskJuggler/WebServer.html +702 -0
  208. data/doc/TaskJuggler/WorkingHours.html +38 -18
  209. data/doc/TaskJuggler/WorkingHoursAttribute.html +61 -41
  210. data/doc/TaskJuggler/XMLBlob.html +24 -4
  211. data/doc/TaskJuggler/XMLComment.html +24 -4
  212. data/doc/TaskJuggler/XMLDocument.html +30 -10
  213. data/doc/TaskJuggler/XMLElement.html +34 -14
  214. data/doc/TaskJuggler/XMLNamedText.html +22 -2
  215. data/doc/TaskJuggler/XMLText.html +24 -4
  216. data/doc/index.html +1841 -1666
  217. data/doc/lib/AccountScenario_rb.html +1 -1
  218. data/doc/lib/Account_rb.html +1 -1
  219. data/doc/lib/Allocation_rb.html +1 -1
  220. data/doc/lib/AppConfig_rb.html +2 -2
  221. data/doc/lib/AttributeBase_rb.html +1 -1
  222. data/doc/lib/AttributeDefinition_rb.html +1 -1
  223. data/doc/lib/Attributes_rb.html +2 -2
  224. data/doc/lib/BatchProcessor_rb.html +1 -1
  225. data/doc/lib/Booking_rb.html +1 -1
  226. data/doc/lib/ChargeSet_rb.html +1 -1
  227. data/doc/lib/Charge_rb.html +1 -1
  228. data/doc/lib/HTMLDocument_rb.html +1 -1
  229. data/doc/lib/Interval_rb.html +1 -1
  230. data/doc/lib/Journal_rb.html +2 -2
  231. data/doc/lib/KeywordArray_rb.html +1 -1
  232. data/doc/lib/KeywordDocumentation_rb.html +1 -1
  233. data/doc/lib/Limits_rb.html +2 -2
  234. data/doc/lib/LogFile_rb.html +2 -2
  235. data/doc/lib/Log_rb.html +1 -1
  236. data/doc/lib/LogicalExpression_rb.html +1 -1
  237. data/doc/lib/LogicalFunction_rb.html +2 -2
  238. data/doc/lib/LogicalOperation_rb.html +2 -2
  239. data/doc/lib/MacroParser_rb.html +1 -1
  240. data/doc/lib/MacroTable_rb.html +1 -1
  241. data/doc/lib/MessageHandler_rb.html +1 -1
  242. data/doc/lib/Message_rb.html +1 -1
  243. data/doc/lib/ProjectFileParser_rb.html +4 -8
  244. data/doc/lib/ProjectFileScanner_rb.html +67 -0
  245. data/doc/lib/Project_rb.html +2 -2
  246. data/doc/lib/PropertyList_rb.html +2 -2
  247. data/doc/lib/PropertySet_rb.html +2 -2
  248. data/doc/lib/PropertyTreeNode_rb.html +2 -2
  249. data/doc/lib/Query_rb.html +2 -2
  250. data/doc/lib/RTFHandlers_rb.html +73 -0
  251. data/doc/lib/RTFNavigator_rb.html +1 -1
  252. data/doc/lib/RTFQuery_rb.html +1 -1
  253. data/doc/lib/RTFReportLink_rb.html +71 -0
  254. data/doc/lib/RTFReport_rb.html +2 -2
  255. data/doc/lib/RealFormat_rb.html +1 -1
  256. data/doc/lib/ResourceScenario_rb.html +2 -2
  257. data/doc/lib/Resource_rb.html +2 -2
  258. data/doc/lib/RichTextDocument_rb.html +1 -1
  259. data/doc/lib/RichTextElement_rb.html +2 -2
  260. data/doc/lib/RichTextFunctionExample_rb.html +1 -1
  261. data/doc/lib/RichTextFunctionHandler_rb.html +1 -1
  262. data/doc/lib/RichTextParser_rb.html +2 -2
  263. data/doc/lib/RichTextScanner_rb.html +2 -2
  264. data/doc/lib/RichTextSnip_rb.html +1 -1
  265. data/doc/lib/RichTextSyntaxRules_rb.html +2 -2
  266. data/doc/lib/RichText_rb.html +1 -1
  267. data/doc/lib/RuntimeConfig_rb.html +1 -1
  268. data/doc/lib/ScenarioData_rb.html +2 -2
  269. data/doc/lib/Scenario_rb.html +1 -1
  270. data/doc/lib/Scoreboard_rb.html +1 -1
  271. data/doc/lib/SheetHandlerBase_rb.html +1 -1
  272. data/doc/lib/SheetReceiver_rb.html +2 -2
  273. data/doc/lib/SheetSender_rb.html +4 -2
  274. data/doc/lib/ShiftAssignments_rb.html +4 -2
  275. data/doc/lib/ShiftScenario_rb.html +1 -1
  276. data/doc/lib/Shift_rb.html +1 -1
  277. data/doc/lib/SourceFileInfo_rb.html +1 -1
  278. data/doc/lib/StatusSheetReceiver_rb.html +2 -2
  279. data/doc/lib/StatusSheetSender_rb.html +2 -2
  280. data/doc/lib/SyntaxReference_rb.html +1 -1
  281. data/doc/lib/TOCEntry_rb.html +1 -1
  282. data/doc/lib/TableColumnDefinition_rb.html +2 -2
  283. data/doc/lib/TableOfContents_rb.html +1 -1
  284. data/doc/lib/TaskDependency_rb.html +1 -1
  285. data/doc/lib/TaskJuggler_rb.html +2 -2
  286. data/doc/lib/TaskScenario_rb.html +2 -2
  287. data/doc/lib/Task_rb.html +2 -2
  288. data/doc/lib/TextFormatter_rb.html +1 -1
  289. data/doc/lib/TextParser/Pattern_rb.html +1 -1
  290. data/doc/lib/TextParser/Rule_rb.html +2 -2
  291. data/doc/lib/TextParser/StackElement_rb.html +2 -2
  292. data/doc/lib/TextParser/TokenDoc_rb.html +1 -1
  293. data/doc/lib/TextParser_rb.html +2 -2
  294. data/doc/lib/TextScanner_rb.html +6 -2
  295. data/doc/lib/TimeSheetReceiver_rb.html +2 -2
  296. data/doc/lib/TimeSheetSender_rb.html +2 -2
  297. data/doc/lib/TimeSheetSummary_rb.html +2 -2
  298. data/doc/lib/TimeSheets_rb.html +2 -2
  299. data/doc/lib/Tj3AppBase_rb.html +2 -2
  300. data/doc/lib/Tj3Config_rb.html +2 -2
  301. data/doc/lib/Tj3SheetAppBase_rb.html +2 -2
  302. data/doc/lib/TjException_rb.html +1 -1
  303. data/doc/lib/TjTime_rb.html +2 -2
  304. data/doc/lib/TjpExample_rb.html +1 -1
  305. data/doc/lib/TjpSyntaxRules_rb.html +2 -2
  306. data/doc/lib/URLParameter_rb.html +67 -0
  307. data/doc/lib/UTF8String_rb.html +1 -1
  308. data/doc/lib/UserManual_rb.html +1 -1
  309. data/doc/lib/WorkingHours_rb.html +1 -1
  310. data/doc/lib/XMLDocument_rb.html +2 -2
  311. data/doc/lib/XMLElement_rb.html +1 -1
  312. data/doc/lib/daemon/Daemon_rb.html +1 -1
  313. data/doc/lib/daemon/ProcessIntercom_rb.html +2 -2
  314. data/doc/lib/daemon/ProjectBroker_rb.html +4 -2
  315. data/doc/lib/daemon/ProjectServer_rb.html +2 -2
  316. data/doc/lib/daemon/ReportServer_rb.html +2 -2
  317. data/doc/lib/daemon/WebServer_rb.html +75 -0
  318. data/doc/lib/deep_copy_rb.html +2 -2
  319. data/doc/lib/reports/CSVFile_rb.html +2 -2
  320. data/doc/lib/reports/ColumnTable_rb.html +1 -1
  321. data/doc/lib/reports/GanttChart_rb.html +2 -2
  322. data/doc/lib/reports/GanttContainer_rb.html +1 -1
  323. data/doc/lib/reports/GanttHeaderScaleItem_rb.html +1 -1
  324. data/doc/lib/reports/GanttHeader_rb.html +1 -1
  325. data/doc/lib/reports/GanttLine_rb.html +1 -1
  326. data/doc/lib/reports/GanttLoadStack_rb.html +1 -1
  327. data/doc/lib/reports/GanttMilestone_rb.html +1 -1
  328. data/doc/lib/reports/GanttRouter_rb.html +1 -1
  329. data/doc/lib/reports/GanttTaskBar_rb.html +1 -1
  330. data/doc/lib/reports/HTMLGraphics_rb.html +1 -1
  331. data/doc/lib/reports/Navigator_rb.html +2 -2
  332. data/doc/lib/reports/NikuReport_rb.html +2 -2
  333. data/doc/lib/reports/ReportBase_rb.html +2 -2
  334. data/doc/lib/reports/ReportContext_rb.html +2 -2
  335. data/doc/lib/reports/ReportTableCell_rb.html +2 -2
  336. data/doc/lib/reports/ReportTableColumn_rb.html +1 -1
  337. data/doc/lib/reports/ReportTableLegend_rb.html +1 -1
  338. data/doc/lib/reports/ReportTableLine_rb.html +1 -1
  339. data/doc/lib/reports/ReportTable_rb.html +2 -2
  340. data/doc/lib/reports/Report_rb.html +2 -2
  341. data/doc/lib/reports/ResourceListRE_rb.html +2 -2
  342. data/doc/lib/reports/StatusSheetReport_rb.html +2 -2
  343. data/doc/lib/reports/TableReport_rb.html +2 -2
  344. data/doc/lib/reports/TaskListRE_rb.html +2 -2
  345. data/doc/lib/reports/TextReport_rb.html +1 -1
  346. data/doc/lib/reports/TimeSheetReport_rb.html +2 -2
  347. data/doc/lib/reports/TjpExportRE_rb.html +2 -2
  348. data/doc/lib/ruby-signal-bug_rb.html +1 -1
  349. data/doc/lib/taskjuggler3_rb.html +2 -2
  350. data/doc/lib/tj3client_rb.html +2 -2
  351. data/doc/lib/tj3d_rb.html +2 -2
  352. data/doc/lib/tj3man_rb.html +1 -1
  353. data/doc/lib/tj3ss_receiver_rb.html +1 -1
  354. data/doc/lib/tj3ss_sender_rb.html +2 -2
  355. data/doc/lib/tj3ts_receiver_rb.html +2 -2
  356. data/doc/lib/tj3ts_sender_rb.html +1 -1
  357. data/doc/lib/tj3ts_summary_rb.html +1 -1
  358. data/doc/rdoc.css +9 -4
  359. data/examples/tutorial.tjp +17 -15
  360. data/gem_spec.rb +3 -0
  361. data/lib/AppConfig.rb +3 -1
  362. data/lib/Attributes.rb +10 -8
  363. data/lib/Journal.rb +55 -20
  364. data/lib/Limits.rb +72 -46
  365. data/lib/LogFile.rb +3 -3
  366. data/lib/LogicalFunction.rb +19 -6
  367. data/lib/LogicalOperation.rb +5 -1
  368. data/lib/Project.rb +37 -9
  369. data/lib/ProjectFileParser.rb +25 -22
  370. data/lib/ProjectFileScanner.rb +365 -0
  371. data/lib/PropertyList.rb +2 -1
  372. data/lib/PropertySet.rb +14 -4
  373. data/lib/PropertyTreeNode.rb +42 -29
  374. data/lib/Query.rb +3 -1
  375. data/lib/RTFHandlers.rb +35 -0
  376. data/lib/RTFReport.rb +23 -6
  377. data/lib/RTFReportLink.rb +72 -0
  378. data/lib/Resource.rb +8 -16
  379. data/lib/ResourceScenario.rb +1 -22
  380. data/lib/RichTextElement.rb +42 -0
  381. data/lib/RichTextParser.rb +6 -4
  382. data/lib/RichTextScanner.rb +5 -5
  383. data/lib/RichTextSyntaxRules.rb +48 -10
  384. data/lib/ScenarioData.rb +5 -3
  385. data/lib/SheetReceiver.rb +24 -6
  386. data/lib/SheetSender.rb +17 -13
  387. data/lib/ShiftAssignments.rb +56 -52
  388. data/lib/StatusSheetReceiver.rb +1 -0
  389. data/lib/StatusSheetSender.rb +1 -0
  390. data/lib/TableColumnDefinition.rb +4 -2
  391. data/lib/Task.rb +8 -9
  392. data/lib/TaskJuggler.rb +21 -11
  393. data/lib/TaskScenario.rb +61 -26
  394. data/lib/TextParser.rb +268 -106
  395. data/lib/TextParser/Rule.rb +4 -2
  396. data/lib/TextParser/StackElement.rb +6 -4
  397. data/lib/TextScanner.rb +283 -700
  398. data/lib/TimeSheetReceiver.rb +1 -0
  399. data/lib/TimeSheetSender.rb +1 -0
  400. data/lib/TimeSheetSummary.rb +51 -26
  401. data/lib/TimeSheets.rb +1 -1
  402. data/lib/Tj3AppBase.rb +8 -1
  403. data/lib/Tj3Config.rb +1 -1
  404. data/lib/TjTime.rb +5 -0
  405. data/lib/TjpSyntaxRules.rb +360 -144
  406. data/lib/URLParameter.rb +30 -0
  407. data/lib/XMLDocument.rb +2 -2
  408. data/lib/daemon/ProcessIntercom.rb +50 -9
  409. data/lib/daemon/ProjectBroker.rb +63 -10
  410. data/lib/daemon/ProjectServer.rb +47 -16
  411. data/lib/daemon/ReportServer.rb +9 -4
  412. data/lib/daemon/WebServer.rb +204 -0
  413. data/lib/deep_copy.rb +4 -1
  414. data/lib/reports/CSVFile.rb +150 -66
  415. data/lib/reports/GanttChart.rb +2 -1
  416. data/lib/reports/Navigator.rb +18 -5
  417. data/lib/reports/NikuReport.rb +32 -2
  418. data/lib/reports/Report.rb +65 -37
  419. data/lib/reports/ReportBase.rb +14 -9
  420. data/lib/reports/ReportContext.rb +19 -4
  421. data/lib/reports/ReportTable.rb +2 -2
  422. data/lib/reports/ReportTableCell.rb +54 -30
  423. data/lib/reports/ResourceListRE.rb +4 -3
  424. data/lib/reports/StatusSheetReport.rb +8 -13
  425. data/lib/reports/TableReport.rb +47 -32
  426. data/lib/reports/TaskListRE.rb +3 -3
  427. data/lib/reports/TimeSheetReport.rb +14 -5
  428. data/lib/reports/TjpExportRE.rb +14 -19
  429. data/lib/taskjuggler3.rb +15 -0
  430. data/lib/tj3client.rb +9 -7
  431. data/lib/tj3d.rb +38 -10
  432. data/lib/tj3ss_sender.rb +7 -0
  433. data/lib/tj3ts_receiver.rb +1 -0
  434. data/manual/Day_To_Day_Juggling +1 -1
  435. data/manual/Rich_Text_Attributes +8 -0
  436. data/manual/TaskJuggler_2x_Migration +7 -0
  437. data/manual/The_TaskJuggler_Syntax +20 -6
  438. data/prj_cfg.rb +3 -2
  439. data/tasks/changelog.rake +36 -0
  440. data/tasks/csts.rake +2 -3
  441. data/tasks/gem.rake +10 -0
  442. data/tasks/missing.rake +0 -17
  443. data/test/TestSuite/ReportGenerator/Correct/Journal.html +63 -0
  444. data/test/TestSuite/ReportGenerator/Correct/Journal.tjp +14 -0
  445. data/test/TestSuite/{HTML-Reports/LogicalFunctions.tjp → ReportGenerator/Correct/LogicalFunctions1.tjp} +2 -2
  446. data/test/TestSuite/ReportGenerator/Correct/LogicalFunctions2.csv +3 -0
  447. data/test/TestSuite/ReportGenerator/Correct/LogicalFunctions2.tjp +19 -0
  448. data/test/TestSuite/ReportGenerator/Correct/css/tjmanual.css +66 -0
  449. data/test/TestSuite/ReportGenerator/Correct/css/tjreport.css +407 -0
  450. data/test/TestSuite/ReportGenerator/Correct/icons/details.png +0 -0
  451. data/test/TestSuite/ReportGenerator/Correct/icons/flag-green.png +0 -0
  452. data/test/TestSuite/ReportGenerator/Correct/icons/flag-red.png +0 -0
  453. data/test/TestSuite/ReportGenerator/Correct/icons/flag-yellow.png +0 -0
  454. data/test/TestSuite/ReportGenerator/Correct/icons/resource.png +0 -0
  455. data/test/TestSuite/ReportGenerator/Correct/icons/resourcegroup.png +0 -0
  456. data/test/TestSuite/ReportGenerator/Correct/icons/task.png +0 -0
  457. data/test/TestSuite/ReportGenerator/Correct/icons/taskgroup.png +0 -0
  458. data/test/TestSuite/ReportGenerator/Correct/icons/trend-down.png +0 -0
  459. data/test/TestSuite/ReportGenerator/Correct/icons/trend-flat.png +0 -0
  460. data/test/TestSuite/ReportGenerator/Correct/icons/trend-up.png +0 -0
  461. data/test/TestSuite/ReportGenerator/Correct/opennodes.csv +2 -0
  462. data/test/TestSuite/ReportGenerator/Correct/opennodes.tjp +26 -0
  463. data/test/TestSuite/ReportGenerator/Correct/refs/Journal-1.csv +6 -0
  464. data/test/TestSuite/ReportGenerator/Correct/refs/LogicalFunctions1-1.csv +7 -0
  465. data/test/TestSuite/ReportGenerator/Correct/refs/LogicalFunctions2-1.csv +2 -0
  466. data/test/TestSuite/ReportGenerator/Correct/refs/opennodes-1.csv +2 -0
  467. data/test/TestSuite/ReportGenerator/Correct/scripts/wz_tooltip.js +1301 -0
  468. data/test/TestSuite/ReportGenerator/Errors/rtp_report_recursion.tjp +20 -0
  469. data/test/TestSuite/Scheduler/Correct/Container.html +349 -0
  470. data/test/TestSuite/Scheduler/Correct/Limits.tjp +11 -4
  471. data/test/TestSuite/Scheduler/Correct/Shift2.html +464 -150
  472. data/test/TestSuite/Scheduler/Correct/TimeSheet2.html +108 -0
  473. data/test/TestSuite/Scheduler/Correct/TimeSheet2.tjp +7 -0
  474. data/test/TestSuite/Scheduler/Correct/css/tjmanual.css +14 -0
  475. data/test/TestSuite/Scheduler/Correct/css/tjreport.css +233 -21
  476. data/test/TestSuite/Scheduler/Correct/scripts/wz_tooltip.js +20 -20
  477. data/test/TestSuite/StatusSheetTemplates/project.tji +35 -0
  478. data/test/TestSuite/StatusSheetTemplates/project.tjp +56 -0
  479. data/test/TestSuite/StatusSheets/run +3 -2
  480. data/test/TestSuite/Syntax/Correct/Include.tjp +1 -1
  481. data/test/TestSuite/Syntax/Correct/Macro-1.tjp +1 -1
  482. data/test/TestSuite/Syntax/Correct/Macro-2.tjp +6 -0
  483. data/test/TestSuite/Syntax/Correct/Macro-3.tjp +14 -0
  484. data/test/TestSuite/Syntax/Correct/ResourcePrefix.html +32 -0
  485. data/test/TestSuite/Syntax/Correct/css/tjmanual.css +66 -0
  486. data/test/TestSuite/Syntax/Correct/css/tjreport.css +407 -0
  487. data/test/TestSuite/Syntax/Correct/icons/details.png +0 -0
  488. data/test/TestSuite/Syntax/Correct/icons/flag-green.png +0 -0
  489. data/test/TestSuite/Syntax/Correct/icons/flag-red.png +0 -0
  490. data/test/TestSuite/Syntax/Correct/icons/flag-yellow.png +0 -0
  491. data/test/TestSuite/Syntax/Correct/icons/resource.png +0 -0
  492. data/test/TestSuite/Syntax/Correct/icons/resourcegroup.png +0 -0
  493. data/test/TestSuite/Syntax/Correct/icons/task.png +0 -0
  494. data/test/TestSuite/Syntax/Correct/icons/taskgroup.png +0 -0
  495. data/test/TestSuite/Syntax/Correct/icons/trend-down.png +0 -0
  496. data/test/TestSuite/Syntax/Correct/icons/trend-flat.png +0 -0
  497. data/test/TestSuite/Syntax/Correct/icons/trend-up.png +0 -0
  498. data/test/TestSuite/Syntax/Correct/include/dir1/file2.tji +3 -0
  499. data/test/TestSuite/Syntax/Correct/include/dir1/file5.tji +2 -0
  500. data/test/TestSuite/Syntax/Correct/include/dir3/all.tji +3 -0
  501. data/test/TestSuite/Syntax/Correct/include/dir3/file1.tji +2 -0
  502. data/test/TestSuite/Syntax/Correct/include/dir3/file2.tji +1 -0
  503. data/test/TestSuite/Syntax/Correct/scripts/wz_tooltip.js +1301 -0
  504. data/test/TestSuite/Syntax/Correct/tutorial.tjp +13 -13
  505. data/test/TestSuite/Syntax/Errors/empty.tjp +1 -1
  506. data/test/TestSuite/Syntax/Errors/include_recursion.tjp +1 -0
  507. data/test/TestSuite/Syntax/Errors/macro_stack_overflow.tjp +2 -1
  508. data/test/TestSuite/Syntax/Errors/{eof_in_istring1.tjp → no_token_match1.tjp} +2 -2
  509. data/test/TestSuite/Syntax/Errors/{eof_in_istring2.tjp → no_token_match2.tjp} +2 -2
  510. data/test/TestSuite/Syntax/Errors/{eof_in_istring3.tjp → no_token_match3.tjp} +1 -1
  511. data/test/TestSuite/Syntax/Errors/{eof_in_istring4.tjp → no_token_match4.tjp} +1 -1
  512. data/test/TestSuite/Syntax/Errors/{eof_in_istring5.tjp → no_token_match5.tjp} +1 -1
  513. data/test/TestSuite/Syntax/Errors/not_scheduled.tjp +13 -0
  514. data/test/TestSuite/Syntax/Errors/unsupported_token.tjp +1 -1
  515. data/test/TestSuite/TimeSheets/run +5 -5
  516. data/test/test_CSVFile.rb +75 -0
  517. data/test/test_Limits.rb +63 -5
  518. data/test/test_ProjectFileScanner.rb +163 -0
  519. data/test/test_ReportGenerator.rb +81 -0
  520. data/test/test_RichText.rb +21 -3
  521. data/test/test_Scheduler.rb +1 -1
  522. data/test/test_ShiftAssignments.rb +4 -4
  523. data/test/test_Syntax.rb +1 -1
  524. data/test/test_URLParameter.rb +30 -0
  525. metadata +126 -32
  526. data/test/TestSuite/Scheduler/Errors/not_scheduled.tjp +0 -8
  527. data/test/TestSuite/Syntax/Errors/bad_comment.tjp +0 -7
  528. data/test/TestSuite/TimeSheets/TimeSheetTemplates/2002-03-01/boss_2002-03-01.tji +0 -36
  529. data/test/TestSuite/TimeSheets/TimeSheetTemplates/2002-03-01/dev1_2002-03-01.tji +0 -48
  530. data/test/TestSuite/TimeSheets/TimeSheetTemplates/2002-03-01/dev2_2002-03-01.tji +0 -67
  531. data/test/TestSuite/TimeSheets/TimeSheetTemplates/2002-03-01/dev3_2002-03-01.tji +0 -67
  532. data/test/TestSuite/TimeSheets/TimeSheetTemplates/2002-03-01/doc_2002-03-01.tji +0 -48
  533. data/test/TestSuite/TimeSheets/TimeSheetTemplates/2002-03-01/resources.yml +0 -31
  534. data/test/TestSuite/TimeSheets/TimeSheetTemplates/2002-03-01/test_2002-03-01.tji +0 -36
  535. data/test/TestSuite/TimeSheets/TimeSheetTemplates/acceptable_intervals +0 -1
  536. data/test/TestSuite/TimeSheets/TimeSheets/2002-03-01/all.tji +0 -1
  537. data/test/TestSuite/TimeSheets/TimeSheets/2002-03-01/dev2_2002-03-01.tji +0 -54
  538. data/test/TestSuite/TimeSheets/TimeSheets/all.tji +0 -2
  539. data/test/TestSuite/TimeSheets/receiver.log +0 -102
  540. data/test/TestSuite/TimeSheets/sender.log +0 -794
  541. data/test/TestSuite/TimeSheets/summary.log +0 -884
  542. data/test/TestSuite/TimeSheets/timesheets.log +0 -45
  543. data/test/TestSuite/TimeSheets/tj3d.log +0 -292
  544. data/test/test_TextScanner.rb +0 -95
@@ -46,6 +46,7 @@ text, UTF-8 encoded and the status sheet header from 'statussheet' to the period
46
46
  end date must be in a single line that starts at the beginning of the line.
47
47
 
48
48
  EOT
49
+ @mailSubject = "Your weekly status report template for %s"
49
50
  end
50
51
 
51
52
  end
@@ -57,7 +57,7 @@ class TaskJuggler
57
57
  # The column is uniquely identified by an ID.
58
58
  class TableColumnDefinition
59
59
 
60
- attr_reader :id, :cellText, :tooltip, :cellColor, :fontColor
60
+ attr_reader :id, :cellText, :tooltip, :hAlign, :cellColor, :fontColor
61
61
  attr_accessor :title, :start, :end, :scale, :width, :content, :column
62
62
 
63
63
  def initialize(id, title)
@@ -73,8 +73,10 @@ class TaskJuggler
73
73
  # the actual cell content.
74
74
  @cellText = CellSettingPatternList.new
75
75
  # The content attribute is only used for calendar columns. It specifies
76
- # what content should be displayed in the colendar columns.
76
+ # what content should be displayed in the calendar columns.
77
77
  @content = 'load'
78
+ # Horizontal alignment of the cell content.
79
+ @hAlign = CellSettingPatternList.new
78
80
  # An alternative content for the tooltip message. It should be a
79
81
  # RichText object.
80
82
  @tooltip = CellSettingPatternList.new
data/lib/Task.rb CHANGED
@@ -32,21 +32,21 @@ class TaskJuggler
32
32
  end
33
33
 
34
34
  def query_journal(query)
35
- journalMessages(query, true)
35
+ journalText(query, true)
36
36
  end
37
37
 
38
38
  private
39
39
 
40
40
  # Create a blog-style list of all alert messages that match the Query.
41
- def journalMessages(query, longVersion)
41
+ def journalText(query, longVersion)
42
42
  # The components of the message are either UTF-8 text or RichText. For
43
43
  # the RichText components, we use the originally provided markup since
44
44
  # we compose the result as RichText markup first.
45
45
  rText = ''
46
46
  list = @project['journal'].entriesByTask(self, query.start, query.end)
47
47
  list.reverse.each do |entry|
48
- # The TimeSheetRecords associated with this entry.
49
48
  tsRecord = entry.timeSheetRecord
49
+
50
50
  if entry.property.is_a?(Task)
51
51
  levelRecord = @project['alertLevels'][entry.alertLevel]
52
52
  alertName = "[[File:icons/flag-#{levelRecord[0]}.png|" +
@@ -79,15 +79,14 @@ class TaskJuggler
79
79
  end
80
80
  end
81
81
 
82
+ # Don't generate a RichText object for an empty String.
83
+ return if rText.empty?
84
+
82
85
  # Now convert the RichText markup String into RichTextIntermediate
83
86
  # format.
84
- handlers = [
85
- RTFNavigator.new(@project),
86
- RTFQuery.new(@project),
87
- RTFReport.new(@project)
88
- ]
89
87
  begin
90
- rti = RichText.new(rText, handlers).generateIntermediateFormat
88
+ rti = RichText.new(rText, RTFHandlers.create(@project)).
89
+ generateIntermediateFormat
91
90
  rescue RichTextException => msg
92
91
  $stderr.puts "Error while processing Rich Text\n" +
93
92
  "Line #{msg.lineNo}: #{msg.text}\n" +
data/lib/TaskJuggler.rb CHANGED
@@ -20,7 +20,7 @@ require 'Log'
20
20
  # files, schedule them and generate the reports.
21
21
  class TaskJuggler
22
22
 
23
- attr_reader :messageHandler
23
+ attr_reader :project, :messageHandler
24
24
  attr_accessor :maxCpuCores, :warnTsDeltas
25
25
 
26
26
  # Create a new TaskJuggler object. _console_ is a boolean that determines
@@ -73,9 +73,6 @@ class TaskJuggler
73
73
  @parser.open(fileName, false)
74
74
  @parser.setGlobalMacros
75
75
  return nil if (res = @parser.parse(rule)).nil?
76
- # Make sure that _rule_ described the full content of the file. There
77
- # should be no more content left.
78
- @parser.checkForEnd
79
76
  @parser.close
80
77
  res
81
78
  end
@@ -94,7 +91,8 @@ class TaskJuggler
94
91
  # Generate all specified reports. The project must have be scheduled before
95
92
  # this method can be called. It returns true if no error occured, false
96
93
  # otherwise.
97
- def generateReports(outputDir)
94
+ def generateReports(outputDir = './')
95
+ outputDir += '/' unless outputDir.empty? || outputDir[-1] == '/'
98
96
  @project.outputDir = outputDir
99
97
  Log.enter('reports', 'Generating reports ...')
100
98
  res = @project.generateReports(@maxCpuCores)
@@ -102,11 +100,15 @@ class TaskJuggler
102
100
  res
103
101
  end
104
102
 
105
- # Generate the report with the ID _reportId_.
106
- def generateReport(reportId, regExpMode)
103
+ # Generate the report with the ID _reportId_. If _regExpMode_ is true,
104
+ # _reportId_ is interpreted as a Regular Expression and all reports with
105
+ # matching IDs are generated. _dynamicAtributes_ is a String that may
106
+ # contain attributes to supplement the report definition. The String must be
107
+ # in TJP format and may be nil if no additional attributes are provided.
108
+ def generateReport(reportId, regExpMode, dynamicAttributes = nil)
107
109
  begin
108
110
  Log.enter('generateReport', 'Generating report #{reportId} ...')
109
- @project.generateReport(reportId, regExpMode)
111
+ @project.generateReport(reportId, regExpMode, dynamicAttributes)
110
112
  rescue TjException
111
113
  Log.exit('generateReport')
112
114
  return false
@@ -115,7 +117,9 @@ class TaskJuggler
115
117
  true
116
118
  end
117
119
 
118
- # Generate the report with the ID _reportId_.
120
+ # Generate the report with the ID _reportId_. If _regExpMode_ is true,
121
+ # _reportId_ is interpreted as a Regular Expression and all reports with
122
+ # matching IDs are listed.
119
123
  def listReports(reportId, regExpMode)
120
124
  begin
121
125
  Log.enter('listReports', 'Generating report list for #{reportId} ...')
@@ -137,7 +141,7 @@ class TaskJuggler
137
141
  # Make sure we don't use data from old time sheets or Journal entries.
138
142
  @project.timeSheets.clear
139
143
  @project['journal'] = Journal.new
140
- return false unless (ts = parseFile(fileName, 'timeSheet'))
144
+ return false unless (ts = parseFile(fileName, 'timeSheetFile'))
141
145
  return false unless @project.checkTimeSheets
142
146
  queryAttrs = { 'project' => @project,
143
147
  'property' => ts.resource,
@@ -169,7 +173,7 @@ class TaskJuggler
169
173
  def checkStatusSheet(fileName)
170
174
  begin
171
175
  Log.enter('checkStatusSheet', 'Parsing #{fileName} ...')
172
- return false unless (ss = parseFile(fileName, 'statusSheet'))
176
+ return false unless (ss = parseFile(fileName, 'statusSheetFile'))
173
177
  queryAttrs = { 'project' => @project,
174
178
  'property' => ss[0],
175
179
  'scopeProperty' => nil,
@@ -201,6 +205,12 @@ class TaskJuggler
201
205
  @project['projectid']
202
206
  end
203
207
 
208
+ # Return the name of the project or nil if no project has been loaded yet.
209
+ def projectName
210
+ return nil if @project.nil?
211
+ @project['name']
212
+ end
213
+
204
214
  # Return the number of errors that had been reported during processing.
205
215
  def errors
206
216
  @project.messageHandler.errors
data/lib/TaskScenario.rb CHANGED
@@ -95,10 +95,10 @@ class TaskJuggler
95
95
  markMilestone
96
96
  end
97
97
 
98
- # The parser only stores the full task IDs for each of the dependencies. This
99
- # function resolves them to task references and checks them. In addition
100
- # to the 'depends' and 'precedes' property lists we also keep 4 additional
101
- # lists.
98
+ # The parser only stores the full task IDs for each of the dependencies.
99
+ # This function resolves them to task references and checks them. In
100
+ # addition to the 'depends' and 'precedes' property lists we also keep 4
101
+ # additional lists.
102
102
  # startpreds: All precedessors to the start of this task
103
103
  # startsuccs: All successors to the start of this task
104
104
  # endpreds: All predecessors to the end of this task
@@ -304,7 +304,7 @@ class TaskJuggler
304
304
  if ((startSpeced && endSpeced) ||
305
305
  (hasDependencies(false) && a('forward') && endSpeced) ||
306
306
  (hasDependencies(true) && !a('forward') && startSpeced)) &&
307
- durationSpecs > 0 && !a('scheduled')
307
+ durationSpecs > 0 && !@property.provided('scheduled', @scenarioIdx)
308
308
  error('task_overspecified',
309
309
  "Task #{@property.fullId} has a start, an end and a " +
310
310
  'duration specification.')
@@ -842,6 +842,10 @@ class TaskJuggler
842
842
  propagateDate(a(thisEnd), !atEnd)
843
843
  end
844
844
  Log << "Milestone #{@property.fullId}: #{a('start')} -> #{a('end')}"
845
+ elsif !a('scheduled') && a('start') && a('end') &&
846
+ !(a('length') == 0 && a('duration') == 0 && a('effort') == 0 &&
847
+ !a('allocate').empty?)
848
+ @property['scheduled', @scenarioIdx] = true
845
849
  end
846
850
 
847
851
  # Propagate date to all dependent tasks.
@@ -942,8 +946,6 @@ class TaskJuggler
942
946
  end
943
947
  end
944
948
 
945
- @property['scheduled', @scenarioIdx] = true
946
-
947
949
  startSet = endSet = false
948
950
  # Propagate the dates to other dependent tasks.
949
951
  if a('start').nil? || a('start') > nStart
@@ -954,6 +956,10 @@ class TaskJuggler
954
956
  @property['end', @scenarioIdx] = nEnd
955
957
  endSet = true
956
958
  end
959
+ unless a('start') && a('end')
960
+ raise "Start (#{a('start')}) and end (#{a('end')}) must be set"
961
+ end
962
+ @property['scheduled', @scenarioIdx] = true
957
963
  Log << "Container task #{@property.fullId}: #{a('start')} -> #{a('end')}"
958
964
 
959
965
  # If we have modified the start or end date, we need to communicate this
@@ -1166,10 +1172,10 @@ class TaskJuggler
1166
1172
  str += "* <nowiki>#{task.name}</nowiki> (#{task.fullId}) "
1167
1173
  if onEnd
1168
1174
  taskEnd = task['end', query.scenarioIdx].to_s(query.timeFormat)
1169
- str += "[->[ #{taskEnd}"
1175
+ str += "[->] #{taskEnd}"
1170
1176
  else
1171
1177
  taskStart = task['start', query.scenarioIdx].to_s(query.timeFormat)
1172
- str += "[->] #{taskStart}"
1178
+ str += "[->[ #{taskStart}"
1173
1179
  end
1174
1180
  str += "\n"
1175
1181
  end
@@ -1178,10 +1184,10 @@ class TaskJuggler
1178
1184
  str += "* <nowiki>#{task.name}</nowiki> (#{task.fullId}) "
1179
1185
  if onEnd
1180
1186
  taskEnd = task['end', query.scenarioIdx].to_s(query.timeFormat)
1181
- str += "]->[ #{taskEnd}"
1187
+ str += "]->] #{taskEnd}"
1182
1188
  else
1183
1189
  taskStart = task['start', query.scenarioIdx].to_s(query.timeFormat)
1184
- str += "]->] #{taskStart}"
1190
+ str += "]->[ #{taskStart}"
1185
1191
  end
1186
1192
  str += "\n"
1187
1193
  end
@@ -1198,7 +1204,7 @@ class TaskJuggler
1198
1204
  str += "* <nowiki>#{task.name}</nowiki> (#{task.fullId}) "
1199
1205
  if onEnd
1200
1206
  taskEnd = task['end', query.scenarioIdx].to_s(query.timeFormat)
1201
- str += "]->[ #{taskEnd}"
1207
+ str += "]->] #{taskEnd}"
1202
1208
  else
1203
1209
  taskStart = task['start', query.scenarioIdx].to_s(query.timeFormat)
1204
1210
  str += "[->[ #{taskStart}"
@@ -1213,7 +1219,7 @@ class TaskJuggler
1213
1219
  str += "[->] #{taskEnd}"
1214
1220
  else
1215
1221
  taskStart = task['start', query.scenarioIdx].to_s(query.timeFormat)
1216
- str += "]->] #{taskStart}"
1222
+ str += "]->[ #{taskStart}"
1217
1223
  end
1218
1224
  str += "\n"
1219
1225
  end
@@ -1333,6 +1339,7 @@ class TaskJuggler
1333
1339
 
1334
1340
  # Check if the Task _task_ depends on this task. _depth_ specifies how
1335
1341
  # many dependent task are traversed at max. A value of 0 means no limit.
1342
+ # TODO: Change this to a non-recursive implementation.
1336
1343
  def isDependencyOf(task, depth)
1337
1344
  return true if task == @property
1338
1345
 
@@ -1364,6 +1371,21 @@ class TaskJuggler
1364
1371
  false
1365
1372
  end
1366
1373
 
1374
+ # If _task_ or any of its sub-tasks depend on this task or any of its
1375
+ # sub-tasks, we call this task a feature of _task_.
1376
+ def isFeatureOf(task)
1377
+ sources = @property.all
1378
+ destinations = task.all
1379
+
1380
+ sources.each do |s|
1381
+ destinations.each do |d|
1382
+ return true if s.isDependencyOf(@scenarioIdx, d, 0)
1383
+ end
1384
+ end
1385
+
1386
+ false
1387
+ end
1388
+
1367
1389
  # Returns true of the _resource_ is assigned to this task or any of its
1368
1390
  # children.
1369
1391
  def hasResourceAllocated?(interval, resource)
@@ -1421,7 +1443,6 @@ class TaskJuggler
1421
1443
  # or start date and propagate the value to neighbouring tasks.
1422
1444
  if (a('length') > 0 && @doneLength >= a('length')) ||
1423
1445
  (a('duration') > 0 && @doneDuration >= a('duration'))
1424
- @property['scheduled', @scenarioIdx] = true
1425
1446
  if a('forward')
1426
1447
  propagateDate(slot + slotDuration, true)
1427
1448
  else
@@ -1434,7 +1455,6 @@ class TaskJuggler
1434
1455
  if @doneEffort >= a('effort')
1435
1456
  # The specified effort has been reached. The has been fully scheduled
1436
1457
  # now.
1437
- @property['scheduled', @scenarioIdx] = true
1438
1458
  if a('forward')
1439
1459
  propagateDate(@tentativeEnd, true)
1440
1460
  else
@@ -1508,11 +1528,9 @@ class TaskJuggler
1508
1528
  return if !shifts.onShift?(date)
1509
1529
  end
1510
1530
 
1511
- # If the task has allocation limits we need to make sure that none of them
1512
- # is already exceeded.
1513
- @limits.each do |limit|
1514
- return if !limit.ok?(date)
1515
- end
1531
+ # If the task has resource independent allocation limits we need to make
1532
+ # sure that none of them is already exceeded.
1533
+ return unless limitsOk?(date)
1516
1534
 
1517
1535
  sbIdx = @project.dateToIdx(date)
1518
1536
 
@@ -1530,8 +1548,11 @@ class TaskJuggler
1530
1548
  # group must be available.
1531
1549
  allAvailable = true
1532
1550
  candidate.allLeaves.each do |resource|
1533
- if !resource.available?(@scenarioIdx, sbIdx) ||
1551
+ if !limitsOk?(date, resource) ||
1552
+ !resource.available?(@scenarioIdx, sbIdx) ||
1534
1553
  takenMandatories.include?(resource)
1554
+ # We've found a mandatory resource that is not available for
1555
+ # the slot.
1535
1556
  allAvailable = false
1536
1557
  break
1537
1558
  else
@@ -1573,8 +1594,10 @@ class TaskJuggler
1573
1594
  booked = false
1574
1595
  resource.allLeaves.each do |r|
1575
1596
  # Prevent overbooking when multiple resources are allocated and
1576
- # available.
1577
- break if a('effort') > 0 && @doneEffort >= a('effort')
1597
+ # available. If the task has allocation limits we need to make sure
1598
+ # that none of them is already exceeded.
1599
+ break if a('effort') > 0 && @doneEffort >= a('effort') ||
1600
+ !limitsOk?(date, resource)
1578
1601
 
1579
1602
  if r.book(@scenarioIdx, sbIdx, @property)
1580
1603
 
@@ -1593,7 +1616,7 @@ class TaskJuggler
1593
1616
  # Limits do not take efficiency into account. Limits are usage limits,
1594
1617
  # not effort limits.
1595
1618
  @limits.each do |limit|
1596
- limit.inc(date)
1619
+ limit.inc(date, resource)
1597
1620
  end
1598
1621
 
1599
1622
  unless a('assignedresources').include?(r)
@@ -1606,6 +1629,18 @@ class TaskJuggler
1606
1629
  booked
1607
1630
  end
1608
1631
 
1632
+ # Check if all of the task limits are not exceded at the given _date_. If
1633
+ # a _resource_ is provided, the limit for that particular resource is
1634
+ # checked. If no resource is provided, only non-resource-specific limits
1635
+ # are checked.
1636
+ def limitsOk?(date, resource = nil)
1637
+ @limits.each do |limit|
1638
+ return false if !limit.ok?(date, true, resource)
1639
+ end
1640
+ true
1641
+ end
1642
+
1643
+
1609
1644
  # Register the user provided bookings with the Resource scoreboards. A
1610
1645
  # booking describes the assignment of a Resource to a certain Task for a
1611
1646
  # specified Interval.
@@ -1617,7 +1652,7 @@ class TaskJuggler
1617
1652
  "Booked resources may not be group resources", true,
1618
1653
  booking.sourceFileInfo)
1619
1654
  end
1620
- unless a('forward') || a('scheduled')
1655
+ unless a('forward') || scheduled
1621
1656
  error('booking_forward_only',
1622
1657
  "Only forward scheduled tasks may have booking statements.")
1623
1658
  end
@@ -1639,7 +1674,7 @@ class TaskJuggler
1639
1674
  @lastSlot = date if @lastSlot.nil? || date > @lastSlot
1640
1675
  @tentativeEnd = tEnd if @tentativeEnd.nil? ||
1641
1676
  @tentativeEnd < tEnd
1642
- if !a('scheduled') && (a('start').nil? || date < a('start'))
1677
+ if !scheduled && (a('start').nil? || date < a('start'))
1643
1678
  @property['start', @scenarioIdx] = date
1644
1679
  end
1645
1680
 
data/lib/TextParser.rb CHANGED
@@ -161,7 +161,7 @@ class TaskJuggler
161
161
  @@expectedTokens = []
162
162
  updateParserTables
163
163
  begin
164
- result = parseRule(@rules[ruleName])
164
+ result = parseRuleR(@rules[ruleName])
165
165
  rescue TjException
166
166
  # error('parse_error', $!.message)
167
167
  return nil
@@ -170,21 +170,12 @@ class TaskJuggler
170
170
  result
171
171
  end
172
172
 
173
- # Return true if the scanner has processed all files.
174
- def checkForEnd
175
- token = @scanner.nextToken
176
- unless token[0] == '.'
177
- error('junk_at_expected_eof',
178
- "Found garbage at expected end of file: #{token[1]}")
179
- end
180
- end
181
-
182
173
  # Return the SourceFileInfo of the TextScanner at the beginning of the
183
174
  # currently processed TextParser::Rule. Or return nil if we don't have a
184
175
  # current position.
185
176
  def sourceFileInfo
186
177
  return nil if @stack.empty?
187
- @stack.last.sourceFileInfo
178
+ @stack.last.sourceFileInfo[0]
188
179
  end
189
180
 
190
181
  def matchingRules(keyword)
@@ -196,12 +187,18 @@ class TaskJuggler
196
187
  matches
197
188
  end
198
189
 
199
- def error(id, text, property = nil)
200
- @scanner.error(id, text, property)
190
+ def error(id, text, property = nil, sfi = nil)
191
+ if @scanner
192
+ @scanner.error(id, text, property, sfi)
193
+ else
194
+ message = Message.new(id, 'error', text, property, sfi)
195
+ @messageHandler.send(message)
196
+ raise TjException.new, ''
197
+ end
201
198
  end
202
199
 
203
- def warning(id, text, property = nil)
204
- @scanner.warning(id, text, property)
200
+ def warning(id, text, property = nil, sfi = nil)
201
+ @scanner.warning(id, text, property, sfi)
205
202
  end
206
203
 
207
204
  private
@@ -274,7 +271,8 @@ class TaskJuggler
274
271
  if type == ?$
275
272
  if @variables.index(token).nil?
276
273
  error('unsupported_token',
277
- "The token #{token} is not supported here.")
274
+ "The token #{token} is not supported here.", nil,
275
+ token[2])
278
276
  end
279
277
  elsif type == ?!
280
278
  if @rules[token].nil?
@@ -289,8 +287,10 @@ class TaskJuggler
289
287
  # This function processes the input starting with the syntax description of
290
288
  # _rule_. It recursively calls this function whenever the syntax description
291
289
  # contains the reference to another rule.
292
- def parseRule(rule)
293
- Log.enter('parseRule', "Parsing with rule #{rule.name}")
290
+ # This recursive version has cleaner code and is about 8% faster than
291
+ # parseRuleNR.
292
+ def parseRuleR(rule)
293
+ #Log.enter('parseRuleR', "Parsing with rule #{rule.name}")
294
294
  result = rule.repeatable ? TextParserResultArray.new : nil
295
295
  # Rules can be marked 'repeatable'. This flag will be set to true after
296
296
  # the first iteration has been completed.
@@ -300,105 +300,34 @@ class TaskJuggler
300
300
  # which pattern of the rule needs to be processed.
301
301
  token = getNextToken
302
302
 
303
- # The scanner cannot differentiate between keywords and identifiers. So
304
- # whenever an identifier is returned we have to see if we have a
305
- # matching keyword first. If none is found, then look for normal
306
- # identifiers.
307
- if token[0] == 'ID'
308
- if (patIdx = rule.matchingPatternIndex('_' + token[1])).nil?
309
- patIdx = rule.matchingPatternIndex("$ID")
310
- end
311
- elsif token[0] == 'LITERAL'
312
- patIdx = rule.matchingPatternIndex('_' + token[1])
313
- elsif token[0] == false
314
- patIdx = rule.matchingPatternIndex('.')
315
- else
316
- patIdx = rule.matchingPatternIndex('$' + token[0])
317
- end
318
-
319
- # If no matching pattern is found for the token we have to check if the
320
- # rule is optional or we are in repeat mode. If this is the case, return
321
- # the token back to the scanner. Otherwise we have found a token we
322
- # cannot handle at this point.
323
- if patIdx.nil?
324
- # Append the list of expected tokens to the @@expectedToken array.
325
- # This may be used in a later rule to provide more details when an
326
- # error occured.
327
- rule.transitions.each do |transition|
328
- keys = transition.keys
329
- keys.collect! { |key| key[1..-1] }
330
- @@expectedTokens += keys
331
- @@expectedTokens.sort!
332
- end
333
-
334
- unless rule.optional?(@rules) || repeatMode
335
- error('unexpctd_token',
336
- (token[0] != false ?
337
- "Unexpected token '#{token[1]}' of type '#{token[0]}'. " :
338
- "Unexpected end of file in #{@scanner.fileName}. ") +
339
- (@@expectedTokens.length > 1 ?
340
- "Expecting one of #{@@expectedTokens.join(', ')}" :
341
- "Expecting #{@@expectedTokens[0]}"))
342
- end
343
- returnToken(token)
344
- Log.exit('parseRule', "Finished rule #{rule.name}")
345
- return result
346
- end
347
-
348
- pattern = rule.pattern(patIdx)
349
- @stack << TextParser::StackElement.new(rule, pattern.function,
350
- @scanner.sourceFileInfo)
303
+ return result unless (pattern = findPattern(rule, token, repeatMode))
304
+ # The @stack will store the resulting value of each element in the
305
+ # pattern.
306
+ @stack << TextParser::StackElement.new(pattern.function)
351
307
 
352
308
  pattern.each do |element|
353
309
  # Separate the type and token text for pattern element.
354
310
  elType = element[0]
355
311
  elToken = element[1..-1]
356
312
  if elType == ?!
357
- # The element is a reference to another rule. Return the token if we
358
- # still have one and continue with the referenced rule.
313
+ # The element is a reference to another rule. Return the token if
314
+ # we still have one and continue with the referenced rule.
359
315
  unless token.nil?
316
+ sfi = token[2]
360
317
  returnToken(token)
361
318
  token = nil
319
+ else
320
+ sfi = nil
362
321
  end
363
- @stack.last.store(parseRule(@rules[elToken]),
364
- @scanner.sourceFileInfo)
322
+ @stack.last.store(parseRuleR(@rules[elToken]), sfi)
323
+ #Log << "Resuming rule #{rule.name}"
365
324
  else
366
325
  # In case the element is a keyword or variable we have to get a new
367
326
  # token if we don't have one anymore.
368
- if token.nil?
369
- token = getNextToken
370
- end
327
+ token = getNextToken unless token
328
+
329
+ processNormalElements(elType, elToken, token)
371
330
 
372
- if elType == ?_
373
- # If the element requires a keyword the token must match this
374
- # keyword.
375
- if elToken != token[1]
376
- text = "'#{elToken}' expected but found " +
377
- "'#{token[1]}' (#{token[0]})."
378
- unless @@expectedTokens.empty?
379
- text = "#{@@expectedTokens.join(', ')} or " + text
380
- end
381
- error('spec_keywork_expctd', text)
382
- end
383
- @stack.last.store(elToken, @scanner.sourceFileInfo)
384
- elsif elType == ?.
385
- if token != [ '.', '<END>' ]
386
- error('end_expected', 'End expected but found ' +
387
- "'#{token[1]}' (#{token[0]}).")
388
- end
389
- else
390
- # The token must match the expected variable type.
391
- if token[0] != elToken
392
- text = "'#{elToken}' expected but found " +
393
- "'#{token[1]}' (#{token[0]})."
394
- unless @@expectedTokens.empty?
395
- text = "#{@@expectedTokens.join(', ')} or " + text
396
- end
397
- error('spec_token_expctd', text)
398
- end
399
- # If the element is a variable store the value of the token.
400
- @stack.last.store(token[1], @scanner.sourceFileInfo)
401
- end
402
331
  # The token has been consumed. Reset the variable.
403
332
  token = nil
404
333
  @@expectedTokens = []
@@ -409,6 +338,7 @@ class TaskJuggler
409
338
  # function for this pattern to operate on the value array. Then pop the
410
339
  # entry for this rule from the stack.
411
340
  @val = @stack.last.val
341
+ @sourceFileInfo = @stack.last.sourceFileInfo
412
342
  res = nil
413
343
  res = @stack.last.function.call unless @stack.last.function.nil?
414
344
  @stack.pop
@@ -426,24 +356,256 @@ class TaskJuggler
426
356
  repeatMode = true
427
357
  end
428
358
 
429
- Log.exit('parseRule', "Finished rule #{rule.name}")
359
+ #Log.exit('parseRuleR', "Finished rule #{rule.name}")
360
+ return result
361
+ end
362
+
363
+ # This function processes the input starting with the syntax description
364
+ # of _rule_. It's implemented as an unrolled recursion. It recursively
365
+ # iterates over the rule tree as controlled by the input file.
366
+ # This version is not limited by the size of the system stack. So far, I'm
367
+ # not aware of any project that is too large for the system stack. Since
368
+ # the recursive version parseRuleR is about 8% faster and has cleaner
369
+ # code, we use that by default.
370
+ def parseRuleNR(rule)
371
+ elementIdx = 0
372
+ recursionResult = nil
373
+ # These flags are used to managed the control flow to and from the
374
+ # recursion point.
375
+ recur = resume = false
376
+ # The stack that holds the context for the recursion levels. It's either
377
+ # just a rule to start a new recursion or an Array of state variables.
378
+ recursionStack = [ rule ]
379
+ begin
380
+ # Pop the top entry from the recursion stack.
381
+ se = recursionStack.pop
382
+ if se.is_a?(Array)
383
+ # We have essentially finished a recursion level and need to get
384
+ # back to the place where we started the recursion. First, we need
385
+ # to restore the state again.
386
+ rule, pattern, elementIdx, result, repeatMode, sfi = se
387
+ #Log << "Recursion loop started in resume mode for rule #{rule.name}"
388
+ # Now jump to the recursion point without doing anything else.
389
+ resume = true
390
+ else
391
+ # Start a new recursion level. The rule tells us how to interpret
392
+ # the input text.
393
+ rule = se
394
+ #Log.enter('parseRuleNR', "Parsing with rule #{rule.name}")
395
+ resume = false
396
+ end
397
+
398
+ unless resume
399
+ result = rule.repeatable ? TextParserResultArray.new : nil
400
+ # Rules can be marked 'repeatable'. This flag will be set to true
401
+ # after the first iteration has been completed.
402
+ repeatMode = false
403
+ end
404
+
405
+ loop do
406
+ unless resume
407
+ # At the beginning of a rule we need a token from the input to
408
+ # determine which pattern of the rule needs to be processed.
409
+ token = getNextToken
410
+
411
+ break unless (pattern = findPattern(rule, token, repeatMode))
412
+ # The @stack will store the resulting value of each element in the
413
+ # pattern.
414
+ @stack << TextParser::StackElement.new(pattern.function)
415
+
416
+ # Once we've found the right pattern, we need to process each
417
+ # element.
418
+ elementIdx = 0
419
+ end
420
+
421
+ elementCount = pattern.length
422
+ while elementIdx < elementCount
423
+ element = pattern[elementIdx]
424
+ # Separate the type and token text for pattern element.
425
+ elType = element[0]
426
+ elToken = element[1..-1]
427
+ if elType == ?!
428
+ unless resume
429
+ # The element is a reference to another rule. Return the token
430
+ # if we still have one and continue with the referenced rule.
431
+ if token
432
+ sfi = token[2]
433
+ returnToken(token)
434
+ token = nil
435
+ else
436
+ sfi = nil
437
+ end
438
+ # This is where the recursion would happen. Instead, we push
439
+ # the state variables and then the next rule onto the
440
+ # recursion stack.
441
+ recursionStack.push([ rule, pattern, elementIdx, result,
442
+ repeatMode, sfi ])
443
+ recursionStack.push(@rules[elToken])
444
+ # Now terminate all but the outer loops without doing anything
445
+ # else.
446
+ recur = true
447
+ break
448
+ else
449
+ # We're back right after where the recursion started. Store
450
+ # the result and turn resume mode off again.
451
+ @stack.last.store(recursionResult, sfi)
452
+ resume = false
453
+ end
454
+ else
455
+ # In case the element is a keyword or variable we have to get a
456
+ # new token if we don't have one anymore.
457
+ token = getNextToken unless token
458
+
459
+ processNormalElements(elType, elToken, token)
460
+
461
+ # The token has been consumed. Reset the variable.
462
+ token = nil
463
+ @@expectedTokens = []
464
+ end
465
+ elementIdx += 1
466
+ end # of pattern while loop
467
+
468
+ # Skip the rest of the loop in recur mode.
469
+ break if recur
470
+
471
+ elementIdx = 0
472
+
473
+ # Once the complete pattern has been processed we call the
474
+ # processing function for this pattern to operate on the value
475
+ # array. Then pop the entry for this rule from the stack. The
476
+ # called function will use @val and @sourceFileInfo to retrieve
477
+ # data from the parser.
478
+ @val = @stack.last.val
479
+ @sourceFileInfo = @stack.last.sourceFileInfo
480
+ res = @stack.last.function ? @stack.last.function.call : nil
481
+ @stack.pop
482
+
483
+ # If the rule is not repeatable we can store the result and break
484
+ # the outer loop to exit the function.
485
+ unless rule.repeatable
486
+ result = res
487
+ break
488
+ end
489
+
490
+ # Otherwise we append the result to the result array and turn repeat
491
+ # mode on.
492
+ result << res
493
+ # We have completed the first iteration. Set the repeat mode flag to
494
+ # indicate that further iterations are already re-runs.
495
+ repeatMode = true
496
+ end # of rule processing loop
497
+
498
+ if recur
499
+ recur = false
500
+ else
501
+ #Log.exit('parseRuleNR', "Finished rule #{rule.name}")
502
+ recursionResult = result
503
+ end
504
+ end while !recursionStack.empty?
505
+
430
506
  return result
431
507
  end
432
508
 
433
509
  def getNextToken
434
510
  begin
435
511
  token = nextToken
436
- Log << "Token: [#{token[0]}][#{token[1]}]"
512
+ #Log << "Token: [#{token[0]}][#{token[1]}]"
437
513
  rescue TjException
438
514
  error('parse_rule', $!.message)
439
515
  end
440
516
  if @badVariables.include?(token[0])
441
517
  error('unsupported_token',
442
- "The token #{token[1]} is not supported in this context.")
518
+ "The token #{token[1]} is not supported in this context.",
519
+ nil, token[2])
443
520
  end
444
521
  token
445
522
  end
446
523
 
524
+ def findPattern(rule, token, repeatMode)
525
+ # The scanner cannot differentiate between keywords and identifiers. So
526
+ # whenever an identifier is returned we have to see if we have a
527
+ # matching keyword first. If none is found, then look for normal
528
+ # identifiers.
529
+ if token[0] == 'ID'
530
+ if (patIdx = rule.matchingPatternIndex('_' + token[1])).nil?
531
+ patIdx = rule.matchingPatternIndex("$ID")
532
+ end
533
+ elsif token[0] == 'LITERAL'
534
+ patIdx = rule.matchingPatternIndex('_' + token[1])
535
+ elsif token[0] == false
536
+ patIdx = rule.matchingPatternIndex('.')
537
+ else
538
+ patIdx = rule.matchingPatternIndex('$' + token[0])
539
+ end
540
+
541
+ # If no matching pattern is found for the token we have to check if the
542
+ # rule is optional or we are in repeat mode. If this is the case, return
543
+ # the token back to the scanner. Otherwise, we have found a token we
544
+ # cannot handle at this point.
545
+ if patIdx.nil?
546
+ # Append the list of expected tokens to the @@expectedToken array.
547
+ # This may be used in a later rule to provide more details when an
548
+ # error occured.
549
+ rule.transitions.each do |transition|
550
+ keys = transition.keys
551
+ keys.collect! { |key| key[1..-1] }
552
+ @@expectedTokens += keys
553
+ @@expectedTokens.sort!
554
+ end
555
+
556
+ unless rule.optional?(@rules) || repeatMode
557
+ error('unexpctd_token',
558
+ (token[0] != false ?
559
+ "Unexpected token '#{token[1]}' of type " +
560
+ "'#{token[0]}'. " :
561
+ "Unexpected end of file in #{@scanner.fileName}. ") +
562
+ (@@expectedTokens.length > 1 ?
563
+ "Expecting one of #{@@expectedTokens.join(', ')}" :
564
+ "Expecting #{@@expectedTokens[0]}"))
565
+ end
566
+ returnToken(token)
567
+ return nil
568
+ end
569
+
570
+ rule.pattern(patIdx)
571
+ end
572
+
573
+ # Handle the elements that don't trigger a recursion.
574
+ def processNormalElements(elType, elToken, token)
575
+ if elType == ?_
576
+ # If the element requires a keyword the token must match this
577
+ # keyword.
578
+ if elToken != token[1]
579
+ text = "'#{elToken}' expected but found " +
580
+ "'#{token[1]}' (#{token[0]})."
581
+ unless @@expectedTokens.empty?
582
+ text = "#{@@expectedTokens.join(', ')} or " + text
583
+ end
584
+ error('spec_keywork_expctd', text)
585
+ end
586
+ @stack.last.store(elToken, token[2])
587
+ elsif elType == ?.
588
+ if token[0..1] != [ '.', '<END>' ]
589
+ error('end_expected',
590
+ "Found garbage at expected end of file: #{token[1]}\n" +
591
+ "If you see this in the middle of your file, you probably " +
592
+ "have closed your context too early.")
593
+ end
594
+ else
595
+ # The token must match the expected variable type.
596
+ if token[0] != elToken
597
+ text = "'#{elToken}' expected but found " +
598
+ "'#{token[1]}' (#{token[0]})."
599
+ unless @@expectedTokens.empty?
600
+ text = "#{@@expectedTokens.join(', ')} or " + text
601
+ end
602
+ error('spec_token_expctd', text)
603
+ end
604
+ # If the element is a variable store the value of the token.
605
+ @stack.last.store(token[1], token[2])
606
+ end
607
+ end
608
+
447
609
  end
448
610
 
449
611
  end