001    package com.saelist.command;
002    import java.io.*;
003    import com.saelist.util.*;
004    import com.saelist.stx.*;
005    import com.saelist.stx.parser.*;
006    import com.saelist.stx.xpath.*;
007    import java.util.*;
008    import org.jaxen.JaxenException;
009    import org.jaxen.XPathFunctionContext;
010    import org.apache.log4j.*;
011    
012    /**
013      * Evaluates all references of the form {xpath} under /template.
014      * for each element specified by the xpath under /input.
015      * The result is put into the file given by /output.
016      * Warning: There is no check for circular references.
017      */
018    public class TransformCommand extends AbstractCommand {
019    
020      protected static Logger logger = Logger.getLogger("com.saelist.command.TransformCommand");
021    
022      public void execute() {
023        if(config == null)
024          throw new IllegalStateException("Command isn't initialized.");
025    
026        try {
027    
028          Pair input = LstxParser.parse(Strings.loadFile(config.eval1("input-file")));
029    
030          // To do: Fix encoding problem. Umlauts show as question marks.
031          Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(config.eval1("output-file")), "ISO-8859-1"));
032    
033          // Writer out = new BufferedWriter(new FileWriter(config.eval1("output-file")));
034    
035          // root wil contain a copy of the template plus the input pair
036          // while the template is evaluated for that pair.
037    
038          Pair root = new Pair(null, "root");
039          Pair template = config.select1("/template");
040    
041          for(Iterator inputs = input.select(config.eval("/input-xpath")); inputs.hasNext(); ) {
042            root.clear();
043            root.add(template.copy());
044            root.add(((Pair) inputs.next()).copy());
045            transform(root.get("template"));
046            LstxParser.unParse(out, root.get("template"), true);
047          }
048    
049        } catch(JaxenException e) {
050          e.printStackTrace();
051        }  catch(IOException e) {
052          e.printStackTrace();
053        }
054    
055      }
056    
057      private void transform(Pair pair) throws JaxenException {
058    
059        String text = pair.getText();
060        StringBuffer text2 = new StringBuffer();
061    
062        // xxx { xxx } xxxxxxx { xxxxxxx } xxx ...
063        //            |        |         |
064        //            start    left      right
065    
066        int start = 0;
067        int left = text.indexOf("{", start);
068        int right = text.indexOf("}", left);
069        while(0 <= start && start <= left && left < right) {
070          if(start < left)
071            text2.append(text.substring(start, left));
072          text2.append(eval1(pair, text.substring(left + 1, right)));
073          start = right + 1;
074          left = text.indexOf("{", start);
075          right = text.indexOf("}", left);
076        }
077    
078        if(start < text.length())
079          text2.append(text.substring(start));
080    
081        pair.setText(text2.toString());
082    
083        for(Iterator children = pair.getPairs(); children.hasNext(); )
084          transform((Pair) children.next());
085    
086      }
087    
088      /**
089          Gets the first value of the xpath relative to the given pair or its
090          ancestors.
091    
092          The usual scope rules apply. That is, the first step in the
093          xpath must match an ancestor (including self) or its siblings.
094    
095          Procedurally that means that the xpath is tried on self and
096          each ancestor in turn until a result is had. The first value
097          of the result if any is then the returned. If the result has
098          no value or if no ancestor provided a result then "" is
099          returned.
100    
101          E.g. for the structure
102          <pre>
103    
104            bbb1
105              aaaa
106                 bbbb
107                   cccc=xx{bbb1}
108                 bbb1
109                   ccc1
110                   ccc2
111    
112          </pre>
113          the xpath bbb1 would evaluate ccc1.
114      */
115      private String eval1(Pair pair, String xpath) throws JaxenException {
116        if(pair == null)
117          return "";
118        Pair match = pair.select1(xpath);
119        if(match != null)
120          if(match.size() > 0)
121            return match.getValue();
122          else
123            return "";
124        return eval1(pair.getParent(), xpath);
125      }
126    
127      List toList(Iterator it) {
128        List list = new LinkedList();
129        while(it.hasNext())
130          list.add(it.next());
131        return list;
132      }
133    
134    
135    }