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

package w3c.tools.resources;


import java.util.*;
import java.security.Principal;
import java.security.acl.*;

import w3c.www.http.*;
import w3c.jigsaw.http.*;

public class LookupState implements Cloneable {
  private Request request ;
  private boolean is_internal  = false ;

  /**
   * The resource space in which the path is being resolved.
   */
  protected ResourceSpace space = null;
  /**
   * The path to be looked up.
   */
  protected String path[] = null;
  /**
   * The length of the path to be resolved.
   */
  protected int pathLen = 0;
  /**
   * The current lookup state within above path.
   */
  protected int index = 0;
  /**
   * Is this state meant to lookup a target for editing.
   * Some resources may act as <em>aliases</em> to other resources; this 
   * flag notifies them <strong>not</strong> to perform any aliasing.
   * @see #setEdit
   * @see #isEdit
   */
  protected boolean edit = false;
  /**
   * Permission associated with the state.
   * This permission may grant access to protected resources during lookup.
   * @see #setPermission
   * @see #getPermission
   */
  protected Permission permission = null;
  /**
   * The principal making the resolve request.
   * @see #getPrincipal
   * @see #setPrincipal
   */
  protected Principal principal = null;

  // Used by the parsePath method
  private final void addComponent(String comp) {
    if ( pathLen+1 >= path.length ) {
      String np[] = new String[path.length << 1];
      System.arraycopy(path, 0, np, 0, path.length);
      path = np;
    }
    path[pathLen++] = comp;
  }

  /**
   * Parse the given slash separated path into an  array of components.
   * This method will set the appropriate instance variables as its output.
   * @param str The string to parse.
   */

  protected void parsePath(String str) {
    int    strlen = str.length();
    Vector comps  = new Vector(8);
    int    start  = 0 ;
    int    slash  = -1 ;

  loop:
    while ( true ) {
      slash = str.indexOf ('/', start) ;
      if (slash < 0) {
	break loop;
      } else if ( slash == start ) {
	start = slash + 1;
	continue loop;
      } else if ( slash > 0 ) {
	addComponent(str.substring (start, slash));
	start = slash + 1;
	continue loop;
      } 
    }
    // Add the last component, if any:
    if ( start < strlen )
      addComponent(str.substring(start));
  }

  /**
   * Display that lookup state.
   * @return A String instance.
   */

  public String toString() {
    StringBuffer sb = new StringBuffer();
    for (int i = 0 ; i < pathLen ; i++) {
      if (i < index) {
	sb.append('+');
	sb.append(path[i]);
      } else {
	sb.append(path[i]);
      }
      sb.append(' ');
    }
    return sb.toString();
  }
	
  /**
   * Clone that current lookup state.
   * @return A copy of that lookup state.
   */

  public Object clone() {
    try {
      return super.clone();
    } catch (CloneNotSupportedException ex) {
      throw new InternalError("clone not supported in Cloneable");
    }
  }

  /**
   * Set the <em>edit</em> flag for this state.
   * @param forEdit is this state for editring the target ?
   */

  public void setEdit(boolean forEdit) {
    this.edit = forEdit;
  }

  /**
   * Is this lookup stat meant for editing the target ?
   * @return A boolean, <strong>true</strong> if lookup state is meant
   * for editing the target.
   */

  public boolean isEdit() {
    return edit;
  }

  /**
   * Associate a permission with that lookup state.
   * @param permission The permission to associate with the lookup state.
   */

  public void setPermission(Permission permission) {
    this.permission = permission;
  }

  /**
   * Get the permission associated with that lookup state.
   * @return A Permission instance, or <strong>null</strong> if not 
   * available.
   */
    
  public Permission getPermission() {
    return permission;
  }
  
  /**
   * Associate a principal with this lookup request.
   * @param principal The principal making the request.
   */

  public void setPrincipal(Principal principal) {
    this.principal = principal;
  }

  /**
   * Get the principal associated with that lookup request.
   * @return A Principal instance, or <strong>null</strong> if the request
   * is anonymous.
   */

  public Principal getPrincipal() {
    return principal;
  }

  /**
   * Are there more components to be looked up ?
   * @return A boolean.
   */

  public boolean hasMoreComponents() {
    return index < pathLen;
  }

  /**
   * Peek the next component to be looked up.
   * @return The next component to be solved.
   */

  public String peekNextComponent() {
    return ( index < pathLen ) ? path[index] : null;
  }

  /**
   * Get and consume the next component of the path being resolved.
   * @return The next component to be solved.
   * @exception NoSuchElementException If no more components are available.
   */

  public String getNextComponent() {
    return path[index++];
  }
    
  /**
   * Skip to next component.
   */

  public void nextComponent() {
    index++;
  }

  /**
   * Get this lookup state request.
   * @return An instance of Request, or <strong>null</strong> if this is 
   *    an internal request.
   */

  public final Request getRequest () {
    return request ;
  }

  /**
   * Is this lookup state object associated with a request ?
   * @return A boolean <strong>true</strong> if a request is associated.
   */

  public boolean hasRequest() {
    return (request != null) ;
  }


  /**
   * Mark this lookup state as being done internally.
   * This allows lookup methods to be more kind (for example, not throwing
   * redirections error, etc).
   */

  public void markInternal() {
    is_internal = true ;
  }
  
  /**
   * Is this lookup state internal to the server.
   * Internal lookup state may not have an associated request.
   * @return A boolean <strong>true</strong> if this is an internal request.
   */
  
  public boolean isInternal() {
    return is_internal ;
  }
  
  /**
   * Parse the given slash separated path, and create the appropriate 
   * lookup state.
   * @param path A slash separated path.
   */

  // FIXME (request)
  public LookupState(ResourceSpace space, String str) {
    this.space   = space;
    this.path    = new String[8];
    this.pathLen = 0;
    parsePath(str);
  }

  /**
   * Create a lookup state out of a parsed path.
   * @param path The parsed path as an array of (String instance) components.
   */

  public LookupState(ResourceSpace space, String path[]) {
    this.space   = space;
    this.path    = path;
    this.pathLen = path.length;
  }

}
