diff --git a/grails/src/java/grails/test/GroovyPagesTestCase.groovy b/grails/src/java/grails/test/GroovyPagesTestCase.groovy index b91bbc560..fe47c430e 100644 --- a/grails/src/java/grails/test/GroovyPagesTestCase.groovy +++ b/grails/src/java/grails/test/GroovyPagesTestCase.groovy @@ -16,6 +16,7 @@ package grails.test import org.codehaus.groovy.grails.web.pages.GroovyPagesTemplateEngine import org.springframework.web.context.request.RequestContextHolder +import org.codehaus.groovy.grails.web.util.GrailsPrintWriter /** * A test harness that eases testing of GSP and tag libraries for Grails @@ -53,7 +54,7 @@ class GroovyPagesTestCase extends GroovyTestCase { def w = t.make(params) def sw = new StringWriter() - def out = new PrintWriter(sw) + def out = new GrailsPrintWriter(sw) webRequest.out = out w.writeTo(out) @@ -76,7 +77,7 @@ class GroovyPagesTestCase extends GroovyTestCase { def w = t.make(params) def sw = new StringWriter() - def out = new PrintWriter(sw) + def out = new GrailsPrintWriter(sw) webRequest.out = out w.writeTo(out) diff --git a/grails/src/java/grails/util/GrailsUtil.java b/grails/src/java/grails/util/GrailsUtil.java index e6aacb18f..2d15b3109 100644 --- a/grails/src/java/grails/util/GrailsUtil.java +++ b/grails/src/java/grails/util/GrailsUtil.java @@ -18,6 +18,17 @@ import groovy.lang.GroovyShell; import groovy.lang.Writable; import groovy.util.slurpersupport.GPathResult; + +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintWriter; +import java.io.Writer; +import java.util.ArrayList; +import java.util.List; +import java.util.jar.Attributes; +import java.util.jar.Manifest; + +import org.apache.commons.io.IOUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.codehaus.groovy.grails.commons.ApplicationAttributes; @@ -34,14 +45,6 @@ import org.springframework.mock.web.MockServletContext; import org.springframework.util.Assert; -import java.io.IOException; -import java.io.PrintWriter; -import java.io.Writer; -import java.util.ArrayList; -import java.util.List; -import java.util.jar.Attributes; -import java.util.jar.Manifest; - /** * * Grails utility methods for command line and GUI applications @@ -77,34 +80,42 @@ public class GrailsUtil { }; static { - PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); - String version = null; - try { - Resource[] manifests = resolver.getResources("classpath*:META-INF/MANIFEST.MF"); - Manifest grailsManifest = null; - for (int i = 0; i < manifests.length; i++) { - Resource r = manifests[i]; - Manifest mf = new Manifest(r.getInputStream()); - String implTitle = mf.getMainAttributes().getValue(Attributes.Name.IMPLEMENTATION_TITLE); - if(!isBlank(implTitle) && implTitle.equals(GRAILS_IMPLEMENTATION_TITLE)) { - grailsManifest = mf; - break; - } - } - - if(grailsManifest != null) { - version = grailsManifest.getMainAttributes().getValue(Attributes.Name.IMPLEMENTATION_VERSION); - } - - if(isBlank(version)) { - LOG.error("Unable to read Grails version from MANIFEST.MF. Are you sure the grails-core jar is on the classpath? " ); - version = "Unknown"; - } - } catch (IOException e) { - version = "Unknown"; - LOG.error("Unable to read Grails version from MANIFEST.MF. Are you sure it the grails-core jar is on the classpath? " + e.getMessage(), e); + String version = GrailsUtil.class.getPackage().getImplementationVersion(); + if(version==null || isBlank(version)) { + PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); + try { + Resource[] manifests = resolver.getResources("classpath*:META-INF/MANIFEST.MF"); + Manifest grailsManifest = null; + for (int i = 0; i < manifests.length; i++) { + Resource r = manifests[i]; + InputStream inputStream = null; + Manifest mf = null; + try { + inputStream = r.getInputStream(); + mf = new Manifest(inputStream); + } finally { + IOUtils.closeQuietly(inputStream); + } + String implTitle = mf.getMainAttributes().getValue(Attributes.Name.IMPLEMENTATION_TITLE); + if(!isBlank(implTitle) && implTitle.equals(GRAILS_IMPLEMENTATION_TITLE)) { + grailsManifest = mf; + break; + } + } + + if(grailsManifest != null) { + version = grailsManifest.getMainAttributes().getValue(Attributes.Name.IMPLEMENTATION_VERSION); + } + + if(isBlank(version)) { + LOG.error("Unable to read Grails version from MANIFEST.MF. Are you sure the grails-core jar is on the classpath? " ); + version = "Unknown"; + } + } catch (IOException e) { + version = "Unknown"; + LOG.error("Unable to read Grails version from MANIFEST.MF. Are you sure it the grails-core jar is on the classpath? " + e.getMessage(), e); + } } - GRAILS_VERSION = version; } diff --git a/grails/src/java/org/codehaus/groovy/grails/commons/DefaultArtefactInfo.java b/grails/src/java/org/codehaus/groovy/grails/commons/DefaultArtefactInfo.java index 2edef3b0c..4e5d53f82 100644 --- a/grails/src/java/org/codehaus/groovy/grails/commons/DefaultArtefactInfo.java +++ b/grails/src/java/org/codehaus/groovy/grails/commons/DefaultArtefactInfo.java @@ -103,11 +103,11 @@ public Map getGrailsClassesByName() { return this.grailsClassesByName; } - public synchronized GrailsClass getGrailsClass(String name) { + public GrailsClass getGrailsClass(String name) { return this.grailsClassesByName.get(name); } - public synchronized GrailsClass getGrailsClassByLogicalPropertyName(String logicalName) { + public GrailsClass getGrailsClassByLogicalPropertyName(String logicalName) { return logicalPropertyNameToClassMap.get(logicalName); } diff --git a/grails/src/java/org/codehaus/groovy/grails/commons/metaclass/GenericDynamicProperty.java b/grails/src/java/org/codehaus/groovy/grails/commons/metaclass/GenericDynamicProperty.java index 462942e75..f1fc729b5 100644 --- a/grails/src/java/org/codehaus/groovy/grails/commons/metaclass/GenericDynamicProperty.java +++ b/grails/src/java/org/codehaus/groovy/grails/commons/metaclass/GenericDynamicProperty.java @@ -20,6 +20,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; /** * A generic dyanmic property for any type @@ -31,7 +32,7 @@ public class GenericDynamicProperty extends AbstractDynamicProperty { private Class type; private boolean readyOnly; - private Map propertyToInstanceMap = Collections.synchronizedMap(new HashMap()); + private Map propertyToInstanceMap = new ConcurrentHashMap(); private Object initialValue; private FunctionCallback initialValueGenerator; diff --git a/grails/src/java/org/codehaus/groovy/grails/commons/metaclass/WeakGenericDynamicProperty.java b/grails/src/java/org/codehaus/groovy/grails/commons/metaclass/WeakGenericDynamicProperty.java index 2e25b3819..b54789242 100644 --- a/grails/src/java/org/codehaus/groovy/grails/commons/metaclass/WeakGenericDynamicProperty.java +++ b/grails/src/java/org/codehaus/groovy/grails/commons/metaclass/WeakGenericDynamicProperty.java @@ -18,8 +18,10 @@ import groovy.lang.MissingPropertyException; import org.apache.commons.collections.map.ReferenceMap; +import java.lang.ref.SoftReference; import java.util.Collections; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; /** * A generic dyanmic property for any type used a soft hashmap implementation for generic properties @@ -32,7 +34,7 @@ public class WeakGenericDynamicProperty extends AbstractDynamicProperty { private Class type; private boolean readyOnly; - private Map propertyToInstanceMap = Collections.synchronizedMap(new ReferenceMap(ReferenceMap.SOFT,ReferenceMap.SOFT,true)); + private Map> propertyToInstanceMap = new ConcurrentHashMap>(); private Object initialValue; private FunctionCallback initialValueGenerator; @@ -82,16 +84,19 @@ public WeakGenericDynamicProperty(String propertyName, Class type, FunctionCallb public Object get(Object object) { String propertyKey = System.identityHashCode(object) + getPropertyName(); - if(propertyToInstanceMap.containsKey(propertyKey)) { - return propertyToInstanceMap.get(propertyKey); + + SoftReference ref=propertyToInstanceMap.get(propertyKey); + Object val=(ref != null)?ref.get():null; + if(val != null) { + return val; } else if (this.initialValueGenerator != null) { final Object value = this.initialValueGenerator.execute(object); - propertyToInstanceMap.put(propertyKey, value); + propertyToInstanceMap.put(propertyKey, new SoftReference(value)); return value; } else if(this.initialValue != null) { - propertyToInstanceMap.put(propertyKey, this.initialValue); + propertyToInstanceMap.put(propertyKey, new SoftReference(this.initialValue)); return this.initialValue; } return null; @@ -100,7 +105,7 @@ else if(this.initialValue != null) { public void set(Object object, Object newValue) { if(!readyOnly) { if(this.type.isInstance(newValue)) - propertyToInstanceMap.put(String.valueOf(System.identityHashCode(object)) + getPropertyName(), newValue ); + propertyToInstanceMap.put(String.valueOf(System.identityHashCode(object)) + getPropertyName(), new SoftReference(newValue) ); else if(newValue != null) throw new MissingPropertyException("Property '"+this.getPropertyName()+"' for object '"+object.getClass()+"' cannot be set with value '"+newValue+"'. Incorrect type.",object.getClass()); } diff --git a/grails/src/java/org/codehaus/groovy/grails/plugins/web/GroovyPagesGrailsPlugin.groovy b/grails/src/java/org/codehaus/groovy/grails/plugins/web/GroovyPagesGrailsPlugin.groovy index cf8321dc6..2fcf0cb8d 100644 --- a/grails/src/java/org/codehaus/groovy/grails/plugins/web/GroovyPagesGrailsPlugin.groovy +++ b/grails/src/java/org/codehaus/groovy/grails/plugins/web/GroovyPagesGrailsPlugin.groovy @@ -17,11 +17,16 @@ package org.codehaus.groovy.grails.plugins.web +import groovy.lang.MetaClass; + +import org.codehaus.groovy.grails.web.pages.TagLibraryLookup; +import org.springframework.context.ApplicationContext; import org.springframework.web.context.request.RequestContextHolder as RCH import org.codehaus.groovy.grails.commons.GrailsClassUtils as GCU import org.codehaus.groovy.grails.plugins.web.taglib.* import org.springframework.context.ApplicationContext +import org.codehaus.groovy.grails.web.pages.GroovyPageBinding; import org.codehaus.groovy.grails.web.pages.GroovyPagesTemplateEngine import org.codehaus.groovy.grails.web.pages.ext.jsp.TagLibraryResolver import org.codehaus.groovy.grails.web.pages.TagLibraryLookup @@ -163,6 +168,8 @@ public class GroovyPagesGrailsPlugin { for(taglib in application.tagLibClasses) { "${taglib.fullName}"(taglib.clazz) { bean -> bean.autowire = true + // Taglib scoping support could be easily added here. Scope could be based on a static field in the taglib class. + //bean.scope = 'request' } } @@ -230,8 +237,7 @@ public class GroovyPagesGrailsPlugin { WebMetaUtils.registerCommonWebProperties(mc, application) for(tag in taglib.tagNames) { - def tagLibrary = gspTagLibraryLookup.lookupTagLibrary(namespace, tag) - WebMetaUtils.registerMethodMissingForTags(mc, tagLibrary, tag) + WebMetaUtils.registerMethodMissingForTags(mc, gspTagLibraryLookup, namespace, tag) } mc.throwTagError = {String message -> @@ -245,19 +251,19 @@ public class GroovyPagesGrailsPlugin { mc.getPageScope = {-> def request = RequestContextHolder.currentRequestAttributes().currentRequest - def binding = request[GrailsApplicationAttributes.PAGE_SCOPE] + def binding = request.getAttribute(GrailsApplicationAttributes.PAGE_SCOPE) if (!binding) { - binding = new Binding() - request[GrailsApplicationAttributes.PAGE_SCOPE] = binding + binding = new GroovyPageBinding() + request.setAttribute(GrailsApplicationAttributes.PAGE_SCOPE, binding) } binding } mc.getOut = {-> - RequestContextHolder.currentRequestAttributes().out + org.codehaus.groovy.grails.web.pages.GroovyPageOutputStack.currentWriter() } mc.setOut = {Writer newOut -> - RequestContextHolder.currentRequestAttributes().out = newOut + org.codehaus.groovy.grails.web.pages.GroovyPageOutputStack.currentStack().push(newOut,true) } mc.propertyMissing = {String name -> def result = gspTagLibraryLookup.lookupNamespaceDispatcher(name) @@ -279,10 +285,15 @@ public class GroovyPagesGrailsPlugin { } mc.methodMissing = {String name, args -> + def usednamespace = namespace def tagLibrary = gspTagLibraryLookup.lookupTagLibrary(namespace, name) - if(!tagLibrary) tagLibrary = gspTagLibraryLookup.lookupTagLibrary(GroovyPage.DEFAULT_NAMESPACE, name) + if(!tagLibrary) { + tagLibrary = gspTagLibraryLookup.lookupTagLibrary(GroovyPage.DEFAULT_NAMESPACE, name) + usednamespace = GroovyPage.DEFAULT_NAMESPACE + } if(tagLibrary) { - WebMetaUtils.registerMethodMissingForTags(mc, tagLibrary, name) + WebMetaUtils.registerMethodMissingForTags(mc, gspTagLibraryLookup, usednamespace, name) + //WebMetaUtils.registerMethodMissingForTags(mc, tagLibrary, name) } if (mc.respondsTo(delegate, name, args)) { return mc.invokeMethod(delegate, name, args) @@ -291,7 +302,6 @@ public class GroovyPagesGrailsPlugin { throw new MissingMethodException(name, delegate.class, args) } } - ctx.getBean(taglib.fullName).metaClass = mc } } @@ -305,7 +315,7 @@ public class GroovyPagesGrailsPlugin { def tagLibrary = lookup.lookupTagLibrary(GroovyPage.DEFAULT_NAMESPACE, name) if (tagLibrary) { MetaClass controllerMc = delegate.class.metaClass - WebMetaUtils.registerMethodMissingForTags(controllerMc,tagLibrary, name) + WebMetaUtils.registerMethodMissingForTags(controllerMc, lookup, GroovyPage.DEFAULT_NAMESPACE, name) if(controllerMc.respondsTo(delegate, name, args)) { return controllerMc.invokeMethod(delegate, name, args) } @@ -330,6 +340,7 @@ public class GroovyPagesGrailsPlugin { def beans = beans { "$beanName"(taglibClass.getClazz()) {bean -> bean.autowire = true + //bean.scope = 'request' } } beans.registerBeans(event.ctx) diff --git a/grails/src/java/org/codehaus/groovy/grails/plugins/web/taglib/JavascriptTagLib.groovy b/grails/src/java/org/codehaus/groovy/grails/plugins/web/taglib/JavascriptTagLib.groovy index 4dce4a349..668df9d24 100644 --- a/grails/src/java/org/codehaus/groovy/grails/plugins/web/taglib/JavascriptTagLib.groovy +++ b/grails/src/java/org/codehaus/groovy/grails/plugins/web/taglib/JavascriptTagLib.groovy @@ -404,10 +404,10 @@ class PrototypeProvider implements JavascriptProvider { } if(attrs.url) { - url = taglib.createLink(attrs.url) + url = taglib.createLink(attrs.url)?.toString() } else { - url = taglib.createLink(attrs) + url = taglib.createLink(attrs)?.toString() } if(!attrs.params) attrs.params = [:] diff --git a/grails/src/java/org/codehaus/groovy/grails/plugins/web/taglib/ValidationTagLib.groovy b/grails/src/java/org/codehaus/groovy/grails/plugins/web/taglib/ValidationTagLib.groovy index 4fce4f8f4..97da29591 100644 --- a/grails/src/java/org/codehaus/groovy/grails/plugins/web/taglib/ValidationTagLib.groovy +++ b/grails/src/java/org/codehaus/groovy/grails/plugins/web/taglib/ValidationTagLib.groovy @@ -148,6 +148,10 @@ class ValidationTagLib { * Loops through each error for either field or global errors */ def eachError = { attrs, body -> + eachErrorInternal(attrs,body) + } + + def eachErrorInternal(attrs, body) { def errorsList = extractErrors(attrs) def var = attrs.var def field = attrs['field'] @@ -167,9 +171,11 @@ class ValidationTagLib { if(var) { out << body([(var):error]) } else { - out << body(error) + out << body(error) } } + + null } /** @@ -184,7 +190,7 @@ class ValidationTagLib { if (codec=='none') codec = '' out << "
    " - out << eachError(attrs, { + out << eachErrorInternal(attrs, { out << "
  • ${message(error:it, encodeAs:codec)}
  • " } ) @@ -192,11 +198,11 @@ class ValidationTagLib { } else if(renderAs.equalsIgnoreCase("xml")) { def mkp = new MarkupBuilder(out) - mkp.errors { - eachError(attrs, { + mkp.errors() { + eachErrorInternal(attrs, { error(object:it.objectName, field:it.field, - message:message(error:it), + message:message(error:it)?.toString(), 'rejected-value':StringEscapeUtils.escapeXml(it.rejectedValue)) }) } diff --git a/grails/src/java/org/codehaus/groovy/grails/web/errors/GrailsWrappedRuntimeException.java b/grails/src/java/org/codehaus/groovy/grails/web/errors/GrailsWrappedRuntimeException.java index 080fd2cb6..fb3eb5a86 100644 --- a/grails/src/java/org/codehaus/groovy/grails/web/errors/GrailsWrappedRuntimeException.java +++ b/grails/src/java/org/codehaus/groovy/grails/web/errors/GrailsWrappedRuntimeException.java @@ -23,6 +23,7 @@ import org.codehaus.groovy.grails.commons.*; import org.codehaus.groovy.grails.exceptions.GrailsException; import org.codehaus.groovy.grails.exceptions.SourceCodeAware; +import org.codehaus.groovy.grails.web.pages.FastStringWriter; import org.codehaus.groovy.grails.web.pages.GroovyPagesTemplateEngine; import org.codehaus.groovy.grails.web.servlet.DefaultGrailsApplicationAttributes; import org.codehaus.groovy.grails.web.servlet.GrailsApplicationAttributes; @@ -68,10 +69,9 @@ public class GrailsWrappedRuntimeException extends GrailsException { public GrailsWrappedRuntimeException(ServletContext servletContext, Throwable t) { super(t.getMessage(), t); this.cause = t; - StringWriter sw = new StringWriter(); - PrintWriter pw = new PrintWriter(sw); + FastStringWriter pw = new FastStringWriter(); cause.printStackTrace(pw); - this.stackTrace = sw.toString(); + this.stackTrace = pw.toString(); while(cause.getCause()!=cause) { if(cause.getCause() == null) { diff --git a/grails/src/java/org/codehaus/groovy/grails/web/pages/FastStringWriter.java b/grails/src/java/org/codehaus/groovy/grails/web/pages/FastStringWriter.java index 0a9233ef4..a41e934d7 100644 --- a/grails/src/java/org/codehaus/groovy/grails/web/pages/FastStringWriter.java +++ b/grails/src/java/org/codehaus/groovy/grails/web/pages/FastStringWriter.java @@ -20,9 +20,9 @@ import org.codehaus.groovy.grails.web.util.StreamCharBuffer; /** - * Java's default StringWriter uses a StringBuffer which is synchronized. - * This implementation doesn't use synchronization - * + * Java's default StringWriter uses a StringBuffer which is synchronized. This + * implementation doesn't use synchronization + * * @author Graeme Rocher * @author Lari Hotari * @since 1.1 @@ -30,24 +30,33 @@ * Created: Jan 20, 2009 */ public class FastStringWriter extends GrailsPrintWriter { - private StreamCharBuffer streamBuffer; - - public FastStringWriter() { - super(new StreamCharBuffer().getWriter()); - this.streamBuffer = ((StreamCharBuffer.StreamCharBufferWriter)this.out).getBuffer(); - } - - protected FastStringWriter(Object o) { - this(); - this.print(o); - } - - @Override - public String toString() { - return streamBuffer.toString(); - } - - public Reader getReader() { - return streamBuffer.getReader(); - } + private final StreamCharBuffer streamBuffer; + + public FastStringWriter() { + super(new StreamCharBuffer().getWriter()); + this.streamBuffer = ((StreamCharBuffer.StreamCharBufferWriter) this.out) + .getBuffer(); + } + + protected FastStringWriter(Object o) { + this(); + this.print(o); + } + + public StreamCharBuffer getBuffer() { + return streamBuffer; + } + + @Override + public String toString() { + return this.getValue(); + } + + public String getValue() { + return streamBuffer.toString(); + } + + public Reader getReader() { + return streamBuffer.getReader(); + } } diff --git a/grails/src/java/org/codehaus/groovy/grails/web/pages/GSPResponseWriter.java b/grails/src/java/org/codehaus/groovy/grails/web/pages/GSPResponseWriter.java index 646b4d660..6b6c9a221 100644 --- a/grails/src/java/org/codehaus/groovy/grails/web/pages/GSPResponseWriter.java +++ b/grails/src/java/org/codehaus/groovy/grails/web/pages/GSPResponseWriter.java @@ -115,7 +115,7 @@ private GSPResponseWriter(Writer activeWriter, final ServletResponse response, B private GSPResponseWriter(Writer activeWriter) { super(activeWriter); } - + /** * Close the stream. * @see #checkError() diff --git a/grails/src/java/org/codehaus/groovy/grails/web/pages/GSPWriter.java b/grails/src/java/org/codehaus/groovy/grails/web/pages/GSPWriter.java index c54ed1bfb..e6f937ee6 100644 --- a/grails/src/java/org/codehaus/groovy/grails/web/pages/GSPWriter.java +++ b/grails/src/java/org/codehaus/groovy/grails/web/pages/GSPWriter.java @@ -41,8 +41,11 @@ public void write(char buf[], int off, int len) { } public void printlnToResponse(String s) { - if(s == null) s = "''"; - super.print("out.print("); + if(s==null || s.trim().length()==0) { + return; + } + parse.flushTagBuffering(); + super.print("printToOut("); super.print(s); super.print(")"); println(); diff --git a/grails/src/java/org/codehaus/groovy/grails/web/pages/GroovyPage.java b/grails/src/java/org/codehaus/groovy/grails/web/pages/GroovyPage.java index 5416b6959..8a08f7874 100644 --- a/grails/src/java/org/codehaus/groovy/grails/web/pages/GroovyPage.java +++ b/grails/src/java/org/codehaus/groovy/grails/web/pages/GroovyPage.java @@ -19,19 +19,26 @@ import groovy.lang.GroovyObject; import groovy.lang.MetaProperty; import groovy.lang.Script; + +import java.io.PrintWriter; +import java.io.Writer; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.codehaus.groovy.grails.web.errors.GrailsExceptionResolver; import org.codehaus.groovy.grails.web.pages.exceptions.GroovyPagesException; import org.codehaus.groovy.grails.web.pages.ext.jsp.TagLibraryResolver; import org.codehaus.groovy.grails.web.servlet.mvc.GrailsWebRequest; -import org.codehaus.groovy.grails.web.taglib.GroovyPageTagWriter; import org.codehaus.groovy.grails.web.taglib.GroovyPageTagBody; +import org.codehaus.groovy.grails.web.taglib.GroovyPageTagWriter; import org.codehaus.groovy.grails.web.taglib.exceptions.GrailsTagException; -import org.codehaus.groovy.grails.web.errors.GrailsExceptionResolver; - -import java.io.IOException; -import java.io.Writer; -import java.util.*; +import org.codehaus.groovy.grails.web.util.GrailsPrintWriter; +import org.codehaus.groovy.grails.web.util.StreamCharBuffer; /** * NOTE: Based on work done by on the GSP standalone project (https://gsp.dev.java.net/) @@ -81,31 +88,46 @@ public abstract class GroovyPage extends Script { add(FLASH); add(PLUGIN_CONTEXT_PATH); add(PAGE_SCOPE); - }}; + private static final String BINDING = "binding"; private static final String BLANK_STRING = ""; private Map jspTags = Collections.EMPTY_MAP; private TagLibraryResolver jspTagLibraryResolver; private TagLibraryLookup gspTagLibraryLookup; private String[] htmlParts; + private GrailsPrintWriter out; + private GroovyPageOutputStack outputStack; + private GrailsWebRequest webRequest; - private static final Closure EMPTY_BODY_CLOSURE = new Closure(null) { + private static ThreadLocal activeWriterCache=new ThreadLocal(); + + public static final class ConstantClosure extends Closure { + private static final long serialVersionUID = 1L; + final Object retval; + + public ConstantClosure(Object retval) { + super(null); + this.retval=retval; + } + public Object doCall(Object obj) { - return BLANK_STRING; + return retval; } public Object doCall() { - return BLANK_STRING; + return retval; } public Object doCall(Object[] args) { - return BLANK_STRING; + return retval; } public Object call(Object[] args) { - return BLANK_STRING; + return retval; } - }; - - + } + + protected static final Closure EMPTY_BODY_CLOSURE = new ConstantClosure(BLANK_STRING); + + public GroovyPage() { super(); init(); @@ -113,6 +135,33 @@ public GroovyPage() { protected void init() { + } + + public Writer getOut() { + return out; + } + + public void setOut(Writer newWriter) { + throw new IllegalStateException("Setting out in page isn't allowed."); + } + + public void initRun(Writer target, GrailsWebRequest webRequest) { + this.outputStack = GroovyPageOutputStack.currentStack(true, target, false, true); + this.out = outputStack.getProxyWriter(); + this.webRequest = webRequest; + if(webRequest != null) { + this.webRequest.setOut(this.out); + } + getBinding().setVariable(OUT, this.out); + } + + public void cleanup() { + outputStack.pop(true); + } + + protected Closure createClosureForHtmlPart(int partNumber) { + final String htmlPart = htmlParts[partNumber]; + return new ConstantClosure(htmlPart); } /** @@ -162,25 +211,39 @@ public Object evaluate(String exprText, int lineNumber, Object outerIt, Closure 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(); - MetaProperty mp = getMetaClass().getMetaProperty(property); - if(mp!= null)return mp.getProperty(this); - Object value = getBinding().getVariables().get(property); - if(value == null) { - value = gspTagLibraryLookup!=null ? gspTagLibraryLookup.lookupNamespaceDispatcher(property) : null; + if(value != null) { + return value; } - 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) { + 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; } @@ -232,9 +295,6 @@ public void invokeTag(String tagName, String tagNamespace, int lineNumber, Map a }}; } - final GrailsWebRequest webRequest = (GrailsWebRequest)getBinding().getVariable(WEB_REQUEST); - final Writer out = webRequest.getOut(); - try { if( gspTagLibraryLookup.hasNamespace(tagNamespace) ) { @@ -248,7 +308,7 @@ public void invokeTag(String tagName, String tagNamespace, int lineNumber, Map a case 1: tag.call( new Object[]{ attrs }); - if(body != null) { + if(body != null && body != EMPTY_BODY_CLOSURE) { body.call(); } @@ -277,19 +337,18 @@ public void invokeTag(String tagName, String tagNamespace, int lineNumber, Map a plainTag.append(" ").append(entry.getKey()).append("=\"").append(entry.getValue()).append("\""); } plainTag.append(">"); - try { - out.write(plainTag.toString()); - if(body != null) { - Object bodyOutput = body.call(); - if(bodyOutput != null) out.write(bodyOutput.toString()); - } - out.write(""); - } catch(IOException e) { - throw new GrailsTagException("I/O error invoking tag library closure as method: " + e.getMessage(), getGroovyPageFileName(),0); + out.write(plainTag.toString()); + if(body != null) { + Object bodyOutput = body.call(); + if(bodyOutput != null) out.write(bodyOutput.toString()); } + out.write(""); } } catch(Exception e) { + if(LOG.isTraceEnabled()) { + LOG.trace("Full exception for problem at " + getGroovyPageFileName() + ":" + lineNumber, e); + } Throwable cause = GrailsExceptionResolver.getRootCause(e); if(cause instanceof GrailsTagException) { // catch and rethrow with context @@ -299,10 +358,6 @@ public void invokeTag(String tagName, String tagNamespace, int lineNumber, Map a throw new GrailsTagException("Error executing tag <"+tagNamespace+":"+tagName+">: " + e.getMessage(),e, getGroovyPageFileName(),lineNumber); } } - finally { - getBinding().setVariable(OUT,out); - webRequest.setOut(out); - } } private GroovyObject getTagLib(String tagName) { @@ -328,122 +383,87 @@ public Object invokeMethod(final String methodName, Object args) { Object body = null; GroovyObject tagLib = getTagLib(methodName); if(tagLib != null) { - final GrailsWebRequest webRequest = (GrailsWebRequest)getBinding().getVariable(WEB_REQUEST); - Writer originalOut = webRequest.getOut(); - try { - // get attributes and body closure - if (args instanceof Object[]) { - Object[] argArray = (Object[])args; - if(argArray.length > 0 && argArray[0] instanceof Map) - attrs = (Map)argArray[0]; - if(argArray.length > 1) { - body = argArray[1]; - } + // get attributes and body closure + if (args instanceof Object[]) { + Object[] argArray = (Object[])args; + if(argArray.length > 0 && argArray[0] instanceof Map) + attrs = (Map)argArray[0]; + if(argArray.length > 1) { + body = argArray[1]; } - else if(args instanceof Map) { - attrs = (Map)args; - } - - if(attrs == null) { - attrs = new HashMap(); - } - return captureTagOutput(tagLib,methodName, attrs, body, webRequest); - } - finally { - getBinding().setVariable(OUT,originalOut); - webRequest.setOut(originalOut); + else if(args instanceof Map) { + attrs = (Map)args; } + if(attrs == null) { + attrs = new HashMap(); + } + + return captureTagOutput(tagLib,methodName, attrs, body, webRequest, false); } else { return super.invokeMethod(methodName, args); } } + + public static Object captureTagOutput(GroovyObject tagLib, String methodName, Map attrs, Object body, GrailsWebRequest webRequest) { + return captureTagOutput(tagLib, methodName, attrs, body, webRequest, true); + } - public static String captureTagOutput(GroovyObject tagLib, String methodName, Map attrs, Object body, GrailsWebRequest webRequest) { - Object tagLibProp;// retrieve tag lib and create wrapper writer - Writer originalOut = webRequest.getOut(); - try { - final GroovyPageTagWriter out = new GroovyPageTagWriter(); - webRequest.setOut(out); - - // in a direct invocation the body is expected to return a string - // invoke the body closure and create a new closure that outputs - // to the response writer on each body invokation - Closure actualBody = createTagOutputCapturingClosure(tagLib,methodName, out, body); - - tagLibProp = tagLib.getProperty(methodName); - if(tagLibProp instanceof Closure) { - Closure tag = (Closure) ((Closure)tagLibProp).clone(); - - if(tag.getParameterTypes().length == 1) { - tag.call( new Object[]{ attrs }); - if(actualBody != null) { - actualBody.call(); - } - }else if(tag.getParameterTypes().length == 2) { - tag.call( new Object[] { attrs, actualBody }); - }else { - throw new GrailsTagException("Tag ["+methodName+"] does not specify expected number of params in tag library ["+tagLib.getClass().getName()+"]"); - } - return out.getValue(); - }else { - throw new GrailsTagException("Tag ["+methodName+"] does not exist in tag library ["+tagLib.getClass().getName()+"]"); - } + public static Object captureTagOutput(GroovyObject tagLib, String methodName, Map attrs, Object body, GrailsWebRequest webRequest, boolean mcMethodCall) { + // in a direct invocation the body is expected to return a string + // invoke the body closure and create a new closure that outputs + // to the response writer on each body invocation + Closure actualBody = createTagOutputCapturingClosure(tagLib, body, webRequest); - } - finally { - webRequest.setOut(originalOut); - } + final GroovyPageTagWriter out = new GroovyPageTagWriter(); + try { + GroovyPageOutputStack.currentStack().push(out); + Object tagLibProp = tagLib.getProperty(methodName); // 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(bodyResult==null) { + bodyResult=bodyResult2; + } + } + } else if(tag.getParameterTypes().length == 2) { + bodyResult=tag.call( new Object[] { attrs, actualBody }); + } else { + throw new GrailsTagException("Tag ["+methodName+"] does not specify expected number of params in tag library ["+tagLib.getClass().getName()+"]"); + } + + StreamCharBuffer buffer=out.getBuffer(); + if(buffer.charsAvailable()==0 && bodyResult != null && !(bodyResult instanceof Writer)) { + return bodyResult; + } + // add some method to always return string, configurable? + return buffer.toString(); + }else { + throw new GrailsTagException("Tag ["+methodName+"] does not exist in tag library ["+tagLib.getClass().getName()+"]"); + } + } finally { + GroovyPageOutputStack.currentStack().pop(); + } } - private static Closure createTagOutputCapturingClosure(Object wrappedInstance, final String methodName, final Writer out, final Object body1) { + private static Closure createTagOutputCapturingClosure(Object wrappedInstance, final Object body1, final GrailsWebRequest webRequest) { if(body1==null) { return EMPTY_BODY_CLOSURE; - } - else if(body1 instanceof GroovyPageTagBody) { - return (Closure) body1; + } else if (body1 instanceof GroovyPageTagBody){ + return (GroovyPageTagBody) body1; + } else if (body1 instanceof Closure) { + return new GroovyPageTagBody(wrappedInstance, webRequest, (Closure)body1); + } else { + return new ConstantClosure(body1); } - else { - return new Closure(wrappedInstance) { - public Object doCall(Object obj) { - return call(new Object[] {obj} ); - } - public Object doCall() { - return call(new Object[0]); - } - public Object doCall(Object[] args) { - return call(args); - } - public Object call(Object[] args) { - if(body1 != null) { - Object bodyResponse; - if(body1 instanceof Closure) { - if(args!=null && args.length>0){ - bodyResponse = ((Closure)body1).call(args); - } - else { - bodyResponse = ((Closure)body1).call(); - } - } - else { - bodyResponse = body1; - } - - if(bodyResponse != null && !(bodyResponse instanceof Writer) ){ - try { - out.write(bodyResponse.toString()); - } catch (IOException e) { - throw new GrailsTagException("I/O error invoking tag library closure ["+methodName+"] as method: " + e.getMessage(),e); - } - } - } - return BLANK_STRING; - } - }; - } } /** @@ -456,6 +476,23 @@ public static boolean isReservedName(String name) { return RESERVED_NAMES.contains(name); } + public final void printHtmlPart(final int partNumber) { + out.write(htmlParts[partNumber]); + } + + public final void printToOut(final Object value) { + if(value==null) { + return; + } + if(value instanceof String) { + out.print((String)value); + } else if(value instanceof char[]) { + out.print((char[])value); + } else { + out.print(value); + } + } + /** * Sets the JSP tags used by this GroovyPage instance * @@ -472,5 +509,9 @@ public String[] getHtmlParts() { public void setHtmlParts(String[] htmlParts) { this.htmlParts = htmlParts; } + + public GroovyPageOutputStack getOutputStack() { + return outputStack; + } } // GroovyPage diff --git a/grails/src/java/org/codehaus/groovy/grails/web/pages/GroovyPageBinding.java b/grails/src/java/org/codehaus/groovy/grails/web/pages/GroovyPageBinding.java new file mode 100644 index 000000000..382007b7e --- /dev/null +++ b/grails/src/java/org/codehaus/groovy/grails/web/pages/GroovyPageBinding.java @@ -0,0 +1,27 @@ +/** + * + */ +package org.codehaus.groovy.grails.web.pages; + +import groovy.lang.Binding; + +import java.io.Writer; +import java.util.Map; + +import org.codehaus.groovy.grails.web.servlet.GrailsApplicationAttributes; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; + +public class GroovyPageBinding extends Binding { + public GroovyPageBinding() { + super(); + } + + public GroovyPageBinding(Map variables) { + super(variables); + } + + public GroovyPageBinding(String[] args) { + super(args); + } +} \ No newline at end of file diff --git a/grails/src/java/org/codehaus/groovy/grails/web/pages/GroovyPageOutputStack.java b/grails/src/java/org/codehaus/groovy/grails/web/pages/GroovyPageOutputStack.java new file mode 100644 index 000000000..da2ee4eba --- /dev/null +++ b/grails/src/java/org/codehaus/groovy/grails/web/pages/GroovyPageOutputStack.java @@ -0,0 +1,173 @@ +package org.codehaus.groovy.grails.web.pages; + +import java.io.StringWriter; +import java.io.Writer; +import java.util.Stack; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.codehaus.groovy.grails.web.servlet.GrailsApplicationAttributes; +import org.codehaus.groovy.grails.web.servlet.mvc.GrailsWebRequest; +import org.codehaus.groovy.grails.web.util.GrailsPrintWriter; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; + +public final class GroovyPageOutputStack { + public static final Log log = LogFactory.getLog(GroovyPageOutputStack.class); + + private static final ThreadLocal instances=new ThreadLocal(); + + public static final GroovyPageOutputStack currentStack() { + return currentStack(true); + } + + public static final GroovyPageOutputStack currentStack(boolean allowCreate) { + return currentStack(allowCreate, (Writer)RequestContextHolder.currentRequestAttributes().getAttribute(GrailsApplicationAttributes.OUT, RequestAttributes.SCOPE_REQUEST), true, false); + } + + public static final GroovyPageOutputStack currentStack(boolean allowCreate, Writer topWriter, boolean autoSync, boolean pushTop) { + GroovyPageOutputStack outputStack=instances.get(); + if(outputStack!=null) { + if(pushTop) { + outputStack.push(topWriter); + } + return outputStack; + } else if(allowCreate) { + return createNew(topWriter, autoSync); + } else { + return null; + } + } + + public static final GroovyPageOutputStack createNew(Writer topWriter) { + return createNew(topWriter, false); + } + + private static final GroovyPageOutputStack createNew(Writer topWriter, boolean autoCreated) { + GroovyPageOutputStack instance=new GroovyPageOutputStack(topWriter, autoCreated); + instances.set(instance); + return instance; + } + + public static final void removeCurrentInstance() { + instances.remove(); + } + + public static final Writer currentWriter() { + GroovyPageOutputStack outputStack=currentStack(false); + if(outputStack != null) { + return outputStack.getProxyWriter(); + } else { + return (Writer)RequestContextHolder.currentRequestAttributes().getAttribute(GrailsApplicationAttributes.OUT, RequestAttributes.SCOPE_REQUEST); + } + } + + private Stack stack=new Stack(); + private GroovyPageProxyWriter proxyWriter; + private boolean autoSync; + + private class WriterPair { + Writer originalTarget; + Writer unwrappedTarget; + + WriterPair(Writer originalTarget, Writer unwrappedTarget) { + this.originalTarget = originalTarget; + this.unwrappedTarget = unwrappedTarget; + } + } + + private class GroovyPageProxyWriter extends GrailsPrintWriter { + public GroovyPageProxyWriter() { + super(new StringWriter()); + } + + public void setOut(Writer newOut) { + this.out=newOut; + } + + public GroovyPageOutputStack getOutputStack() { + return GroovyPageOutputStack.this; + } + } + + private GroovyPageOutputStack(Writer topWriter, boolean autoSync) { + this.proxyWriter=new GroovyPageProxyWriter(); + this.autoSync=autoSync; + push(topWriter); + if(!autoSync) { + applyWriterThreadLocals(proxyWriter); + } + } + + private Writer unwrapTargetWriter(Writer targetWriter) { + if(targetWriter instanceof GrailsPrintWriter) { + return ((GrailsPrintWriter)targetWriter).getOut(); + } else { + return targetWriter; + } + } + + public void push(final Writer newWriter) { + push(newWriter, false); + } + + public void push(final Writer newWriter, final boolean checkExisting) { + if(newWriter == proxyWriter) { + stack.push(stack.peek()); + return; + } + if(checkExisting) { + for(WriterPair item : stack) { + if(item.originalTarget==newWriter) { + log.warn("Pushed a writer to stack a second time. Writer type " + newWriter.getClass().getName(), new Exception()); + } + } + } + Writer unwrappedWriter = unwrapTargetWriter(newWriter); + if(unwrappedWriter == proxyWriter) { + stack.push(stack.peek()); + return; + } + stack.push(new WriterPair(newWriter, unwrappedWriter)); + + proxyWriter.setOut(newWriter); + if(autoSync) { + applyWriterThreadLocals(newWriter); + } + } + + public void pop() { + pop(autoSync); + } + + public void pop(boolean forceSync) { + stack.pop(); + if(stack.size() > 0) { + WriterPair pair=stack.peek(); + proxyWriter.setOut(pair.unwrappedTarget); + if(forceSync) { + applyWriterThreadLocals(pair.originalTarget); + } + } + } + + public GroovyPageProxyWriter getProxyWriter() { + return proxyWriter; + } + + public Writer getCurrentOriginalWriter() { + return stack.peek().originalTarget; + } + + public void restoreThreadLocalsToOriginals() { + Writer originalTopWriter=stack.firstElement().originalTarget; + applyWriterThreadLocals(originalTopWriter); + } + + private void applyWriterThreadLocals(Writer writer) { + GrailsWebRequest webRequest = (GrailsWebRequest) RequestContextHolder.currentRequestAttributes(); + if(webRequest != null) { + webRequest.setOut(writer); + } + } +} diff --git a/grails/src/java/org/codehaus/groovy/grails/web/pages/GroovyPageParser.java b/grails/src/java/org/codehaus/groovy/grails/web/pages/GroovyPageParser.java index 17f91045d..0d6b2e3f9 100644 --- a/grails/src/java/org/codehaus/groovy/grails/web/pages/GroovyPageParser.java +++ b/grails/src/java/org/codehaus/groovy/grails/web/pages/GroovyPageParser.java @@ -72,10 +72,15 @@ public class GroovyPageParser implements Tokens { private boolean finalPass = false; private int tagIndex; private Map tagContext; - private List tagMetaStack = new ArrayList(); + private Stack tagMetaStack = new Stack(); private GrailsTagRegistry tagRegistry = GrailsTagRegistry.getInstance(); private Environment environment; private List htmlParts = new ArrayList(); + + Set bodyVarsDefined=new HashSet(); + Map attrsVarsMapDefinition=new HashMap(); + + int closureLevel=0; /* * Set to true when whitespace is currently being saved for later output if @@ -96,7 +101,7 @@ public class GroovyPageParser implements Tokens { private int state; private static final String DEFAULT_CONTENT_TYPE = "text/html;charset=UTF-8"; private int constantCount = 0; - private Map constantsToNames = new HashMap(); + private Map constantsToNumbers = new HashMap(); private final String pageName; public static final String[] DEFAULT_IMPORTS = new String[] { @@ -142,6 +147,9 @@ class TagMeta { boolean hasAttributes; int lineNumber; boolean emptyTag; + int tagIndex; + boolean bufferMode=false; + int bufferPartNumber=-1; public String toString() { return "<" + namespace + ":" + name + ">"; @@ -478,9 +486,44 @@ private void bufferedPrintlnToResponse(String s) { if (currentlyBufferingWhitespace) { whitespaceBuffer.append(s); } else { + flushTagBuffering(); out.printlnToResponse(s); } } + + private void htmlPartPrintlnToResponse(int partNumber) { + if(!tagMetaStack.isEmpty()) { + TagMeta tm=tagMetaStack.peek(); + if(tm.bufferMode && tm.bufferPartNumber==-1) { + tm.bufferPartNumber=partNumber; + return; + } + } + + flushTagBuffering(); + + htmlPartPrintlnRaw(partNumber); + } + + private void htmlPartPrintlnRaw(int partNumber) { + out.print("printHtmlPart("); + out.print(String.valueOf(partNumber)); + out.print(")"); + out.println(); + } + + public void flushTagBuffering() { + if(!tagMetaStack.isEmpty()) { + TagMeta tm=tagMetaStack.peek(); + if(tm.bufferMode) { + writeTagBodyStart(tm); + if(tm.bufferPartNumber != -1) { + htmlPartPrintlnRaw(tm.bufferPartNumber); + } + tm.bufferMode=false; + } + } + } private void html() { if (!finalPass) @@ -504,15 +547,32 @@ private void html() { // tag safety checks previousContentWasNonWhitespace = !contentIsWhitespace; + if(currentlyBufferingWhitespace) { + whitespaceBuffer.append(text); + } else { + appendHtmlPart(text); + } + } // html() + + private void appendHtmlPart(String text) { + // flush previous white space if any + if(whitespaceBuffer.length() > 0) { + if(text != null) { + whitespaceBuffer.append(text); + } + text=whitespaceBuffer.toString(); + clearBufferedWhiteSpace(); + } + // de-dupe constants - String constantName = (String) constantsToNames.get(text); - if (constantName == null) { - constantName = "htmlParts[" + (constantCount++) + "]"; - constantsToNames.put(text, constantName); + Integer constantNumber = constantsToNumbers.get(text); + if (constantNumber == null) { + constantNumber = new Integer(constantCount++); + constantsToNumbers.put(text, constantNumber); htmlParts.add(text); } - bufferedPrintlnToResponse(constantName); - } // html() + htmlPartPrintlnToResponse(constantNumber); + } private void makeName(String uri) { String name; @@ -582,6 +642,7 @@ private void page() { out.println("def request = binding.request"); out.println("def flash = binding.flash"); out.println("def response = binding.response"); + //out.println("def out = binding.out"); if (codecClassName != null) { out .println("request.setAttribute('org.codehaus.groovy.grails.GSP_CODEC', '" @@ -706,10 +767,10 @@ private void endTag() { if (tagMetaStack.isEmpty()) throw new GrailsTagException( "Found closing Grails tag with no opening [" + tagName - + "]"); + + "]", pageName, + out.getCurrentLineNumber()); - TagMeta tm = (TagMeta) tagMetaStack - .remove(this.tagMetaStack.size() - 1); + TagMeta tm = (TagMeta) tagMetaStack.pop(); String lastInStack = tm.name; String lastNamespaceInStack = tm.namespace; @@ -720,7 +781,8 @@ private void endTag() { if (!lastInStack.equals(tagName) || !lastNamespaceInStack.equals(ns)) { throw new GrailsTagException("Grails tag [" + lastNamespaceInStack - + ":" + lastInStack + "] was not closed"); + + ":" + lastInStack + "] was not closed", pageName, + out.getCurrentLineNumber()); } if (GroovyPage.DEFAULT_NAMESPACE.equals(ns) @@ -730,13 +792,28 @@ private void endTag() { tag.doEndTag(); } else { throw new GrailsTagException("Grails tag [" + tagName - + "] was not closed"); + + "] was not closed", pageName, + out.getCurrentLineNumber()); } } else { String bodyTagClosureName = "null"; - if (!tm.emptyTag) { + if (!tm.emptyTag && !tm.bufferMode) { bodyTagClosureName = "body" + tagIndex; out.println("}"); + closureLevel--; + } + + if(tm.bufferMode && tm.bufferPartNumber != -1){ + if(!bodyVarsDefined.contains(tm.tagIndex)) { + out.print("def "); + bodyVarsDefined.add(tm.tagIndex); + } + out + .println("body" + + tm.tagIndex + + " = createClosureForHtmlPart(" + tm.bufferPartNumber + ")"); + bodyTagClosureName = "body" + tm.tagIndex; + tm.bufferMode = false; } if (jspTags.containsKey(ns)) { @@ -746,13 +823,13 @@ private void endTag() { out .println("if(!jspTag) throw new GrailsTagException('Unknown JSP tag " + ns + ":" + tagName + "')"); - out.println("jspTag.doTag(out,attrs" + tagIndex + ", " + out.println("jspTag.doTag(out," + attrsVarsMapDefinition.get(tagIndex) + ", " + bodyTagClosureName + ")"); } else { if (tm.hasAttributes) { out.println("invokeTag('" + tagName + "','" + ns + "'," - + getCurrentOutputLineNumber() + ",attrs" - + tagIndex + "," + bodyTagClosureName + ")"); + + getCurrentOutputLineNumber() + "," + attrsVarsMapDefinition.get(tagIndex) + + "," + bodyTagClosureName + ")"); } else { out.println("invokeTag('" + tagName + "','" + ns + "'," + getCurrentOutputLineNumber() + ",[:]," @@ -760,6 +837,8 @@ private void endTag() { } } } + + tm.bufferMode = false; tagIndex--; } @@ -813,8 +892,11 @@ private void startTag() { throw new GrailsTagException( "Unexpected end of file encountered parsing Tag [" + tagName + "] for " + className - + ". Are you missing a closing brace '}'?"); + + ". Are you missing a closing brace '}'?", pageName, + out.getCurrentLineNumber()); } + + flushTagBuffering(); TagMeta tm = new TagMeta(); tm.name = tagName; @@ -822,7 +904,8 @@ private void startTag() { tm.hasAttributes = !attrs.isEmpty(); tm.lineNumber = getCurrentOutputLineNumber(); tm.emptyTag = emptyTag; - tagMetaStack.add(tm); + tm.tagIndex = tagIndex; + tagMetaStack.push(tm); if (GroovyPage.DEFAULT_NAMESPACE.equals(ns) && tagRegistry.isSyntaxTag(tagName)) { @@ -842,7 +925,8 @@ private void startTag() { throw new GrailsTagException( "Tag [" + tag.getName() - + "] cannot have non-whitespace characters directly preceding it."); + + "] cannot have non-whitespace characters directly preceding it.", pageName, + out.getCurrentLineNumber()); } else { // If tag does not specify buffering of WS, we swallow it here clearBufferedWhiteSpace(); @@ -854,26 +938,43 @@ private void startTag() { // Custom taglibs have to always flush the whitespace, there's no // "allowPrecedingWhitespace" property on tags yet flushBufferedWhiteSpace(); + if (attrs.size() > 0) { - out.print("attrs" + tagIndex + " = ["); + FastStringWriter buffer=new FastStringWriter(); + buffer.print('['); for (Iterator i = attrs.keySet().iterator(); i.hasNext();) { String name = (String) i.next(); - out.print(name); - out.print(':'); + buffer.print(name); + buffer.print(':'); - out.print(getExpressionText(attrs.get(name).toString())); + buffer.print(getExpressionText(attrs.get(name).toString())); if (i.hasNext()) - out.print(','); + buffer.print(','); else - out.println(']'); + buffer.println(']'); } + attrsVarsMapDefinition.put(tagIndex, buffer.toString()); } - if (!emptyTag) { - out - .println("body" - + tagIndex - + " = new GroovyPageTagBody(this,binding.webRequest) {"); + + if(!emptyTag) { + tm.bufferMode = true; } + + } + } + + private void writeTagBodyStart(TagMeta tm) { + if (tm.bufferMode) { + tm.bufferMode = false; + if(!bodyVarsDefined.contains(tm.tagIndex)) { + out.print("def "); + bodyVarsDefined.add(tm.tagIndex); + } + out + .println("body" + + tm.tagIndex + + " = new GroovyPageTagBody(this,binding.webRequest) {"); + closureLevel++; } } @@ -885,8 +986,7 @@ private void clearBufferedWhiteSpace() { // Write out any whitespace we saved between tags private void flushBufferedWhiteSpace() { if (currentlyBufferingWhitespace) { - out.printlnToResponse(whitespaceBuffer.toString()); - clearBufferedWhiteSpace(); + appendHtmlPart(null); } currentlyBufferingWhitespace = false; } @@ -1009,4 +1109,8 @@ public List getHtmlParts() { public String[] getHtmlPartsArray() { return htmlParts.toArray(new String[htmlParts.size()]); } + + public boolean isInClosure() { + return closureLevel > 0; + } } // Parse diff --git a/grails/src/java/org/codehaus/groovy/grails/web/pages/GroovyPageUtils.java b/grails/src/java/org/codehaus/groovy/grails/web/pages/GroovyPageUtils.java index 404d1623a..380af2528 100644 --- a/grails/src/java/org/codehaus/groovy/grails/web/pages/GroovyPageUtils.java +++ b/grails/src/java/org/codehaus/groovy/grails/web/pages/GroovyPageUtils.java @@ -15,11 +15,14 @@ package org.codehaus.groovy.grails.web.pages; import grails.util.GrailsNameUtils; +import groovy.lang.Binding; import groovy.lang.GroovyObject; import groovy.lang.MissingPropertyException; import org.codehaus.groovy.grails.commons.ControllerArtefactHandler; +import org.codehaus.groovy.grails.commons.TagLibArtefactHandler; import org.codehaus.groovy.grails.web.metaclass.ControllerDynamicMethods; import org.codehaus.groovy.grails.web.servlet.GrailsApplicationAttributes; +import org.codehaus.groovy.grails.web.servlet.mvc.GrailsWebRequest; import javax.servlet.ServletRequest; @@ -205,4 +208,15 @@ private static String getViewURIInternal(String controllerName, String viewName, return buf.toString(); } } + + public static Binding findPageScopeBinding(Object owner, GrailsWebRequest webRequest) { + if(owner instanceof GroovyPage) + return ((GroovyPage) owner).getBinding(); + else if(owner != null && owner.getClass().getName().endsWith(TagLibArtefactHandler.TYPE)) { + return (Binding) ((GroovyObject)owner).getProperty(GroovyPage.PAGE_SCOPE); + } else { + return (Binding)webRequest.getCurrentRequest().getAttribute(GrailsApplicationAttributes.PAGE_SCOPE); + } + } + } diff --git a/grails/src/java/org/codehaus/groovy/grails/web/pages/GroovyPageWritable.java b/grails/src/java/org/codehaus/groovy/grails/web/pages/GroovyPageWritable.java index b4bed4852..2d5e3d58d 100644 --- a/grails/src/java/org/codehaus/groovy/grails/web/pages/GroovyPageWritable.java +++ b/grails/src/java/org/codehaus/groovy/grails/web/pages/GroovyPageWritable.java @@ -26,6 +26,7 @@ import org.codehaus.groovy.grails.web.servlet.GrailsApplicationAttributes; import org.codehaus.groovy.grails.web.servlet.WrappedResponseHolder; import org.codehaus.groovy.grails.web.servlet.mvc.GrailsWebRequest; +import org.codehaus.groovy.grails.web.util.GrailsPrintWriter; import org.codehaus.groovy.runtime.InvokerHelper; import org.springframework.context.ApplicationContext; import org.springframework.web.context.request.RequestContextHolder; @@ -48,13 +49,13 @@ * Time: 11:36:44 AM */ class GroovyPageWritable implements Writable { - private static final Log LOG = LogFactory.getLog(GroovyPageWritable.class); private HttpServletResponse response; private HttpServletRequest request; private GroovyPageMetaInfo metaInfo; private boolean showSource; + private GrailsWebRequest webRequest; private ServletContext context; private Map additionalBinding = new HashMap(); @@ -90,6 +91,9 @@ public void setShowSource(boolean showSource) { this.showSource = showSource; } + + + /** * Writes the template to the specified Writer * @@ -136,8 +140,12 @@ public Writer writeTo(Writer out) throws IOException { page.setJspTagLibraryResolver(metaInfo.getJspTagLibraryResolver()); page.setGspTagLibraryLookup(metaInfo.getTagLibraryLookup()); page.setHtmlParts(metaInfo.getHtmlParts()); - - page.run(); + page.initRun(out, webRequest); + try { + page.run(); + } finally { + page.cleanup(); + } request.setAttribute(GrailsApplicationAttributes.PAGE_SCOPE, oldBinding); } return out; @@ -158,12 +166,12 @@ protected void copyBinding(Binding binding, Binding oldBinding, Writer out) thro } private Binding createBinding() { - Binding binding = new Binding(); + Binding binding = new GroovyPageBinding(); request.setAttribute(GrailsApplicationAttributes.PAGE_SCOPE, binding); binding.setVariable(GroovyPage.PAGE_SCOPE, binding); return binding; } - + /** * Copy all of input to output. * @param in The input stream to writeInputStreamToResponse from @@ -200,6 +208,7 @@ protected void writeInputStreamToResponse(InputStream in, Writer out) throws IOE */ protected void writeGroovySourceToResponse(GroovyPageMetaInfo info, Writer out) throws IOException { InputStream in = info.getGroovySource(); + if(in==null) return; try { try { in.reset(); diff --git a/grails/src/java/org/codehaus/groovy/grails/web/pages/GroovyPagesServlet.java b/grails/src/java/org/codehaus/groovy/grails/web/pages/GroovyPagesServlet.java index 97963988f..8574ea643 100644 --- a/grails/src/java/org/codehaus/groovy/grails/web/pages/GroovyPagesServlet.java +++ b/grails/src/java/org/codehaus/groovy/grails/web/pages/GroovyPagesServlet.java @@ -37,6 +37,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; +import java.io.PrintWriter; import java.io.Writer; import java.util.Collection; import java.util.HashMap; @@ -204,7 +205,7 @@ private void defaultExceptionHandling(Exception exception, Writer out, GroovyPag * @return The created java.io.Writer */ protected Writer createResponseWriter(HttpServletResponse response) { - Writer out = GSPResponseWriter.getInstance(response, BUFFER_SIZE); + PrintWriter out = GSPResponseWriter.getInstance(response, BUFFER_SIZE); GrailsWebRequest webRequest = (GrailsWebRequest) RequestContextHolder.currentRequestAttributes(); webRequest.setOut(out); return out; diff --git a/grails/src/java/org/codehaus/groovy/grails/web/pages/TagLibraryLookup.java b/grails/src/java/org/codehaus/groovy/grails/web/pages/TagLibraryLookup.java index 9ec2d7992..2d8c73941 100644 --- a/grails/src/java/org/codehaus/groovy/grails/web/pages/TagLibraryLookup.java +++ b/grails/src/java/org/codehaus/groovy/grails/web/pages/TagLibraryLookup.java @@ -15,6 +15,11 @@ package org.codehaus.groovy.grails.web.pages; import groovy.lang.GroovyObject; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + import org.codehaus.groovy.grails.commons.GrailsApplication; import org.codehaus.groovy.grails.commons.GrailsClass; import org.codehaus.groovy.grails.commons.GrailsTagLibClass; @@ -26,10 +31,6 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - /** * A class that is look-up tag library instances * @@ -41,7 +42,7 @@ public class TagLibraryLookup implements ApplicationContextAware, GrailsApplicationAware, InitializingBean{ private ApplicationContext applicationContext; private GrailsApplication grailsApplication; - private Map tagLibraries = new HashMap(); + private Map tagLibraries = new HashMap(); private Map namespaceDispatchers = new HashMap(); public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { @@ -58,7 +59,7 @@ public void afterPropertiesSet() throws Exception { for (GrailsClass grailsClass : taglibs) { registerTagLib((GrailsTagLibClass)grailsClass); } - namespaceDispatchers.put(GroovyPage.TEMPLATE_NAMESPACE, new NamespacedTagDispatcher(GroovyPage.DEFAULT_NAMESPACE, GroovyPage.class, grailsApplication, applicationContext) { + namespaceDispatchers.put(GroovyPage.TEMPLATE_NAMESPACE, new NamespacedTagDispatcher(GroovyPage.DEFAULT_NAMESPACE, GroovyPage.class, grailsApplication, this) { @Override public Object invokeMethod(final String name, Object args) { @@ -90,9 +91,9 @@ public Object invokeMethod(final String name, Object args) { */ public void registerTagLib(GrailsTagLibClass taglib) { String namespace = taglib.getNamespace(); - namespaceDispatchers.put(namespace, new NamespacedTagDispatcher(namespace, GroovyPage.class, grailsApplication, applicationContext)); + namespaceDispatchers.put(namespace, new NamespacedTagDispatcher(namespace, GroovyPage.class, grailsApplication, this)); for(String tagName : taglib.getTagNames()) { - tagLibraries.put(namespace+':'+tagName, (GroovyObject) applicationContext.getBean(taglib.getFullName())); + tagLibraries.put(namespace+':'+tagName, taglib.getFullName()); } } @@ -104,7 +105,12 @@ public void registerTagLib(GrailsTagLibClass taglib) { * @return The tag library or null if it wasn't found */ public GroovyObject lookupTagLibrary(String namespace, String tagName) { - return tagLibraries.get(namespace+':'+tagName); + String fullName = tagLibraries.get(namespace+':'+tagName); + if(fullName != null) { + return (GroovyObject) applicationContext.getBean(fullName); + } else { + return null; + } } /** diff --git a/grails/src/java/org/codehaus/groovy/grails/web/pages/ext/jsp/PageContextFactory.groovy b/grails/src/java/org/codehaus/groovy/grails/web/pages/ext/jsp/PageContextFactory.groovy index 80c4a92cf..714e73d52 100644 --- a/grails/src/java/org/codehaus/groovy/grails/web/pages/ext/jsp/PageContextFactory.groovy +++ b/grails/src/java/org/codehaus/groovy/grails/web/pages/ext/jsp/PageContextFactory.groovy @@ -17,6 +17,7 @@ package org.codehaus.groovy.grails.web.pages.ext.jsp import javax.servlet.jsp.PageContext as PC import org.codehaus.groovy.grails.web.servlet.mvc.GrailsWebRequest import org.springframework.web.context.request.RequestContextHolder +import org.codehaus.groovy.grails.web.pages.GroovyPageBinding; import org.codehaus.groovy.grails.web.pages.GroovyPagesServlet import org.codehaus.groovy.grails.web.servlet.GrailsApplicationAttributes as GAA import javax.servlet.jsp.PageContext @@ -63,7 +64,7 @@ class PageContextFactory { } def pageScope = request.getAttribute(GrailsApplicationAttributes.PAGE_SCOPE) if(!pageScope) { - pageScope = new Binding() + pageScope = new GroovyPageBinding() request.setAttribute(GrailsApplicationAttributes.PAGE_SCOPE, pageScope) } diff --git a/grails/src/java/org/codehaus/groovy/grails/web/plugins/support/WebMetaUtils.groovy b/grails/src/java/org/codehaus/groovy/grails/web/plugins/support/WebMetaUtils.groovy index 7b5e4e3eb..534e6228b 100644 --- a/grails/src/java/org/codehaus/groovy/grails/web/plugins/support/WebMetaUtils.groovy +++ b/grails/src/java/org/codehaus/groovy/grails/web/plugins/support/WebMetaUtils.groovy @@ -19,6 +19,7 @@ import org.springframework.context.ApplicationContext import org.codehaus.groovy.grails.commons.GrailsTagLibClass import org.springframework.beans.BeanWrapperImpl import org.codehaus.groovy.grails.web.pages.GroovyPage +import org.codehaus.groovy.grails.web.pages.TagLibraryLookup; import org.springframework.web.context.request.RequestContextHolder as RCH import org.codehaus.groovy.grails.commons.GrailsApplication import org.codehaus.groovy.grails.web.taglib.GroovyPageTagBody @@ -92,71 +93,34 @@ class WebMetaUtils { } - static registerMethodMissingForTags(MetaClass mc, GroovyObject tagLibrary, String name) { + static registerMethodMissingForTags(MetaClass mc, TagLibraryLookup gspTagLibraryLookup, String namespace, String name) { + def tagLibBean=gspTagLibraryLookup.lookupTagLibrary(namespace, name) + def lookupTagLib={-> tagLibBean } + // for Request scope support, change this + // def lookupTagLib={-> gspTagLibraryLookup.lookupTagLibrary(namespace, name) } + mc."$name" = {Map attrs, Closure body -> - def webRequest = RCH.currentRequestAttributes() - def originalOut = webRequest.out - def capturedOutput - try { - if(body && !(body instanceof GroovyPageTagBody)) { - body = new GroovyPageTagBody(delegate, webRequest, true, body) - - } - capturedOutput = GroovyPage.captureTagOutput(tagLibrary, name, attrs, body, webRequest) - } finally { - webRequest.out = originalOut - } - capturedOutput + GroovyPage.captureTagOutput(lookupTagLib(), name, attrs, body, RCH.currentRequestAttributes()) } mc."$name" = {Map attrs, String body -> - Closure bodyClosure = { - out << body - } - delegate."$name"(attrs, bodyClosure) + delegate."$name"(attrs, new GroovyPage.ConstantClosure(body)) } mc."$name" = {Map attrs -> - def webRequest = RCH.currentRequestAttributes() - - - def originalOut = webRequest.out - def capturedOutput - try { - capturedOutput = GroovyPage.captureTagOutput(tagLibrary, name, attrs, null, webRequest) - } finally { - webRequest.out = originalOut - } - capturedOutput + GroovyPage.captureTagOutput(lookupTagLib(), name, attrs, null, RCH.currentRequestAttributes()) } mc."$name" = {Closure body -> - def webRequest = RCH.currentRequestAttributes() - - def originalOut = webRequest.out - def capturedOutput - try { - capturedOutput = GroovyPage.captureTagOutput(tagLibrary, name, [:], body, webRequest) - } finally { - webRequest.out = originalOut - } - capturedOutput + GroovyPage.captureTagOutput(lookupTagLib(), name, [:], body, RCH.currentRequestAttributes()) } mc."$name" = {-> - def webRequest = RCH.currentRequestAttributes() - - - def originalOut = webRequest.out - def capturedOutput - try { - capturedOutput = GroovyPage.captureTagOutput(tagLibrary, name, [:], null, webRequest) - } finally { - webRequest.out = originalOut - } - capturedOutput + GroovyPage.captureTagOutput(lookupTagLib(), name, [:], null, RCH.currentRequestAttributes()) } } static registerMethodMissingForTags(MetaClass mc, ApplicationContext ctx, GrailsTagLibClass tagLibraryClass, String name) { - def tagLibrary = ctx.getBean(tagLibraryClass.fullName) - registerMethodMissingForTags(mc,tagLibrary,name) + //def tagLibrary = ctx.getBean(tagLibraryClass.fullName) + TagLibraryLookup gspTagLibraryLookup = ctx.getBean("gspTagLibraryLookup") + String namespace = tagLibraryClass.namespace ?: GroovyPage.DEFAULT_NAMESPACE + registerMethodMissingForTags(mc,gspTagLibraryLookup,namespace,name) } diff --git a/grails/src/java/org/codehaus/groovy/grails/web/servlet/filter/GrailsReloadServletFilter.java b/grails/src/java/org/codehaus/groovy/grails/web/servlet/filter/GrailsReloadServletFilter.java index 70a2c6fa3..eebadf7e6 100644 --- a/grails/src/java/org/codehaus/groovy/grails/web/servlet/filter/GrailsReloadServletFilter.java +++ b/grails/src/java/org/codehaus/groovy/grails/web/servlet/filter/GrailsReloadServletFilter.java @@ -38,6 +38,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; +import java.io.PrintWriter; import java.io.Writer; import java.util.HashMap; import java.util.Map; @@ -102,7 +103,7 @@ protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServl } protected Writer createResponseWriter(HttpServletResponse response) { - Writer out = GSPResponseWriter.getInstance(response, BUFFER_SIZE); + PrintWriter out = GSPResponseWriter.getInstance(response, BUFFER_SIZE); GrailsWebRequest webRequest = (GrailsWebRequest) RequestContextHolder.currentRequestAttributes(); webRequest.setOut(out); return out; diff --git a/grails/src/java/org/codehaus/groovy/grails/web/servlet/view/GroovyPageView.java b/grails/src/java/org/codehaus/groovy/grails/web/servlet/view/GroovyPageView.java index 922c850fb..58b2d1a6b 100644 --- a/grails/src/java/org/codehaus/groovy/grails/web/servlet/view/GroovyPageView.java +++ b/grails/src/java/org/codehaus/groovy/grails/web/servlet/view/GroovyPageView.java @@ -31,6 +31,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; +import java.io.PrintWriter; import java.io.Writer; import java.util.Map; @@ -144,7 +145,7 @@ protected void handleException(Exception exception, Writer out, GroovyPagesTempl */ //TODO this method is dupe'd across GSP servlet, reload servlet and here... protected Writer createResponseWriter(HttpServletResponse response) { - Writer out = GSPResponseWriter.getInstance(response, BUFFER_SIZE); + PrintWriter out = GSPResponseWriter.getInstance(response, BUFFER_SIZE); GrailsWebRequest webRequest = (GrailsWebRequest) RequestContextHolder.currentRequestAttributes(); webRequest.setOut(out); return out; diff --git a/grails/src/java/org/codehaus/groovy/grails/web/sitemesh/GrailsLayoutDecoratorMapper.java b/grails/src/java/org/codehaus/groovy/grails/web/sitemesh/GrailsLayoutDecoratorMapper.java index bb026df50..41fbda74c 100644 --- a/grails/src/java/org/codehaus/groovy/grails/web/sitemesh/GrailsLayoutDecoratorMapper.java +++ b/grails/src/java/org/codehaus/groovy/grails/web/sitemesh/GrailsLayoutDecoratorMapper.java @@ -48,6 +48,7 @@ import java.util.HashMap; import java.util.Map; import java.util.Properties; +import java.util.concurrent.ConcurrentHashMap; /** * Implements the SiteMesh decorator mapper interface and allows grails views to map to grails layouts @@ -63,7 +64,7 @@ public class GrailsLayoutDecoratorMapper extends AbstractDecoratorMapper impleme private static final Log LOG = LogFactory.getLog( GrailsLayoutDecoratorMapper.class ); - private Map decoratorMap = new HashMap(); + private Map decoratorMap = new ConcurrentHashMap(); private ServletContext servletContext; private WebApplicationContext applicationContext; private PluginMetaManager pluginMetaManager; diff --git a/grails/src/java/org/codehaus/groovy/grails/web/taglib/GrailsTagRegistry.java b/grails/src/java/org/codehaus/groovy/grails/web/taglib/GrailsTagRegistry.java index c076a2db5..939165ff9 100644 --- a/grails/src/java/org/codehaus/groovy/grails/web/taglib/GrailsTagRegistry.java +++ b/grails/src/java/org/codehaus/groovy/grails/web/taglib/GrailsTagRegistry.java @@ -19,6 +19,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; /** * A registry for holding all Grails tag implementations @@ -29,7 +30,7 @@ public class GrailsTagRegistry { private static GrailsTagRegistry instance; - private static Map tagRegistry = Collections.synchronizedMap(new HashMap()); + private static Map tagRegistry = new ConcurrentHashMap(); static { GrailsTagRegistry tagRegistry = getInstance(); diff --git a/grails/src/java/org/codehaus/groovy/grails/web/taglib/GroovyPageTagBody.java b/grails/src/java/org/codehaus/groovy/grails/web/taglib/GroovyPageTagBody.java index 3a3f01f0b..00ac842fd 100644 --- a/grails/src/java/org/codehaus/groovy/grails/web/taglib/GroovyPageTagBody.java +++ b/grails/src/java/org/codehaus/groovy/grails/web/taglib/GroovyPageTagBody.java @@ -16,16 +16,16 @@ import groovy.lang.Binding; import groovy.lang.Closure; -import groovy.lang.GString; -import groovy.lang.GroovyObject; -import org.codehaus.groovy.grails.commons.TagLibArtefactHandler; -import org.codehaus.groovy.grails.web.pages.GroovyPage; -import org.codehaus.groovy.grails.web.servlet.mvc.GrailsWebRequest; import java.io.Writer; import java.util.HashMap; import java.util.Map; +import org.codehaus.groovy.grails.web.pages.GroovyPageOutputStack; +import org.codehaus.groovy.grails.web.pages.GroovyPageUtils; +import org.codehaus.groovy.grails.web.servlet.mvc.GrailsWebRequest; +import org.codehaus.groovy.grails.web.util.StreamCharBuffer; + /** * A closure that represents the body of a tag and captures its output returning the result when invoked * @@ -43,11 +43,11 @@ public class GroovyPageTagBody extends Closure { private static final String BLANK_STRING = ""; private boolean writeStringResult = false; - public GroovyPageTagBody(Object owner, GrailsWebRequest webRequest,Closure bodyClosure) { + public GroovyPageTagBody(Object owner, GrailsWebRequest webRequest, Closure bodyClosure) { this(owner, webRequest, false, bodyClosure); } - public GroovyPageTagBody(Object owner, GrailsWebRequest webRequest,boolean writeStringResult, Closure bodyClosure) { + public GroovyPageTagBody(Object owner, GrailsWebRequest webRequest, boolean writeStringResult, Closure bodyClosure) { super(owner); if(bodyClosure == null) throw new IllegalStateException("Argument [bodyClosure] cannot be null!"); @@ -55,24 +55,16 @@ public GroovyPageTagBody(Object owner, GrailsWebRequest webRequest,boolean write this.bodyClosure = bodyClosure; this.webRequest = webRequest; - binding = null; - if(owner instanceof GroovyPage) - binding = ((GroovyPage) owner).getBinding(); - else if(owner != null && owner.getClass().getName().endsWith(TagLibArtefactHandler.TYPE)) { - binding = (Binding) ((GroovyObject)owner).getProperty(GroovyPage.PAGE_SCOPE); - } + this.binding = GroovyPageUtils.findPageScopeBinding(owner, webRequest); this.writeStringResult=writeStringResult; } - private Object captureClosureOutput(Object args) { - Writer originalOut = webRequest.getOut(); - + final GroovyPageTagWriter capturedOut = new GroovyPageTagWriter(); try { - final GroovyPageTagWriter capturedOut = createWriter(); - - + GroovyPageOutputStack.currentStack().push(capturedOut); + Object bodyResult; if(args!=null) { @@ -104,9 +96,8 @@ private Object captureClosureOutput(Object args) { currentBinding.putAll((Map) args); } - try { - bodyResult = bodyClosure.call(args); + bodyResult = executeClosure(bodyClosure, args); } finally { if(binding!=null) { @@ -117,44 +108,32 @@ private Object captureClosureOutput(Object args) { } } else { - bodyResult = bodyClosure.call(args); + bodyResult = executeClosure(bodyClosure, args); } } else { - bodyResult = bodyClosure.call(); - } - String output = capturedOut.getValue(); - if(org.apache.commons.lang.StringUtils.isBlank(output)) { - if(bodyResult instanceof String) { - return bodyResult; - } - else if(bodyResult instanceof GString) { - return bodyResult.toString(); - } - else { - return BLANK_STRING; - } + bodyResult = executeClosure(bodyClosure, null); } - return output; + StreamCharBuffer buffer=capturedOut.getBuffer(); + if(buffer.charsAvailable()==0 && bodyResult != null && !(bodyResult instanceof Writer)) { + return bodyResult; + } + return buffer; } finally { - if(binding!=null) { - binding.setVariable(GroovyPage.OUT, originalOut); - } - webRequest.setOut(originalOut); + GroovyPageOutputStack.currentStack().pop(); } } - private GroovyPageTagWriter createWriter() { - - GroovyPageTagWriter out = new GroovyPageTagWriter(); - if(binding!=null) { - binding.setVariable(GroovyPage.OUT, out); - } - webRequest.setOut(out); - - return out; - } + private Object executeClosure(Closure bodyClosure, Object args) { + Object bodyResult=null; + if(args != null) { + bodyResult=bodyClosure.call(args); + } else { + bodyResult=bodyClosure.call(); + } + return bodyResult; + } public Object doCall() { return captureClosureOutput(null); diff --git a/grails/src/java/org/codehaus/groovy/grails/web/taglib/NamespacedTagDispatcher.groovy b/grails/src/java/org/codehaus/groovy/grails/web/taglib/NamespacedTagDispatcher.groovy index b477adf35..b6be784b1 100644 --- a/grails/src/java/org/codehaus/groovy/grails/web/taglib/NamespacedTagDispatcher.groovy +++ b/grails/src/java/org/codehaus/groovy/grails/web/taglib/NamespacedTagDispatcher.groovy @@ -16,8 +16,8 @@ package org.codehaus.groovy.grails.web.taglib import org.codehaus.groovy.grails.commons.GrailsApplication -import org.springframework.context.ApplicationContext import org.codehaus.groovy.grails.commons.TagLibArtefactHandler +import org.codehaus.groovy.grails.web.pages.TagLibraryLookup /** *

    This class allows dispatching to namespaced tag libraries and is used within controllers and tag libraries @@ -33,20 +33,19 @@ import org.codehaus.groovy.grails.commons.TagLibArtefactHandler class NamespacedTagDispatcher extends GroovyObjectSupport { String namespace GrailsApplication application - ApplicationContext applicationContext Class type + TagLibraryLookup lookup - NamespacedTagDispatcher(String ns, Class callingType, GrailsApplication application, ApplicationContext applicationContext) { + NamespacedTagDispatcher(String ns, Class callingType, GrailsApplication application, TagLibraryLookup lookup) { this.namespace = ns this.application = application - this.applicationContext = applicationContext + this.lookup = lookup this.type = callingType } + def invokeMethod(String name, args) { - def tag = "$namespace:$name" - def tagLibClass = application.getArtefactForFeature(TagLibArtefactHandler.TYPE, tag.toString()) - if(tagLibClass) { - def tagBean = applicationContext.getBean(tagLibClass.fullName) + def tagBean = lookup.lookupTagLibrary(namespace, name) + if(tagBean) { Object result = tagBean.invokeMethod(name, args) return result } diff --git a/grails/src/java/org/codehaus/groovy/grails/web/taglib/RenderInputTag.java b/grails/src/java/org/codehaus/groovy/grails/web/taglib/RenderInputTag.java index c7c297064..7ba6de65d 100644 --- a/grails/src/java/org/codehaus/groovy/grails/web/taglib/RenderInputTag.java +++ b/grails/src/java/org/codehaus/groovy/grails/web/taglib/RenderInputTag.java @@ -34,6 +34,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; /** * A tag that attempts to render an input for a bean property into an appropriate component based on the type. @@ -65,7 +66,7 @@ public class RenderInputTag extends RequestContextTag { private String property; private BeanWrapper beanWrapper; private Map constrainedProperties = Collections.EMPTY_MAP; - private Map cachedUris = Collections.synchronizedMap(new HashMap()); + private Map cachedUris = new ConcurrentHashMap(); protected RenderInputTag() { super(TAG_NAME); diff --git a/grails/src/java/org/codehaus/groovy/grails/web/taglib/exceptions/GrailsTagException.java b/grails/src/java/org/codehaus/groovy/grails/web/taglib/exceptions/GrailsTagException.java index ddf77cff4..fe9600d14 100644 --- a/grails/src/java/org/codehaus/groovy/grails/web/taglib/exceptions/GrailsTagException.java +++ b/grails/src/java/org/codehaus/groovy/grails/web/taglib/exceptions/GrailsTagException.java @@ -38,13 +38,13 @@ public GrailsTagException(Throwable arg0) { } public GrailsTagException(String message, String pageName, int lineNumber) { - super(message); + super(message + " (" + pageName + ":" + lineNumber + ")"); this.fileName = pageName; this.lineNumber = lineNumber; } public GrailsTagException(String arg0, Throwable arg1, String fileName, int lineNumber) { - super(arg0, arg1); + super(arg0 + " (" + fileName + ":" + lineNumber + ")", arg1); this.fileName = fileName; this.lineNumber = lineNumber; } diff --git a/grails/src/java/org/codehaus/groovy/grails/web/util/GrailsPrintWriter.java b/grails/src/java/org/codehaus/groovy/grails/web/util/GrailsPrintWriter.java index ff065ecb5..9a1d5ef49 100644 --- a/grails/src/java/org/codehaus/groovy/grails/web/util/GrailsPrintWriter.java +++ b/grails/src/java/org/codehaus/groovy/grails/web/util/GrailsPrintWriter.java @@ -32,7 +32,7 @@ public GrailsPrintWriter(Writer out) { public Writer getOut() { return out; } - + public boolean isFinalTargetHere() { return this.finalTargetHere; } @@ -65,6 +65,10 @@ public GrailsPrintWriter leftShift(Object value) throws IOException { } return this; } + + public GrailsPrintWriter plus(Object value) throws IOException { + return this.leftShift(value); + } /** * Flush the stream if it's not closed and check its error state. @@ -112,7 +116,7 @@ protected void handleIOException(IOException e) { * @param obj The Object to be printed * @see java.lang.Object#toString() */ - public void print(Object obj) { + public void print(final Object obj) { if (trouble || obj == null) { return; } else { @@ -122,6 +126,8 @@ public void print(Object obj) { } catch (IOException e) { handleIOException(e); } + } else if(obj instanceof StreamCharBuffer) { + this.write((StreamCharBuffer)obj); } else { write(String.valueOf(obj)); } @@ -137,7 +143,7 @@ public void print(Object obj) { * * @param s The String to be printed */ - public void print(String s) { + public void print(final String s) { if (s == null) { return; } @@ -151,7 +157,7 @@ public void print(String s) { * @param s The String to be printed */ @Override - public void write(String s) { + public void write(final String s) { if(trouble || s == null) { return; } @@ -167,7 +173,7 @@ public void write(String s) { * @param c int specifying a character to be written. */ @Override - public void write(int c) { + public void write(final int c) { if (trouble) return; try { out.write(c); @@ -183,7 +189,7 @@ public void write(int c) { * @param len Number of characters to write */ @Override - public void write(char buf[], int off, int len) { + public void write(final char buf[], final int off, final int len) { if (trouble || buf == null || len == 0) return; try { out.write(buf, off, len); @@ -199,7 +205,7 @@ public void write(char buf[], int off, int len) { * @param len Number of characters to write */ @Override - public void write(String s, int off, int len) { + public void write(final String s, final int off, final int len) { if (trouble || s == null || s.length() == 0) return; try { out.write(s, off, len); @@ -209,14 +215,14 @@ public void write(String s, int off, int len) { } // write() @Override - public void write(char buf[]) { + public void write(final char buf[]) { write(buf, 0, buf.length); } /** delegate methods, not synchronized **/ @Override - public void print(boolean b) { + public void print(final boolean b) { if(b) write("true"); else @@ -224,32 +230,32 @@ public void print(boolean b) { } @Override - public void print(char c) { + public void print(final char c) { write(c); } @Override - public void print(int i) { + public void print(final int i) { write(String.valueOf(i)); } @Override - public void print(long l) { + public void print(final long l) { write(String.valueOf(l)); } @Override - public void print(float f) { + public void print(final float f) { write(String.valueOf(f)); } @Override - public void print(double d) { + public void print(final double d) { write(String.valueOf(d)); } @Override - public void print(char s[]) { + public void print(final char s[]) { write(s); } @@ -259,61 +265,61 @@ public void println() { } @Override - public void println(boolean b) { + public void println(final boolean b) { print(b); println(); } @Override - public void println(char c) { + public void println(final char c) { print(c); println(); } @Override - public void println(int i) { + public void println(final int i) { print(i); println(); } @Override - public void println(long l) { + public void println(final long l) { print(l); println(); } @Override - public void println(float f) { + public void println(final float f) { print(f); println(); } @Override - public void println(double d) { + public void println(final double d) { print(d); println(); } @Override - public void println(char c[]) { + public void println(final char c[]) { print(c); println(); } @Override - public void println(String s) { + public void println(final String s) { print(s); println(); } @Override - public void println(Object o) { + public void println(final Object o) { print(o); println(); } @Override - public PrintWriter append(char c) { + public PrintWriter append(final char c) { try { out.append(c); } catch (IOException e) { @@ -323,7 +329,7 @@ public PrintWriter append(char c) { } @Override - public PrintWriter append(CharSequence csq, int start, int end) { + public PrintWriter append(final CharSequence csq, final int start, final int end) { try { out.append(csq, start, end); } catch (IOException e) { @@ -333,7 +339,7 @@ public PrintWriter append(CharSequence csq, int start, int end) { } @Override - public PrintWriter append(CharSequence csq) { + public PrintWriter append(final CharSequence csq) { try { out.append(csq); } catch (IOException e) { @@ -346,4 +352,33 @@ public PrintWriter append(CharSequence csq) { protected Object clone() throws CloneNotSupportedException { throw new CloneNotSupportedException(); } + + public void write(final StreamCharBuffer otherBuffer) { + if(trouble) return; + try { + otherBuffer.writeTo(getOut(),true,false); + } catch (IOException e) { + handleIOException(e); + } + } + + public void print(final StreamCharBuffer otherBuffer) { + write(otherBuffer); + } + + public void append(final StreamCharBuffer otherBuffer) { + write(otherBuffer); + } + + public void println(final StreamCharBuffer otherBuffer) { + write(otherBuffer); + println(); + } + + public GrailsPrintWriter leftShift(final StreamCharBuffer otherBuffer) { + if(otherBuffer != null) { + write(otherBuffer); + } + return this; + } } diff --git a/grails/src/java/org/codehaus/groovy/grails/web/util/StreamCharBuffer.java b/grails/src/java/org/codehaus/groovy/grails/web/util/StreamCharBuffer.java index 309930056..523ab0504 100644 --- a/grails/src/java/org/codehaus/groovy/grails/web/util/StreamCharBuffer.java +++ b/grails/src/java/org/codehaus/groovy/grails/web/util/StreamCharBuffer.java @@ -15,6 +15,8 @@ */ package org.codehaus.groovy.grails.web.util; +import groovy.lang.Writable; + import java.io.EOFException; import java.io.IOException; import java.io.Reader; @@ -86,7 +88,7 @@ * @author Lari Hotari, Sagire Software Oy * */ -public class StreamCharBuffer { +public class StreamCharBuffer implements Writable { private static final int DEFAULT_CHUNK_SIZE = Integer.getInteger("streamcharbuffer.chunksize", 512); private static final int DEFAULT_MAX_CHUNK_SIZE = Integer.getInteger("streamcharbuffer.maxchunksize", 1024*1024); private static final int DEFAULT_CHUNK_SIZE_GROW_PROCENT = Integer.getInteger("streamcharbuffer.growprocent",100); @@ -245,8 +247,9 @@ public Reader getReader() { * @param target * @throws IOException */ - public void writeTo(Writer target) throws IOException { + public Writer writeTo(Writer target) throws IOException { writeTo(target, true, true); + return getWriter(); } /** @@ -319,6 +322,10 @@ public String toString() { } return cachedToString; } + + public String plus(String value) { + return toString() + value; + } /** * reads (and empties) the buffer to a char[], but caches the return value for subsequent calls. @@ -458,7 +465,7 @@ final public class StreamCharBufferWriter extends Writer { private boolean writerUsed = false; @Override - public void write(char[] b, int off, int len) throws IOException { + public void write(final char[] b, final int off, final int len) throws IOException { if (b == null) { throw new NullPointerException(); } else if ((off < 0) || (off > b.length) || (len < 0) @@ -485,7 +492,7 @@ public void write(char[] b, int off, int len) throws IOException { } } - private boolean shouldWriteDirectly(int len) { + private boolean shouldWriteDirectly(final int len) { if(connectedWriters.isEmpty()) { return false; } @@ -493,12 +500,12 @@ private boolean shouldWriteDirectly(int len) { } @Override - public void write(String str) throws IOException { + public void write(final String str) throws IOException { write(str, 0, str.length()); } @Override - public void write(String str, int off, int len) throws IOException { + public void write(final String str, final int off, final int len) throws IOException { if(len==0) return; writerUsed = true; if(shouldWriteDirectly(len)) { @@ -522,7 +529,7 @@ public void write(String str, int off, int len) throws IOException { } @Override - public Writer append(CharSequence csq, int start, int end) + public Writer append(final CharSequence csq, final int start, final int end) throws IOException { writerUsed = true; if(csq==null) { @@ -554,7 +561,7 @@ public Writer append(CharSequence csq, int start, int end) } @Override - public Writer append(CharSequence csq) throws IOException { + public Writer append(final CharSequence csq) throws IOException { writerUsed = true; if(csq==null) { write("null"); @@ -580,7 +587,7 @@ public boolean isUsed() { } @Override - public void write(int b) throws IOException { + public void write(final int b) throws IOException { writerUsed = true; allocateSpace(); currentWriteChunk.write((char) b); @@ -614,11 +621,11 @@ public boolean ready() throws IOException { } @Override - public int read(char[] b, int off, int len) throws IOException { + public int read(final char[] b, final int off, final int len) throws IOException { return readImpl(b, off, len); } - int readImpl(char[] b, int off, int len) throws EOFException { + int readImpl(final char[] b, final int off, final int len) throws EOFException { if (b == null) { throw new NullPointerException(); } else if ((off < 0) || (off > b.length) || (len < 0) @@ -700,7 +707,7 @@ public void reuseBuffer() { writingStringChunkGroup=null; } - public boolean write(char ch) { + public boolean write(final char ch) { if (used < size) { buffer[used++] = ch; return true; @@ -713,12 +720,12 @@ public int chunkSize() { return buffer.length; } - public void write(char[] ch, int off, int len) { + public void write(final char[] ch, final int off, final int len) { arrayCopy(ch, off, buffer, used, len); used += len; } - public void appendStringChunk(String str, int off, int len) throws IOException { + public void appendStringChunk(final String str, final int off, final int len) throws IOException { if(writingStringChunkGroup == null || used!=writingStringChunkGroup.getOwnerIndex()) { writingStringChunkGroup = new StringChunkGroup(used); if(StringChunkGroups==null) { @@ -747,22 +754,22 @@ private boolean prepareChildArrayChunkReading() { return true; } - public void writeString(String str, int off, int len) { + public void writeString(final String str, final int off, final int len) { str.getChars(off, off+len, buffer, used); used += len; } - public void writeStringBuilder(StringBuilder stringBuilder, int off, int len) { + public void writeStringBuilder(final StringBuilder stringBuilder, final int off, final int len) { stringBuilder.getChars(off, off+len, buffer, used); used += len; } - public void writeStringBuffer(StringBuffer stringBuffer, int off, int len) { + public void writeStringBuffer(final StringBuffer stringBuffer, final int off, final int len) { stringBuffer.getChars(off, off+len, buffer, used); used += len; } - public void read(char[] ch, int off, int len) { + public void read(final char[] ch, final int off, final int len) { int readLen = len; int readOff = off; while(readLen > 0) { @@ -791,7 +798,7 @@ public void read(char[] ch, int off, int len) { } } - public int writeTo(Writer target) throws IOException { + public int writeTo(final Writer target) throws IOException { int writtenCount=0; while(charsUnread() > 0) { if(prepareChildArrayChunkReading()) { @@ -857,7 +864,7 @@ public int getUnreadChars() { return unreadChars + ((currentStringChunkUnderRead != null)?currentStringChunkUnderRead.getUnreadChars():0); } - public int appendString(String str, int off, int len) { + public int appendString(final String str, final int off, final int len) { if(str.length() > 0) { StringChunk child=new StringChunk(str,off,len); unreadStringChunks.add(child); @@ -887,7 +894,7 @@ private void afterReading() { } } - public int read(char[] ch, int off, int len) { + public int read(final char[] ch, final int off, final int len) { int totalChars = 0; int readLen = Math.min(getUnreadChars(), len); int readOff = off; @@ -901,7 +908,7 @@ public int read(char[] ch, int off, int len) { return totalChars; } - public int writeTo(Writer target) throws IOException { + public int writeTo(final Writer target) throws IOException { int writtenCount=0; while(prepareReading()) { writtenCount+=currentStringChunkUnderRead.writeTo(target); @@ -922,11 +929,11 @@ public int writeTo(Writer target) throws IOException { * */ static final class StringChunk { - private String str; + final private String str; private int readOffset; private int unreadChars; - public StringChunk(String str, int off, int len) { + public StringChunk(final String str, final int off, final int len) { this.str=str; this.readOffset=off; this.unreadChars=len; @@ -936,7 +943,7 @@ public int getUnreadChars() { return unreadChars; } - public int read(char[] ch, int off, int len) { + public int read(final char[] ch, final int off, final int len) { int readCharsLen = Math.min(unreadChars, len); str.getChars(readOffset, (readOffset + readCharsLen), ch, off); readOffset += readCharsLen; @@ -944,7 +951,7 @@ public int read(char[] ch, int off, int len) { return readCharsLen; } - public int writeTo(Writer target) throws IOException { + public int writeTo(final Writer target) throws IOException { int len=unreadChars; target.write(str, readOffset, len); readOffset+=len; @@ -973,14 +980,14 @@ public static interface LazyInitializingWriter { static final class ConnectedWriter { Writer writer; LazyInitializingWriter lazyInitializingWriter; - boolean autoFlush; + final boolean autoFlush; - ConnectedWriter(Writer writer, boolean autoFlush) { + ConnectedWriter(final Writer writer, final boolean autoFlush) { this.writer=writer; this.autoFlush=autoFlush; } - ConnectedWriter(LazyInitializingWriter lazyInitializingWriter, boolean autoFlush) { + ConnectedWriter(final LazyInitializingWriter lazyInitializingWriter, final boolean autoFlush) { this.lazyInitializingWriter=lazyInitializingWriter; this.autoFlush=autoFlush; } @@ -1008,9 +1015,9 @@ public boolean isAutoFlush() { * */ static final class MultiOutputWriter extends Writer { - List writers; + final List writers; - public MultiOutputWriter(List writers) { + public MultiOutputWriter(final List writers) { this.writers = writers; } @@ -1027,14 +1034,14 @@ public void flush() throws IOException { } @Override - public void write(char[] cbuf, int off, int len) throws IOException { + public void write(final char[] cbuf, final int off, final int len) throws IOException { for(ConnectedWriter writer : writers) { writer.getWriter().write(cbuf, off, len); } } @Override - public Writer append(CharSequence csq, int start, int end) + public Writer append(final CharSequence csq, final int start, final int end) throws IOException { for(ConnectedWriter writer : writers) { writer.getWriter().append(csq, start, end); diff --git a/grails/src/java/org/codehaus/groovy/grails/webflow/engine/builder/AbstractDelegate.groovy b/grails/src/java/org/codehaus/groovy/grails/webflow/engine/builder/AbstractDelegate.groovy index 0755d36c5..d08591f09 100644 --- a/grails/src/java/org/codehaus/groovy/grails/webflow/engine/builder/AbstractDelegate.groovy +++ b/grails/src/java/org/codehaus/groovy/grails/webflow/engine/builder/AbstractDelegate.groovy @@ -72,7 +72,7 @@ abstract class AbstractDelegate extends WebRequestDelegatingRequestContext { def application = applicationContext?.getBean(GrailsApplication.APPLICATION_ID) def tagLibraryClass = application?.getArtefactForFeature(TagLibArtefactHandler.TYPE, name) if(tagLibraryClass) { - def ntd = new NamespacedTagDispatcher(tagLibraryClass.namespace,controller ? controller.class : getClass(), application, applicationContext) + def ntd = new NamespacedTagDispatcher(tagLibraryClass.namespace,controller ? controller.class : getClass(), application, applicationContext?.getBean('gspTagLibraryLookup')) AbstractDelegate.metaClass."${GrailsClassUtils.getGetterName(name)}" = {-> ntd } return ntd } diff --git a/grails/src/test/org/codehaus/groovy/grails/plugins/web/ServletsGrailsPluginTests.groovy b/grails/src/test/org/codehaus/groovy/grails/plugins/web/ServletsGrailsPluginTests.groovy index 66023a3fb..0422e1133 100644 --- a/grails/src/test/org/codehaus/groovy/grails/plugins/web/ServletsGrailsPluginTests.groovy +++ b/grails/src/test/org/codehaus/groovy/grails/plugins/web/ServletsGrailsPluginTests.groovy @@ -126,7 +126,7 @@ class ServletsGrailsPluginTests extends AbstractGrailsPluginTests { def results = request.findAll { println it - it.key.startsWith("foo") + it.key.toString().startsWith("foo") } assertEquals( [foo:"bar",foobar:"yes!"],results ) @@ -139,7 +139,7 @@ class ServletsGrailsPluginTests extends AbstractGrailsPluginTests { request["bar"] = "foo" request["foobar"] = "yes!" - def results = request.find { it.key.startsWith("bar") } + def results = request.find { it.key.toString().startsWith("bar") } assertEquals( [bar:"foo"],results ) } diff --git a/grails/src/test/org/codehaus/groovy/grails/web/pages/GroovyPageMethodDispatchTests.groovy b/grails/src/test/org/codehaus/groovy/grails/web/pages/GroovyPageMethodDispatchTests.groovy index ff1c1cca6..247569211 100644 --- a/grails/src/test/org/codehaus/groovy/grails/web/pages/GroovyPageMethodDispatchTests.groovy +++ b/grails/src/test/org/codehaus/groovy/grails/web/pages/GroovyPageMethodDispatchTests.groovy @@ -10,6 +10,7 @@ import org.codehaus.groovy.grails.web.servlet.* import org.springframework.mock.web.* import org.springframework.validation.* import org.springframework.web.servlet.* +import org.codehaus.groovy.grails.web.util.GrailsPrintWriter class GroovyPageMethodDispatchTests extends AbstractGrailsControllerTests { @@ -17,6 +18,8 @@ class GroovyPageMethodDispatchTests extends AbstractGrailsControllerTests { gcl.parseClass( """ import org.codehaus.groovy.grails.web.taglib.* +import org.codehaus.groovy.grails.web.pages.* + class TestController { def index = {} } @@ -35,6 +38,8 @@ class Test1TagLib { } class Test2TagLib { def tag2 = { attrs, body -> out << attrs.test } + def tag3 = { attrs, body -> + out << body() } } class MyPage extends org.codehaus.groovy.grails.web.pages.GroovyPage { String getGroovyPageFileName() { "test" } @@ -43,6 +48,10 @@ class MyPage extends org.codehaus.groovy.grails.web.pages.GroovyPage { out << "foo" "" } + def tagResult=tag3([:], new GroovyPage.ConstantClosure('TEST'))?.toString() + if(tagResult != 'TEST') { + out << '' << tagResult + } out << "hello" + tag2(test:"test2", new GroovyPageTagBody(this, webRequest) { }) @@ -59,14 +68,15 @@ class MyPage extends org.codehaus.groovy.grails.web.pages.GroovyPage { script.setGspTagLibraryLookup(appCtx.getBean('gspTagLibraryLookup')) def controller = ga.getControllerClass("TestController").newInstance() def sw = new StringWriter() - webRequest.out = new PrintWriter(sw) - def b = new Binding(application:controller.servletContext, + webRequest.out = new GrailsPrintWriter(sw) + def b = new GroovyPageBinding(application:controller.servletContext, request:controller.request, response:controller.response, flash:controller.flash, out: webRequest.out , webRequest:webRequest) script.binding = b + script.initRun(webRequest.out, webRequest) script.run() assertEquals "printblahfoohellotest2",sw.toString() diff --git a/grails/src/test/org/codehaus/groovy/grails/web/pages/GroovyPageMethodDispatchWithNamespaceTests.groovy b/grails/src/test/org/codehaus/groovy/grails/web/pages/GroovyPageMethodDispatchWithNamespaceTests.groovy index bef136e57..63eb09394 100644 --- a/grails/src/test/org/codehaus/groovy/grails/web/pages/GroovyPageMethodDispatchWithNamespaceTests.groovy +++ b/grails/src/test/org/codehaus/groovy/grails/web/pages/GroovyPageMethodDispatchWithNamespaceTests.groovy @@ -70,6 +70,7 @@ class MyPage extends org.codehaus.groovy.grails.web.pages.GroovyPage { out: webRequest.out , webRequest:webRequest) script.binding = b + script.initRun(webRequest.out, webRequest) script.run() assertEquals "printblahfoohellotest2",sw.toString() diff --git a/grails/src/test/org/codehaus/groovy/grails/web/pages/GroovyPageTests.groovy b/grails/src/test/org/codehaus/groovy/grails/web/pages/GroovyPageTests.groovy index 1848ee6bb..76f3ab925 100644 --- a/grails/src/test/org/codehaus/groovy/grails/web/pages/GroovyPageTests.groovy +++ b/grails/src/test/org/codehaus/groovy/grails/web/pages/GroovyPageTests.groovy @@ -38,6 +38,7 @@ import org.codehaus.groovy.grails.plugins.* import org.springframework.web.context.request.* import org.codehaus.groovy.grails.web.servlet.mvc.* import org.codehaus.groovy.grails.web.servlet.* +import org.codehaus.groovy.grails.web.util.GrailsPrintWriter; import org.springframework.mock.web.* import org.springframework.validation.* import org.springframework.web.servlet.* @@ -61,7 +62,7 @@ public class GroovyPageTests extends AbstractGrailsControllerTests { "class MyTagLib {\n"+ "def isaid = { attrs, body ->\n"+ "out.print('I said, \"')\n"+ - "body()\n" + + "out << body()\n" + "out.print('\"')\n"+ "}\n"+ "}" ; @@ -90,6 +91,7 @@ public class GroovyPageTests extends AbstractGrailsControllerTests { "class test_index_gsp extends GroovyPage {\n"+ "String getGroovyPageFileName() { \"test\" }\n"+ "public Object run() {\n"+ + "def out=binding.out\n"+ "out.print('

    RunPage test
    ')\n"+ "}\n"+ "}" ; @@ -103,15 +105,14 @@ public class GroovyPageTests extends AbstractGrailsControllerTests { def result = null runTest { StringWriter sw = new StringWriter(); - PrintWriter pw = new PrintWriter(sw); + PrintWriter pw = new GrailsPrintWriter(sw); String contentType = "text/html;charset=UTF-8"; response.setContentType(contentType); // must come before response.getWriter() GroovyPage gspScript = parseGroovyPage(pageCode) gspScript.binding = getBinding(pw) - webRequest.out = pw - + gspScript.initRun(pw, webRequest) gspScript.run() result = sw.toString() } @@ -151,6 +152,7 @@ public class GroovyPageTests extends AbstractGrailsControllerTests { "class test_index_gsp extends GroovyPage {\n"+ "String getGroovyPageFileName() { \"test\" }\n"+ "public Object run() {\n"+ + "def out=binding.out\n"+ "body1 = { out.print('Boo!') }\n"+ "invokeTag('Person','foaf',[a:'b',c:'d'],body1)\n"+ "}\n"+ @@ -181,7 +183,7 @@ public class GroovyPageTests extends AbstractGrailsControllerTests { def getBinding(out) { // if there is no controller in the request configure using existing attributes, creating objects where necessary - Binding binding = new Binding(); + Binding binding = new GroovyPageBinding(); GrailsApplicationAttributes attrs = new DefaultGrailsApplicationAttributes(servletContext) binding.setVariable(GroovyPage.REQUEST, request); binding.setVariable(GroovyPage.RESPONSE, response); @@ -192,8 +194,9 @@ public class GroovyPageTests extends AbstractGrailsControllerTests { binding.setVariable(GrailsApplication.APPLICATION_ID, appContext.getBean(GrailsApplication.APPLICATION_ID)); binding.setVariable(GroovyPage.SESSION, request.getSession()); binding.setVariable(GroovyPage.PARAMS, new GrailsParameterMap(request)); + binding.setVariable(GroovyPage.WEB_REQUEST, webRequest) binding.setVariable(GroovyPage.OUT, out); - binding.setVariable(GroovyPage.WEB_REQUEST, RequestContextHolder.currentRequestAttributes()) + webRequest.out = out return binding } diff --git a/grails/src/test/org/codehaus/groovy/grails/web/pages/GroovyPagesTemplateEngineTests.groovy b/grails/src/test/org/codehaus/groovy/grails/web/pages/GroovyPagesTemplateEngineTests.groovy index ed561cab7..493d2f9fe 100644 --- a/grails/src/test/org/codehaus/groovy/grails/web/pages/GroovyPagesTemplateEngineTests.groovy +++ b/grails/src/test/org/codehaus/groovy/grails/web/pages/GroovyPagesTemplateEngineTests.groovy @@ -30,7 +30,7 @@ class GroovyPagesTemplateEngineTests extends GroovyTestCase { w.writeTo(pw) - assertTrue(sw.toString().indexOf("out.print('hello')") > -1) + assertTrue(sw.toString().indexOf("printToOut('hello')") > -1) } finally { diff --git a/grails/src/test/org/codehaus/groovy/grails/web/pages/ParseTests.java b/grails/src/test/org/codehaus/groovy/grails/web/pages/ParseTests.java index 9f5bdd1b9..d55627966 100644 --- a/grails/src/test/org/codehaus/groovy/grails/web/pages/ParseTests.java +++ b/grails/src/test/org/codehaus/groovy/grails/web/pages/ParseTests.java @@ -65,7 +65,9 @@ public void testParse() throws Exception { "def request = binding.request\n"+ "def flash = binding.flash\n"+ "def response = binding.response\n"+ - "out.print(htmlParts[0])\n"+ + //"def out = binding.out\n"+ + + "printHtmlPart(0)\n"+ "}\n"+ GSP_FOOTER; assertEquals(trimAndRemoveCR(expected), trimAndRemoveCR(result.generatedGsp)); assertEquals("
    hi
    ", result.htmlParts[0]); @@ -82,9 +84,9 @@ public void testParseWithUnclosedSquareBracket() throws Exception { "def request = binding.request\n"+ "def flash = binding.flash\n"+ "def response = binding.response\n"+ + //"def out = binding.out\n"+ - "attrs1 = [\"code\":evaluate('\"[\"', 1, it) { return \"[\" }]\n" + - "invokeTag('message','g',1,attrs1,null)\n"+ + "invokeTag('message','g',1,[\"code\":evaluate('\"[\"', 1, it) { return \"[\" }],null)\n"+ "}\n" + GSP_FOOTER; assertEquals(trimAndRemoveCR(expected), trimAndRemoveCR(output)); @@ -94,7 +96,7 @@ public void testParseWithUnclosedGstringThrowsException() throws IOException { try{ parseCode("myTest3", ""); }catch(GrailsTagException e){ - assertEquals("Unexpected end of file encountered parsing Tag [message] for myTest3. Are you missing a closing brace '}'?", e.getMessage()); + assertEquals("Unexpected end of file encountered parsing Tag [message] for myTest3. Are you missing a closing brace '}'? (myTest3:14)", e.getMessage()); return; } fail("Expected parse exception not thrown"); @@ -128,8 +130,8 @@ public void testParseWithUTF8() throws IOException { "def request = binding.request\n"+ "def flash = binding.flash\n"+ "def response = binding.response\n"+ - - "out.print(htmlParts[0])\n"+ + //"def out = binding.out\n"+ + "printHtmlPart(0)\n"+ "}\n" + GSP_FOOTER;; assertEquals(trimAndRemoveCR(expected), trimAndRemoveCR(output.generatedGsp)); assertEquals(src, output.htmlParts[0]); @@ -163,8 +165,8 @@ public void testParseWithLocalEncoding() throws IOException { "def request = binding.request\n"+ "def flash = binding.flash\n"+ "def response = binding.response\n"+ - - "out.print(htmlParts[0])\n"+ + //"def out = binding.out\n"+ + "printHtmlPart(0)\n"+ "}\n" + GSP_FOOTER;; assertEquals(trimAndRemoveCR(expected), trimAndRemoveCR(output.generatedGsp)); assertEquals(src, output.htmlParts[0]); @@ -228,10 +230,10 @@ public void testParseWithWhitespaceNotEaten() throws Exception { "def request = binding.request\n"+ "def flash = binding.flash\n"+ "def response = binding.response\n"+ - - "out.print(htmlParts[0])\n" + - "out.print(evaluate('uri', 3, it) { return uri })\n"+ - "out.print(htmlParts[1])\n" + + //"def out = binding.out\n"+ + "printHtmlPart(0)\n" + + "printToOut(evaluate('uri', 3, it) { return uri })\n"+ + "printHtmlPart(1)\n" + "}\n" + GSP_FOOTER; diff --git a/grails/src/test/org/codehaus/groovy/grails/web/pages/TagLibWithNullValuesTests.groovy b/grails/src/test/org/codehaus/groovy/grails/web/pages/TagLibWithNullValuesTests.groovy index 78a9ab42e..43a264668 100644 --- a/grails/src/test/org/codehaus/groovy/grails/web/pages/TagLibWithNullValuesTests.groovy +++ b/grails/src/test/org/codehaus/groovy/grails/web/pages/TagLibWithNullValuesTests.groovy @@ -31,11 +31,11 @@ class MyTagLib { void testNullValueHandling() { def template = '

    This is tag1:

    ' - assertOutputEquals '

    This is tag1: org.codehaus.groovy.grails.web.pages.GSPResponseWriter: [abc] []

    ', template + assertOutputEquals '

    This is tag1: org.codehaus.groovy.grails.web.pages.GroovyPageOutputStack$GroovyPageProxyWriter: [abc] []

    ', template template = '

    This is tag2:

    ' - assertOutputEquals '

    This is tag2: org.codehaus.groovy.grails.web.taglib.GroovyPageTagWriter: [abc] []

    ', template + assertOutputEquals '

    This is tag2: org.codehaus.groovy.grails.web.pages.GroovyPageOutputStack$GroovyPageProxyWriter: [abc] []

    ', template } } \ No newline at end of file diff --git a/grails/src/test/org/codehaus/groovy/grails/web/pages/ext/jsp/GroovyPageWithJSPTagsTests.groovy b/grails/src/test/org/codehaus/groovy/grails/web/pages/ext/jsp/GroovyPageWithJSPTagsTests.groovy index 6693f9f10..e3a0276a0 100644 --- a/grails/src/test/org/codehaus/groovy/grails/web/pages/ext/jsp/GroovyPageWithJSPTagsTests.groovy +++ b/grails/src/test/org/codehaus/groovy/grails/web/pages/ext/jsp/GroovyPageWithJSPTagsTests.groovy @@ -154,7 +154,7 @@ goodbye ''' request.setAttribute "address", new TestJspTagAddress(zip:"342343") - assertOutputEquals '''
    \n\nZip:
    ''', + assertOutputEquals '''
    \nZip: \n
    ''', template, [:], { it.toString().trim() } } } diff --git a/grails/src/test/org/codehaus/groovy/grails/web/taglib/AbstractGrailsTagTests.groovy b/grails/src/test/org/codehaus/groovy/grails/web/taglib/AbstractGrailsTagTests.groovy index 7232598c4..98b1fbacf 100644 --- a/grails/src/test/org/codehaus/groovy/grails/web/taglib/AbstractGrailsTagTests.groovy +++ b/grails/src/test/org/codehaus/groovy/grails/web/taglib/AbstractGrailsTagTests.groovy @@ -32,6 +32,7 @@ import org.springframework.ui.context.ThemeSource import org.springframework.ui.context.Theme import org.springframework.ui.context.support.SimpleTheme import org.springframework.context.MessageSource +import org.codehaus.groovy.grails.web.pages.GroovyPageOutputStack; import org.codehaus.groovy.grails.web.pages.GroovyPagesTemplateEngine import org.springframework.context.ApplicationContext import org.codehaus.groovy.grails.web.servlet.mvc.GrailsWebRequest @@ -84,6 +85,9 @@ abstract class AbstractGrailsTagTests extends GroovyTestCase { def withTag(String tagName, Writer out, Closure callable) { def result = null runTest { + def webRequest = RequestContextHolder.currentRequestAttributes() + webRequest.out = out + def mockController = grailsApplication.getControllerClass("MockController").newInstance() request.setAttribute(GrailsApplicationAttributes.CONTROLLER, mockController); @@ -100,9 +104,9 @@ abstract class AbstractGrailsTagTests extends GroovyTestCase { if(go instanceof ApplicationContextAware) { go.applicationContext = appCtx } - def webRequest = RequestContextHolder.currentRequestAttributes() - webRequest.out = out + GroovyPageOutputStack stack=GroovyPageOutputStack.createNew(out) + println "calling tag '${tagName}'" result = callable.call(go.getProperty(tagName)) } @@ -189,7 +193,8 @@ abstract class AbstractGrailsTagTests extends GroovyTestCase { servletContext.setAttribute( GrailsApplicationAttributes.APPLICATION_CONTEXT, appCtx) GroovySystem.metaClassRegistry.removeMetaClass(String.class) GroovySystem.metaClassRegistry.removeMetaClass(Object.class) - grailsApplication.tagLibClasses.each { tc -> GroovySystem.metaClassRegistry.removeMetaClass(tc.clazz)} + // Why are the TagLibClasses removed? + //grailsApplication.tagLibClasses.each { tc -> GroovySystem.metaClassRegistry.removeMetaClass(tc.clazz)} mockManager.doDynamicMethods() request = webRequest.currentRequest request.setAttribute(DispatcherServlet.THEME_SOURCE_ATTRIBUTE, new MockThemeSource(messageSource)) @@ -256,7 +261,6 @@ abstract class AbstractGrailsTagTests extends GroovyTestCase { def text = getCompiledSource(template, params) println "----- GSP SOURCE -----" println text - } def getCompiledSource(template, params = [:]) { @@ -308,8 +312,15 @@ abstract class AbstractGrailsTagTests extends GroovyTestCase { def engine = appCtx.groovyPagesTemplateEngine + //printCompiledSource(template) + assert engine def t = engine.createTemplate(template, "test_"+ System.currentTimeMillis()) + + /* + println "------------HTMLPARTS----------------------" + t.metaInfo.htmlParts.eachWithIndex {it, i -> print "htmlpart[${i}]:\n>${it}<\n--------\n" } + */ def w = t.make(params) @@ -326,6 +337,8 @@ abstract class AbstractGrailsTagTests extends GroovyTestCase { GroovyPagesTemplateEngine engine = appCtx.groovyPagesTemplateEngine + printCompiledSource(template) + assert engine def t = engine.createTemplate(template, "test_"+ System.currentTimeMillis()) @@ -334,7 +347,9 @@ abstract class AbstractGrailsTagTests extends GroovyTestCase { def sw = new StringWriter() def out = new PrintWriter(sw) webRequest.out = out + w.writeTo(out) + out.flush() return sw.toString() } diff --git a/grails/src/test/org/codehaus/groovy/grails/web/taglib/FormTagLib3Tests.groovy b/grails/src/test/org/codehaus/groovy/grails/web/taglib/FormTagLib3Tests.groovy index dc8a98868..ea57264a9 100644 --- a/grails/src/test/org/codehaus/groovy/grails/web/taglib/FormTagLib3Tests.groovy +++ b/grails/src/test/org/codehaus/groovy/grails/web/taglib/FormTagLib3Tests.groovy @@ -20,6 +20,7 @@ public class FormTagLib3Tests extends AbstractGrailsTagTests { private static final Collection DATE_PRECISIONS_INCLUDING_DAY = Collections.unmodifiableCollection(Arrays.asList(["day", "hour", "minute", null] as String[] )) private static final Collection DATE_PRECISIONS_INCLUDING_MONTH = Collections.unmodifiableCollection(Arrays.asList(["month", "day", "hour", "minute", null] as String[] )) + def lineSep = new String([(char)13,(char)10] as char[]) public void testHiddenFieldTag() { final StringWriter sw = new StringWriter(); @@ -76,7 +77,6 @@ public class FormTagLib3Tests extends AbstractGrailsTagTests { def attributes = new TreeMap([name: "testRadio", labels:['radio.1', 'radio.2', 'radio.3'], values:[1,2,3], value: "1"]) tag.call(attributes, {"

    ${it.radio}

    "}) - def lineSep = System.getProperty("line.separator") assertEquals ("

    " + lineSep + "

    " + lineSep + "

    " @@ -91,7 +91,6 @@ public class FormTagLib3Tests extends AbstractGrailsTagTests { def attributes = new TreeMap([name: "testRadio2", values:[3,2], value: "1"]) tag.call(attributes, {"

    ${it.radio}

    "}) - def lineSep = System.getProperty("line.separator") assertEquals ("

    " + lineSep + "

    " + lineSep , sw.toString()) @@ -105,7 +104,6 @@ public class FormTagLib3Tests extends AbstractGrailsTagTests { def attributes = new TreeMap([name: "testRadio2", values:[4,1], value: 1]) tag.call(attributes, {"

    ${it.radio}

    "}) - def lineSep = System.getProperty("line.separator") assertEquals ("

    " + lineSep + "

    " + lineSep , sw.toString()) @@ -119,7 +117,6 @@ public class FormTagLib3Tests extends AbstractGrailsTagTests { def attributes = new TreeMap([name: "testRadio2", values:[4,1]]) tag.call(attributes, {"

    ${it.radio}

    "}) - def lineSep = System.getProperty("line.separator") assertEquals ("

    " + lineSep + "

    " + lineSep , sw.toString()) diff --git a/grails/src/test/org/codehaus/groovy/grails/web/taglib/GroovyEachParseTests.groovy b/grails/src/test/org/codehaus/groovy/grails/web/taglib/GroovyEachParseTests.groovy index ec1a5b89d..de247befc 100644 --- a/grails/src/test/org/codehaus/groovy/grails/web/taglib/GroovyEachParseTests.groovy +++ b/grails/src/test/org/codehaus/groovy/grails/web/taglib/GroovyEachParseTests.groovy @@ -19,11 +19,11 @@ def request = binding.request def flash = binding.flash def response = binding.response -out.print(htmlParts[0]) +printHtmlPart(0) evaluate('"blah"', 2, it) { return "blah" }.each { t -> -out.print(htmlParts[0]) +printHtmlPart(0) } -out.print(htmlParts[0]) +printHtmlPart(0) }""" + GSP_FOOTER ),trimAndRemoveCR(output.toString()) ) assertEquals("\n", output.htmlParts[0]) @@ -42,7 +42,7 @@ def request = binding.request def flash = binding.flash def response = binding.response -out.print(htmlParts[0]) +printHtmlPart(0) evaluate('"blah"', 1, it) { return "blah" }.each { t -> } }""" + GSP_FOOTER @@ -66,11 +66,11 @@ def request = binding.request def flash = binding.flash def response = binding.response -out.print(htmlParts[0]) +printHtmlPart(0) evaluate('"blah"', 2, it) { return "blah" }.eachWithIndex { t,i -> -out.print(htmlParts[0]) +printHtmlPart(0) } -out.print(htmlParts[0]) +printHtmlPart(0) }""" + GSP_FOOTER ),trimAndRemoveCR(output.toString()) ) assertEquals("\n", output.htmlParts[0]) diff --git a/grails/src/test/org/codehaus/groovy/grails/web/taglib/JavascriptTagLibTests.groovy b/grails/src/test/org/codehaus/groovy/grails/web/taglib/JavascriptTagLibTests.groovy index 9e7fac85f..2e320ea58 100644 --- a/grails/src/test/org/codehaus/groovy/grails/web/taglib/JavascriptTagLibTests.groovy +++ b/grails/src/test/org/codehaus/groovy/grails/web/taglib/JavascriptTagLibTests.groovy @@ -10,7 +10,7 @@ import org.springframework.web.util.WebUtils; public class JavascriptTagLibTests extends AbstractGrailsTagTests { - private static final String EOL = System.getProperty("line.separator") + private static final String EOL = new String([(char)13,(char)10] as char[]) public void onSetUp() { gcl.parseClass(''' @@ -39,8 +39,8 @@ class TestUrlMappings { webRequest.controllerName = "foo" def template = '''

    ''' - String newLine = System.getProperty("line.separator") - assertOutputContains('' + newLine + '

    ', template) + String newLine = EOL + assertOutputContains('\r\n

    ', template) } @@ -56,7 +56,7 @@ class TestUrlMappings { controllerClass.metaClass.getPluginContextPath = {-> "/plugin/one"} request.setAttribute(JavascriptTagLib.CONTROLLER, controllerClass.newInstance()) - assertOutputContains '' + EOL, template + assertOutputContains '', template } void testJavascriptIncludeWithPluginNoLeadingSlash() { @@ -210,7 +210,7 @@ class TestUrlMappings { request.setAttribute("org.codehaus.grails.INCLUDED_JS_LIBRARIES", includedLibrary) def attrs = [controller: 'test', action: 'changeTitle', update: 'titleDiv', name: 'title', value: 'testValue'] - tag.call(attrs) {"body"} + def retval = tag.call(attrs) {"body"} assertEquals("" + System.getProperty("line.separator"), sw.toString()) + assertEquals("" + EOL, sw.toString()) } } @@ -242,7 +242,7 @@ class TestUrlMappings { setRequestContext('/otherapp/') def attrs = [src: 'lib.js'] tag.call(attrs) {} - assertEquals("" + System.getProperty("line.separator"), sw.toString()) + assertEquals("" + EOL, sw.toString()) } } @@ -253,7 +253,7 @@ class TestUrlMappings { setupPluginController(tag) def attrs = [library: 'lib'] tag.call(attrs) {} - assertEquals("" + System.getProperty("line.separator"), sw.toString()) + assertEquals("" + EOL, sw.toString()) } } @@ -264,7 +264,7 @@ class TestUrlMappings { def attrs = [src: 'lib.js'] setRequestContext() tag.call(attrs) {} - assertEquals("" + System.getProperty("line.separator"), sw.toString()) + assertEquals("" + EOL, sw.toString()) } } @@ -275,7 +275,7 @@ class TestUrlMappings { def attrs = [src: 'lib.js'] setRequestContext('/otherapp/') tag.call(attrs) {} - assertEquals("" + System.getProperty("line.separator"), sw.toString()) + assertEquals("" + EOL, sw.toString()) } } @@ -287,7 +287,7 @@ class TestUrlMappings { setRequestContext() request.setAttribute(GrailsApplicationAttributes.CONTROLLER, null); tag.call(attrs) {} - assertEquals("" + System.getProperty("line.separator"), sw.toString()) + assertEquals("" + EOL, sw.toString()) } } @@ -298,7 +298,7 @@ class TestUrlMappings { def attrs = [library: 'lib'] setRequestContext() tag.call(attrs) {} - assertEquals("" + System.getProperty("line.separator"), sw.toString()) + assertEquals("" + EOL, sw.toString()) } } @@ -309,7 +309,7 @@ class TestUrlMappings { def attrs = [library: 'lib'] setRequestContext('/otherapp/') tag.call(attrs) {} - assertEquals("" + System.getProperty("line.separator"), sw.toString()) + assertEquals("" + EOL, sw.toString()) } } @@ -319,7 +319,7 @@ class TestUrlMappings { withTag("javascript", pw) {tag -> setRequestContext() tag.call([:]) {"do.this();"} - assertEquals("" + System.getProperty("line.separator"), sw.toString()) + assertEquals("" + EOL, sw.toString()) } } @@ -331,7 +331,7 @@ class TestUrlMappings { def attrs = [library: 'lib', base: 'http://testserver/static/'] setRequestContext() tag.call(attrs) {} - assertEquals("" + System.getProperty("line.separator"), sw.toString()) + assertEquals("" + EOL, sw.toString()) } } @@ -343,7 +343,7 @@ class TestUrlMappings { def attrs = [src: 'mylib.js', base: 'http://testserver/static/'] setRequestContext() tag.call(attrs) {} - assertEquals("" + System.getProperty("line.separator"), sw.toString()) + assertEquals("" + EOL, sw.toString()) } } diff --git a/grails/src/test/org/codehaus/groovy/grails/web/taglib/ValidationTagLibTests.groovy b/grails/src/test/org/codehaus/groovy/grails/web/taglib/ValidationTagLibTests.groovy index 8a1daf33e..194df48ab 100644 --- a/grails/src/test/org/codehaus/groovy/grails/web/taglib/ValidationTagLibTests.groovy +++ b/grails/src/test/org/codehaus/groovy/grails/web/taglib/ValidationTagLibTests.groovy @@ -1,5 +1,7 @@ package org.codehaus.groovy.grails.web.taglib; +import java.util.Locale; + import org.springframework.validation.Errors import org.springframework.validation.FieldError import org.springframework.util.StringUtils @@ -28,6 +30,7 @@ class Article { String title } ''') + } void testFieldValueWithClassAndPropertyNameLookupFromBundle() { @@ -41,6 +44,7 @@ class Article { def template = '' + webRequest.currentRequest.addPreferredLocale(Locale.US) assertOutputEquals 'Property [Subject] of class [Reading Material] cannot be null', template, [book:b] } @@ -56,6 +60,7 @@ class Article { def template = '' + webRequest.currentRequest.addPreferredLocale(Locale.US) assertOutputEquals 'Property [Subject] of class [Reading Material] cannot be null', template, [book:b] } @@ -190,6 +195,7 @@ class Article { def template = '''${err.field}|''' def result = applyTemplate(template, [book:b]) + println result assertTrue result.contains("title|") assertTrue result.contains("releaseDate|") assertTrue result.contains("publisherURL|") diff --git a/grails/src/test/org/codehaus/groovy/grails/webflow/FlowTagInvokationTests.groovy b/grails/src/test/org/codehaus/groovy/grails/webflow/FlowTagInvokationTests.groovy index 2db9bf08f..f01d13413 100644 --- a/grails/src/test/org/codehaus/groovy/grails/webflow/FlowTagInvokationTests.groovy +++ b/grails/src/test/org/codehaus/groovy/grails/webflow/FlowTagInvokationTests.groovy @@ -21,7 +21,7 @@ class FlowTagInvokationTests extends AbstractGrailsTagAwareFlowExecutionTests { void testRegularTagInvokation() { request[GrailsApplicationAttributes.CONTROLLER] = ga.getControllerClass("TestController").newInstance() startFlow() - signalEvent( "two" ) + signalEvent( 'two' ) def model = getFlowScope() @@ -29,9 +29,8 @@ class FlowTagInvokationTests extends AbstractGrailsTagAwareFlowExecutionTests { } void testNamespacedTagInvokation() { - startFlow() - signalEvent( "three" ) + signalEvent( 'three' ) def model = getFlowScope() @@ -50,18 +49,18 @@ class TestController { public Closure getFlowClosure() { return { one { - on("two"){ + on('two'){ [theLink:link(controller:"foo", action:"bar")] - }.to "two" - on("three"){ + }.to 'two' + on('three'){ [theLink:g.link(controller:"foo", action:"bar")] - }.to "three" + }.to 'three' } two { - on("success").to "end" + on('success').to 'end' } three { - on("success").to "end" + on('success').to 'end' } end() } diff --git a/grails/src/test/org/codehaus/groovy/grails/webflow/support/AbstractGrailsTagAwareFlowExecutionTests.groovy b/grails/src/test/org/codehaus/groovy/grails/webflow/support/AbstractGrailsTagAwareFlowExecutionTests.groovy index 3e3b55571..6735af24d 100644 --- a/grails/src/test/org/codehaus/groovy/grails/webflow/support/AbstractGrailsTagAwareFlowExecutionTests.groovy +++ b/grails/src/test/org/codehaus/groovy/grails/webflow/support/AbstractGrailsTagAwareFlowExecutionTests.groovy @@ -137,7 +137,7 @@ abstract class AbstractGrailsTagAwareFlowExecutionTests extends AbstractFlowExec servletContext.setAttribute( GrailsApplicationAttributes.APPLICATION_CONTEXT, appCtx) GroovySystem.metaClassRegistry.removeMetaClass(String.class) GroovySystem.metaClassRegistry.removeMetaClass(Object.class) - grailsApplication.tagLibClasses.each { tc -> GroovySystem.metaClassRegistry.removeMetaClass(tc.clazz)} + //grailsApplication.tagLibClasses.each { tc -> GroovySystem.metaClassRegistry.removeMetaClass(tc.clazz)} mockManager.doDynamicMethods() request = webRequest.currentRequest request.characterEncoding = "utf-8" @@ -147,7 +147,7 @@ abstract class AbstractGrailsTagAwareFlowExecutionTests extends AbstractFlowExec } final void tearDown() { - RequestContextHolder.setRequestAttributes(null) + RequestContextHolder.setRequestAttributes(null) GroovySystem.metaClassRegistry.setMetaClassCreationHandle(originalHandler); onDestroy() @@ -197,7 +197,7 @@ abstract class AbstractGrailsTagAwareFlowExecutionTests extends AbstractFlowExec } - String getFlowId() { "testFlow" } + String getFlowId() { 'testFlow' } abstract Closure getFlowClosure(); }