cocoon-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Clark C. Evans" <clark.ev...@manhattanproject.com>
Subject Present: An internal processor architecture for Cocoon2 ?
Date Sun, 26 Dec 1999 20:57:48 GMT
Stefano & Company,

About 6-9 months ago, like many of you, I was 
waving the SAX banner and exclaiming that Cocoon 
should be build on top of SAX and not DOM.

...

I've since changed my mind.  SAX is the opposite
extreme, and has other problems associated with
it namely:

  (a) the programmer must explicitly manage 
      the state of their processor.

  (b) the programmer must explicitly manage
      the storage of intermediate items
      needed by their processor.

In DOM world, neither of these are problems;
which is why Cocoon is great to program.  
Unfortunately, these two factors extract 
a price... memory and cpu usage.

Instead of running from one extreme to another,
the Cocoon group needs to devise its own interface.
A ballance between the DOM and SAX extremes,
one that can not only act "just like DOM", or
"just like SAX", but can take on various
shades of intermediate behavior.

The solution to this problem seems two fold:

(a) State management -- Programmer's perspective.

    In DOM, the programmer is put in the singular 
    "objective" context, overseeing the entire
    text at once.   This state is great for many
    situations, especially for small documents.

    In SAX, the programmer is put in the heat
    of it all and must mitigate multiple "subjective"
    contexts and make sense of it.

(b) Memory management -- Access to information

    In DOM, everything is given a random access
    interface, shielding the programmer from all
    access concerns.

    In SAX, events are presented in a sequential
    access interface, requiring the programmer to
    store what is needed for later use.

Interestingly enough, SAX is actually a hybrid.
It uses random access for attributes, and sequential
access for elements.  Further, the programmer is
put in a objective mode with respect to attributes.
Thus, SAX isn't all that "extreme" as I had 
claimed above.

...

  <tangent note="read to understand rest of proposal">

  I've been struggling with this for a while.  To visualize it, 
  I introduced a different, imaginary/experimental syntax called 
  YML, that allows for a binary choice for each node (in the 
  syntax the names "attribute" and "element" are dropped).  The 
  binary choice is the answer to the following question:

   "Should this node be gien random or sequential access?"

  I was driving the choice via a syntax distinction:

  <tag random-access >
     sequential-access
  </tag>

  In the syntax allowes for recursion in the place
  where attributes normally go.  Anyway, what you end up 
  with, is, as Rick Jelliffe states: "A self-pruning tree".

  Thus, the following YML text would be read entirely 
  into memory before control is given (DOM behavior):
   
   <root <child/> <sibling/> />

  While, in the other extreme, this YML text would be
  processed in an entirely sequential way (SAX behavor):

   <root> <child/> <grand-child/> </root>

  Of course, what the syntax shows, is that there are two
  other permutations (where we agree order does not matter):

   <root <child/>> <sibling/> </root>
           and
   <root <sibling/>> <child/> </root>

  It is this intermediate behavior that is interesting,
  and what I call ballanced.

  </tangent>

...

What is not important is that I let the "syntax" make
the choice, but that a choice, given recursively
can give a large spectrum of results.  If you answer
"random" to the question recursively, you get "DOM".
If you answer "sequential" to the question, you 
get "SAX".  If you answer "sequential" for several
layers, and then recursively answer "random" for
a given sub-tree, you have behavior much like Pyxie's
hybrid approach.

So, instead of letting syntax make the choice, you let
the application.  To make this work, the question 
is changed somewhat:

   "So, you now have been given sequential access
    to this node, would you like it saved for
    use later?"

Thus, we get what Rick would call an,

    "Application directed self-pruning tree".

This is one factor in the proposal, "memory management"

...

The other factor, is "state management".  Where DOM
has one thread of control, SAX has many.  Since one
thread of control is'nt an option any more, we have
to ask, how to we better mitigate state management?

The answer: Objects.  However, I'm less clear how
to solve this problem.  Typically, a "dispatch system"
is the common solution when stream based programming
meets objects.

Thus, I think the approach to take is to view the
processor not as a single event handler, nor as
a single object; but more of a handful of event 
driven objects.   This is probably very close to
what Ricoh has done... but I still have not looked
at their details yet.

