package org.sinnlabs.dbvim.zk.model;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.sinnlabs.dbvim.config.Configurator;
import org.sinnlabs.dbvim.rules.engine.RulesEngine;
import org.sinnlabs.dbvim.rules.engine.exceptions.DisplayableRulesException;
import org.sinnlabs.dbvim.rules.engine.exceptions.RulesException;
import org.sinnlabs.dbvim.ui.DesignerElements;
import org.zkoss.idom.Element;
import org.zkoss.lang.Classes;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.sys.DesktopCtrl;
import org.zkoss.zul.Messagebox;
public class ComponentFactory {
/**
* Creates a new UI Component of the given class.
* @param cls The component's class
* @return The newly created component with default
* properties
* @throws Exception
*/
public static Component createComponent(String sComponentClass, IDeveloperStudio developer) throws Exception
{
if (StringUtils.isEmpty(sComponentClass))
return null;
// get an instance of the given component class
Class<?> componentClass = Class.forName(sComponentClass);
// create a new instance of the specified component class
Component newComponent = (Component) componentClass.newInstance();
try
{
// check for any pre-creation rules
RulesEngine.applyRules(newComponent, RulesEngine.PRE_CREATION_RULES, developer);
}
catch (RulesException re)
{
// if a rules exception has been thrown,
// do not create the component on canvas
newComponent = null;
// if this is a displayable exception,
// display the message on a Messagebox.
// display an error message and exit
if (re instanceof DisplayableRulesException)
{
Messagebox.show(re.getMessage(), "Rules Exception", Messagebox.OK, Messagebox.ERROR);
}
return null;
}
Method method = null;
try
{
// check if the component implements 'onCreate' method
method = Classes.getAnyMethod(componentClass, "onCreate", null);
// if yes, invoke it now to do custom initialization
method.invoke(newComponent, (Object[])null);
}
catch (NoSuchMethodException e)
{
}
// assign the component Id
newComponent.setId(fixAutoId(newComponent.getUuid()));
// return the newly created component
return newComponent;
}
/**
* Returns the simple name of a component's class.
* @param clazz The component class to resolve
*/
public static String getSimpleClassName(Component cmp)
{
if (cmp == null)
return "";
// get the component's implementation class
Class<? extends Component> clazz = cmp.getClass();
if (clazz == null)
return "";
return clazz.getSimpleName();
}
/**
* Converts an auto-generated component
* Id assigned from the framework into
* a valid one for later re-loading.
* @param sId component's Id
* @return A new valid Id
*/
public static String fixAutoId(String sId)
{
if (StringUtils.isEmpty(sId))
return "";
// if the given Id is not auto-generated,
// do not change it
if (! isAutoId(sId))
return sId;
String sNewId = "";
// the easiest way to convert an auto-generated
// Id into a new valid one, is by removing the
// leading 'z_' chars and using something else.
sNewId = StringUtils.removeStart(sId, "z_");
sNewId = "id_" + sNewId;
// return fixed Id
return sNewId;
}
/**
* Walks through the given component model and
* assigns new Ids to all the contained elements.
* @param element
* @return
*/
public static Component assignNewIds(Component element)
{
if (element == null)
return null;
// assign a new Id to the component
assignNewId(element);
return element;
}
public static void assignNewId(Component component) {
// TODO Generate new custom ID
// get the next available component Id for current Desktop
if (component == null)
return;
// assign a new Id to the given component
component.setId(createId(component));
// fix auto id
component.setId(fixAutoId(component.getId()));
// get component's children
List<Component> listChildren = component.getChildren();
if (listChildren.size() == 0)
return;
// loop through all component's children
Iterator<Component> iter = listChildren.iterator();
while (iter.hasNext())
{
try
{
// get next component in the list
Component child = (Component) iter.next();
if (child == null)
continue;
/*** RECURSION ***/
assignNewId(child);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
protected static String createId(Component comp) {
return ((DesktopCtrl) Executions.getCurrent().getDesktop()).getNextUuid(comp);
}
/**
* Returns all the valid properties of the component
* that should be displayed on Property view.
* @param cmp component to resolve
* @return PropertyDescriptor array
*/
public static PropertyDescriptor[] getComponentProperties(Component cmp)
{
if (cmp == null)
return null;
// get property descriptors of the Component class
PropertyDescriptor[] arrDescriptors = PropertyUtils.getPropertyDescriptors(cmp);
if (ArrayUtils.isEmpty(arrDescriptors))
return null;
// get the list of the component's properties
// that should not be displayed onto the view
String[] arrExcludedProps = RulesEngine.getComponentAttributes(
cmp, RulesEngine.ATTRIBUTES_EXCLUDE_FROM_PROPERTY_VIEW);
// get the list of the custom component's properties
// that should be displayed onto property view
String[] arrCustomProps = RulesEngine.getComponentAttributes(cmp,
RulesEngine.ATTRIBUTES_CUSTOM_PROPERTIES);
PropertyDescriptor[] arrValidProps = null;
// loop through the component property descriptors
for (int i = 0; i < arrDescriptors.length; i++)
{
try
{
// get the next property descriptor
PropertyDescriptor descriptor = arrDescriptors[i];
if (descriptor == null)
continue;
// check the list of custom component properties
if (! ArrayUtils.isEmpty(arrCustomProps))
{
// if yes, add custom property
if (ArrayUtils.contains(arrCustomProps, descriptor.getName())) {
if (arrValidProps == null)
arrValidProps = new PropertyDescriptor[]{};
// this is a valid property, so add it to the array
arrValidProps = (PropertyDescriptor[]) ArrayUtils.add(arrValidProps, descriptor);
continue;
}
}
// get read / write property methods
Method methodRead = descriptor.getReadMethod();
Method methodWrite = descriptor.getWriteMethod();
// for all properties, both read and write
// methods should exist
if ((methodRead == null) ||
(methodWrite == null))
continue;
// get the name of the object property
String sName = descriptor.getName();
// check the list of excluded properties to see
// if this property is banned by the rules
if (! ArrayUtils.isEmpty(arrExcludedProps))
{
// if yes, move to the next property
if (ArrayUtils.contains(arrExcludedProps, sName))
continue;
}
// Filter out the following properties,
// as they are used internally:
//
// * childable
// * zIndexByClient
// * innerAttrs
// * leftByClient
// * topByClient
// * outerAttrs
// * transparent
if (sName.equalsIgnoreCase("childable") ||
sName.equalsIgnoreCase("zIndexByClient") ||
sName.equalsIgnoreCase("innerAttrs") ||
sName.equalsIgnoreCase("leftByClient") ||
sName.equalsIgnoreCase("topByClient") ||
sName.equalsIgnoreCase("outerAttrs") ||
sName.equalsIgnoreCase("transparent"))
continue;
// Display only properties of the following types
//
// * String
// * String[]
// * int
// * boolean
// * long
if (((descriptor.getPropertyType() != String.class) &&
(descriptor.getPropertyType() != String[].class) &&
(descriptor.getPropertyType() != boolean.class) &&
(descriptor.getPropertyType() != int.class) &&
(descriptor.getPropertyType() != long.class)) ||
(descriptor.isHidden())
)
{
continue;
}
if (arrValidProps == null)
arrValidProps = new PropertyDescriptor[]{};
// this is a valid property, so add it to the array
arrValidProps = (PropertyDescriptor[]) ArrayUtils.add(arrValidProps, descriptor);
}
catch (Exception e)
{
continue;
}
}
// dispose
arrDescriptors = null;
// return the array of valid properties
return arrValidProps;
}
/**
* In ZK version 7.0.3 we can not determine which id is auto generated.
* In this situation we check for leading 'z_' prefix
* @param id - Component id that needs to be checked
* @return
*/
public static boolean isAutoId(String id) {
if (id.startsWith("z_"))
return true;
return false;
}
/**
* Get component events to be displayed
* @param clzz Component class
* @return Array of event names
*/
public static String[] getComponentEvents(Class<?> clazz)
{
if ((clazz == null))
return null;
// get canonical name of the component's class
String sClassName = clazz.getName();
// get component's Configurator instance
// from the toolkit object
Configurator config = DesignerElements.getComponentsConfigurator();
if (config == null)
return null;
// first try to locate the component element within
// the configuration iDOM
Element domClass = config.getElement("class", sClassName, null);
if (domClass == null)
return null;
// if a <class> element was found, get
// its parent which is the <component> element
Element domComponent = (Element) domClass.getParent();
if (domComponent == null)
return null;
// get the required <events> tag from the node
Element domImage = config.getElement("events", domComponent);
if (domImage == null)
return null;
// get component's event list
String sEventList = domImage.getText();
if (StringUtils.isEmpty(sEventList))
return null;
// split the event list into an array
// of events
String[] arrEvents = StringUtils.split(sEventList, ',');
// return the events array
return arrEvents;
}
}