Updated error messages when an error occurs within an executable array
[DTRules.git] / DTRules / src / main / java / com / dtrules / decisiontables / DTLoader.java
blob8b5751f019f68517669e6bf343a8c907fc179a39
1 /*
2 * $Id$
3 *
4 * Copyright 2004-2007 MTBJ, Inc.
5 *
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
9 *
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.
17 */
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;
27 import java.util.Map;
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;
39 /**
40 * The DTLoader loads decision tables into the EntityFactory. Warning messages
41 * are routed through a RSession object.
43 * @author paul snow
44 * Feb 12, 2007
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.
70 /**
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.
75 **/
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;
95 /**
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.
100 * @param _session
101 * @param _ef
103 public DTLoader(IRSession _session, EntityFactory _ef){
104 session = _session;
105 state = _session.getState();
106 ef = _ef;
110 public void end_decision_tables()throws Exception {
112 * Now we build all the decision tables.
114 Iterator<RName> it = ef.getDecisionTableRNameIterator();
115 while(it.hasNext()){
116 try {
117 RDecisionTable dt = ef.getDecisionTable(it.next());
118 dt.build();
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);
138 c_cnt = 0;
139 a_cnt = 0;
140 col_cnt = 1;
141 col = 0;
142 ia_cnt = 0;
143 context_cnt = 0;
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 **/
166 dt.maxcol = col_cnt;
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);
249 }else {
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 (){
274 context_cnt++;
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 () {
290 ia_cnt++;
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 {
307 if(col>=col_cnt){
308 col_cnt++;
310 int theCol = Integer.parseInt((String)_attribs.get("column_number"))-1;
311 c_table[c_cnt][theCol]= (String) _attribs.get("column_value");
312 col++;
315 public void end_condition_details(){
316 c_cnt++;
320 * Load Actions
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;
344 if(a_col>=col_cnt){
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() {
353 a_cnt++;
356 public void beginTag(String[] tagstk, int tagstkptr, String tag, HashMap attribs) throws IOException, Exception {
358 _tag = tag;
359 _attribs = attribs;
360 _body = null;
361 String tagname = "begin_"+tag;
362 try {
363 Method m = methodCache.get(tagname);
364 if(m==null){
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 {
383 _tag = tag;
384 _attribs = attribs;
385 body = body.trim().replaceAll("[\\n\\r]"," ");
386 _body = body;
387 String tagname = "end_"+tag;
388 try {
389 Class[] classArr = null;
390 Object[] objArr = null;
391 Method m = methodCache.get(tagname);
392 if(m==null){
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.
399 process_field();
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
413 * care about.
415 public boolean error(String v) throws Exception {
416 return true;