Thus, I see a processor as a collection of 
"tag-handlers" registered for "paths" which 
they would like to handle.

  +-------------------------------------------+
  | Processor                                 |
  +-------------------------------------------+
  | time             timeTagHandler           |
  | day               dayTagHandler           |
  | task             taskTagHandler           |
  | duration     durationTagHandler           |
  |                extends integerHandler     |
  | description  descriptionTagHandler        |
  |                extends stringHandler      |
  | task/title   taskTitleTagHandler          |
  |                extends stringHandler      |
  | sheet/title  timesheetTitleHandler        |
  |                extends stringHandler      |
  +-------------------------------------------+

The tag handler's job is to take content,
perhaps a sequence of children, and to create
an application specific object from them.

These objects are then attached to the "application
directed self-pruning tree".

The handler's communicate through the tree, 
or, if they can locally produce output, by
pasing on information to the next processor.

Anyway... I'm a bit less clear on this approach,
but it does look very promising.  I'd love your
feedback on it.

...

Note:  This is an architecture for an internal processor
framework, which, I believe shoud work orthogonally with
the sitemap work...

That being said, following is a set of java interfaces
describing a possible "reduction of practice" of the
ideas presented above.   This is being done as part
of the "SML" effort, since I believe most of XML is
completely irrelevant to stream based back-end 
processors like Cocoon.

Here is the post to the SML-DEV list....

Best wishes for a happy new year!

Clark

---------- Forwarded message ----------
Date: Sun, 26 Dec 1999 14:34:48 -0500 (EST)
From: Clark C. Evans <clark.evans@manhattanproject.com>
Reply-To: sml-dev@egroups.com
To: sml-dev@egroups.com
Subject: [sml-dev] [SAI] Initial Interfaces


I spent some time designing an interface
that is as simple as I could make it
for SML (YML is one additional method).

Notes:

 0. The API contains both random acces and
    sequential access interfaces, implementing
    the "application-directed-pruning tree" 
  
 1. For the random access interface, I decided 
    against using DOM like node structure since 
    by elmininating attributes and banning 
    mixed-content, the model simplifies dramatically.
    Further, the node structure is write-once,
    reflecting the nature of back-end stream
    based processing.  A read/write access interface
    could be layered on top, of course.

 2. A SAX like interface is provided for the
    sequential access interface, only PIs, 
    attributes, and other items were stripped.
    Also the word "document" was removed, since
    I feel this introduces a bias.

 3. The API does not address namespaces.

 4. The API assumes that attributes are
    going to be discarded, but provides 
    for (a) node coloring, and (b) stream
    paritioning.  I'm not sure about these.

 5. A bulk of the code is for event dispatching.
    I found that pull processing didn't quite
    scale to a processor pipeline, and I found
    that push processing requires the user to 
    manage state.  Instead, I introduce two
    handlers, a stream handler, and a tag 
    handler.  I'd like comments here...

 6. I *really* liked the idea of having application
    specific node values for nodes that do
    not have children. (assuming mixed-content has
    been rejected for SML) --  This was a great 
    idea Paul.

 7. I used my own domain name for now, this can 
    easily be changed if there is enough support.

Your comments would be wonderful, as I was thinking
of implementating a parser for SML, and getting
the dispatch system operational.

Best Wishes,
       
Clark

//=====================================================================
//
// SaiNode                 A read-only random access structure
// SaiHandler              A sequential access event handler
// SaiPath                 Used for registering/finding tag handlers
// SaiDispatcher           A tag handler dispatch system
// SaiTagHandler           A handler interface for a single tag
// SaiBuilder              A write-once wrapper for SaiNode
// SaiTagHandlerBase       A tag handler class /w simplified interface
// SaiTagHandlerInteger    A tag handler for tags with integer content
// SaiException            A exception tunneler
// SaiMultiValueException  Used for multiple child name matches
//
//----------------------------------------------------------------------

// -----------------------------------------------
// SaiNode.java
// No warranty; no copyright -- use this as you will.
// -----------------------------------------------
package com.clarkevans.sai;
/**
 * Provides a read-only backbone tree for keeping
 * random-access processing state.
 * @version $Revision: 0.1 $ $Date: 1999/12/24 14:00:00 $
 * @author Clark C. Evans (sai@clarkevans.com)
 */
