001    package com.saelist.stx.operators;
002    
003    import java.util.*;
004    import java.io.IOException;
005    import org.apache.log4j.*;
006    import com.saelist.util.Strings;
007    import com.saelist.stx.*;
008    import com.saelist.stx.parser.*;
009    
010    
011    /** See {@link com.saelist.stx.Operator} for general description of the Operator
012      * concept in stx.
013      * <pre>
014      * Lifecycle: Create { init(config) init() { visit(pairs) { operator(pair) } } }
015      * Usage: Create { init(config) { visit(pairs) } }
016      * </pre>
017      */
018    public abstract class AbstractOperator implements Operator {
019    
020      protected Logger logger = Logger.getLogger(getClass());
021      private boolean isInitialized;
022      protected Pair config;
023    
024      /** Assumes a schema file in the package with the name [classname].schema.ls.txt. */
025      // To do: Cache schemas per class.
026      private Pair getSchema() {
027        try {
028          String classname = getClass().getName();
029          int pos = classname.lastIndexOf(".");
030          if(pos > 0)
031            classname = classname.substring(pos + 1);
032          return LstxParser.parse(Strings.loadStream(getClass().getResourceAsStream(classname + ".schema.ls.txt"))).get("config");
033        } catch(IOException e) {
034          throw new RuntimeException(e);
035        }
036      }
037    
038      /** Validates the configuration against the schema and stores it in config,
039        * then calls init() to allow descendants to further initialize themselves.
040        * @throws IllegalArgumentExeption if <code>config</code> doesn't
041        * validate against {@link #getSchema}. The Validator will log
042        * warnings aswell as print linenumbers of the mismatches in both
043        * files to the standard error.
044        */
045      public void init(Pair config) {
046        // logger.setLevel(Level.DEBUG);
047        logger.debug("init(): config=" + config);
048        logger.debug("init(): config=" + getSchema());
049        if(! new Validator().matches(getSchema(), config))
050          throw new IllegalArgumentException("Invalid configuration for " + getClass().getName() + ": " + config + " doesn't match " + getSchema());
051        this.config = config;
052        init();
053        isInitialized = true;
054      }
055    
056      /** Descendants should overide this if they vant to cache elements
057        * of config in member variables or do some other inital setup.
058        */
059      protected void init() {
060      }
061    
062      /** Visits the given pairs, typically the result of Pair.select(xpath).
063        * @throws ClassCastException if <code>pairs</code> contains a non-Pair.
064        */
065      public final void visit(Iterator pairs) {
066        if(! isInitialized)
067          throw new IllegalStateException("Not initialized.");
068        while(pairs.hasNext()) {
069          Pair pair = (Pair) pairs.next();
070          logger.debug("visit(pairs): pair=" +  pair.getText());
071          operate(pair);
072        }
073      }
074    
075      /** Descendant classes will put the code for the actual operation
076        * here.
077        * @throws IllegalStateException if this hasn't been initialized.
078        */
079      public abstract void operate(Pair pair);
080    
081    
082    
083    
084    
085    
086    }