# Tuesday, 02 February 2010

Every once in awhile I run into a requirement when using Apex where I think there must be a real obvious way to do something, only to hit a wall and decide to develop the functionality myself.

When this happens, the result either ends up being:
a) I later discover there actually is a faster/better way to do something and I feel stupid for taking the NIH path (Not Invented Here).
b) The functionality really is missing and the effort invested turns out to be well spent.

Let's just hope the time spent on this wrapper class is in the latter category :-)

In this use case, a custom database object is wrapped in an Apex class that abstracts the underlying object properties and provides some methods. One of the properties (a database custom field) is a multipicklist that is stored in semicolon delimited format, but is better represented in object-oriented terms as a List. Methods for Adding and Removing items from the list are also needed.

public class Foo{
	private Foo__c m_record = null;
	public Foo(Foo__c record){
		m_record = record;
	}

	private MultiSelectProperty m_categories = new MultiSelectProperty();
	public List<string> Categories{
		get{return m_categories.ToList(m_record.Category__c);}
	}
	public void AddCategory(string category){
		m_record.Category__c = m_categories.Add(m_record.Category__c, category);
	}
	public void RemoveCategory(string category){
		m_record.Category__c = m_categories.Remove(m_record.Category__c, category);
	}
}

The MultiSelectProperty class is defined here. The unit tests should help tell the story on how it can be used and the assumptions it makes.

//Manages semicolon-delimited multipicklist data. 
global class MultiSelectProperty {
	
	public List<string> ToList(string storedValue){
		List<string> values = new List<string>();
		if(storedValue == null || storedValue == '')
			return values;
		else{			
			string[] splitValues = storedValue.split(';');
			for(string v : splitValues){
				if(v != null && v != '')
					values.add(v);
			}
			return values;
		}
	}
	
	public boolean Contains(string field, string value){
		List<string> values = this.ToList(field);
		for(string v : values){
			if(v==value){
				return true;
			}
		}
		return false;
	}
	
	public string Add(string field, string value){
		if(field == null)
			field = '';
		
		if(value == null || value == '')
			return '';
		
		if(this.Contains(field, value))
			return field;
		
		if(field.endsWith(';')){
			return field + value;
		}
		else
			return field + ';' + value;
	}
	
	public string Remove(string field, string value){
		if(field == null || field == '')
			return '';
		
		if(value == null || value == '')
			return field;
		
		if(!this.Contains(field, value))
			return field;
		
		List<string> values = this.ToList(field);
		string formattedFields = '';
		for(string v : values){
			if(v == value)
				continue;
			formattedFields += v + ';'; 
		}
		return formattedFields;		
	}
	
	@IsTest public static void tests(){
		final string SINGLE_VALUE_NULL = null;
		final string SINGLE_VALUE_EMPTY = '';
		final string SINGLE_VALUE = 'first option;';
		final string SINGLE_VALUE_EMPTY_TRAIL = 'first option';
		final string DOUBLE_VALUE = 'first option;second option;';
		final string TRIPLE_VALUE = 'first option;second option;third option;';
		
		MultiSelectProperty categories = new MultiSelectProperty();
		System.assert(categories.ToList(SINGLE_VALUE_NULL).size() == 0);
		System.assert(categories.ToList(SINGLE_VALUE_EMPTY).size() == 0);
		System.assert(categories.ToList(SINGLE_VALUE).size() == 1);
		System.assert(categories.ToList(SINGLE_VALUE_EMPTY_TRAIL).size() == 1);
		System.assert(categories.ToList(DOUBLE_VALUE).size() == 2);
		System.assert(categories.ToList(TRIPLE_VALUE).size() == 3);
		
		System.assert(categories.Contains(SINGLE_VALUE, 'first option') == true);
		System.assert(categories.Contains(SINGLE_VALUE, 'second option') == false);
		System.assert(categories.Contains(DOUBLE_VALUE, 'second option') == true);
		System.assert(categories.Contains(DOUBLE_VALUE, 'third option') == false);
		System.assert(categories.Contains(TRIPLE_VALUE, 'first option') == true);
		System.assert(categories.Contains(TRIPLE_VALUE, 'second option') == true);
		System.assert(categories.Contains(TRIPLE_VALUE, 'third option') == true);
		
		string fields = categories.Add(SINGLE_VALUE, null);	
		System.assert(categories.ToList(fields).size() == 0);
		fields = categories.Add(null, null);
		System.assert(categories.ToList(fields).size() == 0);
		fields = categories.Add(null, '');
		System.assert(categories.ToList(fields).size() == 0);
		fields = categories.Add(SINGLE_VALUE, '');
		System.assert(categories.ToList(fields).size() == 0);
		
		fields = categories.Add(SINGLE_VALUE, 'second option');
		System.assert(categories.ToList(fields).size() == 2);
		System.assert(categories.Contains(fields, 'second option') == true);
		
		fields = categories.Add(SINGLE_VALUE_EMPTY_TRAIL, 'second option');
		System.assert(categories.ToList(fields).size() == 2);
		System.assert(categories.Contains(fields, 'second option') == true);
		
		fields = categories.Add(DOUBLE_VALUE, 'second option');
		System.assert(categories.ToList(fields).size() == 2);
		System.assert(categories.Contains(fields, 'second option') == true);
		
		fields = categories.Remove(null, '');
		System.assert(categories.ToList(fields).size() == 0);
		
		fields = categories.Remove(null, null);
		System.assert(categories.ToList(fields).size() == 0);
		
		fields = categories.Remove(SINGLE_VALUE, '');		
		System.assert(categories.ToList(fields).size() == 1);
		
		fields = categories.Remove(SINGLE_VALUE, null);		
		System.assert(categories.ToList(fields).size() == 1);
		
		fields = categories.Remove(SINGLE_VALUE, 'second option');
		System.assert(categories.ToList(fields).size() == 1);
		System.assert(categories.Contains(fields, 'second option') == false);
		
		fields = categories.Remove(SINGLE_VALUE, 'first option');
		System.assert(categories.ToList(fields).size() == 0);
		System.assert(categories.Contains(fields, 'first option') == false);
		
		fields = categories.Remove(TRIPLE_VALUE, 'first option');
		System.debug('results from remove ' + fields);
		System.assert(categories.ToList(fields).size() == 2);
		System.assert(categories.Contains(fields, 'first option') == false);
		System.assert(categories.Contains(fields, 'second option') == true);
		System.assert(categories.Contains(fields, 'third option') == true);
	}
}