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.lang.ref.Reference;
023 import java.lang.ref.WeakReference;
024 import java.util.Map;
025 import java.util.WeakHashMap;
026 
027 /**
028  * <p>Emulates the object properties of JavaScript.</p>
029  * <p>This class is only used in the emulation of for-in statement.</p>
030  * 
031  * @see For
032  * 
033  * @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>
034  * @since Descripter 1.0
035  */
036 public final class Key implements Value
037 {
038     private static int next = -1;
039     private final static Map<String, Key> map = new WeakHashMap<String, Key>();
040     private final static Map<Key, Key> set = new WeakHashMap<Key, Key>();
041 
042     /**
043      * <p>Finds or creates a {@link Key} associated with the specified string name.</p>
044      * <p>Note that a {@link Key} does not strongly reference any objects except that it has a unique hash code.</p>
045      * @param cntx The context invoking this method.
046      * @param name A string name
047      * @return The found or created {@link Key}
048      * @since Descripter 1.0
049      */
050     public static synchronized final Key get(Context<?, ?, ?> cntx, String name) {
051         Key k = map.get(name);
052         if (k == null) {
053             try {
054                 int i = Integer.decode(name);
055                 if (i >= 0) {
056                     return new Key(null, name, i);
057                 }
058             } catch (NumberFormatException nfe) {
059             }
060             k = new Key(cntx, name, next--);
061             map.put(name, k);
062             set.put(k, k);
063         }
064         return k;
065     }
066 
067     /**
068      * <p>Checks if there is already a {@link Key} associated with the specified string name.</p>
069      * <p>Note that a {@link Key} does not strongly reference any objects except that it has a unique hash code.</p>
070      * @param name A string name
071      * @return <tt>true</tt> if there is already a {@link Key} associated with the specified string name; 
072      * <tt>false</tt>, otherwise.
073      * @since Descripter 1.0
074      */
075     public static synchronized final boolean has(String name) {
076         return map.containsKey(name);
077     }
078 
079     /**
080      * <p>Finds a {@link Key} that has the specified hash code.</p>
081      * <p>Note that a {@link Key} does not strongly reference any objects except that it has a unique hash code.</p>
082      * @param hash The integer hash code
083      * @return The found {@link Key} or <tt>null</tt> for none.
084      * @since Descripter 1.0
085      */
086     public static synchronized final Key get(int hash) {
087         return set.get(new Key(hash));
088     }
089 
090     private final Reference<Context<?, ?, ?>> cntx;
091     private final Reference<String> name;
092     private final int hash;
093 
094     private Key(int hash) {
095         this(null, null, hash);
096     }
097 
098     private Key(Context<?, ?, ?> cntx, String name, int hash) {
099         this.cntx = new WeakReference<Context<?, ?, ?>>(cntx);
100         this.name = new WeakReference<String>(name);
101         this.hash = hash;
102     }
103 
104     /**
105      * <p>Returns the hash code of this {@link Key}.</p>
106      * <p>Note that a {@link Key} does not strongly reference any objects except that it has a unique hash code.</p>
107      * @return The hash code of this {@link Key}.
108      * @since Descripter 1.0
109      */
110     @Override
111     public int hashCode() {
112         return hash;
113     }
114 
115     /**
116      * <p>Checks if this {@link Key} is equal to another object.</p>
117      * <p>Two {@link Key}s equal each other if and only if their hash codes are equal.</p>
118      * @param o Another object
119      * @return <tt>true</tt> if this {@link Key} is equal to the specified object.
120      * @since Descripter 1.0
121      */
122     @Override
123     public boolean equals(Object o) {
124         return o instanceof Key && ((Key)o).hash == hash;
125     }
126 
127     /**
128      * <p>Returns the context associated with this {@link Key}.</p>
129      * <p>Note that a {@link Key} does not strongly reference any objects except that it has a unique hash code.</p>
130      * @return The context associated with this {@link Key} or <tt>null</tt> for none.
131      * @since Descripter 1.0
132      */
133     public final Context<?, ?, ?> context() {
134         return cntx.get();
135     }
136 
137     /**
138      * <p>Returns the string name associated with this {@link Key}.</p>
139      * <p>Note that a {@link Key} does not strongly reference any objects except that it has a unique hash code.</p>
140      * @return The string name associated with this {@link Key} or <tt>null</tt> for none.
141      * @since Descripter 1.0
142      */
143     @Override
144     public String evaluate() {
145         return name.get();
146     }
147 
148     /**
149      * <p>Returns the string name associated with this {@link Key}.</p>
150      * <p>Note that a {@link Key} does not strongly reference any objects except that it has a unique hash code.</p>
151      * @return The string name associated with this {@link Key} or <tt>null</tt> for none.
152      * @since Descripter 1.0
153      */
154     @Override
155     public String toString() {
156         return name.get();
157     }
158 }