org.codehaus.groovy.grails.validation.metaclass.ConstraintsEvaluatingDynamicProperty.java Source code

Java tutorial

Introduction

Here is the source code for org.codehaus.groovy.grails.validation.metaclass.ConstraintsEvaluatingDynamicProperty.java

Source

/* Copyright 2004-2005 the original author or 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.
 */
package org.codehaus.groovy.grails.validation.metaclass;

import groovy.lang.*;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.groovy.control.CompilationFailedException;
import org.codehaus.groovy.grails.commons.GrailsClassUtils;
import org.codehaus.groovy.grails.commons.GrailsDomainClassProperty;
import org.codehaus.groovy.grails.commons.metaclass.AbstractDynamicProperty;
import org.codehaus.groovy.grails.commons.metaclass.ProxyMetaClass;
import org.codehaus.groovy.grails.validation.ConstrainedProperty;
import org.codehaus.groovy.grails.validation.ConstrainedPropertyBuilder;

import java.io.InputStream;
import java.util.*;

/**
 * This is a dynamic property that instead of returning the closure sets a new proxy meta class for the scope 
 * of the call and invokes the closure itself which builds up a list of ConstrainedProperty instances
 * 
 * @author Graeme Rocher
 * @author Sergey Nebolsin
 * @since 07-Nov-2005
 */
public class ConstraintsEvaluatingDynamicProperty extends AbstractDynamicProperty {

    private static final String CONSTRAINTS_GROOVY = "Constraints.groovy";

    private static final Log LOG = LogFactory.getLog(ConstraintsDynamicProperty.class);

    public static final String PROPERTY_NAME = "constraints";

    private GrailsDomainClassProperty[] properties;

    public ConstraintsEvaluatingDynamicProperty(GrailsDomainClassProperty[] properties) {
        super(PROPERTY_NAME);
        this.properties = properties;
    }

    public ConstraintsEvaluatingDynamicProperty() {
        super(PROPERTY_NAME);
    }

    public Object get(Object object) {

        // Suppress recursion problems if a GroovyObject
        if (object instanceof GroovyObject) {
            GroovyObject go = (GroovyObject) object;

            if (go.getMetaClass() instanceof ProxyMetaClass) {
                go.setMetaClass(((ProxyMetaClass) go.getMetaClass()).getAdaptee());
            }
        }

        // Compile list of ancestors to query for constraints
        LinkedList classChain = new LinkedList();
        Class clazz = object.getClass();
        while (clazz != Object.class) {
            classChain.addFirst(clazz);
            clazz = clazz.getSuperclass();
        }

        ConstrainedPropertyBuilder delegate = new ConstrainedPropertyBuilder(object);

        // Evaluate all the constraints closures in the inheritance chain
        for (Iterator it = classChain.iterator(); it.hasNext();) {
            clazz = (Class) it.next();
            Closure c = (Closure) GrailsClassUtils.getStaticPropertyValue(clazz, PROPERTY_NAME);
            if (c == null) {
                c = getConstraintsFromScript(object);
            }

            if (c != null) {
                c.setDelegate(delegate);
                c.call();
            } else {
                LOG.debug("User-defined constraints not found on class [" + clazz
                        + "], applying default constraints");
            }
        }

        Map constrainedProperties = delegate.getConstrainedProperties();
        if (this.properties != null) {
            for (int i = 0; i < this.properties.length; i++) {
                GrailsDomainClassProperty p = this.properties[i];
                ConstrainedProperty cp = (ConstrainedProperty) constrainedProperties.get(p.getName());
                if (cp == null) {
                    cp = new ConstrainedProperty(p.getDomainClass().getClazz(), p.getName(), p.getType());
                    cp.setOrder(constrainedProperties.size() + 1);
                    constrainedProperties.put(p.getName(), cp);
                }
                // Make sure all fields are required by default, unless specified otherwise by the constraints
                if (!cp.hasAppliedConstraint(ConstrainedProperty.NULLABLE_CONSTRAINT)
                        && !p.getName().equals(GrailsDomainClassProperty.DATE_CREATED)
                        && !p.getName().equals(GrailsDomainClassProperty.LAST_UPDATED)
                        && !((p.isOneToOne() || p.isManyToOne()) && p.isCircular())) {
                    // TODO remove "p.isOptional()" in 0.6
                    //                    cp.applyConstraint(ConstrainedProperty.NULLABLE_CONSTRAINT, Boolean.valueOf(p.isAssociation() || p.isOptional()));
                    cp.applyConstraint(ConstrainedProperty.NULLABLE_CONSTRAINT,
                            Boolean.valueOf(p.isOptional() || Collection.class.isAssignableFrom(p.getType())
                                    || Map.class.isAssignableFrom(p.getType())));
                }
            }
        }

        return constrainedProperties;
    }

    private Closure getConstraintsFromScript(Object object) {
        // Fallback to xxxxConstraints.groovy script for Java domain classes
        String className = object.getClass().getName();
        String constraintsScript = className.replaceAll("\\.", "/") + CONSTRAINTS_GROOVY;
        InputStream stream = getClass().getClassLoader().getResourceAsStream(constraintsScript);

        if (stream != null) {
            GroovyClassLoader gcl = new GroovyClassLoader();
            try {
                Class scriptClass = gcl.parseClass(stream);
                Script script = (Script) scriptClass.newInstance();
                script.run();
                Binding binding = script.getBinding();
                if (binding.getVariables().containsKey(PROPERTY_NAME)) {
                    return (Closure) binding.getVariable(PROPERTY_NAME);
                } else {
                    LOG.warn("Unable to evaluate constraints from [" + constraintsScript
                            + "], constraints closure not found!");
                    return null;
                }
            } catch (CompilationFailedException e) {
                LOG.error("Compilation error evaluating constraints for class [" + object.getClass() + "]: "
                        + e.getMessage(), e);
                return null;
            } catch (InstantiationException e) {
                LOG.error("Instantiation error evaluating constraints for class [" + object.getClass() + "]: "
                        + e.getMessage(), e);
                return null;
            } catch (IllegalAccessException e) {
                LOG.error("Illegal access error evaluating constraints for class [" + object.getClass() + "]: "
                        + e.getMessage(), e);
                return null;
            }
        } else {
            return null;
        }
    }

    public void set(Object object, Object newValue) {
        throw new UnsupportedOperationException("Cannot set read-only property [" + PROPERTY_NAME + "]");
    }

}