diff --git a/grails-core/src/main/groovy/org/grails/plugins/DefaultGrailsPlugin.java b/grails-core/src/main/groovy/org/grails/plugins/DefaultGrailsPlugin.java index b1cdc115f2c..2cf463c6d62 100644 --- a/grails-core/src/main/groovy/org/grails/plugins/DefaultGrailsPlugin.java +++ b/grails-core/src/main/groovy/org/grails/plugins/DefaultGrailsPlugin.java @@ -504,7 +504,9 @@ public BeanBuilder beans(Closure closure) { public void doWithApplicationContext(ApplicationContext ctx) { if(plugin instanceof Plugin) { - ((Plugin)plugin).doWithApplicationContext(); + Plugin pluginObject = (Plugin) plugin; + pluginObject.setApplicationContext(ctx); + pluginObject.doWithApplicationContext(); } else { Object[] args = {ctx}; diff --git a/grails-plugin-codecs/src/main/groovy/org/grails/plugins/CodecsGrailsPlugin.groovy b/grails-plugin-codecs/src/main/groovy/org/grails/plugins/CodecsGrailsPlugin.groovy index 98ef3c0c8f3..41918b0e263 100644 --- a/grails-plugin-codecs/src/main/groovy/org/grails/plugins/CodecsGrailsPlugin.groovy +++ b/grails-plugin-codecs/src/main/groovy/org/grails/plugins/CodecsGrailsPlugin.groovy @@ -15,6 +15,8 @@ */ package org.grails.plugins +import grails.core.GrailsApplication +import grails.plugins.Plugin import grails.util.GrailsUtil import org.grails.plugins.codecs.DefaultCodecLookup import org.grails.commons.CodecArtefactHandler @@ -41,7 +43,7 @@ import org.grails.plugins.codecs.XMLCodec * @author Jeff Brown * @since 0.4 */ -class CodecsGrailsPlugin { +class CodecsGrailsPlugin extends Plugin { def version = GrailsUtil.getGrailsVersion() def dependsOn = [core:version] def watchedResources = "file:./grails-app/utils/**/*Codec.groovy" @@ -64,14 +66,17 @@ class CodecsGrailsPlugin { RawCodec ] - def onChange = { event -> + @Override + void onChange(Map event) { + def application = grailsApplication if (application.isArtefactOfType(CodecArtefactHandler.TYPE, event.source)) { - def codecClass = application.addArtefact(CodecArtefactHandler.TYPE, event.source) - event.ctx.codecLookup.reInitialize() + application.addArtefact(CodecArtefactHandler.TYPE, event.source) + applicationContext.getBean('codecLookup', DefaultCodecLookup).reInitialize() } } - def doWithSpring = { + @Override + Closure doWithSpring() {{ -> codecLookup(DefaultCodecLookup) - } + }} } diff --git a/grails-plugin-databinding/src/main/groovy/org/grails/plugins/databinding/DataBindingGrailsPlugin.groovy b/grails-plugin-databinding/src/main/groovy/org/grails/plugins/databinding/DataBindingGrailsPlugin.groovy index c3f765b51c4..15e8d69de00 100644 --- a/grails-plugin-databinding/src/main/groovy/org/grails/plugins/databinding/DataBindingGrailsPlugin.groovy +++ b/grails-plugin-databinding/src/main/groovy/org/grails/plugins/databinding/DataBindingGrailsPlugin.groovy @@ -15,6 +15,7 @@ */ package org.grails.plugins.databinding +import grails.plugins.Plugin import grails.util.GrailsUtil import grails.web.databinding.DataBindingUtils import grails.web.databinding.GrailsWebDataBinder @@ -31,39 +32,46 @@ import org.grails.databinding.converters.web.LocaleAwareBigDecimalConverter import org.grails.databinding.converters.web.LocaleAwareNumberConverter /** + * Plugin for configuring the data binding features of Grails + * * @author Jeff Brown + * @author Graeme Rocher + * * @since 2.3 */ -class DataBindingGrailsPlugin { - - def version = GrailsUtil.getGrailsVersion() +class DataBindingGrailsPlugin extends Plugin { - def doWithSpring = { - def databindingConfig + public static final String TRIM_STRINGS = 'grails.databinding.trimStrings' + public static final String CONVERT_EMPTY_STRINGS_TO_NULL = 'grails.databinding.convertEmptyStringsToNull' + public static final String AUTO_GROW_COLLECTION_LIMIT = 'grails.databinding.autoGrowCollectionLimit' + public static final String DATE_FORMATS = 'grails.databinding.dateFormats' - databindingConfig = application?.config?.grails?.databinding + def version = GrailsUtil.getGrailsVersion() - def autoGrowCollectionLimitSetting = databindingConfig?.autoGrowCollectionLimit + @Override + Closure doWithSpring() {{-> + def application = grailsApplication + def config = application.config + boolean trimStringsSetting = config.getProperty(TRIM_STRINGS, Boolean, true) + boolean convertEmptyStringsToNullSetting = config.getProperty(CONVERT_EMPTY_STRINGS_TO_NULL, Boolean, true) + Integer autoGrowCollectionLimitSetting = config.getProperty(AUTO_GROW_COLLECTION_LIMIT, Integer, 256) + List dateFormats = config.getProperty(DATE_FORMATS, List, []) - "${DataBindingUtils.DATA_BINDER_BEAN_NAME}"(GrailsWebDataBinder, ref('grailsApplication')) { + "${DataBindingUtils.DATA_BINDER_BEAN_NAME}"(GrailsWebDataBinder, grailsApplication) { // trimStrings defaults to TRUE - trimStrings = !Boolean.FALSE.equals(databindingConfig?.trimStrings) - + trimStrings = trimStringsSetting // convertEmptyStringsToNull defaults to TRUE - convertEmptyStringsToNull = !Boolean.FALSE.equals(databindingConfig?.convertEmptyStringsToNull) - + convertEmptyStringsToNull = convertEmptyStringsToNullSetting // autoGrowCollectionLimit defaults to 256 - if(autoGrowCollectionLimitSetting instanceof Integer) { - autoGrowCollectionLimit = autoGrowCollectionLimitSetting - } + autoGrowCollectionLimit = autoGrowCollectionLimitSetting } timeZoneConverter(TimeZoneConverter) defaultDateConverter(DateConversionHelper) { - if(databindingConfig?.dateFormats instanceof List) { - formatStrings = databindingConfig.dateFormats + if(dateFormats) { + formatStrings = dateFormats } } [Short, Short.TYPE, @@ -90,5 +98,5 @@ class DataBindingGrailsPlugin { halXmlDataBindingSourceCreator(HalXmlDataBindingSourceCreator) defaultCurrencyConverter CurrencyValueConverter - } + }} } diff --git a/grails-plugin-domain-class/src/main/groovy/org/grails/plugins/domain/DomainClassGrailsPlugin.groovy b/grails-plugin-domain-class/src/main/groovy/org/grails/plugins/domain/DomainClassGrailsPlugin.groovy index b9e457a84c1..9b5d5accd04 100644 --- a/grails-plugin-domain-class/src/main/groovy/org/grails/plugins/domain/DomainClassGrailsPlugin.groovy +++ b/grails-plugin-domain-class/src/main/groovy/org/grails/plugins/domain/DomainClassGrailsPlugin.groovy @@ -15,47 +15,32 @@ */ package org.grails.plugins.domain -import grails.artefact.Enhanced import grails.config.Config import grails.core.ComponentCapableDomainClass import grails.core.GrailsApplication import grails.core.GrailsDomainClass -import grails.core.support.GrailsApplicationAware -import grails.util.GrailsClassUtils +import grails.plugins.Plugin import grails.util.GrailsUtil import grails.validation.ConstraintsEvaluator -import grails.validation.ValidationErrors import groovy.transform.CompileStatic - import org.grails.core.artefact.DomainClassArtefactHandler import org.grails.core.legacy.LegacyGrailsApplication -import org.grails.core.support.GrailsDomainConfigurationUtil -import org.grails.core.util.SoftThreadLocalMap import org.grails.datastore.gorm.config.GrailsDomainClassMappingContext -import org.grails.datastore.mapping.model.MappingContext import org.grails.datastore.mapping.reflect.ClassPropertyFetcher -import org.grails.datastore.mapping.simple.SimpleMapDatastore -import org.grails.plugins.domain.support.GormApiSupport import org.grails.plugins.domain.support.GrailsDomainClassCleaner import org.grails.validation.ConstraintEvalUtils import org.grails.validation.ConstraintsEvaluatorFactoryBean import org.grails.validation.GrailsDomainClassValidator -import org.springframework.beans.BeanUtils import org.springframework.beans.factory.config.AutowireCapableBeanFactory import org.springframework.beans.factory.config.MethodInvokingFactoryBean import org.springframework.context.ApplicationContext -import org.springframework.context.ConfigurableApplicationContext -import org.springframework.validation.BeanPropertyBindingResult -import org.springframework.validation.Errors -import org.springframework.validation.Validator - /** * Configures the domain classes in the spring context. * * @author Graeme Rocher * @since 0.4 */ -class DomainClassGrailsPlugin implements GrailsApplicationAware{ +class DomainClassGrailsPlugin extends Plugin { def watchedResources = ["file:./grails-app/domain/**/*.groovy", "file:./plugins/*/grails-app/domain/**/*.groovy"] @@ -66,7 +51,7 @@ class DomainClassGrailsPlugin implements GrailsApplicationAware{ GrailsApplication grailsApplication - def doWithSpring = { + Closure doWithSpring() {{-> def application = grailsApplication def config = application.config @@ -108,19 +93,19 @@ class DomainClassGrailsPlugin implements GrailsApplicationAware{ delegate.grailsApplication = application } } - } + }} static getDefaultConstraints(Config config) { ConstraintEvalUtils.getDefaultConstraints(config) } - static final PROPERTY_INSTANCE_MAP = new SoftThreadLocalMap() - - def doWithDynamicMethods = { ApplicationContext ctx-> - enhanceDomainClasses(grailsApplication, ctx) + @Override + void doWithDynamicMethods() { + enhanceDomainClasses(grailsApplication, applicationContext) } - def onChange = { event -> + @Override + void onChange(Map event) { def cls = event.source try { ClassPropertyFetcher.@cachedClassPropertyFetchers.clear() @@ -132,6 +117,8 @@ class DomainClassGrailsPlugin implements GrailsApplicationAware{ return } + + def application = grailsApplication final domainClass = application.addArtefact(DomainClassArtefactHandler.TYPE, cls) if (domainClass.abstract) { return @@ -157,15 +144,15 @@ class DomainClassGrailsPlugin implements GrailsApplicationAware{ messageSource = ref("messageSource") bean.lazyInit = true domainClass = ref("${domainClass.fullName}DomainClass") - grailsApplication = ref("grailsApplication", true) + application = ref("grailsApplication", true) } } beans.registerBeans(event.ctx) - enhanceDomainClasses(event.application, event.ctx) - event.application.refreshConstraints() + enhanceDomainClasses(application, applicationContext) + application.refreshConstraints() } - def onConfigChange = { event -> + void onConfigChange(Map event) { ConstraintEvalUtils.clearDefaultConstraints() def beans = beans { def defaultConstraintsMap = getDefaultConstraints(event.source) @@ -173,8 +160,8 @@ class DomainClassGrailsPlugin implements GrailsApplicationAware{ defaultConstraints = defaultConstraintsMap } } - beans.registerBeans(event.ctx) - event.application.refreshConstraints() + beans.registerBeans(applicationContext) + grailsApplication.refreshConstraints() } @@ -183,10 +170,9 @@ class DomainClassGrailsPlugin implements GrailsApplicationAware{ enhanceDomainClasses(((LegacyGrailsApplication)application).grailsApplication, ctx) } - static enhanceDomainClasses(GrailsApplication application, ApplicationContext ctx) { + static void enhanceDomainClasses(GrailsApplication application, ApplicationContext ctx) { for (GrailsDomainClass dc in application.domainClasses) { def domainClass = dc - def isEnhanced = dc.clazz.getAnnotation(Enhanced) != null if (dc instanceof ComponentCapableDomainClass) { for (GrailsDomainClass component in dc.getComponents()) { if (!application.isDomainClass(component.clazz)) { @@ -195,106 +181,16 @@ class DomainClassGrailsPlugin implements GrailsApplicationAware{ } } MetaClass metaClass = domainClass.metaClass - registerConstraintsProperty(metaClass, domainClass) - addRelationshipManagementMethods(domainClass, ctx) - metaClass.getDomainClass = {-> domainClass } - if (!isEnhanced) { - if (!dc.abstract) { - metaClass.constructor = { -> - getDomainInstance domainClass, ctx - } - } - metaClass.ident = {-> delegate[domainClass.identifier.name] } - metaClass.static.create = { -> - getDomainInstance domainClass, ctx - } - addValidationMethods(application, domainClass, ctx) - } - else { - AutowireCapableBeanFactory autowireCapableBeanFactory = ctx.autowireCapableBeanFactory - int byName = AutowireCapableBeanFactory.AUTOWIRE_BY_NAME - metaClass.static.autowireDomain = { instance -> - autowireCapableBeanFactory.autowireBeanProperties(instance, byName, false) - } + AutowireCapableBeanFactory autowireCapableBeanFactory = ctx.autowireCapableBeanFactory + int byName = AutowireCapableBeanFactory.AUTOWIRE_BY_NAME + metaClass.static.autowireDomain = { instance -> + autowireCapableBeanFactory.autowireBeanProperties(instance, byName, false) } } } - public static void addValidationMethods(GrailsApplication application, GrailsDomainClass dc, ApplicationContext ctx) { - def isEnhanced = dc.clazz.getAnnotation(Enhanced) != null - if(isEnhanced && ctx) { - if(!dc.abstract) { - def datastore = new SimpleMapDatastore(ctx.getBean("grailsDomainClassMappingContext", MappingContext), ctx as ConfigurableApplicationContext) - String validatorBeanName = "${dc.fullName}Validator" - Validator validator - if(ctx.containsBean(validatorBeanName)) { - validator = ctx.getBean(validatorBeanName, Validator) - } else { - validator = new GrailsDomainClassValidator() - validator.domainClass = dc - validator.grailsApplication = application - } - dc.clazz.setInstanceGormValidationApi(GormApiSupport.getGormValidationApi(datastore, dc.clazz, validator)) - return - } - } - - def metaClass = dc.metaClass - def domainClass = dc - - metaClass.hasErrors = {-> delegate.errors?.hasErrors() } - - def get - def put - try { - def rch = application.classLoader.loadClass("org.springframework.web.context.request.RequestContextHolder") - get = { - def attributes = rch.getRequestAttributes() - if (attributes) { - return attributes.request.getAttribute(it) - } - return PROPERTY_INSTANCE_MAP.get().get(it) - } - put = { key, val -> - def attributes = rch.getRequestAttributes() - if (attributes) { - attributes.request.setAttribute(key,val) - } - else { - PROPERTY_INSTANCE_MAP.get().put(key,val) - } - } - } - catch (Throwable e) { - get = { PROPERTY_INSTANCE_MAP.get().get(it) } - put = { key, val -> PROPERTY_INSTANCE_MAP.get().put(key,val) } - } - - metaClass.getErrors = { -> - def errors - def key = "org.codehaus.groovy.grails.ERRORS_${delegate.class.name}_${System.identityHashCode(delegate)}" - errors = get(key) - if (!errors) { - errors = new ValidationErrors(delegate) - put key, errors - } - errors - } - metaClass.setErrors = { Errors errors -> - def key = "org.codehaus.groovy.grails.ERRORS_${delegate.class.name}_${System.identityHashCode(delegate)}" - put key, errors - } - metaClass.clearErrors = { -> - delegate.setErrors (new BeanPropertyBindingResult(delegate, delegate.getClass().getName())) - } - if (!domainClass.hasMetaMethod("validate")) { - metaClass.validate = { -> - DomainClassPluginSupport.validateInstance(delegate, ctx) - } - } - } /** * Registers the constraints property for the given MetaClass and domainClass instance @@ -305,114 +201,4 @@ class DomainClassGrailsPlugin implements GrailsApplicationAware{ metaClass.getConstraints = {-> domainClass.constrainedProperties } } - private static getDomainInstance(domainClass, ctx) { - def obj - if (ctx.containsBean(domainClass.fullName)) { - obj = ctx.getBean(domainClass.fullName) - } - else { - obj = BeanUtils.instantiateClass(domainClass.clazz) - } - obj - } - - static addRelationshipManagementMethods(GrailsDomainClass dc, ApplicationContext ctx) { - def metaClass = dc.metaClass - for (p in dc.persistentProperties) { - def prop = p - if (prop.basicCollectionType) { - def collectionName = GrailsClassUtils.getClassNameRepresentation(prop.name) - metaClass."addTo$collectionName" = { obj -> - if (obj instanceof CharSequence && !(obj instanceof String)) { - obj = obj.toString() - } - if (prop.referencedPropertyType.isInstance(obj)) { - if (delegate[prop.name] == null) { - delegate[prop.name] = GrailsClassUtils.createConcreteCollection(prop.type) - } - delegate[prop.name] << obj - return delegate - } - else { - throw new MissingMethodException("addTo${collectionName}", dc.clazz, [obj] as Object[]) - } - } - metaClass."removeFrom$collectionName" = { obj -> - if (delegate[prop.name]) { - if (obj instanceof CharSequence && !(obj instanceof String)) { - obj = obj.toString() - } - delegate[prop.name].remove(obj) - } - return delegate - } - } - else if (prop.oneToOne || prop.manyToOne) { - def identifierPropertyName = "${prop.name}Id" - if (!dc.hasMetaProperty(identifierPropertyName)) { - def getterName = GrailsClassUtils.getGetterName(identifierPropertyName) - metaClass."$getterName" = {-> GrailsDomainConfigurationUtil.getAssociationIdentifier( - delegate, prop.name, prop.referencedDomainClass) } - } - } - else if (prop.oneToMany || prop.manyToMany) { - if (metaClass instanceof ExpandoMetaClass) { - def propertyName = prop.name - def collectionName = GrailsClassUtils.getClassNameRepresentation(propertyName) - def otherDomainClass = prop.referencedDomainClass - - metaClass."addTo${collectionName}" = { Object arg -> - Object obj - if (delegate[prop.name] == null) { - delegate[prop.name] = GrailsClassUtils.createConcreteCollection(prop.type) - } - if (arg instanceof Map) { - obj = getDomainInstance(otherDomainClass, ctx) - obj.properties = arg - delegate[prop.name].add(obj) - } - else if (otherDomainClass.clazz.isInstance(arg)) { - obj = arg - delegate[prop.name].add(obj) - } - else { - throw new MissingMethodException("addTo${collectionName}", dc.clazz, [arg] as Object[]) - } - if (prop.bidirectional && prop.otherSide) { - def otherSide = prop.otherSide - if (otherSide.oneToMany || otherSide.manyToMany) { - String name = prop.otherSide.name - if (!obj[name]) { - obj[name] = GrailsClassUtils.createConcreteCollection(prop.otherSide.type) - } - obj[prop.otherSide.name].add(delegate) - } - else { - obj[prop.otherSide.name] = delegate - } - } - delegate - } - metaClass."removeFrom${collectionName}" = {Object arg -> - if (otherDomainClass.clazz.isInstance(arg)) { - delegate[prop.name]?.remove(arg) - if (prop.bidirectional && prop.otherSide) { - if (prop.manyToMany) { - String name = prop.otherSide.name - arg[name]?.remove(delegate) - } - else { - arg[prop.otherSide.name] = null - } - } - } - else { - throw new MissingMethodException("removeFrom${collectionName}", dc.clazz, [arg] as Object[]) - } - delegate - } - } - } - } - } } diff --git a/grails-plugin-domain-class/src/test/groovy/org/codehaus/groovy/grails/plugins/AddToSpec.groovy b/grails-plugin-domain-class/src/test/groovy/org/codehaus/groovy/grails/plugins/AddToSpec.groovy deleted file mode 100644 index 47b4eb4d49a..00000000000 --- a/grails-plugin-domain-class/src/test/groovy/org/codehaus/groovy/grails/plugins/AddToSpec.groovy +++ /dev/null @@ -1,75 +0,0 @@ -package org.codehaus.groovy.grails.plugins - -import grails.persistence.Entity -import grails.core.DefaultGrailsApplication -import grails.core.GrailsDomainClass -import org.grails.plugins.domain.DomainClassGrailsPlugin -import spock.lang.Specification - -class AddToSpec extends Specification { - - def "Test that the addTo method assigns the correct instance of collection"() { - given: - def application = new DefaultGrailsApplication([Project, Member] as Class[], getClass().classLoader) - application.initialise() - - GrailsDomainClass projectClass = application.getDomainClass(Project.name) - GrailsDomainClass memberClass = application.getDomainClass(Member.name) - - DomainClassGrailsPlugin.addRelationshipManagementMethods(projectClass, null) - - def project = projectClass.newInstance() - def member = memberClass.newInstance() - - when: - project.addToManagers(member).addToManagers(member) - - then: - project.managers.size() == 1 - project.managers instanceof Set - - when: - project.addToDevelopers(member).addToDevelopers(member) - - then: - project.developers.size() == 1 - project.developers instanceof SortedSet - - when: - project.addToTesters(member).addToTesters(member) - - then: - project.testers.size() == 2 - project.testers instanceof List - - when: - project.addToSales(member).addToSales(member) - - then: - project.sales.size() == 2 - project.sales instanceof List - } - -} - -@Entity -class Project { - SortedSet developers - List testers - Collection sales - - static hasMany = [ - managers: Member, - developers: Member, - testers: Member, - sales: Member - ] -} - -@Entity -class Member implements Comparable { - String name - - @Override - int compareTo(Object o) { name <=> o?.name } -} diff --git a/grails-plugin-gsp/src/main/groovy/org/grails/plugins/web/GroovyPagesGrailsPlugin.groovy b/grails-plugin-gsp/src/main/groovy/org/grails/plugins/web/GroovyPagesGrailsPlugin.groovy index 9b4b247520d..fbfdd222d4a 100644 --- a/grails-plugin-gsp/src/main/groovy/org/grails/plugins/web/GroovyPagesGrailsPlugin.groovy +++ b/grails-plugin-gsp/src/main/groovy/org/grails/plugins/web/GroovyPagesGrailsPlugin.groovy @@ -14,20 +14,20 @@ * limitations under the License. */ package org.grails.plugins.web - +import grails.config.Config import grails.core.GrailsApplication import grails.core.GrailsClass import grails.core.GrailsTagLibClass -import grails.core.support.GrailsApplicationAware import grails.gsp.PageRenderer import grails.plugins.GrailsPluginManager -import grails.plugins.PluginManagerAware +import grails.plugins.Plugin import grails.util.BuildSettings import grails.util.Environment import grails.util.GrailsUtil import grails.web.pages.GroovyPagesUriService import grails.web.util.GrailsApplicationAttributes import groovy.transform.CompileStatic +import groovy.util.logging.Commons import org.grails.buffer.StreamCharBufferMetaUtils import org.grails.core.artefact.TagLibArtefactHandler import org.grails.plugins.web.api.ControllerTagLibraryApi @@ -40,27 +40,28 @@ import org.grails.web.pages.* import org.grails.web.pages.discovery.CachingGrailsConventionGroovyPageLocator import org.grails.web.pages.discovery.CachingGroovyPageStaticResourceLocator import org.grails.web.pages.ext.jsp.TagLibraryResolverImpl -import org.grails.web.servlet.context.GrailsConfigUtils import org.grails.web.servlet.view.GroovyPageViewResolver import org.grails.web.sitemesh.GroovyPageLayoutFinder import org.grails.web.taglib.TagLibraryLookup import org.grails.web.taglib.util.TagLibraryMetaUtils import org.springframework.beans.factory.config.PropertiesFactoryBean -import org.springframework.boot.context.embedded.ServletContextInitializer -import org.springframework.context.ApplicationContext +import org.springframework.boot.context.embedded.ServletRegistrationBean import org.springframework.util.ClassUtils import org.springframework.web.servlet.view.InternalResourceViewResolver - -import javax.servlet.ServletContext -import javax.servlet.ServletException - /** * Sets up and configures the GSP and GSP tag library support in Grails. * * @author Graeme Rocher * @since 1.1 */ -class GroovyPagesGrailsPlugin implements ServletContextInitializer, GrailsApplicationAware, PluginManagerAware{ +@Commons +class GroovyPagesGrailsPlugin extends Plugin { + + public static final String GSP_RELOAD_INTERVAL = "grails.gsp.reload.interval" + public static final String GSP_VIEWS_DIR = 'grails.gsp.view.dir' + public static final String GSP_VIEW_LAYOUT_RESOLVER_ENABLED = 'grails.gsp.view.layoutViewResolver' + public static final String SITEMESH_DEFAULT_LAYOUT = 'grails.sitemesh.default.layout' + public static final String SITEMESH_ENABLE_NONGSP = 'grails.sitemesh.enable.nongsp' def watchedResources = ["file:./plugins/*/grails-app/taglib/**/*TagLib.groovy", "file:./grails-app/taglib/**/*TagLib.groovy"] @@ -90,34 +91,48 @@ class GroovyPagesGrailsPlugin implements ServletContextInitializer, GrailsApplic * Clear the page cache with the ApplicationContext is loaded */ @CompileStatic - def doWithApplicationContext(ApplicationContext ctx) { - ctx.getBean("groovyPagesTemplateEngine", GroovyPagesTemplateEngine).clearPageCache() + @Override + void doWithApplicationContext() { + applicationContext.getBean("groovyPagesTemplateEngine", GroovyPagesTemplateEngine).clearPageCache() } /** * Configures the various Spring beans required by GSP */ - def doWithSpring = { - RuntimeSpringConfiguration spring = springConfig + Closure doWithSpring() {{-> def application = grailsApplication + Config config = application.config + boolean developmentMode = !application.warDeployed + Environment env = Environment.current + + boolean enableReload = env.isReloadEnabled() || + config.getProperty(GroovyPagesTemplateEngine.CONFIG_PROPERTY_GSP_ENABLE_RELOAD, Boolean, false) || + (developmentMode && env == Environment.DEVELOPMENT) + boolean warDeployedWithReload = application.warDeployed && enableReload + + long gspCacheTimeout = config.getProperty(GSP_RELOAD_INTERVAL, Long, (developmentMode && env == Environment.DEVELOPMENT) ? 0L : 5000L) + boolean enableCacheResources = !config.getProperty(GroovyPagesTemplateEngine.CONFIG_PROPERTY_DISABLE_CACHING_RESOURCES, Boolean, false) + String viewsDir = config.getProperty(GSP_VIEWS_DIR, '') + def disableLayoutViewResolver = config.getProperty(GSP_VIEW_LAYOUT_RESOLVER_ENABLED, Boolean, true) + String defaultDecoratorNameSetting = config.getProperty(SITEMESH_DEFAULT_LAYOUT, '') + def sitemeshEnableNonGspViews = config.getProperty(SITEMESH_ENABLE_NONGSP, Boolean, false) + + + + RuntimeSpringConfiguration spring = springConfig + + + // resolves JSP tag libraries jspTagLibraryResolver(TagLibraryResolverImpl) // resolves GSP tag libraries gspTagLibraryLookup(TagLibraryLookup) - boolean developmentMode = !application.warDeployed - Environment env = Environment.current - boolean enableReload = env.isReloadEnabled() || - GrailsConfigUtils.isConfigTrue(application, GroovyPagesTemplateEngine.CONFIG_PROPERTY_GSP_ENABLE_RELOAD) || - (developmentMode && env == Environment.DEVELOPMENT) - long gspCacheTimeout = Long.getLong("grails.gsp.reload.interval", (developmentMode && env == Environment.DEVELOPMENT) ? 0L : 5000L) - boolean warDeployedWithReload = application.warDeployed && enableReload - boolean enableCacheResources = !(application?.flatConfig?.get(GroovyPagesTemplateEngine.CONFIG_PROPERTY_DISABLE_CACHING_RESOURCES) == true) boolean customResourceLoader = false // If the development environment is used we need to load GSP files relative to the base directory // as oppose to in WAR deployment where views are loaded from /WEB-INF - def viewsDir = application.config.grails.gsp.view.dir + if (viewsDir) { log.info "Configuring GSP views directory as '${viewsDir}'" customResourceLoader = true @@ -197,8 +212,8 @@ class GroovyPagesGrailsPlugin implements ServletContextInitializer, GrailsApplic groovyPageLayoutFinder(GroovyPageLayoutFinder) { gspReloadEnabled = enableReload - defaultDecoratorName = application.flatConfig['grails.sitemesh.default.layout'] ?: null - enableNonGspViews = application.flatConfig['grails.sitemesh.enable.nongsp'] ?: false + defaultDecoratorName = defaultDecoratorNameSetting ?: null + enableNonGspViews = sitemeshEnableNonGspViews } // Setup the GroovyPagesUriService @@ -226,7 +241,8 @@ class GroovyPagesGrailsPlugin implements ServletContextInitializer, GrailsApplic // "grails.gsp.view.layoutViewResolver=false" can be used to disable GrailsLayoutViewResolver // containsKey check must be made to check existence of boolean false values in ConfigObject - if(!(application.config.grails.gsp.view.containsKey('layoutViewResolver') && application.config.grails.gsp.view.layoutViewResolver==false)) { + + if(disableLayoutViewResolver) { grailsLayoutViewResolverPostProcessor(GrailsLayoutViewResolverPostProcessor) } @@ -249,10 +265,16 @@ class GroovyPagesGrailsPlugin implements ServletContextInitializer, GrailsApplic errorsViewStackTracePrinter(ErrorsViewStackTracePrinter, ref('grailsResourceLocator')) - javascriptLibraryHandlerInterceptor(JavascriptLibraryHandlerInterceptor, ref('grailsApplication')) + javascriptLibraryHandlerInterceptor(JavascriptLibraryHandlerInterceptor, application) - filteringCodecsByContentTypeSettings(FilteringCodecsByContentTypeSettings, ref('grailsApplication')) - } + filteringCodecsByContentTypeSettings(FilteringCodecsByContentTypeSettings, application) + + groovyPagesServlet(ServletRegistrationBean, new GroovyPagesServlet(), "*.gsp") { + if(Environment.isDevelopmentMode()) { + initParameters = [showSource:"1"] + } + } + }} static String transformToValidLocation(String location) { if (location == '.') return location @@ -265,22 +287,24 @@ class GroovyPagesGrailsPlugin implements ServletContextInitializer, GrailsApplic * Sets up dynamic methods required by the GSP implementation including dynamic tag method dispatch */ @CompileStatic - def doWithDynamicMethods(ApplicationContext ctx) { + @Override + void doWithDynamicMethods() { StreamCharBufferMetaUtils.registerStreamCharBufferMetaClass() - TagLibraryLookup gspTagLibraryLookup = ctx.getBean('gspTagLibraryLookup',TagLibraryLookup) + TagLibraryLookup gspTagLibraryLookup = applicationContext.getBean('gspTagLibraryLookup',TagLibraryLookup) for(GrailsClass cls in grailsApplication.getArtefacts(TagLibArtefactHandler.TYPE)) { TagLibraryMetaUtils.enhanceTagLibMetaClass((GrailsTagLibClass)cls, gspTagLibraryLookup) } } - def onChange = { event -> + @Override + void onChange(Map event) { def application = grailsApplication - def ctx = event.ctx ?: application.mainContext + def ctx = applicationContext if (application.isArtefactOfType(TagLibArtefactHandler.TYPE, event.source)) { - GrailsClass taglibClass = application.addArtefact(TagLibArtefactHandler.TYPE, event.source) + GrailsTagLibClass taglibClass = (GrailsTagLibClass)application.addArtefact(TagLibArtefactHandler.TYPE, event.source) if (taglibClass) { // replace tag library bean def beanName = taglibClass.fullName @@ -293,28 +317,18 @@ class GroovyPagesGrailsPlugin implements ServletContextInitializer, GrailsApplic // The tag library lookup class caches "tag -> taglib class" // so we need to update it now. - def lookup = event.ctx.gspTagLibraryLookup + def lookup = applicationContext.getBean('gspTagLibraryLookup', TagLibraryLookup) lookup.registerTagLib(taglibClass) - - TagLibraryMetaUtils.enhanceTagLibMetaClass(taglibClass, ctx.gspTagLibraryLookup) + TagLibraryMetaUtils.enhanceTagLibMetaClass(taglibClass, lookup) } } // clear uri cache after changes ctx.getBean('groovyPagesUriService',GroovyPagesUriService).clear() } - def onConfigChange = { event -> - def ctx = event.ctx ?: application.mainContext - ctx.filteringCodecsByContentTypeSettings.initialize(application) - } - - @Override @CompileStatic - void onStartup(ServletContext servletContext) throws ServletException { - def gspServlet = servletContext.addServlet("gsp", GroovyPagesServlet) - gspServlet.addMapping("*.gsp") - if(Environment.isDevelopmentMode()) { - gspServlet.setInitParameter("showSource", "1") - } + void onConfigChange(Map event) { + applicationContext.getBean('filteringCodecsByContentTypeSettings', FilteringCodecsByContentTypeSettings).initialize(grailsApplication) } + } diff --git a/grails-plugin-testing/src/main/groovy/grails/test/runtime/ControllerTestPlugin.groovy b/grails-plugin-testing/src/main/groovy/grails/test/runtime/ControllerTestPlugin.groovy index 7470547ada1..7347f99a8ab 100644 --- a/grails-plugin-testing/src/main/groovy/grails/test/runtime/ControllerTestPlugin.groovy +++ b/grails-plugin-testing/src/main/groovy/grails/test/runtime/ControllerTestPlugin.groovy @@ -16,6 +16,7 @@ package grails.test.runtime +import grails.config.Settings import grails.core.GrailsApplication import grails.test.mixin.support.GroovyPageUnitTestResourceLoader import grails.test.mixin.support.LazyTagLibraryLookup @@ -80,11 +81,11 @@ class ControllerTestPlugin implements TestPlugin { def config = grailsApplication.config defineBeans(runtime) { rendererRegistry(DefaultRendererRegistry) { - modelSuffix = config.flatten().get('grails.scaffolding.templates.domainSuffix') ?: '' + modelSuffix = config.getProperty('grails.scaffolding.templates.domainSuffix', '') } instanceControllerTagLibraryApi(ControllerTagLibraryApi) - def urlConverterType = config?.grails?.web?.url?.converter + String urlConverterType = config.getProperty(Settings.WEB_URL_CONVERTER) "${grails.web.UrlConverter.BEAN_NAME}"('hyphenated' == urlConverterType ? HyphenatedUrlConverter : CamelCaseUrlConverter) grailsLinkGenerator(DefaultLinkGenerator, config?.grails?.serverURL ?: "http://localhost:8080") @@ -124,7 +125,7 @@ class ControllerTestPlugin implements TestPlugin { localeResolver(SessionLocaleResolver) } - defineBeans(runtime, new CodecsGrailsPlugin().doWithSpring) + defineBeans(runtime, new CodecsGrailsPlugin().doWithSpring()) } protected void applicationInitialized(TestRuntime runtime, GrailsApplication grailsApplication) { diff --git a/grails-plugin-testing/src/main/groovy/grails/test/runtime/CoreBeansTestPlugin.groovy b/grails-plugin-testing/src/main/groovy/grails/test/runtime/CoreBeansTestPlugin.groovy index 40f1f33d2c0..2b95af1f1eb 100644 --- a/grails-plugin-testing/src/main/groovy/grails/test/runtime/CoreBeansTestPlugin.groovy +++ b/grails-plugin-testing/src/main/groovy/grails/test/runtime/CoreBeansTestPlugin.groovy @@ -58,7 +58,10 @@ public class CoreBeansTestPlugin implements TestPlugin { @CompileStatic(TypeCheckingMode.SKIP) protected void registerBeans(TestRuntime runtime, GrailsApplication grailsApplicationParam) { - defineBeans(runtime, new DataBindingGrailsPlugin().doWithSpring) + def plugin = new DataBindingGrailsPlugin() + plugin.grailsApplication = grailsApplicationParam + plugin.applicationContext = grailsApplicationParam.mainContext + defineBeans(runtime, plugin.doWithSpring()) defineBeans(runtime) { xmlns context:"http://www.springframework.org/schema/context" diff --git a/grails-test-suite-base/src/main/groovy/org/grails/web/servlet/mvc/AbstractGrailsControllerTests.groovy b/grails-test-suite-base/src/main/groovy/org/grails/web/servlet/mvc/AbstractGrailsControllerTests.groovy index 5a52cad37e8..b2b5ebc4f32 100644 --- a/grails-test-suite-base/src/main/groovy/org/grails/web/servlet/mvc/AbstractGrailsControllerTests.groovy +++ b/grails-test-suite-base/src/main/groovy/org/grails/web/servlet/mvc/AbstractGrailsControllerTests.groovy @@ -142,9 +142,6 @@ abstract class AbstractGrailsControllerTests extends GroovyTestCase { request = new GrailsMockHttpServletRequest(characterEncoding: "utf-8") response = new GrailsMockHttpServletResponse() webRequest = GrailsWebMockUtil.bindMockWebRequest(appCtx, request, response) - domainClasses.each { c -> - addValidationMethods c - } } protected setCurrentController(controller) { @@ -209,11 +206,4 @@ abstract class AbstractGrailsControllerTests extends GroovyTestCase { app } - public void addValidationMethods(Class clazz) { - addValidationMethods((GrailsDomainClass)ga.getDomainClass(clazz.name)) - } - - public void addValidationMethods(GrailsDomainClass dc) { - DomainClassGrailsPlugin.addValidationMethods(ga, dc, appCtx) - } } diff --git a/grails-test-suite-base/src/main/groovy/org/grails/web/taglib/AbstractGrailsTagTests.groovy b/grails-test-suite-base/src/main/groovy/org/grails/web/taglib/AbstractGrailsTagTests.groovy index 17342c7271b..0c4070b66df 100644 --- a/grails-test-suite-base/src/main/groovy/org/grails/web/taglib/AbstractGrailsTagTests.groovy +++ b/grails-test-suite-base/src/main/groovy/org/grails/web/taglib/AbstractGrailsTagTests.groovy @@ -324,7 +324,7 @@ abstract class AbstractGrailsTagTests extends GroovyTestCase { Holders.servletContext = null GroovyPageMetaInfo.DEFAULT_PLUGIN_PATH = "" - registryCleaner.clean() +// registryCleaner.clean() GroovySystem.metaClassRegistry.removeMetaClassRegistryChangeEventListener(registryCleaner) } diff --git a/grails-test-suite-persistence/src/test/groovy/grails/web/databinding/GrailsWebDataBinderBindingXmlSpec.groovy b/grails-test-suite-persistence/src/test/groovy/grails/web/databinding/GrailsWebDataBinderBindingXmlSpec.groovy index 19d65e25143..c96b11f9042 100644 --- a/grails-test-suite-persistence/src/test/groovy/grails/web/databinding/GrailsWebDataBinderBindingXmlSpec.groovy +++ b/grails-test-suite-persistence/src/test/groovy/grails/web/databinding/GrailsWebDataBinderBindingXmlSpec.groovy @@ -4,7 +4,7 @@ import grails.persistence.Entity import grails.test.mixin.Mock import grails.test.mixin.TestMixin import grails.test.mixin.domain.DomainClassUnitTestMixin -import grails.validation.trait.Validateable +import grails.artefact.Validateable import spock.lang.Issue import spock.lang.Specification diff --git a/grails-test-suite-persistence/src/test/groovy/grails/web/databinding/GrailsWebDataBinderSpec.groovy b/grails-test-suite-persistence/src/test/groovy/grails/web/databinding/GrailsWebDataBinderSpec.groovy index 8c417773ea5..d72c58b56ac 100644 --- a/grails-test-suite-persistence/src/test/groovy/grails/web/databinding/GrailsWebDataBinderSpec.groovy +++ b/grails-test-suite-persistence/src/test/groovy/grails/web/databinding/GrailsWebDataBinderSpec.groovy @@ -25,7 +25,7 @@ import grails.test.mixin.Mock import grails.test.mixin.TestMixin import grails.test.mixin.domain.DomainClassUnitTestMixin import grails.validation.DeferredBindingActions -import grails.validation.trait.Validateable +import grails.artefact.Validateable import org.apache.commons.lang.builder.CompareToBuilder import org.grails.databinding.BindingFormat as LegacyBindingFormat diff --git a/grails-test-suite-uber/src/test/groovy/grails/compiler/GrailsTypeCheckedCompilationErrorsSpec.groovy b/grails-test-suite-uber/src/test/groovy/grails/compiler/GrailsTypeCheckedCompilationErrorsSpec.groovy index 23894920a0f..be73ca776d6 100644 --- a/grails-test-suite-uber/src/test/groovy/grails/compiler/GrailsTypeCheckedCompilationErrorsSpec.groovy +++ b/grails-test-suite-uber/src/test/groovy/grails/compiler/GrailsTypeCheckedCompilationErrorsSpec.groovy @@ -98,7 +98,7 @@ class SomeClass { } @Issue(['GRAILS-11056', 'GRAILS-11204']) - void 'Test compiling @Validateable'() { + void 'Test compiling @grails.artefact.Validateable'() { given: def gcl = new GroovyClassLoader() @@ -122,7 +122,7 @@ class SomeClass { } @Issue(['GRAILS-11056', 'GRAILS-11204']) - void 'Test compiling @Validateable which contains unrelated type checking error'() { + void 'Test compiling @grails.artefact.Validateable which contains unrelated type checking error'() { given: def gcl = new GroovyClassLoader() @@ -151,7 +151,7 @@ class SomeClass { } @Issue(['GRAILS-11056', 'GRAILS-11204']) - void 'Test compiling @Validateable which attempts to constrain a non existent property'() { + void 'Test compiling @grails.artefact.Validateable which attempts to constrain a non existent property'() { given: def gcl = new GroovyClassLoader() @@ -178,7 +178,7 @@ class SomeClass { @Issue(['GRAILS-11056', 'GRAILS-11204']) - void 'Test compiling @Validateable which attempts to constrain an inherited property'() { + void 'Test compiling @grails.artefact.Validateable which attempts to constrain an inherited property'() { given: def gcl = new GroovyClassLoader() diff --git a/grails-test-suite-uber/src/test/groovy/grails/test/mixin/ControllerUnitTestMixinTests.groovy b/grails-test-suite-uber/src/test/groovy/grails/test/mixin/ControllerUnitTestMixinTests.groovy index 4458e8c072a..ed839e5f494 100644 --- a/grails-test-suite-uber/src/test/groovy/grails/test/mixin/ControllerUnitTestMixinTests.groovy +++ b/grails-test-suite-uber/src/test/groovy/grails/test/mixin/ControllerUnitTestMixinTests.groovy @@ -4,7 +4,7 @@ import grails.artefact.Artefact import grails.converters.JSON import grails.converters.XML import grails.test.mixin.web.ControllerUnitTestMixin -import grails.validation.trait.Validateable +import grails.artefact.Validateable import grails.web.mapping.LinkGenerator import grails.web.mime.MimeUtility diff --git a/grails-test-suite-uber/src/test/groovy/grails/test/mixin/SpyBeanSpec.groovy b/grails-test-suite-uber/src/test/groovy/grails/test/mixin/SpyBeanSpec.groovy index def70e32fec..51497b62a21 100644 --- a/grails-test-suite-uber/src/test/groovy/grails/test/mixin/SpyBeanSpec.groovy +++ b/grails-test-suite-uber/src/test/groovy/grails/test/mixin/SpyBeanSpec.groovy @@ -17,7 +17,9 @@ import spock.lang.Specification class SpyBeanSpec extends Specification { def myAddressValueConverter=Spy(MyAddressValueConverter) def doWithSpring = { - def otherClosure=new DataBindingGrailsPlugin().doWithSpring.clone() + def plugin = new DataBindingGrailsPlugin() + plugin.grailsApplication = delegate.application + def otherClosure= plugin.doWithSpring().clone() otherClosure.delegate=delegate otherClosure.call() myAddressValueConverter(InstanceFactoryBean, myAddressValueConverter, MyAddressValueConverter) diff --git a/grails-test-suite-uber/src/test/groovy/grails/validation/trait/ValidateableTraitSpec.groovy b/grails-test-suite-uber/src/test/groovy/grails/validation/trait/ValidateableTraitSpec.groovy index 3392b75de4d..9fe22b87fda 100644 --- a/grails-test-suite-uber/src/test/groovy/grails/validation/trait/ValidateableTraitSpec.groovy +++ b/grails-test-suite-uber/src/test/groovy/grails/validation/trait/ValidateableTraitSpec.groovy @@ -1,5 +1,9 @@ package grails.validation.trait +import grails.artefact.Validateable + + + import grails.test.mixin.TestMixin import grails.test.mixin.support.GrailsUnitTestMixin diff --git a/grails-test-suite-uber/src/test/groovy/org/grails/plugins/web/ControllersGrailsPluginTests.groovy b/grails-test-suite-uber/src/test/groovy/org/grails/plugins/web/ControllersGrailsPluginTests.groovy index 71f27b871b3..839d7340fcd 100644 --- a/grails-test-suite-uber/src/test/groovy/org/grails/plugins/web/ControllersGrailsPluginTests.groovy +++ b/grails-test-suite-uber/src/test/groovy/org/grails/plugins/web/ControllersGrailsPluginTests.groovy @@ -109,7 +109,7 @@ class FormTagLib { } } plugin.grailsApplication.initialise() - def beans = plugin.doWithSpring + def beans = plugin.doWithSpring() def bb = new BeanBuilder() bb.setBinding(new Binding(manager:mockManager)) bb.beans(beans) diff --git a/grails-test-suite-uber/src/test/groovy/org/grails/validation/ConstrainedPropertyBuilderForCommandsTests.groovy b/grails-test-suite-uber/src/test/groovy/org/grails/validation/ConstrainedPropertyBuilderForCommandsTests.groovy index e138e2b7184..5e7ba476be2 100644 --- a/grails-test-suite-uber/src/test/groovy/org/grails/validation/ConstrainedPropertyBuilderForCommandsTests.groovy +++ b/grails-test-suite-uber/src/test/groovy/org/grails/validation/ConstrainedPropertyBuilderForCommandsTests.groovy @@ -13,7 +13,7 @@ class ConstrainedPropertyBuilderForCommandsTests extends AbstractGrailsControlle private void parseDomainTestClasses() { gcl.parseClass(''' - class Person { + class Person implements grails.artefact.Validateable{ Long id Long version String firstName diff --git a/grails-test-suite-web/src/test/groovy/org/grails/web/commandobjects/SomeValidateableClass.groovy b/grails-test-suite-web/src/test/groovy/org/grails/web/commandobjects/SomeValidateableClass.groovy index a21a3f6370b..3993706ad99 100644 --- a/grails-test-suite-web/src/test/groovy/org/grails/web/commandobjects/SomeValidateableClass.groovy +++ b/grails-test-suite-web/src/test/groovy/org/grails/web/commandobjects/SomeValidateableClass.groovy @@ -1,6 +1,6 @@ package org.grails.web.commandobjects -import grails.validation.trait.Validateable +import grails.artefact.Validateable class SomeValidateableClass implements Validateable { String name diff --git a/grails-test-suite-web/src/test/groovy/org/grails/web/taglib/ValidationTagLibSpec.groovy b/grails-test-suite-web/src/test/groovy/org/grails/web/taglib/ValidationTagLibSpec.groovy new file mode 100644 index 00000000000..7a80771a714 --- /dev/null +++ b/grails-test-suite-web/src/test/groovy/org/grails/web/taglib/ValidationTagLibSpec.groovy @@ -0,0 +1,828 @@ +package org.grails.web.taglib + +import grails.persistence.Entity +import grails.test.mixin.Mock +import grails.test.mixin.TestFor +import grails.test.mixin.TestMixin +import grails.test.mixin.web.GroovyPageUnitTestMixin +import grails.web.util.GrailsApplicationAttributes +import org.grails.core.io.MockStringResourceLoader +import org.grails.plugins.web.taglib.ValidationTagLib +import org.springframework.beans.factory.support.RootBeanDefinition +import org.springframework.context.MessageSourceResolvable +import org.springframework.context.i18n.LocaleContextHolder +import org.springframework.validation.Errors +import org.springframework.validation.FieldError +import spock.lang.Specification + +/* + * Copyright 2014 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @author graemerocher + */ +@TestMixin(GroovyPageUnitTestMixin) +@TestFor(ValidationTagLib) +@Mock(ValidationTagLibBook) +class ValidationTagLibSpec extends Specification { + void testDefaultErrorMessage() { + + when: + def b = new ValidationTagLibBook() + + then: + !b.validate() + b.hasErrors() + + when: + def template = '' + webRequest.currentRequest.addPreferredLocale(Locale.US) + def result = applyTemplate( template, [book:b] ) + + then: + result == 'Property [title] of class [class org.grails.web.taglib.ValidationTagLibBook] cannot be null' + + when:"the object is made valid" + b.title = "The Stand" + b.validate() + then:"No message is output" + + } + + void testFieldValueWithClassAndPropertyNameLookupFromBundle() { + given: + LocaleContextHolder.setLocale(Locale.US) + messageSource.addMessage("org.grails.web.taglib.ValidationTagLibBook.label", Locale.US, "Reading Material") + messageSource.addMessage("org.grails.web.taglib.ValidationTagLibBook.title.label", Locale.US, "Subject") + + when: + def b = new ValidationTagLibBook() + + then: + !b.validate() + b.hasErrors() + + when: + def template = '' + webRequest.currentRequest.addPreferredLocale(Locale.US) + def result = applyTemplate( template, [book:b] ) + + then: + result == 'Property [Subject] of class [Reading Material] cannot be null' + } + + void testFieldValueWithShortClassAndPropertyNameLookupFromBundle() { + given: + LocaleContextHolder.setLocale(Locale.US) + messageSource.addMessage("validationTagLibBook.label", Locale.US, "Reading Material") + messageSource.addMessage("validationTagLibBook.title.label", Locale.US, "Subject") + + when: + def b = new ValidationTagLibBook() + + then: + !b.validate() + b.hasErrors() + + when: + def template = '' + webRequest.currentRequest.addPreferredLocale(Locale.US) + def result = applyTemplate( template, [book:b] ) + + then: + result == 'Property [Subject] of class [Reading Material] cannot be null' + } + + + + void testFieldValueHTMLEscape() { + given: + def b = new ValidationTagLibBook(title:"") + def template = '''''' + def htmlCodecDirective = '<%@page defaultCodec="HTML" %>' + def expected = "<script>alert('escape me')</script>" + expect:"content with tags is output" + applyTemplate( template, [book:b] ) == expected + applyTemplate( htmlCodecDirective + template, [book:b] ) == expected + + } + + // TODO: port remaing tests to new format +// void testFieldValueHtmlEscapingWithFunctionSyntaxCall() { +// def b = ga.getDomainClass("ValidationTagLibBook").newInstance() +// b.properties = [title:""] +// +// def template = '''${fieldValue(bean:book, field:"title")}''' +// def htmlCodecDirective = '<%@page defaultCodec="HTML" %>' +// def expected = "<script>alert('escape me')</script>" +// assertOutputEquals(expected, template, [book:b]) +// assertOutputEquals(expected, htmlCodecDirective + template, [book:b]) +// } +// +// void testFieldValueHtmlEscapingDifferentEncodings() { +// def b = ga.getDomainClass("ValidationTagLibBook").newInstance() +// b.properties = [title:""] +// +// def template = '''${fieldValue(bean:book, field:"title")}''' +// def htmlCodecDirective = '<%@page defaultCodec="HTML" %>' +// def expected = "<script>alert('escape me')</script>" +// +// def resourceLoader = new MockStringResourceLoader() +// resourceLoader.registerMockResource('/_sometemplate.gsp', htmlCodecDirective + template) +// resourceLoader.registerMockResource('/_sometemplate_nocodec.gsp', template) +// appCtx.groovyPagesTemplateEngine.groovyPageLocator.addResourceLoader(resourceLoader) +// +// assertOutputEquals(expected, '', [book:b]) +// assertOutputEquals(expected + expected, template + '', [book:b]) +// assertOutputEquals(expected + expected, htmlCodecDirective + template + '', [book:b]) +// assertOutputEquals(expected + expected, '' + template, [book:b]) +// assertOutputEquals(expected + expected, htmlCodecDirective + '' + template, [book:b]) +// +// assertOutputEquals(expected, '', [book:b]) +// assertOutputEquals(expected + expected, template + '', [book:b]) +// assertOutputEquals(expected + expected, htmlCodecDirective + template + '', [book:b]) +// assertOutputEquals(expected + expected, '' + template, [book:b]) +// assertOutputEquals(expected + expected, htmlCodecDirective + '' + template, [book:b]) +// } +// +// void testFieldValueTag() { +// def b = ga.getDomainClass("ValidationTagLibBook").newInstance() +// b.properties = [publisherURL:"a_bad_url"] +// assertTrue b.hasErrors() +// +// def template = '''''' +// +// assertOutputEquals("a_bad_url", template, [book:b]) +// +// b.properties = [publisherURL:"http://google.com"] +// assertFalse b.hasErrors() +// +// assertOutputEquals("http://google.com", template, [book:b]) +// } +// +// void testFieldValueTagWithDecimalNumber() { +// def b = ga.getDomainClass("ValidationTagLibBook").newInstance() +// b.properties = [publisherURL:"http://google.com", usPrice: 1045.99] +// +// // First test with English. +// webRequest.currentRequest.addPreferredLocale(Locale.US) +// +// def template = '' +// assertOutputEquals("1,045.99", template, [book:b]) +// +// webRequest.currentRequest.removeAttribute(GrailsApplicationAttributes.PROPERTY_REGISTRY) +// // And then with German. +// webRequest.currentRequest.addPreferredLocale(Locale.GERMANY) +// assertOutputEquals("1.045,99", template, [book:b]) +// +// // No decimal part. +// b.properties = [publisherURL:"http://google.com", usPrice: 1045G] +// assertOutputEquals("1.045", template, [book:b]) +// +// // Several decimal places. +// b.properties = [publisherURL:"http://google.com", usPrice: 1045.45567] +// assertOutputEquals("1.045,456", template, [book:b]) +// } +// +// void testFieldValueTagWithFrenchLocaleInTextField() { +// def b = ga.getDomainClass("ValidationTagLibBook").newInstance() +// b.properties = [publisherURL:"http://google.com", usPrice: 1045.99] +// +// String template = '''''' +// +// // First test with English. +// webRequest.currentRequest.addPreferredLocale(Locale.US) +// +// assertOutputEquals '', +// template, [book:b] +// +// webRequest.currentRequest.removeAttribute(GrailsApplicationAttributes.PROPERTY_REGISTRY) +// // And then with French. +// webRequest.currentRequest.addPreferredLocale(Locale.FRENCH) +// +// assertOutputEquals '', +// template, [book:b] +// } +// +// void testHasErrorsTag() { +// def b = ga.getDomainClass("ValidationTagLibBook").newInstance() +// b.validate() +// +// assertTrue b.hasErrors() +// +// def template = '''success''' +// +// assertOutputEquals("success", template, [book:b]) +// +// b = ga.getDomainClass("ValidationTagLibBook").newInstance() +// b.title = "Groovy In Action" +// b.publisherURL = new URL("http://canoo.com/gia") +// b.validate() +// assertTrue b.hasErrors() +// assertOutputEquals("success", '''success''', [book:b]) +// assertOutputEquals("success", '''success''', [book:b]) +// assertOutputEquals("success", '''${hasErrors(bean: book, field:"releaseDate") { "success" }}''', [book:b]) +// assertOutputEquals("success", '''${hasErrors(model: [book: book], field:"releaseDate") { "success" }}''', [book:b]) +// assertOutputEquals("success", '''${g.hasErrors(bean: book, field:"releaseDate") { "success" }}''', [book:b]) +// assertOutputEquals("success", '''${g.hasErrors(model: [book: book], field:"releaseDate") { "success" }}''', [book:b]) +// assertOutputEquals("", '''success''', [book:b]) +// assertOutputEquals("", '''success''', [book:b]) +// +// b.clearErrors() +// b.title = "Groovy in Action" +// b.publisherURL = new URL("http://canoo.com/gia") +// b.releaseDate = new Date() +// b.usPrice = 10.99 +// +// assertTrue b.validate() +// assertFalse b.hasErrors() +// +// assertOutputEquals("", template, [book:b]) +// +// b = ga.getDomainClass("ValidationTagLibBook").newInstance() +// b.title = "Groovy In Action" +// b.publisherURL = new URL("http://canoo.com/gia") +// b.releaseDate = new Date() +// b.usPrice = 10.99 +// b.validate() +// def a = ga.getDomainClass("ValidationTagLibArticle").newInstance() +// a.validate() +// assertOutputEquals("success", '''success''', [book:b,article:a]) +// assertOutputEquals("success", '''success''', [book:b,article:a]) +// assertOutputEquals("", '''success''', [book:b,article:a]) +// assertOutputEquals("", '''success''', [book:b,article:a]) +// assertOutputEquals("success", '''success''', [book:b,article:a]) +// assertOutputEquals("success", '''success''', [book:b,article:a]) +// assertOutputEquals("", '''success''', [book:b,article:a]) +// assertOutputEquals("", '''success''', [book:b,article:a]) +// assertOutputEquals("success", '''success''', [book:b,article:a]) +// assertOutputEquals("success", '''success''', [book:b,article:a]) +// assertOutputEquals("", '''success''', [book:b,article:a]) +// } +// +// void testEachErrorTag() { +// def b = ga.getDomainClass("ValidationTagLibBook").newInstance() +// b.validate() +// +// assertTrue b.hasErrors() +// +// def template = '''${err.field}|''' +// +// def result = applyTemplate(template, [book:b]) +// assertTrue result.contains("title|") +// assertTrue result.contains("releaseDate|") +// assertTrue result.contains("publisherURL|") +// +// template = '''${it.field}|''' +// result = applyTemplate(template, [book:b]) +// assertTrue result.contains("title|") +// assertTrue result.contains("releaseDate|") +// assertTrue result.contains("publisherURL|") +// } +// +// void testEachErrorTagInController() { +// def b = ga.getDomainClass("ValidationTagLibBook").newInstance() +// b.validate() +// +// assertTrue b.hasErrors() +// +// def g = appCtx.gspTagLibraryLookup.lookupNamespaceDispatcher("g") +// def errorFields = [] +// g.eachError(bean: b) { +// errorFields << it.field +// } +// assertTrue errorFields.contains("title") +// assertTrue errorFields.contains("releaseDate") +// assertTrue errorFields.contains("publisherURL") +// } +// +// void testRenderErrorsTag() { +// def b = ga.getDomainClass("ValidationTagLibBook").newInstance() +// b.validate() +// +// assertTrue b.hasErrors() +// +// def template = '''''' +// +// def result = applyTemplate(template,[book:b]) +// assertTrue result.contains("
  • Property [title] of class [class ValidationTagLibBook] cannot be null
  • ") +// assertTrue result.contains("
  • Property [publisherURL] of class [class ValidationTagLibBook] cannot be null
  • ") +// assertTrue result.contains("
  • Property [releaseDate] of class [class ValidationTagLibBook] cannot be null
  • ") +// assertTrue result.startsWith("
      ") +// assertTrue result.endsWith("
    ") +// +// b.clearErrors() +// b.title = "Groovy in Action" +// b.publisherURL = new URL("http://canoo.com/gia") +// b.releaseDate = new Date() +// b.usPrice = 10.99 +// +// assertTrue b.validate() +// assertFalse b.hasErrors() +// +// // should render nothing, not an empty ul - GRAILS-2709 +// assertOutputEquals("", template, [book:b]) +// } +// +// void testRenderErrorsTagAsListWithNoBeanAttribute() { +// def b = ga.getDomainClass("ValidationTagLibBook").newInstance() +// b.validate() +// +// assertTrue b.hasErrors() +// request.setAttribute("bookInstance", b) +// +// def template = '''''' +// +// def result = applyTemplate(template,[book:b]) +// assertEquals 1, result.count("
  • Property [title] of class [class ValidationTagLibBook] cannot be null
  • ") +// assertEquals 1, result.count("
  • Property [publisherURL] of class [class ValidationTagLibBook] cannot be null
  • ") +// assertEquals 1, result.count("
  • Property [releaseDate] of class [class ValidationTagLibBook] cannot be null
  • ") +// assertTrue result.startsWith("
      ") +// assertTrue result.endsWith("
    ") +// } +// +// void testRenderErrorsAsXMLTag() { +// def b = ga.getDomainClass("ValidationTagLibBook").newInstance() +// b.validate() +// +// assertTrue b.hasErrors() +// +// def template = '''''' +// +// def result = applyTemplate(template,[book:b]) +// +// println result +// +// def xml = new XmlSlurper().parseText(result) +// +// assertEquals 4, xml.error.size() +// assertEquals "ValidationTagLibBook", xml.error[0].@object.text() +// assertEquals "publisherURL", xml.error[0].@field.text() +// assertEquals "Property [publisherURL] of class [class ValidationTagLibBook] cannot be null", xml.error[0].@message.text() +// } +// +// void testHasErrorsWithRequestAttributes() { +// StringWriter sw = new StringWriter() +// +// withTag("hasErrors", sw) { tag -> +// +// def mockErrors = [hasErrors:{true}] +// +// request.setAttribute("somethingErrors", mockErrors as Errors) +// +// // test when no message found it returns code +// def attrs = [:] +// tag.call(attrs, { "error found"}) +// +// } +// assertEquals "error found", sw.toString() +// } +// +// void testMessageHtmlEscaping() { +// def b = ga.getDomainClass("ValidationTagLibBook").newInstance() +// b.properties = [title:""] +// +// messageSource.addMessage("default.show.label", Locale.ENGLISH, ">{0}<") +// +// def template = '''<g:message code="default.show.label" args="[book.title]" />''' +// def htmlCodecDirective = '<%@page defaultCodec="HTML" %>' +// def expected = "><script>alert('escape me')</script><" +// assertOutputEquals(expected, template, [book:b]) +// assertOutputEquals(expected, htmlCodecDirective + template, [book:b]) +// } +// +// void testMessageRawEncodeAs() { +// def b = ga.getDomainClass("ValidationTagLibBook").newInstance() +// b.properties = [title:"bold is ok"] +// +// messageSource.addMessage("default.show.label", Locale.ENGLISH, ">{0}<") +// +// def template = '''<g:message code="default.show.label" args="[book.title]" encodeAs="raw"/>''' +// def htmlCodecDirective = '<%@page defaultCodec="HTML" %>' +// def expected = "><b>bold</b> is ok<" +// assertOutputEquals(expected, template, [book:b]) +// assertOutputEquals(expected, htmlCodecDirective + template, [book:b]) +// } +// +// void testMessageNoneEncodeAs() { +// def b = ga.getDomainClass("ValidationTagLibBook").newInstance() +// b.properties = [title:"bold is ok"] +// +// messageSource.addMessage("default.show.label", Locale.ENGLISH, ">{0}<") +// +// def template = '''<g:message code="default.show.label" args="[book.title]" encodeAs="none"/>''' +// def htmlCodecDirective = '<%@page defaultCodec="HTML" %>' +// def expected = "><b>bold</b> is ok<" +// assertOutputEquals(expected, template, [book:b]) +// assertOutputEquals(expected, htmlCodecDirective + template, [book:b]) +// } +// +// void testMessageHtmlEscapingWithFunctionSyntaxCall() { +// def b = ga.getDomainClass("ValidationTagLibBook").newInstance() +// b.properties = [title:""] +// +// messageSource.addMessage("default.show.label", Locale.ENGLISH, "{0}") +// +// def template = '''${g.message([code:"default.show.label", args:[book.title]])}''' +// def htmlCodecDirective = '<%@page defaultCodec="HTML" %>' +// def expected = "<script>alert('escape me')</script>" +// assertOutputEquals(expected, template, [book:b]) +// assertOutputEquals(expected, htmlCodecDirective + template, [book:b]) +// } +// +// void testMessageHtmlEscapingDifferentEncodings() { +// def b = ga.getDomainClass("ValidationTagLibBook").newInstance() +// b.properties = [title:""] +// +// messageSource.addMessage("default.show.label", Locale.ENGLISH, "{0}") +// +// def template = '''${g.message([code:"default.show.label", args:[book.title]])}''' +// def htmlCodecDirective = '<%@page defaultCodec="HTML" %>' +// def expected = "<script>alert('escape me')</script>" +// +// def resourceLoader = new MockStringResourceLoader() +// resourceLoader.registerMockResource('/_sometemplate.gsp', htmlCodecDirective + template) +// resourceLoader.registerMockResource('/_sometemplate_nocodec.gsp', template) +// appCtx.groovyPagesTemplateEngine.groovyPageLocator.addResourceLoader(resourceLoader) +// +// assertOutputEquals(expected, '', [book:b]) +// assertOutputEquals(expected + expected, template + '', [book:b]) +// assertOutputEquals(expected + expected, htmlCodecDirective + template + '', [book:b]) +// assertOutputEquals(expected + expected, '' + template, [book:b]) +// assertOutputEquals(expected + expected, htmlCodecDirective + '' + template, [book:b]) +// +// assertOutputEquals(expected, '', [book:b]) +// assertOutputEquals(expected + expected, template + '', [book:b]) +// assertOutputEquals(expected + expected, htmlCodecDirective + template + '', [book:b]) +// assertOutputEquals(expected + expected, '' + template, [book:b]) +// assertOutputEquals(expected + expected, htmlCodecDirective + '' + template, [book:b]) +// } +// +// void testMessageTagWithError() { +// def error = new FieldError("foo", "bar",1, false, ["my.error.code"] as String[], null, "This is default") +// def template = '' +// +// assertOutputEquals("This is default", template, [error:error]) +// } +// +// void testMessageTagWithLocaleAttribute() { +// messageSource.addMessage("welcome.message", Locale.ENGLISH, "Hello!") +// messageSource.addMessage("welcome.message", Locale.ITALIAN, "Ciao!") +// +// def template = '' +// +// assertOutputEquals("Hello!", template, [:]) +// assertOutputEquals("Hello!", template, [locale:Locale.ITALIAN]) +// } +// +// void testMessageTagWithBlankButExistingMessageBundleValue() { +// +// messageSource.addMessage("test.blank.message", Locale.ENGLISH, "") +// +// def template = '' +// +// assertOutputEquals("", template, [:]) +// } +// +// void testMessageTagWithMessage() { +// def resolvable = [ +// getArguments: {-> [] as Object[] }, +// getCodes: {-> ["my.message.code"] as String[] }, +// getDefaultMessage: {-> "The Default Message" } +// ] as MessageSourceResolvable +// +// def template = '' +// +// assertOutputEquals("The Default Message", template, [message: resolvable]) +// } +// +// void testDefaultMessageAttributeWithAnEmptyStringValue() { +// def template = '' +// assertOutputEquals "", template +// } +// +// void testFieldValueTagWithMessageSourceResolvablePropertyUsesDefaultMessage() { +// def Title = ga.getClassForName("Title") +// def person = ga.getDomainClass("ValidationTagLibPerson").newInstance() +// person.properties = [title: Title.MR, name: "Al Coholic"] +// +// def template = '' +// +// assertOutputEquals "Mr", template, [person: person] +// } +// +// void testFieldValueTagWithMessageSourceResolvablePropertyUsesI18nBundle() { +// def Title = ga.getClassForName("Title") +// def person = ga.getDomainClass("ValidationTagLibPerson").newInstance() +// person.properties = [title: Title.MR, name: "Al Coholic"] +// +// def locale = new Locale("af", "ZA") +// messageSource.addMessage("Title.MR", locale, "Mnr") +// +// webRequest.currentRequest.addPreferredLocale(locale) +// def template = '' +// +// assertOutputEquals "Mnr", template, [person: person] +// } +// +// void testFieldValueTagForNonDomainInstance() { +// def template = '''''' +// +// def myBook = gcl.parseClass(''' +// import grails.persistence.* +// @Entity +// class MyBook { +// String title +// URL myUrl +// Date releaseDate +// BigDecimal usPrice +// }''').newInstance() +// +// myBook.myUrl = new URL("http://google.com") +// assertOutputEquals("http://google.com", template, [book:myBook]) +// } +// +// void testFieldValueTagForNonDomainInstanceWithNestedField() { +// def template = '''''' +// +// // gcl.loadClass("MyClass", true) +// def myUrl = gcl.parseClass(''' +// class MyUrl { +// URL publisherUrl +// }''').newInstance() +// +// def myBook = gcl.parseClass(''' +// import grails.persistence.* +// @Entity +// class MyBook { +// String title +// MyUrl myUrl +// Date releaseDate +// BigDecimal usPrice +// }''').newInstance() +// +// myBook.myUrl = myUrl +// myBook.myUrl.publisherUrl = new URL("http://google.com") +// assertOutputEquals("http://google.com", template, [book:myBook]) +// } +// +// private void parsePhoneDomainTestClasses() { +// gcl.parseClass(''' +// import grails.persistence.* +// @Entity +// class PhoneUsDomain { +// String area +// String prefix +// String number +// }''') +// +// gcl.parseClass(''' +// import grails.persistence.* +// @Entity +// class PhoneUsInternationalDomain { +// String country +// String area +// String prefix +// String number +// }''') +// +// gcl.parseClass(''' +// import grails.persistence.* +// @Entity +// +// class PersonDomain { +// String firstName +// String lastName +// PhoneUsInternationalDomain phoneUsInternational +// PhoneUsDomain phoneUs +// PhoneUsDomain otherPhoneUs +// }''') +// } +// +// private void parsePhonePropertyEditorDomainClasses() { +// gcl.parseClass(''' +// import java.beans.PropertyEditorSupport +// +// class PhoneUsDomainMainEditor extends PropertyEditorSupport { +// String getAsText() { +// def phoneUsNumber = getValue() +// return "${phoneUsNumber.area}-${phoneUsNumber.prefix}-${phoneUsNumber.number}" +// } +// }''') +// +// gcl.parseClass(''' +// import java.beans.PropertyEditorSupport +// +// class PhoneUsDomainForPropertyPathEditor extends PropertyEditorSupport { +// String getAsText() { +// def phoneUsNumber = getValue() +// return "(${phoneUsNumber.area})${phoneUsNumber.prefix}-${phoneUsNumber.number}" +// } +// }''') +// +// gcl.parseClass(''' +// import java.beans.PropertyEditorSupport +// +// class PhoneUsInternationalDomainEditor extends PropertyEditorSupport { +// String getAsText() { +// def phoneUsInternationalNumber = getValue() +// return "${phoneUsInternationalNumber.country}(${phoneUsInternationalNumber.area})" + +// "${phoneUsInternationalNumber.prefix}-${phoneUsInternationalNumber.number}" +// } +// }''') +// +// gcl.parseClass(''' +// import org.springframework.beans.PropertyEditorRegistrar +// import org.springframework.beans.PropertyEditorRegistry +// +// class PhonePropertyEditorDomainRegistrar implements PropertyEditorRegistrar { +// void registerCustomEditors(PropertyEditorRegistry registry) { +// registry.registerCustomEditor(PhoneUsInternationalDomain, new PhoneUsInternationalDomainEditor()) +// registry.registerCustomEditor(PhoneUsDomain, new PhoneUsDomainMainEditor()) +// registry.registerCustomEditor(PhoneUsDomain, "phoneUs", new PhoneUsDomainForPropertyPathEditor()) +// } +// }''') +// } +// +// void testFieldValueTagWithPropertyEditorForDomainClasses() { +// parsePhonePropertyEditorDomainClasses() +// +// def phonePropertyEditorDomainRegistrarClazz = gcl.loadClass("PhonePropertyEditorDomainRegistrar") +// appCtx.registerBeanDefinition( +// "phonePropertyEditorDomainRegistrar", new RootBeanDefinition(phonePropertyEditorDomainRegistrarClazz)) +// +// def phoneUsInternationalDomain = ga.getDomainClass("PhoneUsInternationalDomain").newInstance() +// phoneUsInternationalDomain.properties = [country:"+1", area:"123", prefix:"123", number:"1234"] +// assertFalse phoneUsInternationalDomain.hasErrors() +// +// def personDomain = ga.getDomainClass("PersonDomain").newInstance() +// personDomain.properties = [firstName:"firstName1", lastName:"lastName1", phoneUsInternational:phoneUsInternationalDomain] +// assertFalse personDomain.hasErrors() +// +// def template = '''''' +// assertOutputEquals("+1(123)123-1234", template, [person:personDomain]) +// } +// +// void testFieldValueTagWithPropertyEditorForDomainClassesWithPropertyPath() { +// parsePhonePropertyEditorDomainClasses() +// +// def phonePropertyEditorDomainRegistrarClazz = gcl.loadClass("PhonePropertyEditorDomainRegistrar") +// appCtx.registerBeanDefinition( +// "phonePropertyEditorDomainRegistrar", new RootBeanDefinition(phonePropertyEditorDomainRegistrarClazz)) +// +// def phoneUsDomain = ga.getDomainClass("PhoneUsDomain").newInstance() +// phoneUsDomain.properties = [area:"123", prefix:"123", number:"1234"] +// assertFalse phoneUsDomain.hasErrors() +// +// def personDomain = ga.getDomainClass("PersonDomain").newInstance() +// personDomain.properties = [firstName:"firstName1", lastName:"lastName1", phoneUs:phoneUsDomain, otherPhoneUs:phoneUsDomain] +// assertFalse personDomain.hasErrors() +// +// def template = '''''' +// assertOutputEquals("(123)123-1234", template, [person:personDomain]) +// +// def otherTemplate = '''''' +// assertOutputEquals("123-123-1234", otherTemplate, [person:personDomain]) +// } +// +// private void parsePhonePlainTestClasses() { +// gcl.parseClass(''' +// class PhoneUs { +// String area +// String prefix +// String number +// }''') +// +// gcl.parseClass(''' +// class PhoneUsInternational { +// String country +// String area +// String prefix +// String number +// }''') +// +// gcl.parseClass(''' +// class PersonPlain { +// String firstName +// String lastName +// PhoneUsInternational phoneUsInternational +// PhoneUs phoneUs +// PhoneUs otherPhoneUs +// }''') +// } +// +// private void parsePhonePropertyEditorPlainClasses() { +// gcl.parseClass(''' +// import java.beans.PropertyEditorSupport +// +// class PhoneUsMainEditor extends PropertyEditorSupport { +// String getAsText() { +// def phoneUsNumber = getValue() +// return "${phoneUsNumber.area}-${phoneUsNumber.prefix}-${phoneUsNumber.number}" +// } +// }''') +// +// gcl.parseClass(''' +// import java.beans.PropertyEditorSupport +// +// class PhoneUsForPropertyPathEditor extends PropertyEditorSupport { +// String getAsText() { +// def phoneUsNumber = getValue() +// return "(${phoneUsNumber.area})${phoneUsNumber.prefix}-${phoneUsNumber.number}" +// } +// }''') +// +// gcl.parseClass(''' +// import java.beans.PropertyEditorSupport +// +// class PhoneUsInternationalEditor extends PropertyEditorSupport { +// String getAsText() { +// def phoneUsInternationalNumber = getValue() +// return "${phoneUsInternationalNumber.country}(${phoneUsInternationalNumber.area})" + +// "${phoneUsInternationalNumber.prefix}-${phoneUsInternationalNumber.number}" +// } +// }''') +// +// gcl.parseClass(''' +// import org.springframework.beans.PropertyEditorRegistrar +// import org.springframework.beans.PropertyEditorRegistry +// +// class PhonePropertyEditorRegistrar implements PropertyEditorRegistrar { +// void registerCustomEditors(PropertyEditorRegistry registry) { +// registry.registerCustomEditor(PhoneUsInternational, new PhoneUsInternationalEditor()) +// registry.registerCustomEditor(PhoneUs, new PhoneUsMainEditor()) +// registry.registerCustomEditor(PhoneUs, "phoneUs", new PhoneUsForPropertyPathEditor()) +// } +// }''') +// } +// +// void testFieldValueTagWithPropertyEditorForNonDomainClasses() { +// parsePhonePlainTestClasses() +// parsePhonePropertyEditorPlainClasses() +// +// def phonePropertyEditorRegistrarClazz = gcl.loadClass("PhonePropertyEditorRegistrar") +// appCtx.registerBeanDefinition( +// "phonePropertyEditorRegistrar", new RootBeanDefinition(phonePropertyEditorRegistrarClazz)) +// +// def phoneUsInternational = gcl.loadClass("PhoneUsInternational").newInstance() +// phoneUsInternational.country = "+1" +// phoneUsInternational.area = "123" +// phoneUsInternational.prefix = "123" +// phoneUsInternational.number = "1234" +// +// def person = gcl.loadClass("PersonPlain").newInstance() +// person.firstName = "firstName1" +// person.lastName = "lastName1" +// person.phoneUsInternational = phoneUsInternational +// +// def template = '''''' +// assertOutputEquals("+1(123)123-1234", template, [person:person]) +// } +// +// void testFieldValueTagWithPropertyEditorForNonDomainClassesWithPropertyPath() { +// parsePhonePlainTestClasses() +// parsePhonePropertyEditorPlainClasses() +// +// def phonePropertyEditorRegistrarClazz = gcl.loadClass("PhonePropertyEditorRegistrar") +// appCtx.registerBeanDefinition( +// "phonePropertyEditorRegistrar", new RootBeanDefinition(phonePropertyEditorRegistrarClazz)) +// +// def phoneUs = gcl.loadClass("PhoneUs").newInstance() +// phoneUs.area = "123" +// phoneUs.prefix = "123" +// phoneUs.number = "1234" +// +// def person = gcl.loadClass("PersonPlain").newInstance() +// person.firstName = "firstName1" +// person.lastName = "lastName1" +// person.phoneUs = phoneUs +// person.otherPhoneUs = phoneUs +// +// def template = '''''' +// assertOutputEquals("(123)123-1234", template, [person:person]) +// +// def otherTemplate = '''''' +// assertOutputEquals("123-123-1234", otherTemplate, [person:person]) +// } +} + +@Entity +class ValidationTagLibBook { + String title + URL publisherURL + Date releaseDate + BigDecimal usPrice +} \ No newline at end of file diff --git a/grails-test-suite-web/src/test/groovy/org/grails/web/taglib/ValidationTagLibTests.groovy b/grails-test-suite-web/src/test/groovy/org/grails/web/taglib/ValidationTagLibTests.groovy deleted file mode 100644 index 4fec223c5d2..00000000000 --- a/grails-test-suite-web/src/test/groovy/org/grails/web/taglib/ValidationTagLibTests.groovy +++ /dev/null @@ -1,822 +0,0 @@ -package org.grails.web.taglib - -import org.grails.core.io.MockStringResourceLoader -import grails.web.util.GrailsApplicationAttributes -import org.springframework.beans.factory.support.RootBeanDefinition -import org.springframework.context.MessageSourceResolvable -import org.springframework.context.i18n.LocaleContextHolder -import org.springframework.validation.Errors -import org.springframework.validation.FieldError - -class ValidationTagLibTests extends AbstractGrailsTagTests { - - protected void onSetUp() { - - gcl.parseClass ''' -import grails.persistence.* - -@Entity -class ValidationTagLibBook { - String title - URL publisherURL - Date releaseDate - BigDecimal usPrice -} -''' - - gcl.parseClass ''' -import grails.persistence.* - -@Entity -class ValidationTagLibArticle { - String title -} -''' - - gcl.parseClass ''' -import grails.persistence.* - -@Entity -class ValidationTagLibPerson { - Title title - String name -} - -enum Title implements org.springframework.context.MessageSourceResolvable { - MR, MRS, MS, DR - - String[] getCodes() { - ["${getClass().name}.${name()}"] as String[] - } - - Object[] getArguments() { - [] as Object[] - } - - String getDefaultMessage() { - use(org.apache.commons.lang.WordUtils) { - name().toLowerCase().replaceAll(/_+/, " ").capitalizeFully() - } - } -} -''' - - parsePhoneDomainTestClasses() - } - - void testFieldValueWithClassAndPropertyNameLookupFromBundle() { - def domain = ga.getDomainClass("ValidationTagLibBook") - - LocaleContextHolder.setLocale(Locale.US) - messageSource.addMessage("ValidationTagLibBook.label", Locale.US, "Reading Material") - messageSource.addMessage("ValidationTagLibBook.title.label", Locale.US, "Subject") - def b = domain.newInstance() - b.validate() - assertTrue b.hasErrors() - - def template = '' - - webRequest.currentRequest.addPreferredLocale(Locale.US) - assertOutputEquals 'Property [Subject] of class [Reading Material] cannot be null', template, [book:b] - } - - void testFieldValueWithShortClassAndPropertyNameLookupFromBundle() { - def domain = ga.getDomainClass("ValidationTagLibBook") - - LocaleContextHolder.setLocale(Locale.US) - messageSource.addMessage("validationTagLibBook.label", Locale.US, "Reading Material") - messageSource.addMessage("validationTagLibBook.title.label", Locale.US, "Subject") - def b = domain.newInstance() - b.validate() - assertTrue b.hasErrors() - - def template = '' - - webRequest.currentRequest.addPreferredLocale(Locale.US) - assertOutputEquals 'Property [Subject] of class [Reading Material] cannot be null', template, [book:b] - } - - void testRenderErrorTag() { - def domain = ga.getDomainClass("ValidationTagLibBook") - def b = domain.newInstance() - b.validate() - assertTrue b.hasErrors() - - def template = '''''' - - assertOutputEquals 'Property [title] of class [class ValidationTagLibBook] cannot be null', template, [book:b] - b = domain.newInstance() - b.title = "The Stand" - b.validate() - assertOutputEquals '', template, [book:b] - assertOutputEquals '', template, [book:domain.newInstance()] - } - - void testFieldValueHtmlEscaping() { - def b = ga.getDomainClass("ValidationTagLibBook").newInstance() - b.properties = [title:""] - - def template = '''''' - def htmlCodecDirective = '<%@page defaultCodec="HTML" %>' - def expected = "<script>alert('escape me')</script>" - assertOutputEquals(expected, template, [book:b]) - assertOutputEquals(expected, htmlCodecDirective + template, [book:b]) - } - - void testFieldValueHtmlEscapingWithFunctionSyntaxCall() { - def b = ga.getDomainClass("ValidationTagLibBook").newInstance() - b.properties = [title:""] - - def template = '''${fieldValue(bean:book, field:"title")}''' - def htmlCodecDirective = '<%@page defaultCodec="HTML" %>' - def expected = "<script>alert('escape me')</script>" - assertOutputEquals(expected, template, [book:b]) - assertOutputEquals(expected, htmlCodecDirective + template, [book:b]) - } - - void testFieldValueHtmlEscapingDifferentEncodings() { - def b = ga.getDomainClass("ValidationTagLibBook").newInstance() - b.properties = [title:""] - - def template = '''${fieldValue(bean:book, field:"title")}''' - def htmlCodecDirective = '<%@page defaultCodec="HTML" %>' - def expected = "<script>alert('escape me')</script>" - - def resourceLoader = new MockStringResourceLoader() - resourceLoader.registerMockResource('/_sometemplate.gsp', htmlCodecDirective + template) - resourceLoader.registerMockResource('/_sometemplate_nocodec.gsp', template) - appCtx.groovyPagesTemplateEngine.groovyPageLocator.addResourceLoader(resourceLoader) - - assertOutputEquals(expected, '', [book:b]) - assertOutputEquals(expected + expected, template + '', [book:b]) - assertOutputEquals(expected + expected, htmlCodecDirective + template + '', [book:b]) - assertOutputEquals(expected + expected, '' + template, [book:b]) - assertOutputEquals(expected + expected, htmlCodecDirective + '' + template, [book:b]) - - assertOutputEquals(expected, '', [book:b]) - assertOutputEquals(expected + expected, template + '', [book:b]) - assertOutputEquals(expected + expected, htmlCodecDirective + template + '', [book:b]) - assertOutputEquals(expected + expected, '' + template, [book:b]) - assertOutputEquals(expected + expected, htmlCodecDirective + '' + template, [book:b]) - } - - void testFieldValueTag() { - def b = ga.getDomainClass("ValidationTagLibBook").newInstance() - b.properties = [publisherURL:"a_bad_url"] - assertTrue b.hasErrors() - - def template = '''''' - - assertOutputEquals("a_bad_url", template, [book:b]) - - b.properties = [publisherURL:"http://google.com"] - assertFalse b.hasErrors() - - assertOutputEquals("http://google.com", template, [book:b]) - } - - void testFieldValueTagWithDecimalNumber() { - def b = ga.getDomainClass("ValidationTagLibBook").newInstance() - b.properties = [publisherURL:"http://google.com", usPrice: 1045.99] - - // First test with English. - webRequest.currentRequest.addPreferredLocale(Locale.US) - - def template = '' - assertOutputEquals("1,045.99", template, [book:b]) - - webRequest.currentRequest.removeAttribute(GrailsApplicationAttributes.PROPERTY_REGISTRY) - // And then with German. - webRequest.currentRequest.addPreferredLocale(Locale.GERMANY) - assertOutputEquals("1.045,99", template, [book:b]) - - // No decimal part. - b.properties = [publisherURL:"http://google.com", usPrice: 1045G] - assertOutputEquals("1.045", template, [book:b]) - - // Several decimal places. - b.properties = [publisherURL:"http://google.com", usPrice: 1045.45567] - assertOutputEquals("1.045,456", template, [book:b]) - } - - void testFieldValueTagWithFrenchLocaleInTextField() { - def b = ga.getDomainClass("ValidationTagLibBook").newInstance() - b.properties = [publisherURL:"http://google.com", usPrice: 1045.99] - - String template = '''''' - - // First test with English. - webRequest.currentRequest.addPreferredLocale(Locale.US) - - assertOutputEquals '', - template, [book:b] - - webRequest.currentRequest.removeAttribute(GrailsApplicationAttributes.PROPERTY_REGISTRY) - // And then with French. - webRequest.currentRequest.addPreferredLocale(Locale.FRENCH) - - assertOutputEquals '', - template, [book:b] - } - - void testHasErrorsTag() { - def b = ga.getDomainClass("ValidationTagLibBook").newInstance() - b.validate() - - assertTrue b.hasErrors() - - def template = '''success''' - - assertOutputEquals("success", template, [book:b]) - - b = ga.getDomainClass("ValidationTagLibBook").newInstance() - b.title = "Groovy In Action" - b.publisherURL = new URL("http://canoo.com/gia") - b.validate() - assertTrue b.hasErrors() - assertOutputEquals("success", '''success''', [book:b]) - assertOutputEquals("success", '''success''', [book:b]) - assertOutputEquals("success", '''${hasErrors(bean: book, field:"releaseDate") { "success" }}''', [book:b]) - assertOutputEquals("success", '''${hasErrors(model: [book: book], field:"releaseDate") { "success" }}''', [book:b]) - assertOutputEquals("success", '''${g.hasErrors(bean: book, field:"releaseDate") { "success" }}''', [book:b]) - assertOutputEquals("success", '''${g.hasErrors(model: [book: book], field:"releaseDate") { "success" }}''', [book:b]) - assertOutputEquals("", '''success''', [book:b]) - assertOutputEquals("", '''success''', [book:b]) - - b.clearErrors() - b.title = "Groovy in Action" - b.publisherURL = new URL("http://canoo.com/gia") - b.releaseDate = new Date() - b.usPrice = 10.99 - - assertTrue b.validate() - assertFalse b.hasErrors() - - assertOutputEquals("", template, [book:b]) - - b = ga.getDomainClass("ValidationTagLibBook").newInstance() - b.title = "Groovy In Action" - b.publisherURL = new URL("http://canoo.com/gia") - b.releaseDate = new Date() - b.usPrice = 10.99 - b.validate() - def a = ga.getDomainClass("ValidationTagLibArticle").newInstance() - a.validate() - assertOutputEquals("success", '''success''', [book:b,article:a]) - assertOutputEquals("success", '''success''', [book:b,article:a]) - assertOutputEquals("", '''success''', [book:b,article:a]) - assertOutputEquals("", '''success''', [book:b,article:a]) - assertOutputEquals("success", '''success''', [book:b,article:a]) - assertOutputEquals("success", '''success''', [book:b,article:a]) - assertOutputEquals("", '''success''', [book:b,article:a]) - assertOutputEquals("", '''success''', [book:b,article:a]) - assertOutputEquals("success", '''success''', [book:b,article:a]) - assertOutputEquals("success", '''success''', [book:b,article:a]) - assertOutputEquals("", '''success''', [book:b,article:a]) - } - - void testEachErrorTag() { - def b = ga.getDomainClass("ValidationTagLibBook").newInstance() - b.validate() - - assertTrue b.hasErrors() - - def template = '''${err.field}|''' - - def result = applyTemplate(template, [book:b]) - assertTrue result.contains("title|") - assertTrue result.contains("releaseDate|") - assertTrue result.contains("publisherURL|") - - template = '''${it.field}|''' - result = applyTemplate(template, [book:b]) - assertTrue result.contains("title|") - assertTrue result.contains("releaseDate|") - assertTrue result.contains("publisherURL|") - } - - void testEachErrorTagInController() { - def b = ga.getDomainClass("ValidationTagLibBook").newInstance() - b.validate() - - assertTrue b.hasErrors() - - def g = appCtx.gspTagLibraryLookup.lookupNamespaceDispatcher("g") - def errorFields = [] - g.eachError(bean: b) { - errorFields << it.field - } - assertTrue errorFields.contains("title") - assertTrue errorFields.contains("releaseDate") - assertTrue errorFields.contains("publisherURL") - } - - void testRenderErrorsTag() { - def b = ga.getDomainClass("ValidationTagLibBook").newInstance() - b.validate() - - assertTrue b.hasErrors() - - def template = '''''' - - def result = applyTemplate(template,[book:b]) - assertTrue result.contains("
  • Property [title] of class [class ValidationTagLibBook] cannot be null
  • ") - assertTrue result.contains("
  • Property [publisherURL] of class [class ValidationTagLibBook] cannot be null
  • ") - assertTrue result.contains("
  • Property [releaseDate] of class [class ValidationTagLibBook] cannot be null
  • ") - assertTrue result.startsWith("
      ") - assertTrue result.endsWith("
    ") - - b.clearErrors() - b.title = "Groovy in Action" - b.publisherURL = new URL("http://canoo.com/gia") - b.releaseDate = new Date() - b.usPrice = 10.99 - - assertTrue b.validate() - assertFalse b.hasErrors() - - // should render nothing, not an empty ul - GRAILS-2709 - assertOutputEquals("", template, [book:b]) - } - - void testRenderErrorsTagAsListWithNoBeanAttribute() { - def b = ga.getDomainClass("ValidationTagLibBook").newInstance() - b.validate() - - assertTrue b.hasErrors() - request.setAttribute("bookInstance", b) - - def template = '''''' - - def result = applyTemplate(template,[book:b]) - assertEquals 1, result.count("
  • Property [title] of class [class ValidationTagLibBook] cannot be null
  • ") - assertEquals 1, result.count("
  • Property [publisherURL] of class [class ValidationTagLibBook] cannot be null
  • ") - assertEquals 1, result.count("
  • Property [releaseDate] of class [class ValidationTagLibBook] cannot be null
  • ") - assertTrue result.startsWith("
      ") - assertTrue result.endsWith("
    ") - } - - void testRenderErrorsAsXMLTag() { - def b = ga.getDomainClass("ValidationTagLibBook").newInstance() - b.validate() - - assertTrue b.hasErrors() - - def template = '''''' - - def result = applyTemplate(template,[book:b]) - - println result - - def xml = new XmlSlurper().parseText(result) - - assertEquals 4, xml.error.size() - assertEquals "ValidationTagLibBook", xml.error[0].@object.text() - assertEquals "publisherURL", xml.error[0].@field.text() - assertEquals "Property [publisherURL] of class [class ValidationTagLibBook] cannot be null", xml.error[0].@message.text() - } - - void testHasErrorsWithRequestAttributes() { - StringWriter sw = new StringWriter() - - withTag("hasErrors", sw) { tag -> - - def mockErrors = [hasErrors:{true}] - - request.setAttribute("somethingErrors", mockErrors as Errors) - - // test when no message found it returns code - def attrs = [:] - tag.call(attrs, { "error found"}) - - } - assertEquals "error found", sw.toString() - } - - void testMessageHtmlEscaping() { - def b = ga.getDomainClass("ValidationTagLibBook").newInstance() - b.properties = [title:""] - - messageSource.addMessage("default.show.label", Locale.ENGLISH, ">{0}<") - - def template = '''<g:message code="default.show.label" args="[book.title]" />''' - def htmlCodecDirective = '<%@page defaultCodec="HTML" %>' - def expected = "><script>alert('escape me')</script><" - assertOutputEquals(expected, template, [book:b]) - assertOutputEquals(expected, htmlCodecDirective + template, [book:b]) - } - - void testMessageRawEncodeAs() { - def b = ga.getDomainClass("ValidationTagLibBook").newInstance() - b.properties = [title:"bold is ok"] - - messageSource.addMessage("default.show.label", Locale.ENGLISH, ">{0}<") - - def template = '''<g:message code="default.show.label" args="[book.title]" encodeAs="raw"/>''' - def htmlCodecDirective = '<%@page defaultCodec="HTML" %>' - def expected = "><b>bold</b> is ok<" - assertOutputEquals(expected, template, [book:b]) - assertOutputEquals(expected, htmlCodecDirective + template, [book:b]) - } - - void testMessageNoneEncodeAs() { - def b = ga.getDomainClass("ValidationTagLibBook").newInstance() - b.properties = [title:"bold is ok"] - - messageSource.addMessage("default.show.label", Locale.ENGLISH, ">{0}<") - - def template = '''<g:message code="default.show.label" args="[book.title]" encodeAs="none"/>''' - def htmlCodecDirective = '<%@page defaultCodec="HTML" %>' - def expected = "><b>bold</b> is ok<" - assertOutputEquals(expected, template, [book:b]) - assertOutputEquals(expected, htmlCodecDirective + template, [book:b]) - } - - void testMessageHtmlEscapingWithFunctionSyntaxCall() { - def b = ga.getDomainClass("ValidationTagLibBook").newInstance() - b.properties = [title:""] - - messageSource.addMessage("default.show.label", Locale.ENGLISH, "{0}") - - def template = '''${g.message([code:"default.show.label", args:[book.title]])}''' - def htmlCodecDirective = '<%@page defaultCodec="HTML" %>' - def expected = "<script>alert('escape me')</script>" - assertOutputEquals(expected, template, [book:b]) - assertOutputEquals(expected, htmlCodecDirective + template, [book:b]) - } - - void testMessageHtmlEscapingDifferentEncodings() { - def b = ga.getDomainClass("ValidationTagLibBook").newInstance() - b.properties = [title:""] - - messageSource.addMessage("default.show.label", Locale.ENGLISH, "{0}") - - def template = '''${g.message([code:"default.show.label", args:[book.title]])}''' - def htmlCodecDirective = '<%@page defaultCodec="HTML" %>' - def expected = "<script>alert('escape me')</script>" - - def resourceLoader = new MockStringResourceLoader() - resourceLoader.registerMockResource('/_sometemplate.gsp', htmlCodecDirective + template) - resourceLoader.registerMockResource('/_sometemplate_nocodec.gsp', template) - appCtx.groovyPagesTemplateEngine.groovyPageLocator.addResourceLoader(resourceLoader) - - assertOutputEquals(expected, '', [book:b]) - assertOutputEquals(expected + expected, template + '', [book:b]) - assertOutputEquals(expected + expected, htmlCodecDirective + template + '', [book:b]) - assertOutputEquals(expected + expected, '' + template, [book:b]) - assertOutputEquals(expected + expected, htmlCodecDirective + '' + template, [book:b]) - - assertOutputEquals(expected, '', [book:b]) - assertOutputEquals(expected + expected, template + '', [book:b]) - assertOutputEquals(expected + expected, htmlCodecDirective + template + '', [book:b]) - assertOutputEquals(expected + expected, '' + template, [book:b]) - assertOutputEquals(expected + expected, htmlCodecDirective + '' + template, [book:b]) - } - - void testMessageTagWithError() { - def error = new FieldError("foo", "bar",1, false, ["my.error.code"] as String[], null, "This is default") - def template = '' - - assertOutputEquals("This is default", template, [error:error]) - } - - void testMessageTagWithLocaleAttribute() { - messageSource.addMessage("welcome.message", Locale.ENGLISH, "Hello!") - messageSource.addMessage("welcome.message", Locale.ITALIAN, "Ciao!") - - def template = '' - - assertOutputEquals("Hello!", template, [:]) - assertOutputEquals("Hello!", template, [locale:Locale.ITALIAN]) - } - - void testMessageTagWithBlankButExistingMessageBundleValue() { - - messageSource.addMessage("test.blank.message", Locale.ENGLISH, "") - - def template = '' - - assertOutputEquals("", template, [:]) - } - - void testMessageTagWithMessage() { - def resolvable = [ - getArguments: {-> [] as Object[] }, - getCodes: {-> ["my.message.code"] as String[] }, - getDefaultMessage: {-> "The Default Message" } - ] as MessageSourceResolvable - - def template = '' - - assertOutputEquals("The Default Message", template, [message: resolvable]) - } - - void testDefaultMessageAttributeWithAnEmptyStringValue() { - def template = '' - assertOutputEquals "", template - } - - void testFieldValueTagWithMessageSourceResolvablePropertyUsesDefaultMessage() { - def Title = ga.getClassForName("Title") - def person = ga.getDomainClass("ValidationTagLibPerson").newInstance() - person.properties = [title: Title.MR, name: "Al Coholic"] - - def template = '' - - assertOutputEquals "Mr", template, [person: person] - } - - void testFieldValueTagWithMessageSourceResolvablePropertyUsesI18nBundle() { - def Title = ga.getClassForName("Title") - def person = ga.getDomainClass("ValidationTagLibPerson").newInstance() - person.properties = [title: Title.MR, name: "Al Coholic"] - - def locale = new Locale("af", "ZA") - messageSource.addMessage("Title.MR", locale, "Mnr") - - webRequest.currentRequest.addPreferredLocale(locale) - def template = '' - - assertOutputEquals "Mnr", template, [person: person] - } - - void testFieldValueTagForNonDomainInstance() { - def template = '''''' - - def myBook = gcl.parseClass(''' - import grails.persistence.* - @Entity - class MyBook { - String title - URL myUrl - Date releaseDate - BigDecimal usPrice - }''').newInstance() - - myBook.myUrl = new URL("http://google.com") - assertOutputEquals("http://google.com", template, [book:myBook]) - } - - void testFieldValueTagForNonDomainInstanceWithNestedField() { - def template = '''''' - - // gcl.loadClass("MyClass", true) - def myUrl = gcl.parseClass(''' - class MyUrl { - URL publisherUrl - }''').newInstance() - - def myBook = gcl.parseClass(''' - import grails.persistence.* - @Entity - class MyBook { - String title - MyUrl myUrl - Date releaseDate - BigDecimal usPrice - }''').newInstance() - - myBook.myUrl = myUrl - myBook.myUrl.publisherUrl = new URL("http://google.com") - assertOutputEquals("http://google.com", template, [book:myBook]) - } - - private void parsePhoneDomainTestClasses() { - gcl.parseClass(''' - import grails.persistence.* - @Entity - class PhoneUsDomain { - String area - String prefix - String number - }''') - - gcl.parseClass(''' - import grails.persistence.* - @Entity - class PhoneUsInternationalDomain { - String country - String area - String prefix - String number - }''') - - gcl.parseClass(''' - import grails.persistence.* - @Entity - - class PersonDomain { - String firstName - String lastName - PhoneUsInternationalDomain phoneUsInternational - PhoneUsDomain phoneUs - PhoneUsDomain otherPhoneUs - }''') - } - - private void parsePhonePropertyEditorDomainClasses() { - gcl.parseClass(''' - import java.beans.PropertyEditorSupport - - class PhoneUsDomainMainEditor extends PropertyEditorSupport { - String getAsText() { - def phoneUsNumber = getValue() - return "${phoneUsNumber.area}-${phoneUsNumber.prefix}-${phoneUsNumber.number}" - } - }''') - - gcl.parseClass(''' - import java.beans.PropertyEditorSupport - - class PhoneUsDomainForPropertyPathEditor extends PropertyEditorSupport { - String getAsText() { - def phoneUsNumber = getValue() - return "(${phoneUsNumber.area})${phoneUsNumber.prefix}-${phoneUsNumber.number}" - } - }''') - - gcl.parseClass(''' - import java.beans.PropertyEditorSupport - - class PhoneUsInternationalDomainEditor extends PropertyEditorSupport { - String getAsText() { - def phoneUsInternationalNumber = getValue() - return "${phoneUsInternationalNumber.country}(${phoneUsInternationalNumber.area})" + - "${phoneUsInternationalNumber.prefix}-${phoneUsInternationalNumber.number}" - } - }''') - - gcl.parseClass(''' - import org.springframework.beans.PropertyEditorRegistrar - import org.springframework.beans.PropertyEditorRegistry - - class PhonePropertyEditorDomainRegistrar implements PropertyEditorRegistrar { - void registerCustomEditors(PropertyEditorRegistry registry) { - registry.registerCustomEditor(PhoneUsInternationalDomain, new PhoneUsInternationalDomainEditor()) - registry.registerCustomEditor(PhoneUsDomain, new PhoneUsDomainMainEditor()) - registry.registerCustomEditor(PhoneUsDomain, "phoneUs", new PhoneUsDomainForPropertyPathEditor()) - } - }''') - } - - void testFieldValueTagWithPropertyEditorForDomainClasses() { - parsePhonePropertyEditorDomainClasses() - - def phonePropertyEditorDomainRegistrarClazz = gcl.loadClass("PhonePropertyEditorDomainRegistrar") - appCtx.registerBeanDefinition( - "phonePropertyEditorDomainRegistrar", new RootBeanDefinition(phonePropertyEditorDomainRegistrarClazz)) - - def phoneUsInternationalDomain = ga.getDomainClass("PhoneUsInternationalDomain").newInstance() - phoneUsInternationalDomain.properties = [country:"+1", area:"123", prefix:"123", number:"1234"] - assertFalse phoneUsInternationalDomain.hasErrors() - - def personDomain = ga.getDomainClass("PersonDomain").newInstance() - personDomain.properties = [firstName:"firstName1", lastName:"lastName1", phoneUsInternational:phoneUsInternationalDomain] - assertFalse personDomain.hasErrors() - - def template = '''''' - assertOutputEquals("+1(123)123-1234", template, [person:personDomain]) - } - - void testFieldValueTagWithPropertyEditorForDomainClassesWithPropertyPath() { - parsePhonePropertyEditorDomainClasses() - - def phonePropertyEditorDomainRegistrarClazz = gcl.loadClass("PhonePropertyEditorDomainRegistrar") - appCtx.registerBeanDefinition( - "phonePropertyEditorDomainRegistrar", new RootBeanDefinition(phonePropertyEditorDomainRegistrarClazz)) - - def phoneUsDomain = ga.getDomainClass("PhoneUsDomain").newInstance() - phoneUsDomain.properties = [area:"123", prefix:"123", number:"1234"] - assertFalse phoneUsDomain.hasErrors() - - def personDomain = ga.getDomainClass("PersonDomain").newInstance() - personDomain.properties = [firstName:"firstName1", lastName:"lastName1", phoneUs:phoneUsDomain, otherPhoneUs:phoneUsDomain] - assertFalse personDomain.hasErrors() - - def template = '''''' - assertOutputEquals("(123)123-1234", template, [person:personDomain]) - - def otherTemplate = '''''' - assertOutputEquals("123-123-1234", otherTemplate, [person:personDomain]) - } - - private void parsePhonePlainTestClasses() { - gcl.parseClass(''' - class PhoneUs { - String area - String prefix - String number - }''') - - gcl.parseClass(''' - class PhoneUsInternational { - String country - String area - String prefix - String number - }''') - - gcl.parseClass(''' - class PersonPlain { - String firstName - String lastName - PhoneUsInternational phoneUsInternational - PhoneUs phoneUs - PhoneUs otherPhoneUs - }''') - } - - private void parsePhonePropertyEditorPlainClasses() { - gcl.parseClass(''' - import java.beans.PropertyEditorSupport - - class PhoneUsMainEditor extends PropertyEditorSupport { - String getAsText() { - def phoneUsNumber = getValue() - return "${phoneUsNumber.area}-${phoneUsNumber.prefix}-${phoneUsNumber.number}" - } - }''') - - gcl.parseClass(''' - import java.beans.PropertyEditorSupport - - class PhoneUsForPropertyPathEditor extends PropertyEditorSupport { - String getAsText() { - def phoneUsNumber = getValue() - return "(${phoneUsNumber.area})${phoneUsNumber.prefix}-${phoneUsNumber.number}" - } - }''') - - gcl.parseClass(''' - import java.beans.PropertyEditorSupport - - class PhoneUsInternationalEditor extends PropertyEditorSupport { - String getAsText() { - def phoneUsInternationalNumber = getValue() - return "${phoneUsInternationalNumber.country}(${phoneUsInternationalNumber.area})" + - "${phoneUsInternationalNumber.prefix}-${phoneUsInternationalNumber.number}" - } - }''') - - gcl.parseClass(''' - import org.springframework.beans.PropertyEditorRegistrar - import org.springframework.beans.PropertyEditorRegistry - - class PhonePropertyEditorRegistrar implements PropertyEditorRegistrar { - void registerCustomEditors(PropertyEditorRegistry registry) { - registry.registerCustomEditor(PhoneUsInternational, new PhoneUsInternationalEditor()) - registry.registerCustomEditor(PhoneUs, new PhoneUsMainEditor()) - registry.registerCustomEditor(PhoneUs, "phoneUs", new PhoneUsForPropertyPathEditor()) - } - }''') - } - - void testFieldValueTagWithPropertyEditorForNonDomainClasses() { - parsePhonePlainTestClasses() - parsePhonePropertyEditorPlainClasses() - - def phonePropertyEditorRegistrarClazz = gcl.loadClass("PhonePropertyEditorRegistrar") - appCtx.registerBeanDefinition( - "phonePropertyEditorRegistrar", new RootBeanDefinition(phonePropertyEditorRegistrarClazz)) - - def phoneUsInternational = gcl.loadClass("PhoneUsInternational").newInstance() - phoneUsInternational.country = "+1" - phoneUsInternational.area = "123" - phoneUsInternational.prefix = "123" - phoneUsInternational.number = "1234" - - def person = gcl.loadClass("PersonPlain").newInstance() - person.firstName = "firstName1" - person.lastName = "lastName1" - person.phoneUsInternational = phoneUsInternational - - def template = '''''' - assertOutputEquals("+1(123)123-1234", template, [person:person]) - } - - void testFieldValueTagWithPropertyEditorForNonDomainClassesWithPropertyPath() { - parsePhonePlainTestClasses() - parsePhonePropertyEditorPlainClasses() - - def phonePropertyEditorRegistrarClazz = gcl.loadClass("PhonePropertyEditorRegistrar") - appCtx.registerBeanDefinition( - "phonePropertyEditorRegistrar", new RootBeanDefinition(phonePropertyEditorRegistrarClazz)) - - def phoneUs = gcl.loadClass("PhoneUs").newInstance() - phoneUs.area = "123" - phoneUs.prefix = "123" - phoneUs.number = "1234" - - def person = gcl.loadClass("PersonPlain").newInstance() - person.firstName = "firstName1" - person.lastName = "lastName1" - person.phoneUs = phoneUs - person.otherPhoneUs = phoneUs - - def template = '''''' - assertOutputEquals("(123)123-1234", template, [person:person]) - - def otherTemplate = '''''' - assertOutputEquals("123-123-1234", otherTemplate, [person:person]) - } -} diff --git a/grails-validation/src/main/groovy/grails/validation/trait/Validateable.groovy b/grails-validation/src/main/groovy/grails/artefact/Validateable.groovy similarity index 63% rename from grails-validation/src/main/groovy/grails/validation/trait/Validateable.groovy rename to grails-validation/src/main/groovy/grails/artefact/Validateable.groovy index dc8cf278336..e1fd4c6e8d5 100644 --- a/grails-validation/src/main/groovy/grails/validation/trait/Validateable.groovy +++ b/grails-validation/src/main/groovy/grails/artefact/Validateable.groovy @@ -13,30 +13,39 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package grails.validation.trait +package grails.artefact + +import grails.util.GrailsClassUtils -import grails.util.GrailsNameUtils import grails.util.Holders import grails.validation.ConstrainedProperty import grails.validation.ConstraintsEvaluator import grails.validation.ValidationErrors - -import java.lang.reflect.Method -import java.lang.reflect.Modifier - +import groovy.transform.CompileStatic +import org.grails.core.artefact.DomainClassArtefactHandler import org.grails.datastore.gorm.support.BeforeValidateHelper +import org.grails.datastore.mapping.reflect.NameUtils +import org.grails.validation.DefaultConstraintEvaluator import org.springframework.beans.factory.BeanFactory +import org.springframework.context.ApplicationContext +import org.springframework.context.MessageSource import org.springframework.validation.Errors import org.springframework.validation.FieldError -import org.springframework.web.context.support.WebApplicationContextUtils + +import java.lang.reflect.Method +import java.lang.reflect.Modifier /** + * A trait that can be applied to make any object Validateable + * * @since 3.0 + * * @author Jeff Brown + * @author Graeme Rocher */ trait Validateable { private BeforeValidateHelper beforeValidateHelper = new BeforeValidateHelper() - private static Map constraintsMapInternal + private static Map constraintsMapInternal Errors errors Errors getErrors() { @@ -59,22 +68,24 @@ trait Validateable { } } - static Map getConstraintsMap() { + static Map getConstraintsMap() { if(this.constraintsMapInternal == null) { - BeanFactory ctx = Holders.applicationContext - ConstraintsEvaluator evaluator = ctx.getBean(ConstraintsEvaluator.BEAN_NAME, ConstraintsEvaluator) + ConstraintsEvaluator evaluator = findConstraintsEvaluator() this.constraintsMapInternal = evaluator.evaluate this, defaultNullable() if(!defaultNullable()) { def methods = this.getDeclaredMethods() + def ignoredProperties = ['metaClass', 'errors'] + if(DomainClassArtefactHandler.isDomainClass(this)) { + ignoredProperties << 'id' << 'version' + } for(Method method : methods) { if(!Modifier.isStatic(method.modifiers) && !method.parameterTypes) { def methodName = method.name - if(methodName ==~ /get[A-Z].*/) { - def propertyName = GrailsNameUtils.getPropertyName(methodName[3..-1]) - if(propertyName != 'metaClass' && - propertyName != 'errors' && - !this.constraintsMapInternal.containsKey(propertyName)) { + if(GrailsClassUtils.isGetter(methodName, method.parameterTypes)) { + def propertyName = NameUtils.getPropertyNameForGetterOrSetter(methodName) + if( !ignoredProperties.contains(propertyName) && + !this.constraintsMapInternal.containsKey(propertyName)) { def cp = new ConstrainedProperty(this, propertyName, method.returnType) cp.applyConstraint 'nullable', false this.constraintsMapInternal.put propertyName, cp @@ -87,19 +98,28 @@ trait Validateable { this.constraintsMapInternal } - boolean validate(List fieldsToValidate = null) { + @CompileStatic + private static ConstraintsEvaluator findConstraintsEvaluator() { + try { + BeanFactory ctx = Holders.applicationContext + ConstraintsEvaluator evaluator = ctx.getBean(ConstraintsEvaluator.BEAN_NAME, ConstraintsEvaluator) + return evaluator + } catch (Throwable e) { + return new DefaultConstraintEvaluator() + } + } + + boolean validate() { + validate(null) + } + + boolean validate(List fieldsToValidate) { beforeValidateHelper.invokeBeforeValidate(this, fieldsToValidate) def constraints = getConstraintsMap() - if (constraints) { - def ctx - - def sch = Holders.servletContext - if (sch) { - ctx = WebApplicationContextUtils.getWebApplicationContext(sch) - } - def messageSource = ctx?.containsBean('messageSource') ? ctx.getBean('messageSource') : null + if (constraints) { + Object messageSource = findMessageSource() def localErrors = new ValidationErrors(this, this.class.name) def originalErrors = getErrors() for (originalError in originalErrors.allErrors) { @@ -116,7 +136,9 @@ trait Validateable { def fieldError = originalErrors.getFieldError(prop.propertyName) if(fieldError == null || !fieldError.bindingFailure) { prop.messageSource = messageSource - prop.validate(this, this.getProperty(prop.propertyName), localErrors) + + def value = getPropertyValue(prop) + prop.validate(this, value, localErrors) } } } @@ -126,6 +148,20 @@ trait Validateable { return !errors.hasErrors() } + private Object getPropertyValue(ConstrainedProperty prop) { + this.getProperty(prop.propertyName) + } + + private MessageSource findMessageSource() { + try { + ApplicationContext ctx = Holders.applicationContext + MessageSource messageSource = ctx?.containsBean('messageSource') ? ctx.getBean('messageSource', MessageSource) : null + return messageSource + } catch (Throwable e) { + return null + } + } + static boolean defaultNullable() { false } diff --git a/grails-web-common/src/main/groovy/grails/web/api/WebAttributes.groovy b/grails-web-common/src/main/groovy/grails/web/api/WebAttributes.groovy index 67c296114c2..2935601f8a3 100644 --- a/grails-web-common/src/main/groovy/grails/web/api/WebAttributes.groovy +++ b/grails-web-common/src/main/groovy/grails/web/api/WebAttributes.groovy @@ -28,6 +28,7 @@ import org.springframework.web.context.support.WebApplicationContextUtils /** * * @author Jeff Brown + * * @since 3.0 * */ diff --git a/grails-web/src/main/groovy/org/grails/web/servlet/context/GrailsConfigUtils.java b/grails-web/src/main/groovy/org/grails/web/servlet/context/GrailsConfigUtils.java index 72ce97b3333..1aa6ef03f73 100644 --- a/grails-web/src/main/groovy/org/grails/web/servlet/context/GrailsConfigUtils.java +++ b/grails-web/src/main/groovy/org/grails/web/servlet/context/GrailsConfigUtils.java @@ -179,8 +179,7 @@ public static GrailsRuntimeConfigurator determineGrailsRuntimeConfiguratorFromSe * @return true if the Config parameter is true or the System property with the same name is true */ public static boolean isConfigTrue(GrailsApplication application, String propertyName) { - return ((application != null && application.getFlatConfig() != null && DefaultTypeTransformation.castToBoolean(application.getFlatConfig().get(propertyName))) || - Boolean.getBoolean(propertyName)); + return application.getConfig().getProperty(propertyName, Boolean.class, false); } // support GrailsApplication mocking, see ControllersGrailsPluginTests