public interface SaiNode  {
  /**
   * Provides read access to the node's container.
   * @return            The node's container, which
   *                    may be a <code>SaiBrachNoce</code>
   */
  public SaiNode parent();
  /**
   * Provides read access to the node's name.
   * This may be null, if the node is the top
   * level container... hmm.
   *
   * @return            The node's name.
   */
  public String name();
  /**
   * Provides read access to the node's value.
   * A node a value or children or neither, but never both.
   * Thus, if a null is returned
   * @return            The node's value, an application
   *                    specific object.
   */
  public Object value();
  /**
   * Provides for application specific coloring or other
   * bookkeeping othogonal to the actual value of the node.
   * @return            The node's coloring, an application
   *                    specific object.
   */
  public Object color();
  /**
   * Provides access to the number of children.
   * @param  name       The name of the child requested.
   * @return            The number of children.
   */
  public SaiNode child(String name)
    throws SaiMultiValueException;
  /**
   * Provides access to the number of children.
   * @return            The number of children.
   */
  public int count();
  /**
   * Provides access to a child by position.
   * @param  i         The position (from 0) of the child requested.
   * @return           The child node requested.
   */
  public SaiNode child(int i);
}
// -----------------------------------------------
// SaiHandler.java
// No warranty; no copyright -- use this as you will.
// -----------------------------------------------
package com.clarkevans.sai;
/**
 * This is the receiver interface for a SML stream.
 * It uses the start/finish, begin/end pairing, unlike
 * David Megginson's start/end paring.
 *
 * @version $Revision: 0.1 $ $Date: 1999/12/24 14:00:00 $
 * @author Clark C. Evans (sai@clarkevans.com)
 */
public interface SaiHandler {
  /**
   * Initiate the start of the text stream.
   */
  public void start();
  /**
   *  Initiate the begining of a tag.
   *
   *  @param name       Name of the tag
   */
  public void begin(String name);
  /**
   * Dispatches character data for the active tag.
   *
   * @param  ch         A character array containing
   *                    a subset of the characters for
   *                    the text.
   * @param  start      The starting index in the array
   *                    where the text is begins.
   * @param  len        The length of the portion of
   *                    the text stored in the array.
   *
   */
  public void characters(char ch[], int start, int len)
    throws SaiException;
  /**
   * Marks the ending of the tag.
   *
   * @param name        Name of the tag
   */
  public void end(String name)
      throws SaiException;
  /**
   *  Notification of optional partitions in the child sequence.
   *  For XML, this seperates attributes from elements.
   *  For YML, it allows for dual recursion:
   *    &lt;tag numerator&gt;demoninator&lt;/tag&gt;
   *  I suppose it could be used to partition a stream of M children
   *  into N < M partitions.  This is viewed purely as syntatic sugar.
   *  Note that this method may not be called if <code>characters</code>
   *  has been called or will be called.
   */
  public void partition()
      throws SaiException;     
  /**
   * Finalize the finish of the text stream.
   */
  public void finish();
}
// -----------------------------------------------
// SaiPath.java
// No warranty; no copyright -- use this as you will.
// -----------------------------------------------
package com.clarkevans.sai;
/**
 * Provides a stack of tags for determing which
 * SaiHandler is appropriate.
 * @version $Revision: 0.1 $ $Date: 1999/12/24 14:00:00 $
 * @author Clark C. Evans (sai@clarkevans.com)
 */
public interface SaiPath  {
  /**
   * Gives the depth of the tag stack.
   * @returns   The node under construction.
   */
  public int height();
  /**
   * Provides for the tag at the given position.
   * @param   i Index of the tag requested, 0 is the
                root of the path.
   * @returns   The tag name.
   */
  public String tag(int i);   
}
// -----------------------------------------------
// SaiDispatcher.java
// No warranty; no copyright -- use this as you will.
// -----------------------------------------------
package com.clarkevans.sai;
/**
 * Provides for a dispatching implementation of the 
 * SaiHandler.
 *
 * @version $Revision: 0.1 $ $Date: 1999/12/24 14:00:00 $
 * @author Clark C. Evans (sai@clarkevans.com)
 */
