1 | |
|
2 | |
|
3 | |
|
4 | |
|
5 | |
|
6 | |
|
7 | |
|
8 | |
|
9 | |
|
10 | |
|
11 | |
|
12 | |
|
13 | |
|
14 | |
|
15 | |
|
16 | |
|
17 | |
|
18 | |
|
19 | |
package org.apache.myfaces.view.facelets.compiler; |
20 | |
|
21 | |
import java.util.ArrayList; |
22 | |
import java.util.List; |
23 | |
import java.util.Stack; |
24 | |
import java.util.logging.Level; |
25 | |
import java.util.logging.Logger; |
26 | |
|
27 | |
import javax.faces.application.FacesMessage; |
28 | |
import javax.faces.view.facelets.FaceletHandler; |
29 | |
import javax.faces.view.facelets.Tag; |
30 | |
import javax.faces.view.facelets.TagAttribute; |
31 | |
import javax.faces.view.facelets.TagAttributeException; |
32 | |
import javax.faces.view.facelets.TagDecorator; |
33 | |
import javax.faces.view.facelets.TagException; |
34 | |
|
35 | |
import org.apache.myfaces.view.facelets.tag.TagAttributesImpl; |
36 | |
import org.apache.myfaces.view.facelets.tag.TagLibrary; |
37 | |
import org.apache.myfaces.view.facelets.tag.composite.CompositeLibrary; |
38 | |
import org.apache.myfaces.view.facelets.tag.composite.ImplementationHandler; |
39 | |
import org.apache.myfaces.view.facelets.tag.composite.InterfaceHandler; |
40 | |
import org.apache.myfaces.view.facelets.tag.ui.ComponentRefHandler; |
41 | |
import org.apache.myfaces.view.facelets.tag.ui.CompositionHandler; |
42 | |
import org.apache.myfaces.view.facelets.tag.ui.UILibrary; |
43 | |
|
44 | |
|
45 | |
|
46 | |
|
47 | |
|
48 | |
|
49 | |
|
50 | |
|
51 | |
|
52 | |
final class CompilationManager |
53 | |
{ |
54 | |
|
55 | |
|
56 | 0 | private final static Logger log = Logger.getLogger(CompilationManager.class.getName()); |
57 | |
|
58 | |
private final Compiler compiler; |
59 | |
|
60 | |
private final TagLibrary tagLibrary; |
61 | |
|
62 | |
private final TagDecorator tagDecorator; |
63 | |
|
64 | |
private final NamespaceManager namespaceManager; |
65 | |
|
66 | |
private final Stack<CompilationUnit> units; |
67 | |
|
68 | |
private int tagId; |
69 | |
|
70 | |
private boolean finished; |
71 | |
|
72 | |
private final String alias; |
73 | |
|
74 | |
private CompilationUnit interfaceCompilationUnit; |
75 | |
|
76 | |
private final FaceletsProcessingInstructions faceletsProcessingInstructions; |
77 | |
|
78 | |
public CompilationManager(String alias, Compiler compiler, FaceletsProcessingInstructions instructions) |
79 | 0 | { |
80 | |
|
81 | |
|
82 | 0 | this.alias = alias; |
83 | |
|
84 | |
|
85 | 0 | this.compiler = compiler; |
86 | 0 | this.tagDecorator = compiler.createTagDecorator(); |
87 | 0 | this.tagLibrary = compiler.createTagLibrary(); |
88 | |
|
89 | |
|
90 | 0 | this.namespaceManager = new NamespaceManager(); |
91 | |
|
92 | |
|
93 | 0 | this.tagId = 0; |
94 | |
|
95 | |
|
96 | 0 | this.finished = false; |
97 | |
|
98 | |
|
99 | 0 | this.units = new Stack<CompilationUnit>(); |
100 | 0 | this.units.push(new CompilationUnit()); |
101 | |
|
102 | 0 | this.interfaceCompilationUnit = null; |
103 | 0 | this.faceletsProcessingInstructions = instructions; |
104 | 0 | } |
105 | |
|
106 | |
public void writeInstruction(String value) |
107 | |
{ |
108 | 0 | if (this.finished) |
109 | |
{ |
110 | 0 | return; |
111 | |
} |
112 | |
|
113 | |
|
114 | 0 | if (value.length() == 0) |
115 | |
{ |
116 | 0 | return; |
117 | |
} |
118 | |
|
119 | |
TextUnit unit; |
120 | 0 | if (this.currentUnit() instanceof TextUnit) |
121 | |
{ |
122 | 0 | unit = (TextUnit) this.currentUnit(); |
123 | |
} |
124 | |
else |
125 | |
{ |
126 | 0 | unit = new TextUnit(this.alias, this.nextTagId(), |
127 | |
faceletsProcessingInstructions.isEscapeInlineText(), |
128 | |
faceletsProcessingInstructions.isCompressSpaces()); |
129 | 0 | this.startUnit(unit); |
130 | |
} |
131 | 0 | unit.writeInstruction(value); |
132 | 0 | } |
133 | |
|
134 | |
public void writeDoctype(String name, String publicId, String systemId) |
135 | |
{ |
136 | 0 | if (this.finished) |
137 | |
{ |
138 | 0 | return; |
139 | |
} |
140 | |
|
141 | 0 | DoctypeUnit unit = new DoctypeUnit(this.alias, this.nextTagId(), |
142 | |
name, publicId, systemId, faceletsProcessingInstructions.isHtml5Doctype()); |
143 | 0 | this.startUnit(unit); |
144 | 0 | } |
145 | |
|
146 | |
public void writeText(String value) |
147 | |
{ |
148 | |
|
149 | 0 | if (this.finished) |
150 | |
{ |
151 | 0 | return; |
152 | |
} |
153 | |
|
154 | |
|
155 | 0 | if (value.length() == 0) |
156 | |
{ |
157 | 0 | return; |
158 | |
} |
159 | |
|
160 | |
TextUnit unit; |
161 | 0 | if (this.currentUnit() instanceof TextUnit) |
162 | |
{ |
163 | 0 | unit = (TextUnit) this.currentUnit(); |
164 | |
} |
165 | |
else |
166 | |
{ |
167 | 0 | unit = new TextUnit(this.alias, this.nextTagId(), |
168 | |
faceletsProcessingInstructions.isEscapeInlineText(), |
169 | |
faceletsProcessingInstructions.isCompressSpaces()); |
170 | 0 | this.startUnit(unit); |
171 | |
} |
172 | 0 | unit.write(value); |
173 | 0 | } |
174 | |
|
175 | |
public void writeComment(String text) |
176 | |
{ |
177 | 0 | if (this.compiler.isTrimmingComments()) |
178 | |
{ |
179 | 0 | return; |
180 | |
} |
181 | |
|
182 | 0 | if (this.finished) |
183 | |
{ |
184 | 0 | return; |
185 | |
} |
186 | |
|
187 | |
|
188 | 0 | if (text.length() == 0) |
189 | |
{ |
190 | 0 | return; |
191 | |
} |
192 | |
|
193 | |
TextUnit unit; |
194 | 0 | if (this.currentUnit() instanceof TextUnit) |
195 | |
{ |
196 | 0 | unit = (TextUnit) this.currentUnit(); |
197 | |
} |
198 | |
else |
199 | |
{ |
200 | 0 | unit = new TextUnit(this.alias, this.nextTagId(), |
201 | |
faceletsProcessingInstructions.isEscapeInlineText(), |
202 | |
faceletsProcessingInstructions.isCompressSpaces()); |
203 | 0 | this.startUnit(unit); |
204 | |
} |
205 | |
|
206 | 0 | unit.writeComment(text); |
207 | 0 | } |
208 | |
|
209 | |
public void writeWhitespace(String text) |
210 | |
{ |
211 | 0 | if (!this.compiler.isTrimmingWhitespace()) |
212 | |
{ |
213 | 0 | this.writeText(text); |
214 | |
} |
215 | 0 | } |
216 | |
|
217 | |
private String nextTagId() |
218 | |
{ |
219 | 0 | return Integer.toHexString(Math.abs(this.alias.hashCode() ^ 13 * this.tagId++)); |
220 | |
} |
221 | |
|
222 | |
public void pushTag(Tag orig) |
223 | |
{ |
224 | |
|
225 | 0 | if (this.finished) |
226 | |
{ |
227 | 0 | return; |
228 | |
} |
229 | |
|
230 | 0 | if (log.isLoggable(Level.FINE)) |
231 | |
{ |
232 | 0 | log.fine("Tag Pushed: " + orig); |
233 | |
} |
234 | |
|
235 | 0 | Tag t = this.tagDecorator.decorate(orig); |
236 | 0 | String[] qname = this.determineQName(t); |
237 | 0 | t = this.trimAttributes(t); |
238 | |
|
239 | 0 | if (isTrimmed(qname[0], qname[1])) |
240 | |
{ |
241 | 0 | log.fine("Composition Found, Popping Parent Tags"); |
242 | 0 | this.units.clear(); |
243 | 0 | NamespaceUnit nsUnit = this.namespaceManager.toNamespaceUnit(this.tagLibrary); |
244 | 0 | this.units.push(nsUnit); |
245 | 0 | this.startUnit(new TrimmedTagUnit(this.tagLibrary, qname[0], qname[1], t, this.nextTagId())); |
246 | 0 | log.fine("New Namespace and [Trimmed] TagUnit pushed"); |
247 | 0 | } |
248 | 0 | else if (isRemove(qname[0], qname[1])) |
249 | |
{ |
250 | 0 | this.units.push(new RemoveUnit()); |
251 | |
} |
252 | 0 | else if (isCompositeComponentInterface(qname[0], qname[1])) |
253 | |
{ |
254 | |
|
255 | |
|
256 | |
|
257 | |
|
258 | |
|
259 | |
|
260 | |
|
261 | |
|
262 | |
|
263 | |
|
264 | |
|
265 | |
|
266 | |
|
267 | |
|
268 | |
|
269 | |
|
270 | |
|
271 | |
|
272 | 0 | log.fine("Composite Component Interface Found, saving unit"); |
273 | 0 | CompositeComponentUnit compositeRootCompilationUnit = new CompositeComponentUnit(); |
274 | 0 | this.startUnit(compositeRootCompilationUnit); |
275 | 0 | interfaceCompilationUnit = new TagUnit(this.tagLibrary, qname[0], qname[1], t, this.nextTagId()); |
276 | 0 | this.startUnit(interfaceCompilationUnit); |
277 | 0 | } |
278 | 0 | else if (isCompositeComponentImplementation(qname[0], qname[1])) |
279 | |
{ |
280 | 0 | log.fine("Composite component Found, Popping Parent Tags"); |
281 | 0 | this.units.clear(); |
282 | 0 | NamespaceUnit nsUnit = this.namespaceManager.toNamespaceUnit(this.tagLibrary); |
283 | 0 | this.units.push(nsUnit); |
284 | 0 | CompositeComponentUnit compositeRootCompilationUnit = new CompositeComponentUnit(); |
285 | 0 | this.startUnit(compositeRootCompilationUnit); |
286 | 0 | if (interfaceCompilationUnit != null) |
287 | |
{ |
288 | 0 | this.currentUnit().addChild(interfaceCompilationUnit); |
289 | 0 | interfaceCompilationUnit = null; |
290 | |
} |
291 | 0 | this.startUnit(new TrimmedTagUnit(this.tagLibrary, qname[0], qname[1], t, this.nextTagId())); |
292 | 0 | log.fine("New Namespace and TagUnit pushed"); |
293 | 0 | } |
294 | 0 | else if (this.tagLibrary.containsTagHandler(qname[0], qname[1])) |
295 | |
{ |
296 | 0 | this.startUnit(new TagUnit(this.tagLibrary, qname[0], qname[1], t, this.nextTagId())); |
297 | |
} |
298 | 0 | else if (this.tagLibrary.containsNamespace(qname[0])) |
299 | |
{ |
300 | 0 | throw new TagException(orig, "Tag Library supports namespace: " + qname[0] |
301 | |
+ ", but no tag was defined for name: " + qname[1]); |
302 | |
} |
303 | |
else |
304 | |
{ |
305 | |
TextUnit unit; |
306 | 0 | if (this.currentUnit() instanceof TextUnit) |
307 | |
{ |
308 | 0 | unit = (TextUnit) this.currentUnit(); |
309 | |
} |
310 | |
else |
311 | |
{ |
312 | 0 | unit = new TextUnit(this.alias, this.nextTagId(), |
313 | |
faceletsProcessingInstructions.isEscapeInlineText(), |
314 | |
faceletsProcessingInstructions.isCompressSpaces()); |
315 | 0 | this.startUnit(unit); |
316 | |
} |
317 | |
|
318 | 0 | if (this.compiler.isDevelopmentProjectStage()) |
319 | |
{ |
320 | 0 | String qName = null; |
321 | 0 | boolean isPrefixed = false; |
322 | 0 | TagAttribute jsfc = t.getAttributes().get("jsfc"); |
323 | 0 | if (jsfc != null) |
324 | |
{ |
325 | 0 | qName = jsfc.getValue(); |
326 | 0 | if (jsfc.getValue().indexOf(':') > 0) |
327 | |
{ |
328 | 0 | isPrefixed = true; |
329 | |
} |
330 | |
} |
331 | 0 | else if (t.getQName().indexOf(':') > 0 ) |
332 | |
{ |
333 | 0 | qName = t.getQName(); |
334 | 0 | isPrefixed = true; |
335 | |
} |
336 | 0 | if (isPrefixed) |
337 | |
{ |
338 | 0 | unit.addMessage(FacesMessage.SEVERITY_WARN, |
339 | |
"Warning: The page "+alias+" declares namespace "+qname[0]+ |
340 | |
" and uses the tag " + qName + " , but no TagLibrary associated to namespace.", |
341 | |
"Warning: The page "+alias+" declares namespace "+qname[0]+ |
342 | |
" and uses the tag " + qName + " , but no TagLibrary associated to namespace. "+ |
343 | |
"Please check the namespace name and if it is correct, it is probably that your " + |
344 | |
"library .taglib.xml cannot be found on the current classpath, or if you are " + |
345 | |
"referencing a composite component library check your library folder match with the " + |
346 | |
"namespace and can be located by the installed ResourceHandler."); |
347 | |
} |
348 | |
} |
349 | |
|
350 | 0 | unit.startTag(t); |
351 | |
} |
352 | 0 | } |
353 | |
|
354 | |
public void popTag() |
355 | |
{ |
356 | |
|
357 | 0 | if (this.finished) |
358 | |
{ |
359 | 0 | return; |
360 | |
} |
361 | |
|
362 | 0 | CompilationUnit unit = this.currentUnit(); |
363 | |
|
364 | 0 | if (unit instanceof TextUnit) |
365 | |
{ |
366 | 0 | TextUnit t = (TextUnit) unit; |
367 | 0 | if (t.isClosed()) |
368 | |
{ |
369 | 0 | this.finishUnit(); |
370 | |
} |
371 | |
else |
372 | |
{ |
373 | 0 | t.endTag(); |
374 | 0 | return; |
375 | |
} |
376 | |
} |
377 | |
|
378 | 0 | unit = this.currentUnit(); |
379 | 0 | if (unit instanceof TagUnit) |
380 | |
{ |
381 | 0 | TagUnit t = (TagUnit) unit; |
382 | 0 | if (t instanceof TrimmedTagUnit) |
383 | |
{ |
384 | 0 | this.finished = true; |
385 | 0 | return; |
386 | |
} |
387 | 0 | } |
388 | 0 | else if (unit instanceof CompositeComponentUnit) |
389 | |
{ |
390 | 0 | this.finished = true; |
391 | 0 | return; |
392 | |
} |
393 | |
|
394 | 0 | this.finishUnit(); |
395 | 0 | } |
396 | |
|
397 | |
public void popNamespace(String ns) |
398 | |
{ |
399 | 0 | this.namespaceManager.popNamespace(ns); |
400 | 0 | if (this.currentUnit() instanceof NamespaceUnit) |
401 | |
{ |
402 | 0 | this.finishUnit(); |
403 | |
} |
404 | 0 | } |
405 | |
|
406 | |
public void pushNamespace(String prefix, String uri) |
407 | |
{ |
408 | |
|
409 | 0 | if (log.isLoggable(Level.FINE)) |
410 | |
{ |
411 | 0 | log.fine("Namespace Pushed " + prefix + ": " + uri); |
412 | |
} |
413 | |
|
414 | 0 | this.namespaceManager.pushNamespace(prefix, uri); |
415 | |
NamespaceUnit unit; |
416 | 0 | if (this.currentUnit() instanceof NamespaceUnit) |
417 | |
{ |
418 | 0 | unit = (NamespaceUnit) this.currentUnit(); |
419 | |
} |
420 | |
else |
421 | |
{ |
422 | 0 | unit = new NamespaceUnit(this.tagLibrary); |
423 | 0 | this.startUnit(unit); |
424 | |
} |
425 | 0 | unit.setNamespace(prefix, uri); |
426 | 0 | } |
427 | |
|
428 | |
public FaceletHandler createFaceletHandler() |
429 | |
{ |
430 | 0 | return ((CompilationUnit) this.units.get(0)).createFaceletHandler(); |
431 | |
} |
432 | |
|
433 | |
private CompilationUnit currentUnit() |
434 | |
{ |
435 | 0 | if (!this.units.isEmpty()) |
436 | |
{ |
437 | 0 | return (CompilationUnit) this.units.peek(); |
438 | |
} |
439 | 0 | return null; |
440 | |
} |
441 | |
|
442 | |
private void finishUnit() |
443 | |
{ |
444 | 0 | Object obj = this.units.pop(); |
445 | |
|
446 | 0 | if (log.isLoggable(Level.FINE)) |
447 | |
{ |
448 | 0 | log.fine("Finished Unit: " + obj); |
449 | |
} |
450 | 0 | } |
451 | |
|
452 | |
private void startUnit(CompilationUnit unit) |
453 | |
{ |
454 | |
|
455 | 0 | if (log.isLoggable(Level.FINE)) |
456 | |
{ |
457 | 0 | log.fine("Starting Unit: " + unit + " and adding it to parent: " + this.currentUnit()); |
458 | |
} |
459 | |
|
460 | 0 | this.currentUnit().addChild(unit); |
461 | 0 | this.units.push(unit); |
462 | 0 | } |
463 | |
|
464 | |
private Tag trimAttributes(Tag tag) |
465 | |
{ |
466 | 0 | Tag t = this.trimJSFCAttribute(tag); |
467 | 0 | t = this.trimNSAttributes(t); |
468 | 0 | return t; |
469 | |
} |
470 | |
|
471 | |
protected static boolean isRemove(String ns, String name) |
472 | |
{ |
473 | 0 | return (UILibrary.NAMESPACE.equals(ns) || UILibrary.ALIAS_NAMESPACE.equals(ns)) && "remove".equals(name); |
474 | |
} |
475 | |
|
476 | |
protected static boolean isTrimmed(String ns, String name) |
477 | |
{ |
478 | 0 | return (UILibrary.NAMESPACE.equals(ns) || UILibrary.ALIAS_NAMESPACE.equals(ns)) |
479 | |
&& (CompositionHandler.NAME.equals(name) || ComponentRefHandler.NAME.equals(name)); |
480 | |
} |
481 | |
|
482 | |
protected static boolean isCompositeComponentInterface(String ns, String name) |
483 | |
{ |
484 | 0 | return (CompositeLibrary.NAMESPACE.equals(ns) || CompositeLibrary.ALIAS_NAMESPACE.equals(ns)) |
485 | |
&& InterfaceHandler.NAME.equals(name); |
486 | |
} |
487 | |
|
488 | |
protected static boolean isCompositeComponentImplementation(String ns, String name) |
489 | |
{ |
490 | 0 | return (CompositeLibrary.NAMESPACE.equals(ns) || CompositeLibrary.ALIAS_NAMESPACE.equals(ns)) |
491 | |
&& ImplementationHandler.NAME.equals(name); |
492 | |
} |
493 | |
|
494 | |
private String[] determineQName(Tag tag) |
495 | |
{ |
496 | 0 | TagAttribute attr = tag.getAttributes().get("jsfc"); |
497 | 0 | if (attr != null) |
498 | |
{ |
499 | 0 | if (log.isLoggable(Level.FINE)) |
500 | |
{ |
501 | 0 | log.fine(attr + " JSF Facelet Compile Directive Found"); |
502 | |
} |
503 | 0 | String value = attr.getValue(); |
504 | |
String namespace; |
505 | |
String localName; |
506 | 0 | int c = value.indexOf(':'); |
507 | 0 | if (c == -1) |
508 | |
{ |
509 | 0 | namespace = this.namespaceManager.getNamespace(""); |
510 | 0 | localName = value; |
511 | |
} |
512 | |
else |
513 | |
{ |
514 | 0 | String prefix = value.substring(0, c); |
515 | 0 | namespace = this.namespaceManager.getNamespace(prefix); |
516 | 0 | if (namespace == null) |
517 | |
{ |
518 | 0 | throw new TagAttributeException(tag, attr, "No Namespace matched for: " + prefix); |
519 | |
} |
520 | 0 | localName = value.substring(c + 1); |
521 | |
} |
522 | 0 | return new String[] { namespace, localName }; |
523 | |
} |
524 | |
else |
525 | |
{ |
526 | 0 | return new String[] { tag.getNamespace(), tag.getLocalName() }; |
527 | |
} |
528 | |
} |
529 | |
|
530 | |
private Tag trimJSFCAttribute(Tag tag) |
531 | |
{ |
532 | 0 | TagAttribute attr = tag.getAttributes().get("jsfc"); |
533 | 0 | if (attr != null) |
534 | |
{ |
535 | 0 | TagAttribute[] oa = tag.getAttributes().getAll(); |
536 | 0 | TagAttribute[] na = new TagAttribute[oa.length - 1]; |
537 | 0 | int p = 0; |
538 | 0 | for (int i = 0; i < oa.length; i++) |
539 | |
{ |
540 | 0 | if (!"jsfc".equals(oa[i].getLocalName())) |
541 | |
{ |
542 | 0 | na[p++] = oa[i]; |
543 | |
} |
544 | |
} |
545 | 0 | return new Tag(tag, new TagAttributesImpl(na)); |
546 | |
} |
547 | 0 | return tag; |
548 | |
} |
549 | |
|
550 | |
private Tag trimNSAttributes(Tag tag) |
551 | |
{ |
552 | 0 | TagAttribute[] attr = tag.getAttributes().getAll(); |
553 | 0 | int remove = 0; |
554 | 0 | for (int i = 0; i < attr.length; i++) |
555 | |
{ |
556 | 0 | if (attr[i].getQName().startsWith("xmlns") && this.tagLibrary.containsNamespace(attr[i].getValue())) |
557 | |
{ |
558 | 0 | remove |= 1 << i; |
559 | 0 | if (log.isLoggable(Level.FINE)) |
560 | |
{ |
561 | 0 | log.fine(attr[i] + " Namespace Bound to TagLibrary"); |
562 | |
} |
563 | |
} |
564 | |
} |
565 | 0 | if (remove == 0) |
566 | |
{ |
567 | 0 | return tag; |
568 | |
} |
569 | |
else |
570 | |
{ |
571 | 0 | List<TagAttribute> attrList = new ArrayList<TagAttribute>(attr.length); |
572 | 0 | int p = 0; |
573 | 0 | for (int i = 0; i < attr.length; i++) |
574 | |
{ |
575 | 0 | p = 1 << i; |
576 | 0 | if ((p & remove) == p) |
577 | |
{ |
578 | 0 | continue; |
579 | |
} |
580 | 0 | attrList.add(attr[i]); |
581 | |
} |
582 | 0 | attr = attrList.toArray(new TagAttribute[attrList.size()]); |
583 | 0 | return new Tag(tag.getLocation(), tag.getNamespace(), tag.getLocalName(), tag.getQName(), |
584 | |
new TagAttributesImpl(attr)); |
585 | |
} |
586 | |
} |
587 | |
|
588 | |
|
589 | |
|
590 | |
|
591 | |
|
592 | |
|
593 | |
public FaceletsProcessingInstructions getFaceletsProcessingInstructions() |
594 | |
{ |
595 | 0 | return faceletsProcessingInstructions; |
596 | |
} |
597 | |
} |
598 | |
|