From a2c53f8d7f26e95b5497c1b6d3a2e85b4c457c4e Mon Sep 17 00:00:00 2001 From: Lari Hotari Date: Fri, 28 Aug 2009 19:38:38 +0300 Subject: [PATCH] Major GSP performance patch . 1) Optimizes GSP -> groovy code generation (less generated Groovy code, more in the Java base class GroovyPage.). Generates less bytecode -> reduces permgen memory usage. Some performance improvements. 2) Uses StreamCharBuffer for buffering taglib body closure's content and makes it possible to pass the body on to the "upper level" without transforming the output to a java.lang.String in between. "out" reference lookup was improved. "out" is a single proxy to the correct Writer instance. There is a stack for handling the "out" Writer replacement. "body()" in taglib returns a StreamCharBuffer instance instead of a java.lang.String. This might break some plugins. 3) Initial changes for adding support for request or session scoped taglibs. A scope property should be defined when the Taglib bean in created in the app context. This feature isn't active yet. Session scoped taglibs could be used for shopping carts etc. The state can be kept directly in the taglib bean's properties. 4) Taglib can return any object value. Not just a java.lang.String. --- .../grails/test/GroovyPagesTestCase.groovy | 5 +- grails/src/java/grails/util/GrailsUtil.java | 81 +++-- .../grails/commons/DefaultArtefactInfo.java | 4 +- .../metaclass/GenericDynamicProperty.java | 3 +- .../metaclass/WeakGenericDynamicProperty.java | 17 +- .../web/GroovyPagesGrailsPlugin.groovy | 33 +- .../web/taglib/JavascriptTagLib.groovy | 4 +- .../web/taglib/ValidationTagLib.groovy | 16 +- .../errors/GrailsWrappedRuntimeException.java | 6 +- .../grails/web/pages/FastStringWriter.java | 55 +-- .../grails/web/pages/GSPResponseWriter.java | 2 +- .../groovy/grails/web/pages/GSPWriter.java | 7 +- .../groovy/grails/web/pages/GroovyPage.java | 323 ++++++++++-------- .../grails/web/pages/GroovyPageBinding.java | 27 ++ .../web/pages/GroovyPageOutputStack.java | 173 ++++++++++ .../grails/web/pages/GroovyPageParser.java | 170 +++++++-- .../grails/web/pages/GroovyPageUtils.java | 14 + .../grails/web/pages/GroovyPageWritable.java | 19 +- .../grails/web/pages/GroovyPagesServlet.java | 3 +- .../grails/web/pages/TagLibraryLookup.java | 24 +- .../pages/ext/jsp/PageContextFactory.groovy | 3 +- .../web/plugins/support/WebMetaUtils.groovy | 68 +--- .../filter/GrailsReloadServletFilter.java | 3 +- .../web/servlet/view/GroovyPageView.java | 3 +- .../sitemesh/GrailsLayoutDecoratorMapper.java | 3 +- .../grails/web/taglib/GrailsTagRegistry.java | 3 +- .../grails/web/taglib/GroovyPageTagBody.java | 79 ++--- .../web/taglib/NamespacedTagDispatcher.groovy | 15 +- .../grails/web/taglib/RenderInputTag.java | 3 +- .../taglib/exceptions/GrailsTagException.java | 4 +- .../grails/web/util/GrailsPrintWriter.java | 89 +++-- .../grails/web/util/StreamCharBuffer.java | 73 ++-- .../engine/builder/AbstractDelegate.groovy | 2 +- .../web/ServletsGrailsPluginTests.groovy | 4 +- .../GroovyPageMethodDispatchTests.groovy | 14 +- ...ageMethodDispatchWithNamespaceTests.groovy | 1 + .../grails/web/pages/GroovyPageTests.groovy | 15 +- .../GroovyPagesTemplateEngineTests.groovy | 2 +- .../groovy/grails/web/pages/ParseTests.java | 26 +- .../pages/TagLibWithNullValuesTests.groovy | 4 +- .../ext/jsp/GroovyPageWithJSPTagsTests.groovy | 2 +- .../web/taglib/AbstractGrailsTagTests.groovy | 23 +- .../grails/web/taglib/FormTagLib3Tests.groovy | 5 +- .../web/taglib/GroovyEachParseTests.groovy | 14 +- .../web/taglib/JavascriptTagLibTests.groovy | 32 +- .../web/taglib/ValidationTagLibTests.groovy | 6 + .../webflow/FlowTagInvokationTests.groovy | 17 +- ...actGrailsTagAwareFlowExecutionTests.groovy | 6 +- 48 files changed, 975 insertions(+), 530 deletions(-) create mode 100644 grails/src/java/org/codehaus/groovy/grails/web/pages/GroovyPageBinding.java create mode 100644 grails/src/java/org/codehaus/groovy/grails/web/pages/GroovyPageOutputStack.java 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(); }