public interface SaiDispatcher extends SaiHandler {
  /**
   * Registers with a given path, a handler that can be
   * cloned to respond to a particular production.  Note
   * that the handlers provided may be re-used in a pool.
   *
   * @param path        A path expression (allowing for wild cards)
   * @param handler     The handler for matches
   */
  public void register(SaiPath path, SaiTagHandler handler);
}
// -----------------------------------------------
// SaiTagHandler.java
// No warranty; no copyright -- use this as you will.
// -----------------------------------------------
package com.clarkevans.sai;
/**
 * Provides for the activity required when a node
 * is encountered.  A <code>Class</code> implementing
 * this interface is registered to <code>SaiStage</code>
 * with a given <code>SaiPath</code> expression.  If that
 * expression is encountered, than an instance of the class
 * is instantiated to handle the request.
 *
 * <p>There are two types of children, other nodes and
 * characters.  Node children are passed as an object
 * implementing <code>SaiNode</code>.   Character children
 * are passed as a sequence of character arrrays.  It is
 * expected that the characters are converted into an
 * application specific object.</p>
 *
 * @version $Revision: 0.1 $ $Date: 1999/12/24 14:00:00 $
 * @author Clark C. Evans (sai@clarkevans.com)
 */
public interface SaiTagHandler extends Cloneable {
  /**
   * Initializes the handler for a particular production.
   *
   * <p>Note that these handlers may be put in a pool, so
   * this method should initialize all local variables.</p>
   *
   * @param builder    A builder for the current node.
   * @param parent     The parent hander.
   * @param next       (optional) the next stage in procesing.
   */
  public void begin( SaiBuilder builder,
                     SaiTagHandler parent,
                     SaiHandler next )
    throws SaiException;
  /**
   * Indicates that a child was encountered.  This method
   * is exclusive with <code>characters</code>, either a
   * sequence of one or the other may be called, but never
   * a mixture of both.
   *
   * <p>The child will be garbage collected, unless either:
   * (a) <code>builder.add(child);</code> is called, adding the
   * child under this node in the current tree, or
   * (b) <code>parent.child(child);</code> is called, moving
   * processing of the child to this handler's parent.
   * Note that this child may be consumed, adding other,
   * perhaps newly created items to the tree, or creating
   * replacement items to send to <code>parent</code>.</p>
   *
   * @param  child      The child node encountered.
   */
  public void child(SaiBuilder child)
    throws SaiException;
  /**                     
   * Indicates that a sequence of character data was
   * encountered.  This method is exclusive with
   * <code>child</code>, either a sequence of one or
   * the other may be called, but never a mixture of both.
   *
   * <p>These characters must be copied if they are to be
   * saved from garbage collection, perferrably by turning
   * them into an application specific object and setting
   * the value of the current node like
   *   <code>builder.setValue(myObject);</code>
   * </p>
   *
   * <p>Under no circumstance should <code>parent.characters</code>,
   * be called, since this would violate the exclusive
   * restriction -- unless, of course, all children do
   * this -- however, this would be strange indeed.</p>
   *
   * <p>Further note, that this method may be called
   * multiple times, delivering the character stream
   * in chunks.  This is, unfortunately needed since
   * the read buffer of the implementation cannot
   * gaurentee that a tag's content won't fall on
   * a buffer boundary.</p>
   *
   * @param  ch         A character array containing
   *                    a subset of the characters for
   *                    the text.
   * @param  start      The starting index in the array
   *                    where the text is begins.
   * @param  len        The length of the portion of
   *                    the text stored in the array.
   *
   */
  public void characters(char ch[], int start, int len)
    throws SaiException;
  /**
   * Finalizes the object for garbage collection.  Since this
   * object may be kept around to build other nodes, it is
   * important to free resources kept by setting them to
   * <code>null</code>.
   *
   * <p>The current node will be garbage collected unless
   * the parent handler is notified of its existence by
   * calling <code>parent.child(builder.node());</code>.</p>
   *
   * <p>Also, if this node had <code>characters</code> events,
   * then it is important to package up the Object created and,
   * if it has not already been done, set the current node's
   * value:  <code>builder.setValue(myValue);</code>  </p>
   */
  public void end()          
      throws SaiException;
  /**
   *  Notification of optional partitions in the child bag.
   */
  public void partition()
      throws SaiException;
  /**
   *  Given a document path, this method allows the current handler,
   *  to choose a handler class (which implements SaiTagHandler) to be
   *  used for the construction of each child.  It is proper behavior
   *  to <code>return parent.resolve(path);</code> if no preference
   *  exists.  If null is returned, then the registry is checked.
   *  Note, instances of the class returned may be pooled for efficiency.
   *
   *  @param path       Stack of relevant tag names.
   *  @return           A class implementing SaiTagHandler, or null.
   */                      
  public SaiTagHandler resolve(SaiPath path);
}
// -----------------------------------------------
// SaiBuilder.java
// No warranty; no copyright -- use this as you will.
// -----------------------------------------------
package com.clarkevans.sai;
/**
 * Provides a read/write node tree for keeping
 * random-access processing state.
 * @version $Revision: 0.1 $ $Date: 1999/12/24 14:00:00 $
 * @author Clark C. Evans (sai@clarkevans.com)
 */
