/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.seam.wiki.core.wikitext.renderer.jsf; import antlr.ANTLRException; import antlr.RecognitionException; import com.sun.facelets.FaceletContext; import com.sun.facelets.tag.MetaRuleset; import com.sun.facelets.tag.MetaTagHandler; import com.sun.facelets.tag.TagAttribute; import com.sun.facelets.tag.TagConfig; import org.jboss.seam.log.Log; import org.jboss.seam.log.Logging; import org.jboss.seam.wiki.core.wikitext.engine.WikiTextParser; import javax.el.ELException; import javax.faces.FacesException; import javax.faces.component.UIComponent; import java.io.IOException; import java.util.Iterator; /** * Creates a <tt>UIWikiFormattedText</tt> JSF component and substitutes macro names in wiki * text with real macro components in the tree. These <tt>UIMacro</tt> components are * build from XHTML fragments/includes. Interacts closely with the state of the * <tt>UIWikiFormattedText</tt> component to split component tree creation and rendering duties. * * @author Peter Muir * @author Christian Bauer */ public class WikiFormattedTextHandler extends MetaTagHandler { private Log log = Logging.getLog(WikiFormattedTextHandler.class); private static final String MARK = "org.jboss.seam.wiki.core.ui.WikiFormattedTextHandler"; private TagAttribute valueAttribute; public WikiFormattedTextHandler(TagConfig config) { super(config); this.valueAttribute = this.getRequiredAttribute("value"); } /* * Main apply method called by facelets to create this component. */ public void apply(FaceletContext ctx, UIComponent parent) throws IOException, FacesException, ELException { log.debug(">>> building wiki text components for child of: " + parent.getClientId(ctx.getFacesContext())); String id = ctx.generateUniqueId(this.tagId); UIComponent cmp = findChildByTagId(parent, id); if (cmp == null) { cmp = createComponent(ctx); cmp.getAttributes().put(MARK, id); } this.nextHandler.apply(ctx, cmp); parent.getChildren().add(cmp); createMacroComponents(ctx, cmp); log.debug("<<< completed building wiki text components for child of: " + parent.getClientId(ctx.getFacesContext())); } private UIComponent createComponent(FaceletContext ctx) { UIWikiFormattedText wikiFormattedText = new UIWikiFormattedText(); setAttributes(ctx, wikiFormattedText); return wikiFormattedText; } /* * Have to manually wire the component as the Facelets magic wirer * is a package scoped class. */ @Override protected void setAttributes(FaceletContext ctx, Object instance) { UIComponent cmp = (UIComponent) instance; setAttribute(ctx, cmp, UIWikiFormattedText.ATTR_LINK_STYLE_CLASS); setAttribute(ctx, cmp, UIWikiFormattedText.ATTR_BROKEN_LINK_STYLE_CLASS); setAttribute(ctx, cmp, UIWikiFormattedText.ATTR_ATTACHMENT_LINK_STYLE_CLASS); setAttribute(ctx, cmp, UIWikiFormattedText.ATTR_THUMBNAIL_LINK_STYLE_CLASS); setAttribute(ctx, cmp, UIWikiFormattedText.ATTR_INTERNAL_TARGET_FRAME); setAttribute(ctx, cmp, UIWikiFormattedText.ATTR_EXTERNAL_TARGET_FRAME); setAttribute(ctx, cmp, UIWikiFormattedText.ATTR_LINK_BASE_FILE); setAttribute(ctx, cmp, UIWikiFormattedText.ATTR_CURRENT_AREA_NUMBER); setAttribute(ctx, cmp, UIWikiFormattedText.ATTR_ENABLE_MACRO_RENDERING, false); setAttribute(ctx, cmp, UIWikiFormattedText.ATTR_ENABLE_TRANSIENT_MACROS, false); } @Override protected MetaRuleset createMetaRuleset(Class type) { return super.createMetaRuleset(type).ignoreAll(); } private void createMacroComponents(final FaceletContext ctx, final UIComponent parent) { if (!(parent instanceof UIWikiFormattedText)) return; final UIWikiFormattedText wikiFormattedTextComponent = (UIWikiFormattedText) parent; String unparsed = valueAttribute.getValue(ctx); // Don't forget this, transporting the value to the handled component, we need to render it (again) later wikiFormattedTextComponent.setValue(unparsed); if (getAttribute(UIWikiFormattedText.ATTR_ENABLE_MACRO_RENDERING) == null || !getAttribute(UIWikiFormattedText.ATTR_ENABLE_MACRO_RENDERING).getBoolean(ctx)) { log.debug("macro rendering disabled"); return; } // We need to parse the wiki text once (later again for rendering) to find all macros in the text log.debug("creating macro components from wiki text macros"); WikiTextParser parser = new WikiTextParser(unparsed, true, false); // This is a special flag that disables/enables caching of ValueExpressions inside macro templates, // see the MacroIncludeTextRenderer for an explanation... boolean enableTransientMacros = (getAttribute(UIWikiFormattedText.ATTR_ENABLE_TRANSIENT_MACROS) != null && getAttribute(UIWikiFormattedText.ATTR_ENABLE_TRANSIENT_MACROS).getBoolean(ctx)); // We use a renderer to implement the callbacks from the parsing and to create the components in the tree parser.setRenderer( new MacroIncludeTextRenderer(wikiFormattedTextComponent, ctx, enableTransientMacros) ); try { parser.parse(); } catch (RecognitionException rex) { // Swallow parsing errors, we don't really care here... } catch (ANTLRException ex) { // All other errors are fatal; throw new RuntimeException(ex); } } // Some utilities... private static UIComponent findChildByTagId(UIComponent parent, String id) { Iterator itr = parent.getFacetsAndChildren(); while (itr.hasNext()) { UIComponent c = (UIComponent) itr.next(); String cid = (String) c.getAttributes().get(MARK); if (id.equals(cid)) { return c; } } return null; } private void setAttribute(FaceletContext ctx, UIComponent cmp, String name) { setAttribute(ctx, cmp, name, null); } private void setAttribute(FaceletContext ctx, UIComponent cmp, String name, Object defaultValue) { TagAttribute attribute = this.getAttribute(name); if (attribute != null) { Object o = attribute.getObject(ctx); if (o == null && defaultValue == null) { throw new IllegalArgumentException("Attribute '" + name + "' resolved to null and no default value specified"); } else if (o == null) { cmp.getAttributes().put(name, defaultValue); } else { cmp.getAttributes().put(name, o); } } } }