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