1 |
| |
2 |
| |
3 |
| |
4 |
| |
5 |
| |
6 |
| |
7 |
| |
8 |
| |
9 |
| |
10 |
| |
11 |
| |
12 |
| |
13 |
| |
14 |
| |
15 |
| package org.apache.tapestry.services.impl; |
16 |
| |
17 |
| import java.util.HashSet; |
18 |
| import java.util.Iterator; |
19 |
| import java.util.Map; |
20 |
| import java.util.Set; |
21 |
| |
22 |
| import org.apache.commons.logging.Log; |
23 |
| import org.apache.hivemind.ApplicationRuntimeException; |
24 |
| import org.apache.hivemind.Location; |
25 |
| import org.apache.tapestry.IBinding; |
26 |
| import org.apache.tapestry.IComponent; |
27 |
| import org.apache.tapestry.IRender; |
28 |
| import org.apache.tapestry.IRequestCycle; |
29 |
| import org.apache.tapestry.ITemplateComponent; |
30 |
| import org.apache.tapestry.Tapestry; |
31 |
| import org.apache.tapestry.binding.BindingConstants; |
32 |
| import org.apache.tapestry.binding.BindingSource; |
33 |
| import org.apache.tapestry.binding.LiteralBinding; |
34 |
| import org.apache.tapestry.engine.IPageLoader; |
35 |
| import org.apache.tapestry.parse.CloseToken; |
36 |
| import org.apache.tapestry.parse.ComponentTemplate; |
37 |
| import org.apache.tapestry.parse.LocalizationToken; |
38 |
| import org.apache.tapestry.parse.OpenToken; |
39 |
| import org.apache.tapestry.parse.TemplateToken; |
40 |
| import org.apache.tapestry.parse.TextToken; |
41 |
| import org.apache.tapestry.parse.TokenType; |
42 |
| import org.apache.tapestry.services.TemplateSource; |
43 |
| import org.apache.tapestry.spec.IComponentSpecification; |
44 |
| import org.apache.tapestry.spec.IContainedComponent; |
45 |
| import org.apache.tapestry.spec.IParameterSpecification; |
46 |
| |
47 |
| |
48 |
| |
49 |
| |
50 |
| |
51 |
| |
52 |
| |
53 |
| |
54 |
| |
55 |
| public class ComponentTemplateLoaderLogic |
56 |
| { |
57 |
| private Log _log; |
58 |
| |
59 |
| private IPageLoader _pageLoader; |
60 |
| |
61 |
| private IRequestCycle _requestCycle; |
62 |
| |
63 |
| private ITemplateComponent _loadComponent; |
64 |
| |
65 |
| private BindingSource _bindingSource; |
66 |
| |
67 |
| private IComponent[] _stack; |
68 |
| |
69 |
| private int _stackx; |
70 |
| |
71 |
| private IComponent _activeComponent = null; |
72 |
| |
73 |
| private Set _seenIds = new HashSet(); |
74 |
| |
75 |
206
| public ComponentTemplateLoaderLogic(Log log, IPageLoader pageLoader, BindingSource bindingSource)
|
76 |
| { |
77 |
206
| _log = log;
|
78 |
206
| _pageLoader = pageLoader;
|
79 |
206
| _bindingSource = bindingSource;
|
80 |
| } |
81 |
| |
82 |
206
| public void loadTemplate(IRequestCycle requestCycle, ITemplateComponent loadComponent,
|
83 |
| ComponentTemplate template) |
84 |
| { |
85 |
206
| _requestCycle = requestCycle;
|
86 |
206
| _loadComponent = loadComponent;
|
87 |
| |
88 |
206
| process(template);
|
89 |
| } |
90 |
| |
91 |
206
| private void process(ComponentTemplate template)
|
92 |
| { |
93 |
206
| int count = template.getTokenCount();
|
94 |
| |
95 |
206
| _stack = new IComponent[count];
|
96 |
| |
97 |
206
| for (int i = 0; i < count; i++)
|
98 |
| { |
99 |
3343
| TemplateToken token = template.getToken(i);
|
100 |
| |
101 |
3343
| TokenType type = token.getType();
|
102 |
| |
103 |
3343
| if (type == TokenType.TEXT)
|
104 |
| { |
105 |
1479
| process((TextToken) token);
|
106 |
1479
| continue;
|
107 |
| } |
108 |
| |
109 |
1864
| if (type == TokenType.OPEN)
|
110 |
| { |
111 |
931
| process((OpenToken) token);
|
112 |
924
| continue;
|
113 |
| } |
114 |
| |
115 |
933
| if (type == TokenType.CLOSE)
|
116 |
| { |
117 |
919
| process((CloseToken) token);
|
118 |
919
| continue;
|
119 |
| } |
120 |
| |
121 |
14
| if (type == TokenType.LOCALIZATION)
|
122 |
| { |
123 |
14
| process((LocalizationToken) token);
|
124 |
14
| continue;
|
125 |
| } |
126 |
| } |
127 |
| |
128 |
| |
129 |
| |
130 |
| |
131 |
199
| if (_stackx != 0)
|
132 |
0
| throw new ApplicationRuntimeException(Tapestry
|
133 |
| .getMessage("BaseComponent.unbalance-open-tags"), _loadComponent, null, null); |
134 |
| |
135 |
199
| checkAllComponentsReferenced();
|
136 |
| } |
137 |
| |
138 |
| |
139 |
| |
140 |
| |
141 |
| |
142 |
| |
143 |
| |
144 |
| |
145 |
| |
146 |
1479
| private void process(TextToken token)
|
147 |
| { |
148 |
1479
| if (_activeComponent == null)
|
149 |
| { |
150 |
409
| _loadComponent.addOuter(token);
|
151 |
409
| return;
|
152 |
| } |
153 |
| |
154 |
1070
| if (!_activeComponent.getSpecification().getAllowBody())
|
155 |
0
| throw createBodylessComponentException(_activeComponent);
|
156 |
| |
157 |
1070
| _activeComponent.addBody(token);
|
158 |
| } |
159 |
| |
160 |
931
| private void process(OpenToken token)
|
161 |
| { |
162 |
931
| String id = token.getId();
|
163 |
931
| IComponent component = null;
|
164 |
931
| String componentType = token.getComponentType();
|
165 |
| |
166 |
931
| if (componentType == null)
|
167 |
348
| component = getEmbeddedComponent(id);
|
168 |
| else |
169 |
| { |
170 |
583
| checkForDuplicateId(id, token.getLocation());
|
171 |
| |
172 |
582
| component = createImplicitComponent(id, componentType, token.getLocation());
|
173 |
| } |
174 |
| |
175 |
| |
176 |
| |
177 |
927
| if (_seenIds.contains(id))
|
178 |
0
| throw new ApplicationRuntimeException(ImplMessages.multipleComponentReferences(
|
179 |
| _loadComponent, |
180 |
| id), _loadComponent, token.getLocation(), null); |
181 |
| |
182 |
927
| _seenIds.add(id);
|
183 |
| |
184 |
927
| if (_activeComponent == null)
|
185 |
314
| _loadComponent.addOuter(component);
|
186 |
| else |
187 |
| { |
188 |
| |
189 |
| |
190 |
| |
191 |
613
| if (!_activeComponent.getSpecification().getAllowBody())
|
192 |
0
| throw createBodylessComponentException(_activeComponent);
|
193 |
| |
194 |
613
| _activeComponent.addBody(component);
|
195 |
| } |
196 |
| |
197 |
927
| addTemplateBindings(component, token);
|
198 |
| |
199 |
924
| _stack[_stackx++] = _activeComponent;
|
200 |
| |
201 |
924
| _activeComponent = component;
|
202 |
| } |
203 |
| |
204 |
583
| private void checkForDuplicateId(String id, Location location)
|
205 |
| { |
206 |
583
| if (id == null)
|
207 |
0
| return;
|
208 |
| |
209 |
583
| IContainedComponent cc = _loadComponent.getSpecification().getComponent(id);
|
210 |
| |
211 |
583
| if (cc != null)
|
212 |
1
| throw new ApplicationRuntimeException(ImplMessages.dupeComponentId(id, cc),
|
213 |
| _loadComponent, location, null); |
214 |
| } |
215 |
| |
216 |
582
| private IComponent createImplicitComponent(String id, String componentType, Location location)
|
217 |
| { |
218 |
582
| IComponent result = _pageLoader.createImplicitComponent(
|
219 |
| _requestCycle, |
220 |
| _loadComponent, |
221 |
| id, |
222 |
| componentType, |
223 |
| location); |
224 |
| |
225 |
579
| return result;
|
226 |
| } |
227 |
| |
228 |
348
| private IComponent getEmbeddedComponent(String id)
|
229 |
| { |
230 |
348
| return _loadComponent.getComponent(id);
|
231 |
| } |
232 |
| |
233 |
919
| private void process(CloseToken token)
|
234 |
| { |
235 |
| |
236 |
| |
237 |
| |
238 |
919
| if (_stackx <= 0)
|
239 |
0
| throw new ApplicationRuntimeException(ImplMessages.unbalancedCloseTags(),
|
240 |
| _loadComponent, token.getLocation(), null); |
241 |
| |
242 |
| |
243 |
| |
244 |
919
| _stack[_stackx--] = null;
|
245 |
| |
246 |
919
| _activeComponent = _stack[_stackx];
|
247 |
| } |
248 |
| |
249 |
14
| private void process(LocalizationToken token)
|
250 |
| { |
251 |
14
| IRender render = new LocalizedStringRender(_loadComponent, token);
|
252 |
| |
253 |
14
| if (_activeComponent == null)
|
254 |
7
| _loadComponent.addOuter(render);
|
255 |
| else |
256 |
7
| _activeComponent.addBody(render);
|
257 |
| } |
258 |
| |
259 |
| |
260 |
| |
261 |
| |
262 |
| |
263 |
927
| void addTemplateBindings(IComponent component, OpenToken token)
|
264 |
| { |
265 |
927
| IComponentSpecification spec = component.getSpecification();
|
266 |
| |
267 |
927
| Map attributes = token.getAttributesMap();
|
268 |
| |
269 |
927
| if (attributes != null)
|
270 |
| { |
271 |
510
| Iterator i = attributes.entrySet().iterator();
|
272 |
| |
273 |
510
| while (i.hasNext())
|
274 |
| { |
275 |
683
| Map.Entry entry = (Map.Entry) i.next();
|
276 |
| |
277 |
683
| String attributeName = (String) entry.getKey();
|
278 |
683
| String value = (String) entry.getValue();
|
279 |
| |
280 |
683
| IParameterSpecification pspec = spec.getParameter(attributeName);
|
281 |
683
| String parameterName = pspec == null ? attributeName : pspec.getParameterName();
|
282 |
| |
283 |
683
| if (!attributeName.equals(parameterName))
|
284 |
0
| _log.warn(ImplMessages.usedTemplateParameterAlias(
|
285 |
| token, |
286 |
| attributeName, |
287 |
| parameterName)); |
288 |
| |
289 |
683
| String description = ImplMessages.templateParameterName(parameterName);
|
290 |
| |
291 |
| |
292 |
| |
293 |
683
| IBinding binding = _bindingSource.createBinding(
|
294 |
| _loadComponent, |
295 |
| description, |
296 |
| value, |
297 |
| BindingConstants.LITERAL_PREFIX, |
298 |
| token.getLocation()); |
299 |
| |
300 |
683
| addBinding(component, spec, parameterName, binding);
|
301 |
| } |
302 |
| } |
303 |
| |
304 |
| |
305 |
| |
306 |
| |
307 |
| |
308 |
924
| if (spec.getParameter(TemplateSource.TEMPLATE_TAG_PARAMETER_NAME) != null
|
309 |
| && component.getBinding(TemplateSource.TEMPLATE_TAG_PARAMETER_NAME) == null) |
310 |
| { |
311 |
6
| IBinding binding = _bindingSource.createBinding(
|
312 |
| component, |
313 |
| TemplateSource.TEMPLATE_TAG_PARAMETER_NAME, |
314 |
| token.getTag(), |
315 |
| BindingConstants.LITERAL_PREFIX, |
316 |
| token.getLocation()); |
317 |
| |
318 |
6
| addBinding(component, spec, TemplateSource.TEMPLATE_TAG_PARAMETER_NAME, binding);
|
319 |
| } |
320 |
| } |
321 |
| |
322 |
| |
323 |
| |
324 |
| |
325 |
| |
326 |
| |
327 |
| |
328 |
689
| private void addBinding(IComponent component, IComponentSpecification spec, String name,
|
329 |
| IBinding binding) |
330 |
| { |
331 |
| |
332 |
| |
333 |
| |
334 |
| |
335 |
689
| boolean valid = validate(component, spec, name, binding);
|
336 |
| |
337 |
686
| if (valid)
|
338 |
661
| component.setBinding(name, binding);
|
339 |
| } |
340 |
| |
341 |
689
| private boolean validate(IComponent component, IComponentSpecification spec, String name,
|
342 |
| IBinding binding) |
343 |
| { |
344 |
| |
345 |
| |
346 |
| |
347 |
689
| boolean isLiteral = binding instanceof LiteralBinding;
|
348 |
689
| boolean isBound = component.getBinding(name) != null;
|
349 |
689
| boolean isFormal = spec.getParameter(name) != null;
|
350 |
| |
351 |
689
| if (!isFormal)
|
352 |
| { |
353 |
56
| if (!spec.getAllowInformalParameters())
|
354 |
| { |
355 |
| |
356 |
| |
357 |
| |
358 |
1
| if (isLiteral)
|
359 |
0
| return false;
|
360 |
| |
361 |
1
| throw new ApplicationRuntimeException(ImplMessages
|
362 |
| .templateBindingForInformalParameter(_loadComponent, name, component), |
363 |
| component, binding.getLocation(), null); |
364 |
| } |
365 |
| |
366 |
| |
367 |
| |
368 |
| |
369 |
55
| if (spec.isReservedParameterName(name))
|
370 |
| { |
371 |
| |
372 |
| |
373 |
| |
374 |
25
| if (isLiteral)
|
375 |
24
| return false;
|
376 |
| |
377 |
1
| throw new ApplicationRuntimeException(ImplMessages
|
378 |
| .templateBindingForReservedParameter(_loadComponent, name, component), |
379 |
| component, binding.getLocation(), null); |
380 |
| } |
381 |
| } |
382 |
| |
383 |
| |
384 |
| |
385 |
| |
386 |
| |
387 |
| |
388 |
| |
389 |
663
| if (isBound)
|
390 |
| { |
391 |
| |
392 |
| |
393 |
| |
394 |
2
| if (isLiteral)
|
395 |
1
| return false;
|
396 |
| |
397 |
1
| throw new ApplicationRuntimeException(ImplMessages.dupeTemplateBinding(
|
398 |
| name, |
399 |
| component, |
400 |
| _loadComponent), component, binding.getLocation(), null); |
401 |
| } |
402 |
| |
403 |
661
| return true;
|
404 |
| |
405 |
| } |
406 |
| |
407 |
199
| private void checkAllComponentsReferenced()
|
408 |
| { |
409 |
| |
410 |
| |
411 |
| |
412 |
199
| Map components = _loadComponent.getComponents();
|
413 |
| |
414 |
199
| Set ids = components.keySet();
|
415 |
| |
416 |
| |
417 |
| |
418 |
| |
419 |
199
| if (_seenIds.containsAll(ids))
|
420 |
199
| return;
|
421 |
| |
422 |
| |
423 |
| |
424 |
| |
425 |
0
| ids = new HashSet(ids);
|
426 |
0
| ids.removeAll(_seenIds);
|
427 |
| |
428 |
0
| _log.warn(ImplMessages.missingComponentSpec(_loadComponent, ids));
|
429 |
| |
430 |
| } |
431 |
| |
432 |
0
| private ApplicationRuntimeException createBodylessComponentException(IComponent component)
|
433 |
| { |
434 |
0
| return new ApplicationRuntimeException(ImplMessages.bodylessComponent(), component, null,
|
435 |
| null); |
436 |
| } |
437 |
| } |