/*******************************************************************************
* Copyright 2013 Geoscience Australia
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package au.gov.ga.earthsci.editable;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import org.eclipse.sapphire.ElementType;
import org.eclipse.sapphire.Property;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import au.gov.ga.earthsci.editable.annotations.Factory;
import au.gov.ga.earthsci.editable.annotations.InstanceElementType;
/**
* Helper class that is used to instantiate a new object used as the value for a
* property.
*
* @author Michael de Hoog (michael.dehoog@ga.gov.au)
*/
public class PropertyValueFactory
{
/**
* Create a new instance of the instance type of <code>type</code> that can
* be set for the given property on the parent object.
* <p/>
* The logic is as follows:
* <ol>
* <li>The property is checked for a {@link Factory} annotation. If found,
* the {@link IFactory} defined is used to create the instance.</li>
* <li>The property is checked for an {@link InstanceElementType}
* annotation. If found, the element type is instantiated and returned.</li>
* <li>The type is checked for a {@link Factory} annotation. If found, the
* {@link IFactory} defined is used to create the instance.</li>
* <li>The type is checked for an {@link InstanceElementType} annotation. If
* found, the element type is instantiated and returned.</li>
* <li>If the <code>fallbackType</code> is non-null, it is instantiated and
* returned.</li>
* <li>Otherwise <code>null</code> is returned.</li>
* </ol>
*
* @param property
* Property that the new instance is for
* @param type
* Model type of the new instance
* @param parent
* Parent object that the new instance will be set on
* @param fallbackType
* Type of object to create if both the property and type don't
* define a {@link Factory} or an {@link InstanceElementType}
* @return New instance
* @throws SecurityException
* @throws IllegalArgumentException
* @throws NoSuchMethodException
* @throws InstantiationException
* @throws IllegalAccessException
* @throws InvocationTargetException
*/
public static Object create(Property property, ElementType type, Object parent, Class<?> fallbackType)
throws SecurityException, IllegalArgumentException, NoSuchMethodException, InstantiationException,
IllegalAccessException, InvocationTargetException
{
//first see if the property has factory or type annotations
Factory factoryAnnotation = property.definition().getAnnotation(Factory.class);
InstanceElementType elementTypeAnnotation = property.definition().getAnnotation(InstanceElementType.class);
if (factoryAnnotation == null && elementTypeAnnotation == null)
{
//if the property has no relevant annotations, see if the type does
factoryAnnotation = type.getAnnotation(Factory.class);
elementTypeAnnotation = type.getAnnotation(InstanceElementType.class);
}
if (factoryAnnotation != null)
{
//a factory is defined, instantiate it and use it to create a property value
Class<? extends IFactory<?>> factoryClass = factoryAnnotation.value();
//first find a constructor (prefer a constructor that takes the annotation
//as a parameter, otherwise just use the empty constructor)
Constructor<? extends IFactory<?>> constructor;
boolean constructorTakesAnnotation;
try
{
constructor = factoryClass.getConstructor(Factory.class);
constructorTakesAnnotation = true;
}
catch (Exception e)
{
constructor = factoryClass.getConstructor();
constructorTakesAnnotation = false;
}
constructor.setAccessible(true);
//instantiate the factory
IFactory<?> factory =
constructorTakesAnnotation ? constructor.newInstance(factoryAnnotation) : constructor
.newInstance();
//use the factory to create a new property value
Shell shell = Display.getDefault().getActiveShell();
return factory.create(type, property, parent, shell);
}
else if (elementTypeAnnotation != null)
{
//if an element type is defined, create an instance and return
Class<?> elementType = elementTypeAnnotation.value();
return elementType.newInstance();
}
else if (fallbackType != null)
{
//otherwise, if the fallback type is defined, use it
return fallbackType.newInstance();
}
return null;
}
}