A few more explainations about what I mean by 'Context-passing'.

The setContext( Object ) method does not only set a kind of global 
variable. It acts as an implicit 'push' when done at a deeper Element 
level during the document parsing. The pop is also implicit, implicit 
meaning "done when the ElementStack decreases". The top of the Context 
stack is the last Context object pushed in, until :
- another Context object is pushed in by a setContext( ... ), while in 
the same "branch" of Elements,
- the stack decreases and pops the Context object automatically.

Now a little scenario :

step 1 -----
 >>  <module id="1">
      <module id="1.1" />
      <module id="1.2">
        <module id="1.2.1" />
      <module id="1.2.2" />
    </module>

  Context stack :
  module(1)

step 2 -----
    <module id="1">
 >>    <module id="1.1" />
      <module id="1.2">
        <module id="1.2.1" />
      <module id="1.2.2" />
    </module>

  Context stack :
  module(1.1)
  module(1)

step 3 -----
    <module id="1">
      <module id="1.1" />
 >>    <module id="1.2">
        <module id="1.2.1" />
      <module id="1.2.2" />
    </module>

  Context stack :
  module(1.2)
  module(1)

step 4 -----
    <module id="1">
      <module id="1.1" />
      <module id="1.2">
 >>      <module id="1.2.1" />
      <module id="1.2.2" />
    </module>

  Context stack :
  module(1.2.1)
  module(1.2)
  module(1)


Please note what happens in step 3 : the top of the stack is now 
module(1.2), replacing module(1.1).

My initial solution would cause some type-unsafety problems, and 
impacted modifying code. Ok, let's try something smarter.


First, the generic class.


  public class ContextSupport implements ElementHandler {

    private ElementHandler delegate = null ;

    /** Allows to act as a wrapper.
     */
    public ContextSupport( ElementHandler handler ) {
      this.delegate = handler ;
    }

    /** Used when another ElementHandler wants to control
     * kinematic. Also allows switching between
     * ContextSupports.
     */
    public ContextSupport() {
      this.delegate = null ;
    }
    
    public void setDelegate( ElementHandler eh ) {
      delegate = eh ;
    }

    // ArrayList used instead of Stack for accessing
    // bottom (known as 'root')
    private ArrayList contextStack = new ArrayList() ;

    private int top = -1 ;

    public final void onStart( ElementPath ep ) {
      Object lowerContext = null ;
      if( top >= 0 ) {
        lowerContext = contextStack.get( top ) ;
      }
      top++ ;                                        
      contextStack.set( top, lowerContext ) ;
      if( delegate != null ) {
        delegate.onStart( ep ) ;
      }
    }
              
    public final void onEnd( ElementPath ep ) {
      top-- ;
      if( delegate != null ) {
        delegate.onEnd( ep ) ;
      }
    }

    public final void setContext( Object context ) {
      contextStack.set( top, lowerContext ) ;
    }

    public final Object getContext() {
      return contextStack.get( top ) ;
    }

    public final Object getRoot() {
      if( contextStack.size() > 0 ) {
        return contextStack.get( 0 ) ;
      } else {
        return null ;
      }
    }

  }
                        
                        
                        
Use it asfollows :

  // Subclassing for type-safety
                                           
  private class ModuleContext extends ContextSupport {

    public Module getModuleContext() {
      return ( Module ) getContext() ;
    }

    public void setModuleContext( Module module ) {
      setContext( module ) ;
    }
    
    public Module getRootModule() {
      return ( Module ) getRoot() ;    
    }
  }
 
  // Now the handler
                                             
  PatternHandler ph = new PatternHandler() ;
  final ModuleContext mc = new ModuleContext( ph ) ;

  ph.add(
      "/module",
      new ElementHandler() {
        public void onStart( ElementPath ep ) {
          Module rootModule = new Module(
              ep.getCurrentAttribute( "id" ) ;
          )
          mc.setModuleContext( rootModule ) ;
        }
        public void onEnd( ElementPath ep ) { }
      }
  ) ;

  ph.add(
      "module",
      new ElementHandler() {
        public void onStart( ElementPath ep ) {
          Module module = new Module(
              ep.getCurrentAttribute( "id" ) ;
          )
          Module parent = mc.getModuleContext() ;
          parent.add( module ) ;
        }
        public void onEnd( ElementPath ep ) { }
      }

  ) ;

  ContextSupport cs = new ContextSupport( ph ) ;
  reader.setDefaultHandler( mc ) ;
  reader.read( input ) // standard parsing ;
  Module rootModule = mc.getRootContext() ;


OK, I started with a small example in mind an I finished with a 
near-complete implementation of what I need ; I did not test it yet, 
though.

The new ContextSupport also allows switching between different 
ContextSupports, becoming a delegate instead of delegating.
This could be useful when the Composite pattern appears in a case like 
this :

  <module id="1">
    <module id="1.1" />
    <module id="1.2">
      <module id="1.2.1">
        <part id="a" />
        <part id="b">
          <part id="b.a">
          <part id="b.b">
        </part>
        <part id="a" />
      </module>
      <module id="1.2.2" />
    </module>
  </module>

I'll try to implement this case in order to validate this last idea. 


_______________________________________________
dom4j-dev mailing list
[EMAIL PROTECTED]
https://lists.sourceforge.net/lists/listinfo/dom4j-dev

Reply via email to