KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > google > gwt > dev > js > ast > JsScope


1 /*
2  * Copyright 2007 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy of
6  * the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations under
14  * the License.
15  */

16 package com.google.gwt.dev.js.ast;
17
18 import com.google.gwt.dev.js.JsKeywords;
19
20 import java.util.ArrayList JavaDoc;
21 import java.util.Iterator JavaDoc;
22 import java.util.List JavaDoc;
23 import java.util.Map JavaDoc;
24 import java.util.TreeMap JavaDoc;
25
26 /**
27  * A scope is a factory for creating and allocating
28  * {@link com.google.gwt.compiler.jjs.jsc.JsName}s. A JavaScript AST is built
29  * in terms of abstract name objects without worrying about obfuscation,
30  * keyword/identifier blacklisting, and so on.
31  *
32  * <p>
33  *
34  * Scopes are associated with {@link com.google.gwt.dev.js.ast.JsFunction}s,
35  * but the two are not equivalent. Functions <i>have</i> scopes, but a scope
36  * does not necessarily have an associated Function. Examples of this include
37  * the {@link com.google.gwt.dev.js.ast.JsRootScope} and synthetic scopes that
38  * might be created by a client.
39  *
40  * <p>
41  *
42  * Scopes can have parents to provide constraints when allocating actual
43  * identifiers for names. Specifically, names in child scopes are chosen such
44  * that they do not conflict with names in their parent scopes. The ultimate
45  * parent is usually the global scope (see
46  * {@link com.google.gwt.compiler.jjs.jsc.JsProgram#getGlobalScope()}), but
47  * parentless scopes are useful for managing names that are always accessed with
48  * a qualifier and could therefore never be confused with the global scope
49  * heirarchy.
50  */

51 public class JsScope {
52
53   /**
54    * Prevents the client from programmatically creating an illegal ident.
55    */

56   private static String JavaDoc maybeMangleKeyword(String JavaDoc ident) {
57     if (JsKeywords.isKeyword(ident)) {
58       ident = ident + "_$";
59     }
60     return ident;
61   }
62
63   private final List JavaDoc/* <JsScope> */children = new ArrayList JavaDoc();
64   private final String JavaDoc description;
65   private final Map JavaDoc/* <String, JsName> */names = new TreeMap JavaDoc();
66   private final JsScope parent;
67
68   /**
69    * Create a scope with parent.
70    */

71   public JsScope(JsScope parent, String JavaDoc description) {
72     assert (parent != null);
73     this.description = description;
74     this.parent = parent;
75     this.parent.children.add(this);
76   }
77
78   /**
79    * Subclasses can be parentless.
80    */

81   protected JsScope(String JavaDoc description) {
82     this.description = description;
83     this.parent = null;
84   }
85
86   /**
87    * Gets a name object associated with the specified ident in this scope,
88    * creating it if necessary.
89    *
90    * @param ident An identifier that is unique within this scope.
91    */

92   public JsName declareName(String JavaDoc ident) {
93     ident = maybeMangleKeyword(ident);
94     JsName name = findExistingNameNoRecurse(ident);
95     if (name != null) {
96       return name;
97     }
98     return doCreateName(ident, ident);
99   }
100
101   /**
102    * Gets a name object associated with the specified ident in this scope,
103    * creating it if necessary.
104    *
105    * @param ident An identifier that is unique within this scope.
106    * @param shortIdent A "pretty" name that does not have to be unique.
107    * @throws IllegalArgumentException if ident already exists in this scope but
108    * the requested short name does not match the existing short name.
109    */

110   public JsName declareName(String JavaDoc ident, String JavaDoc shortIdent) {
111     ident = maybeMangleKeyword(ident);
112     shortIdent = maybeMangleKeyword(shortIdent);
113     JsName name = findExistingNameNoRecurse(ident);
114     if (name != null) {
115       if (!name.getShortIdent().equals(shortIdent)) {
116         throw new IllegalArgumentException JavaDoc("Requested short name " + shortIdent
117             + " conflicts with preexisting short name " + name.getShortIdent()
118             + " for identifier " + ident);
119       }
120       return name;
121     }
122     return doCreateName(ident, shortIdent);
123   }
124
125   /**
126    * Attempts to find the name object for the specified ident, searching in this
127    * scope, and if not found, in the parent scopes.
128    *
129    * @return <code>null</code> if the identifier has no associated name
130    */

131   public final JsName findExistingName(String JavaDoc ident) {
132     ident = maybeMangleKeyword(ident);
133     JsName name = findExistingNameNoRecurse(ident);
134     if (name == null && parent != null) {
135       return parent.findExistingName(ident);
136     }
137     return name;
138   }
139
140   /**
141    * Attempts to find an unobfuscatable name object for the specified ident,
142    * searching in this scope, and if not found, in the parent scopes.
143    *
144    * @return <code>null</code> if the identifier has no associated name
145    */

146   public final JsName findExistingUnobfuscatableName(String JavaDoc ident) {
147     ident = maybeMangleKeyword(ident);
148     JsName name = findExistingNameNoRecurse(ident);
149     if (name != null && name.isObfuscatable()) {
150       name = null;
151     }
152     if (name == null && parent != null) {
153       return parent.findExistingUnobfuscatableName(ident);
154     }
155     return name;
156   }
157
158   /**
159    * Returns an iterator for all the names defined by this scope.
160    */

161   public Iterator JavaDoc getAllNames() {
162     return names.values().iterator();
163   }
164
165   /**
166    * Returns a list of this scope's child scopes.
167    */

168   public final List JavaDoc getChildren() {
169     return children;
170   }
171
172   /**
173    * Returns the parent scope of this scope, or <code>null</code> if this is
174    * the root scope.
175    */

176   public final JsScope getParent() {
177     return parent;
178   }
179
180   /**
181    * Returns the associated program.
182    */

183   public JsProgram getProgram() {
184     assert (parent != null) : "Subclasses must override getProgram() if they do not set a parent";
185     return parent.getProgram();
186   }
187
188   public final String JavaDoc toString() {
189     if (parent != null) {
190       return description + "->" + parent;
191     } else {
192       return description;
193     }
194   }
195
196   /**
197    * Creates a new name in this scope.
198    */

199   protected JsName doCreateName(String JavaDoc ident, String JavaDoc shortIdent) {
200     JsName name = new JsName(ident, shortIdent);
201     names.put(ident, name);
202     return name;
203   }
204
205   /**
206    * Attempts to find the name object for the specified ident, searching in this
207    * scope only.
208    *
209    * @return <code>null</code> if the identifier has no associated name
210    */

211   protected JsName findExistingNameNoRecurse(String JavaDoc ident) {
212     return (JsName) names.get(ident);
213   }
214
215 }
216
Popular Tags