001 
002 /*
003  *  Descripter 1.0 - Java Script Engines
004  *  Copyright (C) 2010-2015  Jianjun Liu (J.J.Liu)
005  *  
006  *  This program is free software: you can redistribute it and/or modify
007  *  it under the terms of the GNU Affero General Public License as published by
008  *  the Free Software Foundation, either version 3 of the License, or
009  *  (at your option) any later version.
010  *  
011  *  This program is distributed in the hope that it will be useful,
012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
014  *  GNU Affero General Public License for more details.
015  *  
016  *  You should have received a copy of the GNU Affero General Public License
017  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
018  */
019 
020 package org.descripter.js.api;
021 
022 import java.util.HashMap;
023 import java.util.HashSet;
024 import java.util.Set;
025 
026 /**
027  * <p>A base class for representing various related contexts in JavaScript.</p>
028  * 
029  * @author <a href="mailto:jianjunliu@126.com">J.J.Liu (Jianjun Liu)</a> at <a href="http://www.descripter.org" target="_blank">http://www.descripter.org</a>
030  * @since Descripter 1.0
031  */
032 public class Context<S, T, W extends Context<S, T, ?>>
033 {
034     private final HashMap<S, T> map = new HashMap<S, T>();
035     private boolean readOnly = false;
036     /**
037      * <p>The container of the current context.</p>
038      * @since Descripter 1.0
039      */
040     public final W with;
041 
042     /**
043      * <p>Constructs a context of this type.</p>
044      * @param with The containing context.
045      * @since Descripter 1.0
046      */
047     public Context(W with) {
048         this.with = with;
049     }
050 
051     /**
052      * <p>Tells if the current context is read-only.</p>
053      * @return <tt>true</tt> if the current context is read-only; <tt>false</tt>, otherwise.
054      * @since Descripter 1.0
055      */
056     public boolean readOnly() {
057         return readOnly;
058     }
059 
060     /**
061      * <p>Sets the current context to <tt>readOnly</tt>.</p>
062      * @param readOnly <tt>true</tt> to set the current context read-only; <tt>false</tt>, otherwise.
063      * @since Descripter 1.0
064      */
065     public void readOnly(boolean readOnly) {
066         if (this.readOnly ^ readOnly) {
067             this.readOnly = readOnly;
068             for (T t : map.values()) {
069                 if (t instanceof Context) {
070                     ((Context<?, ?, ?>)t).readOnly(readOnly);
071                 }
072             }
073         }
074     }
075 
076     /**
077      * <p>Finds the most inner context that owns the specified key.</p>
078      * @param key A key to lookup
079      * @return The most inner context that owns the specified key or <tt>null</tt> if no such containers.
080      * @since Descripter 1.0
081      */
082     public Context<S, T, ?> in(S key) {
083         if (map.containsKey(key) || with == null) {
084             return this;
085         } else {
086             return with.in(key);
087         }
088     }
089 
090     /**
091      * <p>Tells if a specified context is one of the containers of the current one.</p>
092      * @param ctx A context to test
093      * @return <tt>true</tt> if <tt>ctx</tt> contains the current context; <tt>false</tt>, otherwise.
094      * @since Descripter 1.0
095      */
096     public boolean with(Context<S, T, ?> ctx) {
097         return with != null && (with == ctx || with.with(ctx));
098     }
099 
100     /**
101      * <p>Tells if the current context owns the specified key.</p>
102      * @param key A key to test
103      * @return <tt>true</tt> if the current context owns the <tt>key</tt>; <tt>false</tt>, otherwise.
104      * @since Descripter 1.0
105      */
106     public boolean owns(S key) {
107         return map.containsKey(key) && map.get(key) != null;
108     }
109 
110     /**
111      * <p>Tells if the specified key is visible in the current context.</p>
112      * @param key A key to test
113      * @return <tt>true</tt> if the <tt>key</tt> is visible in the current context; <tt>false</tt>, otherwise.
114      * @since Descripter 1.0
115      */
116     public boolean has(S key) {
117         return map.containsKey(key) || with != null && with.has(key);
118     }
119 
120     /**
121      * <p>Returns the {@link Set} of keys that are visible to the current context.</p>
122      * @return The {@link Set} of keys that are visible to the current context.
123      * @since Descripter 1.0
124      */
125     public Set<S> keys() {
126         Set<S> keys = new HashSet<S>(map.keySet());
127         if (with != null) {
128             keys.addAll(with.keys());
129         }
130         return keys;
131     }
132 
133     /**
134      * <p>Returns the value associated with the specified key.</p>
135      * @param key A key to lookup
136      * @return The value associated with the specified key or <tt>null</tt> for none.
137      * @since Descripter 1.0
138      */
139     public T get(S key) {
140         if (!map.containsKey(key) && with != null) {
141             return with.get(key);
142         }
143         return map.get(key);
144     }
145 
146     /**
147      * <p>Updates the value associated with the specified key if it is visible.</p>
148      * <p>Note that this method does nothing if the key is not visible.</p>
149      * @param key A key to update
150      * @throws RuntimeException if the current context is read-only.
151      * @since Descripter 1.0
152      */
153     public void update(S key, T val) {
154         if (map.containsKey(key)) {
155             put(key, val);
156         } else if (with != null) {
157             with.update(key, val);
158         }
159     }
160 
161     /**
162      * <p>Hides the specified key from the current context if it is visible.</p>
163      * <p>Note that this method does nothing if the key is not visible.</p>
164      * @param key A key to remove
165      * @throws RuntimeException if the current context is read-only.
166      * @since Descripter 1.0
167      */
168     public void hide(S key) {
169         if (has(key)) {
170             put(key, null);
171         }
172     }
173 
174     /**
175      * <p>Sets the value associated with the specified key.</p>
176      * @param key A key to set
177      * @param val The value to set the value
178      * @throws RuntimeException if the current context is read-only.
179      * @since Descripter 1.0
180      */
181     public void put(S key, T val) {
182         if (readOnly) {
183             throw new RuntimeException();
184         }
185         map.put(key, val);
186     }
187 }