View Javadoc
1   /*
2    * $Header: /cvsroot/sinon/sinon/src/java/eteg/sinon/dao/xml/XMLCollectorDAO.java,v 1.2 2005/06/21 14:25:08 thiagohp Exp $
3    * $Revision: 1.2 $
4    * $Date: 2005/06/21 14:25:08 $
5    * $Author: thiagohp $
6    *
7    * =============================================================================
8    *
9    * Copyright 2004-2005 Eteg Internet Ltda. (http://www.eteg.com.br)
10   *
11   * Licensed under the Apache License, Version 2.0 (the "License");
12   * you may not use this file except in compliance with the License.
13   * You may obtain a copy of the License at
14   *
15   *     http://www.apache.org/licenses/LICENSE-2.0
16   *
17   * Unless required by applicable law or agreed to in writing, software
18   * distributed under the License is distributed on an "AS IS" BASIS,
19   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20   * See the License for the specific language governing permissions and
21   * limitations under the License.
22   */
23  package eteg.sinon.dao.xml;
24  
25  import eteg.sinon.core.Action;
26  import eteg.sinon.core.Catalog;
27  import eteg.sinon.core.Collector;
28  import eteg.sinon.core.CollectorConfiguration;
29  import eteg.sinon.core.DataExtraction;
30  import eteg.sinon.core.LoopStep;
31  import eteg.sinon.core.Page;
32  import eteg.sinon.core.PageConfiguration;
33  import eteg.sinon.core.PageStorageConfiguration;
34  import eteg.sinon.core.Parameter;
35  import eteg.sinon.core.ParameterSet;
36  import eteg.sinon.core.PositioningStep;
37  import eteg.sinon.core.StartPage;
38  import eteg.sinon.core.Step;
39  import eteg.sinon.dao.CollectorDAO;
40  import eteg.sinon.exception.SinonException;
41  import org.jdom.CDATA;
42  import org.jdom.Content;
43  import org.jdom.Document;
44  import org.jdom.Element;
45  import org.jdom.JDOMException;
46  import org.jdom.Text;
47  import org.jdom.input.SAXBuilder;
48  import org.jdom.output.Format;
49  import org.jdom.output.XMLOutputter;
50  
51  import java.io.File;
52  import java.io.FileOutputStream;
53  import java.io.IOException;
54  import java.io.FileFilter;
55  import java.util.*;
56  
57  /***
58   * Implementation of {@link CollectorDAO} that read and write instances of
59   * {@link Collector} in XML files. These files must have <code>.xml</code>
60   * suffix. Files are written and read from the directory passed to
61   * {@link XMLCollectorDAO#XMLCollectorDAO}.
62   *
63   * @author <a href="mailto:thiagohp at users.sourceforge.net">Thiago H. de Paula Figueiredo</a>
64   * @author Last modified by $Author: thiagohp $
65   * @version $Revision: 1.2 $
66   * @todo improve implementation to write files exactly as they were read.
67   */
68  public class XMLCollectorDAO implements CollectorDAO {
69  
70      /***
71       * Sufixo que todos os arquivox XML descrevendo coletores devem usar.
72       */
73      public static final String COLLECTOR_SUFFIX = ".xml";
74  
75      final private static XMLFileFilter XML_FILE_FILTER = new XMLFileFilter();
76  
77      final private static String ID_ATTRIBUTE = "id";
78      final private static String CONFIG_TAG = "config";
79      final private static String CATALOG_TAG = "catalog";
80      final private static String GLOBAL_DATA_EXTRACTIONS_TAG =
81              "global-data-extractions";
82      final private static String DATA_EXTRACTION_TAG = "data-extraction";
83      final private static String DATA_EXTRACTIONS_TAG = "data-extractions";
84      final private static String TRIM_ATTRIBUTE = "trim";
85      final private static String DIRECTION_ATTRIBUTE = "direction";
86      final private static String START_PAGES_TAG = "start-pages";
87      final private static String REFID_ATTRIBUTE = "refid";
88      final private static String PROPERTY_TAG = "property";
89      final private static String NAME_ATTRIBUTE = "name";
90      final private static String VALUE_ATTRIBUTE = "value";
91      final private static String PAGE_TAG = "page";
92      final private static String FOLLOW_REDIRECT_ATTRIBUTE = "follow-redirect";
93      final private static String URL_ATTRIBUTE = "url";
94      final private static String PAGE_CONFIG_TAG = "page-config";
95      final private static String PARAMETER_SET_TAG = "parameter-set";
96      final private static String INCLUDE_PARAMETER_SET_TAG =
97              "include-parameter-set";
98      final private static String PARAMETER_TAG = "parameter";
99      final private static String HEADERS_TAG = "headers";
100     final private static String HEADER_TAG = "header";
101     final private static String THINK_TIME_TAG = "thinkTime";
102     final private static String CLASS_ATTRIBUTE = "class";
103     final private static String COLLECTOR_TAG = "collector";
104     final private static String DO_DATA_EXTRACTION_TAG = "do-data-extraction";
105     final private static String RESET_POSITION_ATTRIBUTE = "resetPosition";
106     final private static String LOOP_TAG = "loop";
107     final private static String LIST_ATTRIBUTE = "list";
108     final private static String CONDITION_ATTRIBUTE = "condition";
109 //    final private static String TEST_BEFORE_ATTRIBUTE = "testBefore";
110     final private static String ACTION_TAG = "action";
111     final private static String PAGE_ID_ATTRIBUTE = "pageId";
112     final private static String FOREACH_ATTRIBUTE = "foreach";
113     final private static String VARIABLE_ATTRIBUTE = "variable";
114     final private static String INDEX_ATTRIBUTE = "index";
115     final private static String PROPERTY_ID_ATTRIBUTE = "propertyId";
116     final private static String PAGE_LISTENERS_TAG = "listeners";
117     final private static String ON_ERROR_TAG = "on-error";
118     final private static String ON_STATE_CHANGE_TAG = "on-state-change";
119     final private static String ON_PROMPT_TAG = "on-prompt";
120     final private static String METHOD_ATTRIBUTE = "method";
121     final private static String START_PAGE_TAG = "start-page";
122     final private static String TIMEOUT_TAG = "timeout";
123     final private static String RETRIES_TAG = "retries";
124     final private static String SAVE_TAG = "save";
125     final private static String FILE_ATTRIBUTE = "file";
126     final private static String FAIL_ON_ERROR_ATTRIBUTE = "failOnError";
127     final private static String VALUE_ON_ERROR_ATTRIBUTE = "valueOnError";
128     final private static String NEWLINE_TAG = "newline";
129     final private static String ALLOW_DUPLICATES_ATTRIBUTE =
130             "allowDuplicates";
131     final private static String ON_CATALOG_STATE_CHANGE_TAG =
132             "on-catalog-state-change";
133     final private static String PROPERTIES_TAG = "properties";
134     final private static String BEFORE_ATTRIBUTE = "before";
135     final private static String ENCODING_ATTRIBUTE = "encoding";
136     final private static String FAIL_IF_FOUND_ATTRIBUTE = "failIfFound";
137     final private static String BACKWARDS_ATTRIBUTE = "backwards";
138 
139     /***
140      * Collector directory used when no one is specified. Value:
141      * <code>collectors</code>.
142      */
143     final public static String DEFAULT_COLLECTOR_DIRECTORY = "collectors";
144 
145     private Catalog currentCatalog;
146 
147     /***
148      * Directory where the XML configuration files are stored.
149      */
150     private File collectorDirectory;
151 
152     /***
153      * Constructor that receives a <code>File</code> pointing to the
154      * configurations file directory.
155      *
156      * @param collectorDirectory a <code>java.io.File</code>.
157      * @throws IllegalArgumentException if <code>collectorDirectory</code> is
158      * <code>null</code>.
159      */
160     public XMLCollectorDAO(File collectorDirectory) {
161 
162         if (collectorDirectory == null) {
163 
164             throw new IllegalArgumentException(
165                     "Parameter collectorDirectory cannot be null.");
166 
167         }
168 
169         this.collectorDirectory = collectorDirectory;
170 
171     }
172 
173     /***
174      * No-arg constructor. The collectors file directory is the current one.
175      */
176     public XMLCollectorDAO() {
177         this(new File(DEFAULT_COLLECTOR_DIRECTORY));
178     }
179 
180     /***
181      * Returns the collectors file directory.
182      *
183      * @return a <code>File</code>.
184      */
185     public File getCollectorDirectory() {
186         return collectorDirectory;
187     }
188 
189     /***
190      * Writes <code>collector</code> in
191      * <code>collectorDirectory + "/" + collector.getId() + ".xml"</code>.
192      *
193      * @param collector a {@link Collector} instance.
194      * @throws SinonException if some error ocurrs.
195      */
196     public void store(Collector collector) throws SinonException {
197 
198         final File destination =
199                 new File(collectorDirectory + "/" + collector.getId() + ".xml");
200 
201         store(collector, destination);
202 
203     }
204 
205     /***
206      * Writes <code>collector</code> in a given <code>destination</code>
207      * directory.
208      *
209      * @param collector a {@link Collector} instance.
210      * @param destination a <code>java.util.File</code>.
211      * @throws SinonException if some error ocurrs.
212      */
213     public void store(Collector collector, File destination)
214             throws SinonException {
215 
216         Format format = getXMLFormat();
217 
218         XMLOutputter outputter = new XMLOutputter(format);
219 
220         FileOutputStream outputStream = null;
221         Element rootElement = createRootElement(collector);
222         Document document = new Document(rootElement);
223 
224         try {
225 
226             outputStream = new FileOutputStream(destination);
227             outputter.output(document, outputStream);
228 
229         } catch (IOException e) {
230 
231             throw new SinonException("Problemas ao salvar o arquivo " +
232                     destination.getAbsolutePath(), null);
233 
234         }
235 
236     }
237 
238     /***
239      * Returns all available collectors.
240      *
241      * @return a <code>java.util.List</code>.
242      * @throws Exception if some error ocurrs.
243      */
244     public List findAll() throws Exception {
245 
246         File[] files = collectorDirectory.listFiles(XML_FILE_FILTER);
247         ArrayList collectors = new ArrayList();
248 
249         for (int i = 0; i < files.length; i++) {
250 
251             String name = files[i].getName();
252             name = name.substring(0, name.length() - COLLECTOR_SUFFIX.length());
253             collectors.add(load(name));
254 
255         }
256 
257         return collectors;
258 
259     }
260 
261     private Format getXMLFormat() {
262 
263         Format format = Format.getPrettyFormat();
264         format.setEncoding("ISO-8859-1");
265         format.setIndent("    ");
266         format.setLineSeparator("\n");
267         format.setExpandEmptyElements(false);
268         format.setTextMode(Format.TextMode.PRESERVE);
269 
270         return format;
271 
272     }
273 
274     private Element createRootElement(Collector collector) {
275 
276         Element element = new Element(COLLECTOR_TAG);
277         element.setAttribute(ID_ATTRIBUTE, collector.getId());
278 
279         element.addContent(createConfigElement(collector));
280 
281         if (collector.getCatalogStateListenerClassName() != null) {
282             element.addContent(createOnCatalogStateChangeElement(collector));
283         }
284 
285         Catalog[] catalogs = collector.getCatalogs();
286 
287         for (int i = 0; i < catalogs.length; i++) {
288             element.addContent(createCatalogElement(catalogs[i]));
289         }
290 
291         return element;
292 
293     }
294 
295     private Content createCatalogElement(Catalog catalog) {
296 
297         Element element = new Element(CATALOG_TAG);
298         element.setAttribute(ID_ATTRIBUTE, catalog.getId());
299 
300         element.addContent(createPropertiesElement(catalog.getProperties()));
301 
302         element.addContent(createStartPageElement(catalog.getStartPages()));
303 
304         ParameterSet[] parameterSets = catalog.getParameterSets();
305 
306         for (int i = 0; i < parameterSets.length; i++) {
307             element.addContent(createParameterSetElement(parameterSets[i]));
308         }
309 
310         DataExtraction[] globalDataExtractions = catalog.getDataExtractions();
311         element.addContent(createlDataExtractionElement(
312                 GLOBAL_DATA_EXTRACTIONS_TAG, globalDataExtractions));
313 
314         Page[] pages = catalog.getPages();
315 
316         for (int i = 0; i < pages.length; i++) {
317             element.addContent(createPageElement(pages[i]));
318         }
319 
320         return element;
321 
322     }
323 
324     private Element createPageElement(Page page) {
325 
326         Element element = new Element(PAGE_TAG);
327         element.setAttribute(ID_ATTRIBUTE, page.getId());
328         element.setAttribute(URL_ATTRIBUTE, page.getUrl());
329 
330         if (page.getFollowRedirects() == false) {
331             element.setAttribute(FOLLOW_REDIRECT_ATTRIBUTE,
332                     String.valueOf(page.getFollowRedirects()));
333         }
334 
335         String method = page.getMethod() == Page.POST_METHOD ? "post" : "get";
336         element.setAttribute(METHOD_ATTRIBUTE, method);
337 
338         PageConfiguration configuration = page.getConfiguration();
339 
340         if (configuration != null) {
341             element.addContent(createPageConfigurationElement(configuration));
342         }
343 
344         PageStorageConfiguration storageConfiguration =
345                 page.getStorageConfiguration();
346 
347         if (storageConfiguration.getFilename() != null) {
348             element.addContent(createPageStorageConfigurationElement(
349                     storageConfiguration));
350         }
351 
352         Element dataExtractions = new Element(DATA_EXTRACTIONS_TAG);
353         Step[] steps = page.getSteps();
354 
355         for (int i = 0; i < steps.length; i++) {
356             dataExtractions.addContent(createStepElement(steps[i])); //ok
357         }
358 
359         element.addContent(createPageStateListenerElement(page));
360 
361         element.addContent(dataExtractions);
362 
363         Action[] actions = page.getActions();
364 
365         for (int i = 0; i < actions.length; i++) {
366             element.addContent(createActionElement(actions[i])); //ok
367         }
368 
369         return element;
370 
371     }
372 
373     private Element createPageStateListenerElement(Page page) {
374 
375         Element element = new Element(PAGE_LISTENERS_TAG);
376 
377         String stateListener = page.getPageStateListenerClassName();
378         String promptListener = page.getPromptListenerClassName();
379         String errorListener = page.getErrorListenerClassName();
380 
381         if (stateListener != null) {
382 
383             Element stateTag = new Element(ON_STATE_CHANGE_TAG);
384             stateTag.setAttribute(CLASS_ATTRIBUTE, stateListener);
385             element.addContent(stateTag);
386 
387         }
388 
389         if (promptListener != null) {
390 
391             Element promptTag = new Element(ON_PROMPT_TAG);
392             promptTag.setAttribute(CLASS_ATTRIBUTE, promptListener);
393             element.addContent(promptTag);
394 
395         }
396 
397         if (errorListener != null) {
398 
399             Element errorTag = new Element(ON_ERROR_TAG);
400             errorTag.setAttribute(CLASS_ATTRIBUTE, errorListener);
401             element.addContent(errorTag);
402 
403         }
404 
405         return element;
406 
407     }
408 
409     private Element createActionElement(Action action) {
410 
411         Element element = new Element(ACTION_TAG);
412         element.setAttribute(PAGE_ID_ATTRIBUTE, action.getPageId());
413 
414         if (action.getUrl() != null) {
415             element.setAttribute(URL_ATTRIBUTE, action.getUrl());
416         }
417 
418         if (action.getForeach() != null) {
419 
420             element.setAttribute(FOREACH_ATTRIBUTE, action.getForeach());
421             element.setAttribute(VARIABLE_ATTRIBUTE, action.getVariable());
422 
423             if (action.getIndex() != null) {
424                 element.setAttribute(INDEX_ATTRIBUTE, action.getIndex());
425             }
426 
427         }
428 
429         if (action.getCondition() != null) {
430 
431             element.setAttribute(CONDITION_ATTRIBUTE, action.getCondition());
432             element.setAttribute(PROPERTY_ID_ATTRIBUTE, action.getPropertyId());
433 
434         }
435 
436         final PageConfiguration configuration = action.getConfiguration();
437 
438         if (configuration != null) {
439             element.addContent(createPageConfigurationElement(configuration));
440         }
441 
442         return element;
443 
444     }
445 
446     private Element createPageStorageConfigurationElement(
447             PageStorageConfiguration configuration) {
448 
449         Element element = new Element(SAVE_TAG);
450         element.setAttribute(FILE_ATTRIBUTE, configuration.getFilename());
451 
452         return element;
453 
454     }
455 
456     private Element createPageConfigurationElement(
457             PageConfiguration configuration) {
458 
459         Element element = new Element(PAGE_CONFIG_TAG);
460         element.addContent(createParameterSetElement(
461                 configuration.getParameterSet()));
462 
463         return element;
464 
465     }
466 
467     private Element createlDataExtractionElement(
468             String tagName, DataExtraction[] dataExtractions) {
469 
470         Element element = new Element(tagName);
471 
472         for (int i = 0; i < dataExtractions.length; i++) {
473             element.addContent(createDataExtractionElement(dataExtractions[i]));
474         }
475 
476         return element;
477 
478     }
479 
480     private Element createStepElement(Step step) {
481 
482         if (step.isDataExtraction()) {
483             return createDataExtractionElement((DataExtraction) step);
484         } else if (step.isPositioningStep()) {
485             return createPositioningStepElement((PositioningStep) step);
486         } else if (step.isLoop()) {
487             return createLoopElement((LoopStep) step);
488         } else if (step.isAction()) {
489             return new Element("actionXMLTagNotImplementedYet");
490         } else {
491             throw new RuntimeException("Unknown extraction step type.");
492         }
493 
494     }
495 
496     private Element createLoopElement(LoopStep loop) {
497 
498         Element element = new Element(LOOP_TAG);
499         element.setAttribute(CONDITION_ATTRIBUTE, loop.getCondition());
500 
501         if (loop.getValue() != null) {
502             element.setAttribute(VALUE_ATTRIBUTE, loop.getValue());
503         }
504 
505         Step[] steps = loop.getSteps();
506 
507         for (int i = 0; i < steps.length; i++) {
508             element.addContent(createStepElement(steps[i]));
509         }
510 
511         return element;
512 
513     }
514 
515     /***
516      * @param step
517      * @return
518      */
519     private Element createPositioningStepElement(PositioningStep step) {
520 
521         Element element = new Element(step.getType());
522 
523         if (step.getBefore() != null) {
524             element.setAttribute(BEFORE_ATTRIBUTE, step.getBefore());
525         }
526 
527         if (step.getDirection() == PositioningStep.BACKWARD) {
528             element.setAttribute(DIRECTION_ATTRIBUTE, BACKWARDS_ATTRIBUTE);
529         }
530 
531         if (step.isMustTrimReference() == false) {
532             element.setAttribute(TRIM_ATTRIBUTE,
533                     String.valueOf(step.isMustTrimReference()));
534         }
535 
536         if (step.isFailIfFound() == true) {
537             element.setAttribute(FAIL_IF_FOUND_ATTRIBUTE,
538                     String.valueOf(step.isFailIfFound()));
539         }
540 
541         element.addContent(new CDATA(step.getReference()));
542 
543         return element;
544 
545     }
546 
547     private Element createDataExtractionElement(DataExtraction dataExtraction) {
548 
549         Element element = new Element(DATA_EXTRACTION_TAG);
550         element.setAttribute(ID_ATTRIBUTE, dataExtraction.getId());
551         element.setAttribute(TRIM_ATTRIBUTE,
552                 String.valueOf(dataExtraction.isTrimValueNeeded()));
553         element.setAttribute(LIST_ATTRIBUTE,
554                 String.valueOf(dataExtraction.isMultivalued()));
555 
556         if (dataExtraction.isMultivalued()) {
557             element.setAttribute(ALLOW_DUPLICATES_ATTRIBUTE,
558                     String.valueOf(dataExtraction.isAllowDuplicates()));
559         }
560 
561         if (dataExtraction.isResetPositionNeeded()) {
562             element.setAttribute(RESET_POSITION_ATTRIBUTE,
563                     String.valueOf(dataExtraction.isResetPositionNeeded()));
564         }
565 
566         if (dataExtraction.isFailOnError() == false) {
567 
568             element.setAttribute(FAIL_ON_ERROR_ATTRIBUTE,
569                     String.valueOf(dataExtraction.isFailOnError()));
570 
571             element.setAttribute(VALUE_ON_ERROR_ATTRIBUTE,
572                     dataExtraction.getValueOnError());
573 
574         }
575 
576         Step[] steps = dataExtraction.getSteps();
577 
578         for (int i = 0; i < steps.length; i++) {
579             element.addContent(createStepElement(steps[i]));
580         }
581 
582         return element;
583 
584     }
585 
586     private Content createStartPageElement(StartPage[] startPages) {
587 
588         Element element = new Element(START_PAGES_TAG);
589 
590         for (int i = 0; i < startPages.length; i++) {
591 
592             StartPage startPage = startPages[i];
593 
594             Element startPageElement = new Element(START_PAGE_TAG);
595             startPageElement.setAttribute(
596                     REFID_ATTRIBUTE, startPage.getRefid());
597 
598             Properties properties = startPage.getProperties();
599 
600             Enumeration enumeration = properties.keys();
601 
602             while (enumeration.hasMoreElements()) {
603 
604                 String name = (String) enumeration.nextElement();
605                 String value = properties.getProperty(name);
606 
607                 Element property = new Element(PROPERTY_TAG);
608                 property.setAttribute(NAME_ATTRIBUTE, name);
609                 property.setAttribute(VALUE_ATTRIBUTE, value);
610 
611                 startPageElement.addContent(property);
612 
613             }
614 
615             element.addContent(startPageElement);
616 
617         }
618 
619         return element;
620 
621     }
622 
623     private Content createParameterSetElement(ParameterSet parameterSet) {
624 
625         Element element = new Element(PARAMETER_SET_TAG);
626 
627         if (parameterSet.getId() != null) {
628             element.setAttribute(ID_ATTRIBUTE, parameterSet.getId());
629         }
630 
631         Map map = parameterSet.getParameters();
632 
633         Iterator iterator = map.keySet().iterator();
634 
635         while (iterator.hasNext()) {
636 
637             String name = (String) iterator.next();
638             Parameter value = (Parameter) map.get(name);
639 
640             Element parameter = new Element(PARAMETER_TAG);
641             parameter.setAttribute(NAME_ATTRIBUTE, name);
642             parameter.setAttribute(VALUE_ATTRIBUTE, value.getValue());
643 
644             element.addContent(parameter);
645 
646         }
647 
648         return element;
649 
650     }
651 
652     private Element createPropertiesElement(Properties properties) {
653 
654         Element element = new Element(PROPERTIES_TAG);
655 
656         Enumeration enumeration = properties.keys();
657 
658         while (enumeration.hasMoreElements()) {
659 
660             String name = (String) enumeration.nextElement();
661             String value = properties.getProperty(name);
662 
663             Element property = new Element(PROPERTY_TAG);
664             property.setAttribute(NAME_ATTRIBUTE, name);
665             property.setAttribute(VALUE_ATTRIBUTE, value);
666 
667             element.addContent(property);
668 
669         }
670 
671         return element;
672 
673     }
674 
675     private Element createOnCatalogStateChangeElement(Collector collector) {
676 
677         Element element = new Element(ON_CATALOG_STATE_CHANGE_TAG);
678         element.setAttribute(CLASS_ATTRIBUTE,
679                 collector.getCatalogStateListenerClassName());
680 
681         return element;
682 
683 
684     }
685 
686     private Content createConfigElement(Collector collector) {
687 
688         Element element = new Element(CONFIG_TAG);
689         CollectorConfiguration configuration = collector.getConfiguration();
690 
691         element.addContent(createHeadersTag(configuration));
692         element.addContent(createThinkTimeTag(configuration));
693         element.addContent(createTimeoutTag(configuration));
694         element.addContent(createRetriesTag(configuration));
695 
696         return element;
697 
698     }
699 
700     private Content createRetriesTag(CollectorConfiguration configuration) {
701 
702         Element element = new Element(RETRIES_TAG);
703         element.setAttribute(VALUE_ATTRIBUTE,
704                 String.valueOf(configuration.getRetries()));
705 
706         return element;
707 
708     }
709 
710     private Content createTimeoutTag(CollectorConfiguration configuration) {
711 
712         Element element = new Element(TIMEOUT_TAG);
713         element.setAttribute(VALUE_ATTRIBUTE,
714                 String.valueOf(configuration.getTimeout()));
715 
716         return element;
717 
718     }
719 
720     private Content createThinkTimeTag(CollectorConfiguration configuration) {
721 
722         Element element = new Element(THINK_TIME_TAG);
723         element.setAttribute(CLASS_ATTRIBUTE,
724                 configuration.getThinkTimeImplementationClassName());
725 
726         Properties properties = configuration.getThinkTimeProperties();
727 
728         Enumeration enumeration = properties.keys();
729 
730         while (enumeration.hasMoreElements()) {
731 
732             String name = (String) enumeration.nextElement();
733             String value = properties.getProperty(name);
734 
735             Element property = new Element(PROPERTY_TAG);
736             property.setAttribute(NAME_ATTRIBUTE, name);
737             property.setAttribute(VALUE_ATTRIBUTE, value);
738 
739             element.addContent(property);
740 
741         }
742 
743         return element;
744 
745     }
746 
747     private Content createHeadersTag(CollectorConfiguration configuration) {
748 
749         Element element = new Element(HEADERS_TAG);
750 
751         final Properties headers = configuration.getHttpHeaders();
752 
753         Enumeration keys = headers.keys();
754 
755         while (keys.hasMoreElements()) {
756 
757             String name = (String) keys.nextElement();
758             String value = headers.getProperty(name);
759 
760             Element header = new Element(HEADER_TAG);
761             header.setAttribute(NAME_ATTRIBUTE, name);
762             header.setAttribute(VALUE_ATTRIBUTE, value);
763 
764             element.addContent(header);
765 
766         }
767 
768         return element;
769 
770     }
771 
772     /***
773      * Loads the collector which id is <code>id</code>.
774      *
775      * @param id a <code>String</code> containing the collector id.
776      * @return a {@link Collector} instance.
777      * @throws SinonException if some error ocurrs.
778      */
779     public Collector load(String id) throws SinonException {
780 
781         File file = new File(collectorDirectory, id + ".xml");
782         Collector collector = null;
783 
784         if (file.exists() == false) {
785 
786             throw new SinonException("There is no collector named " + id +
787                     ".xml in directory " + collectorDirectory);
788 
789         }
790 
791         if (file.canRead() == false) {
792 
793             throw new SinonException("You do not have access to file " + id +
794                     ".xml in directory " + collectorDirectory);
795 
796         }
797 
798         try {
799             collector = processCollectorXMLFile(file, id);
800         } catch (SinonException e) {
801             throw e;
802         } catch (Exception e) {
803             throw new SinonException(e);
804         }
805 
806         return collector;
807 
808     }
809 
810     /***
811      * Reads a collector from an XML file.
812      *
813      * @param file a <code>File</code> pointing to the XML file.
814      * @param id a <code>String</code> containing the collector id.
815      * @return a {@link Collector} instance.
816      */
817     private Collector processCollectorXMLFile(File file, String id)
818             throws SinonException, JDOMException, IOException {
819 
820         SAXBuilder builder = new SAXBuilder();
821 
822         Document doc = builder.build(file);
823 
824         Element collectorTag = doc.getRootElement();
825 
826         if (collectorTag.getName().equals(COLLECTOR_TAG) == false) {
827 
828             throw new SinonException("Every Sinon configuration XML file " +
829                     "must have a " + COLLECTOR_TAG + " as root element");
830 
831         }
832 
833         return parseCollector(collectorTag, id);
834 
835     }
836 
837     private Collector parseCollector(Element collectorTag, String id)
838             throws SinonException {
839 
840         Collector collector = new Collector();
841 
842         String thisId = collectorTag.getAttributeValue(ID_ATTRIBUTE);
843 
844         if (thisId == null || thisId.trim().length() == 0) {
845 
846             throw new SinonException("Tag " + COLLECTOR_TAG + " must have " +
847                     "an " + ID_ATTRIBUTE + " attribute whose value is not " +
848                     "comprised of white space.");
849 
850         }
851 
852         thisId = thisId.trim();
853 
854         if (thisId.equals(id) == false) {
855 
856             throw new SinonException("The value of attribute " + ID_ATTRIBUTE +
857                     " (tag " + COLLECTOR_TAG + ") in file " +
858                     id + COLLECTOR_SUFFIX + " must be " + id + " instead of " +
859                     thisId);
860 
861         }
862 
863         collector.setId(thisId);
864 
865         Element configTag = collectorTag.getChild(CONFIG_TAG);
866 
867         collector.setConfiguration(parseCollectorConfiguration(configTag));
868 
869         Element element = collectorTag.getChild(ON_CATALOG_STATE_CHANGE_TAG);
870 
871         if (element != null) {
872 
873             final String className = element.getAttributeValue(CLASS_ATTRIBUTE);
874             collector.setCatalogStateListenerClassName(className);
875 
876         }
877 
878         addCatalogs(collectorTag, collector);
879 
880         return collector;
881 
882     }
883 
884     private void addCatalogs(Element collectorTag, Collector collector)
885             throws SinonException {
886 
887         List catalogTags = collectorTag.getChildren(CATALOG_TAG);
888 
889         for (int i = 0; i < catalogTags.size(); i++) {
890 
891             Element catalogTag = (Element) catalogTags.get(i);
892             collector.addCatalog(parseCatalog(catalogTag));
893 
894         }
895 
896     }
897 
898     private Catalog parseCatalog(Element catalogTag) throws SinonException {
899 
900         Catalog catalog = new Catalog();
901 
902         currentCatalog = catalog;
903 
904         String id = catalogTag.getAttributeValue(ID_ATTRIBUTE);
905 
906         if (id == null) {
907 
908             throw new SinonException("Tag " + CATALOG_TAG + " must have an " +
909                     ID_ATTRIBUTE + " attribute.");
910 
911         }
912 
913         id = id.trim();
914 
915         if (id.length() == 0) {
916 
917             throw new SinonException("Attribute " + ID_ATTRIBUTE +
918                     " (tag " + CATALOG_TAG + ") cannot " +
919                     "be empty or comprised of only white space.");
920 
921         }
922 
923         catalog.setId(id);
924 
925         addParameterSets(catalogTag, catalog);
926 
927         addDataExtractions(catalogTag, catalog);
928 
929         addPages(catalogTag, catalog);
930 
931         addStartPages(catalogTag, catalog);
932 
933         Element propertiesTag = catalogTag.getChild(PROPERTIES_TAG);
934 
935         if (propertiesTag != null) {
936 
937             List propertyTags = propertiesTag.getChildren(PROPERTY_TAG);
938 
939             final int size = propertyTags.size();
940             Element element;
941             Properties properties = catalog.getProperties();
942 
943             for (int i = 0; i < size; i++) {
944 
945                 element = (Element) propertyTags.get(i);
946                 String name = element.getAttributeValue(NAME_ATTRIBUTE);
947                 String value = element.getAttributeValue(VALUE_ATTRIBUTE);
948 
949                 if (name == null || value == null) {
950 
951                     throw new SinonException("Every property tag must have " +
952                             "a " + NAME_ATTRIBUTE + " and a " +
953                             VALUE_ATTRIBUTE + " attributes.");
954 
955                 }
956 
957                 name = name.trim();
958 
959                 if (name.length() == 0) {
960 
961                     throw new SinonException("Attribute " + NAME_ATTRIBUTE +
962                             " cannot be empty or comprised of only white " +
963                             "space");
964 
965                 }
966 
967                 properties.setProperty(name, value);
968 
969             }
970 
971 
972         }
973 
974         return catalog;
975 
976     }
977 
978     private void addDataExtractions(Element catalogTag, Catalog catalog)
979             throws SinonException {
980 
981         Element globalDataExtractionsTag =
982                 catalogTag.getChild(GLOBAL_DATA_EXTRACTIONS_TAG);
983 
984         if (globalDataExtractionsTag == null) {
985             return;
986         }
987 
988         List dataExtractions =
989                 getGlobalDataExtractions(globalDataExtractionsTag);
990 
991         for (int i = 0; i < dataExtractions.size(); i++) {
992 
993             DataExtraction dataExtraction =
994                     (DataExtraction) dataExtractions.get(i);
995 
996             catalog.addDataExtraction(dataExtraction);
997 
998         }
999 
1000     }
1001 
1002     private List getGlobalDataExtractions(Element element)
1003             throws SinonException {
1004 
1005         ArrayList dataExtractions = new ArrayList();
1006 
1007         List dataExtractionTags = element.getChildren(DATA_EXTRACTION_TAG);
1008 
1009         for (int i = 0; i < dataExtractionTags.size(); i++) {
1010 
1011             Element dataExtractionTag = (Element) dataExtractionTags.get(i);
1012             DataExtraction dataExtraction;
1013 
1014             dataExtraction = getDataExtraction(dataExtractionTag);
1015 
1016             dataExtractions.add(dataExtraction);
1017 
1018         }
1019 
1020         return dataExtractions;
1021 
1022     }
1023 
1024     private List getSteps(Element element) throws SinonException {
1025 
1026         // @todo something is adding a null to stepTags.
1027 
1028         ArrayList steps = new ArrayList();
1029 
1030         List stepTags = element.getChildren();
1031 
1032         for (int i = 0; i < stepTags.size(); i++) {
1033 
1034             Element dataExtractionTag = (Element) stepTags.get(i);
1035             String type = dataExtractionTag.getName();
1036             DataExtraction dataExtraction = null;
1037 
1038             if (type.equals(DATA_EXTRACTION_TAG)) {
1039 
1040                 dataExtraction = getDataExtraction(dataExtractionTag);
1041 
1042             } else if (type.equals(DO_DATA_EXTRACTION_TAG)) {
1043 
1044                 dataExtraction = getDoDataExtraction(dataExtractionTag);
1045 
1046             } else if (type.equals(LOOP_TAG)) {
1047 
1048                 LoopStep loopStep = getLoopStep(dataExtractionTag);
1049                 steps.add(loopStep);
1050 
1051                 continue;
1052 
1053             } else if (type.equals(ACTION_TAG)) {
1054 
1055                 throw new SinonException("The action tag cannot be put " +
1056                         "inside a data extraction or loop.");
1057 
1058             } else {
1059 
1060                 PositioningStep step = getPositioningStep(dataExtractionTag);
1061                 steps.add(step);
1062 
1063                 continue;
1064 
1065             }
1066 
1067             String resetPosition = dataExtractionTag.getAttributeValue(
1068                     RESET_POSITION_ATTRIBUTE);
1069 
1070             if (resetPosition != null) {
1071 
1072                 Boolean mustResetPosition = Boolean.valueOf(resetPosition);
1073                 dataExtraction.setResetPositionNeeded(
1074                         mustResetPosition.booleanValue());
1075 
1076             }
1077 
1078             steps.add(dataExtraction);
1079 
1080         }
1081 
1082         return steps;
1083 
1084     }
1085 
1086     private Action getAction(Element element) throws SinonException {
1087 
1088         Action action = new Action();
1089 
1090         String pageId = element.getAttributeValue(PAGE_ID_ATTRIBUTE);
1091         action.setPageId(pageId);
1092 
1093         if (pageId == null || pageId.trim().length() == 0) {
1094 
1095             throw new SinonException("Every action tag must have a " +
1096                     PAGE_ID_ATTRIBUTE + " attribute.");
1097 
1098         }
1099 
1100         String foreach = element.getAttributeValue(FOREACH_ATTRIBUTE);
1101         action.setForeach(foreach);
1102 
1103         String variable = element.getAttributeValue(VARIABLE_ATTRIBUTE);
1104         action.setVariable(variable);
1105 
1106         if (foreach != null && variable == null) {
1107 
1108             throw new SinonException("Every action tag with a + " +
1109                     FOREACH_ATTRIBUTE + " must have a " + VARIABLE_ATTRIBUTE +
1110                     " attribute.");
1111 
1112         }
1113 
1114         String index = element.getAttributeValue(INDEX_ATTRIBUTE);
1115         action.setIndex(index);
1116 
1117         String condition = element.getAttributeValue(CONDITION_ATTRIBUTE);
1118         action.setCondition(condition);
1119 
1120         String propertyId = element.getAttributeValue(PROPERTY_ID_ATTRIBUTE);
1121         action.setPropertyId(propertyId);
1122 
1123         if (condition != null && propertyId == null) {
1124 
1125             throw new SinonException("Todo action com " + CONDITION_ATTRIBUTE +
1126                     " deve ter um atributo " + PROPERTY_ID_ATTRIBUTE);
1127 
1128         }
1129 
1130         String url = element.getAttributeValue(URL_ATTRIBUTE);
1131 
1132         if (url != null) {
1133 
1134             url = url.trim();
1135 
1136             if (url.length() == 0) {
1137 
1138                 throw new SinonException("Attribute " + URL_ATTRIBUTE +
1139                         " (tag " + ACTION_TAG + ") is optional but cannot " +
1140                         "be empty or comprised of only white space.");
1141 
1142             }
1143 
1144             action.setUrl(url);
1145 
1146         }
1147 
1148         Element pageConfigurationTag = element.getChild(PAGE_CONFIG_TAG);
1149 
1150         if (pageConfigurationTag != null) {
1151 
1152             PageConfiguration pageConfiguration =
1153                     parsePageConfiguration(pageConfigurationTag);
1154 
1155             action.setConfiguration(pageConfiguration);
1156 
1157         }
1158 
1159         return action;
1160 
1161     }
1162 
1163     private PositioningStep getPositioningStep(Element element) {
1164 
1165         PositioningStep step = new PositioningStep();
1166 
1167         step.setType(element.getName());
1168 
1169         String direction = element.getAttributeValue(DIRECTION_ATTRIBUTE);
1170 
1171         if (direction != null) {
1172 
1173             boolean backwards = direction.equalsIgnoreCase(BACKWARDS_ATTRIBUTE);
1174             if (backwards) {
1175                 step.setDirection(PositioningStep.BACKWARD);
1176             } else {
1177                 step.setDirection(PositioningStep.FORWARD);
1178             }
1179 
1180         }
1181 
1182         String before = element.getAttributeValue(BEFORE_ATTRIBUTE);
1183         step.setBefore(before);
1184 
1185         String mustTrimReference = element.getAttributeValue(TRIM_ATTRIBUTE);
1186 
1187         Boolean trim;
1188 
1189         if (mustTrimReference != null) {
1190             trim = Boolean.valueOf(mustTrimReference);
1191         } else {
1192             trim = Boolean.TRUE;
1193         }
1194 
1195         step.setMustTrimReference(trim.booleanValue());
1196 
1197         StringBuffer referenceBuffer = new StringBuffer();
1198 
1199         List contents = element.getContent();
1200 
1201         final int size = contents.size();
1202         Content content;
1203 
1204         for (int i = 0; i < size; i++) {
1205 
1206             content = (Content) contents.get(i);
1207 
1208             if (content instanceof Text) {
1209 
1210                 Text text = (Text) content;
1211                 referenceBuffer.append(text.getText());
1212 
1213             } else if (content instanceof Element) {
1214 
1215                 Element text = (Element) content;
1216 
1217                 if (text.getName().equals(NEWLINE_TAG)) {
1218                     referenceBuffer.append("\n");
1219                 }
1220 
1221             }
1222 
1223         }
1224 
1225         String reference = referenceBuffer.toString();
1226 
1227         if (trim.booleanValue()) {
1228             reference = reference.trim();
1229         }
1230 
1231         step.setReference(reference);
1232 
1233         String failIfFound = element.getAttributeValue(FAIL_IF_FOUND_ATTRIBUTE);
1234 
1235         if (failIfFound != null) {
1236             Boolean fif = Boolean.valueOf(failIfFound);
1237             step.setFailIfFound(fif.booleanValue());
1238         }
1239 
1240         return step;
1241 
1242     }
1243 
1244     private LoopStep getLoopStep(Element element)
1245             throws SinonException {
1246 
1247         LoopStep loopStep = new LoopStep();
1248 
1249         String condition = element.getAttributeValue(CONDITION_ATTRIBUTE);
1250         loopStep.setCondition(condition);
1251 
1252         if (condition == null) {
1253 
1254             throw new SinonException("Tag " + LOOP_TAG + " must have a  " +
1255                     CONDITION_ATTRIBUTE + " attribute.");
1256 
1257         }
1258 
1259         condition = condition.trim();
1260 
1261         if (condition.length() == 0) {
1262 
1263             throw new SinonException("Attribute " + CONDITION_ATTRIBUTE +
1264                     " (tag " + LOOP_TAG + ") cannot be empty of comprised of " +
1265                     "only white space");
1266 
1267         }
1268 
1269         String value = element.getAttributeValue(VALUE_ATTRIBUTE);
1270         loopStep.setValue(value);
1271 
1272         List steps = getSteps(element);
1273 
1274         for (int i = 0; i < steps.size(); i++) {
1275 
1276             Step step = (Step) steps.get(i);
1277             loopStep.addStep(step);
1278 
1279         }
1280 
1281         return loopStep;
1282 
1283     }
1284 
1285     private DataExtraction getDoDataExtraction(Element dataExtractionTag)
1286             throws SinonException {
1287 
1288         DataExtraction dataExtraction;
1289         String refid =
1290                 dataExtractionTag.getAttributeValue(REFID_ATTRIBUTE);
1291 
1292         DataExtraction referred =
1293                 currentCatalog.getDataExtraction(refid);
1294 
1295         if (referred == null) {
1296 
1297             throw new SinonException("There is no data extraction whose id " +
1298                     "is " + refid);
1299 
1300         }
1301 
1302         dataExtraction = referred.copy();
1303 
1304         if (dataExtraction == null) {
1305 
1306             throw new SinonException("There is no data extraction whose id " +
1307                     "is " + refid);
1308 
1309         }
1310 
1311         return dataExtraction;
1312 
1313     }
1314 
1315     private DataExtraction getDataExtraction(Element dataExtractionTag)
1316             throws SinonException {
1317 
1318         DataExtraction dataExtraction;
1319         dataExtraction = new DataExtraction();
1320 
1321         String id = dataExtractionTag.getAttributeValue(ID_ATTRIBUTE);
1322         dataExtraction.setId(id);
1323 
1324         if (id == null) {
1325 
1326             throw new SinonException("Every " + DATA_EXTRACTION_TAG +
1327                     " tag must have an " + ID_ATTRIBUTE + " attribute");
1328 
1329         }
1330 
1331         id = id.trim();
1332 
1333         if (id.length() == 0) {
1334 
1335             throw new SinonException("Attribute " + ID_ATTRIBUTE +
1336                     " (tag " + DATA_EXTRACTION_TAG +
1337                     ") cannot be empty or comprised of only white space.");
1338 
1339         }
1340 
1341         String trimValue =
1342                 dataExtractionTag.getAttributeValue(TRIM_ATTRIBUTE);
1343 
1344         if (trimValue != null) {
1345 
1346             Boolean trim = Boolean.valueOf(trimValue);
1347             dataExtraction.setTrimValueNeeded(trim.booleanValue());
1348 
1349         }
1350 
1351         String multivaluedString =
1352                 dataExtractionTag.getAttributeValue(LIST_ATTRIBUTE);
1353 
1354         if (multivaluedString != null) {
1355 
1356             Boolean multivalued = Boolean.valueOf(multivaluedString);
1357             dataExtraction.setMultivalued(multivalued.booleanValue());
1358 
1359         }
1360 
1361         String failOnErrorString =
1362                 dataExtractionTag.getAttributeValue(FAIL_ON_ERROR_ATTRIBUTE);
1363 
1364         if (failOnErrorString != null) {
1365 
1366             Boolean failOnError = Boolean.valueOf(failOnErrorString);
1367             dataExtraction.setFailOnError(failOnError.booleanValue());
1368 
1369         }
1370 
1371         String allowDuplicatesString =
1372                 dataExtractionTag.getAttributeValue(ALLOW_DUPLICATES_ATTRIBUTE);
1373 
1374         if (allowDuplicatesString != null) {
1375 
1376             Boolean allowDuplicates = Boolean.valueOf(allowDuplicatesString);
1377             dataExtraction.setAllowDuplicates(allowDuplicates.booleanValue());
1378 
1379         }
1380 
1381         String valueOnError =
1382                 dataExtractionTag.getAttributeValue(VALUE_ON_ERROR_ATTRIBUTE);
1383 
1384         if (valueOnError != null) {
1385             dataExtraction.setValueOnError(valueOnError);
1386         }
1387 
1388         List steps = getSteps(dataExtractionTag);
1389 
1390         for (int i = 0; i < steps.size(); i++) {
1391 
1392             Step step = (Step) steps.get(i);
1393             dataExtraction.addStep(step);
1394 
1395         }
1396 
1397         return dataExtraction;
1398 
1399     }
1400 
1401     private void addStartPages(Element catalogTag, Catalog catalog)
1402             throws SinonException {
1403 
1404         Element startPagesTag = catalogTag.getChild(START_PAGES_TAG);
1405 
1406         if (startPagesTag == null) {
1407 
1408             throw new SinonException("Catalog " + catalog.getId() + " does " +
1409                     "not have any " + START_PAGES_TAG + " tag.");
1410 
1411         }
1412 
1413         List startPageTags = startPagesTag.getChildren(START_PAGE_TAG);
1414 
1415         if (startPageTags.size() == 0) {
1416 
1417             throw new SinonException("Catalog " + catalog.getId() + " does " +
1418                     "not have any start page.");
1419 
1420         }
1421 
1422         for (int i = 0; i < startPageTags.size(); i++) {
1423 
1424             Element element = (Element) startPageTags.get(i);
1425             StartPage startPage = new StartPage();
1426 
1427             String refid = element.getAttributeValue(REFID_ATTRIBUTE);
1428 
1429             if (refid == null) {
1430 
1431                 throw new SinonException("Every " + START_PAGE_TAG + " must " +
1432                         "have a " + REFID_ATTRIBUTE + " attribute");
1433 
1434             }
1435 
1436             startPage.setRefid(refid);
1437 
1438             Properties properties = new Properties();
1439 
1440             List propertyTags = element.getChildren(PROPERTY_TAG);
1441 
1442             for (int j = 0; j < propertyTags.size(); j++) {
1443 
1444                 Element propertyElement = (Element) propertyTags.get(j);
1445 
1446                 String name = propertyElement.getAttributeValue(NAME_ATTRIBUTE);
1447                 String value =
1448                         propertyElement.getAttributeValue(VALUE_ATTRIBUTE);
1449 
1450                 properties.setProperty(name, value);
1451 
1452             }
1453 
1454             startPage.setProperties(properties);
1455 
1456             catalog.addStartPage(startPage);
1457 
1458         }
1459 
1460     }
1461 
1462     private void addPages(Element catalogTag, Catalog catalog)
1463             throws SinonException {
1464 
1465         List pageTags = catalogTag.getChildren(PAGE_TAG);
1466 
1467         for (int i = 0; i < pageTags.size(); i++) {
1468 
1469             Element element = (Element) pageTags.get(i);
1470             catalog.addPage(parsePage(element));
1471 
1472         }
1473 
1474     }
1475 
1476     private void addParameterSets(Element catalogTag, Catalog catalog)
1477             throws SinonException {
1478 
1479         List parameterSetTags = catalogTag.getChildren(PARAMETER_SET_TAG);
1480 
1481         for (int i = 0; i < parameterSetTags.size(); i++) {
1482 
1483             Element element = (Element) parameterSetTags.get(i);
1484             catalog.addParameterSet(parseParameterSet(element));
1485 
1486         }
1487 
1488     }
1489 
1490     private Page parsePage(Element pageTag) throws SinonException {
1491 
1492         Page page = new Page();
1493 
1494         String id = pageTag.getAttributeValue(ID_ATTRIBUTE);
1495 
1496         if (id == null) {
1497 
1498             throw new SinonException("Every " + PAGE_TAG + " tag must have " +
1499                     "an " + ID_ATTRIBUTE + " attribute");
1500 
1501         }
1502 
1503         id = id.trim();
1504 
1505         if (id.length() == 0) {
1506 
1507             throw new SinonException("Attribute " + ID_ATTRIBUTE +
1508                     " (tag " + PAGE_TAG + ") cannot be empty or " +
1509                     "comprised of only white space.");
1510 
1511         }
1512 
1513         page.setId(id);
1514 
1515         String followRedirectAttribute =
1516                 pageTag.getAttributeValue(FOLLOW_REDIRECT_ATTRIBUTE);
1517 
1518         if (followRedirectAttribute != null) {
1519 
1520             final Boolean followRedirect =
1521                     Boolean.valueOf(followRedirectAttribute);
1522             page.setFollowRedirects(followRedirect.booleanValue());
1523 
1524         }
1525 
1526         String urlAttribute = pageTag.getAttributeValue(URL_ATTRIBUTE);
1527 
1528         page.setUrl(urlAttribute);
1529 
1530         String methodAttribute = pageTag.getAttributeValue(METHOD_ATTRIBUTE);
1531 
1532         if (methodAttribute != null) {
1533 
1534             if (methodAttribute.equalsIgnoreCase("post")) {
1535                 page.setMethod(Page.POST_METHOD);
1536             } else if (methodAttribute.equalsIgnoreCase("get")) {
1537                 page.setMethod(Page.GET_METHOD);
1538             } else {
1539                 throw new SinonException("Unknown method: " + methodAttribute);
1540             }
1541 
1542         }
1543 
1544         Element pageConfigurationTag = pageTag.getChild(PAGE_CONFIG_TAG);
1545 
1546         if (pageConfigurationTag != null) {
1547             page.setConfiguration(parsePageConfiguration(pageConfigurationTag));
1548         }
1549 
1550         Element storageConfigurationTag = pageTag.getChild(SAVE_TAG);
1551 
1552         if (storageConfigurationTag != null) {
1553 
1554             String file =
1555                     storageConfigurationTag.getAttributeValue(FILE_ATTRIBUTE);
1556 
1557             if (file == null) {
1558 
1559                 throw new SinonException("Tag " + SAVE_TAG + " must have a " +
1560                         FILE_ATTRIBUTE + " attribute");
1561 
1562             }
1563 
1564             file = file.trim();
1565 
1566             if (file.length() == 0) {
1567 
1568                 throw new SinonException("Attribute " + FILE_ATTRIBUTE +
1569                         " (tag " + SAVE_TAG + ") cannot be empty or " +
1570                         "comprised of only white space.");
1571 
1572             }
1573 
1574             page.getStorageConfiguration().setFilename(file);
1575 
1576         }
1577 
1578         addSteps(pageTag, page);
1579 
1580         List actions = pageTag.getChildren(ACTION_TAG);
1581 
1582         for (int i = 0; i < actions.size(); i++) {
1583 
1584             Element element = (Element) actions.get(i);
1585             page.addAction(getAction(element));
1586 
1587         }
1588 
1589         Element listenersTag = pageTag.getChild(PAGE_LISTENERS_TAG);
1590 
1591         if (listenersTag != null) {
1592             addListeners(listenersTag, page);
1593         }
1594 
1595         return page;
1596 
1597     }
1598 
1599     private void addListeners(Element listenersTag, Page page)
1600             throws SinonException {
1601 
1602         List listeners = listenersTag.getChildren();
1603 
1604         for (int i = 0; i < listeners.size(); i++) {
1605 
1606             Element element = (Element) listeners.get(i);
1607             String type = element.getName();
1608 
1609             String className = element.getAttributeValue(CLASS_ATTRIBUTE);
1610 
1611             if (type.equals(ON_ERROR_TAG)) {
1612 
1613                 if (className == null || className.trim().length() == 0) {
1614 
1615                     throw new SinonException("Tag " + ON_ERROR_TAG +
1616                             " must have a " + CLASS_ATTRIBUTE +
1617                             " attribute");
1618 
1619                 }
1620 
1621                 page.setErrorListenerClassName(className);
1622 
1623             } else if (type.equals(ON_STATE_CHANGE_TAG)) {
1624 
1625                 if (className == null || className.trim().length() == 0) {
1626 
1627                     throw new SinonException("Tag " + ON_STATE_CHANGE_TAG +
1628                             " must have a " + CLASS_ATTRIBUTE + " attribute.");
1629 
1630                 }
1631 
1632                 page.setPageStateListenerClassName(className);
1633 
1634             } else if (type.equals(ON_PROMPT_TAG)) {
1635 
1636                 if (className == null || className.trim().length() == 0) {
1637 
1638                     throw new SinonException("Tag " + ON_PROMPT_TAG +
1639                             " must have an " + CLASS_ATTRIBUTE + " attribute");
1640 
1641                 }
1642 
1643                 page.setPromptListenerClassName(className);
1644 
1645             } else {
1646                 throw new SinonException("Unknown listener type: " + type);
1647             }
1648 
1649         }
1650 
1651     }
1652 
1653     /***
1654      * Chamado por {@link #parsePage}.
1655      *
1656      * @param pageTag
1657      * @param page
1658      * @throws SinonException
1659      */
1660     private void addSteps(Element pageTag, Page page)
1661             throws SinonException {
1662 
1663         Element dataExtractionsTag = pageTag.getChild(DATA_EXTRACTIONS_TAG);
1664 
1665         if (dataExtractionsTag == null) {
1666             return;
1667         }
1668 
1669         List steps = getSteps(dataExtractionsTag);
1670 
1671         final int size = steps.size();
1672         Step step;
1673 
1674         for (int i = 0; i < size; i++) {
1675 
1676             step = (Step) steps.get(i);
1677             page.addStep(step);
1678 
1679         }
1680 
1681     }
1682 
1683     private PageConfiguration parsePageConfiguration(
1684             Element pageConfigurationTag)
1685             throws SinonException {
1686 
1687         PageConfiguration pageConfiguration = new PageConfiguration();
1688 
1689         Element parameterSetTag =
1690                 pageConfigurationTag.getChild(PARAMETER_SET_TAG);
1691 
1692         pageConfiguration.setParameterSet(parseParameterSet(parameterSetTag));
1693 
1694         return pageConfiguration;
1695 
1696     }
1697 
1698     /***
1699      * @param parameterSetTag
1700      * @return
1701      */
1702     private ParameterSet parseParameterSet(Element parameterSetTag)
1703             throws SinonException {
1704 
1705         ParameterSet parameterSet;
1706 
1707         Element includeTag =
1708                 parameterSetTag.getChild(INCLUDE_PARAMETER_SET_TAG);
1709 
1710         if (includeTag != null) {
1711 
1712             String refid = includeTag.getAttributeValue(REFID_ATTRIBUTE);
1713             ParameterSet included = currentCatalog.getParameterSet(refid);
1714 
1715             if (included == null) {
1716 
1717                 throw new SinonException("There is no parameter set with id " +
1718                         refid);
1719 
1720             }
1721 
1722             parameterSet = new ParameterSet(included);
1723 
1724         } else {
1725             parameterSet = new ParameterSet();
1726         }
1727 
1728         List parameters = parameterSetTag.getChildren(PARAMETER_TAG);
1729 
1730         String id = parameterSetTag.getAttributeValue(ID_ATTRIBUTE);
1731         parameterSet.setId(id);
1732 
1733         for (int i = 0; i < parameters.size(); i++) {
1734 
1735             Element parameter = (Element) parameters.get(i);
1736 
1737             String name = parameter.getAttributeValue(NAME_ATTRIBUTE);
1738             String value = parameter.getAttributeValue(VALUE_ATTRIBUTE);
1739             String encoding =
1740                     parameter.getAttributeValue(ENCODING_ATTRIBUTE);
1741 
1742             if (name == null || value == null) {
1743 
1744                 throw new SinonException("Tag " + PROPERTY_TAG + " must have " +
1745                         " a " + NAME_ATTRIBUTE + " and a " +
1746                         VALUE_ATTRIBUTE + " attributes.");
1747 
1748             }
1749 
1750             name = name.trim();
1751 
1752             if (name.length() == 0) {
1753 
1754                 throw new SinonException("Attribute " + NAME_ATTRIBUTE +
1755                         " cannot be empty or comprised of only white space.");
1756 
1757             }
1758 
1759             Parameter newParameter = new Parameter();
1760 
1761             if (encoding != null) {
1762                 newParameter.setEncoding(encoding);
1763             }
1764 
1765             newParameter.setName(name);
1766             newParameter.setValue(value);
1767 
1768             parameterSet.setParameter(newParameter);
1769 
1770         }
1771 
1772         return parameterSet;
1773 
1774     }
1775 
1776     /***
1777      * Retorna um {@link CollectorConfiguration} de acordo com a descrição
1778      * em <code>configTag</code>.
1779      *
1780      * @param configTag um <code>org.jdom.Element</code> descrevendo um
1781      *                  {@link CollectorConfiguration}.
1782      * @return um {@link CollectorConfiguration}.
1783      */
1784     private CollectorConfiguration parseCollectorConfiguration(
1785             Element configTag)
1786             throws SinonException {
1787 
1788         if (configTag == null) {
1789 
1790             throw new SinonException("Every " + COLLECTOR_TAG + " tag " +
1791                     " must contain a " + CONFIG_TAG + " tag.");
1792 
1793         }
1794 
1795         CollectorConfiguration configuration = new CollectorConfiguration();
1796 
1797         Element httpHeadersTag = configTag.getChild(HEADERS_TAG);
1798 
1799         if (httpHeadersTag != null) {
1800 
1801             List httpHeaderTag = httpHeadersTag.getChildren(HEADER_TAG);
1802 
1803             for (int i = 0; i < httpHeaderTag.size(); i++) {
1804 
1805                 Element element = (Element) httpHeaderTag.get(i);
1806                 String name = element.getAttributeValue(NAME_ATTRIBUTE);
1807                 String value = element.getAttributeValue(VALUE_ATTRIBUTE);
1808 
1809                 if (name == null || value == null) {
1810 
1811                     throw new SinonException("Every " + HEADER_TAG + " tag " +
1812                             " must have a " + NAME_ATTRIBUTE + " and a " +
1813                             VALUE_ATTRIBUTE + " attributes");
1814 
1815                 }
1816 
1817                 name = name.trim();
1818 
1819                 if (name.length() == 0) {
1820 
1821                     throw new SinonException("The " + NAME_ATTRIBUTE +
1822                             " name attribute (header tag) cannot be empty " +
1823                             "or comprised of white space only.");
1824 
1825                 }
1826 
1827                 configuration.setHTTPHeader(name, value);
1828 
1829             }
1830 
1831         }
1832 
1833         Element thinkTimeTag = configTag.getChild(THINK_TIME_TAG);
1834 
1835         addThinkTime(configuration, thinkTimeTag);
1836 
1837         addTimeout(configTag, configuration);
1838 
1839         addRetries(configTag, configuration);
1840 
1841         return configuration;
1842 
1843     }
1844 
1845     private void addRetries(Element configTag,
1846                             CollectorConfiguration configuration)
1847             throws SinonException {
1848 
1849         Element retriesTag = configTag.getChild(RETRIES_TAG);
1850 
1851         if (retriesTag == null) {
1852 
1853             throw new SinonException("Every " + CONFIG_TAG + " tag must " +
1854                     "contain a " + RETRIES_TAG + " tag.");
1855 
1856         }
1857 
1858         String retries = retriesTag.getAttributeValue(VALUE_ATTRIBUTE);
1859 
1860         if (retries == null) {
1861 
1862             throw new SinonException("Every " + RETRIES_TAG + " tag must " +
1863                     "have a " + VALUE_ATTRIBUTE + " attribute.");
1864 
1865         }
1866 
1867         boolean throwException = false;
1868 
1869         try {
1870             configuration.setRetries(Integer.parseInt(retries));
1871         } catch (NumberFormatException e) {
1872             throwException = true;
1873         }
1874 
1875         if (throwException || configuration.getRetries() < 0) {
1876             throw new SinonException("Invalid retries value : " + retries);
1877         }
1878 
1879     }
1880 
1881     private void addTimeout(Element configTag,
1882                             CollectorConfiguration configuration)
1883             throws SinonException {
1884 
1885         Element timeoutTag = configTag.getChild(TIMEOUT_TAG);
1886 
1887         if (timeoutTag == null) {
1888 
1889             throw new SinonException("Every " + CONFIG_TAG + " " +
1890                     "tag must contain a " + TIMEOUT_TAG + " tag.");
1891 
1892         }
1893 
1894         String timeout = timeoutTag.getAttributeValue(VALUE_ATTRIBUTE);
1895 
1896         if (timeout == null) {
1897 
1898             throw new SinonException("Every " + TIMEOUT_TAG + " tag must " +
1899                     "have a " + VALUE_ATTRIBUTE + " attribute.");
1900 
1901         }
1902 
1903         boolean throwException = false;
1904 
1905         try {
1906             configuration.setTimeout(Integer.parseInt(timeout));
1907         } catch (NumberFormatException e) {
1908             throwException = true;
1909         }
1910 
1911         if (throwException || configuration.getTimeout() < 0) {
1912 
1913             throw new SinonException("Invalid timeout value :  " + timeout);
1914 
1915         }
1916 
1917     }
1918 
1919     private void addThinkTime(CollectorConfiguration configuration,
1920                               Element thinkTimeTag) throws SinonException {
1921 
1922         if (thinkTimeTag == null) {
1923 
1924             throw new SinonException("Every " + CONFIG_TAG + " tag must " +
1925                     "contain a " + THINK_TIME_TAG + " tag.");
1926 
1927         }
1928 
1929         String thinkTimeImplementationName =
1930                 thinkTimeTag.getAttributeValue(CLASS_ATTRIBUTE);
1931 
1932         if (thinkTimeImplementationName == null) {
1933 
1934             throw new SinonException("Every " + THINK_TIME_TAG + " must have " +
1935                     " a " + CLASS_ATTRIBUTE + " attribute.");
1936 
1937         }
1938 
1939         thinkTimeImplementationName = thinkTimeImplementationName.trim();
1940 
1941         if (thinkTimeImplementationName.length() == 0) {
1942 
1943             throw new SinonException("Attribute " + CLASS_ATTRIBUTE +
1944                     "(tag " + THINK_TIME_TAG + ") cannot be empty or " +
1945                     "comprised of white space only.");
1946 
1947         }
1948 
1949         configuration.setThinkTimeImplementationClassName(
1950                 thinkTimeImplementationName);
1951 
1952         List propertyTags = thinkTimeTag.getChildren(PROPERTY_TAG);
1953 
1954         for (int i = 0; i < propertyTags.size(); i++) {
1955 
1956             Element element = (Element) propertyTags.get(i);
1957             String name = element.getAttributeValue(NAME_ATTRIBUTE);
1958             String value = element.getAttributeValue(VALUE_ATTRIBUTE);
1959 
1960             configuration.setThinkTimeProperty(name, value);
1961 
1962         }
1963 
1964     }
1965 
1966     /***
1967      * <code>FileFilter</code> que aceita apenas arquivos com
1968      * sufixo <code>.xml</code>.
1969      */
1970     private static class XMLFileFilter implements FileFilter {
1971 
1972         public boolean accept(File pathname) {
1973             return pathname.getAbsolutePath().endsWith(COLLECTOR_SUFFIX);
1974         }
1975 
1976     }
1977 
1978 }