All Downloads are FREE. Search and download functionalities are using the official Maven repository.

nz.co.senanque.validationengine.ValidationEngineImpl Maven / Gradle / Ivy

Go to download

This is essentially a JAXB/XJC plugin that adds automatic validation to the generated Java classes. You define business objects in an XSD file, pass it through XJC and the plugin will add the validation code. It uses information in the XSD to pick up validation, and you can specify extensions to that in the XSD. The resulting Java classes check for validity when the setter is called and they reject attempts to set invalid values (this is a difference from other validation frameworks). The Java classes also expose a metadata interface to make it easy for UIs to generate, say, lists of options for a select box. The validation framework handles single field validation but you can inject a rule engine (or several) to handle cross field validation. But to any Java code the objects still look like ordinary Java beans. The surrounding application is unaware that they are anything different until they throw an exception. This makes it easy to use with frameworks that expect Java beans, and most of them do.

There is a newer version: 3.3.5
Show newest version
/*******************************************************************************
 * Copyright (c)2014 Prometheus Consulting
 *
 * 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 nz.co.senanque.validationengine;

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Date;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import javax.annotation.PostConstruct;

import nz.co.senanque.resourceloader.MessageResource;
import nz.co.senanque.validationengine.choicelists.Choice;
import nz.co.senanque.validationengine.choicelists.ChoiceBase;
import nz.co.senanque.validationengine.fieldvalidators.FieldValidator;
import nz.co.senanque.validationengine.metadata.ClassMetadata;
import nz.co.senanque.validationengine.metadata.EngineMetadata;
import nz.co.senanque.validationengine.metadata.PropertyMetadata;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceAware;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.stereotype.Service;

/** 
 * This is the validation engine implementation. 
 * It implements validation on individual fields, including custom validation, as well as labels etc.
 * It accepts plugins for cross field validation and generation of new data.
 * 
 * @author Roger Parkinson
 * @version $Revision: 1.6 $
 */