public interface SaiBuilder  {
  /**
   * Gives access to the partially completed node
   * @returns   The node under construction.
   */
  public SaiNode node();
  /**
   * Provides for the addition of node values.
   * @param  child      The child to be added.
   */
  public void add(SaiNode child);
  /**
   * Gives a method to set the value of the node.
   * Note: a child may have children or a value,
   * but never both.
   * @param  obj        The value of the node.
   */
  public void setValue(Object obj);
  /**
   * Gives a method to set an application specific
   * coloring object to the node.  
   * @param  obj        The application specifi value.
   */
  public void setColor(Object obj);
}
// -----------------------------------------------
// SaiTagHandlerBase.java
// No warranty; no copyright -- use this as you will.
// -----------------------------------------------
package com.clarkevans.sai;
/**
 * This provides for a base implementation of 
 * SaiTagHandler, it also gives a simpler interface,
 * in case the added complexity of the underlying
 * interface is not needed.  This can be used, as-is,
 * as the handler for building an in-memory tree.
 *
 * @version $Revision: 0.1 $ $Date: 1999/12/24 14:00:00 $
 * @author Clark C. Evans (sai@clarkevans.com)
 */
public class SaiTagHandlerBase implements SaiTagHandler {
  /**
   * Private/Simplified start tag notification.
   */
  protected void start(SaiNode me)
    throws SaiException {}
  /**
   * Private/Simplified child notification.
   * @param  child      The child node encountered.
   * @returns           <code>true</code> if the child is to
   *                    be added to this
   */
  protected boolean child(SaiNode child)
    throws SaiException { return true; }
  /**                     
   * Private/Simplified character content notification.
   * @param  text       The string containing the characters
   * @returns           The object which is to become this
   *                    node's value.
   */
  protected Object characters(String text)
    throws SaiException { return text; }
  /**
   * Private/Simplified finish tag notification.
   */
  protected void finish()
    throws SaiException {}
  //
  // Private variables
  // 
  protected SaiBuilder    builder;
  protected SaiTagHandler parent;
  protected SaiHandler    next;
  protected StringBuffer  buff;
  //
  // Implemention of public methods.
  //
  public void begin( SaiBuilder builder,
                     SaiTagHandler parent,
                     SaiHandler next )
    throws SaiException
  {
    this.builder = builder;
    this.parent  = parent;
    this.next    = next;
    this.buff    = null;
    start(builder.node());
  }
  public void child(SaiBuilder child)
    throws SaiException
  {
    SaiNode node = child.node();
    if(child(node))
      builder.add(node);
  }
  public void characters(char ch[], int start, int len)
    throws SaiException
  {
    if(null==buff)
      buff = new StringBuffer(len);
    buff.append(ch,start,len);
  }
  public void end()          
      throws SaiException
  {
    if(null!=buff) {
      String value = buff.toString();
      builder.setValue(characters(value));
    }
    finish();
    builder = null;
    parent  = null;
    next    = null;
    buff    = null;
  }
  public void partition()
      throws SaiException 
  {
    // nothing to do.
  }
  public SaiTagHandler resolve(SaiPath path) 
  { 
    return null; 
  }
}
// -----------------------------------------------
// SaiTagHandlerInteger.java
// No warranty; no copyright -- use this as you will.
// -----------------------------------------------
package com.clarkevans.sai;
/**
 * This provides for an implementation of tags which
 * have an Integer for character text.
 *
 * @version $Revision: 0.1 $ $Date: 1999/12/24 14:00:00 $
 * @author Clark C. Evans (sai@clarkevans.com)
 */
