FileDocCategorySizeDatePackage
MBeanMetaHelper.javaAPI DocGlassfish v2 API40418Fri May 04 22:25:36 BST 2007com.sun.enterprise.admin.meta

MBeanMetaHelper

public class MBeanMetaHelper extends Object implements MBeanMetaConstants

Fields Summary
static int
ONLY_CHILD
static int
MULTY_CHILDS
static int
SETTER_METHODTYPE
static int
GETTER_METHODTYPE
static Class[]
_clsStr
static Class[]
_clsStrArr
static Class[]
_clsAttr
static Class[]
_clsAttrList
static Class[]
_clsServAndOname
static Class[]
_clsModelMBI
static Class[]
_clsBoolean
static Class[]
_clsObjAndStr
static Class[]
_clsInvokeParms
private static String[]
strArray
Constructors Summary
Methods Summary
private static voidaddDataToChildOperInfo(java.lang.String childName, boolean bMulti, com.sun.org.apache.commons.modeler.FeatureInfo info)

        addFieldToInfo(CHILD_FIELD_NAME, childName, info);
        if(bMulti)
           addFieldToInfo(MULTI_FIELD_NAME, "true", info);
    
private static voidaddFieldToInfo(java.lang.String name, java.lang.String value, com.sun.org.apache.commons.modeler.FeatureInfo info)

        FieldInfo field = new FieldInfo();
        field.setName(name);
        field.setValue(value);
        info.addField(field);
    
private static com.sun.org.apache.commons.modeler.OperationInfocreateOperationInfo(java.lang.String name, java.lang.String impact, java.lang.String returnType, com.sun.org.apache.commons.modeler.ParameterInfo param, java.lang.String whereType)

        OperationInfo info = new OperationInfo();
        info.setName(name);
        info.setImpact(impact);
        if(returnType!=null)
           info.setReturnType(returnType);
        if(param!=null)
            info.addParameter(param);
        if (whereType!=null)
            info.addField(newField(WHERE_LOCATED_FIELD_NAME, whereType));
        return info;
    
