/** * Copyright (c) Rich Hickey. All rights reserved. * The use and distribution terms for this software are covered by the * Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) * which can be found in the file epl-v10.html at the root of this distribution. * By using this software in any fashion, you are agreeing to be bound by * the terms of this license. * You must not remove this notice, or any other, from this software. **/ /* rich Jan 23, 2008 */ package clojure.lang; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicReference; public class Namespace extends AReference{ final public Symbol name; final AtomicReference<IPersistentMap> mappings = new AtomicReference<IPersistentMap>(); final AtomicReference<IPersistentMap> aliases = new AtomicReference<IPersistentMap>(); final static ConcurrentHashMap<Symbol, Namespace> namespaces = new ConcurrentHashMap<Symbol, Namespace>(); public String toString(){ return name.toString(); } Namespace(Symbol name){ super(name.meta()); this.name = name; mappings.set(RT.DEFAULT_IMPORTS); aliases.set(RT.map()); } public static ISeq all(){ return RT.seq(namespaces.values()); } public Symbol getName(){ return name; } public IPersistentMap getMappings(){ return mappings.get(); } public Var intern(Symbol sym){ if(sym.ns != null) { throw new IllegalArgumentException("Can't intern namespace-qualified symbol"); } IPersistentMap map = getMappings(); Object o; Var v = null; while((o = map.valAt(sym)) == null) { if(v == null) v = new Var(this, sym); IPersistentMap newMap = map.assoc(sym, v); mappings.compareAndSet(map, newMap); map = getMappings(); } if(o instanceof Var && ((Var) o).ns == this) return (Var) o; throw new IllegalStateException(sym + " already refers to: " + o + " in namespace: " + name); } Object reference(Symbol sym, Object val){ if(sym.ns != null) { throw new IllegalArgumentException("Can't intern namespace-qualified symbol"); } IPersistentMap map = getMappings(); Object o; while((o = map.valAt(sym)) == null) { IPersistentMap newMap = map.assoc(sym, val); mappings.compareAndSet(map, newMap); map = getMappings(); } if(o == val) return o; throw new IllegalStateException(sym + " already refers to: " + o + " in namespace: " + name); } public void unmap(Symbol sym) throws Exception{ if(sym.ns != null) { throw new IllegalArgumentException("Can't unintern namespace-qualified symbol"); } IPersistentMap map = getMappings(); while(map.containsKey(sym)) { IPersistentMap newMap = map.without(sym); mappings.compareAndSet(map, newMap); map = getMappings(); } } public Class importClass(Symbol sym, Class c){ return (Class) reference(sym, c); } public Class importClass(Class c){ String n = c.getName(); return importClass(Symbol.intern(n.substring(n.lastIndexOf('.') + 1)), c); } public Var refer(Symbol sym, Var var){ return (Var) reference(sym, var); } public static Namespace findOrCreate(Symbol name){ Namespace ns = namespaces.get(name); if(ns != null) return ns; Namespace newns = new Namespace(name); ns = namespaces.putIfAbsent(name, newns); return ns == null ? newns : ns; } public static Namespace remove(Symbol name){ if(name.equals(RT.CLOJURE_NS.name)) throw new IllegalArgumentException("Cannot remove clojure namespace"); return namespaces.remove(name); } public static Namespace find(Symbol name){ return namespaces.get(name); } public Object getMapping(Symbol name){ return mappings.get().valAt(name); } public Var findInternedVar(Symbol symbol){ Object o = mappings.get().valAt(symbol); if(o != null && o instanceof Var && ((Var) o).ns == this) return (Var) o; return null; } public IPersistentMap getAliases(){ return aliases.get(); } public Namespace lookupAlias(Symbol alias){ IPersistentMap map = getAliases(); return (Namespace) map.valAt(alias); } public void addAlias(Symbol alias, Namespace ns){ if (alias == null || ns == null) throw new NullPointerException("Expecting Symbol + Namespace"); IPersistentMap map = getAliases(); while(!map.containsKey(alias)) { IPersistentMap newMap = map.assoc(alias, ns); aliases.compareAndSet(map, newMap); map = getAliases(); } // you can rebind an alias, but only to the initially-aliased namespace. if(!map.valAt(alias).equals(ns)) throw new IllegalStateException("Alias " + alias + " already exists in namespace " + name + ", aliasing " + map.valAt(alias)); } public void removeAlias(Symbol alias) throws Exception{ IPersistentMap map = getAliases(); while(map.containsKey(alias)) { IPersistentMap newMap = map.without(alias); aliases.compareAndSet(map, newMap); map = getAliases(); } } }