[Index]

Creating you own PropertyType


Rules

You can always make a new Property Type, the RULES are:
  1. A new property must extends dk.heick.properties.PropertyType.
  2. Make a short type name
  3. Implements getRestrictions(). Make a list of descriptions, which describe type and any constructor options and make use of PropertyType.getDefaultValueDescription.
  4. Make a constructor with no arguments (if possible) where null is the default value. You should have a good reason not to.
  5. If your PropertyType should implement one or more of the marker interfaces.
    • public MyNewPropertyType() { 
        super(TYPE,null); 
      }
      
    • public MyNewPropertyType(T defaultValue) { 
        super(TYPE,defaultValue); 
      }
      
  6. Implement validateValue(name,value) method. (Changed from version 1.6)
  7. Override validateConstraints(name,value) method. (Changed from version 1.6) if needed where you validate the constraints your PropertyType might have, example min and max limits, where you validate that min isnt greater than max.
  8. Override, if necessary, toString(String name, String value) to ensure that you can associative rule is upheld toString(name,validate(name,value))==value.
    Important: call validate(String name,String value) with your converted String value, to ensure that it is valid, make sure that you dont use the same method when you generate error message is in the validate method.
  9. Remember if validate(name,value) fails with a PropertyException the method toType(name,value) shall return null.
  10. It is possible to override the priority in witch the type is initalized, by setting the "setInitializationPriority(int)" value default is "99". The highest is intialized first.
  11. To avoid constructor bloating divide your property constraints into three parts.
  12. Whenever a method use the "String name" consider if it should be "String propertyName" instead, so it is consistent
    • A. Required constructor parameters, avoid this if possible and have a empty constructor.
    • B. Common used constructor attributes, which sets up normal constraints behaviour.
    • C. Rarely used constraints, do not put these in as constructor parameters, but only have set and get methods which can be initialized in "preValidation" method in the PropertyCollection.
The minimum unit test to perform is:

Description of tests

  • You must test a series of valid values
  • You must test a series of invalid values
  • You must test empty string and string only with white space, in some cases it is valid in some case it is not.
  • You must test null value.

Abstract

	@Test
	public void testPropertyTypeDefinition() {
		//Declaration of PropertyType
		PropertyType t = null;
				
		//Values
		final String name = "name";
		final String validStrings[] = {};   //Array of valid values
		final String invalidStrings[] = {}; //Array of invalid values.
		final String emptyStrings[]={"","  "};     //invalid in most cases. Customize that bit of code
		final String nullString = null; //undefined		
		T defaultValue=<not null default value>;		
		
		//Valid values -----------------------------------------------------------
		//instance of PropertType.
		t = new PropertyType(); 
		for (String value : validStrings) {
			try {
				T validT  = t.validate(name,value);
				assertNotNull(validT);			
				assertEquals(value,t.toString(name,validT));
			} catch (PropertyException e) {
				fail(e.getMessage());
			}
		}
		
		//Invalid values -----------------------------------------------------------
		//instance of PropertType.
		t = new PropertyType(); 
		for (String value : invalidStrings) {
			try {
				t.validate(name,value);
				fail("Should not validate ["+value+"]");
			} catch (PropertyException e) {
				assertNull(t.toType(name, value));
			}
		}
				
		//Null Undefined, with default value ----------------------------------------
		//instance of PropertType with default value.
		t = new PropertyType(defaultValue); 
		try {
			t.validate(name,nullString);
			fail();		
		} catch (PropertyException e) {
			assertEquals(defaultValue,t.toType(name, nullString));
		}	
			
		//Null Undefined, no default value. ------------------------------------------
		//instance of PropertType.
		t = new PropertyType();
		try {
			t.validate(name,nullString);
			fail();
		} catch (PropertyException e) {
			assertNull(t.toType(name, nullString));
		}
		
		
		//invalid in most cases. Customize ------------------------------------------
		//Empty values, can vary in PropertyTypes.
		//instance of PropertType.
		t = new PropertyType();
		for (String value : emptyStrings) {
			try {
				t.validate(name,value);
				fail("Should not validate ["+value+"]");
			} catch (PropertyException e) {
				assertNull(t.toType(name, value));
			}
		}		
	}


Example with IntegerPropertyType

	@Test
	public void testPropertyTypeDefinition() {
		//Declaration of PropertyType
		IntegerPropertyType t = null;
				
		//Values
		final String name = "name";
		final String validStrings[] = {123,-123};   //Array of valid values
		final String invalidStrings[] = {"cow","1.2"}; //Array of invalid values.
		final String emptyStrings[]={"","  "};     //invalid 
		final String nullString = null; //undefined		
		Integer defaultValue=42;			
		
		
		//Valid values -----------------------------------------------------------
		//instance of PropertType.
		t = new IntegerPropertyType(); 
		for (String value : validStrings) {
			try {
				T validT  = t.validate(name,value);
				assertNotNull(validT);			
				assertEquals(value,t.toString(name,validT));
			} catch (PropertyException e) {
				fail(e.getMessage());
			}
		}
		
		//Invalid values -----------------------------------------------------------
		//instance of PropertType.
		t = new IntegerPropertyType(); 
		for (String value : invalidStrings) {
			try {
				t.validate(name,value);
				fail("Should not validate ["+value+"]");
			} catch (PropertyException e) {
				assertNull(t.toType(name, value));
			}
		}
				
		//Null Undefined, with default value ----------------------------------------
		//instance of PropertType with default value.
		t = new IntegerPropertyType(defaultValue); 
		try {
			t.validate(name,nullString);
			fail();		
		} catch (PropertyException e) {
			assertEquals(defaultValue,t.toType(name, nullString));
		}	
			
		//Null Undefined, no default value. ------------------------------------------
		//instance of PropertType.
		t = new IntegerPropertyType();
		try {
			t.validate(name,nullString);
			fail();
		} catch (PropertyException e) {
			assertNull(t.toType(name, nullString));
		}
		
		
		//invalid in most cases. Customize ------------------------------------------
		//Empty values, can vary in PropertyTypes.
		//instance of PropertType.
		t = new IntegerPropertyType();
		for (String value : emptyStrings) {
			try {
				t.validate(name,value);
				fail("Should not validate ["+value+"]");
			} catch (PropertyException e) {
				assertNull(t.toType(name, value));
			}
		}		
	}