public static java.lang.StringcutAttributeTokenFromXPath(java.lang.String xpath)

       if(!xpath.endsWith("]") && !xpath.endsWith("/"))
       {
           int idx = xpath.lastIndexOf('/");
           if(idx>0 && xpath.length()>idx+1 && xpath.charAt(idx+1)=='@")
           {
               return xpath.substring(0, idx);
           }
       }
       return xpath;
   
public static java.lang.StringcutLastElementFromXPath(java.lang.String xpath)

       if(xpath.endsWith("]"))
       {
           int idx = xpath.lastIndexOf('[");
           if(idx>0)
              xpath=xpath.substring(0,idx);
       }
       int idx = xpath.lastIndexOf('/");
       if(idx>=0)
           return xpath.substring(0, idx);
       return null;
   
public static java.lang.StringdescriptorToString(com.sun.org.apache.commons.modeler.FeatureInfo descr)

        return descriptorToString(descr.getFields());
    
public static java.lang.StringdescriptorToString(java.util.List fields)

        String str = "Descriptor[";
        for(int i=0; i<fields.size(); i++)
        {
            FieldInfo field = (FieldInfo)fields.get(i);
            str = str + field.getName() + "=" + field.getValue() + " ";
        }
        return  str + "]";
    
public static java.lang.StringextractLastElemNameFromXPath(java.lang.String xpath)

       if(xpath.endsWith("]"))
       {
           int idx = xpath.lastIndexOf('[");
           if(idx>0)
              xpath=xpath.substring(0,idx);
       }
       int idx = xpath.lastIndexOf('/");
       if(idx>=0)
           return xpath.substring(idx+1);
       return null;
   
public static com.sun.enterprise.admin.meta.MBeanMetaHelper$AttrIntrofindOrCreateAttrInfo(java.util.Hashtable attrs, java.lang.String attrName, java.lang.String methodName, java.lang.Class supposedTypeClass, java.lang.String whereType, int methodType)

        AttrIntro ai = (AttrIntro)attrs.get(attrName);
        if(ai==null)
        {
            ai = new AttrIntro();
            ai.name = attrName;
            ai.type = supposedTypeClass.getName();
            if((methodType&GETTER_METHODTYPE)!=0)
                ai.getName = methodName;
            if((methodType&SETTER_METHODTYPE)!=0)
                ai.setName = methodName;
            ai.getName = methodName;
            ai.whereType = whereType;
            attrs.put(ai.name, ai);
        }
        else
           if(ai.type==null || ai.type.equals(supposedTypeClass.getName()))
           {
                if((methodType&GETTER_METHODTYPE)!=0)
                    ai.getName = methodName;
                if((methodType&SETTER_METHODTYPE)!=0)
                    ai.setName = methodName;
           }
           else
           {
               return null;
           }
        return ai;
    
private static java.lang.ClassforNameOrNull(java.lang.String str)

        try {
            return Class.forName(str);
        } catch (Exception e) {
            return null;
        }
    
private static java.lang.StringgetAttrNameFromMethodName(java.lang.String name, boolean bDecamelaze)

        return getAttrNameFromMethodName(name, bDecamelaze, ATTRIBUTE_NAME_DELIMITER_SYMBOL);
    
private static java.lang.StringgetAttrNameFromMethodName(java.lang.String name, boolean bDecamelaze, char separatorSymbol)

        if(name.startsWith("set") || name.startsWith("get"))
            name = name.substring(3);
        else
            if(name.startsWith("is"))
                name = name.substring(2);
        if(!bDecamelaze)
            return name;
        if(name.length()==0)
            return name;
        String attrName = name.toLowerCase();
        char[] arr1 =  name.toCharArray();
        char[] arr2 =  name.toLowerCase().toCharArray();
        StringBuffer buf = new StringBuffer(arr1.length*2);
        buf.append(arr2[0]);
        for(int i=1; i<arr1.length; i++)
        {
            if(arr1[i]!=arr2[i])
                buf.append(separatorSymbol);
            buf.append(arr2[i]);
        }
        return buf.toString();
    
public static java.lang.ClassgetConfigBeanClass(java.lang.String xPath)

        // get ConfigBean classname from XPath
        String beanName = ConfigBeansFactory.getConfigBeanNameByXPath(xPath);
        //getting the class object
        try
        {
            Class cl = Class.forName("com.sun.enterprise.config.serverbeans."+beanName);
            return cl;
        }
        catch(Exception e)
        {
            return null;
        }
    
public static java.lang.StringgetMultipleElementKeyValue(java.lang.String xpath)

       if(xpath.endsWith("']"))
       {
           int idx = xpath.lastIndexOf('\'",  xpath.length()-3);
           if(idx>0)
           {
               return xpath.substring(idx+1, xpath.length()-2);
           }
       }
       return null;
   
private static com.sun.org.apache.commons.modeler.OperationInfogetOperationInfo(java.lang.reflect.Method method, java.lang.String whereType)

        OperationInfo info = new OperationInfo();
        info.setName(method.getName());
        info.setReturnType(method.getReturnType().getName());
        Class paramsClasses[]=method.getParameterTypes();
        for(int k=0; k<paramsClasses.length; k++)
        {
            info.addParameter(new ParameterInfo("param"+(k+1), paramsClasses[k].getName(), null));
        }
        if (whereType!=null)
            info.addField(newField(WHERE_LOCATED_FIELD_NAME, whereType));
        return info;
    
public static java.lang.ClassgetRuntimeModelBeanClass(java.lang.String j2eeType)

        try
        {
            return Class.forName("com.sun.enterprise.management.model."+j2eeType+"Mdl");
        }
        catch(Exception e)
        {
//            e.printStackTrace();
            return null;
        }
    
private static booleanisMethodMatch(java.lang.reflect.Method m, java.lang.String name, java.lang.Class[] clParams)

        if(!name.equals(m.getName()))
           return false;
        Class[] cls = m.getParameterTypes();
        if(clParams==null)
            if(cls==null || cls.length==0)
                return true;
            else
                return false;
        if(cls==null)
            if(clParams.length==0)
                return true;
            else
                return false;
         if(clParams.length!=cls.length)
             return false;
        for(int i=0; i<cls.length; i++)
            if(!clParams[i].equals(cls[i]))
                return false;
        return true;
    
public static java.lang.StringmapToConfigBeanAttributeName(java.lang.String name)

        if(name!=null)
            return name.replace('_", '-");
        return name;    
    
public static java.lang.StringmapToMBeanAttributeName(java.lang.String name)

        if(ATTRIBUTE_NAME_DELIMITER_SYMBOL!='_" && name!=null)
            return name.replace('_", ATTRIBUTE_NAME_DELIMITER_SYMBOL);
        return name;    
    
public static voidmergeWithConfigBean(com.sun.org.apache.commons.modeler.ManagedBean managedBean, java.lang.Class objectClass, int mode)
Process the methods and extract 'attributes', methods, etc

                 
    //*****************************************************************************
            
    
        
        if(objectClass==null)
            return;
        Hashtable attrs = new Hashtable();
        Hashtable children = new Hashtable();
        int shift   = 0;
        
        //Introspect and get all the methods
        Method[] methods = objectClass.getMethods();
        for (int j = 0; j < methods.length; ++j)
        {
            String methodName=methods[j].getName();
            
            //----------GETTER----------------
            if( methodName.startsWith("get") || methodName.startsWith("is"))
            {
                shift = methodName.startsWith("is")?2:3;
                if( Modifier.isStatic(methods[j].getModifiers()) || !Modifier.isPublic(methods[j].getModifiers()) )
                {
                    continue;
                }
                Class params[]=methods[j].getParameterTypes();
                if( params.length != 0 )
                {
                    continue;
                }
                if( methods[j].getDeclaringClass() != objectClass )
                    continue;
                
                Class ret=methods[j].getReturnType();
                if( ! supportedType( ret ) )
                {
                    //maybe this is child 
                    String childClassName = ret.getName();
                    if(childClassName.endsWith("."+methodName.substring(shift)) ||
                       childClassName.endsWith("."+methodName.substring(shift)+";"))
                    {
                        children.put(methodName.substring(shift), childClassName); 
                    }
                    continue;
                }
                if((mode&EXPOSE_GETTERS)==0 )
                    continue;
//                if(methodName.startsWith( "getDefault" ))
//                    continue;
                
                AttrIntro ai = (AttrIntro)attrs.get(getAttrNameFromMethodName(methodName, true));
                if(ai==null)
                {
                    ai = new AttrIntro();
                    ai.name = getAttrNameFromMethodName(methodName, true);
                    attrs.put(ai.name, ai);
                }
                if(ai.type!=null)
                {
                    if(!ai.type.equals(ret.getName()))
                        continue;
                }
                else
                {
                    ai.type = ret.getName();
                }
                ai.getName = methodName;
                //----------SETTER----------------
            } else if( methodName.startsWith( "set" ) )
            {
                if((mode&EXPOSE_SETTERS)==0 )
                    continue;

                Class params[]=methods[j].getParameterTypes();
                if( params.length != 1 )
                {
                    continue;
                }
                if( ! supportedType( params[0] ) )
                {
                    continue;
                }
                if( ! Modifier.isPublic( methods[j].getModifiers() ) )
                {
                    continue;
                }
                if( methods[j].getDeclaringClass() != objectClass )
                    continue;

                AttrIntro ai = (AttrIntro)attrs.get(getAttrNameFromMethodName(methodName, true));
                if(ai==null)
                {
                    ai = new AttrIntro();
                    ai.name = getAttrNameFromMethodName(methodName, true);
                    attrs.put(ai.name, ai);
                }
                if(ai.type!=null)
                {
                    if(!ai.type.equals(params[0].getName()))
                        continue;
                }
                else
                {
                    ai.type = params[0].getName();
                }
                ai.setName = methodName;
                if(methodName.startsWith( "setDefault" )) //???
                {
                    ai.setName = "set"+methodName.substring(3);
                }
                
            } else
            {
                continue;
            }
        }
        
        OperationInfo operationInfo;

        //**** A T T R I B U T E S ******
//        attrs.remove("x_path");
//        attrs.remove("attribute_names");
//        attrs.remove("monitoring_enabled");
        
        if(attrs.size()>0)
        {
            AttributeInfo[] infos = managedBean.getAttributes();
            Hashtable infosTable = new Hashtable();
            for(int i=0; i<infos.length; i++)
            {
                infosTable.put(infos[i].getName(), infos[i]);
            }
            
            String key;
            Enumeration keys = attrs.keys();
            while(keys.hasMoreElements())
            {
                key = (String)keys.nextElement();
                AttrIntro ai = (AttrIntro)attrs.get(key);
                AttributeInfo info = (AttributeInfo)infosTable.get(key);
                if(info==null)
                {
                    ai.whereType = LOCATED_IN_CONFIGBEAN; 
                    info = ai.createAttributeInfo();
                    managedBean.addAttribute(info);
                    infosTable.put(key, info);
                }
                else
                {
                    ai.mergeWithAttributeInfo(info);
                }
            }

            //getDefaultAttributeValue
            operationInfo = createOperationInfo("getDefaultAttributeValue", "INFO", 
                   "java.lang.String", 
                   new ParameterInfo("attributeName", "java.lang.String", null),
                   LOCATED_IN_CONFIGBEAN);
            mergeWithOperationInfo(managedBean, operationInfo);
        }
        
        //classnames values for method's types
        String nameClass        = "javax.management.ObjectName";
        String attrClass        = "javax.management.Attribute";
        String namesClass       = (new ObjectName[0]).getClass().getName();
        String attrListClass    = (new AttributeList()).getClass().getName();
        String stringsClass     = (new String[0]).getClass().getName();

        FieldInfo field;
        ParameterInfo param;

        // **** P R O P E R T I E S  ******
        if(children.get("ElementProperty")!=null)
        {
            children.remove("ElementProperty");

            
            //getProperties
            operationInfo = createOperationInfo("getProperties", "INFO", 
                   attrListClass, null, LOCATED_IN_CONFIGBEAN);
            mergeWithOperationInfo(managedBean, operationInfo);
            
            //getDefaulCustomProperties
//            operationInfo = createOperationInfo("getDefaultCustomProperties", "INFO", 
//                   attrListClass, null, null);
//            mergeWithOperationInfo(managedBean, operationInfo);
            
            //getProperty
            operationInfo = createOperationInfo("getPropertyValue", "INFO", 
                   "java.lang.Object", 
                   new ParameterInfo("propertyName", "java.lang.String", null),
                   LOCATED_IN_CONFIGBEAN);
            mergeWithOperationInfo(managedBean, operationInfo);
            
            //setProperty
            operationInfo = createOperationInfo("setProperty", "ACTION", 
                   "void", 
                   new ParameterInfo("nameAndValue", attrClass, null),
                   LOCATED_IN_CONFIGBEAN);
            mergeWithOperationInfo(managedBean, operationInfo);
        }

        // **** S Y S T E M       P R O P E R T I E S  ******
        if(children.get("SystemProperty")!=null)
        {
            children.remove("SystemProperty");
            
            //getSystemProperties
            operationInfo = createOperationInfo("getSystemProperties", "INFO", 
                   attrListClass, null, LOCATED_IN_CONFIGBEAN);
            mergeWithOperationInfo(managedBean, operationInfo);
            
            //getSystemProperty
            operationInfo = createOperationInfo("getSystemPropertyValue", "INFO", 
                   "java.lang.Object", 
                   new ParameterInfo("propertyName", "java.lang.String", null),
                   LOCATED_IN_CONFIGBEAN);
            mergeWithOperationInfo(managedBean, operationInfo);
            
            //setSystemProperty
            operationInfo = createOperationInfo("setSystemProperty", "ACTION", 
                   "void", 
                   new ParameterInfo("nameAndValue", attrClass, null),
                   LOCATED_IN_CONFIGBEAN);
            mergeWithOperationInfo(managedBean, operationInfo);
        }

        //**** C H I L D R E N  ******

        //children.remove("ElementProperty");
        
        if(children.size()>0)
        {
            String key;
            Enumeration keys = children.keys();
            while(keys.hasMoreElements())
            {
                key = (String)keys.nextElement();
                String clazz = (String)children.get(key);
                boolean bMulti = clazz.charAt(0)=='["?true:false;
                String  childName = getAttrNameFromMethodName(key, true, '-");
                //getter
                if((mode&EXPOSE_GETCHILD)!=0)
                {
                    //get child (ObjectName)
                    operationInfo = createOperationInfo("get"+key, "INFO", 
                           bMulti?namesClass:nameClass, 
                           null, LOCATED_IN_CONFIGBEAN);
                    addDataToChildOperInfo(childName, bMulti, operationInfo);
                    mergeWithOperationInfo(managedBean, operationInfo);
                    if(bMulti)
                    {
                        //get child NamesList(ObjectName)
                        operationInfo = createOperationInfo(
                             "get"+key+GET_LISTNAMES_OP_SUFFIX, 
                             "INFO", stringsClass, null, null);
                        addDataToChildOperInfo(childName, bMulti, operationInfo);
                        mergeWithOperationInfo(managedBean, operationInfo);
                    }
                    
                    //getChildByKey
                    if(bMulti)
                    {
                        String prefix = "get"+key+"By";
                        for (int j = 0; j < methods.length; ++j)
                        {
                            String methodName=methods[j].getName();
                            if(methodName.startsWith(prefix))
                            {
                                operationInfo = createOperationInfo(methodName, "INFO", 
                                       nameClass, 
                                       new ParameterInfo("key", "java.lang.String", null),
                                       LOCATED_IN_CONFIGBEAN);
                                addDataToChildOperInfo(childName, bMulti, operationInfo);
                                mergeWithOperationInfo(managedBean, operationInfo);
                                break;
                            }
                        }
                    }
                }
                if((mode&EXPOSE_CREATECHILD)!=0)
                {
                    //CreateChild
                    String prefix = bMulti?"add"+key:"set"+key;
                    for (int j = 0; j < methods.length; ++j)
                    {
                        String methodName=methods[j].getName();
                        if(methodName.startsWith(prefix))
                        {
                            operationInfo = createOperationInfo("create"+key, "ACTION_INFO", 
                                   nameClass, 
                                   new ParameterInfo("attribute_list", attrListClass, null),
                                   LOCATED_IN_CONFIGBEAN);
                            addDataToChildOperInfo(childName, bMulti, operationInfo);
                            mergeWithOperationInfo(managedBean, operationInfo);
                            break;
                        }
                    }
                }
                if((mode&EXPOSE_DESTROYCHILD)!=0)
                {
                    if(!bMulti)
                    {
                        operationInfo = createOperationInfo("remove"+key, 
                               "ACTION", "void", null, LOCATED_IN_CONFIGBEAN);
                        addDataToChildOperInfo(childName, bMulti, operationInfo);
                        mergeWithOperationInfo(managedBean, operationInfo);
                    }
                    else
                    {
                        String prefix = "get"+key+"By";
                        for (int j = 0; j < methods.length; ++j)
                        {
                            String methodName=methods[j].getName();
                            if(methodName.startsWith(prefix))
                            {
                                operationInfo = createOperationInfo("remove"+methodName.substring(3), 
                                       "ACTION", "void", 
                                       new ParameterInfo("key", "java.lang.String", null),
                                       LOCATED_IN_CONFIGBEAN);
                                addDataToChildOperInfo(childName, bMulti, operationInfo);
                                mergeWithOperationInfo(managedBean, operationInfo);
                                break;
                            }
                        }
                    }
                }
            }
        }
    
public static voidmergeWithDynamicMBean(com.sun.org.apache.commons.modeler.ManagedBean managedBean, java.lang.Class objectClass)

    //*****************************************************************************
          
    

        if(objectClass==null)
            return;
        //Introspect and get all the methods
        Method[] methods = objectClass.getMethods();
        for (int j = 0; j < methods.length; ++j)
        {
            if( Modifier.isStatic(methods[j].getModifiers()))
                continue;
            if( ! Modifier.isPublic( methods[j].getModifiers() ) ) 
                continue;

            if( methods[j].getDeclaringClass() == Object.class )
                continue;
            Class declaringClass = methods[j].getDeclaringClass();
//            if( declaringClass == DynamicMBean.class )
//                continue;
            if( NotificationBroadcasterSupport.class.equals(declaringClass) )
                continue;
            String methodName=methods[j].getName();
            if( isMethodMatch(methods[j], "getAttribute", _clsStr)          || 
                isMethodMatch(methods[j], "getAttributes", _clsStrArr)      ||
                isMethodMatch(methods[j], "setAttribute", _clsAttr)         || 
                isMethodMatch(methods[j], "setAttributes", _clsAttrList)    ||
                isMethodMatch(methods[j], "preRegister", _clsServAndOname)  || 
                isMethodMatch(methods[j], "postRegister", _clsBoolean)  ||
                isMethodMatch(methods[j], "preDeregister", null) || 
                isMethodMatch(methods[j], "postDeregister", null)||
                isMethodMatch(methods[j], "setManagedResource", _clsObjAndStr) || 
                isMethodMatch(methods[j], "setModelMBeanInfo", _clsModelMBI)||
                isMethodMatch(methods[j], "getMBeanInfo", null)  || 
                isMethodMatch(methods[j], "invoke", _clsInvokeParms) )
                continue;
            OperationInfo operationInfo = getOperationInfo(methods[j], LOCATED_IN_MBEAN);
            mergeWithOperationInfo(managedBean, operationInfo);
        }
        
    
private static voidmergeWithOperationInfo(com.sun.org.apache.commons.modeler.ManagedBean managedBean, com.sun.org.apache.commons.modeler.OperationInfo info)

        //OperationInfo[] infos = managedBean.getOperations();
        //FIXME
        managedBean.addOperation(info);
    
public static voidmergeWithRuntimeModelBean(com.sun.org.apache.commons.modeler.ManagedBean managedBean, java.lang.Class objectClass)

        
        if(objectClass==null)
            return;
        Hashtable attrs = new Hashtable();
        
        //Introspect and get all the methods
        Method[] methods = objectClass.getMethods();
        for (int j = 0; j < methods.length; ++j)
        {
            if( Modifier.isStatic(methods[j].getModifiers()))
                continue;
            if( ! Modifier.isPublic( methods[j].getModifiers() ) ) 
                continue;
            if( methods[j].getDeclaringClass() == Object.class )
                continue;
            
            String methodName=methods[j].getName();
            Class  params[]=methods[j].getParameterTypes();
            Class  ret=methods[j].getReturnType();
            
            //----------GETTER----------------
            if( (methodName.startsWith("get") || methodName.startsWith("is")) &&
//                !methodName.startsWith( "getDefault" ) &&
                params.length == 0 && 
                supportedType( ret ) )
            {
                findOrCreateAttrInfo(attrs, getAttrNameFromMethodName(methodName,false), methodName, ret, LOCATED_IN_RUNTIMEBEAN, GETTER_METHODTYPE);
            } 
            //----------SETTER----------------
            else if( methodName.startsWith( "set" ) &&
                     params.length == 1 &&
                     supportedType( params[0] ))
            {
                findOrCreateAttrInfo(attrs, getAttrNameFromMethodName(methodName,false), methodName, params[0], LOCATED_IN_RUNTIMEBEAN, SETTER_METHODTYPE);
                
            } 
            //----------OPERATIONS----------------
            else
            {
                OperationInfo operationInfo = getOperationInfo(methods[j], LOCATED_IN_RUNTIMEBEAN);
                mergeWithOperationInfo(managedBean, operationInfo);
            }
        }
        
        //**** A T T R I B U T E S ******
        if(attrs.size()>0)
        {
            AttributeInfo[] infos = managedBean.getAttributes();
            Hashtable infosTable = new Hashtable();
            for(int i=0; i<infos.length; i++)
            {
                infosTable.put(infos[i].getName(), infos[i]);
            }
            
            String key;
            Enumeration keys = attrs.keys();
            while(keys.hasMoreElements())
            {
                key = (String)keys.nextElement();
                AttrIntro ai = (AttrIntro)attrs.get(key);
                AttributeInfo info = (AttributeInfo)infosTable.get(key);
                if(info==null)
                {
                    ai.whereType = LOCATED_IN_RUNTIMEBEAN; 
                    info = ai.createAttributeInfo();
                    managedBean.addAttribute(info);
                    infosTable.put(key, info);
                }
                else
                {
                    ai.mergeWithAttributeInfo(info);
                }
            }
        }
        
    
private static com.sun.org.apache.commons.modeler.FieldInfonewField(java.lang.String name, java.lang.Object value)

        FieldInfo info = new FieldInfo();
        info.setName(name);
        info.setValue(value);
        return info;
    
private static booleansupportedType(java.lang.Class ret)

    //*****************************************************************************
          
    
        return ret == String.class ||
        ret == Integer.class ||
        ret == Integer.TYPE ||
        ret == Long.class ||
        ret == Long.TYPE ||
        ret == java.io.File.class ||
        ret == Boolean.class ||
        ret == Boolean.TYPE ||
        ret == strArray.getClass() || // XXX ???
        ret == ObjectName.class
        ;