initial load
[DTRules.git] / DTRules / src / main / java / com / dtrules / mapping / LoadXMLData.java
blob9710d3cb74529834495299a51e9f165909a195f5
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.mapping;
20 /**
21 * Loads the mapping file (the description of how data should be moved
22 * from XML to the EDD)
25 import java.io.IOException;
26 import java.text.DateFormat;
27 import java.text.ParseException;
28 import java.text.SimpleDateFormat;
29 import java.util.Date;
30 import java.util.HashMap;
31 import java.util.Iterator;
33 import com.dtrules.entity.IREntity;
34 import com.dtrules.entity.REntity;
35 import com.dtrules.infrastructure.RulesException;
36 import com.dtrules.interpreter.IRObject;
37 import com.dtrules.interpreter.RArray;
38 import com.dtrules.interpreter.RBoolean;
39 import com.dtrules.interpreter.RDouble;
40 import com.dtrules.interpreter.RInteger;
41 import com.dtrules.interpreter.RName;
42 import com.dtrules.interpreter.RNull;
43 import com.dtrules.interpreter.RString;
44 import com.dtrules.interpreter.RTime;
45 import com.dtrules.session.DTState;
46 import com.dtrules.session.IRSession;
47 import com.dtrules.session.RSession;
48 import com.dtrules.xmlparser.IGenericXMLParser;
50 @SuppressWarnings({"unchecked"})
51 public class LoadXMLData implements IGenericXMLParser {
52 Mapping map;
53 int codeCnt = 0;
54 IRSession session;
55 IRObject def;
56 DTState state;
57 String ruleSetName;
58 static DateFormat df_in = new SimpleDateFormat ("yyyy-MM-dd");
60 static DateFormat df_out = new SimpleDateFormat ("MM/dd/yyyy");
63 public LoadXMLData(Mapping _map){
64 map = _map;
67 public LoadXMLData(Mapping _map, IRSession session, String _ruleSetName){
68 map = _map;
69 this.session = session;
70 this.state = session.getState();
71 this.ruleSetName = _ruleSetName;
73 try{ // Cache the def operator.
74 def = session.getState().find(RName.getRName("def"));
75 }catch(Exception e){
76 try {
77 state.traceInfo("error", null,"General Rules Engine Failure");
78 } catch (RulesException e1) { } // Ignore since we are going to throw anyway...
79 throw new RuntimeException(e);
82 for(RName ename : this.map.entities.keySet()){
83 try {
84 IREntity e = findEntity(ename.stringValue().toLowerCase(),null,null);
85 state.entitypush(e);
86 } catch (RulesException e) {
87 try {
88 state.traceInfo("error", null,"Failed to initialize the Entity Stack (Failed on "+ename+")\n"+e);
89 } catch (RulesException e1) { } // Ignore since we are going to throw anyway...
90 throw new RuntimeException(e);
95 /**
96 * Looks to make sure that we have not yet created an Entity
97 * of this name with the given code. If we have, we return the
98 * earlier created Entity. Otherwise, we create a new instance.
99 * @param entity -- We assume this is a valid Entity name (no dot syntax)
100 * @param code
101 * @return
102 * @throws RulesException
104 IREntity findEntity( String entity, String code, EntityInfo info) throws RulesException{
105 String number = (String) this.map.entityinfo.get(entity);
106 IREntity e;
107 if(number==null){
108 number = "*";
110 if(number.equals("1")){
111 e = (IREntity)entities.get(entity);
112 if(e==null){
113 e = session.getState().findEntity(RName.getRName(entity+"."+entity));
114 if(e==null){
115 e = ((RSession)session).createEntity(null,entity);
117 entities.put(entity,e);
119 }else { // We assume number.equals("*") || number.equals("+")
120 e = null;
121 String key = "";
122 if(code!=null && code.length()!=0) {
123 key = entity+"$"+code;
124 e = (IREntity)entities.get(key);
126 if(e==null) {
127 e = ((RSession)session).createEntity(null,entity);
129 if(code!=null) entities.put(key,e);
131 if(e==null)throw new RulesException("undefined","LoadXMLData.findEntity()","Failed to create the entity "+entity);
132 UpdateReferences(e,info);
133 return e;
136 public void beginTag(String[] tagstk, int tagstkptr, String tag,
137 HashMap attribs) throws IOException, Exception {
138 String name = tag; // We assume the tag might create an entity
139 @SuppressWarnings("unused")
140 boolean traceopen = false;
141 EntityInfo info = (EntityInfo) this.map.requests.get(name);
142 AttributeInfo aInfo = (AttributeInfo) this.map.setattributes.get(tag);
145 // If I get info, then create an entity.
146 // Get the code from this tag.
147 // If a fixed entity name is specified,
148 // or the tag name, use it. Otherwise use the multiple name
149 if(info!=null){
150 Object objCode = attribs.get(info.id);
151 String code = objCode==null?"":objCode.toString();
152 String eName = info.name;
153 if (eName == null || eName.length() <= 0)
155 eName = (String) attribs.get("name");
157 IREntity e = findEntity(eName, code, info); // Look up the entity I should create.
158 if(e!=null){ // I hope to goodness I can find it!
159 attribs.put("create entity","true");
160 if(code.length()!=0) {
161 e.put(IREntity.mappingKey,RString.newRString(code));
162 }else{
163 e.put(IREntity.mappingKey,RString.newRString("v"+ (++codeCnt)));
166 state.entitypush(e);
167 if(state.testState(DTState.TRACE)){
168 state.traceTagBegin("createEntity", "name='"+info.name+"' id='"+code+"'");
169 traceopen = true;
171 }else{
173 state.traceInfo("error", null, "The Mapping defines '"+info.entity+"', but this entity isn't defined in the EDD");
174 throw new Exception("The Mapping defines '"+info.entity+"', but this entity isn't defined in the EDD");
178 if(aInfo!=null){ // If we are supposed to set an attribute, then we set ourselves up
179 // to define the entity attribute on the end tag. We may be setting this
180 // Attribute to the value of the Entity we just created/looked up.
181 /**
182 * First check enclosures, then check the blank case. This allows
183 * the user to specify a default mapping, yet still direct some attributes to
184 * specific destinations based on the enclosure.
186 AttributeInfo.Attrib attrib;
187 { // Not only do you have to match the attribute name,
188 int i=tagstkptr-2; // But you must match the immediately enclosing tag
189 if(i>=0){
190 attrib = aInfo.lookup(tagstk[i]);
191 if(attrib!=null){
192 queueSetAttribute(attrib, attribs);
196 attrib = aInfo.lookup(""); // If I don't find the enclosure defined, look to see
197 queueSetAttribute(attrib,attribs); // if a general default is defined.
201 public void endTag(String[] tagstk, int tagstkptr, String tag, String body,
202 HashMap attribs) throws Exception, IOException
204 String attr;
205 REntity entity = null;
207 if(attribs.containsKey("create entity")){ // For create Entity Tags, we pop the Entity from the Entity Stack on the End Tag.
208 entity = (REntity) state.entitypop();
209 Iterator pairs = map.attribute2listPairs.iterator();
210 body = ""; // Don't care about the Body if we created an Entity.
211 while(pairs.hasNext()){
212 Object [] pair = (Object []) pairs.next();
213 if(entity.containsAttribute(RName.getRName((String)pair[0]))){
214 RName.getRName((String) pair[1],true).execute(state);
215 state.entitypush(entity);
216 RName.getRName("addto",true).execute(state);
220 if(state.testState(DTState.TRACE)){
221 state.traceTagEnd("createEntity", null);
225 // If this is a Date format, we are going to reformat it, and let it feed into
226 // the regular set attribute code.
227 if ((attr = (String) attribs.get("set attribute date"))!=null){ // Look and see if we have an attribute name defined.
228 if (body.trim().length() > 0)
230 Date date;
231 try {
232 if(false && body.indexOf("7777")>=0){
233 date = new Date(0x7FFFFFFFFFFFL);
234 }else{
235 date = df_in.parse(body);
237 } catch (ParseException e) {
238 try{
239 date = df_out.parse(body);
240 }catch (ParseException e2){
241 date = df_out.parse("01/05/2008");
242 //throw new RuntimeException("Bad Date encountered: ("+tag+")="+body);
245 body = df_out.format(date);
246 attribs.put("set attribute",attr);
250 if ((attr = (String) attribs.get("set attribute"))!=null){
251 // Look and see if we have an attribute name defined.
253 RName a = RName.getRName(attr);
254 IRObject value;
256 IREntity enclosingEntity = session.getState().findEntity(a);
257 if(enclosingEntity!=null){
260 int type = enclosingEntity.getEntry(a).type;
262 if(type == IRObject.iInteger){
263 value = RInteger.getRIntegerValue(body.length()==0? "0" : body);
264 } else if (type == IRObject.iDouble) {
265 value = RDouble.getRDoubleValue(body.length()==0? "0" : body);
266 } else if (type == IRObject.iBoolean){
267 value = RBoolean.getRBoolean(body.length()==0? "false" : body);
268 } else if (type == IRObject.iTime){
269 if(body.trim().length()>0){
270 value = RTime.getRDate(body);
271 if(value == null){
272 throw new RulesException("MappingError","LoadXMLData","Bad Date... Could not parse '"+body+"'");
274 }else{
275 value = RNull.getRNull();
277 } else if (type == IRObject.iEntity){
278 if(entity!=null){
279 value = entity;
280 }else{
281 throw new RulesException("MappingError","LoadXMLData","Entity Tags have to create some Entity Reference");
283 }else {
284 value = RString.newRString(body);
286 // conversion in the Rules Engine to do the proper thing.
287 state.def(a,value,false);
292 return;
295 public boolean error(String v) throws Exception {
296 return true;
299 /**
300 * Does nothing if the info is null... Just means we are not mapping this attribute.
301 * Otherwise it updates the attribs hashmap.
303 * @param info
305 private void queueSetAttribute( AttributeInfo.Attrib attrib, HashMap attribs){
306 if(attrib==null)return;
307 switch (attrib.type ){
308 case AttributeInfo.DATE_STRING_CODE :
309 attribs.put("set attribute date",attrib.rAttribute);
310 break;
311 case AttributeInfo.STRING_CODE :
312 case AttributeInfo.NONE_CODE :
313 case AttributeInfo.INTEGER_CODE :
314 case AttributeInfo.BOOLEAN_CODE :
315 case AttributeInfo.FLOAT_CODE :
316 case AttributeInfo.ENTITY_CODE :
317 case AttributeInfo.XMLVALUE_CODE :
318 attribs.put("set attribute",attrib.rAttribute);
319 break;
320 default:
321 throw new RuntimeException("Bad Type Code "+attrib.type+" in com.dtrules.mapping.AttributeInfo: "+attrib.rAttribute);
326 * We collect all the Entities we create as we go. These
327 * are stored as a value, and their id as the key. Then if
328 * we encounter the same key in the XML, we return the same
329 * Entity.
331 HashMap entities = new HashMap();
333 IREntity UpdateReferences(IREntity e, EntityInfo info)throws RulesException {
334 RName listname;
335 if(info!=null && info.list.length()==0){
336 listname = RName.getRName(e.getName().stringValue()+"s");
337 }else{
338 listname = RName.getRName(info.list);
340 // First add this entity to any list found on the entity stack.
341 for(int i=0; i< state.edepth(); i++){
342 // Look for all Array Lists on the Entity Stack that look like lists of this Entity
343 IREntity entity = state.getes(i);
344 IRObject elist = entity.get(listname);
345 if(elist!=null && elist.type()==IRObject.iArray){
346 // If not a member of this list, then add it.
347 if(!((RArray)elist).contains(e)){
348 ((RArray)elist).add(e);
352 // Then update the reference to this entity that might be on the Entity Stack.
353 // DON'T go wild. Only look at the top entity (or you may overwrite a reference
354 // you'd rather leave alone.
355 // DON'T mess with any entity's self reference though! That is BAD.
356 int i=state.edepth()-1;
357 if(((IREntity)state.getes(i)).get(e.getName())!=null){
358 IREntity refto = state.getes(i);
359 if(! refto.getName().equals(e.getName())) // Update a reference to an Entity of the same name,
360 ((IREntity)state.getes(i)).put(e.getName(), e); // but only if it isn't a self reference.
364 return e;