View Javadoc

1   // Copyright (C) 2003,2004 by Robert C. Martin, Micah D. Martin, and Joseph A. Bergin. All rights reserved.
2   // Released under the terms of the GNU General Public License version 2 or later.
3   package com.jbergin;
4   
5   import java.io.IOException;
6   import java.lang.reflect.InvocationTargetException;
7   import java.lang.reflect.Method;
8   import java.net.URL;
9   import java.util.ArrayList;
10  import java.util.HashMap;
11  import java.util.Iterator;
12  import java.util.List;
13  import java.util.Map;
14  import java.util.NoSuchElementException;
15  import java.util.regex.Pattern;
16  
17  import org.apache.commons.logging.Log;
18  import org.apache.commons.logging.LogFactory;
19  
20  import com.gargoylesoftware.htmlunit.ElementNotFoundException;
21  import com.gargoylesoftware.htmlunit.ScriptResult;
22  import com.gargoylesoftware.htmlunit.WebClient;
23  import com.gargoylesoftware.htmlunit.html.DomNode;
24  import com.gargoylesoftware.htmlunit.html.HtmlElement;
25  import com.gargoylesoftware.htmlunit.html.HtmlPage;
26  import com.gargoylesoftware.htmlunit.html.HtmlTextArea;
27  
28  import fit.Fixture;
29  import fit.Parse;
30  
31  public class HtmlFixture extends Fixture
32  {
33    // static fields are preserved across tables--even across pages
34    private static Map          fixtureSymbols   = new HashMap();
35    private static final String queryAttribute   = " class=\"query\" bgcolor=\"#c0c0c0\" ";
36    
37    private static String       blankToken       = "$blank$";
38    private static String       symbolToken      = "symbol";
39    private static String       lastElementToken = "";
40    private static final String ARRAY_START      = "[";
41    private static final String ARRAY_END        = "]";
42    private static HashMap      commands         = new HashMap();
43  
44    private Parse               completeRow;        // updated for each row.
45  
46    //Accessors used for testing:
47    static String blankToken() {
48      return blankToken;
49    }
50  
51    static String symbolToken() {
52      return symbolToken;
53    }
54  
55    static String lastElementToken() {
56      return lastElementToken;
57    }
58  
59    HtmlElement getFocusElement() {
60      return elementFactory.getFocusElement();
61    }
62    void setFocusElement(HtmlElement e) {
63      elementFactory.setFocusElement(e);
64    }
65  
66    String getHtmlSource() {
67      return elementFactory.getHtmlSource();
68    }
69    void setHtmlSource(String s) {
70      elementFactory.setHtmlSource(s);
71    }
72    public void setJavaScriptEnabled(boolean b) {
73      elementFactory.setJavaScriptEnabled(b);
74    }
75    public boolean isJavaScriptEnabled() {
76      return elementFactory.isJavaScriptEnabled();
77    }
78  
79    public void store(String key, Object value) {
80      elementFactory.store(key, value);
81    }
82    public DomNode retrieve(String key) {
83      return elementFactory.retrieve(key);
84    }
85    void clear() {
86      elementFactory.clear();
87    }
88    int storageSize() {
89      return elementFactory.storageSize();
90    }
91    HtmlPage getHtmlPage() {
92      return elementFactory.getHtmlPage();
93    }
94  
95    URL getUrl() {
96      return elementFactory.getUrl();
97    }
98    
99  
100   // Non-static fields are set anew for each table since each table is run
101   // by a separate instantiation.
102   private boolean      failState = false;
103   final ElementFactory elementFactory;
104   private static final Log log = LogFactory.getLog(HtmlFixture.class);
105 
106   public HtmlFixture() {
107   	this(new ElementFactory(new WebClient()));
108   }
109 
110   // for unit-testing only
111   public HtmlFixture(ElementFactory ef) {
112     elementFactory = ef;
113   }
114   
115   public void doTable(Parse table) {
116     Parse sourceCell = getSourceCell(table);
117     try {
118       if (sourceCell == null) {
119         Parse fixtureCell = table.parts.parts;
120         ignore(fixtureCell);
121         fixtureCell.addToBody("<br>" + label("The row naming the html source is missing"));
122         return;
123       }
124       elementFactory.loadElementSafely(getParam(sourceCell));
125       Parse nameCell = sourceCell.more;
126       if (nameCell != null && !"".equals(nameCell.text())) {
127         storeElement(nameCell, getFocusElement());
128         sourceCell = nameCell;
129       }
130       imageXML(getFocusElement(), sourceCell);
131       doRows(table.parts.more.more);
132     } catch (NoSuchElementException e1) {
133       missingElement(sourceCell, table);
134     } catch (Exception e) {
135       exception(table.parts.parts, e);
136     }
137   }
138 
139   public void doRow(Parse row) {
140     Parse commandCell = null;
141     try {
142       commandCell = row.parts;
143       String commandText = commandCell.text();
144       failState = false;
145       completeRow = row;
146       if ("Fail".equals(commandText)) {
147         failState = true;
148         row = commandCell.more;
149         commandCell = row;
150         commandText = commandCell.text();
151       }
152       Method command = (Method) commands.get(commandText);
153       if (command == null) {
154         final String errorMessage = "Invalid token '" + commandText + "'.  Valid tokens are:<br> Attribute, Blank Token, Clear, Click, Element, " + "Element Focus, Elements, Execute, ExpireAllCookies, Focus, Has Text, Javascript, Last ELement Token, List, <br>"
155             + "Matches Text, Node, Nodes, Preserve, Save, Set Value, Submit, Symbol, Symbol Token, Text, Type, Type Focus, Types";
156         commandCell.addToBody("<br>" + label(errorMessage));
157         wrong(commandCell);
158         ignoreTheRest(commandCell);
159         return;
160       }
161       Parse[] args = { commandCell }; // Sending commandCell here instead of the row
162       // then the first cell is always "commandCell" and the second always commandCell.more
163       // failState captures knowledge of a "Fail" prefix.
164       command.invoke(this, args);
165     } catch (InvocationTargetException e) {
166       log.debug(e.getCause());
167       exception(commandCell, e);
168     } catch (Exception e) {
169       log.debug(e);
170       exception(commandCell, e);
171     }
172   }
173 
174   // Fitnesse can put a fitness variable as a part of a cell,
175   // rather than the entire cell. Symbols here are full cells.
176   // e.g. !define country {us}
177   // | Attribute | href | http://www.ibm.com/${country} |
178 
179   /* *********************** The commands *********************** */
180 
181   void cmd_answer(Parse commandCell) {
182   	final Parse messageCell = commandCell.more;
183   	final Parse answerCell = messageCell.more;
184   	final String message = getParam(messageCell);
185   	final String answer = getParam(answerCell);
186   	
187   	elementFactory.addAnswer(message, Boolean.valueOf(answer).booleanValue());
188   }
189   void cmd_attribute(Parse commandCell) {
190     Parse idCell = commandCell.more;
191     Parse valueCell = idCell.more;
192 
193     String id = getParam(idCell);
194     if (getFocusElement().isAttributeDefined(id)) {
195       right(idCell);
196       if (valueCell != null) {
197         String expectedValue = getParam(valueCell);
198         String actualValue = getFocusElement().getAttributeValue(id);
199         checkValuesFail(expectedValue, actualValue, valueCell);
200         if (!failState) {
201           Parse nameCell = valueCell.more;
202           if (nameCell != null) {
203             setSymbol(nameCell.text(), actualValue);
204           }
205         }
206       }
207     } else {
208       idCell.addToBody("<br>" + label("This attribute is not defined"));
209       if (failState)
210         right(idCell);
211       else
212         wrong(idCell);
213       ignoreTheRest(idCell);
214     }
215   }
216 
217   void cmd_blankToken(Parse commandCell) {
218     if (ignoreFail())
219       return;
220     Parse blankCell = commandCell.more;//row.parts.more;
221     String newBlank = blankCell.text();
222     if (!("".equals(newBlank)))
223       blankToken = newBlank;
224     else {
225       wrong(blankCell);
226       commandCell.addToBody("<br>" + label("Illegal empty blank token."));
227     }
228   }
229 
230   void cmd_clear(Parse commandCell) {
231     if (ignoreFail())
232       return;
233     
234     final Parse elementCell = commandCell.more;
235     if (elementCell == null) {
236     	elementFactory.clear();
237     } else {
238     	final String elementName = elementCell.text();
239     	final Object element = elementFactory.remove(elementName);
240     	if (element == null) {
241     		elementCell.addToBody(" is not in memory");
242     		wrong(elementCell);
243     	}
244     }
245   }
246 
247   void cmd_expireAllCookies(Parse commandCell) {
248     elementFactory.expireAllCookies(getHtmlPage());
249     right(commandCell);
250   }
251 
252   void cmd_element(Parse commandCell) {
253     Parse idCell = commandCell.more;
254     Parse typeCell = idCell.more;
255     Parse nameCell = typeCell.more;
256     HtmlElement e = getElement(idCell);
257     if (e != null) {
258       right(idCell);
259       checkElementTypeFail(typeCell, e);
260       if (nameCell != null) {
261         if (!failState)
262           storeElement(nameCell, e);
263         imageXML(e, nameCell);
264       }
265     } else {
266       if (failState)
267         right(idCell);
268       else
269         wrong(idCell);
270       ignoreTheRest(idCell);
271     }
272   }
273 
274   void cmd_elementFocus(Parse commandCell) {
275     if (ignoreFail())
276       return;
277     Parse idCell = commandCell.more;
278     Parse typeCell = idCell.more;
279     Parse nameCell = typeCell.more;
280     HtmlElement e = getElement(idCell);
281     if (e != null) {
282       right(idCell);
283       checkElementType(typeCell, e);
284       setFocusElement(e);
285       if (nameCell != null) {
286         storeElement(nameCell, e);
287         imageXML(e, nameCell);
288       }
289     } else {
290       wrong(idCell);
291       ignoreTheRest(idCell);
292     }
293   }
294 
295   void cmd_elements(Parse commandCell) {
296     Parse cellTwo = commandCell.more;
297     String cellTwoText = getParam(cellTwo);
298     int actual = elementChildren(getFocusElement()).size();
299 
300     if (cellTwoText.equals("")) {
301       query(cellTwo, "" + actual);
302     } else {
303       try {
304         Comparer comp = Comparer.get(cellTwoText);
305         if (reverseIfFail(comp.compare(actual)))
306           right(cellTwo);
307         else
308           wrong(cellTwo, String.valueOf(actual));
309       } catch (Exception nfe) {
310         exception(cellTwo, nfe);
311       }
312     }
313   }
314 
315   void cmd_execute(Parse commandCell) {
316     if (ignoreFail())
317       return;
318     Parse attribCell = commandCell.more;
319     String actionToRun = getParam(attribCell); // (e.g. onClick)
320     
321     try {
322       Object result = elementFactory.executeEventHandler(actionToRun);
323 	  attribCell.addToBody(": " + (result != null ? result.toString() : "null"));
324       right(attribCell);
325     } catch (IllegalArgumentException e) {
326       attribCell.addToBody("<br>" + label(e.getMessage()));
327       wrong(attribCell);
328       ignoreTheRest(attribCell);
329     } catch (ConfirmWithoutAnswerException e) {
330       attribCell.addToBody(": " + e.getMessage());
331       wrong(attribCell);
332       ignoreTheRest(attribCell);
333     } catch (ConfirmMessageMismatchException e) {
334     	attribCell.addToBody(": confirm message " + e.getMessage());
335         wrong(attribCell);
336         ignoreTheRest(attribCell);
337     }
338   }
339 
340   void cmd_eval(Parse commandCell) {
341     Parse expressionCell = commandCell.more;
342     Parse outCell = expressionCell != null ? expressionCell.more : null;
343     
344     String expression = getParam(expressionCell);
345 
346     ScriptResult scriptResult = (ScriptResult)elementFactory.evaluateExpression(expression);
347     
348     Object result = scriptResult.getJavaScriptResult();
349     
350     if (outCell != null) {
351       String expectedResult = getParam(outCell);
352       checkValuesFail(expectedResult, result.toString(), outCell);
353     }
354   }
355   
356   void cmd_focus(Parse commandCell) {
357     if (ignoreFail())
358       return;
359     Parse elementCell = commandCell.more;
360     if (elementCell == null)
361       return;
362     
363     try {
364       elementFactory.loadElementSafely(getParam(elementCell));
365       imageXML(getFocusElement(), elementCell);
366     } catch (IOException e) {
367       elementCell.addToBody("<br>" + label("Couldn't load " + e.getMessage()));
368       wrong(elementCell);
369     } catch (NoSuchElementException e) {
370       elementCell.addToBody("<br>" + label("This element is not in storage."));
371       wrong(elementCell);
372     }
373   }
374 
375   void cmd_focusRelative(Parse commandCell) {
376     if (ignoreFail())
377       return;
378     Parse elementCell = commandCell.more;
379     if (elementCell != null) {
380       String urlString = getParam(elementCell);
381       try {
382         elementFactory.loadElementRelative(urlString);
383         elementCell.addToBody("<br>" + label(urlString));
384         Parse nameCell = elementCell.more;
385         if (nameCell != null) {
386           storeElement(nameCell, getFocusElement());
387           imageXML(getFocusElement(), nameCell);
388         }
389       } catch (Exception ex) {
390         wrong(elementCell);
391         elementCell.addToBody("<br>" + label("Unable to load:") + "<br>" + label(urlString));
392       }
393     }
394   }
395 
396   void cmd_hasText(Parse commandCell) {
397     Parse textCell = commandCell.more;//row.parts.more;
398     String expectedText = getParam(textCell);
399     String actualText = getElementText(getFocusElement());
400 
401     if (reverseIfFail(actualText.indexOf(expectedText) != -1))
402       right(textCell);
403     else {
404       wrong(textCell);
405       textCell.addToBody("<br>" + label("This text was not found"));
406     }
407   }
408 
409   void cmd_httpStatus(Parse commandCell) {
410   	final Parse outCell = commandCell.more;
411   	
412   	final int statusCode = elementFactory.getHtmlPage().getWebResponse().getStatusCode();
413   	
414   	if (outCell != null) {
415         final String expectedResult = getParam(outCell);
416         checkValuesFail(expectedResult, Integer.toString(statusCode), outCell);
417       }
418   }
419   
420   void cmd_javascript(Parse commandCell) {
421     if (ignoreFail())
422       return;
423     
424     boolean enableJavascript = isJavaScriptEnabled();
425     
426     Parse tokenCell = commandCell.more;
427     if (tokenCell != null) {
428       String newToken = tokenCell.text();
429       if ("on".equalsIgnoreCase(newToken))
430         enableJavascript = true;
431       else if ("off".equalsIgnoreCase(newToken))
432         enableJavascript = false;
433     } else {
434       wrong(commandCell);
435       commandCell.addToBody(": must specify one of [on, off]");
436       return;
437     }
438     setJavaScriptEnabled(enableJavascript);
439     commandCell.addToBody(":" + (enableJavascript ? "" : "not") + " executing Javascript");
440   }
441 
442   void cmd_lastElementToken(Parse commandCell) {
443     if (ignoreFail())
444       return;
445     Parse tokenCell = commandCell.more;
446     String newToken = tokenCell.text();
447     lastElementToken = newToken;
448   }
449 
450   void cmd_list(Parse commandCell) {
451     if (ignoreFail())
452       return;
453     Parse nameCell = commandCell.more;
454     String name = nameCell.text();
455     if (!"".equals(name)) {
456       ArrayList args = new ArrayList(2);
457       Parse cell = nameCell.more;
458       while (cell != null) {
459         args.add(cell.text());
460         cell = cell.more;
461       }
462       setSymbol(name, args);
463     }
464   }
465 
466   void cmd_matchesText(Parse commandCell) {
467     Parse textCell = commandCell.more;
468     String expectedText = getParam(textCell);
469     String actualText = getElementText(getFocusElement());
470 
471     if (reverseIfFail(Pattern.compile(expectedText, Pattern.MULTILINE | Pattern.DOTALL).matcher(actualText).find()))
472       right(textCell);
473     else {
474       wrong(textCell);
475       textCell.addToBody("<br>" + label("This pattern was not found"));
476     }
477   }
478 
479   void cmd_node(Parse commandCell) {
480     Parse idCell = commandCell.more;
481     Parse typeCell = idCell.more;
482     DomNode e = getNode(idCell);
483     if (e != null) {
484       right(idCell);
485       checkElementTypeFail(typeCell, e);
486     } else {
487       if (failState)
488         right(idCell);
489       else
490         wrong(idCell);
491       ignoreTheRest(idCell);
492     }
493   }
494 
495   void cmd_nodes(Parse commandCell) {
496     Parse cellTwo = commandCell.more;
497     String cellTwoText = getParam(cellTwo);
498     int actual = allChildren(getFocusElement()).size();
499 
500     if (cellTwoText.equals("")) {
501       query(cellTwo, "" + actual);
502     } else {
503       try {
504         Comparer comp = Comparer.get(cellTwoText);
505         if (reverseIfFail(comp.compare(actual)))
506           right(cellTwo);
507         else
508           wrong(cellTwo, String.valueOf(actual));
509       } catch (Exception nfe) {
510         exception(cellTwo, nfe);
511       }
512     }
513   }
514 
515   void cmd_preserve(Parse commandCell) {
516     if (ignoreFail())
517       return;
518     
519     String value = "on";
520     if (commandCell.more != null && commandCell.more.text() != null) {
521       value = commandCell.more.text();
522     } else {
523       commandCell.addToBody(": must include 2nd cell with one of [on, true, off, false]");
524       wrong(commandCell);
525       return;
526     }
527     boolean preserve = false;
528     if ("on".equalsIgnoreCase(value) || "true".equalsIgnoreCase(value)) {
529       preserve = true;
530     }
531     elementFactory.setPreserve(preserve);
532     
533     commandCell.addToBody(":" + (preserve ? "" : "not") + " preserving after click/submit");
534   }
535 
536   void cmd_save(Parse commandCell) {
537     if (ignoreFail())
538       return;
539     Parse nameCell = commandCell.more;
540     if (nameCell != null) {
541       storeElement(nameCell, getFocusElement());
542       imageXML(getFocusElement(), nameCell);
543     }
544   }
545 
546   void cmd_setValue(Parse commandCell) {
547     if (ignoreFail())
548       return;
549     Parse textCell = commandCell.more;
550     String textValue = getParam(textCell);
551 
552     try {
553       elementFactory.setInputFieldValue(textValue);
554     } catch (UnsupportedOperationException uoe) {
555       exception(textCell, uoe);
556     }
557   }
558 
559   void cmd_select(Parse commandCell) {
560     if (ignoreFail())
561       return;
562         
563     final Parse textCell = commandCell.more;
564     final Parse outputCell = textCell.more;
565     final Parse storageCell;
566     if (outputCell != null) {
567     	storageCell = outputCell.more;
568     } else {
569     	storageCell = null;
570     }
571     
572     String textValue = getParam(textCell);
573     try {
574       final boolean deselect = "null".equals(textValue);
575       final int found = elementFactory.selectOptions(textValue, deselect);
576       log.debug("new focus element: " + getFocusElement().asText());
577       
578       if (outputCell != null) {
579        	final String expectedResult = getParam(outputCell);
580         checkValuesFail(expectedResult, Integer.toString(found), outputCell);
581         if (storageCell != null) {
582         	storeElement(storageCell, getFocusElement());
583         	right(storageCell);
584         	imageXML(getFocusElement(), storageCell);
585         }
586         return;
587       } else {
588       	textCell.addToBody(": " + found + " options matched");
589       	if (found > 0 || deselect) {
590           right(textCell);
591       	} else {
592           wrong(textCell);
593       	}
594       }
595     } catch (UnsupportedOperationException uoe) {
596       exception(textCell, uoe);
597     }
598     log.debug("new focus element: " + getFocusElement().asText());
599   }
600 
601   void cmd_submit(Parse commandCell) throws Exception {
602     if (ignoreFail())
603       return;
604     Parse nameCell = commandCell.more;
605     
606     try {
607 	  elementFactory.clickFocusElement();
608     } catch (IllegalArgumentException e) {
609       commandCell.addToBody("<br>" + label(e.getMessage()));
610       if (nameCell == null) {
611       	wrong(commandCell);
612       	ignoreTheRest(commandCell);
613       } else {
614       	wrong(nameCell);
615       	ignoreTheRest(nameCell);
616       }
617       return;
618     } catch (Exception e) {
619       LogFactory.getLog(this.getClass()).debug(e);
620       commandCell.addToBody("<br>" + label(e.getMessage()));
621       exception(nameCell, e);
622       return;
623     }
624     
625     if (nameCell != null) {
626     	storeElement(nameCell, getFocusElement());
627     	right(nameCell);
628     	imageXML(getFocusElement(), nameCell);
629     }
630   }
631 
632   void cmd_symbol(Parse commandCell) {
633     if (ignoreFail())
634       return;
635     Parse paramCell = commandCell.more;
636     Parse valueCell = paramCell.more;
637     String param = paramCell.text();
638     String value = valueCell.text();
639     if (!"".equals(param) && !"".equals(value))
640       setSymbol(param, value);
641     else {
642       wrong(paramCell);
643       commandCell.addToBody("<br>" + label("Illegal empty symbol."));
644     }
645   }
646 
647   void cmd_symbolToken(Parse commandCell) {
648     if (ignoreFail())
649       return;
650     Parse tokenCell = commandCell.more;
651     String newToken = tokenCell.text();
652     if (!("".equals(newToken)))
653       symbolToken = newToken;
654     else {
655       wrong(tokenCell);
656       commandCell.addToBody("<br>" + label("Illegal empty symbol token."));
657     }
658   }
659 
660   void cmd_text(Parse commandCell) {
661   	log.debug("focus element: " + getFocusElement().asText());
662     Parse textCell = commandCell.more;//row.parts.more;
663     String expectedText = getParam(textCell);
664     String actualText = getElementText(getFocusElement());
665     checkValuesFail(expectedText, actualText, textCell);
666   }
667 
668   void cmd_type(Parse commandCell)
669   {
670     doTypeCommand(commandCell);
671   }
672 
673   void cmd_typeFocus(Parse commandCell) {
674     HtmlElement e = doTypeCommand(commandCell);
675     if (e != null) {
676       setFocusElement(e);
677     }
678   }
679   
680   void cmd_types(Parse commandCell) {
681     Parse typeCell = commandCell.more;
682     Parse indexCell = typeCell.more;
683     List allElements = getFocusElement().getHtmlElementsByTagName(getParam(typeCell));
684     int actual = allElements.size();
685     if (getParam(indexCell).equals("")) {
686       query(indexCell, "" + actual);
687     } else {
688       try {
689         Comparer comp = Comparer.get(getParam(indexCell));
690         if (reverseIfFail(comp.compare(actual))) {
691           right(indexCell);
692         } else {
693           wrong(indexCell, String.valueOf(actual));
694 
695         }
696       } catch (Exception nfe) {
697         exception(indexCell, nfe);
698       }
699     }
700   }
701 
702   // ****************** End of Test Commands ***********
703 
704   public static HtmlElement getElementByName(List elements, String name) {
705     for (Iterator itr = elements.iterator(); itr.hasNext();) {
706       HtmlElement element = (HtmlElement) itr.next();
707       if (name.equals(element.getAttributeValue("name"))) {
708         return element;
709       }
710     }
711     return null;
712   }
713 
714   private HtmlElement doTypeCommand(Parse commandCell) {
715     Parse typeCell = commandCell.more;
716     Parse idCell = typeCell.more;
717     Parse nameCell = idCell.more;
718     List allElements = getFocusElement().getHtmlElementsByTagName(getParam(typeCell));
719     int size = allElements.size();
720     HtmlElement e = null;
721 
722     String cellValue = getParam(idCell);
723     try {
724       int index = Integer.parseInt(cellValue);
725       if (index <= 0)
726         index += size;
727       try {
728         e = (HtmlElement) allElements.get(index - 1);
729       } catch (Exception ex) {
730         if (failState)
731           right(idCell);
732         else {
733           wrong(idCell);
734           exception(typeCell, ex);
735           ignoreTheRest(idCell);
736         }
737         return null;
738       }
739     } catch (NumberFormatException nfe) {
740       try {
741         e = getFocusElement().getHtmlElementById(cellValue);
742       } catch (ElementNotFoundException enfe) {
743         e = getElementByName(allElements, cellValue);
744         if (e == null) {
745           if (failState)
746             right(idCell);
747           else {
748             exception(idCell, nfe);
749             ignoreTheRest(idCell);
750           }
751           return null;
752         }
753       }
754     }
755     if (reverseIfFail(e != null)) {
756       right(typeCell);
757       right(idCell);
758       if (!failState && nameCell != null) {
759         storeElement(nameCell, e);
760         imageXML(e, nameCell);
761       }
762     } else {
763       wrong(typeCell);
764       wrong(idCell);
765       ignoreTheRest(idCell);
766     }
767     return e;
768   }
769 
770   public static void setSymbol(String key, Object value) {
771     fixtureSymbols.put(key, value);
772   }
773 
774   public static Object getSymbol(String key) {
775     return fixtureSymbols.get(key);
776   }
777 
778   private String getParam(Parse keyCell) {
779     String result = null;
780     String key = keyCell.text();
781     int index = key.trim().indexOf(symbolToken);
782     if (index != 0)
783       return key;
784     key = key.substring(key.indexOf(symbolToken) + symbolToken.length()).trim();
785     index = key.indexOf(ARRAY_START);
786     if (index >= 0) {
787       int finalIndex = key.indexOf(ARRAY_END);
788       String subscriptString = key.substring(index + ARRAY_START.length(), finalIndex);
789       try {
790         int subscript = Integer.parseInt(subscriptString.trim());
791         key = key.substring(0, index).trim();
792         ArrayList args = (ArrayList) getSymbol(key);
793         result = (String) args.get(subscript - 1);
794       } catch (Exception nfe) {
795         exception(keyCell, nfe);
796       }
797     } else
798       result = ((String) getSymbol(key)).trim();
799     keyCell.body = result;
800     return result;
801   }
802 
803   private boolean ignoreFail() {
804     if (failState) {
805       Parse failCell = completeRow.parts;
806       wrong(failCell);
807       failCell.addToBody("<br>" + label(" Ignored"));
808       ignoreTheRest(failCell);
809       return true;
810     }
811     return false;
812   }
813 
814   private void imageXML(HtmlElement anElement, Parse cellToLeft) {
815     Parse valueCell = cellToLeft.more;
816     if (valueCell != null && anElement != null) {
817       valueCell.body = (anElement.asXml());
818     }
819   }
820 
821   private Parse getSourceCell(Parse table) {
822     Parse sourceRow = table.parts.more;
823     if (sourceRow != null)
824       return sourceRow.parts;
825     else
826       return null;
827   }
828 
829   private void missingElement(Parse sourceCell, Parse table) {
830     sourceCell.addToBody("<br>" + label("This element is not in storage"));
831     wrong(sourceCell);
832     Parse row = table.parts.more.more;
833     while (row != null) {
834       ignore(row.parts);
835       ignoreTheRest(row.parts);
836       row = row.more;
837     }
838   }
839 
840   private HtmlElement getElement(Parse idCell) {
841     HtmlElement e = null;
842     StringBuffer errorMessage = new StringBuffer("");
843     String id = getParam(idCell);
844     e = getElementByIdOrName(idCell, id, errorMessage);
845     if (e == null)
846       e = getElementByIndex(idCell, errorMessage);
847     if (e == null)
848       idCell.addToBody("<br>" + label(errorMessage.toString()));
849     return e;
850   }
851 
852   private DomNode getNode(Parse idCell) {
853     DomNode e = null;
854     StringBuffer errorMessage = new StringBuffer("");
855     if (e == null)
856       e = getNodeByIndex(idCell, errorMessage);
857     if (e == null)
858       idCell.addToBody("<br>" + label(errorMessage.toString()));
859     return e;
860   }
861 
862   private HtmlElement getElementByIdOrName(Parse idCell, String idValue, StringBuffer errorMessage) {
863     try {
864       return getFocusElement().getHtmlElementById(idValue);
865     } catch (ElementNotFoundException e1) {
866     }
867     if ((!"".equals(lastElementToken)) && idCell.text().indexOf(lastElementToken) == 0)
868       return null;
869     try {
870       return getFocusElement().getOneHtmlElementByAttribute(getParam(idCell.more), "name", idValue);
871     } catch (ElementNotFoundException e1) {
872       errorMessage.append("No element or multiple elements with this id");
873     }
874     return null;
875   }
876 
877   private int twoWay(Parse cell, int size) throws NumberFormatException {
878     String indexAsString = cell.text();
879     int index = -1;
880     boolean empty = "".equals(lastElementToken);
881     int loc = indexAsString.indexOf(lastElementToken);
882     if (!empty && indexAsString.equals(lastElementToken))
883       index = 0;
884     else {
885       if (!empty && loc == 0) {
886         index = Integer.parseInt(indexAsString.substring(lastElementToken.length()).trim());
887       } else {
888         index = Integer.parseInt(indexAsString);
889       }
890     }
891     if (index <= 0)
892       index += size;
893     return index;
894   }
895 
896   private HtmlElement getElementByIndex(Parse cell, StringBuffer errorMessage) {
897     try {
898       List children = elementChildren(getFocusElement());
899       int size = children.size();
900       int index = twoWay(cell, size);
901       if (size >= index)
902         return (HtmlElement) children.get(index - 1);
903       else
904         errorMessage.append(" or index");
905     } catch (NumberFormatException e1) {
906       // id is not an index
907     }
908     return null;
909   }
910 
911   private DomNode getNodeByIndex(Parse cell, StringBuffer errorMessage) {
912     try {
913       List children = allChildren(getFocusElement());
914       int size = children.size();
915       int index = twoWay(cell, size);
916       if (size >= index)
917         return (DomNode) children.get(index - 1);
918       else
919         errorMessage.append(" or index");
920     } catch (NumberFormatException e1) {
921       // id is not an index
922     }
923     return null;
924   }
925 
926   private void storeElement(Parse nameCell, DomNode e) {
927     String name = nameCell.text();
928     if (!"".equals(name))
929       store(name, e);
930   }
931 
932   private void checkElementType(Parse typeCell, DomNode e) {
933     String expectedType = getParam(typeCell);
934     String actualType = e.getNodeName();
935     checkValues(expectedType, actualType, typeCell);
936   }
937 
938   private void checkElementTypeFail(Parse typeCell, DomNode e) {
939     String expectedType = getParam(typeCell);
940     String actualType = e.getNodeName();
941     checkValuesFail(expectedType, actualType, typeCell);
942   }
943 
944   private void checkValues(String expected, String actual, Parse parse) {
945     if (expected.equals("")) {
946       query(parse, actual);
947       return;
948     } else if (expected.equals(blankToken))
949       expected = "";
950     if (expected.equals(actual))
951       right(parse);
952     else
953       wrong(parse, actual.toString());
954   }
955 
956   private void checkValuesFail(String expected, String actual, Parse parse) {
957     if (expected.equals("")) {
958       query(parse, actual);
959       return;
960     } else if (expected.equals(blankToken))
961       expected = "";
962     if (reverseIfFail(expected.equals(actual)))
963       right(parse);
964     else
965       wrong(parse, actual.toString());
966   }
967 
968   private boolean reverseIfFail(boolean b) {
969     return failState ? !b : b;
970   }
971 
972   private void ignoreTheRest(Parse parse) {
973     while (parse.more != null) {
974       ignore(parse.more);
975       parse = parse.more;
976     }
977   }
978 
979   private String getElementText(HtmlElement element) {
980     String actualText;
981     if ("textarea".equals(element.getTagName()))
982       actualText = ((HtmlTextArea) element).getText();
983     else
984       actualText = element.asText();
985     return actualText;
986   }
987 
988 
989   private List getAll(Iterator i) {
990     ArrayList result = new ArrayList();
991     while (i.hasNext()) {
992       result.add(i.next());
993     }
994     return result;
995   }
996 
997   public List elementChildren(HtmlElement e) {
998     return getAll(e.getChildElementsIterator());
999   }
1000 
1001   public List allChildren(DomNode e) {
1002     return getAll(e.getChildIterator());
1003   }
1004 
1005   private void query(Parse cell, String contents) {
1006     cell.addToTag(queryAttribute);
1007     cell.addToBody(contents);
1008   }
1009 
1010   private static class Comparer
1011   {
1012     private Comparer(int storedValue) {
1013       this.storedValue = storedValue;
1014     }
1015 
1016     static Comparer get(String cellValue) // factory
1017     {
1018       int index = cellValue.indexOf(">=");
1019       if (index > -1) {
1020         return new Comparer(Integer.parseInt(cellValue.substring(index + 2).trim())) {
1021           public boolean compare(int value) {
1022             return value >= storedValue;
1023           }
1024         };
1025       }
1026       index = cellValue.indexOf(">");
1027       if (index > -1) {
1028         return new Comparer(Integer.parseInt(cellValue.substring(index + 1).trim())) {
1029           public boolean compare(int value) {
1030             return value > storedValue;
1031           }
1032         };
1033       }
1034       index = cellValue.indexOf("<=");
1035       if (index > -1) {
1036         return new Comparer(Integer.parseInt(cellValue.substring(index + 2).trim())) {
1037           public boolean compare(int value) {
1038             return value <= storedValue;
1039           }
1040         };
1041       }
1042       index = cellValue.indexOf("<");
1043       if (index > -1) {
1044         return new Comparer(Integer.parseInt(cellValue.substring(index + 1).trim())) {
1045           public boolean compare(int value) {
1046             return value < storedValue;
1047           }
1048         };
1049       }
1050       index = cellValue.indexOf("==");
1051       if (index > -1) {
1052         return new Comparer(Integer.parseInt(cellValue.substring(index + 2).trim()));
1053       }
1054       index = cellValue.indexOf("!=");
1055       if (index > -1) {
1056         return new Comparer(Integer.parseInt(cellValue.substring(index + 2).trim())) {
1057           public boolean compare(int value) {
1058             return value != storedValue;
1059           }
1060         };
1061       }
1062       // default
1063       return new Comparer(Integer.parseInt(cellValue.trim()));
1064     }
1065 
1066     public boolean compare(int value) {
1067       return value == storedValue;
1068     }
1069 
1070     int storedValue = 0;
1071   }
1072 
1073   static {
1074     Class myClass = HtmlFixture.class;
1075     Class[] args = { Parse.class };
1076     try {
1077       commands.put("Answer",                       myClass.getDeclaredMethod("cmd_answer", args));
1078       commands.put("Elements", 						myClass.getDeclaredMethod("cmd_elements", args));
1079       commands.put("Element", 						myClass.getDeclaredMethod("cmd_element", args));
1080       commands.put("Element Focus", 			myClass.getDeclaredMethod("cmd_elementFocus", args));
1081       commands.put("Focus", 							myClass.getDeclaredMethod("cmd_focus", args));
1082       commands.put("Focus Relative", 			myClass.getDeclaredMethod("cmd_focusRelative", args));
1083       commands.put("Types", 							myClass.getDeclaredMethod("cmd_types", args));
1084       commands.put("Type", 								myClass.getDeclaredMethod("cmd_type", args));
1085       commands.put("Type Focus", 					myClass.getDeclaredMethod("cmd_typeFocus", args));
1086       commands.put("Text", 								myClass.getDeclaredMethod("cmd_text", args));
1087       commands.put("Attribute", 					myClass.getDeclaredMethod("cmd_attribute", args));
1088       commands.put("Has Text", 						myClass.getDeclaredMethod("cmd_hasText", args));
1089       commands.put("Matches Text", 				myClass.getDeclaredMethod("cmd_matchesText", args));
1090       commands.put("Save", 								myClass.getDeclaredMethod("cmd_save", args));
1091       commands.put("Execute", 						myClass.getDeclaredMethod("cmd_execute", args));
1092       commands.put("Eval", 						myClass.getDeclaredMethod("cmd_eval", args));
1093       commands.put("Set Value", 					myClass.getDeclaredMethod("cmd_setValue", args));
1094       commands.put("Select", 					myClass.getDeclaredMethod("cmd_select", args));
1095       commands.put("Click", 							myClass.getDeclaredMethod("cmd_submit", args));
1096       commands.put("Submit", 							myClass.getDeclaredMethod("cmd_submit", args));
1097       commands.put("Nodes", 							myClass.getDeclaredMethod("cmd_nodes", args));
1098       commands.put("Node", 								myClass.getDeclaredMethod("cmd_node", args));
1099       commands.put("Blank Token", 				myClass.getDeclaredMethod("cmd_blankToken", args));
1100       commands.put("Preserve", 						myClass.getDeclaredMethod("cmd_preserve", args));
1101       commands.put("Javascript", 					myClass.getDeclaredMethod("cmd_javascript", args));
1102       commands.put("Clear", 							myClass.getDeclaredMethod("cmd_clear", args));
1103       commands.put("ExpireAllCookies", 		myClass.getDeclaredMethod("cmd_expireAllCookies", args));
1104       commands.put("Symbol", 							myClass.getDeclaredMethod("cmd_symbol", args));
1105       commands.put("List", 								myClass.getDeclaredMethod("cmd_list", args));
1106       commands.put("Symbol Token", 				myClass.getDeclaredMethod("cmd_symbolToken", args));
1107       commands.put("Last Element Token", 	myClass.getDeclaredMethod("cmd_lastElementToken", args));
1108       commands.put("Http Status", 	myClass.getDeclaredMethod("cmd_httpStatus", args));
1109     } catch (Exception e) {
1110       System.out.println("Exception in startup. " + e.toString());
1111     }
1112   }
1113 
1114 }