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 |
213
| public ComponentTemplateLoaderLogic(Log log, IPageLoader pageLoader, BindingSource bindingSource)
|
76 |
| { |
77 |
213
| _log = log;
|
78 |
213
| _pageLoader = pageLoader;
|
79 |
213
| _bindingSource = bindingSource;
|
80 |
| } |
81 |
| |
82 |
213
| public void loadTemplate(IRequestCycle requestCycle, ITemplateComponent loadComponent,
|
83 |
| ComponentTemplate template) |
84 |
| { |
85 |
213
| _requestCycle = requestCycle;
|
86 |
213
| _loadComponent = loadComponent;
|
87 |
| |
88 |
213
| process(template);
|
89 |
| } |
90 |
| |
91 |
213
| private void process(ComponentTemplate template)
|
92 |
| { |
93 |
213
| int count = template.getTokenCount();
|
94 |
| |
95 |
213
| _stack = new IComponent[count];
|
96 |
| |
97 |
213
| for (int i = 0; i < count; i++)
|
98 |
| { |
99 |
3507
| TemplateToken token = template.getToken(i);
|
100 |
| |
101 |
3507
| TokenType type = token.getType();
|
102 |
| |
103 |
3507
| if (type == TokenType.TEXT)
|
104 |
| { |
105 |
1551
| process((TextToken) token);
|
106 |
1551
| continue;
|
107 |
| } |
108 |
| |
109 |
1956
| if (type == TokenType.OPEN)
|
110 |
| { |
111 |
977
| process((OpenToken) token);
|
112 |
970
| continue;
|
113 |
| } |
114 |
| |
115 |
979
| if (type == TokenType.CLOSE)
|
116 |
| { |
117 |
965
| process((CloseToken) token);
|
118 |
965
| 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 |
206
| if (_stackx != 0)
|
132 |
0
| throw new ApplicationRuntimeException(Tapestry
|
133 |
| .getMessage("BaseComponent.unbalance-open-tags"), _loadComponent, null, null); |
134 |
| |
135 |
206
| checkAllComponentsReferenced();
|
136 |
| } |
137 |
| |
138 |
| |
139 |
| |
140 |
| |
141 |
| |
142 |
| |
143 |
| |
144 |
| |
145 |
| |
146 |
1551
| private void process(TextToken token)
|
147 |
| { |
148 |
1551
| if (_activeComponent == null)
|
149 |
| { |
150 |
429
| _loadComponent.addOuter(token);
|
151 |
429
| return;
|
152 |
| } |
153 |
| |
154 |
1122
| if (!_activeComponent.getSpecification().getAllowBody())
|
155 |
0
| throw createBodylessComponentException(_activeComponent);
|
156 |
| |
157 |
1122
| _activeComponent.addBody(token);
|
158 |
| } |
159 |
| |
160 |
977
| private void process(OpenToken token)
|
161 |
| { |
162 |
977
| String id = token.getId();
|
163 |
977
| IComponent component = null;
|
164 |
977
| String componentType = token.getComponentType();
|
165 |
| |
166 |
977
| if (componentType == null)
|
167 |
373
| component = getEmbeddedComponent(id);
|
168 |
| else |
169 |
| { |
170 |
604
| checkForDuplicateId(id, token.getLocation());
|
171 |
| |
172 |
603
| component = createImplicitComponent(id, componentType, token.getLocation());
|
173 |
| } |
174 |
| |
175 |
| |
176 |
| |
177 |
973
| if (_seenIds.contains(id))
|
178 |
0
| throw new ApplicationRuntimeException(ImplMessages.multipleComponentReferences(
|
179 |
| _loadComponent, |
180 |
| id), _loadComponent, token.getLocation(), null); |
181 |
| |
182 |
973
| _seenIds.add(id);
|
183 |
| |
184 |
973
| if (_activeComponent == null)
|
185 |
329
| _loadComponent.addOuter(component);
|
186 |
| else |
187 |
| { |
188 |
| |
189 |
| |
190 |
| |
191 |
644
| if (!_activeComponent.getSpecification().getAllowBody())
|
192 |
0
| throw createBodylessComponentException(_activeComponent);
|
193 |
| |
194 |
644
| _activeComponent.addBody(component);
|
195 |
| } |
196 |
| |
197 |
973
| addTemplateBindings(component, token);
|
198 |
| |
199 |
970
| _stack[_stackx++] = _activeComponent;
|
200 |
| |
201 |
970
| _activeComponent = component;
|
202 |
| } |
203 |
| |
204 |
604
| private void checkForDuplicateId(String id, Location location)
|
205 |
| { |
206 |
604
| if (id == null)
|
207 |
0
| return;
|
208 |
| |
209 |
604
| IContainedComponent cc = _loadComponent.getSpecification().getComponent(id);
|
210 |
| |
211 |
604
| if (cc != null)
|
212 |
1
| throw new ApplicationRuntimeException(ImplMessages.dupeComponentId(id, cc),
|
213 |
| _loadComponent, location, null); |
214 |
| } |
215 |
| |
216 |
603
| private IComponent createImplicitComponent(String id, String componentType, Location location)
|
217 |
| { |
218 |
603
| IComponent result = _pageLoader.createImplicitComponent(
|
219 |
| _requestCycle, |
220 |
| _loadComponent, |
221 |
| id, |
222 |
| componentType, |
223 |
| location); |
224 |
| |
225 |
600
| return result;
|
226 |
| } |
227 |
| |
228 |
373
| private IComponent getEmbeddedComponent(String id)
|
229 |
| { |
230 |
373
| return _loadComponent.getComponent(id);
|
231 |
| } |
232 |
| |
233 |
965
| private void process(CloseToken token)
|
234 |
| { |
235 |
| |
236 |
| |
237 |
| |
238 |
965
| if (_stackx <= 0)
|
239 |
0
| throw new ApplicationRuntimeException(ImplMessages.unbalancedCloseTags(),
|
240 |
| _loadComponent, token.getLocation(), null); |
241 |
| |
242 |
| |
243 |
| |
244 |
965
| _stack[_stackx--] = null;
|
245 |
| |
246 |
965
| _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 |
973
| void addTemplateBindings(IComponent component, OpenToken token)
|
264 |
| { |
265 |
973
| IComponentSpecification spec = component.getSpecification();
|
266 |
| |
267 |
973
| Map attributes = token.getAttributesMap();
|
268 |
| |
269 |
973
| if (attributes != null)
|
270 |
| { |
271 |
531
| Iterator i = attributes.entrySet().iterator();
|
272 |
| |
273 |
531
| while (i.hasNext())
|
274 |
| { |
275 |
706
| Map.Entry entry = (Map.Entry) i.next();
|
276 |
| |
277 |
706
| String attributeName = (String) entry.getKey();
|
278 |
706
| String value = (String) entry.getValue();
|
279 |
| |
280 |
706
| IParameterSpecification pspec = spec.getParameter(attributeName);
|
281 |
706
| String parameterName = pspec == null ? attributeName : pspec.getParameterName();
|
282 |
| |
283 |
706
| if (!attributeName.equals(parameterName))
|
284 |
0
| _log.error(ImplMessages.usedTemplateParameterAlias(
|
285 |
| token, |
286 |
| attributeName, |
287 |
| parameterName)); |
288 |
| |
289 |
706
| String description = ImplMessages.templateParameterName(parameterName);
|
290 |
| |
291 |
| |
292 |
| |
293 |
706
| IBinding binding = _bindingSource.createBinding(
|
294 |
| _loadComponent, |
295 |
| description, |
296 |
| value, |
297 |
| BindingConstants.LITERAL_PREFIX, |
298 |
| token.getLocation()); |
299 |
| |
300 |
706
| addBinding(component, spec, parameterName, binding);
|
301 |
| } |
302 |
| } |
303 |
| |
304 |
| |
305 |
| |
306 |
| |
307 |
| |
308 |
970
| 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 |
712
| private void addBinding(IComponent component, IComponentSpecification spec, String name,
|
329 |
| IBinding binding) |
330 |
| { |
331 |
| |
332 |
| |
333 |
| |
334 |
| |
335 |
712
| boolean valid = validate(component, spec, name, binding);
|
336 |
| |
337 |
709
| if (valid)
|
338 |
681
| component.setBinding(name, binding);
|
339 |
| } |
340 |
| |
341 |
712
| private boolean validate(IComponent component, IComponentSpecification spec, String name,
|
342 |
| IBinding binding) |
343 |
| { |
344 |
| |
345 |
| |
346 |
| |
347 |
712
| boolean literal = binding instanceof LiteralBinding;
|
348 |
| |
349 |
712
| boolean isFormal = (spec.getParameter(name) != null);
|
350 |
| |
351 |
712
| if (isFormal)
|
352 |
| { |
353 |
653
| if (component.getBinding(name) != null)
|
354 |
| { |
355 |
| |
356 |
| |
357 |
| |
358 |
2
| if (literal)
|
359 |
1
| return false;
|
360 |
| |
361 |
1
| throw new ApplicationRuntimeException(ImplMessages.dupeTemplateBinding(
|
362 |
| name, |
363 |
| component, |
364 |
| _loadComponent), component, binding.getLocation(), null); |
365 |
| } |
366 |
| |
367 |
651
| return true;
|
368 |
| } |
369 |
| |
370 |
59
| if (!spec.getAllowInformalParameters())
|
371 |
| { |
372 |
| |
373 |
| |
374 |
| |
375 |
1
| if (literal)
|
376 |
0
| return false;
|
377 |
| |
378 |
1
| throw new ApplicationRuntimeException(ImplMessages.templateBindingForInformalParameter(
|
379 |
| _loadComponent, |
380 |
| name, |
381 |
| component), component, binding.getLocation(), null); |
382 |
| } |
383 |
| |
384 |
| |
385 |
| |
386 |
| |
387 |
58
| if (spec.isReservedParameterName(name))
|
388 |
| { |
389 |
| |
390 |
| |
391 |
| |
392 |
28
| if (literal)
|
393 |
27
| return false;
|
394 |
| |
395 |
1
| throw new ApplicationRuntimeException(ImplMessages.templateBindingForReservedParameter(
|
396 |
| _loadComponent, |
397 |
| name, |
398 |
| component), component, binding.getLocation(), null); |
399 |
| } |
400 |
| |
401 |
30
| return true;
|
402 |
| } |
403 |
| |
404 |
206
| private void checkAllComponentsReferenced()
|
405 |
| { |
406 |
| |
407 |
| |
408 |
| |
409 |
206
| Map components = _loadComponent.getComponents();
|
410 |
| |
411 |
206
| Set ids = components.keySet();
|
412 |
| |
413 |
| |
414 |
| |
415 |
| |
416 |
206
| if (_seenIds.containsAll(ids))
|
417 |
206
| return;
|
418 |
| |
419 |
| |
420 |
| |
421 |
| |
422 |
0
| ids = new HashSet(ids);
|
423 |
0
| ids.removeAll(_seenIds);
|
424 |
| |
425 |
0
| _log.error(ImplMessages.missingComponentSpec(_loadComponent, ids));
|
426 |
| |
427 |
| } |
428 |
| |
429 |
0
| private ApplicationRuntimeException createBodylessComponentException(IComponent component)
|
430 |
| { |
431 |
0
| return new ApplicationRuntimeException(ImplMessages.bodylessComponent(), component, null,
|
432 |
| null); |
433 |
| } |
434 |
| } |