taskjuggler 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
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