// AsciiResourceSaver.java
// $Id: AsciiResourceSaver.java,v 1.5 1997/07/30 14:04:51 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.io.*;
import java.net.*;

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


public class AsciiResourceSaver implements ResourceSaver {
 
  /**
   * Properties - ascii source
   */
  public static final 
  String SOURCE_P = "w3c.tools.resources.ascii.source";

  /**
   * Properties - ascii specification
   */
  public static final
  String SPEC_P = "w3c.tools.resources.ascii.spec";


  protected String defaultClass = "w3c.tools.resources.impl.ResourceImpl";

  /**
   * Properties - Resource class to instanciated
   */
  public static final
  String CLASS_P = "w3c.tools.resources.ascii.resourceClass";

  protected Class resourceClass = null;

  /**
   * the rootdir 
   */
  protected String rootdir = null;

  /**
   * The separator to be used to parse the file into items.
   */
  protected char separator = ':';

  /**
   * Our ResourceSpace.
   */
  protected ResourceSpaceImpl space = null;

  /**
   * The ascii source
   */
  protected URL source = null;

  /**
   * The ascii specification
   */
  protected String spec = null;

  protected AsciiResourceFactory factory = null;


  protected Hashtable catalog = null;

  public Enumeration listResourceName() {
    return catalog.keys();
  }

  public void save (Resource r) 
    throws ChildNotSupportedException
  {
    // in fact save all the resources
    // but one time only. ??
    System.out.println(r.getName());
    if (catalog.get(r.getName()) == null) {
      PersistentReferenceImpl pr =
	new PersistentReferenceImpl(this.rootdir,r.getName());
      ResourceReference rr = space.getCache().getReference(pr,r);
      catalog.put(r.getName(),rr);
    }
    try {
      writeSource();
    } catch (IOException ex) {
      System.out.println(ex.getMessage());
      ex.printStackTrace();
    }
  }
  
  public ResourceReference load(PersistentReference pr, 
                                ResourceReference container)
    throws ClassNotFoundException 
  {
    // return resource from cache
    // container unused here.
    PersistentReferenceImpl prImpl = (PersistentReferenceImpl) pr;
    ResourceReference rr = (ResourceReference)catalog.get(prImpl.resourceName);
    try {
      ResourceImpl r = (ResourceImpl)rr.lock();
      r.setContainer(container);
    } finally {
      rr.unlock();
    }
    return rr;
  }
  
  public void delete (Resource r){
    catalog.remove(r.getName());
  }

  public synchronized void delete (String name, 
				   ResourceReference container) 
  {
    catalog.remove(name);
  }

 /**
  * Write back the <em>store</em> in the format it was read from.
  */

  protected synchronized void writeSource() 
    throws IOException
  {
    System.out.println("writeSource .");
    BufferedWriter writer = null;
    try {
      // Fiddle with the URL connection a little:
      if ((source.getProtocol()).equals("file")) {
	File file = new File(source.getFile());
	writer = (new BufferedWriter
		  (new FileWriter(file)));
      }
      else {
	URLConnection conn = source.openConnection();
	conn.setDoOutput(true);
	if ( conn instanceof HttpURLConnection )
	  ((HttpURLConnection) conn).setRequestMethod("PUT");
	writer = (new BufferedWriter
		  (new  OutputStreamWriter
		   (conn.getOutputStream())));
      }
      // Write back the data:
      Enumeration e = catalog.elements();
      while ( e.hasMoreElements() ) {
	ResourceReference rr = (ResourceReference) e.nextElement();
	try {
	  ResourceImpl r = (ResourceImpl) rr.lock();
	  AsciiPropertyHolder holder = 
	    (AsciiPropertyHolder) r.getPropertyHolder();
	  String l = factory.writeResource(holder.getValues());
	  writer.write(l, 0, l.length());
	  writer.newLine();
	  r.setSaved();
	} finally {
	  rr.unlock();
	}
      }
    } finally {
      if ( writer != null )
	writer.close();
    }
  }

  /**
   * Parse the input file, and build resources.
   * This method initialize the catalog of resources held by the store. The
   * store will not try to do smart things (such as loading/unloading of 
   * resoources) since it is expected that ASCII resource stores will
   * remain small (hundreds of resources at most).
   * @exception IOException If the file couldn't be read.
   */