Example with Quartz CronTrigger

A property type which could easily be made, is for the cron pattern.
So in the property list we can define when triggers/timers shall run.

Take the Quartz implementation http://quartz-scheduler.org/.
There is a CronTrigger - http://quartz-scheduler.org/documentation/quartz-1.x/tutorials/crontrigger.

To make a PropertyType for CronTrigger will look like this.

public class CronTriggerPropertyType extends PropertyType<String> {
	
	public CronTriggerPropertyType() {
		super("CronTrigger",null);
	}
	
	public CronTriggerPropertyType(String defaultValue) {
		super("CronTrigger",defaultValue);
	}	

	@Override
	public List<String> getRestrictions() {
		List<String> list = new ArrayList<String>();
		list.add("A CronTrigger value.");
		list.add("Validates a String to a valid CronExpression used by a CronTrigger.");
		list.add(getDefaultValueDescription());
		return list;
	}

	@Override
	public String validateValue(String name, String value) throws PropertyException {
		validateNullType(name,value);
		value = value.trim();
		try {
			new CronExpression(value);
			return value;			
		} catch (ParseException e) {
			throw new PropertyException("Property ["+name+"] with value ["+value+"] is not a legal CronTrigger value, with message ["+e.getMessage()+"]");
		}
	}

}


The test class for this will be.
	@Test
	public void testPropertyTypeDefinition() {
		//Declaration of PropertyType
		QuartzCronTriggerPropertyType t = null;		
				
		//Values
		final String name = "name";
		final String validStrings[] = {"0 15 10 * * ? 2005","0 15 10 ? * 6L 2002-2005"};   //Array of valid values
		final String invalidStrings[] = {"cow","1.2"}; //Array of invalid values.
		final String emptyStrings[]={"","  "};     //invalid 
		final String nullString = null; //undefined		
		String defaultValue="0 0 12 1/5 * ?";		
		
		
		//Valid values -----------------------------------------------------------
		//instance of PropertType.
		t = new QuartzCronTriggerPropertyType(); 
		for (String value : validStrings) {
			try {
				String validT  = t.validate(name,value);
				assertNotNull(validT);			
				assertEquals(value,t.toString(name,validT));
			} catch (PropertyException e) {
				fail(e.getMessage());
			}
		}
		
		//Invalid values -----------------------------------------------------------
		//instance of PropertType.
		t = new QuartzCronTriggerPropertyType(); 
		for (String value : invalidStrings) {
			try {
				t.validate(name,value);
				fail("Should not validate ["+value+"]");
			} catch (PropertyException e) {
				assertNull(t.toType(name, value));
			}
		}
				
		//Null Undefined, with default value ----------------------------------------		
		t = new QuartzCronTriggerPropertyType(defaultValue);
		try {
			t.validate(name,nullString);
			fail();
		} catch (PropertyException e) {
			assertEquals(defaultValue,t.toType(name, nullString));
		}
			
		//Null Undefined, no default value. ------------------------------------------
		//instance of PropertType.
		t = new QuartzCronTriggerPropertyType();
		try {
			t.validate(name,nullString);
			fail();
		} catch (PropertyException e) {
			assertNull(t.toType(name, nullString));
		}
		
		
		//invalid in most cases. Customize ------------------------------------------
		//Empty values, can vary in PropertyTypes.
		//instance of PropertType.
		t = new QuartzCronTriggerPropertyType();
		for (String value : emptyStrings) {
			try {
				t.validate(name,value);
				fail("Should not validate ["+value+"]");
			} catch (PropertyException e) {
				assertNull(t.toType(name, value));
			}
		}		
	}



And used like this.
    Trigger trigger = TriggerBuilder.newTrigger()
    .withIdentity("trigger3", "group1")
    .withSchedule(cronSchedule(MyAppProperties.APP_TIMER1_CRON.getTypedValue()));
    .build();


Than again you could extend the QuartzCronTriggerPropertyType with a method.
	public Trigger buildTrigger(String triggerName,String triggerGroup) {
	    return TriggerBuilder.newTrigger()
	    .withIdentity(triggerName, triggerGroup)
	    .withSchedule(cronSchedule(getTypedValue()))
	    .build();
	}

Example with your own Enum

If your have your own Enum definition.
public enum MyColors {	
	RED,
	GREEN,
	BLUE
}
It is pretty easy to make your on type, there is two ways to do this.

1. Use the EnumPropertyType:
public final static Property<MyColors> P_ENUM_MYCOLORS 	= new Property<MyColors>("p.enum.mycolors",new EnumPropertyType<MyColors>(MyColors.class));


2. Make your own PropertyType
public class MyColorsPropertyType extends EnumPropertyType<MyColors> {

	public MyColorsPropertyType() {
		super(MyColors.class);
	}

	public MyColorsPropertyType(MyColors defaultValue) {
		super(MyColors.class,defaultValue);
	}
}
And use it like:
public final static Property<MyColors> P_ENUM_MYCOLORS 	= new Property<MyColors>("p.enum.mycolors",new MyColorsPropertyType());