rlm@10
|
1 /**
|
rlm@10
|
2 * Copyright (c) Rich Hickey. All rights reserved.
|
rlm@10
|
3 * The use and distribution terms for this software are covered by the
|
rlm@10
|
4 * Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
|
rlm@10
|
5 * which can be found in the file epl-v10.html at the root of this distribution.
|
rlm@10
|
6 * By using this software in any fashion, you are agreeing to be bound by
|
rlm@10
|
7 * the terms of this license.
|
rlm@10
|
8 * You must not remove this notice, or any other, from this software.
|
rlm@10
|
9 **/
|
rlm@10
|
10
|
rlm@10
|
11 /* rich Jan 23, 2008 */
|
rlm@10
|
12
|
rlm@10
|
13 package clojure.lang;
|
rlm@10
|
14
|
rlm@10
|
15 import java.io.ObjectStreamException;
|
rlm@10
|
16 import java.io.Serializable;
|
rlm@10
|
17 import java.util.concurrent.ConcurrentHashMap;
|
rlm@10
|
18 import java.util.concurrent.atomic.AtomicReference;
|
rlm@10
|
19
|
rlm@10
|
20 public class Namespace extends AReference implements Serializable {
|
rlm@10
|
21 final public Symbol name;
|
rlm@10
|
22 transient final AtomicReference<IPersistentMap> mappings = new AtomicReference<IPersistentMap>();
|
rlm@10
|
23 transient final AtomicReference<IPersistentMap> aliases = new AtomicReference<IPersistentMap>();
|
rlm@10
|
24
|
rlm@10
|
25 final static ConcurrentHashMap<Symbol, Namespace> namespaces = new ConcurrentHashMap<Symbol, Namespace>();
|
rlm@10
|
26
|
rlm@10
|
27 public String toString(){
|
rlm@10
|
28 return name.toString();
|
rlm@10
|
29 }
|
rlm@10
|
30
|
rlm@10
|
31 Namespace(Symbol name){
|
rlm@10
|
32 super(name.meta());
|
rlm@10
|
33 this.name = name;
|
rlm@10
|
34 mappings.set(RT.DEFAULT_IMPORTS);
|
rlm@10
|
35 aliases.set(RT.map());
|
rlm@10
|
36 }
|
rlm@10
|
37
|
rlm@10
|
38 public static ISeq all(){
|
rlm@10
|
39 return RT.seq(namespaces.values());
|
rlm@10
|
40 }
|
rlm@10
|
41
|
rlm@10
|
42 public Symbol getName(){
|
rlm@10
|
43 return name;
|
rlm@10
|
44 }
|
rlm@10
|
45
|
rlm@10
|
46 public IPersistentMap getMappings(){
|
rlm@10
|
47 return mappings.get();
|
rlm@10
|
48 }
|
rlm@10
|
49
|
rlm@10
|
50 public Var intern(Symbol sym){
|
rlm@10
|
51 if(sym.ns != null)
|
rlm@10
|
52 {
|
rlm@10
|
53 throw new IllegalArgumentException("Can't intern namespace-qualified symbol");
|
rlm@10
|
54 }
|
rlm@10
|
55 IPersistentMap map = getMappings();
|
rlm@10
|
56 Object o;
|
rlm@10
|
57 Var v = null;
|
rlm@10
|
58 while((o = map.valAt(sym)) == null)
|
rlm@10
|
59 {
|
rlm@10
|
60 if(v == null)
|
rlm@10
|
61 v = new Var(this, sym);
|
rlm@10
|
62 IPersistentMap newMap = map.assoc(sym, v);
|
rlm@10
|
63 mappings.compareAndSet(map, newMap);
|
rlm@10
|
64 map = getMappings();
|
rlm@10
|
65 }
|
rlm@10
|
66 if(o instanceof Var && ((Var) o).ns == this)
|
rlm@10
|
67 return (Var) o;
|
rlm@10
|
68
|
rlm@10
|
69 if(v == null)
|
rlm@10
|
70 v = new Var(this, sym);
|
rlm@10
|
71
|
rlm@10
|
72 warnOrFailOnReplace(sym, o, v);
|
rlm@10
|
73
|
rlm@10
|
74
|
rlm@10
|
75 while(!mappings.compareAndSet(map, map.assoc(sym, v)))
|
rlm@10
|
76 map = getMappings();
|
rlm@10
|
77
|
rlm@10
|
78 return v;
|
rlm@10
|
79 }
|
rlm@10
|
80
|
rlm@10
|
81 private void warnOrFailOnReplace(Symbol sym, Object o, Object v){
|
rlm@10
|
82 if (o instanceof Var)
|
rlm@10
|
83 {
|
rlm@10
|
84 Namespace ns = ((Var)o).ns;
|
rlm@10
|
85 if (ns == this)
|
rlm@10
|
86 return;
|
rlm@10
|
87 if (ns != RT.CLOJURE_NS)
|
rlm@10
|
88 throw new IllegalStateException(sym + " already refers to: " + o + " in namespace: " + name);
|
rlm@10
|
89 }
|
rlm@10
|
90 RT.errPrintWriter().println("WARNING: " + sym + " already refers to: " + o + " in namespace: " + name
|
rlm@10
|
91 + ", being replaced by: " + v);
|
rlm@10
|
92 }
|
rlm@10
|
93
|
rlm@10
|
94 Object reference(Symbol sym, Object val){
|
rlm@10
|
95 if(sym.ns != null)
|
rlm@10
|
96 {
|
rlm@10
|
97 throw new IllegalArgumentException("Can't intern namespace-qualified symbol");
|
rlm@10
|
98 }
|
rlm@10
|
99 IPersistentMap map = getMappings();
|
rlm@10
|
100 Object o;
|
rlm@10
|
101 while((o = map.valAt(sym)) == null)
|
rlm@10
|
102 {
|
rlm@10
|
103 IPersistentMap newMap = map.assoc(sym, val);
|
rlm@10
|
104 mappings.compareAndSet(map, newMap);
|
rlm@10
|
105 map = getMappings();
|
rlm@10
|
106 }
|
rlm@10
|
107 if(o == val)
|
rlm@10
|
108 return o;
|
rlm@10
|
109
|
rlm@10
|
110 warnOrFailOnReplace(sym, o, val);
|
rlm@10
|
111
|
rlm@10
|
112 while(!mappings.compareAndSet(map, map.assoc(sym, val)))
|
rlm@10
|
113 map = getMappings();
|
rlm@10
|
114
|
rlm@10
|
115 return val;
|
rlm@10
|
116
|
rlm@10
|
117 }
|
rlm@10
|
118
|
rlm@10
|
119 public static boolean areDifferentInstancesOfSameClassName(Class cls1, Class cls2) {
|
rlm@10
|
120 return (cls1 != cls2) && (cls1.getName().equals(cls2.getName()));
|
rlm@10
|
121 }
|
rlm@10
|
122
|
rlm@10
|
123 Class referenceClass(Symbol sym, Class val){
|
rlm@10
|
124 if(sym.ns != null)
|
rlm@10
|
125 {
|
rlm@10
|
126 throw new IllegalArgumentException("Can't intern namespace-qualified symbol");
|
rlm@10
|
127 }
|
rlm@10
|
128 IPersistentMap map = getMappings();
|
rlm@10
|
129 Class c = (Class) map.valAt(sym);
|
rlm@10
|
130 while((c == null) || (areDifferentInstancesOfSameClassName(c, val)))
|
rlm@10
|
131 {
|
rlm@10
|
132 IPersistentMap newMap = map.assoc(sym, val);
|
rlm@10
|
133 mappings.compareAndSet(map, newMap);
|
rlm@10
|
134 map = getMappings();
|
rlm@10
|
135 c = (Class) map.valAt(sym);
|
rlm@10
|
136 }
|
rlm@10
|
137 if(c == val)
|
rlm@10
|
138 return c;
|
rlm@10
|
139
|
rlm@10
|
140 throw new IllegalStateException(sym + " already refers to: " + c + " in namespace: " + name);
|
rlm@10
|
141 }
|
rlm@10
|
142
|
rlm@10
|
143 public void unmap(Symbol sym) throws Exception{
|
rlm@10
|
144 if(sym.ns != null)
|
rlm@10
|
145 {
|
rlm@10
|
146 throw new IllegalArgumentException("Can't unintern namespace-qualified symbol");
|
rlm@10
|
147 }
|
rlm@10
|
148 IPersistentMap map = getMappings();
|
rlm@10
|
149 while(map.containsKey(sym))
|
rlm@10
|
150 {
|
rlm@10
|
151 IPersistentMap newMap = map.without(sym);
|
rlm@10
|
152 mappings.compareAndSet(map, newMap);
|
rlm@10
|
153 map = getMappings();
|
rlm@10
|
154 }
|
rlm@10
|
155 }
|
rlm@10
|
156
|
rlm@10
|
157 public Class importClass(Symbol sym, Class c){
|
rlm@10
|
158 return referenceClass(sym, c);
|
rlm@10
|
159
|
rlm@10
|
160 }
|
rlm@10
|
161
|
rlm@10
|
162 public Class importClass(Class c){
|
rlm@10
|
163 String n = c.getName();
|
rlm@10
|
164 return importClass(Symbol.intern(n.substring(n.lastIndexOf('.') + 1)), c);
|
rlm@10
|
165 }
|
rlm@10
|
166
|
rlm@10
|
167 public Var refer(Symbol sym, Var var){
|
rlm@10
|
168 return (Var) reference(sym, var);
|
rlm@10
|
169
|
rlm@10
|
170 }
|
rlm@10
|
171
|
rlm@10
|
172 public static Namespace findOrCreate(Symbol name){
|
rlm@10
|
173 Namespace ns = namespaces.get(name);
|
rlm@10
|
174 if(ns != null)
|
rlm@10
|
175 return ns;
|
rlm@10
|
176 Namespace newns = new Namespace(name);
|
rlm@10
|
177 ns = namespaces.putIfAbsent(name, newns);
|
rlm@10
|
178 return ns == null ? newns : ns;
|
rlm@10
|
179 }
|
rlm@10
|
180
|
rlm@10
|
181 public static Namespace remove(Symbol name){
|
rlm@10
|
182 if(name.equals(RT.CLOJURE_NS.name))
|
rlm@10
|
183 throw new IllegalArgumentException("Cannot remove clojure namespace");
|
rlm@10
|
184 return namespaces.remove(name);
|
rlm@10
|
185 }
|
rlm@10
|
186
|
rlm@10
|
187 public static Namespace find(Symbol name){
|
rlm@10
|
188 return namespaces.get(name);
|
rlm@10
|
189 }
|
rlm@10
|
190
|
rlm@10
|
191 public Object getMapping(Symbol name){
|
rlm@10
|
192 return mappings.get().valAt(name);
|
rlm@10
|
193 }
|
rlm@10
|
194
|
rlm@10
|
195 public Var findInternedVar(Symbol symbol){
|
rlm@10
|
196 Object o = mappings.get().valAt(symbol);
|
rlm@10
|
197 if(o != null && o instanceof Var && ((Var) o).ns == this)
|
rlm@10
|
198 return (Var) o;
|
rlm@10
|
199 return null;
|
rlm@10
|
200 }
|
rlm@10
|
201
|
rlm@10
|
202
|
rlm@10
|
203 public IPersistentMap getAliases(){
|
rlm@10
|
204 return aliases.get();
|
rlm@10
|
205 }
|
rlm@10
|
206
|
rlm@10
|
207 public Namespace lookupAlias(Symbol alias){
|
rlm@10
|
208 IPersistentMap map = getAliases();
|
rlm@10
|
209 return (Namespace) map.valAt(alias);
|
rlm@10
|
210 }
|
rlm@10
|
211
|
rlm@10
|
212 public void addAlias(Symbol alias, Namespace ns){
|
rlm@10
|
213 if (alias == null || ns == null)
|
rlm@10
|
214 throw new NullPointerException("Expecting Symbol + Namespace");
|
rlm@10
|
215 IPersistentMap map = getAliases();
|
rlm@10
|
216 while(!map.containsKey(alias))
|
rlm@10
|
217 {
|
rlm@10
|
218 IPersistentMap newMap = map.assoc(alias, ns);
|
rlm@10
|
219 aliases.compareAndSet(map, newMap);
|
rlm@10
|
220 map = getAliases();
|
rlm@10
|
221 }
|
rlm@10
|
222 // you can rebind an alias, but only to the initially-aliased namespace.
|
rlm@10
|
223 if(!map.valAt(alias).equals(ns))
|
rlm@10
|
224 throw new IllegalStateException("Alias " + alias + " already exists in namespace "
|
rlm@10
|
225 + name + ", aliasing " + map.valAt(alias));
|
rlm@10
|
226 }
|
rlm@10
|
227
|
rlm@10
|
228 public void removeAlias(Symbol alias) throws Exception{
|
rlm@10
|
229 IPersistentMap map = getAliases();
|
rlm@10
|
230 while(map.containsKey(alias))
|
rlm@10
|
231 {
|
rlm@10
|
232 IPersistentMap newMap = map.without(alias);
|
rlm@10
|
233 aliases.compareAndSet(map, newMap);
|
rlm@10
|
234 map = getAliases();
|
rlm@10
|
235 }
|
rlm@10
|
236 }
|
rlm@10
|
237
|
rlm@10
|
238 private Object readResolve() throws ObjectStreamException {
|
rlm@10
|
239 // ensures that serialized namespaces are "deserialized" to the
|
rlm@10
|
240 // namespace in the present runtime
|
rlm@10
|
241 return findOrCreate(name);
|
rlm@10
|
242 }
|
rlm@10
|
243 }
|