001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.xbean.naming.context;
018    
019    import java.util.Collections;
020    import java.util.HashMap;
021    import java.util.Map;
022    
023    import javax.naming.Context;
024    import javax.naming.Name;
025    import javax.naming.NamingException;
026    import javax.naming.OperationNotSupportedException;
027    
028    import org.apache.xbean.naming.reference.CachingReference;
029    
030    /**
031     *
032     * @version $Rev: 417891 $ $Date: 2006-06-28 15:45:07 -0700 (Wed, 28 Jun 2006) $
033     */
034    public class ImmutableContext extends AbstractContext {
035        private final Map<String, Object> localBindings;
036        private final Map<String, Object> absoluteIndex;
037    
038        public ImmutableContext(Map<String, Object> bindings) throws NamingException {
039            this("", bindings, true);
040        }
041    
042        public ImmutableContext(Map<String, Object> bindings, boolean cacheReferences) throws NamingException {
043            this("", bindings, cacheReferences);
044        }
045    
046        public ImmutableContext(String nameInNamespace, Map<String, Object> bindings, boolean cacheReferences) throws NamingException {
047            super(nameInNamespace, ContextAccess.UNMODIFIABLE);
048    
049            if (cacheReferences) {
050                bindings = CachingReference.wrapReferences(bindings, this);
051            }
052    
053            Map<String, Object> localBindings = ContextUtil.createBindings(bindings, this);
054            this.localBindings = Collections.unmodifiableMap(localBindings);
055    
056            Map<String, Object> globalBindings = ImmutableContext.buildAbsoluteIndex("", localBindings);
057            this.absoluteIndex = Collections.unmodifiableMap(globalBindings);
058        }
059    
060        private static Map<String, Object> buildAbsoluteIndex(String nameInNamespace, Map<String, Object> bindings) {
061            String path = nameInNamespace;
062            if (path.length() > 0) {
063                path += "/";
064            }
065    
066            Map<String, Object> globalBindings = new HashMap<String, Object>();
067            for (Map.Entry<String, Object> entry : bindings.entrySet()) {
068                String name = entry.getKey();
069                Object value = entry.getValue();
070                if (value instanceof NestedImmutableContext) {
071                    NestedImmutableContext nestedContext = (NestedImmutableContext) value;
072                    globalBindings.putAll(ImmutableContext.buildAbsoluteIndex(nestedContext.getNameInNamespace(), nestedContext.localBindings));
073                }
074                globalBindings.put(path + name, value);
075            }
076            return globalBindings;
077        }
078    
079        protected Object getDeepBinding(String name) {
080            return absoluteIndex.get(name);
081        }
082    
083        protected Map<String, Object> getBindings() {
084            return localBindings;
085        }
086    
087        protected final void addDeepBinding(String name, Object value, boolean createIntermediateContexts) throws NamingException {
088            throw new OperationNotSupportedException("Context is immutable");
089        }
090    
091        protected final boolean addBinding(String name, Object value, boolean rebind) throws NamingException {
092            throw new OperationNotSupportedException("Context is immutable");
093        }
094    
095        protected final void removeDeepBinding(Name name, boolean pruneEmptyContexts) throws NamingException {
096            throw new OperationNotSupportedException("Context is immutable");
097        }
098    
099        protected final boolean removeBinding(String name, boolean removeNotEmptyContext) throws NamingException {
100            throw new OperationNotSupportedException("Context is immutable");
101        }
102    
103        public boolean isNestedSubcontext(Object value) {
104            if (value instanceof NestedImmutableContext) {
105                NestedImmutableContext context = (NestedImmutableContext) value;
106                return this == context.getImmutableContext();
107            }
108            return false;
109        }
110    
111        public Context createNestedSubcontext(String path, Map bindings) {
112            return new NestedImmutableContext(path, bindings);
113        }
114    
115        /**
116         * Nested context which shares the absolute index map in MapContext.
117         */
118        public final class NestedImmutableContext extends AbstractContext {
119            private final Map<String, Object> localBindings;
120            private final String pathWithSlash;
121    
122            public NestedImmutableContext(String path, Map<String, Object> bindings) {
123                super(ImmutableContext.this.getNameInNamespace(path), ContextAccess.UNMODIFIABLE);
124    
125                path = getNameInNamespace();
126                if (!path.endsWith("/")) path += "/";
127                this.pathWithSlash = path;
128    
129                this.localBindings = Collections.unmodifiableMap(bindings);
130            }
131    
132            protected Object getDeepBinding(String name) {
133                String absoluteName = pathWithSlash + name;
134                return absoluteIndex.get(absoluteName);
135            }
136    
137            protected Map<String, Object> getBindings() {
138                return localBindings;
139            }
140    
141            protected final void addDeepBinding(String name, Object value, boolean createIntermediateContexts) throws NamingException {
142                throw new OperationNotSupportedException("Context is immutable");
143            }
144    
145            protected final boolean addBinding(String name, Object value, boolean rebind) throws NamingException {
146                throw new OperationNotSupportedException("Context is immutable");
147            }
148    
149            protected final void removeDeepBinding(Name name, boolean pruneEmptyContexts) throws NamingException {
150                throw new OperationNotSupportedException("Context is immutable");
151            }
152    
153            protected final boolean removeBinding(String name, boolean removeNotEmptyContext) throws NamingException {
154                throw new OperationNotSupportedException("Context is immutable");
155            }
156    
157            public boolean isNestedSubcontext(Object value) {
158                if (value instanceof NestedImmutableContext) {
159                    NestedImmutableContext context = (NestedImmutableContext) value;
160                    return getImmutableContext() == context.getImmutableContext();
161                }
162                return false;
163            }
164    
165            public Context createNestedSubcontext(String path, Map<String, Object> bindings) {
166                return new NestedImmutableContext(path, bindings);
167            }
168    
169            protected ImmutableContext getImmutableContext() {
170                return ImmutableContext.this;
171            }
172        }
173    }