1 package org.apache.torque.generator.variable;
2
3 /*
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
19 * under the License.
20 */
21
22 import java.util.LinkedList;
23
24 import org.apache.torque.generator.qname.Namespace;
25 import org.apache.torque.generator.qname.QualifiedName;
26 import org.apache.torque.generator.qname.QualifiedNameMap;
27
28 /**
29 * Stores all variables which are currently accessible.
30 */
31 public class VariableStore
32 {
33 /**
34 * contains all variables with the scope
35 * <code>Variable.Scope.OUTLET<code>.
36 */
37 private QualifiedNameMap<Variable> outletScope
38 = new QualifiedNameMap<Variable>();
39
40 /**
41 * contains all variables with the scope
42 * <code>Variable.Scope.CHILDREN<code>.
43 * The QualifiedNameMap with the largest index contains the variables added
44 * in the current outlet, and with descending index the variables added
45 * to the respective calling outlets.
46 */
47 private LinkedList<QualifiedNameMap<Variable>> childrenScopeList
48 = new LinkedList<QualifiedNameMap<Variable>>();
49
50 /**
51 * contains all variables with the scope
52 * <code>Variable.Scope.FILE<code>.
53 */
54 private QualifiedNameMap<Variable> fileScope
55 = new QualifiedNameMap<Variable>();
56
57 /**
58 * contains all variables with the scope
59 * <code>Variable.Scope.GLOBAL<code>.
60 */
61 private QualifiedNameMap<Variable> globalScope
62 = new QualifiedNameMap<Variable>();
63
64 /**
65 * Sets a variable.
66 *
67 * @param variable the variable to set.
68 *
69 * @throws NullPointerException if variable is null.
70 */
71 public void set(Variable variable)
72 {
73 if (Variable.Scope.OUTLET.equals(variable.getScope()))
74 {
75 outletScope.put(variable.getName(), variable);
76 }
77 else if (Variable.Scope.CHILDREN.equals(variable.getScope()))
78 {
79 QualifiedNameMap<Variable> qualifiedNameMap
80 = childrenScopeList.getLast();
81 qualifiedNameMap.put(variable.getName(), variable);
82 }
83 else if (Variable.Scope.FILE.equals(variable.getScope()))
84 {
85 fileScope.put(variable.getName(), variable);
86 }
87 else if (Variable.Scope.GLOBAL.equals(variable.getScope()))
88 {
89 globalScope.put(variable.getName(), variable);
90 }
91 }
92
93 /**
94 * removes a variable from the store.
95 *
96 * @param variable the variable to remove, not null.
97 *
98 * @throws NullPointerException if variable is null.
99 */
100 public void remove(Variable variable)
101 {
102 if (Variable.Scope.OUTLET.equals(variable.getScope()))
103 {
104 outletScope.remove(variable.getName());
105 }
106 else if (Variable.Scope.CHILDREN.equals(variable.getScope()))
107 {
108 QualifiedNameMap<Variable> qualifiedNameMap
109 = childrenScopeList.getLast();
110 qualifiedNameMap.remove(variable.getName());
111 }
112 else if (Variable.Scope.FILE.equals(variable.getScope()))
113 {
114 fileScope.remove(variable.getName());
115 }
116 else if (Variable.Scope.GLOBAL.equals(variable.getScope()))
117 {
118 globalScope.remove(variable.getName());
119 }
120 }
121
122 /**
123 * Signals this store that the processing of a outlet has started.
124 */
125 public void startOutlet()
126 {
127 childrenScopeList.addLast(new QualifiedNameMap<Variable>());
128 }
129
130 /**
131 * Signals this store that the processing of a outlet has ended.
132 * Removes all variables with the scope
133 * <code>Variable.Scope.OUTLET</code>,
134 * and all variables with the scope <code>Variable.Scope.CHILDREN</code>
135 * which were set by the outlet which processing ended.
136 */
137 public void endOutlet()
138 {
139 outletScope.clear();
140 childrenScopeList.removeLast();
141 }
142
143 /**
144 * Signals this store that the processing of one file has ended.
145 * Removes all variables with the scope <code>FILE</code>.
146 */
147 public void endFile()
148 {
149 fileScope.clear();
150 }
151
152 /**
153 * Signals this store that generation has ended. Removes the variables
154 * from all scopes.
155 */
156 public void endGeneration()
157 {
158 clear();
159 }
160
161 /**
162 * Removes all variables from this store.
163 */
164 public void clear()
165 {
166 outletScope.clear();
167 childrenScopeList.clear();
168 fileScope.clear();
169 globalScope.clear();
170 }
171
172 /**
173 * Returns a map with the variables contained in this store.
174 * Note that if one variable is hidden by another with a more specific
175 * scope, only the variable with the more specific scope is returned.
176 * <p>
177 * The Map is not backed by this stores, i.e. adding and removing
178 * variables to the returned nmap has no effect on this store.
179 * However, the variables in the returned map are the variables in this
180 * store, i.e. setting the value of the variable will change the variable
181 * in this store.
182 * <p>
183 * Note: The current store content is rebuilt each time this method is
184 * called. For this reason, <code>store.getInHierarchy(qualifiedName)</code>
185 * is much faster than <code>store.getContent().get(qualifiedName)<code>.
186 *
187 * @return a map with the variables contained ins this store, never null.
188 */
189 public QualifiedNameMap<Variable> getContent()
190 {
191 QualifiedNameMap<Variable> result = new QualifiedNameMap<Variable>();
192 result.putAll(globalScope);
193 result.putAll(fileScope);
194
195 for (QualifiedNameMap<Variable> childrenScope : childrenScopeList)
196 {
197 result.putAll(childrenScope);
198 }
199
200 result.putAll(outletScope);
201
202 return result;
203 }
204
205 /**
206 * Returns the most specific variable which is visible from the key's
207 * namespace and has the same name as the key's name.
208 *
209 * if several most specific variables are found in the different scopes,
210 * the one with the most specific scope is returned.
211 *
212 * @param key The key for the variable.
213 * @return the most specific matching variable, or null if no variable
214 * matches.
215 */
216 public Variable getInHierarchy(QualifiedName key)
217 {
218 Variable inOutletScope = outletScope.getInHierarchy(key);
219 Variable inFileScope = fileScope.getInHierarchy(key);
220 Variable inGenerationScope = globalScope.getInHierarchy(key);
221
222 // look through children, with precedence to the outlets which
223 // started later
224 Variable result = null;
225 for (QualifiedNameMap<Variable> childrenScope : childrenScopeList)
226 {
227 Variable inChildrenScope = childrenScope.getInHierarchy(key);
228 result = getMoreSpecific(inChildrenScope, result);
229 }
230 result = getMoreSpecific(inOutletScope, result);
231 result = getMoreSpecific(result, inFileScope);
232 result = getMoreSpecific(result, inGenerationScope);
233 return result;
234 }
235
236 /**
237 * Returns the more specific variable out of two variables (the variable
238 * which hides the other variable).
239 *
240 * If one of the two variables is null, the other is returned.
241 * If both variables is null, null is returned.
242 * If both variables are in the same namespace, variable1 is returned.
243 *
244 * It is assumed that variable1 is in an ancestor namespace of
245 * variable2 or that variable 2 is in an ancestor namespace of
246 * variable1 or both variables are in the same namespace.
247 *
248 * @param variable1 the first variable to compare.
249 * @param variable2 the second variable to compare.
250 * @return the more specific variable, or null if both variable1 and
251 * variable2 are null.
252 *
253 */
254 private Variable getMoreSpecific(Variable variable1, Variable variable2)
255 {
256 if (variable1 == null)
257 {
258 return variable2;
259 }
260 if (variable2 == null)
261 {
262 return variable1;
263 }
264 Namespace variable1Namespace
265 = variable1.getName().getNamespace();
266 Namespace variable2Namespace
267 = variable2.getName().getNamespace();
268 if (variable2Namespace.isVisibleFrom(variable1Namespace))
269 {
270 // variable1 is more specific or in the same naemspace
271 // as variable2, so return variable1
272 return variable1;
273 }
274 else
275 {
276 // variable2 is more specific than variable1
277 return variable2;
278 }
279 }
280 }