public class SaiTagHandlerInteger extends SaiTagHandlerBase {
  protected boolean notify(SaiNode child)
    throws SaiException 
  {
    throw new SaiException(child,"Unexpected");
  }
  protected Object notify(String text)
    throws SaiException 
  { 
    try {
     return Integer.valueOf(text);
    } 
    catch(NumberFormatException e)
    {
       builder.setValue(text);
       throw new SaiException(builder.node(),e);
    }
  }
}
// -----------------------------------------------
// SaiException.java
// No warranty; no copyright -- use this as you will.
// -----------------------------------------------
package com.clarkevans.sai;
 /**
  * Encapsulate a general Sai error or warning.
  * <p>This class can contain basic error or warning information from
  * either the XML parser or the application: a parser writer or
  * application writer can subclass it to provide additional
  * functionality.  Sai handlers may throw this exception or
  * any exception subclassed from it.</p>
  * <p>If the application needs to pass through other types of
  * exceptions, it must wrap those exceptions in a SaiException
  * or an exception derived from a SaiException.</p>
  * <p>If the parser or application needs to include information about a
  * specific location in an XML document, it should use the
  * SaiParseException subclass.</p>
  * @author David Megginson (ak117@freenet.carleton.ca)
  * @version 1.0
  * @see com.clarkevans.sai.SaiParseException
  */
public class SaiException extends Exception {
  /**
    * Create a new SaiException wrapping an existing
    * exception, occuring at a particular node.
    */
  public SaiException (SaiNode node, Exception e )
  {
    super();
    this.message = node.name();
    this.exception = e;
  }
  /**
    * Create a new SaiException with a message,
    * occuring at a particular node.
    */
  public SaiException (SaiNode node, String message )
  {
    super();
    this.message = node.name() + message;
    this.exception = null;
  }
  /**
    * Create a new SaiException.
    *
    * @param message The error or warning message.
    * @see com.clarkevans.sai.Parser#setLocale
    */
  public SaiException (String message) {
    super();
    this.message = message;
    this.exception = null;
  }
  /**               
    * Create a new SaiException wrapping an existing exception.
    *
    * <p>The existing exception will be embedded in the new
    * one, and its message will become the default message for
    * the SaiException.</p>
    *
    * @param e The exception to be wrapped in a SaiException.
    */
  public SaiException (Exception e) {
    super();
    this.message = null;
    this.exception = e;
  }
  /**
    * Create a new SaiException from an existing exception.
    *
    * <p>The existing exception will be embedded in the new
    * one, but the new exception will have its own message.</p>
    *
    * @param message The detail message.
    * @param e The exception to be wrapped in a SaiException.
    * @see com.clarkevans.sai.Parser#setLocale
    */
  public SaiException (String message, Exception e) {
    super();
    this.message = message;
    this.exception = e;
  }
  /**
    * Return a detail message for this exception.
    *
    * <p>If there is a embedded exception, and if the SaiException
    * has no detail message of its own, this method will return
    * the detail message from the embedded exception.</p>
    *
    * @return The error or warning message.
    * @see com.clarkevans.sai.Parser#setLocale
    */
  public String getMessage () {
    if (message == null && exception != null) {
      return exception.getMessage();
    } else {
      return this.message;
    }
  }
  /**
    * Return the embedded exception, if any.
    *
    * @return The embedded exception, or null if there is none.
    */
  public Exception getException () {
    return exception;
  }
  /**
    * Convert this exception to a string.
    *
    * @return A string version of this exception.
    */
  public String toString () {
    return getMessage();
  }
  //////////////////////////////////////////////////////////////////////
  // Internal state.
  //////////////////////////////////////////////////////////////////////

  private String message;
  private Exception exception; 
     
}
// -----------------------------------------------
// SaiMultiValueException.java
// No warranty; no copyright -- use this as you will.
// -----------------------------------------------
package com.clarkevans.sai;
 /**
  * An exception used when a named fetch would retrieve two
  * or more children.
  *
  * @author Clark Evans (sai@clarkevans.com)
  * @version 1.0
  */
public class SaiMultiValueException extends SaiException {
  /**
    * Create a new SaiMultiValueException.
    *
    * @param message The error or warning message.
    * @see com.clarkevans.sai.Parser#setLocale
    */
  public SaiMultiValueException (String name) {
    super(name);
  }
}


------------------------------------------------------------------------
post: sml-dev@eGroups.com
unsubscribe: sml-dev-unsubscribe@eGroups.com
info: http://www.egroups.com/group/sml-dev/info.html

------------------------------------------------------------------------
Want to send money instantly to anyone, anywhere, anytime?
You can today at X.com - and we'll give you $20 to try it!  Sign 
up today at X.com.  It's quick, free, & there's no obligation!
http://click.egroups.com/1/332/0/_/162860/_/946236930


eGroups.com home: http://www.egroups.com/group/sml-dev
http://www.egroups.com - Simplifying group communications







Mime
View raw message