/*

 * Copyright 2001-2004 The Apache Software Foundation.

 * 

 * 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 org.jboss.axis.components.encoding;


import org.apache.commons.discovery.ResourceNameIterator;
import org.apache.commons.discovery.resource.ClassLoaders;
import org.apache.commons.discovery.resource.names.DiscoverServiceNames;
import org.jboss.axis.utils.JavaUtils;
import org.jboss.axis.utils.Messages;
import org.jboss.logging.Logger;

import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;


/**
 * Factory for XMLEncoder
 *
 * @author <a href="mailto:jens@void.fm">Jens Schumann</a>
 * @author <a href="mailto:dims@yahoo.com">Davanum Srinivas</a>
 */

public class XMLEncoderFactory
{

   private static Logger log = Logger.getLogger(XMLEncoderFactory.class.getName());


   public static final String ENCODING_UTF_8 = "UTF-8";

   public static final String ENCODING_UTF_16 = "UTF-16";

   public static final String DEFAULT_ENCODING = ENCODING_UTF_8;


   private static Map encoderMap = new HashMap();

   private static final String PLUGABLE_PROVIDER_FILENAME = "org.jboss.axis.components.encoding.XMLEncoder";


   static
   {

      encoderMap.put(ENCODING_UTF_8, new UTF8Encoder());

      encoderMap.put(ENCODING_UTF_16, new UTF16Encoder());

      try
      {

         loadPluggableEncoders();

      }
      catch (Throwable t)
      {

         String msg = t + JavaUtils.LS + JavaUtils.stackToString(t);

         log.info(Messages.getMessage("exception01", msg));

      }

   }


   /**
    * Returns the default encoder
    *
    * @return
    */

   public static XMLEncoder getDefaultEncoder()
   {

      try
      {

         return getEncoder(DEFAULT_ENCODING);

      }
      catch (UnsupportedEncodingException e)
      {

         //  as far I know J++ VMs will throw this exception

         throw new IllegalStateException(Messages.getMessage("unsupportedDefaultEncoding00", DEFAULT_ENCODING));

      }

   }


   /**
    * Returns the requested encoder
    *
    * @param encoding
    * @return
    * @throws UnsupportedEncodingException
    */

   public static XMLEncoder getEncoder(String encoding) throws UnsupportedEncodingException
   {

      XMLEncoder encoder = (XMLEncoder)encoderMap.get(encoding.toUpperCase());

      if (encoder == null)
      {

         throw new UnsupportedEncodingException(Messages.getMessage("unsupportedEncoding00", encoding));

      }

      // test local encoding so we can ensure that getBytes()

      // will not fail later

      "test".getBytes(encoder.getEncoding());

      return encoder;

   }


   /**
    * Look for file META-INF/services/org.jboss.axis.components.encoding.XMLEncoder
    * <p/>
    * in all the JARS, get the classes listed in those files and add them to
    * <p/>
    * encoders list if they are valid encoders.
    * <p/>
    * <p/>
    * <p/>
    * Here is how the scheme would work.
    * <p/>
    * <p/>
    * <p/>
    * A company providing a new encoder will jar up their encoder related
    * <p/>
    * classes in a JAR file. The following file containing the name of the new
    * <p/>
    * encoder class is also made part of this JAR file.
    * <p/>
    * <p/>
    * <p/>
    * META-INF/services/org.jboss.axis.components.encoding.XMLEncoder
    * <p/>
    * <p/>
    * <p/>
    * By making this JAR part of the webapp, the new encoder will be
    * <p/>
    * automatically discovered.
    */

   private static void loadPluggableEncoders()
   {

      ClassLoader clzLoader = XMLEncoder.class.getClassLoader();

      ClassLoaders loaders = new ClassLoaders();

      loaders.put(clzLoader);

      DiscoverServiceNames dsn = new DiscoverServiceNames(loaders);

      ResourceNameIterator iter = dsn.findResourceNames(PLUGABLE_PROVIDER_FILENAME);

      while (iter.hasNext())
      {

         String className = iter.nextResourceName();

         try
         {

            Object o = Class.forName(className).newInstance();

            if (o instanceof XMLEncoder)
            {

               XMLEncoder encoder = (XMLEncoder)o;

               encoderMap.put(encoder.getEncoding(), encoder);

            }

         }
         catch (Exception e)
         {

            String msg = e + JavaUtils.LS + JavaUtils.stackToString(e);

            log.info(Messages.getMessage("exception01", msg));

            continue;

         }

      }

   }

}