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 }