// AsciiPropertyHolder.java
// $Id: AsciiPropertyHolder.java,v 1.4 1997/07/30 14:04:37 ylafon Exp $  
// (c) COPYRIGHT MIT and INRIA, 1997.
// Please first read the full copyright statement in file COPYRIGHT.html

package w3c.tools.resources.ascii;

import java.util.*;
import java.lang.reflect.*;
import java.beans.*;

import w3c.tools.resources.*;
import w3c.tools.resources.impl.*;
import w3c.tools.resources.event.*;

/**
 * This class implements attribute value holding.
 * More precisely, it supports accesses to attributes: description, read
 * and write access along with support for attribute change events.
 * <p>Note that this implementation makes no assumption as to how the
 * attribute values are fetched or stored.
 * @see AttributeHolderInspector
 */

public class AsciiPropertyHolder implements PropertyHolder, 
                                            java.lang.Cloneable
{

  /**
   * The PropertyHolder we mask
   */
  protected PropertyHolderImpl holder = null;


  /**
   * Properties.
   */
  Enumeration properties = null;

  /**
   * the factory we use.
   */
  protected AsciiResourceFactory factory = null;

  /**
   * All the resources values
   */
  protected static Hashtable resourceValues = null;

  /**
   * the values of my resource.
   */
  protected Object values[] = null;

  /**
   * The resource we are managing attributes for.
   */
  protected transient Resource target = null;
  /**
   * Attribute change listener support.
   */
  protected transient AttributeChangedListener attrListener = null;

  protected transient Runner runner = null;
    

  public PropertyHolder getClone() {
    try {
      return (AsciiPropertyHolder)clone();
    } catch (CloneNotSupportedException ex) {
      System.out.println(ex.getMessage());
      ex.printStackTrace();
      return null;
    }
  }

  /**
   * register resource and its values.
   */
  public static void register(Resource r, Object values[]){
    if (resourceValues == null) resourceValues = new Hashtable(23);
    resourceValues.put(r,values);
  }

  /**
   * Fire an attribute change event.
   * @param name The name of the attribute that has changed.
   * @param oldvalue The old value for the attribute.
   * @param newvalue The new value for that attribute.
   */

  protected void fireAttributeChangeEvent(String attr
					  , Object oldvalue
					  , Object newvalue) {
    if ( attrListener != null ) {
      AttributeChangedEvent evt = new AttributeChangedEvent(target
							    , attr
							    , oldvalue
							    , newvalue);
      attrListener.attributeChanged(evt);
    }
  }

  /**
   * Set any attribute, catching any exception.
   */

  public void protectedSetValue(String name, Object value) {
    try {
      setValue(name, value);
    } catch (Exception ex) {
      ex.printStackTrace();
      throw new InternalError("can't set \""+name+"\"");
    }
  }

  public Object protectedGetValue(String name) {
    try {
      return getValue(name);
    } catch (Exception ex) {
      ex.printStackTrace();
      throw new InternalError("can't get \""+name+"\"");
    }
  }

  /**
   * Print all the attributes holded by this implementation.
   * @return A String instance.
   */

  public String toString() {
    StringBuffer sb = new StringBuffer();
    sb.append('[');
    // List the resource's class, and its frames:
    sb.append(target.getName());
    sb.append(' ');
    sb.append(target.getClass().getName());
    // List all atttributes, and their values:
    Enumeration        e = getProperties();
    PropertyDescriptor p = null;
    try {
      while ( e.hasMoreElements() ) {
	p = (PropertyDescriptor) e.nextElement();
	sb.append(' ');
	sb.append(p.getName());
	sb.append('=');
	sb.append(getValue(p.getName()));
      }
    } catch (Exception ex) {
      throw new InternalError("unaccessible declared attribute "
			      + ((p != null) ? p.getName() : "??"));
    }
    sb.append(']');
    return sb.toString();
  }

  /**
   * Enumerate the attributes property descriptors.
   * @return An enumeration of PropertyDescriptor.
   * @see PropertyDescriptor
   */

  public Enumeration getProperties() {
    if (properties != null) return properties;
    Vector V = new Vector(20);
    Enumeration e = factory.getProperties();
    while (e.hasMoreElements()) V.addElement(e.nextElement());
    e = holder.getProperties();
    while (e.hasMoreElements()) V.addElement(e.nextElement());
    properties = V.elements();
    return properties;
  }

  public Object[] getValues() {
    // update name field (if exists)
    try {
      String name = (String)getValue("name");
      factory.setValue(values,"name",name);
    } finally {
      return values;
    }
  }

  /**
   * Get an attribute value.
   * @param name The attribute name.
   * @return The attribute value.
   * @exception IllegalAccessException If the attribute isn't supported,
   * @exception IllegalArgumentException If the provided value doesn't match
   * the attribute type.
   */

  public synchronized Object getValue(String name) 
    throws IllegalAccessException, IllegalArgumentException
  {
    Object value = null;
    try {
      value = holder.getValue(name);
      if (value == null) {
	try {
	  value = factory.getValue(this.values, name);
	} finally {
	  return value;
	}
      }
      else return value;
    } catch (IllegalAccessException ex) {
      return factory.getValue(this.values, name);
    } catch (IllegalArgumentException ex) {
      return factory.getValue(this.values, name);
    }
  }

  /**
   * Get a set of attribute values.
   * @param names The name of the attributes to fetch.
   * @return An array of attribute values.
   * @exception IllegalAccessException If one of the attribute isn't
   * supported,
   * @exception IllegalArgumentException If one of the provided value
   * doesn't match the attribute type.
   */

  public synchronized Object[] getValues(String names[]) 
    throws IllegalAccessException, IllegalArgumentException
  {
    Object values[] = null;
    try {
      values = holder.getValues(names);
      if (values == null) {
	try {
	  values = factory.getValues(this.values, names);
	} finally {
	  return values;
	}
      }
      else return values;
    } catch (IllegalAccessException ex) {
      return factory.getValues(this.values, names);
    } catch (IllegalArgumentException ex) {
      return factory.getValues(this.values, names);
    }
  }
    
  /**
   * Set an attribute value.
   * @param name The name of the attribute.
   * @param value The new value for that attribute.
   */

  public synchronized void setValue(String name, Object newvalue)
    throws IllegalAccessException, IllegalArgumentException
  {
    try {
      holder.setValue(name, newvalue);
    } catch (IllegalAccessException ex) {
      factory.setValue(this.values, name, newvalue);
    }
    //fireAttributeChangeEvent(name, oldvalue, newvalue);
  }

  /**
   * Perform a set of attribute changes atomically.
   * @param names The attribute names.
   * @param values The attribute values.
   */

  public synchronized void setValues(String names[], Object newvalues[])
    throws IllegalAccessException
  {
    try {
      holder.setValues(names, newvalues);
    } catch (IllegalAccessException ex) {
      factory.setValues(this.values, names, newvalues);
    }
    //fireAttributeChangeEvent(names[i], oldvalues[i], newvalues[i]);
  }

  /**
   * Add an attribute change listener.
   * @param l The new attribute change listener.
   */

  public void addAttributeChangedListener(AttributeChangedListener l) {
    attrListener = ResourceEventMulticaster.add(attrListener, l);
  }

  /**
   * Remove an attribute change listener.
   * @param l The listener to remove.
   */
    
  public void removeAttributeChangedListener(AttributeChangedListener l) {
    attrListener = ResourceEventMulticaster.remove(attrListener, l);
  }

  /**
   * Create an attribute holder implementation for the given resource.
   * @param target The target resource to implement attribute holding for.
   */
    
  public AsciiPropertyHolder(Runner runner, 
			     Resource target, 
			     AsciiResourceFactory factory) 
    throws Exception
  {
    this.target = target;
    this.runner = runner;
    this.factory = factory;
    Enumeration e = resourceValues.keys();
    this.values = (Object[])resourceValues.get(target);
    this.holder = new PropertyHolderImpl(runner,target);
    if (values == null)
      this.values = factory.getDefaultValues();
  }

}
