Skip to main content
summaryrefslogblamecommitdiffstats
blob: b394bd60fc697e062a76c6eccce46aaffd80b78a (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11










                                                                                
                            
                                                  
                                                        
                                                      
                                                           
                                                                 
                                                                    
                                                                        
                                                                              
                                                                            
                                                  
                                                                       

                                                                               
                                                          
                                                              

   
                                                                   
   

                                            
                                                 
 
           


                                                                         

                                                   
           
                                                                                                    
                                                                             


           


                                                                         

                                                
           
                                                                                                       
                                                                                


           


                                                                         






                                                                                     
           
                                                                                                                  
                                                                                   
         
 


                                                                         









                                                                                                                   
                                                                                  




                                                                         
                                                       






                                                                                      

                                                                                                                     
                                                                                      
         
 


                                                                         









                                                                                                                      
                                                                                     




                                                                         
                                                        






                                                                                     

                                                                                                                       
                                                                                       
         



                                                                         









                                                                                                                        
                                                                                      




                                                                         
                                                            






                                                                                      
           
                                                                                                                          
                                                                                          




                                                                         









                                                                                                                           
                                                                                         




                                                                         
                                           


                                                                                
                                                                                        
                     
           

                                                           


                                                                     
           

                                                                                                                                                   




                                                                         
                                           


                                                                                

                                                                                  

                                                           


                                                                     
           




                                                                      
                                                                                           




                                                                         
                                           




                                                                                  

                                                           


                                                                     
           




                                                                      





                                                                                                
                                           


                                                                       
           

                                                           


                                                                     
           
                                                                                                                                                    
                                                                                    


         
































































































                                                                                                                 


                                                   







                                                                                     

                                                   













                                                                                                             
                                                       
           

                                                   


                                                  




                                                            



                                                                                                                                                              







                                                                                     

                                                   


                                                  




                                                            



                                                                                                                                                               



                                                                               





                                                                                  

                                                   
                                                              
                                                       









                                                                                                                                    




                                                                               

                                                   











                                                                                                                                     

                                                                          





                                                                                              

                                                   

                                                               

                                           




                                                                      
                                                                                              

         









                                                                           

                                                   



                                                              




                                                                      
                                                                                                              
         
 
 







                                                                                                
                                                                                               




                                                                       


                                                                                   
           

                                                   
                                                 
                                              

                                           



                                                                                                           
                                                                                                                                               
                                                                                 




                                                                                         





                                                                    
           

                                                   


                                                                                                                       
                                                                                        


         
                                                      




                                                                           
              
                                                                                                                  
                                                                                                  
           


                                                             

                                           



                                                                         
                                                                                                                  


           




                                                                                                           
           


                                                             

                                           



                                                                         
                                                                                                                   


           

                                                                               




                                                                                                                   


                                                             
















                                                                                                                                         


                                                             












                                                                                                                              
                                                                                       
              
                                                                                                                  
                                                                                                  
           


                                                             

                                           




                                                                          
                                                                                                                                         


           




                                                                                                           
           


                                                             

                                           




                                                                           
                                                                                                                                          


           

                                                                                       
              
                                                                                                                  
                                                                                                  
           


                                                             

                                           
                                                                                                                          


                                                                         
                                                                                                                           






                                                                                                           
           


                                                             

                                           
                                                                                                                           


                                                                         
                                                                                                                  

         


                                     
           


                                                                                                 


                                                                    
           

                                                                                                                                                                               


           


                                                                          


                                                                    































                                                                                                                                                           


                                                                      
           

                                                                                                                        


           


                                                                         


                                                                      
           

                                                                                                                                                                  


           


                                                             


                                                                      



































                                                                                                                                     


                                                                      
           






                                                                                                 


                                                                      





















                                                                        

         
 
                                              

           





                                                                         

                                                   
           
                                                                                                                                    
                                                                                   


           





                                                                         

                                                   
           
                                                                                                                                     
                                                                                    








                                                                         



                                                    
           
                                                                                                                                                                                                                 
                                                  












                                                                         



                                                    
           
                                                                                                                                                                                                                  
                                                  



                                                         


           
                                                               
                                                                 




                                                                         

                                                   
           
                                                                                                                                                        
                                                                                          


           






                                                                         

                                                   

                                                                                                                                                         
                                                                                           


           
                                                                               
                                                                 




                                                                         

                                                   
           
                                                                                                                                                                                                    
                                                               
                                                                        











                                                                               

                                                   
           
                                                                                                                                                                                                     
                                                               
                                                                         







                                                  
                                                            




                                                                                                   


                                                   





                                                                                    
                                                                                         







                                                                                        


                                                   





                                                                                     
                                                                                          



                                                            




                                                                                                   



                                                    
           
                                                                                                                                



                                                                       
                                                                                                            


           
                                                            



                                                                                        



                                                    
           
                                                                                                                                 



                                                                       
                                                                                                             


           
                                                                    




                                                                                                   



                                                    
           
                                                                                                                                                                  



                                                                       
                                                                                                                     


           
                                                                    



                                                                                        



                                                    
           
                                                                                                                                                                   



                                                                       





                                                                                                                                      



                                                    










                                                                                                              



                                                    





                                                                                                                                                        













                                                                                                   



                                                    






                                                                                                                                             
                                                                                                                                     









                                                                                        



                                                    






                                                                                                                                              
                                                                                                                                      










                                                                                                   



                                                    






                                                                                                                                                                             
                                                                                                                                               









                                                                                        



                                                    






                                                                                                                                                                              
                                                                                                                                                 








                                                                          


                                                   
           

                                                                                                                             

                                                                  
                                                                                                                          








                                                                           


                                                   
           

                                                                                                                              

                                                                  
                                                                                                    


         



                                                                                      
           

                                                   






                                                                                                                                   


                                                                                         
           

                                                   

                                                     

                                                                                                                                                                                                                                          


           

                                                                                                 

                                                   
                                                     
           
                                                                                                                                                    
                                                                                   


         





                                                                                                  
           

                                                   
                                    
           
                                                                      


                                                             


                                                                                


                                                   




                                                                                
 
                                                            



                                                                                               

                               

                                                                                    
           


                                                   

                                         

                                                    


                                                                                                               

         


                                                                                                                             


                                                                                               

                               

                                                                                    
           


                                                   

                                    

                                                    

                                                                                                                          



                                                                                  

                               

                                                                                   
           


                                                   

                                         


                                                    
                                                                                                                


                                                            






                                                                        


                                                   

















                                                                                                                    


                                                   







                                                                                                                     
 




                                                                      
                                           



                                                          
/*******************************************************************************
 * Copyright (c) 2016 Oracle. All rights reserved.
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v1.0, which accompanies this distribution
 * and is available at http://www.eclipse.org/legal/epl-v10.html.
 * 
 * Contributors:
 *     Oracle - initial API and implementation
 ******************************************************************************/
package org.eclipse.jpt.common.utility.internal.model.value;

import java.util.Collection;
import org.eclipse.jpt.common.utility.Association;
import org.eclipse.jpt.common.utility.closure.BiClosure;
import org.eclipse.jpt.common.utility.closure.Closure;
import org.eclipse.jpt.common.utility.internal.ObjectTools;
import org.eclipse.jpt.common.utility.internal.SimpleAssociation;
import org.eclipse.jpt.common.utility.internal.closure.ClosureTools;
import org.eclipse.jpt.common.utility.internal.predicate.PredicateTools;
import org.eclipse.jpt.common.utility.internal.transformer.TransformerAdapter;
import org.eclipse.jpt.common.utility.internal.transformer.TransformerTools;
import org.eclipse.jpt.common.utility.model.Model;
import org.eclipse.jpt.common.utility.model.value.CollectionValueModel;
import org.eclipse.jpt.common.utility.model.value.ModifiablePropertyValueModel;
import org.eclipse.jpt.common.utility.model.value.PropertyValueModel;
import org.eclipse.jpt.common.utility.predicate.Predicate;
import org.eclipse.jpt.common.utility.transformer.Transformer;

/**
 * {@link PropertyValueModel Property value model} utility methods.
 */
public final class PropertyValueModelTools {

	// ********** Boolean adapters **********

	/**
	 * Construct a property value model adapter for the specified
	 * property value model that returns whether the property's value
	 * is <code>null</code>.
	 * 
	 * @see #valueIsNotNull(PropertyValueModel)
	 */
	public static PropertyValueModel<Boolean> valueIsNull(PropertyValueModel<?> propertyModel) {
		return valueAffirms_(propertyModel, PredicateTools.isNull());
	}

	/**
	 * Construct a property value model adapter for the specified
	 * property value model that returns whether the property's value
	 * is <em>not</em> <code>null</code>.
	 * 
	 * @see #valueIsNull(PropertyValueModel)
	 */
	public static PropertyValueModel<Boolean> valueIsNotNull(PropertyValueModel<?> propertyModel) {
		return valueAffirms_(propertyModel, PredicateTools.isNotNull());
	}

	/**
	 * Construct a property value model adapter for the specified
	 * property value model that returns whether the property's value
	 * equals the specified value.
	 * <p>
	 * <strong>NB:</strong> If specified model's value is <code>null</code>,
	 * the returned model's value will be <em>true</em> if the specified value is
	 * also <code>null</code>.
	 * 
	 * @see #valueNotEquals(PropertyValueModel, Object)
	 * @see #valueEquals_(PropertyValueModel, Object)
	 */
	public static PropertyValueModel<Boolean> valueEquals(PropertyValueModel<?> propertyModel, Object value) {
		return valueAffirms_(propertyModel, PredicateTools.isEqual(value));
	}

	/**
	 * Construct a property value model adapter for the specified
	 * property value model that returns whether the property's value
	 * equals the specified value.
	 * <p>
	 * <strong>NB:</strong> If specified model's value is <code>null</code>,
	 * the returned model's value will also be a <code>null</code>
	 * {@link Boolean}.
	 * 
	 * @see #valueNotEquals_(PropertyValueModel, Object)
	 * @see #valueEquals(PropertyValueModel, Object)
	 */
	public static PropertyValueModel<Boolean> valueEquals_(PropertyValueModel<?> propertyModel, Object value) {
		return valueAffirms(propertyModel, PredicateTools.isEqual(value));
	}

	/**
	 * Construct a property value model adapter for the specified
	 * property value model that returns whether the property's value
	 * does <em>not</em> equal the specified value.
	 * <p>
	 * <strong>NB:</strong> If specified model's value is <code>null</code>,
	 * the returned model's value will be <em>false</em> if the specified value is
	 * also <code>null</code>.
	 * 
	 * @see #valueNotEquals_(PropertyValueModel, Object)
	 * @see #valueEquals(PropertyValueModel, Object)
	 */
	public static PropertyValueModel<Boolean> valueNotEquals(PropertyValueModel<?> propertyModel, Object value) {
		return valueAffirms_(propertyModel, PredicateTools.isNotEqual(value));
	}

	/**
	 * Construct a property value model adapter for the specified
	 * property value model that returns whether the property's value
	 * does <em>not</em> equal the specified value.
	 * <p>
	 * <strong>NB:</strong> If specified model's value is <code>null</code>,
	 * the returned model's value will also be a <code>null</code>
	 * {@link Boolean}.
	 * 
	 * @see #valueNotEquals(PropertyValueModel, Object)
	 * @see #valueEquals_(PropertyValueModel, Object)
	 */
	public static PropertyValueModel<Boolean> valueNotEquals_(PropertyValueModel<?> propertyModel, Object value) {
		return valueAffirms(propertyModel, PredicateTools.isNotEqual(value));
	}

	/**
	 * Construct a property value model adapter for the specified
	 * property value model that returns whether the property's value
	 * is <em>identical</em> to the specified value.
	 * <p>
	 * <strong>NB:</strong> If specified model's value is <code>null</code>,
	 * the returned model's value will be <em>true</em> if the specified value is
	 * also <code>null</code>.
	 * 
	 * @see #valueIsIdentical_(PropertyValueModel, Object)
	 * @see #valueIsNotIdentical(PropertyValueModel, Object)
	 */
	public static PropertyValueModel<Boolean> valueIsIdentical(PropertyValueModel<?> propertyModel, Object value) {
		return valueAffirms_(propertyModel, PredicateTools.isIdentical(value));
	}

	/**
	 * Construct a property value model adapter for the specified
	 * property value model that returns whether the property's value
	 * is <em>identical</em> to the specified value.
	 * <p>
	 * <strong>NB:</strong> If specified model's value is <code>null</code>,
	 * the returned model's value will also be a <code>null</code>
	 * {@link Boolean}.
	 * 
	 * @see #valueIsIdentical(PropertyValueModel, Object)
	 * @see #valueIsNotIdentical_(PropertyValueModel, Object)
	 */
	public static PropertyValueModel<Boolean> valueIsIdentical_(PropertyValueModel<?> propertyModel, Object value) {
		return valueAffirms(propertyModel, PredicateTools.isIdentical(value));
	}

	/**
	 * Construct a property value model adapter for the specified
	 * property value model that returns whether the property's value
	 * is <em>not identical</em> to the specified value.
	 * <p>
	 * <strong>NB:</strong> If specified model's value is <code>null</code>,
	 * the returned model's value will be <em>false</em> if the specified value is
	 * also <code>null</code>.
	 * 
	 * @see #valueIsNotIdentical_(PropertyValueModel, Object)
	 * @see #valueIsIdentical(PropertyValueModel, Object)
	 */
	public static PropertyValueModel<Boolean> valueIsNotIdentical(PropertyValueModel<?> propertyModel, Object value) {
		return valueAffirms_(propertyModel, PredicateTools.isNotIdentical(value));
	}

	/**
	 * Construct a property value model adapter for the specified
	 * property value model that returns whether the property's value
	 * is <em>not identical</em> to the specified value.
	 * <p>
	 * <strong>NB:</strong> If specified model's value is <code>null</code>,
	 * the returned model's value will also be a <code>null</code>
	 * {@link Boolean}.
	 * 
	 * @see #valueIsNotIdentical(PropertyValueModel, Object)
	 * @see #valueIsIdentical_(PropertyValueModel, Object)
	 */
	public static PropertyValueModel<Boolean> valueIsNotIdentical_(PropertyValueModel<?> propertyModel, Object value) {
		return valueAffirms(propertyModel, PredicateTools.isNotIdentical(value));
	}

	/**
	 * Construct a property value model adapter for the specified
	 * property value model that returns whether the property's value
	 * affirms the specified predicate.
	 * <p>
	 * <strong>NB:</strong> If specified model's value is <code>null</code>,
	 * the returned model's value will also be a <code>null</code>
	 * {@link Boolean}; and the value will <em>never</em> be passed to the specified
	 * predicate.
	 * 
	 * @param <V> the type of the wrapped model's value
	 * 
	 * @see #valueAffirms_(PropertyValueModel, Predicate)
	 * @see #valueAffirms(PropertyValueModel, Predicate, boolean)
	 * @see #valueAffirms(PropertyValueModel, Predicate, Boolean)
	 */
	public static <V> PropertyValueModel<Boolean> valueAffirms(PropertyValueModel<? extends V> propertyModel, Predicate<? super V> predicate) {
		return valueAffirms(propertyModel, predicate, null);
	}

	/**
	 * Construct a property value model adapter for the specified
	 * property value model that returns whether the property's value
	 * affirms the specified predicate.
	 * <p>
	 * <strong>NB:</strong> If specified model's value is <code>null</code>,
	 * the returned model's value will be the specified null result;
	 * and the value will <em>never</em> be passed to the specified predicate.
	 * 
	 * @param <V> the type of the wrapped model's value
	 * 
	 * @see #valueAffirms(PropertyValueModel, Predicate)
	 * @see #valueAffirms_(PropertyValueModel, Predicate)
	 * @see #valueAffirms(PropertyValueModel, Predicate, Boolean)
	 */
	public static <V> PropertyValueModel<Boolean> valueAffirms(
			PropertyValueModel<? extends V> propertyModel,
			Predicate<? super V> predicate,
			boolean nullResult
	) {
		return valueAffirms(propertyModel, predicate, Boolean.valueOf(nullResult));
	}

	/**
	 * Construct a property value model adapter for the specified
	 * property value model that returns whether the property's value
	 * affirms the specified predicate.
	 * <p>
	 * <strong>NB:</strong> If specified model's value is <code>null</code>,
	 * the returned model's value will be the specified null result;
	 * and the value will <em>never</em> be passed to the specified predicate.
	 * 
	 * @param <V> the type of the wrapped model's value
	 * 
	 * @see #valueAffirms(PropertyValueModel, Predicate)
	 * @see #valueAffirms_(PropertyValueModel, Predicate)
	 * @see #valueAffirms(PropertyValueModel, Predicate, boolean)
	 */
	public static <V> PropertyValueModel<Boolean> valueAffirms(
			PropertyValueModel<? extends V> propertyModel,
			Predicate<? super V> predicate,
			Boolean nullResult
	) {
		return transform_(propertyModel, TransformerTools.adapt(predicate, nullResult));
	}

	/**
	 * Construct a property value model adapter for the specified
	 * property value model that returns whether the property's value
	 * affirms the specified predicate.
	 * <p>
	 * <strong>NB:</strong> The specified predicate must be able to
	 * handle a <code>null</code> variable.
	 * 
	 * @param <V> the type of the wrapped model's value
	 * 
	 * @see #valueAffirms(PropertyValueModel, Predicate)
	 * @see #valueAffirms(PropertyValueModel, Predicate, boolean)
	 * @see #valueAffirms(PropertyValueModel, Predicate, Boolean)
	 */
	public static <V> PropertyValueModel<Boolean> valueAffirms_(PropertyValueModel<? extends V> propertyModel, Predicate<? super V> predicate) {
		return transform_(propertyModel, TransformerTools.adapt(predicate));
	}


	// ********** Composite boolean adapters **********

	/**
	 * Construct a boolean property value model that is a composite AND of the
	 * specified boolean property value models; i.e. the model's value is {@link Boolean#TRUE}
	 * if all the contained models' values are {@link Boolean#TRUE},
	 * otherwise its value is {@link Boolean#FALSE}.
	 * The model's default value, when it contains no nested models, is {@link Boolean#TRUE}.
	 */
	@SafeVarargs
	public static PropertyValueModel<Boolean> and(PropertyValueModel<Boolean>... models) {
		return CollectionValueModelTools.compositePropertyValueModel(AND_TRANSFORMER, models);
	}

	/**
	 * Construct a boolean property value model that is a composite AND of the
	 * specified boolean property value models; i.e. the model's value is {@link Boolean#TRUE}
	 * if all the contained models' values are {@link Boolean#TRUE},
	 * otherwise its value is {@link Boolean#FALSE}.
	 * The model's default value, when it contains no nested models, is {@link Boolean#TRUE}.
	 */
	public static PropertyValueModel<Boolean> and(Collection<? extends PropertyValueModel<Boolean>> models) {
		return CollectionValueModelTools.compositePropertyValueModel(models, AND_TRANSFORMER);
	}

	/**
	 * @see AndTransformer
	 */
	public static final Transformer<Collection<Boolean>, Boolean> AND_TRANSFORMER = new AndTransformer();

	/**
	 * A transformer that transforms a collection of {@link Boolean}s into a single
	 * {@link Boolean} by ANDing them together. Its default value is {@link Boolean#TRUE}.
	 * @see CollectionValueModelTools#and(CollectionValueModel)
	 */
	public static final class AndTransformer
		extends TransformerAdapter<Collection<Boolean>, Boolean>
	{
		@Override
		public Boolean transform(Collection<Boolean> booleans) {
			for (Boolean b : booleans) {
				if ( ! b.booleanValue()) {
					return Boolean.FALSE;
				}
			}
			return Boolean.TRUE;
		}
	}

	/**
	 * Construct a boolean property value model that is a composite OR of the
	 * specified boolean property value models; i.e. the model's value is {@link Boolean#FALSE}
	 * if all the contained models' values are {@link Boolean#FALSE},
	 * otherwise its value is {@link Boolean#TRUE}.
	 * The model's default value, when it contains no nested models, is {@link Boolean#FALSE}.
	 */
	@SafeVarargs
	public static PropertyValueModel<Boolean> or(PropertyValueModel<Boolean>... models) {
		return CollectionValueModelTools.compositePropertyValueModel(OR_TRANSFORMER, models);
	}

	/**
	 * Construct a boolean property value model that is a composite OR of the
	 * specified boolean property value models; i.e. the model's value is {@link Boolean#FALSE}
	 * if all the contained models' values are {@link Boolean#FALSE},
	 * otherwise its value is {@link Boolean#TRUE}.
	 * The model's default value, when it contains no nested models, is {@link Boolean#FALSE}.
	 */
	public static PropertyValueModel<Boolean> or(Collection<? extends PropertyValueModel<Boolean>> models) {
		return CollectionValueModelTools.compositePropertyValueModel(models, OR_TRANSFORMER);
	}

	/**
	 * @see OrTransformer
	 */
	public static final Transformer<Collection<Boolean>, Boolean> OR_TRANSFORMER = new OrTransformer();

	/**
	 * A transformer that transforms a collection of {@link Boolean}s into a single
	 * {@link Boolean} by ORing them together. Its default value is {@link Boolean#FALSE}.
	 * @see CollectionValueModelTools#or(CollectionValueModel)
	 */
	public static final class OrTransformer
		extends TransformerAdapter<Collection<Boolean>, Boolean>
	{
		@Override
		public Boolean transform(Collection<Boolean> booleans) {
			for (Boolean b : booleans) {
				if (b.booleanValue()) {
					return Boolean.TRUE;
				}
			}
			return Boolean.FALSE;
		}
	}


	// ********** filtering wrappers **********

	/**
	 * Construct a property value model that filters the specified
	 * property value model to return the wrapped value only if it is an instance
	 * of the specified class. If the wrapped value is <em>not</em> an instance
	 * of the specified class, it will return <code>null</code>.
	 * <p>
	 * <strong>NB:</strong> If the wrapped value is <code>null</code>,
	 * the model returns a <code>null</code> value.
	 * 
	 * @param <V> the type of the model's value
	 * 
	 * @see #filter(PropertyValueModel, Class, Object)
	 * @see PredicateTools#instanceOf(Class)
	 */
	public static <V> PropertyValueModel<V> filter(PropertyValueModel<?> propertyModel, Class<V> clazz) {
		return filter(propertyModel, clazz, null);
	}

	/**
	 * Construct a property value model that filters the specified
	 * property value model to return the wrapped value only if it is an instance
	 * of the specified class. If the wrapped value is <em>not</em> an instance
	 * of the specified class, it will return the specified default value.
	 * <p>
	 * <strong>NB:</strong> If the wrapped value is <code>null</code>,
	 * the model returns a <code>null</code> value.
	 * 
	 * @param <V> the type of the model's value
	 * 
	 * @see #filter(PropertyValueModel, Class)
	 * @see PredicateTools#instanceOf(Class)
	 */
	public static <V> PropertyValueModel<V> filter(
			PropertyValueModel<?> propertyModel,
			Class<V> clazz,
			V defaultValue
	) {
		return transform(propertyModel, TransformerTools.cast(TransformerTools.filteringTransformer(PredicateTools.instanceOf(clazz), defaultValue)));
	}

	/**
	 * Construct a property value model that filters the specified
	 * property value model to return the wrapped value only if it is an instance
	 * of the specified class. If the wrapped value is <em>not</em> an instance
	 * of the specified class, it will return the specified default value.
	 * <p>
	 * <strong>NB:</strong> If the wrapped value is <code>null</code>,
	 * the model returns the specified default value.
	 * 
	 * @param <V> the type of the model's value
	 * 
	 * @see #filter(PropertyValueModel, Class)
	 * @see PredicateTools#instanceOf(Class)
	 */
	public static <V> PropertyValueModel<V> filter_(
			PropertyValueModel<?> propertyModel,
			Class<V> clazz,
			V defaultValue
	) {
		return transform_(propertyModel, TransformerTools.cast(TransformerTools.filteringTransformer(PredicateTools.instanceOf(clazz), defaultValue)));
	}

	/**
	 * Construct a property value model that wraps the specified
	 * property value model and filters its value with the specified
	 * filter. If the wrapped value passes the filter,
	 * the model simply returns it; otherwise it returns <code>null</code>.
	 * <p>
	 * <strong>NB:</strong> The specified filter will <em>never</em> be passed
	 * a <code>null</code> variable.
	 * Instead, if the wrapped value is <code>null</code>, the model returns a
	 * <code>null</code> value.
	 * 
	 * @param <V> the type of the model's value
	 * 
	 * @see #filter(PropertyValueModel, Predicate, Object)
	 * @see #filter_(PropertyValueModel, Predicate)
	 * @see PluggablePropertyValueModel
	 */
	public static <V> PropertyValueModel<V> filter(PropertyValueModel<? extends V> propertyModel, Predicate<? super V> filter) {
		return filter(propertyModel, filter, null);
	}

	/**
	 * Construct a property value model that wraps the specified
	 * property value model and filters its value with the specified
	 * filter. If the wrapped value passes the filter,
	 * the model simply returns it; otherwise it returns <code>null</code>.
	 * <p>
	 * <strong>NB:</strong> The specified filter must be able to handle
	 * a <code>null</code> variable.
	 * 
	 * @param <V> the type of the model's value
	 * 
	 * @see #filter(PropertyValueModel, Predicate)
	 * @see #filter_(PropertyValueModel, Predicate, Object)
	 * @see PluggablePropertyValueModel
	 */
	public static <V> PropertyValueModel<V> filter_(PropertyValueModel<? extends V> propertyModel, Predicate<? super V> filter) {
		return filter_(propertyModel, filter, null);
	}

	/**
	 * Construct a property value model that wraps the specified
	 * property value model and filters its value with the specified
	 * filter. If the wrapped value passes the filter,
	 * the model simply returns it; otherwise it returns the specified
	 * default value.
	 * <p>
	 * <strong>NB:</strong> The specified filter will <em>never</em> be passed
	 * a <code>null</code> variable.
	 * Instead, if the wrapped value is <code>null</code>, the model returns the specified
	 * default value.
	 * 
	 * @param <V> the type of the model's value
	 * 
	 * @see #filter(PropertyValueModel, Predicate)
	 * @see #filter_(PropertyValueModel, Predicate, Object)
	 * @see PluggablePropertyValueModel
	 */
	public static <V> PropertyValueModel<V> filter(
			PropertyValueModel<? extends V> propertyModel,
			Predicate<? super V> filter,
			V defaultValue
	) {
		return filter_(propertyModel, PredicateTools.nullCheck(filter), defaultValue);
	}

	/**
	 * Construct a property value model that wraps the specified
	 * property value model and filters its value with the specified
	 * filter. If the wrapped value passes the filter,
	 * the model simply returns it; otherwise it returns the specified
	 * default value.
	 * <p>
	 * <strong>NB:</strong> The specified filter must be able to handle
	 * a <code>null</code> variable.
	 * 
	 * @param <V> the type of the model's value
	 * 
	 * @see #filter(PropertyValueModel, Predicate, Object)
	 * @see #filter_(PropertyValueModel, Predicate)
	 * @see PluggablePropertyValueModel
	 */
	public static <V> PropertyValueModel<V> filter_(
			PropertyValueModel<? extends V> propertyModel,
			Predicate<? super V> filter,
			V defaultValue
	) {
		return transform_(propertyModel, TransformerTools.filteringTransformer(filter, defaultValue));
	}


	// ********** buffered wrapper **********

	/**
	 * Construct a trigger that can be used to accept or reset one or more
	 * buffered property value models (or any other listeners).
	 * 
	 * @see #buffer(ModifiablePropertyValueModel, BufferedPropertyValueModelAdapter.Trigger)
	 */
	public static BufferedPropertyValueModelAdapter.Trigger bufferedModelAdapterTrigger() {
		return new BufferedPropertyValueModelAdapter.Trigger();
	}

	/**
	 * Construct a property value model that wraps the specified
	 * property value model and buffers its value using the specified trigger.
	 * Return an association of the buffered model and another boolean model
	 * that indicates whether the buffered model is actually buffering a value.
	 * 
	 * @param <V> the type of the model's value
	 * 
	 * @see BufferedPropertyValueModelAdapter
	 * @see #bufferedModelAdapterTrigger()
	 * @see PluggablePropertyValueModel
	 */
	public static <V> Association<ModifiablePropertyValueModel<V>, PropertyValueModel<Boolean>> buffer(
			ModifiablePropertyValueModel<V> propertyModel,
			BufferedPropertyValueModelAdapter.Trigger trigger
	) {
		BufferedPropertyValueModelAdapter.Factory<V> factory = new BufferedPropertyValueModelAdapter.Factory<>(propertyModel, trigger);
		ModifiablePropertyValueModel<V> model = modifiableModel(factory);
		PropertyValueModel<Boolean> bufferingModel = factory.getBufferingModel();
		return new SimpleAssociation<>(model, bufferingModel);
	}


	// ********** null check wrapper **********

	/**
	 * Construct a property value model that wraps the specified
	 * property value model and returns the specified null value
	 * if the specified model's value is <code>null</code>.
	 * 
	 * @param <V> the type of the model's value
	 * 
	 * @see PluggablePropertyValueModel
	 */
	public static <V> PropertyValueModel<V> nullCheck(PropertyValueModel<? extends V> propertyModel, V nullValue) {
		return transform_(propertyModel, TransformerTools.nullCheck(nullValue));
	}


	// ********** transforming wrappers **********

	/**
	 * Construct a property value model that wraps the specified
	 * property value model and transforms its value with the specified
	 * transformer.
	 * <p>
	 * <strong>NB:</strong> The specified transformer will <em>never</em> be passed a <code>null</code> input.
	 * Instead, a <code>null</code> input will be transformed into a <code>null</code> output.
	 * 
	 * @param <V1> the type of the wrapped model's value
	 * @param <V2> the type of the returned model's value
	 * 
	 * @see PluggablePropertyValueModel
	 */
	public static <V1, V2> PropertyValueModel<V2> transform(
			PropertyValueModel<? extends V1> propertyModel,
			Transformer<? super V1, ? extends V2> transformer
	) {
		return propertyValueModel(transformationPluggableModelAdapterFactory(propertyModel, transformer));
	}

	/**
	 * Construct a property value model that wraps the specified
	 * property value model and transforms its value with the specified
	 * transformer.
	 * <p>
	 * <strong>NB:</strong> The specified transformer must be able to handle a <code>null</code> input.
	 * 
	 * @param <V1> the type of the wrapped model's value
	 * @param <V2> the type of the returned model's value
	 * 
	 * @see PluggablePropertyValueModel
	 */
	public static <V1, V2> PropertyValueModel<V2> transform_(
			PropertyValueModel<? extends V1> propertyModel,
			Transformer<? super V1, ? extends V2> transformer
	) {
		return propertyValueModel(transformationPluggableModelAdapterFactory_(propertyModel, transformer));
	}

	/**
	 * Construct a modifiable property value model that wraps the specified
	 * property value model and transforms its value with the specified
	 * transformers.
	 * <p>
	 * <strong>NB:</strong> The specified transformers will <em>never</em> be passed a <code>null</code> input.
	 * Instead, a <code>null</code> input will be transformed into a <code>null</code> output.
	 * 
	 * @param <V1> the type of the wrapped model's value
	 * @param <V2> the type of the returned model's value
	 * 
	 * @see PluggablePropertyValueModel
	 */
	public static <V1, V2> ModifiablePropertyValueModel<V2> transform(
			ModifiablePropertyValueModel<V1> propertyModel,
			Transformer<? super V1, ? extends V2> getTransformer,
			Transformer<? super V2, ? extends V1> setTransformer
	) {
		return transform_(propertyModel, TransformerTools.nullCheck(getTransformer), TransformerTools.nullCheck(setTransformer));
	}

	/**
	 * Construct a modifiable property value model that wraps the specified
	 * property value model and transforms its value with the specified
	 * transformers.
	 * <p>
	 * <strong>NB:</strong> The specified transformers must be able to handle a <code>null</code> input.
	 * 
	 * @param <V1> the type of the wrapped model's value
	 * @param <V2> the type of the returned model's value
	 * 
	 * @see PluggablePropertyValueModel
	 */
	public static <V1, V2> ModifiablePropertyValueModel<V2> transform_(
			ModifiablePropertyValueModel<V1> propertyModel,
			Transformer<? super V1, ? extends V2> getTransformer,
			Transformer<? super V2, ? extends V1> setTransformer
	) {
		return transform_(propertyModel, getTransformer, setTransformerClosureAdapter(propertyModel, setTransformer));
	}

	/**
	 * Construct a modifiable property value model that wraps the specified
	 * property value model and transforms its value with the specified
	 * transformer. The specified closure is invoked when the model's value is set.
	 * <p>
	 * <strong>NB:</strong> The specified transformer will <em>never</em> be passed a <code>null</code> input.
	 * Instead, a <code>null</code> input will be transformed into a <code>null</code> output.
	 * 
	 * @param <V1> the type of the wrapped model's value
	 * @param <V2> the type of the returned model's value
	 * 
	 * @see PluggablePropertyValueModel
	 */
	public static <V1, V2> ModifiablePropertyValueModel<V2> transform(
			PropertyValueModel<? extends V1> propertyModel,
			Transformer<? super V1, ? extends V2> transformer,
			Closure<? super V2> setValueClosure
	) {
		return pluggableModifiableModel(transformationPluggableModelAdapterFactory(propertyModel, transformer), setValueClosure);
	}

	/**
	 * Construct a modifiable property value model that wraps the specified
	 * property value model and transforms its value with the specified
	 * transformer. The specified closure is invoked when the model's value is set.
	 * <p>
	 * <strong>NB:</strong> The specified transformer must be able to handle a <code>null</code> input.
	 * 
	 * @param <V1> the type of the wrapped model's value
	 * @param <V2> the type of the returned model's value
	 * 
	 * @see PluggablePropertyValueModel
	 */
	public static <V1, V2> ModifiablePropertyValueModel<V2> transform_(
			PropertyValueModel<? extends V1> propertyModel,
			Transformer<? super V1, ? extends V2> transformer,
			Closure<? super V2> setValueClosure
	) {
		return pluggableModifiableModel(transformationPluggableModelAdapterFactory_(propertyModel, transformer), setValueClosure);
	}

	/**
	 * Construct a pluggable property value model adapter factory for the specified
	 * property value model and transformer.
	 * <p>
	 * <strong>NB:</strong> The specified transformer will <em>never</em> be passed a <code>null</code> input.
	 * Instead, a <code>null</code> input will be transformed into a <code>null</code> output.
	 * 
	 * @param <V1> the type of the wrapped model's value
	 * @param <V2> the type of the returned model's value
	 * 
	 * @see PluggablePropertyValueModel
	 */
	public static <V1, V2> PluggablePropertyValueModel.Adapter.Factory<V2> transformationPluggableModelAdapterFactory(
			PropertyValueModel<? extends V1> propertyModel,
			Transformer<? super V1, ? extends V2> transformer
	) {
		return transformationPluggableModelAdapterFactory_(propertyModel, TransformerTools.nullCheck(transformer));
	}

	/**
	 * Construct a pluggable property value model adapter factory for the specified
	 * property value model and transformer.
	 * <p>
	 * <strong>NB:</strong> The specified transformer must be able to handle a <code>null</code> input.
	 * 
	 * @param <V1> the type of the wrapped model's value
	 * @param <V2> the type of the returned model's value
	 * 
	 * @see PluggablePropertyValueModel
	 */
	public static <V1, V2> PluggablePropertyValueModel.Adapter.Factory<V2> transformationPluggableModelAdapterFactory_(
			PropertyValueModel<? extends V1> propertyModel,
			Transformer<? super V1, ? extends V2> transformer
	) {
		return new TransformationPluggablePropertyValueModelAdapter.Factory<>(propertyModel, transformer);
	}


	// ********** misc **********

	/**
	 * Construct a "set" closure that wraps the specified modifiable property value model and
	 * uses the specified "set" transformer to transform the incoming value before
	 * forwarding it to the model.
	 * 
	 * @param <V1> the type of the wrapped model's value
	 * @param <V2> the type of the "set" closure's model's value
	 */
	public static <V1, V2> Closure<V2> setTransformerClosureAdapter(ModifiablePropertyValueModel<V1> propertyModel, Transformer<? super V2, ? extends V1> setTransformer) {
		return new SetTransformerClosureAdapter<>(propertyModel, setTransformer);
	}

	/**
	 * "Set" closure that wraps a modifiable property value model and
	 * uses a "set" transformer to transform the incoming value before
	 * forwarding it to the model.
	 * 
	 * @param <V1> the type of the wrapped model's value
	 * @param <V2> the type of the "set" closure's model's value
	 */
	public static final class SetTransformerClosureAdapter<V1, V2>
		implements Closure<V2>
	{
		private final ModifiablePropertyValueModel<V1> propertyModel;
		private final Transformer<? super V2, ? extends V1> setTransformer;
		
		public SetTransformerClosureAdapter(ModifiablePropertyValueModel<V1> propertyModel, Transformer<? super V2, ? extends V1> setTransformer) {
			super();
			if (propertyModel == null) {
				throw new NullPointerException();
			}
			this.propertyModel = propertyModel;
			if (setTransformer == null) {
				throw new NullPointerException();
			}
			this.setTransformer = setTransformer;
		}

		public void execute(V2 value) {
			this.propertyModel.setValue(this.setTransformer.transform(value));
		}

		@Override
		public String toString() {
			return ObjectTools.toString(this, this.setTransformer);
		}
	}

	/**
	 * Construct a "set" closure that wraps another "set" closure and
	 * provides a hook to do nothing if the target object is <code>null</code>.
	 * 
	 * @param <A1> the type of the "set" closure's "target" object
	 * @param <A2> the type of the "set" closure's argument
	 */
	public static <A1, A2> BiClosure<A1, A2> nullCheckSetClosureWrapper(BiClosure<? super A1, ? super A2> closure) {
		return nullCheckSetClosureWrapper(closure, ClosureTools.nullClosure());
	}

	/**
	 * Construct a "set" closure that wraps another "set" closure and
	 * provides a hook to execute a different closure
	 * if the target object is <code>null</code>.
	 * 
	 * @param <A1> the type of the "set" closure's "target" object
	 * @param <A2> the type of the "set" closure's argument
	 */
	public static <A1, A2> BiClosure<A1, A2> nullCheckSetClosureWrapper(BiClosure<? super A1, ? super A2> closure, Closure<? super A2> nullTargetSetClosure) {
		return new NullCheckSetClosureWrapper<>(closure, nullTargetSetClosure);
	}

	/**
	 * "Set" closure that wraps another "set" closure and
	 * provides a hook to execute a different closure
	 * if the target object is <code>null</code>.
	 * 
	 * @param <A1> the type of the "set" closure's "target" object
	 * @param <A2> the type of the "set" closure's argument
	 */
	public static final class NullCheckSetClosureWrapper<A1, A2>
		implements BiClosure<A1, A2>
	{
		private final BiClosure<? super A1, ? super A2> closure;
		private final Closure<? super A2> nullTargetClosure;

		public NullCheckSetClosureWrapper(BiClosure<? super A1, ? super A2> closure, Closure<? super A2> nullTargetClosure) {
			super();
			if (closure == null) {
				throw new NullPointerException();
			}
			this.closure = closure;
			if (nullTargetClosure == null) {
				throw new NullPointerException();
			}
			this.nullTargetClosure = nullTargetClosure;
		}

		public void execute(A1 argument1, A2 argument2) {
			if (argument1 != null) {
				this.closure.execute(argument1, argument2);
			} else {
				this.nullTargetClosure.execute(argument2);
			}
		}

		@Override
		public String toString() {
			return ObjectTools.toString(this, this.closure);
		}
	}

	/**
	 * Construct a "set" bi-closure that wraps another "set" closure and
	 * forwards only the second argument to it.
	 * 
	 * @param <A1> the type of the "set" closure's "target" object
	 * @param <A2> the type of the "set" closure's argument
	 */
	public static <A1, A2> BiClosure<A1, A2> setClosureAdapter(Closure<? super A2> closure) {
		return new SetClosureAdapter<>(closure);
	}

	/**
	 * "Set" bi-closure that wraps another "set" closure and
	 * forwards only the second argument to it.
	 * 
	 * @param <A1> the type of the "set" closure's "target" object
	 * @param <A2> the type of the "set" closure's argument
	 */
	public static final class SetClosureAdapter<A1, A2>
		implements BiClosure<A1, A2>
	{
		private final Closure<? super A2> closure;
		
		public SetClosureAdapter(Closure<? super A2> closure) {
			super();
			if (closure == null) {
				throw new NullPointerException();
			}
			this.closure = closure;
		}

		public void execute(A1 argument1, A2 argument2) {
			this.closure.execute(argument2);
		}

		@Override
		public String toString() {
			return ObjectTools.toString(this, this.closure);
		}
	}


	// ********** compound PVMs **********

	/**
	 * Construct a compound property value model for the
	 * specified <em>outer</em> property value model.
	 * <p>
	 * <strong>NB:</strong>
	 * If the <em>outer</em> model's value is ever <code>null</code>,
	 * the model's value will also be <code>null</code>.
	 * 
	 * @param <V> the type of the model's value
	 */
	public static <V> PropertyValueModel<V> compound(PropertyValueModel<? extends PropertyValueModel<? extends V>> outerModel) {
		return propertyValueModel(compoundModelAdapterFactory(outerModel));
	}

	/**
	 * Construct a compound property value model for the
	 * specified <em>outer</em> property value model.
	 * <p>
	 * <strong>NB:</strong>
	 * If the <em>outer</em> model's value is ever <code>null</code>,
	 * the model will throw a {@link NullPointerException}.
	 * 
	 * @param <V> the type of the model's value
	 */
	public static <V> PropertyValueModel<V> compound_(PropertyValueModel<? extends PropertyValueModel<? extends V>> outerModel) {
		return propertyValueModel(compoundModelAdapterFactory_(outerModel));
	}

	/**
	 * Construct a compound property value model adapter factory for
	 * the specified <em>outer</em> property value model.
	 * <p>
	 * <strong>NB:</strong>
	 * If the <em>outer</em> model's value is ever <code>null</code>,
	 * the factory's model's value will also be <code>null</code>.
	 * 
	 * @param <V> the type of the model's value
	 * @param <IM> the type of the "inner" model
	 * @param <OM> the type of the "outer" model
	 */
	public static <V, IM extends PropertyValueModel<? extends V>, OM extends PropertyValueModel<? extends IM>> PluggablePropertyAspectAdapter.Factory<V, IM, OM> compoundModelAdapterFactory(OM outerModel) {
		return modelAspectAdapterFactory_(
				outerModel,
				PropertyValueModel.VALUE,
				valueTransformer()
			);
	}

	/**
	 * Construct a compound property value model adapter factory for
	 * the specified <em>outer</em> property value model.
	 * <p>
	 * <strong>NB:</strong>
	 * If the <em>outer</em> model's value is ever <code>null</code>,
	 * the factory's model will throw a {@link NullPointerException}.
	 * 
	 * @param <V> the type of the model's value
	 * @param <IM> the type of the "inner" model
	 * @param <OM> the type of the "outer" model
	 */
	public static <V, IM extends PropertyValueModel<? extends V>, OM extends PropertyValueModel<? extends IM>> PluggablePropertyAspectAdapter.Factory<V, IM, OM> compoundModelAdapterFactory_(OM outerModel) {
		return modelAspectAdapterFactory_(
				outerModel,
				PropertyValueModel.VALUE,
				valueTransformer_()
			);
	}

	/**
	 * Construct a modifiable compound property value model
	 * for the specified <em>outer</em> property value model.
	 * <p>
	 * <strong>NB:</strong>
	 * If the <em>outer</em> model's value is ever <code>null</code>,
	 * the model's value will also be <code>null</code>
	 * and, if the model's value is set, it will do nothing.
	 * 
	 * @param <V> the type of the model's value
	 */
	public static <V> ModifiablePropertyValueModel<V> compoundModifiable(PropertyValueModel<? extends ModifiablePropertyValueModel<V>> outerModel) {
		return modifiableModel(compoundModifiableModelAdapterFactory(outerModel));
	}

	/**
	 * Construct a modifiable compound property value model
	 * for the specified <em>outer</em> property value model.
	 * <p>
	 * <strong>NB:</strong>
	 * If the <em>outer</em> model's value is ever <code>null</code>,
	 * the model will throw a {@link NullPointerException}
	 * when either its value is get or set.
	 * 
	 * @param <V> the type of the model's value
	 */
	public static <V> ModifiablePropertyValueModel<V> compoundModifiable_(PropertyValueModel<? extends ModifiablePropertyValueModel<V>> outerModel) {
		return modifiableModel(compoundModifiableModelAdapterFactory_(outerModel));
	}

	/**
	 * Construct a modifiable compound property value model adapter factory
	 * for the specified <em>outer</em> property value model.
	 * <p>
	 * <strong>NB:</strong>
	 * If the <em>outer</em> model's value is ever <code>null</code>,
	 * the model's value will also be <code>null</code>
	 * and, if the model's value is set, it will do nothing.
	 * 
	 * @param <V> the type of the model's value
	 */
	public static <V> PluggableModifiablePropertyValueModel.Adapter.Factory<V> compoundModifiableModelAdapterFactory(PropertyValueModel<? extends ModifiablePropertyValueModel<V>> outerModel) {
		return modifiablePropertyAspectAdapterFactory_(
				compoundModelAdapterFactory(outerModel),
				setValueClosure()
			);
	}

	/**
	 * Construct a modifiable compound property value model adapter factory
	 * for the specified <em>outer</em> property value model.
	 * <p>
	 * <strong>NB:</strong>
	 * If the <em>outer</em> model's value is ever <code>null</code>,
	 * the factory's model will throw a {@link NullPointerException}
	 * when either its value is get or set.
	 * 
	 * @param <V> the type of the model's value
	 */
	public static <V> PluggableModifiablePropertyValueModel.Adapter.Factory<V> compoundModifiableModelAdapterFactory_(PropertyValueModel<? extends ModifiablePropertyValueModel<V>> outerModel) {
		return modifiablePropertyAspectAdapterFactory_(
				compoundModelAdapterFactory_(outerModel),
				setValueClosure_()
			);
	}


	// ********** aspect adapters **********

	/**
	 * Construct a model property aspect adapter for the
	 * specified subject, aspect name, and transformer.
	 * <p>
	 * <strong>NB:</strong>
	 * The specified transformer will <em>never</em> be passed a <code>null</code> subject.
	 * Instead, a <code>null</code> subject will be transformed into a <code>null</code> value.
	 * 
	 * @param <V> the type of the model's value
	 * @param <S> the type of the subject
	 */
	public static <V, S extends Model> PropertyValueModel<V> modelAspectAdapter(
			S subject,
			String aspectName,
			Transformer<? super S, ? extends V> transformer
	) {
		return modelAspectAdapter(staticModel(subject), aspectName, transformer);
	}

	/**
	 * Construct a model property aspect adapter for the
	 * specified subject, aspect name, and transformer.
	 * <p>
	 * <strong>NB:</strong>
	 * The specified transformer must be able to handle a <code>null</code> subject.
	 * 
	 * @param <V> the type of the model's value
	 * @param <S> the type of the subject
	 */
	public static <V, S extends Model> PropertyValueModel<V> modelAspectAdapter_(
			S subject,
			String aspectName,
			Transformer<? super S, ? extends V> transformer
	) {
		return modelAspectAdapter_(staticModel(subject), aspectName, transformer);
	}

	/**
	 * Construct a model property aspect adapter for the
	 * specified subject model, aspect name, and transformer.
	 * <p>
	 * <strong>NB:</strong>
	 * The specified transformer will <em>never</em> be passed a <code>null</code> subject.
	 * Instead, a <code>null</code> subject will be transformed into a <code>null</code> value.
	 * 
	 * @param <V> the type of the model's value
	 * @param <S> the type of the subject
	 * @param <SM> the type of the subject model
	 */
	public static <V, S extends Model, SM extends PropertyValueModel<? extends S>> PropertyValueModel<V> modelAspectAdapter(
			SM subjectModel,
			String aspectName,
			Transformer<? super S, ? extends V> transformer
	) {
		return propertyValueModel(modelAspectAdapterFactory(subjectModel, aspectName, transformer));
	}

	/**
	 * Construct a model property aspect adapter for the
	 * specified subject model, aspect name, and transformer.
	 * <p>
	 * <strong>NB:</strong>
	 * The specified transformer must be able to handle a <code>null</code> subject.
	 * 
	 * @param <V> the type of the model's value
	 * @param <S> the type of the subject
	 * @param <SM> the type of the subject model
	 */
	public static <V, S extends Model, SM extends PropertyValueModel<? extends S>> PropertyValueModel<V> modelAspectAdapter_(
			SM subjectModel,
			String aspectName,
			Transformer<? super S, ? extends V> transformer
	) {
		return propertyValueModel(modelAspectAdapterFactory_(subjectModel, aspectName, transformer));
	}

	/**
	 * Construct a model property aspect adapter factory for the
	 * specified subject model, aspect name, and transformer.
	 * <p>
	 * <strong>NB:</strong>
	 * The specified transformer will <em>never</em> be passed a <code>null</code> subject.
	 * Instead, a <code>null</code> subject will be transformed into a <code>null</code> value.
	 * 
	 * @param <V> the type of the model's value
	 * @param <S> the type of the subject
	 * @param <SM> the type of the subject model
	 */
	public static <V, S extends Model, SM extends PropertyValueModel<? extends S>> PluggablePropertyAspectAdapter.Factory<V, S, SM> modelAspectAdapterFactory(
			SM subjectModel,
			String aspectName,
			Transformer<? super S, ? extends V> transformer
	) {
		return modelAspectAdapterFactory_(subjectModel, aspectName, TransformerTools.nullCheck(transformer));
	}

	/**
	 * Construct a model property aspect adapter factory for the
	 * specified subject model, aspect name, and transformer.
	 * <p>
	 * <strong>NB:</strong>
	 * The specified transformer must be able to handle a <code>null</code> subject.
	 * 
	 * @param <V> the type of the model's value
	 * @param <S> the type of the subject
	 * @param <SM> the type of the subject model
	 */
	public static <V, S extends Model, SM extends PropertyValueModel<? extends S>> PluggablePropertyAspectAdapter.Factory<V, S, SM> modelAspectAdapterFactory_(
			SM subjectModel,
			String aspectName,
			Transformer<? super S, ? extends V> transformer
	) {
		return pluggableAspectAdapterFactory(subjectModel, new ModelPropertyAspectAdapter.Factory<>(aspectName, transformer));
	}

	/**
	 * Construct a property aspect adapter for the
	 * specified subject model and subject adapter factory.
	 * 
	 * @param <V> the type of the model's value
	 * @param <S> the type of the subject
	 * @param <SM> the type of the subject model
	 */
	public static <V, S, SM extends PropertyValueModel<? extends S>> PropertyValueModel<V> aspectAdapter(
			SM subjectModel,
			PluggablePropertyAspectAdapter.SubjectAdapter.Factory<V, S> subjectAdapterFactory
	) {
		return propertyValueModel(pluggableAspectAdapterFactory(subjectModel, subjectAdapterFactory));
	}

	/**
	 * Construct a property aspect adapter factory for the
	 * specified subject model and subject adapter factory.
	 * 
	 * @param <V> the type of the model's value
	 * @param <S> the type of the subject
	 * @param <SM> the type of the subject model
	 */
	public static <V, S, SM extends PropertyValueModel<? extends S>> PluggablePropertyAspectAdapter.Factory<V, S, SM> pluggableAspectAdapterFactory(
			SM subjectModel,
			PluggablePropertyAspectAdapter.SubjectAdapter.Factory<V, S> subjectAdapterFactory
	) {
		return new PluggablePropertyAspectAdapter.Factory<>(subjectModel, subjectAdapterFactory);
	}


	// ********** modifiable aspect adapters **********

	/**
	 * Construct a modifiable property aspect adapter for the
	 * specified subject model, aspect name, transformer, and closure.
	 * <p>
	 * <strong>NB:</strong>
	 * The specified transformer will <em>never</em> be passed a <code>null</code> subject.
	 * Instead, a <code>null</code> subject will be transformed into a <code>null</code> value.
	 * Likewise, if the subject is <code>null</code>, the specified closure will
	 * not be executed.
	 * 
	 * @param <V> the type of the model's value
	 * @param <S> the type of the subject
	 * @param <SM> the type of the subject model
	 */
	public static <V, S extends Model, SM extends PropertyValueModel<S>> ModifiablePropertyValueModel<V> modifiablePropertyAspectAdapter(
			SM subjectModel,
			String aspectName,
			Transformer<? super S, ? extends V> getTransformer,
			BiClosure<? super S, ? super V> setClosure
	) {
		return modifiableModel(modifiablePropertyAspectAdapterFactory(subjectModel, aspectName, getTransformer, setClosure));
	}

	/**
	 * Construct a modifiable property aspect adapter for the
	 * specified subject model, aspect name, transformer, and closure.
	 * <p>
	 * <strong>NB:</strong>
	 * The specified transformer must be able to handle a <code>null</code> subject.
	 * Likewise, the specified closure must be able to handle a <code>null</code>
	 * subject (i.e. first argument).
	 * 
	 * @param <V> the type of the model's value
	 * @param <S> the type of the subject
	 * @param <SM> the type of the subject model
	 */
	public static <V, S extends Model, SM extends PropertyValueModel<S>> ModifiablePropertyValueModel<V> modifiablePropertyAspectAdapter_(
			SM subjectModel,
			String aspectName,
			Transformer<? super S, ? extends V> getTransformer,
			BiClosure<? super S, ? super V> setClosure
	) {
		return modifiableModel(modifiablePropertyAspectAdapterFactory_(subjectModel, aspectName, getTransformer, setClosure));
	}

	/**
	 * Construct a modifiable property aspect adapter factory for the
	 * specified subject model, aspect name, transformer, and closure.
	 * <p>
	 * <strong>NB:</strong>
	 * The specified transformer will <em>never</em> be passed a <code>null</code> subject.
	 * Instead, a <code>null</code> subject will be transformed into a <code>null</code> value.
	 * Likewise, if the subject is <code>null</code>, the specified closure will
	 * not be executed.
	 * 
	 * @param <V> the type of the model's value
	 * @param <S> the type of the subject
	 * @param <SM> the type of the subject model
	 */
	public static <V, S extends Model, SM extends PropertyValueModel<S>> PluggableModifiablePropertyValueModel.Adapter.Factory<V> modifiablePropertyAspectAdapterFactory(
			SM subjectModel,
			String aspectName,
			Transformer<? super S, ? extends V> getTransformer,
			BiClosure<? super S, ? super V> setClosure
	) {
		return modifiablePropertyAspectAdapterFactory(modelAspectAdapterFactory(subjectModel, aspectName, getTransformer), setClosure);
	}

	/**
	 * Construct a modifiable property aspect adapter factory for the
	 * specified subject model, aspect name, transformer, and closure.
	 * <p>
	 * <strong>NB:</strong>
	 * The specified transformer must be able to handle a <code>null</code> subject.
	 * Likewise, the specified closure must be able to handle a <code>null</code>
	 * subject (i.e. first argument).
	 * 
	 * @param <V> the type of the model's value
	 * @param <S> the type of the subject
	 * @param <SM> the type of the subject model
	 */
	public static <V, S extends Model, SM extends PropertyValueModel<S>> PluggableModifiablePropertyValueModel.Adapter.Factory<V> modifiablePropertyAspectAdapterFactory_(
			SM subjectModel,
			String aspectName,
			Transformer<? super S, ? extends V> getTransformer,
			BiClosure<? super S, ? super V> setClosure
	) {
		return modifiablePropertyAspectAdapterFactory_(modelAspectAdapterFactory_(subjectModel, aspectName, getTransformer), setClosure);
	}

	/**
	 * Construct a modifiable property aspect adapter factory for the
	 * specified factory and closure.
	 * <p>
	 * <strong>NB:</strong>
	 * If the subject is <code>null</code>, the specified closure will
	 * not be executed.
	 * 
	 * @param <V> the type of the model's value
	 * @param <S> the type of the subject
	 */
	public static <V, S> PluggableModifiablePropertyValueModel.Adapter.Factory<V> modifiablePropertyAspectAdapterFactory(
			ModifiablePropertyAspectAdapter.GetAdapter.Factory<V, S> getAdapterFactory,
			BiClosure<? super S, ? super V> setClosure
	) {
		return modifiablePropertyAspectAdapterFactory_(getAdapterFactory, nullCheckSetClosureWrapper(setClosure));
	}

	/**
	 * Construct a modifiable property aspect adapter factory for the
	 * specified factory and closure.
	 * <p>
	 * <strong>NB:</strong>
	 * The specified closure must be able to handle a <code>null</code>
	 * subject (i.e. first argument).
	 * 
	 * @param <V> the type of the model's value
	 * @param <S> the type of the subject
	 */
	public static <V, S> PluggableModifiablePropertyValueModel.Adapter.Factory<V> modifiablePropertyAspectAdapterFactory_(
			ModifiablePropertyAspectAdapter.GetAdapter.Factory<V, S> getAdapterFactory,
			BiClosure<? super S, ? super V> setClosure
	) {
		return new ModifiablePropertyAspectAdapter.Factory<>(getAdapterFactory, setClosure);
	}


	// ********** pluggable PVMs **********

	/**
	 * Construct a property value model adapter for the specified adapter factory.
	 * 
	 * @param <V> the type of the model's value
	 * 
	 * @see PluggablePropertyValueModel
	 */
	public static <V> PropertyValueModel<V> propertyValueModel(PluggablePropertyValueModel.Adapter.Factory<V> adapterFactory) {
		return new PluggablePropertyValueModel<>(adapterFactory);
	}

	/**
	 * Construct a <em>modifiable</em> property value model adapter for the specified
	 * property value model adapter adapter factory and closure.
	 * The specified closure is invoked when the model's value is set.
	 * 
	 * @param <V> the type of the model's value
	 * 
	 * @see PluggableModifiablePropertyValueModel
	 */
	public static <V> ModifiablePropertyValueModel<V> pluggableModifiableModel(BasePluggablePropertyValueModel.Adapter.Factory<V, ? extends BasePluggablePropertyValueModel.Adapter<V>> factory, Closure<? super V> setValueClosure) {
		return modifiableModel(new PluggableModifiablePropertyValueModelAdapter.Factory<>(factory, setValueClosure));
	}

	/**
	 * Construct a modifiable property value model adapter for the specified adapter factory.
	 * 
	 * @param <V> the type of the model's value
	 * 
	 * @see PluggableModifiablePropertyValueModel
	 */
	public static <V> ModifiablePropertyValueModel<V> modifiableModel(PluggableModifiablePropertyValueModel.Adapter.Factory<V> adapterFactory) {
		return new PluggableModifiablePropertyValueModel<>(adapterFactory);
	}


	// ********** factories **********

	/**
	 * Construct property value model that can be used for
	 * returning a static value, but still allows listeners to be added.
	 * Listeners will <em>never</em> be notified of any changes, because there should be none.
	 * 
	 * @param <V> the type of the model's value
	 * 
	 * @see #simpleModel(Object)
	 */
	public static <V> PropertyValueModel<V> staticModel(V value) {
		return new StaticPropertyValueModel<>(value);
	}

	/**
	 * Construct a simple property value model that has the specified value.
	 * 
	 * @param <V> the type of the model's value
	 * 
	 * @see #staticModel(Object)
	 */
	public static <V> ModifiablePropertyValueModel<V> simpleModel(V value) {
		return new SimplePropertyValueModel<>(value);
	}


	// ********** value transformers/closures **********

	/**
	 * Return a transformer that converts a property value model to its value
	 * but first checks whether the property value model passed to it is <code>null</code>.
	 * <p>
	 * <strong>NB:</strong>
	 * If the property value model is <code>null</code>, the transformer returns
	 * <code>null</code>.
	 * 
	 * @param <V> the type of the model's value
	 * @param <PVM> the type of the model
	 * 
	 * @see #valueTransformer(Object)
	 * @see #valueTransformer_()
	 * @see PropertyValueModel#VALUE_TRANSFORMER
	 */
	@SuppressWarnings("unchecked")
	public static <V, PVM extends PropertyValueModel<? extends V>> Transformer<PVM, V> valueTransformer() {
		return NULL_CHECK_VALUE_TRANSFORMER;
	}

	@SuppressWarnings("rawtypes")
	public static final Transformer NULL_CHECK_VALUE_TRANSFORMER = TransformerTools.nullCheck(valueTransformer_(), null);

	/**
	 * Return a transformer that converts a property value model to its value
	 * but first checks whether the property value model passed to it is <code>null</code>.
	 * <p>
	 * <strong>NB:</strong>
	 * If the property value model is <code>null</code>, the transformer returns
	 * the specified null value.
	 * 
	 * @param <V> the type of the model's value
	 * @param <PVM> the type of the model
	 * 
	 * @see #valueTransformer()
	 * @see #valueTransformer_()
	 * @see PropertyValueModel#VALUE_TRANSFORMER
	 */
	public static <V, PVM extends PropertyValueModel<? extends V>> Transformer<PVM, V> valueTransformer(V nullValue) {
		return TransformerTools.nullCheck(valueTransformer_(), nullValue);
	}

	/**
	 * Return a transformer that converts a property value model to its value.
	 * <p>
	 * <strong>NB:</strong>
	 * If the property value model is <code>null</code>, the transformer throws
	 * a {@link NullPointerException}.
	 * 
	 * @param <V> the type of the model's value
	 * @param <PVM> the type of the model
	 * 
	 * @see #valueTransformer(Object)
	 * @see #valueTransformer()
	 * @see PropertyValueModel#VALUE_TRANSFORMER
	 */
	@SuppressWarnings("unchecked")
	public static <V, PVM extends PropertyValueModel<? extends V>> Transformer<PVM, V> valueTransformer_() {
		return PropertyValueModel.VALUE_TRANSFORMER;
	}

	/**
	 * Return a closure that sets a property value model's value.
	 * <p>
	 * <strong>NB:</strong>
	 * If the property value model is <code>null</code>, the closure
	 * does nothing.
	 * 
	 * @param <V> the type of the model's value
	 * @param <PVM> the type of the model
	 * 
	 * @see #setValueClosure_()
	 * @see PropertyValueModel#VALUE_TRANSFORMER
	 */
	@SuppressWarnings("unchecked")
	public static <V, PVM extends ModifiablePropertyValueModel<? super V>> BiClosure<PVM, V> setValueClosure() {
		return NULL_CHECK_SET_VALUE_CLOSURE;
	}

	@SuppressWarnings("rawtypes")
	public static final BiClosure NULL_CHECK_SET_VALUE_CLOSURE = nullCheckSetClosureWrapper(setValueClosure_());

	/**
	 * Return a closure that sets a property value model's value.
	 * <p>
	 * <strong>NB:</strong>
	 * If the property value model is <code>null</code>, the closure throws
	 * a {@link NullPointerException}.
	 * 
	 * @param <V> the type of the model's value
	 * @param <PVM> the type of the model
	 * 
	 * @see #setValueClosure()
	 * @see PropertyValueModel#VALUE_TRANSFORMER
	 */
	@SuppressWarnings("unchecked")
	public static <V, PVM extends ModifiablePropertyValueModel<? super V>> BiClosure<PVM, V> setValueClosure_() {
		return ModifiablePropertyValueModel.SET_VALUE_CLOSURE;
	}


	// ********** suppressed constructor **********

	/**
	 * Suppress default constructor, ensuring non-instantiability.
	 */
	private PropertyValueModelTools() {
		super();
		throw new UnsupportedOperationException();
	}
}

Back to the top