/*
 * The contents of this file are subject to the Mozilla Public License Version 1.1
 * (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.mozilla.org/MPL/>.
 * 
 * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT
 * WARRANTY OF ANY KIND, either express or implied. See the License for the specific
 * language governing rights and limitations under the License.
 * 
 * The Original Code is the Venice Web Communities System.
 * 
 * The Initial Developer of the Original Code is Eric J. Bowersox <erbo@silcom.com>,
 * for Silverwrist Design Studios.  Portions created by Eric J. Bowersox are
 * Copyright (C) 2002 Eric J. Bowersox/Silverwrist Design Studios.  All Rights Reserved.
 * 
 * Contributor(s): 
 */
package com.silverwrist.dynamo.app;

import java.text.*;
import java.util.*;
import com.silverwrist.util.*;
import com.silverwrist.dynamo.iface.*;
import com.silverwrist.dynamo.util.*;

class PropertySerializationSupport implements PropertySerializer, PropertySerializerRegistration
{
  /*--------------------------------------------------------------------------------
   * Static data members
   *--------------------------------------------------------------------------------
   */

  private static final Map SIMPLE_TYPE_SERIALIZE;

  private static final DateFormat s_iso8601;

  /*--------------------------------------------------------------------------------
   * Attributes
   *--------------------------------------------------------------------------------
   */

  private static Vector m_extern_calls = new Vector();

  /*--------------------------------------------------------------------------------
   * Constructor
   *--------------------------------------------------------------------------------
   */

  PropertySerializationSupport()
  { // do nothing
  } // end constructor

  /*--------------------------------------------------------------------------------
   * Internal operations
   *--------------------------------------------------------------------------------
   */

  private final String builtinSerialize(Object value)
  {
    String prefix = (String)(SIMPLE_TYPE_SERIALIZE.get(value.getClass()));
    if (prefix!=null)
      return prefix + value.toString();

    if (value instanceof Boolean)
      return (((Boolean)value).booleanValue() ? "Y" : "Z");

    if (value instanceof java.util.Date)
      return "T" + s_iso8601.format((java.util.Date)value);

    if (value instanceof TimeZone)
      return "_TZ:" + ((TimeZone)value).getID();

    if (value instanceof Language)
      return "_LANG:" + ((Language)value).getCode();

    if (value instanceof Country)
      return "_CTRY:" + ((Country)value).getCode();

    if (value instanceof OptionSet)
      return "_OS:" + ((OptionSet)value).asString();

    return null;

  } // end builtInSerialize

  private final Object extendedDecode(String s)
  {
    if (s.startsWith("_LOC:"))
    { // need to break down
      s = s.substring(5);
      int p = s.indexOf('_');
      if (p>=0)
      { // language + country, maybe + variant
	String language = s.substring(0,p);
	s = s.substring(p+1);
	p = s.indexOf('_');
	if (p>=0)
	  return new Locale(language,s.substring(0,p),s.substring(p+1));
	else
	  return new Locale(language,s);

      } // end if
      else  // just language
	return new Locale(s);

    } // end if
    else if (s.startsWith("_TZ:"))
      return TimeZone.getTimeZone(s.substring(4));
    else if (s.startsWith("_LANG:"))
      return International.get().getLanguageForCode(s.substring(6));
    else if (s.startsWith("_CTRY:"))
      return International.get().getCountryForCode(s.substring(6));
    else if (s.startsWith("_OS:"))
      return new OptionSet(s.substring(4));
    else
      return null;

  } // end extendedDecode

  /*--------------------------------------------------------------------------------
   * Implementations from interface PropertySerializer
   *--------------------------------------------------------------------------------
   */

  public String serializeProperty(Object value)
  {
    if (value==null)
      return null;

    String rc = builtinSerialize(value);
    if (rc!=null)
      return rc;

    Iterator it = m_extern_calls.iterator();
    while (it.hasNext())
    { // try the external serializers
      PropertySerializer psz = (PropertySerializer)(it.next());
      rc = psz.serializeProperty(value);
      if (rc!=null)
	return "." + rc;

    } // end while

    return null;  // cannot serialize

  } // end serializeProperty

  public Object deserializeProperty(String value)
  {
    if (value==null)
      return null;

    try
    { // the first character is the flag...
      switch (value.charAt(0))
      {
	case 'B':
	  return new Byte(value.substring(1));

	case 'C':
	  return new Character(value.charAt(1));
	
	case 'D':
	  return new Double(value.substring(1));
	  
	case 'F':
	  return new Float(value.substring(1));

	case 'I':
	  return new Integer(value.substring(1));

	case 'J':
	  return new Long(value.substring(1));

	case 'S':
	  return new Short(value.substring(1));

	case 'T':
	  return s_iso8601.parse(value.substring(1));

	case 'Y':
	  return Boolean.TRUE;

	case 'Z':
	  return Boolean.FALSE;

	case '!':
	  return value.substring(1);

	case '_':
	  return extendedDecode(value);

	case '.':
	  { // run through the external decoders
	    String s = value.substring(1);
	    Iterator it = m_extern_calls.iterator();
	    while (it.hasNext())
	    { // try the external serializers
	      PropertySerializer psz = (PropertySerializer)(it.next());
	      Object rc = psz.deserializeProperty(s);
	      if (rc!=null)
		return rc;

	    } // end while

	  } // end case
	  break;

	default:
	  break;

      } // end switch

    } // end try
    catch (ParseException e)
    { // if we threw this, we're h0s3d anyway
      return null;

    } // end catch

    return null;

  } // end deserializeProperty

  /*--------------------------------------------------------------------------------
   * Implementations from interface PropertySerializerRegistration
   *--------------------------------------------------------------------------------
   */

  public ComponentShutdown registerPropertySerializer(PropertySerializer psz)
  {
    m_extern_calls.add(psz);
    return new ShutdownVectorRemove(m_extern_calls,psz);

  } // end registerPropertySerializer

  /*--------------------------------------------------------------------------------
   * Static initializer
   *--------------------------------------------------------------------------------
   */

  static
  {
    HashMap tmp = new HashMap();
    tmp.put(Byte.class,"B");
    tmp.put(Byte.TYPE,"B");
    tmp.put(Character.class,"C");
    tmp.put(Character.TYPE,"C");
    tmp.put(Double.class,"D");
    tmp.put(Double.TYPE,"D");
    tmp.put(Float.class,"F");
    tmp.put(Float.TYPE,"F");
    tmp.put(Integer.class,"I");
    tmp.put(Integer.TYPE,"I");
    tmp.put(Long.class,"J");
    tmp.put(Long.TYPE,"J");
    tmp.put(Short.class,"S");
    tmp.put(Short.TYPE,"S");
    tmp.put(String.class,"!");
    tmp.put(StringBuffer.class,"!");
    tmp.put(Locale.class,"_LOC:");
    SIMPLE_TYPE_SERIALIZE = Collections.unmodifiableMap(tmp);

    SimpleDateFormat iso = new SimpleDateFormat("yyyyMMdd'T'HH:mm:ss");
    iso.setCalendar(new GregorianCalendar(new SimpleTimeZone(0,"UTC")));
    s_iso8601 = iso;

  } // end static initializer

} // end class PropertySerializationSupport