  protected synchronized void readSource() 
    throws IOException
  {
    BufferedReader  reader  = null;
    ResourceContext context = 
      new ResourceContextImpl(space,null);
    // We assume a ':' separator, we should be more flexible here
    try {
      URLConnection  conn    = source.openConnection();
      String         line    = null;
      String         items[] = new String[16];
      int            iLen    = 0;
      reader = (new BufferedReader
		(new InputStreamReader
		 (conn.getInputStream())));
      while ((line = reader.readLine()) != null) {
	// Split the line into its components:
	int pos  = 0;
	int last = 0;
	int len  = line.length();
	iLen = 0;
	while ( pos < len ) {
	  if ( line.charAt(pos) != separator ) {
	    pos++;
	    continue;
	  }
	  if ( iLen+2 >= items.length) {
	    String ni[] = new String[items.length<<1];
	    System.arraycopy(ni, 0, items, 0, items.length);
	  }
	  items[iLen++] = line.substring(last, pos);
	  last = ++pos;
	}
	// Add last item:
	items[iLen++] = line.substring(last, pos);
	// Now create the resource:
	try {
	  Resource r = (Resource)resourceClass.newInstance(); 
	  //		  Resource r  = new ResourceImpl();
	  Object values[] = factory.readResource(items,0,iLen);
	  AsciiPropertyHolder.register(r,values);
	  String name = (String)factory.getValue(values,"name");
	  PersistentReferenceImpl pr =
	    new PersistentReferenceImpl(this.rootdir,name);
	  ResourceReference rr = space.getCache().getReference(pr,r);
	  context.setResourceReference(rr);
	  r.create(context);
	  r.init(context);
	  catalog.put(r.getName(),rr);
	} catch (IllegalAccessException  ex) {
	  System.out.println(ex.getMessage());
	  ex.printStackTrace();
	} catch (ResourceInitException ex) {
	  System.out.println(ex.getMessage());
	  ex.printStackTrace();
	} catch (Exception  ex) {
	  System.out.println(ex.getMessage());
	  ex.printStackTrace();
	}
      }
    } finally {
      if ( reader != null )
	reader.close();
    }
  }

  
  public void init(ResourceSpace space, Properties P)
    throws ResourceSaverInitException
  {
    this.space = (ResourceSpaceImpl)space;
    this.rootdir = (String)P.get(this.space.ROOTDIR_P);
    this.spec = (String)P.get(SPEC_P);
    if (spec == null) 
      throw new ResourceSaverInitException("no "+SPEC_P+" prop.");
    this.factory = new AsciiFactoryImpl(this, spec);

    String url = (String)P.get(SOURCE_P);
    if (url == null) 
      throw new ResourceSaverInitException("no "+SOURCE_P+" prop.");
    try {
      this.source = new URL(url);
    } catch (MalformedURLException ex) {
      throw new ResourceSaverInitException("Malformed url : "+url);
    }
    
    String rClass = (String)P.get(CLASS_P);
    if (rClass != null) defaultClass = rClass;
    try {
      resourceClass = Class.forName(defaultClass);
    } catch (ClassNotFoundException ex) {
      throw new ResourceSaverInitException("Class not found : "+
					   ex.getMessage());
    }
    // load all resources in cache
    try {
      readSource();
    } catch (Exception ex) {
      throw new ResourceSaverInitException("unable to read ascii source "+
					   ex.getMessage());
    }
    // init Stores
    ((AsciiResourceStore)
     (((ResourceSpaceImpl)space).getRootStore())).setSaver(this);
  }

  public PropertyHolder getPropertyHolder(Runner run, Resource r) {
    try {
      if (r instanceof ResourceContainer)
	return new PropertyHolderImpl(run,r);
      else 
	return new AsciiPropertyHolder(run, r,factory);
    } catch (Exception ex) {
      System.out.println(ex.getMessage());
      ex.printStackTrace();
      return null;
    }
  }

  public AsciiResourceSaver() {
    // load all the resources and put it in the local cache
    this.catalog = new Hashtable(23);
  }
 
}


