4 * Copyright 2004-2007 MTBJ, Inc.
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
19 package com
.dtrules
.decisiontables
;
21 import java
.io
.IOException
;
22 import java
.lang
.reflect
.InvocationTargetException
;
23 import java
.lang
.reflect
.Method
;
24 import java
.util
.HashMap
;
25 import java
.util
.Iterator
;
26 import java
.util
.List
;
29 import com
.dtrules
.entity
.IREntity
;
30 import com
.dtrules
.infrastructure
.RulesException
;
31 import com
.dtrules
.interpreter
.IRObject
;
32 import com
.dtrules
.interpreter
.RName
;
33 import com
.dtrules
.session
.DTState
;
34 import com
.dtrules
.session
.EntityFactory
;
35 import com
.dtrules
.session
.ICompilerError
;
36 import com
.dtrules
.session
.IRSession
;
37 import com
.dtrules
.session
.RSession
;
38 import com
.dtrules
.xmlparser
.IGenericXMLParser
;
40 * The DTLoader loads decision tables into the EntityFactory. Warning messages
41 * are routed through a RSession object.
47 @SuppressWarnings({"unchecked","unused"})
48 public class DTLoader
implements IGenericXMLParser
{
50 private transient String _tag
;
51 private transient String _body
;
52 private transient Map _attribs
;
54 private final EntityFactory ef
; // As tables are loaded, they need to be registered
55 // with the entity factory.
56 private final IRSession session
; // Even though we are not building anything within
57 // a session, we need a session to print warning messages.
58 private final DTState state
; // For error reporting.
60 private int context_cnt
= 0; // Count of contexts
61 private int ia_cnt
= 0; // Count of initial actions.
62 private int c_cnt
= 0; // Count of conditions
63 private int a_cnt
= 0; // Count of actions
64 private int col_cnt
= 1; // Count of columns (We assume at least one column)
65 private int col
= 0; // Current column
67 private boolean processing_fields
= false; // This boolean is only set within the <attribute_fields> tag.
68 // Any tag within this tag is interpreted as an attribute of
69 // the decision table. The Table_type tag is treated special.
71 * The limit here is many times too large for actual decision tables.
72 * In fact, one should throw an error if a table has more than 16 columns,
73 * or more than 16 actions. Tables larger than that are too hard to
74 * understand to be useful.
76 static final int LIMIT
= 100;
78 // Temp Space for collecting data for the decision tables
79 String context_formal
[] = new String
[LIMIT
];
80 String context_postfix
[] = new String
[LIMIT
];
81 String ia_formal
[] = new String
[LIMIT
];
82 String ia_postfix
[] = new String
[LIMIT
];
83 String ia_comment
[] = new String
[LIMIT
];
84 String c_formal
[] = new String
[LIMIT
];
85 String c_comment
[] = new String
[LIMIT
];
86 String c_postfix
[] = new String
[LIMIT
];
87 String c_table
[][] = new String
[LIMIT
][LIMIT
];
88 String a_formal
[] = new String
[LIMIT
];
89 String a_comment
[] = new String
[LIMIT
];
90 String a_postfix
[] = new String
[LIMIT
];
91 String a_table
[][] = new String
[LIMIT
][LIMIT
];
93 RDecisionTable dt
= null;
96 * In order to load decision tables into an EntityFactory, we create
97 * a loader. The tables are loaded into the entityfactory, and warning
98 * messages will be routed through the session.
103 public DTLoader(IRSession _session
, EntityFactory _ef
){
105 state
= _session
.getState();
110 public void end_decision_tables()throws Exception
{
112 * Now we build all the decision tables.
114 Iterator
<RName
> it
= ef
.getDecisionTableRNameIterator();
117 RDecisionTable dt
= ef
.getDecisionTable(it
.next());
120 } catch (RulesException e
) {
121 state
.traceInfo("error", null,"Error building Decision Table "+dt
+"\r\n"+e
);
128 * Handle the Decision Table setup and initialization.
132 * Decision Table Name
133 * @throws RulesException
135 public void end_table_name() throws RulesException
{
136 dt
= ef
.newDecisionTable(_body
, session
);
145 c_table
= new String
[LIMIT
][LIMIT
];
146 a_table
= new String
[LIMIT
][LIMIT
];
151 * All done gathering the info for the table. Now we need to
152 * pack it away into the actual decision table.
155 public void end_decision_table(){
156 /** Contexts do not have comments **/
157 dt
.contexts
= new String
[context_cnt
];
158 dt
.contextsPostfix
= new String
[context_cnt
];
160 /** Initial Actions have Comments, descriptions, and postfix **/
161 dt
.initialActionsComment
= new String
[ia_cnt
];
162 dt
.initialActions
= new String
[ia_cnt
];
163 dt
.initialActionsPostfix
= new String
[ia_cnt
];
165 /** The count of columns for Conditions and actions have to match **/
168 /** Conditions have a comment, description, Postfix, and a truth table **/
169 dt
.conditionsComment
= new String
[c_cnt
];
170 dt
.conditions
= new String
[c_cnt
];
171 dt
.conditionsPostfix
= new String
[c_cnt
];
172 dt
.conditiontable
= new String
[c_cnt
][col_cnt
];
174 /** Actions have a comment, description, Postfix, and a action table **/
175 dt
.actionsComment
= new String
[a_cnt
];
176 dt
.actions
= new String
[a_cnt
];
177 dt
.actionsPostfix
= new String
[a_cnt
];
178 dt
.actiontable
= new String
[a_cnt
][col_cnt
];
180 //Move over the information for the contexts
181 for(int i
=0;i
<context_cnt
;i
++){
182 dt
.contexts
[i
] = context_formal
[i
];
183 dt
.contextsPostfix
[i
] = context_postfix
[i
];
186 //Move over the information for actions
187 for(int i
=0;i
<ia_cnt
;i
++){
188 dt
.initialActions
[i
] =ia_formal
[i
];
189 dt
.initialActionsPostfix
[i
] =ia_postfix
[i
];
190 dt
.initialActionsComment
[i
] =ia_comment
[i
];
193 //Move over the information for the conditions
194 for(int i
=0;i
<c_cnt
;i
++){
195 dt
.conditions
[i
] = c_formal
[i
];
196 dt
.conditionsComment
[i
] = c_comment
[i
]==null?
"":c_comment
[i
];
197 dt
.conditionsPostfix
[i
] = c_postfix
[i
]==null?
"":c_postfix
[i
];
198 for(int j
=0;j
<col_cnt
;j
++){
199 String v
= c_table
[i
][j
];
200 dt
.conditiontable
[i
][j
]= v
==null ?
" " : v
;
204 //Move over the information for the actions
205 for(int i
=0;i
<a_cnt
;i
++){
206 dt
.actions
[i
] = a_formal
[i
];
207 dt
.actionsComment
[i
] = a_comment
[i
]==null?
"":a_comment
[i
];
208 dt
.actionsPostfix
[i
] = a_postfix
[i
]==null?
"":a_postfix
[i
];
209 for(int j
=0;j
<col_cnt
;j
++){
210 String v
= a_table
[i
][j
];
211 dt
.actiontable
[i
][j
]= v
==null ?
" " : v
;
217 * Set the filename on the Decision Table
219 public void end_xls_file(){
220 dt
.setFilename(_body
);
223 /** Turn on field processing when the <attribute_field> tag is encountered
226 public void begin_attribute_fields() {
227 processing_fields
= true;
230 * Turn off attribute field processing outside the </attribute_filed> tag
232 public void end_attribute_fields() {
233 processing_fields
= false;
237 * Mostly we just collect attribute fields. But if the attribute field is the
238 * table_type tag, then it has to have a value of FIRST, ALL, or BALANCED.
240 public void process_field() throws RulesException
{
241 dt
.fields
.put(RName
.getRName(_tag
),_body
);
242 if(_tag
.equalsIgnoreCase("type")){
243 if(_body
.equalsIgnoreCase("first")){
244 dt
.setType(RDecisionTable
.Type
.FIRST
);
245 }else if (_body
.equalsIgnoreCase("all")){
246 dt
.setType(RDecisionTable
.Type
.ALL
);
247 }else if (_body
.equalsIgnoreCase("balanced")){
248 dt
.setType(RDecisionTable
.Type
.BALANCED
);
250 throw new RulesException("Invalid","Decision Table Load","Bad Decision Table type Encountered: '"+_body
+"'");
257 * Handle each condition in turn.
261 public void begin_condition_details(){
262 col
=0; // Start at the begining of the columns again.
265 public void end_context_description(){
266 context_formal
[context_cnt
] = _body
;
269 public void end_context_postfix (){
270 context_postfix
[context_cnt
] = _body
;
273 public void end_context_details (){
277 public void end_initial_action_description(){
278 ia_formal
[ia_cnt
] = _body
;
281 public void end_initial_action_comment(){
282 ia_comment
[ia_cnt
] = _body
;
285 public void end_initial_action_postfix () {
286 ia_postfix
[ia_cnt
] = _body
;
289 public void end_initial_action_details () {
294 public void end_condition_description(){
295 c_formal
[c_cnt
]=_body
;
298 public void end_condition_postfix(){
299 c_postfix
[c_cnt
]=_body
;
302 public void end_condition_comment(){
303 c_comment
[c_cnt
]=_body
;
306 public void begin_condition_column() throws RulesException
{
310 int theCol
= Integer
.parseInt((String
)_attribs
.get("column_number"))-1;
311 c_table
[c_cnt
][theCol
]= (String
) _attribs
.get("column_value");
315 public void end_condition_details(){
324 public void begin_action_details(){
325 for(int i
=0;i
<col_cnt
;i
++){
326 a_table
[a_cnt
][i
]="";
330 public void end_action_description(){
331 a_formal
[a_cnt
]=_body
;
334 public void end_action_postfix(){
335 a_postfix
[a_cnt
]=_body
;
338 public void end_action_comment(){
339 a_comment
[a_cnt
]=_body
;
342 public void begin_action_column() {
343 int a_col
= Integer
.parseInt((String
)_attribs
.get("column_number"))-1;
345 throw new RuntimeException("Decision Table Loader: Too many columns found in "+dt
.toString());
347 String a_v
= (String
)_attribs
.get("column_value");
349 a_table
[a_cnt
][a_col
]= a_v
;
352 public void end_action_details() {
356 public void beginTag(String
[] tagstk
, int tagstkptr
, String tag
, HashMap attribs
) throws IOException
, Exception
{
361 String tagname
= "begin_"+tag
;
363 Method m
= methodCache
.get(tagname
);
365 m
= this.getClass().getMethod(tagname
,(Class
[])null);
366 methodCache
.put(tagname
,m
);
368 m
.invoke(this,(Object
[])null);
369 } catch (NoSuchMethodException e
){ // Ignore undefined tags
370 } catch (InvocationTargetException e
){ // Ignore errors thrown by Decision Table parsing
371 throw new RuntimeException("An Invocation Target Exception was thrown processing the Begin XML tag "+tag
+
372 "\nError states: "+e
.getCause());
373 } catch (Exception e
) {
374 state
.traceInfo("error", null,e
.getCause().getMessage());
375 throw new RuntimeException("Error Parsing Decision Tables: "+e
.getMessage());
379 HashMap
<String
,Method
> methodCache
= new HashMap
<String
,Method
>();
381 public void endTag(String
[] tagstk
, int tagstkptr
, String tag
, String body
, HashMap attribs
) throws Exception
, IOException
{
385 body
= body
.trim().replaceAll("[\\n\\r]"," ");
387 String tagname
= "end_"+tag
;
389 Class
[] classArr
= null;
390 Object
[] objArr
= null;
391 Method m
= methodCache
.get(tagname
);
393 m
= this.getClass().getMethod(tagname
,classArr
);
394 methodCache
.put(tagname
, m
);
396 m
.invoke(this, objArr
);
397 } catch (NoSuchMethodException e
){ // Ignore undefined tags
398 if(processing_fields
){ // Unless we are in the attribute files section.
401 } catch (InvocationTargetException e
){ // Ignore errors thrown by Decision Table parsing
402 state
.traceInfo("error", null,"An Invocation Target Exception was thrown processing the End XML tag "+tag
+
403 "\nError states "+e
.getCause());
404 } catch (Exception e
) {
405 state
.traceInfo("error", null,e
.getCause().getMessage());
406 throw new RuntimeException("Error Parsing Decision Tables: "+e
.getMessage());
412 * Skip DTD stuff and other parsing things the Rules Engine doesn't
415 public boolean error(String v
) throws Exception
{