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 }