View Javadoc

1   /*
2    * Copyright (C) 2004 TiongHiang Lee
3    *
4    * This library is free software; you can redistribute it and/or
5    * modify it under the terms of the GNU Lesser General Public
6    * License as published by the Free Software Foundation; either
7    * version 2.1 of the License, or (at your option) any later version.
8    *
9    * This library is distributed in the hope that it will be useful,
10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12   * Lesser General Public License for more details.
13   *
14   * You should have received a copy of the GNU Lesser General Public
15   * License along with this library; if not,  write to the Free Software
16   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17   *
18   * Email: thlee@onemindsoft.org
19   */
20  
21  package org.onemind.commons.java.datastructure;
22  
23  import java.util.*;
24  import java.util.logging.Logger;
25  /***
26   * A nametable stack contains key-value mapping that has a scope. 
27   * A new scope can be opened for putting new mappings and all the
28   * mappings added in this scope can be wiped out easily with a 
29   * closeScope command. In effect this is like a stack of Maps, 
30   * hence the name NametableStack. 
31   * 
32   * NOTE: the implementation use a map and list to achieve the behaviour.
33   * @author TiongHiang Lee (thlee@onemindsoft.org)
34   * @version $Id: NametableStack.java,v 1.7 2005/06/22 22:57:37 thlee Exp $ $Name:  $
35   */
36  public class NametableStack
37  {
38  
39      /*** the logger * */
40      private static final Logger _logger = Logger.getLogger(NametableStack.class.getName());
41  
42      /*** the map * */
43      private Nametable _nametable;
44  
45      /*** the list of maps * */
46      private ArrayList _list = new ArrayList();
47  
48      /***
49       * The local nametable defines a scope where local variables mask out the global
50       * variables, but the global variable can still be accessed. This is useful for 
51       * implementing function context
52       * 
53       * @author TiongHiang Lee (thlee@onemindsoft.org)
54       */
55      private static class LocalNametable implements Nametable
56      {
57  
58          /*** the local variables **/
59          private final Map _locals = new HashMap();
60  
61          /*** the global map **/
62          private final Nametable _global;
63  
64          /*** the scope **/
65          private final int _scope;
66  
67          /***
68           * Constructor
69           * @param global the global
70           * @param scope the scope # where this local nametabe is openned
71           */
72          private LocalNametable(Nametable global, int scope)
73          {
74              _global = global;
75              _scope = scope;
76          }
77  
78          /***
79           * Get the global
80           * @return the global
81           */
82          private Nametable getGlobal()
83          {
84              return _global;
85          }
86  
87          /*** 
88           * {@inheritDoc}
89           */
90          public boolean containsName(String key)
91          {
92              return _locals.containsKey(key) || _global.containsName(key);
93          }
94  
95          /*** 
96           * {@inheritDoc}
97           */
98          public void declare(String name, Object value)
99          {
100             _locals.put(name, value);
101         }
102 
103         /*** 
104          * {@inheritDoc}
105          */
106         public Object assign(String name, Object value)
107         {
108             if (_locals.containsKey(name))
109             {
110                 return _locals.put(name, value);
111             } else
112             {
113                 //TODO: determine whether to allow local scope to reassign global
114                 return _global.assign(name, value);
115             }
116         }
117 
118         /*** 
119          * {@inheritDoc}
120          */
121         public Object access(String name)
122         {
123             if (_locals.containsKey(name))
124             {
125                 return _locals.get(name);
126             } else
127             {
128                 return _global.access(name);
129             }
130         }
131 
132         /*** 
133          * {@inheritDoc}
134          */
135         public void undeclare(String name)
136         {          
137             if (_locals.containsKey(name))
138             {
139                 _locals.remove(name);
140             } else
141             {
142                 //TODO: determine whether to allow local scope to undeclare global  
143                 _global.undeclare(name);
144             }
145         }
146         
147         public String toString(){
148             StringBuffer sb = new StringBuffer();
149             sb.append("Scope=");
150             sb.append(_scope);
151             sb.append("\n");
152             sb.append("Locals=" + _locals + "\n");
153             sb.append("Global=" + _global + "\n");
154             return sb.toString();
155         }
156         
157         public Map asMap(){
158             Map m = new HashMap(_global.asMap());
159             m.putAll(_locals);
160             return Collections.unmodifiableMap(m);
161         }
162     }
163 
164     /***
165      * {@inheritDoc}
166      */
167     public NametableStack()
168     {
169         this(new HashMap());
170     }
171 
172     /***
173      * {@inheritDoc}
174      * @param m the initial mapping
175      */
176     public NametableStack(Map m)
177     {
178         _nametable = new SimpleNametable(m);
179     }
180 
181     /***
182      * Open a new scope for mappings. Return an integer that represents a scope id
183      * @return the new scope
184      */
185     public int newScope()
186     {
187         return _list.size();
188     }
189 
190     public int newLocalScope()
191     {
192         _nametable = new LocalNametable(_nametable, _list.size());
193         return _list.size();
194     }
195 
196     public void closeLocalScope(int i)
197     {
198         if (_nametable instanceof LocalNametable)
199         {
200             LocalNametable nt = (LocalNametable) _nametable;
201             if (nt._scope != i)
202             {
203                 throw new IllegalArgumentException("Local scope " + i + " not matched");
204             } else
205             {
206                 for (int k = _list.size(); k > i; k--)
207                 {
208                     _list.remove(k-1);
209                 }
210                 _nametable = nt.getGlobal();
211             }
212         } else
213         {
214             throw new IllegalStateException("Cannot find scope " + i);
215         }
216     }
217 
218     /***
219      * Close a scope
220      * @param l the scope id
221      */
222     public void closeScope(int l)
223     {
224         if (_nametable instanceof LocalNametable)
225         {
226             if (l < ((LocalNametable) _nametable)._scope)
227             {
228                 throw new IllegalStateException("Encounter unclosed local scope");
229             }
230         }
231         int n = _list.size();
232         if (l > n)
233         {
234             throw new IllegalArgumentException("The scope has been closed");
235         } else if (l < n)
236         {
237             int diff = n - l;
238             for (int i = 0; i < diff; i++)
239             {
240                 _nametable.undeclare((String)_list.remove(n - i - 1));
241             }
242         }
243     }
244 
245     /***
246      * Declare name value pair
247      * @param name
248      * @param value
249      * @return
250      */
251     public void declare(String name, Object value)
252     {
253         _nametable.declare(name, value);
254         _list.add(name);
255     }
256 
257     /***
258      * Assign name/value pair
259      * @param name
260      * @param value
261      * @return 
262      */
263     public Object assign(String name, Object value)
264     {
265         return _nametable.assign(name, value);
266     }
267 
268     /***
269      * Resolve the value associated with key name
270      * @param name the key
271      * @return the value associated with key
272      */
273     public Object access(String name)
274     {
275         return _nametable.access(name);
276     }
277 
278     /***
279      * Whether the map contains key name
280      * @param name the key
281      * @return true if map contains key name
282      */
283     public boolean containsName(String name)
284     {
285         return _nametable.containsName(name);
286     }
287     
288     /***
289      * Return map representation of the nametable stack
290      * @return the map
291      */
292     public Map asMap()
293     {
294         return _nametable.asMap();
295     }
296 
297     /***
298      * {@inheritDoc}
299      */
300     public String toString()
301     {
302         return _nametable.toString();
303     }    
304 }