samer@0
|
1 /*
|
samer@0
|
2 * Environment.java
|
samer@0
|
3 *
|
samer@0
|
4 * Copyright (c) 2001, Samer Abdallah, King's College London.
|
samer@0
|
5 * All rights reserved.
|
samer@0
|
6 *
|
samer@0
|
7 * This software is provided AS IS and WITHOUT ANY WARRANTY;
|
samer@0
|
8 * without even the implied warranty of MERCHANTABILITY or
|
samer@0
|
9 * FITNESS FOR A PARTICULAR PURPOSE.
|
samer@0
|
10 */
|
samer@0
|
11
|
samer@0
|
12 package samer.core;
|
samer@0
|
13
|
samer@0
|
14 /**
|
samer@0
|
15 An Environment is a way of managing named values.
|
samer@0
|
16 The names form a heirarchical system using dot (.) as
|
samer@0
|
17 a seperator. (See Node class)
|
samer@0
|
18
|
samer@0
|
19 Each Environment has a parent Environment, except for the
|
samer@0
|
20 singleton top level Environment.
|
samer@0
|
21
|
samer@0
|
22 Each Environment has a Node which serves as a 'current
|
samer@0
|
23 directory'. Environments form a chain: if one
|
samer@0
|
24 Environment cannot satisfy a request for a named
|
samer@0
|
25 binding, it should ask its parent.
|
samer@0
|
26
|
samer@0
|
27 @see samer.core.Node
|
samer@0
|
28 @see samer.core.X
|
samer@0
|
29 */
|
samer@0
|
30
|
samer@0
|
31 public class Environment
|
samer@0
|
32 {
|
samer@0
|
33 protected Environment parent;
|
samer@0
|
34 protected Node base;
|
samer@0
|
35
|
samer@0
|
36 /** Construct a new Environment with the given parent
|
samer@0
|
37 and base node */
|
samer@0
|
38 public Environment(Environment parent, Node base) { this.parent=parent; this.base=base; }
|
samer@0
|
39
|
samer@0
|
40 /** Construct a new Environment with the given parent
|
samer@0
|
41 and a base node relative to the parent Environment.
|
samer@0
|
42 That is, <code>node = new Node(name, parent.node());</code>
|
samer@0
|
43 */
|
samer@0
|
44 public Environment(Environment parent, String name) {
|
samer@0
|
45 this(parent,new Node(name,parent.base));
|
samer@0
|
46 }
|
samer@0
|
47
|
samer@0
|
48 /** Return this environment's parent, or null of top level */
|
samer@0
|
49 public final Environment parent() { return parent; }
|
samer@0
|
50
|
samer@0
|
51 /** Return this environment's associated node */
|
samer@0
|
52 public final Node node() { return base; }
|
samer@0
|
53
|
samer@0
|
54 /*
|
samer@0
|
55 * these methods form the main implementation of the Environment and
|
samer@0
|
56 * are meant to be overridden by subclasses.
|
samer@0
|
57 */
|
samer@0
|
58
|
samer@0
|
59 /** Add and return a new named binding */
|
samer@0
|
60 public Binding add(String nm, Object o) { return parent.add(abs(nm),o); }
|
samer@0
|
61
|
samer@0
|
62 /** Add anonymous datum */
|
samer@0
|
63 public void add(Object obj) { parent.add(obj); }
|
samer@0
|
64
|
samer@0
|
65 public void store(String nm, Autocoder o) { parent.store(abs(nm),o); }
|
samer@0
|
66 public void store(String nm, Object o, Codec c) { parent.store(abs(nm),o,c); }
|
samer@0
|
67
|
samer@0
|
68 /** Return next anonymous datum */
|
samer@0
|
69 public Datum datum() { return parent.datum(); }
|
samer@0
|
70
|
samer@0
|
71 /** Return Datum that best matches given name */
|
samer@0
|
72 public Datum datum(String name) { return parent.datum(abs(name)); }
|
samer@0
|
73
|
samer@0
|
74 /** Return Binding that best matches given name */
|
samer@0
|
75 public Binding binding(String name) { return parent.binding(abs(name)); }
|
samer@0
|
76
|
samer@0
|
77 /** Return an Iterator that will step through all data in this
|
samer@0
|
78 Environment and it's ancestors */
|
samer@0
|
79 public Iterator data() { return parent.data(); }
|
samer@0
|
80
|
samer@0
|
81 // ... Interfaces ....................................................
|
samer@0
|
82
|
samer@0
|
83 /**
|
samer@0
|
84 Basic interface for getting at a particular stored object or
|
samer@0
|
85 value.
|
samer@0
|
86 There is no concept of returning the value as-is: it must
|
samer@0
|
87 be translated in to an object of some desired class.
|
samer@0
|
88 Value can be extracted in one of two ways:
|
samer@0
|
89 a Codec, which is an object which knows how to convert
|
samer@0
|
90 between stored data and some desired class of object.
|
samer@0
|
91 The other way is via an Autocoder, which is basically
|
samer@0
|
92 an object with it's own Codec built in.
|
samer@0
|
93 */
|
samer@0
|
94
|
samer@0
|
95 public interface Datum {
|
samer@0
|
96
|
samer@0
|
97 /** the stored name of this Datum */
|
samer@0
|
98 String name();
|
samer@0
|
99
|
samer@0
|
100 /** goodness of name matching procedure used to find this Datum */
|
samer@0
|
101 int score();
|
samer@0
|
102
|
samer@0
|
103 // Object get() throws Exception; // return intrinsic class ??
|
samer@0
|
104 // Object get(Codec c) throws Exception; // no default supplied
|
samer@0
|
105
|
samer@0
|
106 /** return object as decoded by Codec c. If this Datum is a
|
samer@0
|
107 null Datum, ie contains no data, return def */
|
samer@0
|
108 Object get(Codec c, Object def); // ?? copy semantics? exception?
|
samer@0
|
109
|
samer@0
|
110 /** decode value in to Autocoder obj */
|
samer@0
|
111 void get(Autocoder obj) throws Exception;
|
samer@0
|
112
|
samer@0
|
113 }
|
samer@0
|
114
|
samer@0
|
115 /**
|
samer@0
|
116 A Binding is a Datum in which the value is an accessible Object,
|
samer@0
|
117 ie one that doesn't need decoding. Unlike a Datum, this value
|
samer@0
|
118 can be set or removed from the Environment.
|
samer@0
|
119 */
|
samer@0
|
120 public interface Binding extends Datum {
|
samer@0
|
121 Object get() throws Exception;
|
samer@0
|
122 void set(Object obj);
|
samer@0
|
123 void remove();
|
samer@0
|
124 }
|
samer@0
|
125
|
samer@0
|
126 /**
|
samer@0
|
127 A null Binding that has no data.
|
samer@0
|
128 All attempts to read value throw an Exception, except get()
|
samer@0
|
129 with a default supplied.
|
samer@0
|
130 */
|
samer@0
|
131 public final static Binding Null = new Binding() {
|
samer@0
|
132 public String name() { return "null"; }
|
samer@0
|
133 public int score() { return 1000000000; } // Mr. Billion
|
samer@0
|
134
|
samer@0
|
135 public Object get() throws Exception { throw new Exception(); }
|
samer@0
|
136 public Object get(Codec c, Object def) { return def==null ? def : c.decode(def); } // ??
|
samer@0
|
137 // public void get(Autocoder obj) throws Exception { throw new Exception(); }
|
samer@0
|
138 public void get(Autocoder obj) {}
|
samer@0
|
139
|
samer@0
|
140 public void set(Object obj) { throw new RuntimeException(); }
|
samer@0
|
141 public void remove() { throw new RuntimeException(); }
|
samer@0
|
142 };
|
samer@0
|
143
|
samer@0
|
144 /** This is an object which knows how to code and decode itself. */
|
samer@0
|
145 public interface Autocoder
|
samer@0
|
146 {
|
samer@0
|
147 /** return this object as a string */
|
samer@0
|
148 String string(); // as string
|
samer@0
|
149
|
samer@0
|
150 /** return this object as any object of a convenient class.
|
samer@0
|
151 Strictly speaking, should return an <i>immutable</i>
|
samer@0
|
152 representation of the object.
|
samer@0
|
153 */
|
samer@0
|
154 Object object();
|
samer@0
|
155
|
samer@0
|
156 /** set this object by decoding supplied object, which may be
|
samer@0
|
157 a string. This method can be as smart as you like, behaving
|
samer@0
|
158 differently for different classes of supplied object.
|
samer@0
|
159 */
|
samer@0
|
160 void decode(Object o);
|
samer@0
|
161 }
|
samer@0
|
162
|
samer@0
|
163 /** This is an object which knows how to code and decode on behalf
|
samer@0
|
164 of some other class which represents the actual data. */
|
samer@0
|
165 public interface Codec {
|
samer@0
|
166 /** return Class of object that this Codec deals with */
|
samer@0
|
167 Class targetClass();
|
samer@0
|
168
|
samer@0
|
169 // methods for encoding object for storage
|
samer@0
|
170 String string(Object o); // convert object to string
|
samer@0
|
171 Object object(Object o); // convert object to another object
|
samer@0
|
172 Object decode(Object o); // convert other object to target class
|
samer@0
|
173 }
|
samer@0
|
174
|
samer@0
|
175 /** For iterating through data in environment,
|
samer@0
|
176 returning a Datum for each item
|
samer@0
|
177 */
|
samer@0
|
178 public interface Iterator {
|
samer@0
|
179 boolean hasNext();
|
samer@0
|
180 Datum next();
|
samer@0
|
181 }
|
samer@0
|
182
|
samer@0
|
183 /** Steps through two iterators consecutively */
|
samer@0
|
184 protected static class CompoundIterator implements Iterator {
|
samer@0
|
185 Iterator i1, i2;
|
samer@0
|
186 public CompoundIterator(Iterator a, Iterator b) { i1=a; i2=b; }
|
samer@0
|
187 public boolean hasNext() {
|
samer@0
|
188 if (i1.hasNext()) return true;
|
samer@0
|
189 if (i2==null) return false;
|
samer@0
|
190 i1=i2; i2=null; return i1.hasNext();
|
samer@0
|
191 }
|
samer@0
|
192 public Datum next() {
|
samer@0
|
193 if (i1.hasNext()) return i1.next();
|
samer@0
|
194 i1=i2; i2=null; return i1.next();
|
samer@0
|
195 }
|
samer@0
|
196 }
|
samer@0
|
197
|
samer@0
|
198 // ...................................................................
|
samer@0
|
199
|
samer@0
|
200 /** Return absolute node path for given name. If name begins
|
samer@0
|
201 with '.', then it is already an absolute name. Otherwise, the
|
samer@0
|
202 base node for this Environment is prepended.
|
samer@0
|
203 */
|
samer@0
|
204 public final String abs(String name) {
|
samer@0
|
205 return Node.isAbsolute(name) ? name : base.fullNameFor(name);
|
samer@0
|
206 }
|
samer@0
|
207
|
samer@0
|
208 /** Return node name relative to this Environment's node.
|
samer@0
|
209 Throws an Exception if the supplied name is absolute and
|
samer@0
|
210 NOT a child of this Environment's node.
|
samer@0
|
211 */
|
samer@0
|
212 public final String rel(String name) {
|
samer@0
|
213 if (!Node.isAbsolute(name)) return name;
|
samer@0
|
214 String fullbase=base.fullName();
|
samer@0
|
215 if (!name.startsWith(fullbase)) throw new Error("nonlocal node name:" + name);
|
samer@0
|
216 return name.substring(fullbase.length()+1); // miss out dot!
|
samer@0
|
217 }
|
samer@0
|
218
|
samer@0
|
219 /** Returns true if the given name is a subnode of this environment's node,
|
samer@0
|
220 ie, if it can belong in this environment
|
samer@0
|
221 */
|
samer@0
|
222 protected boolean belongs(String name) { return base.isSubnode(name); }
|
samer@0
|
223
|
samer@0
|
224 /** Return a top level environment */
|
samer@0
|
225 public static Environment top() {
|
samer@0
|
226 return new Environment(null,Node.root()) {
|
samer@0
|
227 public Binding add(String n, Object o) { throw error(); }
|
samer@0
|
228 public void add(Object o) { throw error(); }
|
samer@0
|
229 public void store(String nm, Object o, Codec c) { throw error(); }
|
samer@0
|
230 public void store(String nm, Autocoder o) { throw error(); }
|
samer@0
|
231
|
samer@0
|
232 public Datum datum() { return Null; }
|
samer@0
|
233 public Datum datum(String name) { return Null; }
|
samer@0
|
234 public Binding binding(String name) { return Null; }
|
samer@0
|
235
|
samer@0
|
236 public Iterator data() {
|
samer@0
|
237 return new Iterator() {
|
samer@0
|
238 public boolean hasNext() { return false; }
|
samer@0
|
239 public Datum next() { return Null; }
|
samer@0
|
240 };
|
samer@0
|
241 }
|
samer@0
|
242
|
samer@0
|
243 RuntimeException error() { return new RuntimeException("Environment.top"); }
|
samer@0
|
244 };
|
samer@0
|
245 }
|
samer@0
|
246 }
|
samer@0
|
247
|