private static GroovyObject lookupCachedTagLib( GrailsWebRequest webRequest, TagLibraryLookup gspTagLibraryLookup, String namespace, String tagName) { if (webRequest != null) { // caches the taglibs in request context. is this a good idea or not? String tagKey = namespace + ":" + tagName; Map<String, GroovyObject> tagLibCache = (Map<String, GroovyObject>) webRequest.getCurrentRequest().getAttribute(REQUEST_TAGLIB_CACHE); GroovyObject tagLib = null; if (tagLibCache == null) { tagLibCache = new HashMap<String, GroovyObject>(); webRequest.getCurrentRequest().setAttribute(REQUEST_TAGLIB_CACHE, tagLibCache); } else { tagLib = tagLibCache.get(tagKey); } if (tagLib == null) { tagLib = gspTagLibraryLookup.lookupTagLibrary(namespace, tagName); if (tagLib != null) { tagLibCache.put(tagKey, tagLib); } } return tagLib; } else { return gspTagLibraryLookup != null ? gspTagLibraryLookup.lookupTagLibrary(namespace, tagName) : null; } }
/** * Looks up namespaces on missing property * * @param instance The instance * @param propertyName The property name * @return The namespace or a MissingPropertyException */ public Object propertyMissing(Object instance, String propertyName) { TagLibraryLookup lookup = getTagLibraryLookup(); NamespacedTagDispatcher namespacedTagDispatcher = lookup.lookupNamespaceDispatcher(propertyName); if (namespacedTagDispatcher != null) { if (!developmentMode) { WebMetaUtils.registerPropertyMissingForTag( GrailsMetaClassUtils.getMetaClass(instance), propertyName, namespacedTagDispatcher); } return namespacedTagDispatcher; } throw new MissingPropertyException(propertyName, instance.getClass()); }
/** * Method missing implementation that handles tag invocation by method name * * @param instance The instance * @param methodName The method name * @param argsObject The arguments * @return The result */ public Object methodMissing(Object instance, String methodName, Object argsObject) { Object[] args = argsObject instanceof Object[] ? (Object[]) argsObject : new Object[] {argsObject}; TagLibraryLookup lookup = getTagLibraryLookup(); if (lookup != null) { GroovyObject tagLibrary = lookup.lookupTagLibrary(GroovyPage.DEFAULT_NAMESPACE, methodName); if (tagLibrary != null) { if (!developmentMode) { MetaClass controllerMc = GrailsMetaClassUtils.getMetaClass(instance); WebMetaUtils.registerMethodMissingForTags( controllerMc, lookup, GroovyPage.DEFAULT_NAMESPACE, methodName); } List<MetaMethod> respondsTo = tagLibrary.getMetaClass().respondsTo(tagLibrary, methodName, args); if (respondsTo.size() > 0) { return respondsTo.get(0).invoke(tagLibrary, args); } } } throw new MissingMethodException(methodName, instance.getClass(), args); }
public Object getProperty(String property) { if (OUT.equals(property)) return out; // in GSP we assume if a property doesn't exist that // it is null rather than throw an error this works nicely // with the Groovy Truth if (BINDING.equals(property)) return getBinding(); Object value = getBinding().getVariables().get(property); if (value != null) { return value; } if (value == null) { MetaProperty mp = getMetaClass().getMetaProperty(property); if (mp != null) { return mp.getProperty(this); } } if (value == null) { value = gspTagLibraryLookup != null ? gspTagLibraryLookup.lookupNamespaceDispatcher(property) : null; if (value == null && jspTags.containsKey(property)) { TagLibraryResolver tagResolver = getTagLibraryResolver(); String uri = (String) jspTags.get(property); if (uri != null) value = tagResolver.resolveTagLibrary(uri); } if (value != null) { // cache lookup for next execution getBinding().setVariable(property, value); } } return value; }
public static Object captureTagOutput( TagLibraryLookup gspTagLibraryLookup, String namespace, String tagName, Map attrs, Object body, GrailsWebRequest webRequest) { if (!(attrs instanceof GroovyPageAttributes)) { attrs = new GroovyPageAttributes(attrs); } GroovyObject tagLib = lookupCachedTagLib(webRequest, gspTagLibraryLookup, namespace, tagName); boolean preferSubChunkWhenWritingToOtherBuffer = resolvePreferSubChunk(namespace, tagName); Closure actualBody = createOutputCapturingClosure( tagLib, body, webRequest, preferSubChunkWhenWritingToOtherBuffer); final GroovyPageTagWriter out = new GroovyPageTagWriter(preferSubChunkWhenWritingToOtherBuffer); try { GroovyPageOutputStack.currentStack().push(out); Object tagLibProp = tagLib.getProperty(tagName); // retrieve tag lib and create wrapper writer if (tagLibProp instanceof Closure) { Closure tag = (Closure) ((Closure) tagLibProp).clone(); Object bodyResult = null; if (tag.getParameterTypes().length == 1) { bodyResult = tag.call(new Object[] {attrs}); if (actualBody != null && actualBody != EMPTY_BODY_CLOSURE) { Object bodyResult2 = actualBody.call(); if (bodyResult2 != null) { out.print(bodyResult2); } } } else if (tag.getParameterTypes().length == 2) { bodyResult = tag.call(new Object[] {attrs, actualBody}); } else { throw new GrailsTagException( "Tag [" + tagName + "] does not specify expected number of params in tag library [" + tagLib.getClass().getName() + "]"); } boolean returnsObject = gspTagLibraryLookup.doesTagReturnObject(namespace, tagName); if (returnsObject && bodyResult != null && !(bodyResult instanceof Writer)) { return bodyResult; } // add some method to always return string, configurable? return out.getBuffer(); } else { throw new GrailsTagException( "Tag [" + tagName + "] does not exist in tag library [" + tagLib.getClass().getName() + "]"); } } finally { GroovyPageOutputStack.currentStack().pop(); } }
public void invokeTag( String tagName, String tagNamespace, int lineNumber, Map attrs, Closure body) { // TODO custom namespace stuff needs to be generalized and pluggable if (tagNamespace.equals(TEMPLATE_NAMESPACE)) { final String tmpTagName = tagName; final Map tmpAttrs = attrs; tagName = "render"; tagNamespace = DEFAULT_NAMESPACE; attrs = new HashMap() { { put("model", tmpAttrs); put("template", tmpTagName); } }; } else if (tagNamespace.equals(LINK_NAMESPACE)) { final String tmpTagName = tagName; final Map tmpAttrs = attrs; tagName = "link"; tagNamespace = DEFAULT_NAMESPACE; attrs = new HashMap() { { if (tmpAttrs.size() > 0) { put("params", tmpAttrs); } put("mapping", tmpTagName); } }; } try { GroovyObject tagLib = getTagLib(tagNamespace, tagName); if (tagLib != null || gspTagLibraryLookup.hasNamespace(tagNamespace)) { if (tagLib != null) { boolean returnsObject = gspTagLibraryLookup.doesTagReturnObject(tagNamespace, tagName); Object tagLibProp = tagLib.getProperty(tagName); if (tagLibProp instanceof Closure) { Closure tag = (Closure) ((Closure) tagLibProp).clone(); Object tagresult = null; // GSP<->Sitemesh integration requires that the body or head subchunk isn't written to // output boolean preferSubChunkWhenWritingToOtherBuffer = resolvePreferSubChunk(tagNamespace, tagName); if (body instanceof GroovyPageTagBody && preferSubChunkWhenWritingToOtherBuffer) { ((GroovyPageTagBody) body).setPreferSubChunkWhenWritingToOtherBuffer(true); } switch (tag.getParameterTypes().length) { case 1: tagresult = tag.call(new Object[] {attrs}); if (returnsObject && tagresult != null && !(tagresult instanceof Writer)) { out.print(tagresult); } if (body != null && body != EMPTY_BODY_CLOSURE) { body.call(); } break; case 2: if (tag.getParameterTypes().length == 2) { tagresult = tag.call(new Object[] {attrs, (body != null) ? body : EMPTY_BODY_CLOSURE}); if (returnsObject && tagresult != null && !(tagresult instanceof Writer)) { out.print(tagresult); } } break; } } else { throw new GrailsTagException( "Tag [" + tagName + "] does not exist in tag library [" + tagLib.getClass().getName() + "]", getGroovyPageFileName(), lineNumber); } } else { throw new GrailsTagException( "Tag [" + tagName + "] does not exist. No tag library found for namespace: " + tagNamespace, getGroovyPageFileName(), lineNumber); } } else { out.append('<').append(tagNamespace).append(':').append(tagName); for (Object o : attrs.entrySet()) { Map.Entry entry = (Map.Entry) o; out.append(' '); out.append(entry.getKey()).append('='); String value = String.valueOf(entry.getValue()); // handle attribute value quotes & possible escaping " -> " boolean containsQuotes = (value.indexOf('"') > -1); boolean containsSingleQuote = (value.indexOf('\'') > -1); if (containsQuotes && !containsSingleQuote) { out.append('\'').append(value).append('\''); } else if (containsQuotes & containsSingleQuote) { out.append('\"').append(value.replaceAll("\"", """)).append('\"'); } else { out.append('\"').append(value).append('\"'); } } out.append('>'); if (body != null) { Object bodyOutput = body.call(); if (bodyOutput != null) out.print(bodyOutput); } out.append("</").append(tagNamespace).append(':').append(tagName).append('>'); } } catch (Throwable e) { if (LOG.isTraceEnabled()) { LOG.trace("Full exception for problem at " + getGroovyPageFileName() + ":" + lineNumber, e); } // The capture* tags are internal tags and not to be displayed to the user // hence we don't wrap the exception and simple rethrow it if (tagName.matches("capture(Body|Head|Meta|Title|Component)")) { RuntimeException rte = GrailsExceptionResolver.getFirstRuntimeException(e); if (rte == null) { throwRootCause(tagName, tagNamespace, lineNumber, e); } else { throw rte; } } else { throwRootCause(tagName, tagNamespace, lineNumber, e); } } }