|
|||||||||||||||||||
30 day Evaluation Version distributed via the Maven Jar Repository. Clover is not free. You have 30 days to evaluate it. Please visit http://www.thecortex.net/clover to obtain a licensed version of Clover | |||||||||||||||||||
Source file | Conditionals | Statements | Methods | TOTAL | |||||||||||||||
BaseDesktopComponentTemplateLoader.java | 0% | 0% | 0% | 0% |
|
1 |
/*
|
|
2 |
* Joey and its relative products are published under the terms
|
|
3 |
* of the Apache Software License.
|
|
4 |
*/
|
|
5 |
package org.asyrinx.brownie.tapestry.components.layer;
|
|
6 |
|
|
7 |
import java.util.HashSet;
|
|
8 |
import java.util.Iterator;
|
|
9 |
import java.util.Map;
|
|
10 |
import java.util.Set;
|
|
11 |
|
|
12 |
import org.apache.commons.lang.builder.ToStringBuilder;
|
|
13 |
import org.apache.commons.logging.Log;
|
|
14 |
import org.apache.commons.logging.LogFactory;
|
|
15 |
import org.apache.tapestry.ApplicationRuntimeException;
|
|
16 |
import org.apache.tapestry.IBinding;
|
|
17 |
import org.apache.tapestry.IComponent;
|
|
18 |
import org.apache.tapestry.ILocation;
|
|
19 |
import org.apache.tapestry.IMarkupWriter;
|
|
20 |
import org.apache.tapestry.IRender;
|
|
21 |
import org.apache.tapestry.IRequestCycle;
|
|
22 |
import org.apache.tapestry.Tapestry;
|
|
23 |
import org.apache.tapestry.binding.ExpressionBinding;
|
|
24 |
import org.apache.tapestry.binding.StaticBinding;
|
|
25 |
import org.apache.tapestry.binding.StringBinding;
|
|
26 |
import org.apache.tapestry.engine.IPageLoader;
|
|
27 |
import org.apache.tapestry.engine.IPageSource;
|
|
28 |
import org.apache.tapestry.engine.ITemplateSource;
|
|
29 |
import org.apache.tapestry.parse.AttributeType;
|
|
30 |
import org.apache.tapestry.parse.CloseToken;
|
|
31 |
import org.apache.tapestry.parse.ComponentTemplate;
|
|
32 |
import org.apache.tapestry.parse.LocalizationToken;
|
|
33 |
import org.apache.tapestry.parse.OpenToken;
|
|
34 |
import org.apache.tapestry.parse.TemplateAttribute;
|
|
35 |
import org.apache.tapestry.parse.TemplateToken;
|
|
36 |
import org.apache.tapestry.parse.TextToken;
|
|
37 |
import org.apache.tapestry.parse.TokenType;
|
|
38 |
import org.apache.tapestry.spec.IComponentSpecification;
|
|
39 |
|
|
40 |
/**
|
|
41 |
**/
|
|
42 |
|
|
43 |
public class BaseDesktopComponentTemplateLoader { |
|
44 |
private static final Log LOG = LogFactory |
|
45 |
.getLog(BaseDesktopComponentTemplateLoader.class);
|
|
46 |
|
|
47 |
private IPageLoader _pageLoader;
|
|
48 |
|
|
49 |
private IRequestCycle _requestCycle;
|
|
50 |
|
|
51 |
private BaseDesktopComponent _loadComponent;
|
|
52 |
|
|
53 |
private IPageSource _pageSource;
|
|
54 |
|
|
55 |
private ComponentTemplate _template;
|
|
56 |
|
|
57 |
private IComponent[] _stack;
|
|
58 |
|
|
59 |
private int _stackx = 0; |
|
60 |
|
|
61 |
private IComponent _activeComponent = null; |
|
62 |
|
|
63 |
private Set _seenIds = new HashSet(); |
|
64 |
|
|
65 |
/**
|
|
66 |
* A class used with invisible localizations. Constructed from a
|
|
67 |
* {@link TextToken}.
|
|
68 |
*
|
|
69 |
*
|
|
70 |
*/
|
|
71 |
|
|
72 |
private static class LocalizedStringRender implements IRender { |
|
73 |
private IComponent _component;
|
|
74 |
|
|
75 |
private String _key;
|
|
76 |
|
|
77 |
private Map _attributes;
|
|
78 |
|
|
79 |
private String _value;
|
|
80 |
|
|
81 |
private boolean _raw; |
|
82 |
|
|
83 | 0 |
private LocalizedStringRender(IComponent component,
|
84 |
LocalizationToken token) { |
|
85 | 0 |
_component = component; |
86 | 0 |
_key = token.getKey(); |
87 | 0 |
_raw = token.isRaw(); |
88 | 0 |
_attributes = token.getAttributes(); |
89 |
} |
|
90 |
|
|
91 | 0 |
public void render(IMarkupWriter writer, IRequestCycle cycle) { |
92 | 0 |
if (cycle.isRewinding())
|
93 | 0 |
return;
|
94 |
|
|
95 | 0 |
if (_attributes != null) { |
96 | 0 |
writer.begin("span");
|
97 |
|
|
98 | 0 |
Iterator i = _attributes.entrySet().iterator(); |
99 |
|
|
100 | 0 |
while (i.hasNext()) {
|
101 | 0 |
Map.Entry entry = (Map.Entry) i.next(); |
102 | 0 |
String attributeName = (String) entry.getKey(); |
103 | 0 |
String attributeValue = (String) entry.getValue(); |
104 |
|
|
105 | 0 |
writer.attribute(attributeName, attributeValue); |
106 |
} |
|
107 |
} |
|
108 |
|
|
109 | 0 |
if (_value == null) |
110 | 0 |
_value = _component.getMessage(_key); |
111 |
|
|
112 | 0 |
if (_raw)
|
113 | 0 |
writer.printRaw(_value); |
114 |
else
|
|
115 | 0 |
writer.print(_value); |
116 |
|
|
117 | 0 |
if (_attributes != null) |
118 | 0 |
writer.end(); |
119 |
} |
|
120 |
|
|
121 | 0 |
public String toString() {
|
122 | 0 |
ToStringBuilder builder = new ToStringBuilder(this); |
123 |
|
|
124 | 0 |
builder.append("component", _component);
|
125 | 0 |
builder.append("key", _key);
|
126 | 0 |
builder.append("raw", _raw);
|
127 | 0 |
builder.append("attributes", _attributes);
|
128 |
|
|
129 | 0 |
return builder.toString();
|
130 |
} |
|
131 |
|
|
132 |
} |
|
133 |
|
|
134 | 0 |
public BaseDesktopComponentTemplateLoader(IRequestCycle requestCycle,
|
135 |
IPageLoader pageLoader, BaseDesktopComponent loadComponent, |
|
136 |
ComponentTemplate template, IPageSource pageSource) { |
|
137 | 0 |
_requestCycle = requestCycle; |
138 | 0 |
_pageLoader = pageLoader; |
139 | 0 |
_loadComponent = loadComponent; |
140 | 0 |
_template = template; |
141 | 0 |
_pageSource = pageSource; |
142 |
|
|
143 | 0 |
_stack = new IComponent[template.getTokenCount()];
|
144 |
} |
|
145 |
|
|
146 | 0 |
public void process() { |
147 | 0 |
int count = _template.getTokenCount();
|
148 |
|
|
149 | 0 |
for (int i = 0; i < count; i++) { |
150 | 0 |
TemplateToken token = _template.getToken(i); |
151 |
|
|
152 | 0 |
TokenType type = token.getType(); |
153 |
|
|
154 | 0 |
if (type == TokenType.TEXT) {
|
155 | 0 |
process((TextToken) token); |
156 | 0 |
continue;
|
157 |
} |
|
158 |
|
|
159 | 0 |
if (type == TokenType.OPEN) {
|
160 | 0 |
process((OpenToken) token); |
161 | 0 |
continue;
|
162 |
} |
|
163 |
|
|
164 | 0 |
if (type == TokenType.CLOSE) {
|
165 | 0 |
process((CloseToken) token); |
166 | 0 |
continue;
|
167 |
} |
|
168 |
|
|
169 | 0 |
if (type == TokenType.LOCALIZATION) {
|
170 | 0 |
process((LocalizationToken) token); |
171 | 0 |
continue;
|
172 |
} |
|
173 |
} |
|
174 |
|
|
175 |
// This is also pretty much unreachable, and the message is kind of out
|
|
176 |
// of date, too.
|
|
177 |
|
|
178 | 0 |
if (_stackx != 0)
|
179 | 0 |
throw new ApplicationRuntimeException(Tapestry |
180 |
.getMessage("BaseDesktopComponent.unbalance-open-tags"),
|
|
181 |
_loadComponent, null, null); |
|
182 |
|
|
183 | 0 |
checkAllComponentsReferenced(); |
184 |
} |
|
185 |
|
|
186 |
/**
|
|
187 |
* Adds the token (which implements {@link IRender}) to the active
|
|
188 |
* component (using {@link IComponent#addBody(IRender)}), or to this
|
|
189 |
* component {@link #addOuter(IRender)}.
|
|
190 |
*
|
|
191 |
* <p>
|
|
192 |
* A check is made that the active component allows a body.
|
|
193 |
*
|
|
194 |
*/
|
|
195 |
|
|
196 | 0 |
private void process(TextToken token) { |
197 | 0 |
if (_activeComponent == null) { |
198 | 0 |
_loadComponent.addOuter(token); |
199 | 0 |
return;
|
200 |
} |
|
201 |
|
|
202 | 0 |
if (!_activeComponent.getSpecification().getAllowBody())
|
203 | 0 |
throw createBodylessComponentException(_activeComponent);
|
204 |
|
|
205 | 0 |
_activeComponent.addBody(token); |
206 |
} |
|
207 |
|
|
208 | 0 |
private void process(OpenToken token) { |
209 | 0 |
String id = token.getId(); |
210 | 0 |
IComponent component = null;
|
211 | 0 |
String componentType = token.getComponentType(); |
212 |
|
|
213 | 0 |
if (componentType == null) |
214 | 0 |
component = getEmbeddedComponent(id); |
215 |
else
|
|
216 | 0 |
component = createImplicitComponent(id, componentType, token |
217 |
.getLocation()); |
|
218 |
|
|
219 |
// Make sure the template contains each component only once.
|
|
220 |
|
|
221 | 0 |
if (_seenIds.contains(id))
|
222 | 0 |
throw new ApplicationRuntimeException(Tapestry.format( |
223 |
"BaseDesktopComponent.multiple-component-references",
|
|
224 |
_loadComponent.getExtendedId(), id), _loadComponent, token |
|
225 |
.getLocation(), null);
|
|
226 |
|
|
227 | 0 |
_seenIds.add(id); |
228 |
|
|
229 | 0 |
if (_activeComponent == null) |
230 | 0 |
_loadComponent.addOuter(component); |
231 |
else {
|
|
232 |
// Note: this code may no longer be reachable (because the
|
|
233 |
// template parser does this check first).
|
|
234 |
|
|
235 | 0 |
if (!_activeComponent.getSpecification().getAllowBody())
|
236 | 0 |
throw createBodylessComponentException(_activeComponent);
|
237 |
|
|
238 | 0 |
_activeComponent.addBody(component); |
239 |
} |
|
240 |
|
|
241 | 0 |
addTemplateBindings(component, token); |
242 |
|
|
243 | 0 |
_stack[_stackx++] = _activeComponent; |
244 |
|
|
245 | 0 |
_activeComponent = component; |
246 |
} |
|
247 |
|
|
248 | 0 |
private IComponent createImplicitComponent(String id, String componentType,
|
249 |
ILocation location) { |
|
250 | 0 |
IComponent result = _pageLoader.createImplicitComponent(_requestCycle, |
251 |
_loadComponent, id, componentType, location); |
|
252 |
|
|
253 | 0 |
return result;
|
254 |
} |
|
255 |
|
|
256 | 0 |
private IComponent getEmbeddedComponent(String id) {
|
257 | 0 |
return _loadComponent.getComponent(id);
|
258 |
} |
|
259 |
|
|
260 | 0 |
private void process(CloseToken token) { |
261 |
// Again, this is pretty much impossible to reach because
|
|
262 |
// the template parser does a great job.
|
|
263 |
|
|
264 | 0 |
if (_stackx <= 0)
|
265 | 0 |
throw new ApplicationRuntimeException(Tapestry |
266 |
.getMessage("BaseDesktopComponent.unbalanced-close-tags"),
|
|
267 |
_loadComponent, token.getLocation(), null);
|
|
268 |
|
|
269 |
// Null and forget the top element on the stack.
|
|
270 |
|
|
271 | 0 |
_stack[_stackx--] = null;
|
272 |
|
|
273 | 0 |
_activeComponent = _stack[_stackx]; |
274 |
} |
|
275 |
|
|
276 | 0 |
private void process(LocalizationToken token) { |
277 | 0 |
IRender render = new LocalizedStringRender(_loadComponent, token);
|
278 |
|
|
279 | 0 |
if (_activeComponent == null) |
280 | 0 |
_loadComponent.addOuter(render); |
281 |
else
|
|
282 | 0 |
_activeComponent.addBody(render); |
283 |
} |
|
284 |
|
|
285 |
/**
|
|
286 |
* Adds bindings based on attributes in the template.
|
|
287 |
*
|
|
288 |
* @since 3.0
|
|
289 |
*
|
|
290 |
*/
|
|
291 |
|
|
292 | 0 |
private void addTemplateBindings(IComponent component, OpenToken token) { |
293 | 0 |
IComponentSpecification spec = component.getSpecification(); |
294 |
|
|
295 |
// add a static binding carrying the template tag
|
|
296 | 0 |
addStaticBinding(component, spec, |
297 |
ITemplateSource.TEMPLATE_TAG_PARAMETER_NAME, token.getTag(), |
|
298 |
token.getLocation()); |
|
299 |
|
|
300 | 0 |
Map attributes = token.getAttributesMap(); |
301 |
|
|
302 | 0 |
if (attributes == null) |
303 | 0 |
return;
|
304 |
|
|
305 | 0 |
Iterator i = attributes.entrySet().iterator(); |
306 |
|
|
307 | 0 |
while (i.hasNext()) {
|
308 | 0 |
Map.Entry entry = (Map.Entry) i.next(); |
309 |
|
|
310 | 0 |
String name = (String) entry.getKey(); |
311 | 0 |
TemplateAttribute attribute = (TemplateAttribute) entry.getValue(); |
312 | 0 |
AttributeType type = attribute.getType(); |
313 |
|
|
314 | 0 |
if (type == AttributeType.OGNL_EXPRESSION) {
|
315 | 0 |
addExpressionBinding(component, spec, name, attribute |
316 |
.getValue(), token.getLocation()); |
|
317 | 0 |
continue;
|
318 |
} |
|
319 |
|
|
320 | 0 |
if (type == AttributeType.LOCALIZATION_KEY) {
|
321 | 0 |
addStringBinding(component, spec, name, attribute.getValue(), |
322 |
token.getLocation()); |
|
323 | 0 |
continue;
|
324 |
} |
|
325 |
|
|
326 | 0 |
if (type == AttributeType.LITERAL)
|
327 | 0 |
addStaticBinding(component, spec, name, attribute.getValue(), |
328 |
token.getLocation()); |
|
329 |
} |
|
330 |
} |
|
331 |
|
|
332 |
/**
|
|
333 |
* Adds an expression binding, checking for errors related to reserved and
|
|
334 |
* informal parameters.
|
|
335 |
*
|
|
336 |
* <p>
|
|
337 |
* It is an error to specify expression bindings in both the specification
|
|
338 |
* and the template.
|
|
339 |
*
|
|
340 |
* @since 3.0
|
|
341 |
*/
|
|
342 |
|
|
343 | 0 |
private void addExpressionBinding(IComponent component, |
344 |
IComponentSpecification spec, String name, String expression, |
|
345 |
ILocation location) { |
|
346 |
|
|
347 |
// If matches a formal parameter name, allow it to be set
|
|
348 |
// unless there's already a binding.
|
|
349 |
|
|
350 | 0 |
boolean isFormal = (spec.getParameter(name) != null); |
351 |
|
|
352 | 0 |
if (isFormal) {
|
353 | 0 |
if (component.getBinding(name) != null) |
354 | 0 |
throw new ApplicationRuntimeException(Tapestry.format( |
355 |
"BaseDesktopComponent.dupe-template-expression", name,
|
|
356 |
component.getExtendedId(), _loadComponent |
|
357 |
.getExtendedId()), component, location, null);
|
|
358 |
} else {
|
|
359 | 0 |
if (!spec.getAllowInformalParameters())
|
360 | 0 |
throw new ApplicationRuntimeException( |
361 |
Tapestry |
|
362 |
.format( |
|
363 |
"BaseDesktopComponent.template-expression-for-informal-parameter",
|
|
364 |
name, component.getExtendedId(), |
|
365 |
_loadComponent.getExtendedId()), |
|
366 |
component, location, null);
|
|
367 |
|
|
368 |
// If the name is reserved (matches a formal parameter
|
|
369 |
// or reserved name, caselessly), then skip it.
|
|
370 |
|
|
371 | 0 |
if (spec.isReservedParameterName(name))
|
372 | 0 |
throw new ApplicationRuntimeException( |
373 |
Tapestry |
|
374 |
.format( |
|
375 |
"BaseDesktopComponent.template-expression-for-reserved-parameter",
|
|
376 |
name, component.getExtendedId(), |
|
377 |
_loadComponent.getExtendedId()), |
|
378 |
component, location, null);
|
|
379 |
} |
|
380 |
|
|
381 | 0 |
IBinding binding = new ExpressionBinding(_pageSource
|
382 |
.getResourceResolver(), _loadComponent, expression, location); |
|
383 |
|
|
384 | 0 |
component.setBinding(name, binding); |
385 |
} |
|
386 |
|
|
387 |
/**
|
|
388 |
* Adds an expression binding, checking for errors related to reserved and
|
|
389 |
* informal parameters.
|
|
390 |
*
|
|
391 |
* <p>
|
|
392 |
* It is an error to specify expression bindings in both the specification
|
|
393 |
* and the template.
|
|
394 |
*
|
|
395 |
* @since 3.0
|
|
396 |
*/
|
|
397 |
|
|
398 | 0 |
private void addStringBinding(IComponent component, |
399 |
IComponentSpecification spec, String name, String localizationKey, |
|
400 |
ILocation location) { |
|
401 |
// If matches a formal parameter name, allow it to be set
|
|
402 |
// unless there's already a binding.
|
|
403 |
|
|
404 | 0 |
boolean isFormal = (spec.getParameter(name) != null); |
405 |
|
|
406 | 0 |
if (isFormal) {
|
407 | 0 |
if (component.getBinding(name) != null) |
408 | 0 |
throw new ApplicationRuntimeException(Tapestry.format( |
409 |
"BaseDesktopComponent.dupe-string", name, component
|
|
410 |
.getExtendedId(), _loadComponent |
|
411 |
.getExtendedId()), component, location, null);
|
|
412 |
} else {
|
|
413 | 0 |
if (!spec.getAllowInformalParameters())
|
414 | 0 |
throw new ApplicationRuntimeException( |
415 |
Tapestry |
|
416 |
.format( |
|
417 |
"BaseDesktopComponent.template-expression-for-informal-parameter",
|
|
418 |
name, component.getExtendedId(), |
|
419 |
_loadComponent.getExtendedId()), |
|
420 |
component, location, null);
|
|
421 |
|
|
422 |
// If the name is reserved (matches a formal parameter
|
|
423 |
// or reserved name, caselessly), then skip it.
|
|
424 |
|
|
425 | 0 |
if (spec.isReservedParameterName(name))
|
426 | 0 |
throw new ApplicationRuntimeException( |
427 |
Tapestry |
|
428 |
.format( |
|
429 |
"BaseDesktopComponent.template-expression-for-reserved-parameter",
|
|
430 |
name, component.getExtendedId(), |
|
431 |
_loadComponent.getExtendedId()), |
|
432 |
component, location, null);
|
|
433 |
} |
|
434 |
|
|
435 | 0 |
IBinding binding = new StringBinding(_loadComponent, localizationKey,
|
436 |
location); |
|
437 |
|
|
438 | 0 |
component.setBinding(name, binding); |
439 |
} |
|
440 |
|
|
441 |
/**
|
|
442 |
* Adds a static binding, checking for errors related to reserved and
|
|
443 |
* informal parameters.
|
|
444 |
*
|
|
445 |
* <p>
|
|
446 |
* Static bindings that conflict with bindings in the specification are
|
|
447 |
* quietly ignored.
|
|
448 |
*
|
|
449 |
* @since 3.0
|
|
450 |
*
|
|
451 |
*/
|
|
452 |
|
|
453 | 0 |
private void addStaticBinding(IComponent component, |
454 |
IComponentSpecification spec, String name, String staticValue, |
|
455 |
ILocation location) { |
|
456 |
|
|
457 | 0 |
if (component.getBinding(name) != null) |
458 | 0 |
return;
|
459 |
|
|
460 |
// If matches a formal parameter name, allow it to be set
|
|
461 |
// unless there's already a binding.
|
|
462 |
|
|
463 | 0 |
boolean isFormal = (spec.getParameter(name) != null); |
464 |
|
|
465 | 0 |
if (!isFormal) {
|
466 |
// Skip informal parameters if the component doesn't allow them.
|
|
467 |
|
|
468 | 0 |
if (!spec.getAllowInformalParameters())
|
469 | 0 |
return;
|
470 |
|
|
471 |
// If the name is reserved (matches a formal parameter
|
|
472 |
// or reserved name, caselessly), then skip it.
|
|
473 |
|
|
474 | 0 |
if (spec.isReservedParameterName(name))
|
475 | 0 |
return;
|
476 |
} |
|
477 |
|
|
478 | 0 |
IBinding binding = new StaticBinding(staticValue, location);
|
479 |
|
|
480 | 0 |
component.setBinding(name, binding); |
481 |
} |
|
482 |
|
|
483 | 0 |
private void checkAllComponentsReferenced() { |
484 |
// First, contruct a modifiable copy of the ids of all expected
|
|
485 |
// components
|
|
486 |
// (that is, components declared in the specification).
|
|
487 |
|
|
488 | 0 |
Map components = _loadComponent.getComponents(); |
489 |
|
|
490 | 0 |
Set ids = components.keySet(); |
491 |
|
|
492 |
// If the seen ids ... ids referenced in the template, matches
|
|
493 |
// all the ids in the specification then we're fine.
|
|
494 |
|
|
495 | 0 |
if (_seenIds.containsAll(ids))
|
496 | 0 |
return;
|
497 |
|
|
498 |
// Create a modifiable copy. Remove the ids that are referenced in
|
|
499 |
// the template. The remainder are worthy of note.
|
|
500 |
|
|
501 | 0 |
ids = new HashSet(ids);
|
502 | 0 |
ids.removeAll(_seenIds); |
503 |
|
|
504 | 0 |
int count = ids.size();
|
505 |
|
|
506 | 0 |
String key = (count == 1) ? "BaseDesktopComponent.missing-component-spec-single"
|
507 |
: "BaseDesktopComponent.missing-component-spec-multi";
|
|
508 |
|
|
509 | 0 |
StringBuffer buffer = new StringBuffer(Tapestry.format(key,
|
510 |
_loadComponent.getExtendedId())); |
|
511 |
|
|
512 | 0 |
Iterator i = ids.iterator(); |
513 | 0 |
int j = 1;
|
514 |
|
|
515 | 0 |
while (i.hasNext()) {
|
516 | 0 |
if (j == 1)
|
517 | 0 |
buffer.append(' '); |
518 | 0 |
else if (j == count) { |
519 | 0 |
buffer.append(' '); |
520 | 0 |
buffer.append(Tapestry.getMessage("BaseDesktopComponent.and"));
|
521 | 0 |
buffer.append(' '); |
522 |
} else
|
|
523 | 0 |
buffer.append(", ");
|
524 |
|
|
525 | 0 |
buffer.append(i.next()); |
526 |
|
|
527 | 0 |
j++; |
528 |
} |
|
529 |
|
|
530 | 0 |
buffer.append('.'); |
531 |
|
|
532 | 0 |
LOG.error(buffer.toString()); |
533 |
} |
|
534 |
|
|
535 | 0 |
protected ApplicationRuntimeException createBodylessComponentException(
|
536 |
IComponent component) { |
|
537 | 0 |
return new ApplicationRuntimeException(Tapestry |
538 |
.getMessage("BaseComponentTemplateLoader.bodyless-component"),
|
|
539 |
component, null, null); |
|
540 |
} |
|
541 |
} |
|