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
.entity
;
21 import java
.io
.PrintStream
;
22 import java
.util
.ArrayList
;
23 import java
.util
.HashMap
;
24 import java
.util
.Iterator
;
26 import com
.dtrules
.infrastructure
.RulesException
;
27 import com
.dtrules
.interpreter
.ARObject
;
28 import com
.dtrules
.interpreter
.IRObject
;
29 import com
.dtrules
.interpreter
.RName
;
30 import com
.dtrules
.interpreter
.RNull
;
31 import com
.dtrules
.session
.IRSession
;
32 import com
.dtrules
.session
.RSession
;
35 * Entities serve as the dictionaries of the Rules Engine. These
36 * Dictionaries are typed Hashtables. In other words, you can't
37 * just put any old object into an Entity. There has to be an
38 * attribute with the appropriate name and type in order to put
39 * a particular object with that name into an Entity.
41 * This structure catches many data entry and program structure
42 * errors, as well as provides for a number of convienent automatic
43 * data conversions (if the compiler writer cares to provide such
49 public class REntity
extends ARObject
implements IREntity
{
51 public void removeAttribute(RName attrib
) {
52 attributes
.remove(attrib
);
55 /** This attribute's name */
59 HashMap
<RName
,REntityEntry
> attributes
;
60 ArrayList
<IRObject
> values
= new ArrayList
<IRObject
>();
63 * A readonly entity cannot be modified at run time.
65 public boolean isReadOnly(){ return readonly
; }
68 * All reference Entities (i.e. those from which we clone instances) have
69 * an id of zero. Clones have an id that is non zero, and unique.
74 * Returns the ID number for this instance of the entity. Entities with
75 * an ID of zero are reference entities.
78 public int getID(){ return id
;}
81 * Returns an interator that provides all the names of all the attributes for this entity.
83 public Iterator
<RName
> getAttributeIterator(){
84 return attributes
.keySet().iterator();
87 * Checks to see if the given attribute is defined by this entity.
91 public boolean containsAttribute(RName attName
){
92 return attributes
.containsKey(attName
);
96 * Create a clone of an Entity.
99 public REntity( boolean _readonly
, REntity entity
, IRSession s
) throws RulesException
{
100 id
= s
.getUniqueID();
101 readonly
= _readonly
;
103 attributes
= entity
.attributes
;
104 values
= new ArrayList
<IRObject
>(entity
.values
);
105 for(int i
=0;i
<values
.size();i
++){
106 IRObject value
= values
.get(i
);
107 if(value
.type()!=iEntity
){ // Entities are copied by reference.
108 values
.set(i
,value
.clone(s
));
110 values
.set(i
,value
); // The clone references the same entity
113 put(name
,this); //Patch up the self reference to point to self.
114 put(mappingKey
,RNull
.getRNull()); //
118 * Regular Constructor. This should only be called when building the EntityFactory.
119 * However, we make it public so the EntityFactory can be defined in the Session
120 * package. We might like to reconsider that decision some time in the future.
124 public REntity( int id
, boolean _readonly
, RName _name
) {
126 readonly
= _readonly
;
128 attributes
= new HashMap
<RName
,REntityEntry
>();
129 this.addAttribute(_name
, "", this, false, true, type(),null); // Add a reference to self!
130 this.addAttribute(mappingKey
,"",RNull
.getRNull(),false, true, iString
,null);
134 * @see com.dtrules.entity.IREntity#getName()
136 public RName
getName(){
141 * Adds this REntityEntry to this Entity. If an EntityEntry already exists, it will
142 * be replaced by this new one, no questions asked. The assumption is that one is
143 * editing the Attributes of the Entity, and the latest is the the right one to keep.
145 * @param entry The new Attribute meta data.
148 public void addAttribute(REntityEntry entry
){
149 REntityEntry oldentry
= getEntry(entry
.attribute
);
150 if(oldentry
!=null){ // If the attribute already exists
151 entry
.index
= oldentry
.index
; // replace the old one (keep their index)
153 entry
.index
= values
.size(); // If the attribute is new, make a new
154 values
.add(RNull
.getRNull()); // value index.
156 if(entry
.defaultvalue
==null){ // Update with the default value.
157 values
.set(entry
.index
, RNull
.getRNull());
159 values
.set(entry
.index
, entry
.defaultvalue
);
162 attributes
.put(entry
.attribute
,entry
); // Put the new Entity Entry into this Entity.
167 * This adds an attribute into the REntity. This is called by the Entity
168 * construction during the loading of the Entity Description Dictionary.
170 * @param defaultvalue Default value for this tag
171 * @param writable If true, the attribute is writable by decision tables
172 * @param readable If true, the attribute is readable by decision tables
174 * @return null if successful, and an Error string if it failed.
176 public String
addAttribute(RName attributeName
,
178 IRObject defaultvalue
,
183 REntityEntry entry
= getEntry(attributeName
);
185 int index
= values
.size();
186 if(defaultvalue
==null){
187 values
.add(RNull
.getRNull());
189 values
.add(defaultvalue
);
191 REntityEntry newEntry
= new REntityEntry(this,attributeName
,defaulttxt
, defaultvalue
,writable
,readable
,type
,subtype
,index
);
193 attributes
.put(attributeName
,newEntry
);
196 if(entry
.type
!=type
){
200 type1
="("+ RSession
.typeInt2Str(entry
.type
)+") ";
201 }catch(RulesException e
){}
203 type2
="("+ RSession
.typeInt2Str(type
)+")";
204 }catch(RulesException e
){}
206 return "The entity '"+name
.stringValue()+
207 "' has an attribute '"+attributeName
.stringValue()+
208 "' with two types: "+type1
+" and "+ type2
+ "\n";
210 return null; // Entry already matches what we already have.
213 * @see com.dtrules.entity.IREntity#put(com.dtrules.interpreter.RName, com.dtrules.interpreter.IRObject)
215 public void put(RName attrib
, IRObject value
) throws RulesException
{
216 REntityEntry entry
= (REntityEntry
)attributes
.get(attrib
);
217 if(entry
==null)throw new RulesException("Undefined", "REntity.put()", "Undefined Attribute "+attrib
+" in Entity: "+name
);
218 if(value
.type()!= iNull
&& entry
.type
!= value
.type()){
220 case iInteger
: value
= value
.rIntegerValue(); break;
221 case iDouble
: value
= value
.rDoubleValue(); break;
222 case iBoolean
: value
= value
.rBooleanValue(); break;
223 //case iDecisiontable : value = value.rDecisiontableValue(); break;
224 case iEntity
: value
= value
.rEntityValue(); break;
225 //case iMark : value = value.rMarkValue(); break;
226 case iName
: value
= value
.rNameValue(); break;
227 //case iOperator : value = value.rOperatorValue(); break;
228 case iString
: value
= value
.rStringValue(); break;
229 case iTime
: value
= value
.rTimeValue(); break;
232 values
.set(entry
.index
,value
);
236 * Looks up the name of an attribute,
237 * and returns the associated value.
238 * If no value is defined, returns a null.
240 public IRObject
get(String attribName
)
242 return get(RName
.getRName(attribName
));
246 * Looks up the given name, and returns the associated value. If
247 * no value is defined, returns a null.
249 public IRObject
get(RName attrib
) {
250 REntityEntry entry
= (REntityEntry
)attributes
.get(attrib
);
251 if(entry
==null)return null;
252 return (IRObject
) values
.get(entry
.index
);
256 * @see com.dtrules.entity.IREntity#get(int)
258 public IRObject
get(int i
) {
259 return (IRObject
) values
.get(i
);
263 * Sets a value in the values array. Should only be used
264 * RARELY outside of REntity.
268 public void set(int i
, IRObject v
){
272 * Returns an object that describes all the information we track about an
273 * Entity Attribute (the key to get its value). If the attribute is undefined,
274 * then a null is returned.
276 public REntityEntry
getEntry(RName attrib
) {
277 REntityEntry entry
= (REntityEntry
)attributes
.get(attrib
);
281 * @see com.dtrules.entity.IREntity#getValue(int)
283 public IRObject
getValue(int index
) {
284 return (IRObject
) values
.get(index
);
289 * @see com.dtrules.entity.IREntity#postFix()
291 public String
postFix() {
292 return "/"+name
.stringValue()+" "+id
+" createEntity ";
296 * @see com.dtrules.entity.IREntity#stringValue()
298 public String
stringValue() {
299 return name
.stringValue();
303 * @see com.dtrules.entity.IREntity#type()
309 public String
toString(){
310 String v
= name
.stringValue()+" = {";
311 Iterator
<RName
> ia
= getAttributeIterator();
315 if(o
==null){ // Protect ourselves from nulls.
316 o
= RNull
.getRNull();
318 v
+=n
.stringValue()+" = "+get(n
).stringValue()+" ";
325 public IRObject
clone(IRSession s
) throws RulesException
{
326 if(readonly
)return this;
327 return new REntity(false,this,s
);
333 public IREntity
rEntityValue() throws RulesException
{
337 public void writeXML(PrintStream p
) throws RulesException
{
338 Iterator
<RName
> attribs
= getAttributeIterator();
340 while(attribs
.hasNext()){
341 RName attrib
= attribs
.next();
342 if(attrib
.equals(name
))continue; // Skip the self reference.
343 REntityEntry entry
= attributes
.get(attrib
);
344 p
.print("<entity attribute=\"");
345 p
.print(attrib
.stringValue());
346 p
.print("\" type =\"");
347 p
.print(RSession
.typeInt2Str(entry
.type
));
348 p
.print("\" cdd_default_value=\"");
349 p
.print(entry
.defaulttxt
);
350 p
.print("\" cdd_i_c=\"");
351 p
.print(entry
.writable?
"c":"i");
352 p
.print("\" parseStr=\"\">");
353 p
.print(name
.stringValue());
354 p
.println("</entity>");