@Service("validationEngine")
@MessageResource("ValidationMessages")
//@Vetoed
public final class ValidationEngineImpl implements ValidationEngine,
        MessageSourceAware
{
    private final static Logger log = LoggerFactory.getLogger(ValidationEngineImpl.class);

    @Autowired private transient EngineMetadata m_metadata;
    @Autowired private transient LocaleAwareExceptionFactory m_localeAwareExceptionFactory;
    private transient MessageSource m_messageSource;
    private transient Binder m_binder = new Binder(this);
    @Autowired(required=false)
    private transient List m_plugins;
    @Value("${nz.co.senanque.validationengine.ValidationEngineImpl.identifier:not-set}")
    private transient String m_identifier;

    public Map add(final ListeningArray array, final ValidationObject value,
            final ValidationSession session) throws ValidationException
    {
        log.debug("add {} {}", array.getClass(), value);
        final ProxyField proxyField = array.getProxyField();
        proxyField.useCurrentValue(false);
        final Map bound = getBinder().bind(value, session, proxyField, new Integer(array.size()),null);
        session.provisionalObjects(bound);
        try
        {
            for (Plugin plugin: getPlugins())
            {
                ProxyObject ownerProxyObject = proxyField.getProxyObject();
                plugin.bind(session, bound, proxyField,(ValidationObject)ownerProxyObject.getObject());
            }
        }
        finally
        {
            session.provisionalObjects(null);
        }
        return bound;
    }
    public Map addAll(final ListeningArray listeningArray,
            final List o, final ValidationSession validationSession)
    {
        final Map bound = new IdentityHashMap();
        for (ValidationObject object: o)
        {
            bound.putAll(add(listeningArray,object,validationSession));
        }
        return bound;
    }
    public void removedFrom(final ListeningArray array, final ValidationObject value,
            final ValidationSession session)
    {
        log.debug("remove {} {}", array.getClass(), value);
        final ProxyField proxyField = array.getProxyField();
        session.unbind(proxyField,value);
        return;
    }

    public String getStats(final ValidationSession session)
    {
    	StringBuilder ret = new StringBuilder();
        for (Plugin plugin: getPlugins())
        {
        	ret.append(plugin.getStats(session));
        }
    	return ret.toString();
    }

    public void set(final ValidationObject object, final String name, final Object newValue, final Object currentValue, final ValidationSession session)
            throws ValidationException
    {
        log.debug("set {} {}", name, newValue);
        if (newValue != null && newValue.equals(currentValue)) 
        {
            return;
        }
        PropertyMetadata fieldMetadata = getMetadata().getField(object, name);
        if (fieldMetadata == null)
        {
            return;
        }
        
        if (newValue == null && currentValue == null)
        {
            return;
        }
        ObjectMetadata objectMetadata = session.getMetadata(object);
        ProxyField proxyField = objectMetadata.getProxyField(name);
        proxyField.useCurrentValue(false);
        final ClassMetadata clazz = getMetadata().getClassMetadata(newValue!=null?newValue.getClass():currentValue.getClass());
        if (clazz != null)
        {
            // We are setting a known object, as opposed to a primitive or a String etc
            Map bound = null;
            if (currentValue != null)
            {
                session.unbind(proxyField, (ValidationObject)currentValue);
            }
            if (newValue != null)
            {
                validate(fieldMetadata, newValue,proxyField);
                proxyField.setValue(newValue);
            }
            else
            {
                proxyField.reset();
            }
            bound = session.bind((ValidationObject)newValue,object);
            session.provisionalObjects(bound);
            try
            {
                for (Plugin plugin: getPlugins())
                {
                    plugin.bind(session, bound,null,object);
                }
            }
            finally
            {
                session.provisionalObjects(null);
            }
        }
        else
        {
            Object oldValue = proxyField.getValue();
            List oldHistory = proxyField.getHistory();
            if (proxyField.getChoiceList() != null && oldValue != null)
            {
                for (Plugin plugin: getPlugins())
                {
                    plugin.set(session, proxyField, null);
                }               
            }
            if (newValue != null)
            {
                try
                {
                    validate(fieldMetadata, newValue, proxyField);
                }
                catch (ValidationException e)
                {
                    for (Plugin plugin: getPlugins())
                    {
                        plugin.set(session, proxyField, oldValue);
                    }
                    throw e;
                }
                proxyField.setValue(newValue);
            }
            else
            {
                proxyField.reset();
            }
            try {
				for (Plugin plugin: getPlugins())
				{
				    plugin.set(session, proxyField, newValue);
				}
			} catch (Exception e) {
                proxyField.setHistory(oldHistory);
                for (Plugin plugin: getPlugins())
                {
                    plugin.set(session, proxyField, oldValue);
                }
                throw e;
			}
        }
    }
	public void invokeListeners(ValidationObject object, String name,
			Object newValue, Object currentValue,
			ValidationSession session) {
        PropertyMetadata fieldMetadata = getMetadata().getField(object, name);
        if (fieldMetadata == null)
        {
            return;
        }
        
        ObjectMetadata objectMetadata = session.getMetadata(object);
        ProxyField proxyField = objectMetadata.getProxyField(name);
		for (SetterListener listener: proxyField.getListeners()) {
			listener.run(object, name, newValue, session);
		}
	}
	
	public void addListener(ValidationObject object, String name, ValidationSession session, SetterListener listener) {
        PropertyMetadata fieldMetadata = getMetadata().getField(object, name);
        if (fieldMetadata == null)
        {
            return;
        }
        
        ObjectMetadata objectMetadata = session.getMetadata(object);
        ProxyField proxyField = objectMetadata.getProxyField(name);
        proxyField.addListener(listener);
	}

    public void validate(PropertyMetadata fieldMetadata, Object value, ProxyField proxyField)
    {
        for (FieldValidator fv : fieldMetadata.getConstraintValidators())
        {
            fv.validate(value);
        }
        List choiceList = proxyField.getChoiceList();
        if (choiceList != null)
        {
            boolean found = false;
            for (Choice choice : choiceList)
            {
                if (choice.getKey().equals(value.toString()))
                {
                    found = true;
                    break;
                }
            }
            if (!found)
            {
                String labelName = fieldMetadata.getLabelName();
        		MessageSourceAccessor messageSourceAccessor = new MessageSourceAccessor(m_messageSource);
                String message = messageSourceAccessor.getMessage(
                        "nz.co.senanque.validationengine.choicelist",
                        new Object[]
                        { labelName, value }, "Failed choicelist validation.");
                throw new ValidationException(message);
            }
        }
    }

    public void setMessageSource(MessageSource arg0)
    {
        m_messageSource = arg0;
    }

    public ClassMetadata getClassMetadata(Class clazz)
    {
        return getMetadata().getClassMetadata(clazz);
    }

    public Map bind(ValidationObject object,
            ValidationSession session, ValidationObject owner)
    {
        Map ret = getBinder().bind(object,
                session,owner);
        session.provisionalObjects(ret);
        try
        {
            for (Plugin plugin : getPlugins())
            {
                plugin.bind(session, ret,null,owner);
            }
        }
        finally
        {
            session.provisionalObjects(null);
        }
        return ret;
    }
    public void unbind(ValidationSession session, ProxyField proxyField, ValidationObject validationObject, Map boundMap)
    {
    	if (validationObject == null)
    	{
    		return;
    	}
    	// this actually unbinds all child objects, not the current object
        getBinder().unbind(session, validationObject, boundMap);
        // once all the child objects are unbound then tell the plugins
        // to unbind this one
        ProxyObject proxyObject = boundMap.get(validationObject);
    	if (proxyObject == null)
    	{
    		return;
    	}
        for (Plugin plugin : getPlugins())
        {
        	plugin.unbind(session, proxyField, validationObject);
        }
        ProxyObject removed = boundMap.remove(validationObject);
        if (removed == null)
        {
        	log.warn("failed to remove {} from boundMap",validationObject.toString());
        }
    }

    public void unbindAll(ValidationSession session, Map boundMap)
    {
        for (ValidationObject validationObject: boundMap.keySet())
        {
            for (Plugin plugin : getPlugins())
            {
                plugin.unbind(session,null, validationObject);
            }
        }
        getBinder().unbindAll(boundMap);
    }

    private Binder getBinder()
    {
        return m_binder;
    }

    public EngineMetadata getMetadata()
    {
        return m_metadata;
    }

    public void setMetadata(EngineMetadata metadata)
    {
        m_metadata = metadata;
    }

    public List getPlugins()
    {
        return m_plugins;
    }
    @SuppressWarnings("unchecked")
	public  T getPlugin(Class clazz) {
    	for (Plugin plugin: m_plugins) {
    		if (plugin.getClass().equals(clazz)) {
    			return (T)plugin;
    		}
    	}
    	return null;
    }
    public void setPlugins(List plugins)
    {
        m_plugins = plugins;
    }
    public MessageSource getMessageSource()
    {
        return m_messageSource;
    }
    public boolean clean(ValidationObject object)
    {
        return false;
    }
    public ValidationSession createSession()
    {
        return new ValidationSession(this,Locale.getDefault());
    }
    public ValidationSession createSession(Locale locale)
    {
        return new ValidationSession(this,locale);
    }
    public void close(ValidationSession validationSession)
    {
      for (Plugin plugin: getPlugins())
      {
          plugin.close(validationSession);
      }
      validationSession.unbindAll();
    }
    @PostConstruct
    public void init()
    {
    	if (getPlugins() == null) {
    		setPlugins(new ArrayList());
    	}
        for (Plugin plugin: getPlugins())
        {
            plugin.init(getMetadata());
        }
    }
	public String getIdentifier() {
		return m_identifier;
	}
	public void setIdentifier(String identifier) {
		m_identifier = identifier;
	}
	/* (non-Javadoc)
	 * @see nz.co.senanque.validationengine.ValidationEngine#getCurrentTime()
	 */
	@Override
	public long getCurrentTime() {
		// just return the current ms
		return new Date().getTime();
	}
	public LocaleAwareExceptionFactory getLocaleAwareExceptionFactory() {
		return m_localeAwareExceptionFactory;
	}
	public void setLocaleAwareExceptionFactory(
			LocaleAwareExceptionFactory localeAwareExceptionFactory) {
		m_localeAwareExceptionFactory = localeAwareExceptionFactory;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy