/*
 * The contents of this file are subject to the terms 
 * of the Common Development and Distribution License 
 * (the License).  You may not use this file except in
 * compliance with the License.
 * 
 * You can obtain a copy of the license at 
 * https://glassfish.dev.java.net/public/CDDLv1.0.html or
 * glassfish/bootstrap/legal/CDDLv1.0.txt.
 * See the License for the specific language governing 
 * permissions and limitations under the License.
 * 
 * When distributing Covered Code, include this CDDL 
 * Header Notice in each file and include the License file 
 * at glassfish/bootstrap/legal/CDDLv1.0.txt.  
 * If applicable, add the following below the CDDL Header, 
 * with the fields enclosed by brackets [] replaced by
 * you own identifying information: 
 * "Portions Copyrighted [year] [name of copyright owner]"
 * 
 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
 */

package com.sun.enterprise.admin.server.core.mbean.config;

import java.text.CharacterIterator;
import java.text.StringCharacterIterator;
import java.util.Hashtable;
import java.lang.reflect.Method;
import java.util.Iterator;      
import java.util.Enumeration;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

//JMX imports
import javax.management.*;

//Config imports
import com.sun.enterprise.instance.InstanceEnvironment;
import com.sun.enterprise.config.ConfigException;
//import com.sun.enterprise.config.ConfigNode;
import com.sun.enterprise.config.serverbeans.ServerXPathHelper;
import com.sun.enterprise.config.serverbeans.ServerTags;
import com.sun.enterprise.config.ConfigContext;
import com.sun.enterprise.config.ConfigFactory;
import com.sun.enterprise.config.ConfigBean;
import com.sun.enterprise.config.ConfigBeansFactory;
import com.sun.enterprise.config.serverbeans.ElementProperty;

//Admin imports
import com.sun.enterprise.admin.AdminContext;
import com.sun.enterprise.admin.server.core.mbean.config.naming.ConfigMBeanNamingInfo;

import com.sun.enterprise.admin.server.core.mbean.meta.MBeanEasyConfig;
import com.sun.enterprise.admin.common.constant.AdminConstants;
import com.sun.enterprise.admin.common.constant.ConfigAttributeName;
import com.sun.enterprise.admin.common.exception.MBeanConfigException;
//import com.sun.enterprise.admin.server.core.mbean.config.naming.ConfigMBeanNamingInfo;

//i18n import
import com.sun.enterprise.util.i18n.StringManager;


/****************************************************************************************************************
 * This base admin Dynamic MBean class provides access to ConfigNode's elements attributes according to attribute
 * descriptions provided by child class.
 */
public class ConfigMBeanBase extends AdminBase //implements DynamicMBean
{
    public static final Logger sLogger = Logger.getLogger(AdminConstants.kLoggerName);
    private final static String MSG_BASE_GET_ATTRIBUTE      = "mbean.config.base_get_attribute";
    private final static String MSG_BASE_SET_ATTRIBUTE      = "mbean.config.base_set_attribute";
    private final static String MSG_BASE_GOT_ATTRIBUTE      = "mbean.config.base_got_attribute";
    private final static String MSG_BASE_GET_PROPERTY       = "mbean.config.base_get_property";
    private final static String MSG_BASE_SET_PROPERTY       = "mbean.config.base_set_property";
    private final static String MSG_BASE_GET_DEF_ATTR_VALUE = "mbean.config.get_def_attr_value";
    private final static String MSG_GET_CONFBEANBYXPATH     = "mbean.config.get_confbeanbyxpath";
    private final static String MSG_LOG_CONF_CTX            = "mbean.config.log_config_id";
    public static final char ATTRIBUTE_CHAR =  '@'; //attribute name prefix
    public static final char ALLOWS_EMPTY_CHAR =  '@'; //"allows empty value" prefix symbol (can be only after ATTRIBUTE_CHAR)
    public static final String ATTRIBUTE =  ""+ATTRIBUTE_CHAR; //attribute name prefix
    public static final String E_ATTRIBUTE =  "" + ATTRIBUTE_CHAR + ALLOWS_EMPTY_CHAR; //"allows empty value" attribute name prefix
    public static final String PROPERTY =  ServerTags.ELEMENT_PROPERTY+ ServerXPathHelper.XPATH_SEPARATOR+ATTRIBUTE; //property name prefix
    public static final String E_PROPERTY =  ServerTags.ELEMENT_PROPERTY+ ServerXPathHelper.XPATH_SEPARATOR+E_ATTRIBUTE; //property name prefix
    public static final String ELEMENT_CONTENT   =  "";  //element content prefix
    public static final String PSEUDO_ATTR_DESCRIPTION = ServerTags.DESCRIPTION;

    private AdminContext        m_AdminContext;
    private ConfigContext       m_configContext;
    private String              m_BasePath; //target root-node
    private Hashtable           m_Attrs;
    private MBeanInfo           m_MBeanInfo;
    private String              m_ServerInstanceName;
    private ConfigMBeanNamingInfo   m_MBeanNamingInfo;
    
	// i18n StringManager
	private static StringManager localStrings =
		StringManager.getManager( ConfigMBeanBase.class );

  
    /****************************************************************************************************************
     * Binds MBean with Config Bean 
     * @param instanceName instance name.
     *  @throws MbeanConfigException exception if the param parsing is not successful
     */
    public void initialize(ConfigMBeanNamingInfo namingInfo) throws MBeanConfigException
    {
        String instanceName = namingInfo.getServerInstanceName();
        ConfigContext configContext;
        if (m_AdminContext != null) {
            configContext = m_AdminContext.getAdminConfigContext();
        } else {
            try
            {
                InstanceEnvironment instanceEnvironment = new InstanceEnvironment(instanceName);
                //String fileUrl  = instanceEnvironment.getConfigFilePath();
			    /*Everything should be set in the backup file*/
			    String fileUrl  = instanceEnvironment.getConfigFilePath();
                configContext   = ConfigFactory.createConfigContext(fileUrl);
            }
            catch(ConfigException e)
            {
                //e.printStackTrace();
    			String msg = localStrings.getString( "admin.server.core.mbean.config.error_locating_server_node", e.getMessage() );
                throw new MBeanConfigException( msg );
            }
        }
        
        sLogger.log(Level.FINEST, MSG_LOG_CONF_CTX, new Object[] {
                this.getClass().getName(), new Long(configContext.hashCode())});
        m_ServerInstanceName = instanceName;
        m_configContext = configContext;
        m_MBeanNamingInfo = namingInfo;
        m_BasePath = namingInfo.getXPath();
    }
    /*
     Binds MBean with Config Bean 
     **/
    public void initialize(String mbeanType, String[] locations) throws MBeanConfigException
    {
        ConfigMBeanNamingInfo namingInfo;
        try
        {
            namingInfo = new ConfigMBeanNamingInfo(mbeanType, locations);
        }
        catch (MBeanConfigException mce)
        {
            throw mce;
        }
        catch (Exception e)
        {
            throw new MBeanConfigException(e.getMessage());
        }
        
        initialize(namingInfo);
    }

    /*
     Binds MBean with Config Bean 
     **/
    public void initialize(String dottedName) throws MBeanConfigException
    {
        ConfigMBeanNamingInfo namingInfo;
        try
        {
            namingInfo = new ConfigMBeanNamingInfo(dottedName);
        }
        catch (MBeanConfigException mce)
        {
            throw mce;
        }
        catch (Exception e)
        {
            throw new MBeanConfigException(e.getMessage());
        }
        
        initialize(namingInfo);
    }

    /*
     Binds MBean with Config Bean 
     **/
    public void initialize(ObjectName objectName) throws MBeanConfigException
    {
        ConfigMBeanNamingInfo namingInfo;
        try
        {
            namingInfo = new ConfigMBeanNamingInfo(objectName);
        }
        catch (MBeanConfigException mce)
        {
            throw mce;
        }
        catch (Exception e)
        {
            throw new MBeanConfigException(e.getMessage());
        }
        
        initialize(namingInfo);
    }

    
    
    /****************************************************************************************************************
     * Sets Config MBean description data (this method usually is calling from the child class constructor)
     * @param attrsMapList array of attribute mapping descriptions, each subarray represents one attribute by string pairs:
     * <br> <b>{ &ltexternal name>, &ltpath to value> }</b> for each attribute, where
     * <ul> <b>&ltexternal name></b> defines name identifying value in <code>getAttribute()/SetAttribute()</code> methods;
     * <br> <b>&ltpath to value></b> is relative path to value from the base node in XPath notation;
     * </ul> Example (attribute value):
     * <br>   ORBListener port attribute in orblistener node can be described as
     * <ul>     <b>{ "port", "@port" }</b>
     * </ul>
     * If entire description for attribute is missing, then <code>getAttribute()/SetAttribute()</code> treats this situation the same way as
     * it was described as <b>{"@&ltattrName>]","&ltattrName>"}</b>.
     * </ul> Examples (element value):
     * <ul>     <b>{ "about", "description[@type='thisInstance')]" }</b> //this example for value of element with tag 'description' ans attribute 'type' equales to 'myInstance'
     * <br>     <b>{ "about", "description" }</b> //this example for value of <b>unique</b> element with tag 'description'
     * </ul>
     * @param attrDescriptions String array of MBean attribute descriptions in MBeanEasyConfig format.
     * @param operDescriptions String array of MBean operations descriptions in MBeanEasyConfig format.
     *  @throws MBeanConfigException exception if the param parsing is not successful
     */
    public void setDescriptions(String attrsMapList[][], String[] attrDescriptions, String[] operDescriptions) throws MBeanConfigException
    {
        m_Attrs = createAttrsDescriptors(attrsMapList);
        MBeanEasyConfig easyConfig = new MBeanEasyConfig(getClass(), attrDescriptions, operDescriptions, null);
        m_MBeanInfo = easyConfig.getMBeanInfo();
    }
    
    public void setAdminContext(AdminContext adminContext) {
        m_AdminContext = adminContext;
    }

    public AdminContext getAdminContext() {
        return m_AdminContext;
    }

    /****************************************************************************************************************
     */
    public ConfigContext getConfigContext()
    {
        return m_configContext;
    }
    
    /****************************************************************************************************************
     */
    public String getServerInstanceName()
    {
        return m_ServerInstanceName;
    }
    
    /****************************************************************************************************************
     */
    public ConfigMBeanNamingInfo getConfigMBeanNamingInfo()
    {
        return m_MBeanNamingInfo;
    }
    
    /****************************************************************************************************************
   Get path to base node; all attributes pathes are defined relatively this node.
   @return XPath style path to the ConfigNode object of node which is used as base for locationg of the attributes.
     */
    public String getBasePath()
    {
        return m_BasePath;
    }
    
    /****************************************************************************************************************
   Get base node Config Bean;
   @return ConfigBean related to the ConfigNode object of node which is used as base for locationg of the attributes.
     */
    public ConfigBean getBaseConfigBean()
    {
        try
        {
           return getConfigBeanByXPath(m_BasePath);
        }
        catch (Exception e)
        {
           return null;
        }
    }
    
    /****************************************************************************************************************
      Get <code>MBeanInfo</code> MBean description object (required dynamic MBean method).
      @return <code>MBeanInfo</code> description object for configurable MBean;
     */
    public MBeanInfo getMBeanInfo()
    {
        return m_MBeanInfo;
    }
    /****************************************************************************************************************
      Get <code>ConfigBean</code>  object which contains correspondent attribute.
      @param externalName external name of the attribute
      @return <code>ConfigBean</code>  object which contains correspondent attribute
     */
    public ConfigBean getConfigBean(String externalName)
    {
        //this method can be overriten in child class (temporary until Config static method getBeanByXPath will be ready)
        AttrDescriptor descr = getDescriptor(externalName);
        try
        {
            return descr.getConfigBean();
        }
        catch (Exception e)
        {
            return null;
        }
    }
    
    /****************************************************************************************************************
      Get <code>ConfigBean</code>  object which binded to ConfigNode with given XPath.
      @param xPath XPath to ConfigNode
      @return <code>ConfigBean</code>  object which linked to ConfigNode with given XPath.
     */
    public ConfigBean getConfigBeanByXPath(String xPath) throws ConfigException
    {
        sLogger.log(Level.FINEST, MSG_GET_CONFBEANBYXPATH, xPath);
        return ConfigBeansFactory.getConfigBeanByXPath(m_configContext, xPath);
    }
    
    /****************************************************************************************************************
     * Gets MBean's attribute value.
     * @param externalName the MBean's attribute name.
     * @return The value of the attribute retrieved.
     *  @throws MBeanException exception
     *  @throws AttributeNotFoundException exception
     */
    public Object getAttribute(String externalName)  throws MBeanException,AttributeNotFoundException
    {
        sLogger.log(Level.FINEST, MSG_BASE_GET_ATTRIBUTE, externalName);
        MBeanAttributeInfo ai = getAttrInfo(externalName);
        boolean isProperty = externalName.startsWith(ConfigAttributeName.PROPERTY_NAME_PREFIX);


        if(ai==null && !isProperty)
        {
			String msg = localStrings.getString( "admin.server.core.mbean.config.attribute_not_defined", externalName );
			throw new AttributeNotFoundException( msg );
        }
        if(ai!=null && !ai.isReadable())
        {
			String msg = localStrings.getString( "admin.server.core.mbean.config.attribute_not_readable", externalName );
			throw new AttributeNotFoundException( msg );
        }

        
        
        try
        {
        if(isProperty)
        {
            // get property value
            return getPropertyElementValue(externalName.substring(ConfigAttributeName.PROPERTY_NAME_PREFIX.length()));
        }
        else
        {
            AttrDescriptor descr = getDescriptor(externalName);
            ConfigBean  bean = getConfigBean(externalName);
            String value = null;
            if(descr.isElement())
            {
                //it looks that now we have no Elements with values
                //value = descr.getNode().getContent();
				String msg = localStrings.getString( "admin.server.core.mbean.config.getattribute_not_implemented_for_xml" );
                throw new MBeanException(new MBeanConfigException( msg ));
            }
            else
            {
                value = bean.getRawAttributeValue(descr.getAttributeName());

                //value = descr.getNode().getRawAttributeValue(descr.getAttributeName());
            }
            sLogger.log(Level.FINEST, MSG_BASE_GOT_ATTRIBUTE, new Object[]{externalName,value, MBeanEasyConfig.convertStringValueToProperType(value, ai.getType())});
            return MBeanEasyConfig.convertStringValueToProperType(value, ai.getType());
        }
        }
        catch (MBeanConfigException e)
        {
			String msg = localStrings.getString( "admin.server.core.mbean.config.getattribute_attribute_exception", externalName, e.getMessage() );
            throw new MBeanException(new MBeanConfigException( msg ));
        }

 }
    
    /****************************************************************************************************************
     * Gets MBean's property value.
     * @param externalName the MBean's property name.
     * @return The value of the property retrieved.
     *  @throws MBeanException exception
     *  @throws AttributeNotFoundException exception
     */
    public Object getPropertyElementValue(String propertyName)  throws MBeanException,AttributeNotFoundException
    {
        sLogger.log(Level.FINEST, MSG_BASE_GET_PROPERTY, propertyName);

        ConfigBean baseBean = getBaseConfigBean();
        Class cl = baseBean.getClass();
        ElementProperty prop;
        try
        {
           Method method = cl.getDeclaredMethod("getElementPropertyByName", new Class[]{Class.forName("java.lang.String")});
           prop = (ElementProperty)method.invoke(baseBean, new Object[]{propertyName});
        }
        catch (Exception e)
        {
			String msg = localStrings.getString( "admin.server.core.mbean.config.getattribute.undefined_properties_in_base_element", propertyName );
            throw new MBeanException(new MBeanConfigException( msg ));
        }
        if(prop==null) {
			String msg = localStrings.getString( "admin.server.core.mbean.config.getattribute_properties_not_found_in_base_element", propertyName );
            throw new MBeanException(new MBeanConfigException( msg ));
		}
        return prop.getValue();
    }
    /****************************************************************************************************************
     * Sets MBean's property value.
     * @param attr The identification of the property to be set and the value it is to be set to.
     *  @throws MBeanException exception
     *  @throws AttributeNotFoundException exception
     */
    public void setPropertyElementValue(Attribute attr, boolean bAllowsEmptyValue)  throws MBeanException,AttributeNotFoundException
    {
        String propertyName = attr.getName();
        String value = (String)attr.getValue();
        sLogger.log(Level.FINEST, MSG_BASE_SET_PROPERTY, new Object[]{propertyName, value});
        
        ConfigBean baseBean = getBaseConfigBean();

        Class cl = baseBean.getClass();
        ElementProperty prop;
        try
        {
           Method method = cl.getDeclaredMethod("getElementPropertyByName", new Class[]{Class.forName("java.lang.String")});
           prop = (ElementProperty)method.invoke(baseBean, new Object[]{propertyName});
        }
        catch (Exception e)
        {
			String msg = localStrings.getString( "admin.server.core.mbean.config.setattribute_undefined_properties_in_base_element", propertyName );
            throw new MBeanException(new MBeanConfigException( msg ));
        }
        if(prop==null && value!=null && (bAllowsEmptyValue || !value.equals("")))
        {
            prop = new ElementProperty();
            prop.setName(propertyName);
            prop.setValue(value);
            try
            {
                Method method = cl.getDeclaredMethod("addElementProperty", new Class[]{prop.getClass()});
                method.invoke(baseBean, new Object[]{prop});
            }
            catch (Exception e)
            {
				String msg = localStrings.getString( "admin.server.core.mbean.config.setproperty_invoke_error", propertyName );
                throw new MBeanException(new MBeanConfigException( msg ));
            }
        }
        else
        {
            if(value==null || (!bAllowsEmptyValue && value.equals("")))
            {
                try
                {
                    Method method = cl.getDeclaredMethod("removeElementProperty", new Class[]{prop.getClass()});
                    method.invoke(baseBean, new Object[]{prop});
                }
                catch (Exception e)
                {
					String msg = localStrings.getString( "admin.server.core.mbean.config.setproperty_could_not_remove_propery", propertyName );
                    throw new MBeanException(new MBeanConfigException( msg ));
                }
            }
            else
                prop.setValue(value);
        }
        
        try
        {
            m_configContext.flush();
        }
        catch (ConfigException e)
        {
            throw new MBeanException(new MBeanConfigException(e.getMessage()));
        }
    }
    

    /****************************************************************************************************************
     * Gets MBean's attribute default value or null (if def value is not defined).
     * @param externalName the MBean's attribute name.
     * @return The default value of the attribute retrieved null, if def value is not defined.
     *  @throws MBeanException exception
     *  @throws AttributeNotFoundException exception
     */
    public Object getDefaultAttributeValue(String externalName)  throws AttributeNotFoundException,MBeanConfigException
    {
        sLogger.log(Level.FINEST, MSG_BASE_GET_DEF_ATTR_VALUE, externalName);
        MBeanAttributeInfo ai = getAttrInfo(externalName);
        if(ai==null) {
			String msg = localStrings.getString( "admin.server.core.mbean.config.getdefaultattribute_undefined_attribute", externalName );
            throw new AttributeNotFoundException( msg );
		}
//        try
        {
            AttrDescriptor descr = getDescriptor(externalName);
            String value = descr.getDefaultValue();
            return MBeanEasyConfig.convertStringValueToProperType(value, ai.getType());
        }
/*        catch (ConfigException e)
        {
			String msg = localStrings.getString( "admin.server.core.mbean.config.getdefaultattributevalue_exception_for_externalname_basexpath", externalName, m_BasePath, e.getMessage() );
            throw new MBeanException(new ConfigException( msg ));
        } 
*/
    }

    /****************************************************************************************************************
     * Sets MBean's attribute value.
     * @param attr The identification of the attribute to be set and the value it is to be set to.
     *  @throws MBeanException exception
     *  @throws AttributeNotFoundException exception
     */
    public void setAttribute(Attribute attr)  throws MBeanException,AttributeNotFoundException
    {
        String externalName = attr.getName();
        Object value = attr.getValue();
        sLogger.log(Level.FINEST, MSG_BASE_SET_ATTRIBUTE, new Object[]{externalName, value});
        MBeanAttributeInfo ai = getAttrInfo(externalName);
        boolean isProperty = externalName.startsWith(ConfigAttributeName.PROPERTY_NAME_PREFIX);
        if(ai==null && !isProperty)
        {
			String msg = localStrings.getString( "admin.server.core.mbean.config.setattribute_undefined_attribute", externalName );
            throw new AttributeNotFoundException( msg );
        }
        if(ai!=null && !ai.isWritable())
        {
			String msg = localStrings.getString( "admin.server.core.mbean.config.setattribute_attribute_not_writable", externalName );
            throw new MBeanException(new MBeanConfigException( msg ));
        }
        
        try
        {
            if(isProperty)
            {
                boolean bAllowsEmptyValue = true;
                if(ai!=null)
                {
                    AttrDescriptor descr = getDescriptor(externalName);
                    if(descr!=null)
                    {
                       bAllowsEmptyValue = descr.isEmptyValueAllowed();
                    }
                }
                // set property value
                setPropertyElementValue(new Attribute(externalName.substring(ConfigAttributeName.PROPERTY_NAME_PREFIX.length()), value), bAllowsEmptyValue);
                return;
            }
            else
            { //normal attribute
                // check type (now only for exception)
// left check for Verifyer now (bug #4725686) 
//                MBeanEasyConfig.convertStringValueToProperType(value.toString(), ai.getType());

                AttrDescriptor descr = getDescriptor(externalName);
                ConfigBean  bean = getConfigBean(externalName);
                if(descr.isElement())
                {
                    //it looks that now we have no Elements with values
                    //bean.set???Value(descr.getAttributeName(), value.toString());
					String msg = localStrings.getString( "admin.server.core.mbean.config.getattribute_not_implemented_for_xml" );
                    throw new MBeanException(new MBeanConfigException( msg ));

    //                descr.getNode().setContent(value.toString());
    //                m_configContext.flush();
                }
                else
                {

                    //descr.getNode().setAttribute(descr.getAttributeName(), value.toString());
                    if(value==null || (value.equals("") && !descr.isEmptyValueAllowed()) )
                       bean.setAttributeValue(descr.getAttributeName(), null);
                    else
                       bean.setAttributeValue(descr.getAttributeName(), value.toString());
                    m_configContext.flush();
                }
            }
            
        }
//        catch (MBeanConfigException mce)
//        {
//			String msg = localStrings.getString( "admin.server.core.mbean.config.setattribute_attribute_exception",  externalName, mce.getMessage() );
//            throw new MBeanException(new MBeanConfigException( msg ));
//        }
        catch (ConfigException e)
        {
			String msg = localStrings.getString( "admin.server.core.mbean.config.setAttribute_exception_for_externalname_basexpath", externalName, m_BasePath, e.getMessage() );
            throw new MBeanException(new MBeanConfigException( msg ));
        }
    }
    
    /****************************************************************************************************************
     * Gets MBean's attribute values.
     * @param attributeNames A list of the attributes names to be retrieved.
     * @return The list of attributes retrieved.
     */
    public AttributeList getAttributes(String[] attributeNames)
    {
        AttributeList attrs = new AttributeList();
        for(int i=0; i<attributeNames.length; i++)
        {
//            try
            {
                if(attributeNames[i].length()==0)
                {
                    String[] names = getAllAttributeNames();
                    if(names!=null)
                        attrs.addAll(getAttributes(names));
                }
                else
                {
                    if(attributeNames[i].equals(ConfigAttributeName.PROPERTY_NAME_PREFIX))
                    {
                        String[] names = getAllPropertyNames(true);
                        if(names!=null)
                            attrs.addAll(getAttributes(names));
                    }
                    else
                    {
                        try
                        {
                            Object value  = getAttribute(attributeNames[i]);
                            attrs.add(new Attribute(attributeNames[i], value));
                        }
                        catch (MBeanException ce)
                        {
                            attrs.add(new Attribute(attributeNames[i], null));
                        }
                        catch (AttributeNotFoundException ce)
                        {
                            attrs.add(new Attribute(attributeNames[i], null));
                        }
                        catch (NullPointerException npe) //ConfigBean returns this exception by many reasons
                        {
                            attrs.add(new Attribute(attributeNames[i], null));
                        }
                    }
                }
            }
//            catch (Throwable t)
//            {
//            }
        }
        return attrs;
    }
    
    /****************************************************************************************************************
     * Sets the values of several MBean's attributes.
     * @param attrList A list of attributes: The identification of the attributes to be set and the values they are to be set to.
     * @return The list of attributes that were set, with their new values.
     */
    public AttributeList setAttributes(AttributeList attrList)
    {
        AttributeList attrs = new AttributeList();
        Iterator it = attrList.iterator();
        while (it.hasNext())
        {
            try
            {
                Attribute attribute = (Attribute) it.next();
                setAttribute(attribute);
                attrs.add(attribute);
            }
            catch (MBeanException mbe)
            {
            }
            catch (AttributeNotFoundException anfe)
            {
            }
            catch (NullPointerException npe)
            {
            }
        }
        return attrs;
    }
    /**
    Every resource MBean should override this method to execute specific
    operations on the MBean.
     */
    public Object invoke(String methodName, Object[] methodParams,
    String[] methodSignature) throws MBeanException, ReflectionException
    {
        //return null;
        /*
         * New for 8.0
        */
        return ( super.invoke(methodName, methodParams, methodSignature) );
    }
    
    //****************************************************************************************************************
    private Hashtable createAttrsDescriptors(String[][] attrs) throws MBeanConfigException
    {
        Hashtable ht = new Hashtable();
        if (attrs != null)
        {
            for(int i=0; i<attrs.length; i++)
            {
                ht.put(attrs[i][0], 
                       new AttrDescriptor(attrs[i][1]));
            }
        }
        return ht;
    }
    
    //****************************************************************************************************************
    private AttrDescriptor getDescriptor(String externalName)
    {
        return (AttrDescriptor)m_Attrs.get(externalName);
    }
    
    // THE FOLLOWING METHOD IS LUDO's CODE
    /**
     * Convert a DTD name into a bean name:
     *
     * Any - or _ character is removed. The letter following - and _
     * is changed to be upper-case.
     * If the user mixes upper-case and lower-case, the case is not
     * changed.
     * If the Word is entirely in upper-case, the word is changed to
     * lower-case (except the characters following - and _)
     * The first letter is always upper-case.
     */
    public static String convertName(String name)
    {
        CharacterIterator  ci;
        StringBuffer    n = new StringBuffer();
        boolean    up = true;
        boolean    keepCase = false;
        char    c;
        
        ci = new StringCharacterIterator(name);
        c = ci.first();
        
        // If everything is uppercase, we'll lowercase the name.
        while (c != CharacterIterator.DONE)
        {
            if (Character.isLowerCase(c))
            {
                keepCase = true;
                break;
            }
            c = ci.next();
        }
        
        c = ci.first();
        while (c != CharacterIterator.DONE)
        {
            if (c == '-' || c == '_')
                up = true;
            else
            {
                if (up)
                    c = Character.toUpperCase(c);
                else
                    if (!keepCase)
                        c = Character.toLowerCase(c);
                n.append(c);
                up = false;
            }
            c = ci.next();
        }
        return n.toString();
    }
    
    //****************************************************************************************************
    private MBeanAttributeInfo getAttrInfo(String attrName)
    {
        if(attrName==null || m_MBeanInfo==null)
            return null;
        
        MBeanAttributeInfo[] ai = m_MBeanInfo.getAttributes();
        if(ai!=null)
            for(int i=0; i<ai.length; i++)
            {
                String name = ai[i].getName();
                if(attrName.equals(ai[i].getName()))
                    return ai[i];
            }
        return null;
    }
    
    //****************************************************************************************************
    private String[] getAllAttributeNames()
    {
        if(m_Attrs==null )
            return null;
        Enumeration keys = m_Attrs.keys();   
        ArrayList list = new ArrayList();
        while(keys.hasMoreElements())
        {
            list.add(keys.nextElement());
        }
        return (String[])list.toArray(new String[list.size()]);
    }

    //****************************************************************************************************
    private String[] getAllPropertyNames(boolean bAddPropertyPrefix)
    {

        ConfigBean baseBean = getBaseConfigBean();
        Class cl = baseBean.getClass();
        ElementProperty[] props;
        try
        {
           Method method = cl.getDeclaredMethod("getElementProperty", null);
           props = (ElementProperty[])method.invoke(baseBean, null);
           String[] names = new String[props.length];
           for(int i=0; i<props.length; i++)
           {
               if(bAddPropertyPrefix)
                   names[i] = ConfigAttributeName.PROPERTY_NAME_PREFIX+props[i].getName();
               else
                   names[i] = props[i].getName();
           }
           return names;
        }
        catch (java.lang.NoSuchMethodException nsme)
        {
            return null;
        }
        catch (java.lang.IllegalAccessException iae)
        {
            return null;
        }
        catch (java.lang.reflect.InvocationTargetException ite)
        {
            return null;
        }
    }

    //****************************************************************************************************
    /**
     * This method allows to append another bean attributes (usually child ones) to base attributes;
     * so for MBean user they all will be as base 
     */
    static Object[] MergeAttributesWithAnotherMbean(
             String[][] maplist1, String[] attributes1,
             String[][] maplist2, String[] attributes2,
             String relativeXPath2, String attrNamesPrefix2)
    {
        int size = 0;
        if(maplist1!=null)
            size += maplist1.length;
        if(maplist2!=null)
            size += maplist2.length;
        String[][] new_maplist  = new String[size][];
        String[] new_attributes = new String[size];
        String[] mapelem;
        int i = 0;
        if(maplist1!=null)
            for(i=0; i<maplist1.length; i++)
            {
                new_maplist[i] = (String[])maplist1[i].clone();
                new_attributes[i] = attributes1[i];
            }
        if(maplist2!=null)
            for(int j=0; j<maplist2.length; j++)
            {
                mapelem = (String[])maplist2[j].clone();
                new_attributes[i+j] = attributes2[j];
                if(attrNamesPrefix2!=null)
                {
                   mapelem[0] = attrNamesPrefix2 + mapelem[0];
                   new_attributes[i+j] = attrNamesPrefix2 + attributes2[j].trim();
                }
                mapelem[1] = relativeXPath2 + "/" + mapelem[1];
                new_maplist[i+j] = mapelem;
            }
        return new Object[]{new_maplist, new_attributes};
        
    }

    //****************************************************************************************************
    private static Class getConfigBeanClass(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;
        }
    }

    /** Abstract method that subclasses have to implement. This is the way for
     * invoke method to work, through reflection.
     */
    protected Class getImplementingClass() {
        return ( this.getClass() );
    }
    
    /** Reflection requires the implementing object.  */
    protected Object getImplementingMBean() {
        return ( this );
    }    

     //****************************************************************************************************************
    private class AttrDescriptor
    {
        public String m_xPath = "";
        public String m_attributeName;
        public boolean m_bAllowsEmptyValue = false;
        
        //*******************************************************************************************************
        public AttrDescriptor(String description) throws MBeanConfigException
        {
            try
            {
                //first investigate: is it element value or attriibute value
                int lastSlashIdx = description.lastIndexOf(ServerXPathHelper.XPATH_SEPARATOR);
                if(description.charAt(lastSlashIdx+1)==ATTRIBUTE_CHAR) 
                { //element's attribute
                    if(description.charAt(lastSlashIdx+2)==ALLOWS_EMPTY_CHAR)
                    {
                        m_attributeName = description.substring(lastSlashIdx+3);
                        m_bAllowsEmptyValue = true;
                    }
                    else
                       m_attributeName = description.substring(lastSlashIdx+2); 
                    
                    if(lastSlashIdx>0)
                        m_xPath = description.substring(0,lastSlashIdx);
                }
                else
                { //element's content
                    m_attributeName = null; //not attribute
                }
            }
            catch (Throwable e)
            {
				String msg = localStrings.getString( "admin.server.core.mbean.config.attrdescriptor_constructor_exception", description, e.getMessage() );
                throw new MBeanConfigException( msg );
            }
        }
        
        //*******************************************************************************************************
        public String getAttributeName()
        {
            return m_attributeName;
        }
        
        //*******************************************************************************************************
        public String getXPath()
        {
            if(m_BasePath==null || m_BasePath.length()==0 || 
               m_xPath.indexOf(ServerXPathHelper.XPATH_SEPARATOR)==0)
                return m_xPath;
            if(m_xPath.length()==0)
               return m_BasePath;
            return m_BasePath + ServerXPathHelper.XPATH_SEPARATOR + m_xPath;
        }
        
        //*******************************************************************************************************
        public boolean isEmptyValueAllowed()
        {
            return m_bAllowsEmptyValue;
        }
        //*******************************************************************************************************
        public ConfigBean getConfigBean() throws Exception
        {
            return getConfigBeanByXPath(getXPath());
        }
        
        //****************************************************************************************************
        public String getDefaultValue()
        {
            Class cl = getConfigBeanClass(getXPath());
            //4. initiate class and bind with the node
            try
            {
                Method method = cl.getMethod("getDefaultAttributeValue", new Class[] {Class.forName("java.lang.String")} );
                return (String)method.invoke(null, new Object[] {m_attributeName});
            }
            catch(Exception e)
            {
                return null;
            }
        }
        //*******************************************************************************************************
/*        public ConfigNode getNode() throws ConfigException
        {
            return m_configContext.exactLookup(getXPath());
        }
*/
        //*******************************************************************************************************
        public boolean isElement()
        {
            return (m_attributeName==null);
        }
    }
    
}
