001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.tools.seleniumbuilder;
016    
017    import com.liferay.portal.freemarker.FreeMarkerUtil;
018    import com.liferay.portal.kernel.io.unsync.UnsyncBufferedReader;
019    import com.liferay.portal.kernel.io.unsync.UnsyncStringReader;
020    import com.liferay.portal.kernel.util.ArrayUtil;
021    import com.liferay.portal.kernel.util.CharPool;
022    import com.liferay.portal.kernel.util.FileUtil;
023    import com.liferay.portal.kernel.util.ListUtil;
024    import com.liferay.portal.kernel.util.StringBundler;
025    import com.liferay.portal.kernel.util.StringPool;
026    import com.liferay.portal.kernel.util.StringUtil;
027    import com.liferay.portal.kernel.util.TextFormatter;
028    import com.liferay.portal.kernel.util.Validator;
029    import com.liferay.portal.kernel.xml.Attribute;
030    import com.liferay.portal.kernel.xml.Document;
031    import com.liferay.portal.kernel.xml.DocumentException;
032    import com.liferay.portal.kernel.xml.Element;
033    import com.liferay.portal.kernel.xml.UnsecureSAXReaderUtil;
034    import com.liferay.portal.tools.ToolsUtil;
035    
036    import java.io.ByteArrayInputStream;
037    import java.io.File;
038    import java.io.InputStream;
039    
040    import java.util.ArrayList;
041    import java.util.HashMap;
042    import java.util.HashSet;
043    import java.util.List;
044    import java.util.Map;
045    import java.util.Properties;
046    import java.util.Set;
047    import java.util.TreeSet;
048    import java.util.regex.Matcher;
049    import java.util.regex.Pattern;
050    
051    import javax.xml.xpath.XPath;
052    import javax.xml.xpath.XPathFactory;
053    
054    import org.apache.commons.lang.StringEscapeUtils;
055    
056    /**
057     * @author Michael Hashimoto
058     */
059    public class SeleniumBuilderFileUtil {
060    
061            public SeleniumBuilderFileUtil(String baseDirName, String projectDirName) {
062                    _baseDirName = baseDirName;
063    
064                    try {
065                            String content = FileUtil.read(projectDirName + "/test.properties");
066    
067                            InputStream inputStream = new ByteArrayInputStream(
068                                    content.getBytes());
069    
070                            _properties.load(inputStream);
071                    }
072                    catch (Exception e) {
073                            e.printStackTrace();
074                    }
075    
076                    _componentNames = ListUtil.fromArray(
077                            StringUtil.split(getProperty("component.names")));
078                    _testCaseAvailablePropertyNames = ListUtil.fromArray(
079                            StringUtil.split(
080                                    getProperty("test.case.available.property.names")));
081                    _testrayAvailableComponentNames = ListUtil.fromArray(
082                            StringUtil.split(getProperty("testray.available.component.names")));
083            }
084    
085            public String escapeHtml(String input) {
086                    return StringEscapeUtils.escapeHtml(input);
087            }
088    
089            public String escapeJava(String input) {
090                    return StringEscapeUtils.escapeJava(input);
091            }
092    
093            public List<Element> getAllChildElements(
094                    Element element, String elementName) {
095    
096                    List<Element> allChildElements = new ArrayList<>();
097    
098                    List<Element> childElements = element.elements();
099    
100                    if (childElements.isEmpty()) {
101                            return allChildElements;
102                    }
103    
104                    for (Element childElement : childElements) {
105                            String childElementName = childElement.getName();
106    
107                            if (childElementName.equals(elementName)) {
108                                    allChildElements.add(childElement);
109                            }
110    
111                            allChildElements.addAll(
112                                    getAllChildElements(childElement, elementName));
113                    }
114    
115                    return allChildElements;
116            }
117    
118            public String getBaseDirName() {
119                    return _baseDirName;
120            }
121    
122            public Set<String> getChildElementAttributeValues(
123                    Element element, String attributeName) {
124    
125                    Set<String> childElementAttributeValues = new TreeSet<>();
126    
127                    List<Element> childElements = element.elements();
128    
129                    if (childElements.isEmpty()) {
130                            return childElementAttributeValues;
131                    }
132    
133                    for (Element childElement : childElements) {
134                            String childElementName = childElement.attributeValue(
135                                    attributeName);
136    
137                            if (childElementName != null) {
138                                    int x = childElementName.lastIndexOf(StringPool.POUND);
139    
140                                    if (x != -1) {
141                                            childElementAttributeValues.add(
142                                                    childElementName.substring(0, x));
143                                    }
144                                    else if (attributeName.equals("function")) {
145                                            childElementAttributeValues.add(childElementName);
146                                    }
147                            }
148    
149                            childElementAttributeValues.addAll(
150                                    getChildElementAttributeValues(childElement, attributeName));
151                    }
152    
153                    return childElementAttributeValues;
154            }
155    
156            public Set<String> getChildElementLineNumbers(Element element) {
157                    Set<String> childElementLineNumbers = new TreeSet<>();
158    
159                    List<Element> childElements = element.elements();
160    
161                    if (childElements.isEmpty()) {
162                            return childElementLineNumbers;
163                    }
164    
165                    for (Element childElement : childElements) {
166                            String childElementLineNumber = childElement.attributeValue(
167                                    "line-number");
168    
169                            if (childElementLineNumber != null) {
170                                    childElementLineNumbers.add(childElementLineNumber);
171                            }
172    
173                            childElementLineNumbers.addAll(
174                                    getChildElementLineNumbers(childElement));
175                    }
176    
177                    return childElementLineNumbers;
178            }
179    
180            public String getClassName(String fileName) {
181                    String classSuffix = getClassSuffix(fileName);
182    
183                    return getClassName(fileName, classSuffix);
184            }
185    
186            public String getClassName(String fileName, String classSuffix) {
187                    return
188                            getPackageName(fileName) + "." +
189                                    getSimpleClassName(fileName, classSuffix);
190            }
191    
192            public String getClassSimpleClassName(String className) {
193                    int x = className.lastIndexOf(CharPool.PERIOD);
194    
195                    return className.substring(x + 1);
196            }
197    
198            public String getClassSuffix(String fileName) {
199                    int x = fileName.indexOf(CharPool.PERIOD);
200    
201                    String classSuffix = StringUtil.upperCaseFirstLetter(
202                            fileName.substring(x + 1));
203    
204                    if (classSuffix.equals("Testcase")) {
205                            classSuffix = "TestCase";
206                    }
207    
208                    return classSuffix;
209            }
210    
211            public List<String> getComponentNames() {
212                    return _componentNames;
213            }
214    
215            public String getDefaultCommandName(Element rootElement) {
216                    return rootElement.attributeValue("default");
217            }
218    
219            public String getHTMLFileName(String fileName) {
220                    String javaFileName = getJavaFileName(fileName);
221    
222                    return StringUtil.replace(javaFileName, ".java", ".html");
223            }
224    
225            public String getJavaFileName(String fileName) {
226                    String classSuffix = getClassSuffix(fileName);
227    
228                    return getJavaFileName(fileName, classSuffix);
229            }
230    
231            public String getJavaFileName(String fileName, String classSuffix) {
232                    return
233                            getPackagePath(fileName) + "/" +
234                                    getSimpleClassName(fileName, classSuffix) + ".java";
235            }
236    
237            public int getLocatorCount(Element rootElement) {
238                    String xml = rootElement.asXML();
239    
240                    for (int i = 1;; i++) {
241                            if (xml.contains("${locator" + i + "}")) {
242                                    continue;
243                            }
244    
245                            if (i > 1) {
246                                    i--;
247                            }
248    
249                            return i;
250                    }
251            }
252    
253            public String getName(String fileName) {
254                    int x = fileName.lastIndexOf(StringPool.SLASH);
255                    int y = fileName.lastIndexOf(CharPool.PERIOD);
256    
257                    return fileName.substring(x + 1, y);
258            }
259    
260            public String getNormalizedContent(String fileName) throws Exception {
261                    String content = readFile(fileName);
262    
263                    if (fileName.endsWith(".path")) {
264                            int x = content.indexOf("<tbody>");
265                            int y = content.indexOf("</tbody>");
266    
267                            if ((x == -1) || (y == -1)) {
268                                    throwValidationException(1002, fileName, "tbody");
269                            }
270    
271                            String pathTbody = content.substring(x, y + 8);
272    
273                            Map<String, Object> context = new HashMap<>();
274    
275                            context.put("pathName", getName(fileName));
276                            context.put("pathTbody", pathTbody);
277    
278                            String newContent = processTemplate("path_xml.ftl", context);
279    
280                            if (!content.equals(newContent)) {
281                                    content = newContent;
282    
283                                    writeFile(getBaseDirName(), fileName, newContent, false);
284                            }
285                    }
286    
287                    StringBundler sb = new StringBundler();
288    
289                    int lineNumber = 1;
290    
291                    UnsyncBufferedReader unsyncBufferedReader = new UnsyncBufferedReader(
292                            new UnsyncStringReader(content));
293    
294                    String line = null;
295    
296                    while ((line = unsyncBufferedReader.readLine()) != null) {
297                            Matcher matcher = _tagPattern.matcher(line);
298    
299                            if (matcher.find()) {
300                                    for (String reservedTag : _reservedTags) {
301                                            if (line.contains("<" + reservedTag)) {
302                                                    line = StringUtil.replace(
303                                                            line, matcher.group(),
304                                                            matcher.group() + " line-number=\"" + lineNumber +
305                                                                    "\"");
306    
307                                                    break;
308                                            }
309                                    }
310                            }
311    
312                            sb.append(line);
313    
314                            lineNumber++;
315                    }
316    
317                    content = sb.toString();
318    
319                    if (content != null) {
320                            content = content.trim();
321                            content = StringUtil.replace(content, "\n", "");
322                            content = StringUtil.replace(content, "\r\n", "");
323                            content = StringUtil.replace(content, "\t", " ");
324                            content = content.replaceAll(" +", " ");
325                    }
326    
327                    return content;
328            }
329    
330            public String getObjectName(String name) {
331                    return StringUtil.upperCaseFirstLetter(name);
332            }
333    
334            public String getPackageName(String fileName) {
335                    String packagePath = getPackagePath(fileName);
336    
337                    return StringUtil.replace(
338                            packagePath, StringPool.SLASH, StringPool.PERIOD);
339            }
340    
341            public String getPackagePath(String fileName) {
342                    int x = fileName.lastIndexOf(StringPool.SLASH);
343    
344                    return fileName.substring(0, x);
345            }
346    
347            public String getReturnType(String name) {
348                    if (name.startsWith("Is")) {
349                            return "boolean";
350                    }
351    
352                    return "void";
353            }
354    
355            public Element getRootElement(String fileName) throws Exception {
356                    String content = getNormalizedContent(fileName);
357    
358                    try {
359                            Document document = UnsecureSAXReaderUtil.read(content, true);
360    
361                            Element rootElement = document.getRootElement();
362    
363                            validate(fileName, rootElement);
364    
365                            return rootElement;
366                    }
367                    catch (DocumentException de) {
368                            throwValidationException(1007, fileName, de);
369                    }
370    
371                    return null;
372            }
373    
374            public String getSimpleClassName(String fileName) {
375                    String classSuffix = getClassSuffix(fileName);
376    
377                    return getSimpleClassName(fileName, classSuffix);
378            }
379    
380            public String getSimpleClassName(String fileName, String classSuffix) {
381                    return getName(fileName) + classSuffix;
382            }
383    
384            public String getVariableName(String name) {
385                    return TextFormatter.format(name, TextFormatter.I);
386            }
387    
388            public String normalizeFileName(String fileName) {
389                    return StringUtil.replace(
390                            fileName, StringPool.BACK_SLASH, StringPool.SLASH);
391            }
392    
393            public String readFile(String fileName) throws Exception {
394                    return FileUtil.read(getBaseDirName() + "/" + fileName);
395            }
396    
397            public void writeFile(String fileName, String content, boolean format)
398                    throws Exception {
399    
400                    writeFile(getBaseDirName() + "-generated", fileName, content, format);
401            }
402    
403            public void writeFile(
404                            String baseDirName, String fileName, String content, boolean format)
405                    throws Exception {
406    
407                    File file = new File(baseDirName + "/" + fileName);
408    
409                    if (format) {
410                            ToolsUtil.writeFile(file, content, null);
411                    }
412                    else {
413                            System.out.println("Writing " + file);
414    
415                            FileUtil.write(file, content);
416                    }
417            }
418    
419            protected String getProperty(String propertyName) {
420                    String propertyValue = _properties.getProperty(propertyName);
421    
422                    if (Validator.isNotNull(propertyValue)) {
423                            Matcher matcher = _varElementPattern.matcher(propertyValue);
424    
425                            while (matcher.find()) {
426                                    propertyValue = propertyValue.replace(
427                                            matcher.group(), getProperty(matcher.group(1)));
428                            }
429                    }
430    
431                    return propertyValue;
432            }
433    
434            protected String processTemplate(String name, Map<String, Object> context)
435                    throws Exception {
436    
437                    return StringUtil.strip(
438                            FreeMarkerUtil.process(_TPL_ROOT + name, context), '\r');
439            }
440    
441            protected void throwValidationException(int errorCode, String fileName) {
442                    throwValidationException(
443                            errorCode, fileName, null, null, null, null, null);
444            }
445    
446            protected void throwValidationException(
447                    int errorCode, String fileName, Element element) {
448    
449                    throwValidationException(
450                            errorCode, fileName, element, null, null, null, null);
451            }
452    
453            protected void throwValidationException(
454                    int errorCode, String fileName, Element element, String string1) {
455    
456                    throwValidationException(
457                            errorCode, fileName, element, null, string1, null, null);
458            }
459    
460            protected void throwValidationException(
461                    int errorCode, String fileName, Element element, String string1,
462                    String string2) {
463    
464                    throwValidationException(
465                            errorCode, fileName, element, null, string1, string2, null);
466            }
467    
468            protected void throwValidationException(
469                    int errorCode, String fileName, Element element, String[] array) {
470    
471                    throwValidationException(
472                            errorCode, fileName, element, array, null, null, null);
473            }
474    
475            protected void throwValidationException(
476                    int errorCode, String fileName, Element element, String[] array,
477                    String string1) {
478    
479                    throwValidationException(
480                            errorCode, fileName, element, array, string1, null, null);
481            }
482    
483            protected void throwValidationException(
484                    int errorCode, String fileName, Element element, String[] array,
485                    String string1, String string2, Exception e) {
486    
487                    String prefix = "Error " + errorCode + ": ";
488                    String suffix = fileName;
489    
490                    if (element != null) {
491                            suffix += ":" + element.attributeValue("line-number");
492                    }
493    
494                    if (errorCode == 1000) {
495                            throw new IllegalArgumentException(
496                                    prefix + "Invalid root element in " + suffix);
497                    }
498                    else if (errorCode == 1001) {
499                            throw new IllegalArgumentException(
500                                    prefix + "Missing (" + StringUtil.merge(array, "|") +
501                                            ") child element in " + suffix);
502                    }
503                    else if (errorCode == 1002) {
504                            throw new IllegalArgumentException(
505                                    prefix + "Invalid " + string1 + " element in " + suffix);
506                    }
507                    else if (errorCode == 1003) {
508                            throw new IllegalArgumentException(
509                                    prefix + "Missing " + string1 + " attribute in " + suffix);
510                    }
511                    else if (errorCode == 1004) {
512                            throw new IllegalArgumentException(
513                                    prefix + "Missing (" + StringUtil.merge(array, "|") +
514                                            ") attribute in " + suffix);
515                    }
516                    else if (errorCode == 1005) {
517                            throw new IllegalArgumentException(
518                                    prefix + "Invalid " + string1 + " attribute in " + suffix);
519                    }
520                    else if (errorCode == 1006) {
521                            throw new IllegalArgumentException(
522                                    prefix + "Invalid " + string1 + " attribute value in " +
523                                            suffix);
524                    }
525                    else if (errorCode == 1007) {
526                            throw new IllegalArgumentException(
527                                    prefix + "Poorly formed XML in " + suffix, e);
528                    }
529                    else if (errorCode == 1008) {
530                            throw new IllegalArgumentException(
531                                    prefix + "Duplicate file name " + string1 + " at " + suffix);
532                    }
533                    else if (errorCode == 1009) {
534                            throw new IllegalArgumentException(
535                                    prefix + "Duplicate command name " + string1 + " at " + suffix);
536                    }
537                    else if (errorCode == 1010) {
538                            throw new IllegalArgumentException(
539                                    prefix + "Invalid locator-key " + string1 + " at " + suffix);
540                    }
541                    else if (errorCode == 1011) {
542                            throw new IllegalArgumentException(
543                                    prefix + "Invalid " + string1 + " name " + string2 + " at " +
544                                            suffix);
545                    }
546                    else if (errorCode == 1012) {
547                            throw new IllegalArgumentException(
548                                    prefix + "Invalid " + string1 + " command " + string2 + " at " +
549                                            suffix);
550                    }
551                    else if (errorCode == 1013) {
552                            throw new IllegalArgumentException(
553                                    prefix + "Invalid method " + string1 + " at " + suffix);
554                    }
555                    else if (errorCode == 1014) {
556                            throw new IllegalArgumentException(
557                                    prefix + "Invalid path " + string1 + " at " + suffix);
558                    }
559                    else if (errorCode == 1015) {
560                            throw new IllegalArgumentException(
561                                    prefix + "Poorly formed test case command " + string1 + " at " +
562                                            suffix);
563                    }
564                    else if (errorCode == 1016) {
565                            throw new IllegalArgumentException(
566                                    prefix + "Invalid " + string1 + " attribute value " + string2 +
567                                            " in " + suffix);
568                    }
569                    else if (errorCode == 1017) {
570                            throw new IllegalArgumentException(
571                                    prefix + "Description '" + string1 +
572                                            "' must end with a '.' in " + suffix);
573                    }
574                    else if (errorCode == 1018) {
575                            throw new IllegalArgumentException(
576                                    prefix + "Missing (" + StringUtil.merge(array, "|") +
577                                            ") in attribute " + string1 + " at " + suffix);
578                    }
579                    else if (errorCode == 2000) {
580                            throw new IllegalArgumentException(
581                                    prefix + "Too many child elements in the " + string1 +
582                                            " element in " + suffix);
583                    }
584                    else if (errorCode == 2001) {
585                            throw new IllegalArgumentException(
586                                    prefix + "Action command " + string1 +
587                                            " does not match a function name at " + suffix);
588                    }
589                    else if (errorCode == 2002) {
590                            throw new IllegalArgumentException(
591                                    prefix + "Missing matching " + string1 + ".path for " + suffix);
592                    }
593                    else if (errorCode == 2003) {
594                            throw new IllegalArgumentException(
595                                    prefix + "Illegal XPath " + string1 + " in " + suffix);
596                    }
597                    else if (errorCode == 2004) {
598                            throw new IllegalArgumentException(
599                                    prefix + "Description '" + string1 +
600                                            "' must title convention in " + suffix);
601                    }
602                    else if (errorCode == 3001) {
603                            throw new IllegalArgumentException(
604                                    prefix + "The property '" + string1 +
605                                            "' has an invalid component name '" + string2 + "' in " +
606                                                    suffix);
607                    }
608                    else if (errorCode == 3002) {
609                            throw new IllegalArgumentException(
610                                    prefix + "Missing property '" + string1 + "' for " + suffix);
611                    }
612                    else if (errorCode == 3003) {
613                            throw new IllegalArgumentException(
614                                    prefix + "Invalid property " + string1 + " at " + suffix);
615                    }
616                    else {
617                            throw new IllegalArgumentException(prefix + suffix);
618                    }
619            }
620    
621            protected void throwValidationException(
622                    int errorCode, String fileName, Exception e) {
623    
624                    throwValidationException(
625                            errorCode, fileName, null, null, null, null, e);
626            }
627    
628            protected void throwValidationException(
629                    int errorCode, String fileName, String string1) {
630    
631                    throwValidationException(
632                            errorCode, fileName, null, null, string1, null, null);
633            }
634    
635            protected void validate(String fileName, Element rootElement)
636                    throws Exception {
637    
638                    if (fileName.endsWith(".action")) {
639                            validateActionDocument(fileName, rootElement);
640                    }
641                    else if (fileName.endsWith(".function")) {
642                            validateFunctionDocument(fileName, rootElement);
643                    }
644                    else if (fileName.endsWith(".macro")) {
645                            validateMacroDocument(fileName, rootElement);
646                    }
647                    else if (fileName.endsWith(".path")) {
648                            validatePathDocument(fileName, rootElement);
649                    }
650                    else if (fileName.endsWith(".testcase")) {
651                            validateTestCaseDocument(fileName, rootElement);
652                    }
653            }
654    
655            protected void validateActionCommandElement(
656                    String fileName, Element commandElement,
657                    String[] allowedBlockChildElementNames,
658                    String[] allowedExecuteAttributeNames,
659                    String[] allowedExecuteChildElementNames) {
660    
661                    List<Element> elements = commandElement.elements();
662    
663                    if (elements.isEmpty()) {
664                            throwValidationException(
665                                    1001, fileName, commandElement,
666                                    new String[] {"case", "default"});
667                    }
668    
669                    for (Element element : elements) {
670                            List<Element> descriptionElements = element.elements("description");
671    
672                            String elementName = element.getName();
673    
674                            if (descriptionElements.size() > 1) {
675                                    throwValidationException(
676                                            2000, fileName, descriptionElements.get(1), elementName);
677                            }
678    
679                            List<Element> executeChildElements = element.elements("execute");
680    
681                            if (executeChildElements.size() > 1) {
682                                    throwValidationException(
683                                            2000, fileName, executeChildElements.get(1), elementName);
684                            }
685    
686                            if (elementName.equals("case")) {
687                                    List<Attribute> attributes = element.attributes();
688    
689                                    boolean hasNeededAttributeName = false;
690    
691                                    for (Attribute attribute : attributes) {
692                                            String attributeName = attribute.getName();
693    
694                                            if (attributeName.equals("comparator")) {
695                                                    String attributeValue = attribute.getValue();
696    
697                                                    if (!attributeValue.equals("contains") &&
698                                                            !attributeValue.equals("endsWith") &&
699                                                            !attributeValue.equals("equals") &&
700                                                            !attributeValue.equals("startsWith")) {
701    
702                                                            throwValidationException(
703                                                                    1006, fileName, element, attributeName);
704                                                    }
705                                            }
706                                            else if (attributeName.equals("locator1") ||
707                                                             attributeName.equals("locator2") ||
708                                                             attributeName.equals("locator-key1") ||
709                                                             attributeName.equals("locator-key2") ||
710                                                             attributeName.equals("value1") ||
711                                                             attributeName.equals("value2")) {
712    
713                                                    String attributeValue = attribute.getValue();
714    
715                                                    if (Validator.isNull(attributeValue)) {
716                                                            throwValidationException(
717                                                                    1006, fileName, element, attributeName);
718                                                    }
719    
720                                                    hasNeededAttributeName = true;
721                                            }
722    
723                                            if (!attributeName.equals("comparator") &&
724                                                    !attributeName.equals("line-number") &&
725                                                    !attributeName.equals("locator1") &&
726                                                    !attributeName.equals("locator2") &&
727                                                    !attributeName.equals("locator-key1") &&
728                                                    !attributeName.equals("locator-key2") &&
729                                                    !attributeName.equals("value1") &&
730                                                    !attributeName.equals("value2")) {
731    
732                                                    throwValidationException(
733                                                            1005, fileName, element, attributeName);
734                                            }
735    
736                                            if (attributeName.equals("locator") ||
737                                                    attributeName.equals("locator-key") ||
738                                                    attributeName.equals("value")) {
739    
740                                                    throwValidationException(
741                                                            1005, fileName, element, attributeName);
742                                            }
743                                    }
744    
745                                    if (!hasNeededAttributeName) {
746                                            throwValidationException(
747                                                    1004, fileName, element,
748                                                    new String[] {"locator1", "locator-key1", "value1"});
749                                    }
750    
751                                    validateBlockElement(
752                                            fileName, element, new String[] {"execute"},
753                                            new String[] {"function"}, new String[0], new String[0]);
754                            }
755                            else if (elementName.equals("default")) {
756                                    List<Attribute> attributes = element.attributes();
757    
758                                    if (attributes.size() != 1) {
759                                            Attribute attribute = attributes.get(1);
760    
761                                            String attributeName = attribute.getName();
762    
763                                            throwValidationException(
764                                                    1005, fileName, element, attributeName);
765                                    }
766    
767                                    validateBlockElement(
768                                            fileName, element, new String[] {"description", "execute"},
769                                            new String[] {"function"}, new String[0], new String[0]);
770                            }
771                            else {
772                                    throwValidationException(1002, fileName, element, elementName);
773                            }
774                    }
775            }
776    
777            protected void validateActionDocument(
778                    String fileName, Element rootElement) {
779    
780                    if (!Validator.equals(rootElement.getName(), "definition")) {
781                            throwValidationException(1000, fileName, rootElement);
782                    }
783    
784                    List<Element> elements = rootElement.elements();
785    
786                    if (elements.isEmpty()) {
787                            throwValidationException(
788                                    1001, fileName, rootElement, new String[] {"command"});
789                    }
790    
791                    for (Element element : elements) {
792                            String elementName = element.getName();
793    
794                            if (elementName.equals("command")) {
795                                    String attributeValue = element.attributeValue("name");
796    
797                                    if (attributeValue == null) {
798                                            throwValidationException(1003, fileName, element, "name");
799                                    }
800                                    else if (Validator.isNull(attributeValue)) {
801                                            throwValidationException(1006, fileName, element, "name");
802                                    }
803    
804                                    validateActionCommandElement(
805                                            fileName, element, new String[] {"execute"},
806                                            new String[] {"function"}, new String[0]);
807                            }
808                            else {
809                                    throwValidationException(1002, fileName, element, elementName);
810                            }
811                    }
812            }
813    
814            protected void validateBlockElement(
815                    String fileName, Element commandElement,
816                    String[] allowedBlockChildElementNames,
817                    String[] allowedExecuteAttributeNames,
818                    String[] allowedExecuteChildElementNames,
819                    String[] allowedIfConditionElementNames) {
820    
821                    List<Element> elements = commandElement.elements();
822    
823                    if (elements.isEmpty()) {
824                            throwValidationException(
825                                    1001, fileName, commandElement, allowedBlockChildElementNames);
826                    }
827    
828                    for (Element element : elements) {
829                            String elementName = element.getName();
830    
831                            if (!ArrayUtil.contains(
832                                            allowedBlockChildElementNames, elementName)) {
833    
834                                    throwValidationException(1002, fileName, element, elementName);
835                            }
836    
837                            if (elementName.equals("description")) {
838                                    validateSimpleElement(
839                                            fileName, element, new String[] {"message"});
840    
841                                    String message = element.attributeValue("message");
842    
843                                    if (!message.endsWith(".")) {
844                                            throwValidationException(
845                                                    1017, fileName, commandElement, message);
846                                    }
847                            }
848                            else if (elementName.equals("echo") || elementName.equals("fail")) {
849                                    validateSimpleElement(
850                                            fileName, element, new String[] {"message"});
851                            }
852                            else if (elementName.equals("execute")) {
853                                    validateExecuteElement(
854                                            fileName, element, allowedExecuteAttributeNames, ".+",
855                                            allowedExecuteChildElementNames);
856                            }
857                            else if (elementName.equals("for")) {
858                                    validateForElement(
859                                            fileName, element, new String[] {"list", "param"},
860                                            allowedBlockChildElementNames, allowedExecuteAttributeNames,
861                                            allowedExecuteChildElementNames,
862                                            allowedIfConditionElementNames);
863                            }
864                            else if (elementName.equals("if") || elementName.equals("while")) {
865                                    validateIfElement(
866                                            fileName, element, allowedBlockChildElementNames,
867                                            allowedExecuteAttributeNames,
868                                            allowedExecuteChildElementNames,
869                                            allowedIfConditionElementNames);
870                            }
871                            else if (elementName.equals("property")) {
872                                    validatePropertyElement(fileName, element);
873                            }
874                            else if (elementName.equals("take-screenshot")) {
875                                    validateSimpleElement(fileName, element, new String[0]);
876                            }
877                            else if (elementName.equals("task")) {
878                                    validateBlockElement(
879                                            fileName, element, allowedBlockChildElementNames,
880                                            allowedExecuteAttributeNames,
881                                            allowedExecuteChildElementNames,
882                                            allowedIfConditionElementNames);
883                            }
884                            else if (elementName.equals("var")) {
885                                    validateVarElement(fileName, element);
886                            }
887                            else {
888                                    throwValidationException(1002, fileName, element, elementName);
889                            }
890                    }
891            }
892    
893            protected void validateExecuteElement(
894                    String fileName, Element executeElement,
895                    String[] allowedExecuteAttributeNames,
896                    String allowedExecuteAttributeValuesRegex,
897                    String[] allowedExecuteChildElementNames) {
898    
899                    boolean hasAllowedAttributeName = false;
900    
901                    List<Attribute> attributes = executeElement.attributes();
902    
903                    for (Attribute attribute : attributes) {
904                            String attributeName = attribute.getName();
905    
906                            if (ArrayUtil.contains(
907                                            allowedExecuteAttributeNames, attributeName)) {
908    
909                                    hasAllowedAttributeName = true;
910    
911                                    break;
912                            }
913                    }
914    
915                    if (!hasAllowedAttributeName) {
916                            throwValidationException(
917                                    1004, fileName, executeElement, allowedExecuteAttributeNames);
918                    }
919    
920                    String action = executeElement.attributeValue("action");
921                    String function = executeElement.attributeValue("function");
922                    String macro = executeElement.attributeValue("macro");
923                    String macroDesktop = executeElement.attributeValue("macro-desktop");
924                    String macroMobile = executeElement.attributeValue("macro-mobile");
925                    String selenium = executeElement.attributeValue("selenium");
926                    String testCase = executeElement.attributeValue("test-case");
927                    String testCaseCommand = executeElement.attributeValue(
928                            "test-case-command");
929                    String testClass = executeElement.attributeValue("test-class");
930    
931                    if (action != null) {
932                            if (Validator.isNull(action) ||
933                                    !action.matches(allowedExecuteAttributeValuesRegex)) {
934    
935                                    throwValidationException(
936                                            1006, fileName, executeElement, "action");
937                            }
938    
939                            for (Attribute attribute : attributes) {
940                                    String attributeName = attribute.getName();
941    
942                                    if (!attributeName.equals("action") &&
943                                            !attributeName.equals("line-number") &&
944                                            !attributeName.equals("locator1") &&
945                                            !attributeName.equals("locator2") &&
946                                            !attributeName.equals("locator-key1") &&
947                                            !attributeName.equals("locator-key2") &&
948                                            !attributeName.equals("value1") &&
949                                            !attributeName.equals("value2")) {
950    
951                                            throwValidationException(
952                                                    1005, fileName, executeElement, attributeName);
953                                    }
954    
955                                    if (attributeName.equals("locator") ||
956                                            attributeName.equals("locator-key") ||
957                                            attributeName.equals("value")) {
958    
959                                            throwValidationException(
960                                                    1005, fileName, executeElement, attributeName);
961                                    }
962    
963                                    String attributeValue = attribute.getValue();
964    
965                                    if (attributeName.equals("value1") &&
966                                            attributeValue.contains("move-file")) {
967    
968                                            if (!attributeValue.contains("-Dfile") ||
969                                                    !attributeValue.contains("-Dtofile")) {
970    
971                                                    throwValidationException(
972                                                            1018, fileName, executeElement,
973                                                            new String[] {"-Dfile", "-Dtofile"}, "value1");
974                                            }
975                                    }
976    
977                                    if (attributeName.equals("value1") &&
978                                            attributeValue.contains("replace-file")) {
979    
980                                            if (!attributeValue.contains("-Dfile") ||
981                                                    !attributeValue.contains("-Dtoken") ||
982                                                    !attributeValue.contains("-Dvalue")) {
983    
984                                                    throwValidationException(
985                                                            1018, fileName, executeElement,
986                                                            new String[] {"-Dfile", "-Dtoken", "-Dvalue"},
987                                                            "value1");
988                                            }
989                                    }
990                            }
991                    }
992                    else if (function != null) {
993                            if (Validator.isNull(function) ||
994                                    !function.matches(allowedExecuteAttributeValuesRegex)) {
995    
996                                    throwValidationException(
997                                            1006, fileName, executeElement, "function");
998                            }
999    
1000                            for (Attribute attribute : attributes) {
1001                                    String attributeName = attribute.getName();
1002    
1003                                    if (!attributeName.equals("function") &&
1004                                            !attributeName.equals("ignore-javascript-error") &&
1005                                            !attributeName.equals("line-number") &&
1006                                            !attributeName.startsWith("locator") &&
1007                                            !attributeName.startsWith("value")) {
1008    
1009                                            throwValidationException(
1010                                                    1005, fileName, executeElement, attributeName);
1011                                    }
1012    
1013                                    if (attributeName.equals("locator") ||
1014                                            attributeName.equals("value") ||
1015                                            attributeName.startsWith("locator-key")) {
1016    
1017                                            throwValidationException(
1018                                                    1005, fileName, executeElement, attributeName);
1019                                    }
1020                            }
1021                    }
1022                    else if (macro != null) {
1023                            if (Validator.isNull(macro) ||
1024                                    !macro.matches(allowedExecuteAttributeValuesRegex)) {
1025    
1026                                    throwValidationException(
1027                                            1006, fileName, executeElement, "macro");
1028                            }
1029    
1030                            for (Attribute attribute : attributes) {
1031                                    String attributeName = attribute.getName();
1032    
1033                                    if (!attributeName.equals("line-number") &&
1034                                            !attributeName.equals("macro")) {
1035    
1036                                            throwValidationException(
1037                                                    1005, fileName, executeElement, attributeName);
1038                                    }
1039                            }
1040                    }
1041                    else if (macroDesktop != null) {
1042                            if (Validator.isNull(macroDesktop) ||
1043                                    !macroDesktop.matches(allowedExecuteAttributeValuesRegex)) {
1044    
1045                                    throwValidationException(
1046                                            1006, fileName, executeElement, "macro-desktop");
1047                            }
1048    
1049                            for (Attribute attribute : attributes) {
1050                                    String attributeName = attribute.getName();
1051    
1052                                    if (!attributeName.equals("line-number") &&
1053                                            !attributeName.equals("macro-desktop") &&
1054                                            !attributeName.equals("macro-mobile")) {
1055    
1056                                            throwValidationException(
1057                                                    1005, fileName, executeElement, attributeName);
1058                                    }
1059                            }
1060                    }
1061                    else if (macroMobile != null) {
1062                            if (Validator.isNull(macroMobile) ||
1063                                    !macroMobile.matches(allowedExecuteAttributeValuesRegex)) {
1064    
1065                                    throwValidationException(
1066                                            1006, fileName, executeElement, "macro-mobile");
1067                            }
1068    
1069                            for (Attribute attribute : attributes) {
1070                                    String attributeName = attribute.getName();
1071    
1072                                    if (!attributeName.equals("line-number") &&
1073                                            !attributeName.equals("macro")) {
1074    
1075                                            throwValidationException(
1076                                                    1005, fileName, executeElement, attributeName);
1077                                    }
1078                            }
1079                    }
1080                    else if (selenium != null) {
1081                            if (Validator.isNull(selenium) ||
1082                                    !selenium.matches(allowedExecuteAttributeValuesRegex)) {
1083    
1084                                    throwValidationException(
1085                                            1006, fileName, executeElement, "selenium");
1086                            }
1087    
1088                            for (Attribute attribute : attributes) {
1089                                    String attributeName = attribute.getName();
1090    
1091                                    if (!attributeName.equals("argument1") &&
1092                                            !attributeName.equals("argument2") &&
1093                                            !attributeName.equals("line-number") &&
1094                                            !attributeName.equals("selenium")) {
1095    
1096                                            throwValidationException(
1097                                                    1005, fileName, executeElement, attributeName);
1098                                    }
1099                            }
1100                    }
1101                    else if (testCase != null) {
1102                            if (Validator.isNull(testCase) ||
1103                                    !testCase.matches(allowedExecuteAttributeValuesRegex)) {
1104    
1105                                    throwValidationException(
1106                                            1006, fileName, executeElement, "test-case");
1107                            }
1108    
1109                            if (testCase.contains("#")) {
1110                                    int x = testCase.lastIndexOf("#");
1111    
1112                                    if (x == -1) {
1113                                            throwValidationException(
1114                                                    1015, fileName, executeElement, testCaseCommand);
1115                                    }
1116    
1117                                    String testCaseName = testCase.substring(0, x);
1118    
1119                                    String testCaseCommandName = testCase.substring(x + 1);
1120    
1121                                    if (Validator.isNull(testCaseCommandName) ||
1122                                            Validator.isNull(testCaseName) ||
1123                                            !testCaseName.equals("super")) {
1124    
1125                                            throwValidationException(
1126                                                    1015, fileName, executeElement, testCase);
1127                                    }
1128                            }
1129                            else {
1130                                    throwValidationException(
1131                                            1015, fileName, executeElement, testCase);
1132                            }
1133    
1134                            for (Attribute attribute : attributes) {
1135                                    String attributeName = attribute.getName();
1136    
1137                                    if (!attributeName.equals("line-number") &&
1138                                            !attributeName.equals("test-case")) {
1139    
1140                                            throwValidationException(
1141                                                    1005, fileName, executeElement, attributeName);
1142                                    }
1143                            }
1144                    }
1145                    else if (testCaseCommand != null) {
1146                            if (Validator.isNull(testCaseCommand) ||
1147                                    !testCaseCommand.matches(allowedExecuteAttributeValuesRegex)) {
1148    
1149                                    throwValidationException(
1150                                            1006, fileName, executeElement, "test-case-command");
1151                            }
1152    
1153                            if (testCaseCommand.contains("#")) {
1154                                    int x = testCaseCommand.lastIndexOf("#");
1155    
1156                                    String testCaseName = testCaseCommand.substring(0, x);
1157    
1158                                    String testCaseCommandName = testCaseCommand.substring(x + 1);
1159    
1160                                    if (Validator.isNull(testCaseCommandName) ||
1161                                            Validator.isNull(testCaseName)) {
1162    
1163                                            throwValidationException(
1164                                                    1015, fileName, executeElement, testCaseCommand);
1165                                    }
1166                            }
1167                            else {
1168                                    throwValidationException(
1169                                            1015, fileName, executeElement, testCaseCommand);
1170                            }
1171    
1172                            for (Attribute attribute : attributes) {
1173                                    String attributeName = attribute.getName();
1174    
1175                                    if (!attributeName.equals("line-number") &&
1176                                            !attributeName.equals("test-case-command")) {
1177    
1178                                            throwValidationException(
1179                                                    1005, fileName, executeElement, attributeName);
1180                                    }
1181                            }
1182                    }
1183                    else if (testClass != null) {
1184                            if (Validator.isNull(testClass) ||
1185                                    !testClass.matches(allowedExecuteAttributeValuesRegex)) {
1186    
1187                                    throwValidationException(
1188                                            1006, fileName, executeElement, "test-class");
1189                            }
1190    
1191                            for (Attribute attribute : attributes) {
1192                                    String attributeName = attribute.getName();
1193    
1194                                    if (!attributeName.equals("line-number") &&
1195                                            !attributeName.equals("test-class")) {
1196    
1197                                            throwValidationException(
1198                                                    1005, fileName, executeElement, attributeName);
1199                                    }
1200                            }
1201                    }
1202                    else {
1203                            throwValidationException(0, fileName);
1204                    }
1205    
1206                    List<Element> elements = executeElement.elements();
1207    
1208                    if (allowedExecuteChildElementNames.length == 0) {
1209                            if (!elements.isEmpty()) {
1210                                    Element element = elements.get(0);
1211    
1212                                    String elementName = element.getName();
1213    
1214                                    throwValidationException(1002, fileName, element, elementName);
1215                            }
1216                    }
1217                    else {
1218                            String executeElementName = executeElement.getName();
1219    
1220                            for (Element element : elements) {
1221                                    String elementName = element.getName();
1222    
1223                                    if (executeElementName.equals("condition")) {
1224                                            throwValidationException(
1225                                                    1002, fileName, element, elementName);
1226                                    }
1227    
1228                                    if (elementName.equals("var")) {
1229                                            validateVarElement(fileName, element);
1230                                    }
1231                                    else {
1232                                            throwValidationException(
1233                                                    1002, fileName, element, elementName);
1234                                    }
1235                            }
1236                    }
1237            }
1238    
1239            protected void validateForElement(
1240                    String fileName, Element forElement, String[] neededAttributes,
1241                    String[] allowedBlockChildElementNames,
1242                    String[] allowedExecuteAttributeNames,
1243                    String[] allowedExecuteChildElementNames,
1244                    String[] allowedIfConditionElementNames) {
1245    
1246                    Map<String, Boolean> hasNeededAttributes = new HashMap<>();
1247    
1248                    for (String neededAttribute : neededAttributes) {
1249                            hasNeededAttributes.put(neededAttribute, false);
1250                    }
1251    
1252                    List<Attribute> attributes = forElement.attributes();
1253    
1254                    for (Attribute attribute : attributes) {
1255                            String attributeName = attribute.getName();
1256                            String attributeValue = attribute.getValue();
1257    
1258                            if (!_allowedNullAttributes.contains(attributeName) &&
1259                                    Validator.isNull(attributeValue)) {
1260    
1261                                    throwValidationException(
1262                                            1006, fileName, forElement, attributeName);
1263                            }
1264    
1265                            if (hasNeededAttributes.containsKey(attributeName)) {
1266                                    hasNeededAttributes.put(attributeName, true);
1267                            }
1268    
1269                            if (!attributeName.equals("line-number") &&
1270                                    !hasNeededAttributes.containsKey(attributeName)) {
1271    
1272                                    throwValidationException(
1273                                            1005, fileName, forElement, attributeName);
1274                            }
1275                    }
1276    
1277                    for (String neededAttribute : neededAttributes) {
1278                            if (!hasNeededAttributes.get(neededAttribute)) {
1279                                    throwValidationException(
1280                                            1004, fileName, forElement, neededAttributes);
1281                            }
1282                    }
1283    
1284                    validateBlockElement(
1285                            fileName, forElement, allowedBlockChildElementNames,
1286                            allowedExecuteAttributeNames, allowedExecuteChildElementNames,
1287                            allowedIfConditionElementNames);
1288            }
1289    
1290            protected void validateFunctionDocument(
1291                    String fileName, Element rootElement) {
1292    
1293                    if (!Validator.equals(rootElement.getName(), "definition")) {
1294                            throwValidationException(1000, fileName, rootElement);
1295                    }
1296    
1297                    String defaultCommandName = getDefaultCommandName(rootElement);
1298    
1299                    if (defaultCommandName == null) {
1300                            throwValidationException(1003, fileName, rootElement, "default");
1301                    }
1302    
1303                    List<Element> elements = rootElement.elements();
1304    
1305                    if (elements.isEmpty()) {
1306                            throwValidationException(
1307                                    1001, fileName, rootElement, new String[] {"command"});
1308                    }
1309    
1310                    for (Element element : elements) {
1311                            String elementName = element.getName();
1312    
1313                            if (elementName.equals("command")) {
1314                                    String attributeValue = element.attributeValue("name");
1315    
1316                                    if (attributeValue == null) {
1317                                            throwValidationException(1003, fileName, element, "name");
1318                                    }
1319                                    else if (Validator.isNull(attributeValue)) {
1320                                            throwValidationException(1006, fileName, element, "name");
1321                                    }
1322    
1323                                    validateBlockElement(
1324                                            fileName, element, new String[] {"execute", "if"},
1325                                            new String[] {"function", "selenium"}, new String[0],
1326                                            new String[] {"condition", "contains"});
1327                            }
1328                            else {
1329                                    throwValidationException(1002, fileName, element, elementName);
1330                            }
1331                    }
1332            }
1333    
1334            protected void validateIfElement(
1335                    String fileName, Element ifElement,
1336                    String[] allowedBlockChildElementNames,
1337                    String[] allowedExecuteAttributeNames,
1338                    String[] allowedExecuteChildElementNames,
1339                    String[] allowedIfConditionElementNames) {
1340    
1341                    List<Element> elements = ifElement.elements();
1342    
1343                    Set<String> elementNames = new HashSet<>();
1344    
1345                    boolean hasAllowedIfConditionElementNames = false;
1346    
1347                    for (Element element : elements) {
1348                            String elementName = element.getName();
1349    
1350                            elementNames.add(elementName);
1351    
1352                            if (ArrayUtil.contains(
1353                                            allowedIfConditionElementNames, elementName)) {
1354    
1355                                    hasAllowedIfConditionElementNames = true;
1356                            }
1357    
1358                            String ifElementName = ifElement.getName();
1359    
1360                            if (elementName.equals("and") || elementName.equals("not") ||
1361                                    elementName.equals("or")) {
1362    
1363                                    validateIfElement(
1364                                            fileName, element, allowedBlockChildElementNames,
1365                                            allowedExecuteAttributeNames,
1366                                            allowedExecuteChildElementNames,
1367                                            allowedIfConditionElementNames);
1368                            }
1369                            else if (elementName.equals("condition")) {
1370                                    validateExecuteElement(
1371                                            fileName, element, allowedExecuteAttributeNames,
1372                                            ".*(is|Is).+", allowedExecuteChildElementNames);
1373                            }
1374                            else if (elementName.equals("contains")) {
1375                                    validateSimpleElement(
1376                                            fileName, element, new String[] {"string", "substring"});
1377    
1378                                    if (fileName.endsWith(".function")) {
1379                                            List<Attribute> attributes = element.attributes();
1380    
1381                                            for (Attribute attribute : attributes) {
1382                                                    String attributeValue = attribute.getValue();
1383    
1384                                                    Matcher varElementMatcher = _varElementPattern.matcher(
1385                                                            attributeValue);
1386    
1387                                                    Matcher varElementFunctionMatcher =
1388                                                            _varElementFunctionPattern.matcher(attributeValue);
1389    
1390                                                    if (varElementMatcher.find() &&
1391                                                            !varElementFunctionMatcher.find()) {
1392    
1393                                                            throwValidationException(
1394                                                                    1006, fileName, element, attribute.getName());
1395                                                    }
1396                                            }
1397                                    }
1398                            }
1399                            else if (elementName.equals("else")) {
1400                                    if (ifElementName.equals("while")) {
1401                                            throwValidationException(
1402                                                    1002, fileName, element, elementName);
1403                                    }
1404    
1405                                    validateBlockElement(
1406                                            fileName, element, allowedBlockChildElementNames,
1407                                            allowedExecuteAttributeNames,
1408                                            allowedExecuteChildElementNames,
1409                                            allowedIfConditionElementNames);
1410                            }
1411                            else if (elementName.equals("elseif")) {
1412                                    if (ifElementName.equals("while")) {
1413                                            throwValidationException(
1414                                                    1002, fileName, element, elementName);
1415                                    }
1416    
1417                                    validateIfElement(
1418                                            fileName, element, allowedBlockChildElementNames,
1419                                            allowedExecuteAttributeNames,
1420                                            allowedExecuteChildElementNames,
1421                                            allowedIfConditionElementNames);
1422                            }
1423                            else if (elementName.equals("equals")) {
1424                                    validateSimpleElement(
1425                                            fileName, element, new String[] {"arg1", "arg2"});
1426                            }
1427                            else if (elementName.equals("isset")) {
1428                                    validateSimpleElement(fileName, element, new String[] {"var"});
1429                            }
1430                            else if (elementName.equals("then")) {
1431                                    validateBlockElement(
1432                                            fileName, element, allowedBlockChildElementNames,
1433                                            allowedExecuteAttributeNames,
1434                                            allowedExecuteChildElementNames,
1435                                            allowedIfConditionElementNames);
1436                            }
1437                            else {
1438                                    throwValidationException(1002, fileName, element, elementName);
1439                            }
1440                    }
1441    
1442                    if (!hasAllowedIfConditionElementNames) {
1443                            throwValidationException(
1444                                    1001, fileName, ifElement, allowedIfConditionElementNames);
1445                    }
1446    
1447                    if (Validator.equals(ifElement.getName(), "and") ||
1448                            Validator.equals(ifElement.getName(), "not") ||
1449                            Validator.equals(ifElement.getName(), "or")) {
1450    
1451                            return;
1452                    }
1453    
1454                    if (!elementNames.contains("then")) {
1455                            throwValidationException(
1456                                    1001, fileName, ifElement, new String[] {"then"});
1457                    }
1458            }
1459    
1460            protected void validateMacroDocument(String fileName, Element rootElement) {
1461                    if (!Validator.equals(rootElement.getName(), "definition")) {
1462                            throwValidationException(1000, fileName, rootElement);
1463                    }
1464    
1465                    List<Element> elements = rootElement.elements();
1466    
1467                    String extendsName = rootElement.attributeValue("extends");
1468    
1469                    if (elements.isEmpty() && (extendsName == null)) {
1470                            throwValidationException(
1471                                    1001, fileName, rootElement, new String[] {"command", "var"});
1472                    }
1473                    else if (extendsName != null) {
1474                            if (Validator.isNull(extendsName)) {
1475                                    throwValidationException(
1476                                            1006, fileName, rootElement, "extends");
1477                            }
1478                    }
1479    
1480                    for (Element element : elements) {
1481                            String elementName = element.getName();
1482    
1483                            if (elementName.equals("command")) {
1484                                    String attributeValue = element.attributeValue("name");
1485    
1486                                    if (attributeValue == null) {
1487                                            throwValidationException(1003, fileName, element, "name");
1488                                    }
1489                                    else if (Validator.isNull(attributeValue)) {
1490                                            throwValidationException(1006, fileName, element, "name");
1491                                    }
1492    
1493                                    validateBlockElement(
1494                                            fileName, element,
1495                                            new String[] {
1496                                                    "description", "echo", "execute", "fail", "for", "if",
1497                                                    "take-screenshot", "task", "var", "while"
1498                                            },
1499                                            new String[] {
1500                                                    "action", "function", "macro", "macro-desktop",
1501                                                    "macro-mobile"
1502                                            },
1503                                            new String[] {"var"},
1504                                            new String[] {
1505                                                    "and", "condition", "contains", "equals", "isset",
1506                                                    "not", "or"
1507                                            });
1508                            }
1509                            else if (elementName.equals("var")) {
1510                                    validateVarElement(fileName, element);
1511                            }
1512                            else {
1513                                    throwValidationException(1002, fileName, element, elementName);
1514                            }
1515                    }
1516            }
1517    
1518            protected void validatePathDocument(String fileName, Element rootElement) {
1519                    Element headElement = rootElement.element("head");
1520    
1521                    Element titleElement = headElement.element("title");
1522    
1523                    String title = titleElement.getText();
1524    
1525                    int x = fileName.lastIndexOf(StringPool.SLASH);
1526                    int y = fileName.lastIndexOf(CharPool.PERIOD);
1527    
1528                    String shortFileName = fileName.substring(x + 1, y);
1529    
1530                    if ((title == null) || !shortFileName.equals(title)) {
1531                            throwValidationException(0, fileName);
1532                    }
1533    
1534                    Element bodyElement = rootElement.element("body");
1535    
1536                    Element tableElement = bodyElement.element("table");
1537    
1538                    Element theadElement = tableElement.element("thead");
1539    
1540                    Element trElement = theadElement.element("tr");
1541    
1542                    Element tdElement = trElement.element("td");
1543    
1544                    String tdText = tdElement.getText();
1545    
1546                    if ((tdText == null) || !shortFileName.equals(tdText)) {
1547                            throwValidationException(0, fileName);
1548                    }
1549    
1550                    Element tbodyElement = tableElement.element("tbody");
1551    
1552                    List<Element> elements = tbodyElement.elements();
1553    
1554                    for (Element element : elements) {
1555                            String elementName = element.getName();
1556    
1557                            if (elementName.equals("tr")) {
1558                                    validatePathTrElement(fileName, element);
1559                            }
1560                            else {
1561                                    throwValidationException(1002, fileName, element, elementName);
1562                            }
1563                    }
1564            }
1565    
1566            protected void validatePathTrElement(String fileName, Element trElement) {
1567                    List<Element> elements = trElement.elements();
1568    
1569                    for (Element element : elements) {
1570                            String elementName = element.getName();
1571    
1572                            if (!elementName.equals("td")) {
1573                                    throwValidationException(1002, fileName, element, elementName);
1574                            }
1575                    }
1576    
1577                    if (elements.size() < 3) {
1578                            throwValidationException(
1579                                    1001, fileName, trElement, new String[] {"td"});
1580                    }
1581    
1582                    if (elements.size() > 3) {
1583                            Element element = elements.get(3);
1584    
1585                            String elementName = element.getName();
1586    
1587                            throwValidationException(1002, fileName, element, elementName);
1588                    }
1589    
1590                    Element locatorElement = elements.get(1);
1591    
1592                    String locator = locatorElement.getText();
1593    
1594                    locator = locator.replace("${", "");
1595                    locator = locator.replace("}", "");
1596                    locator = locator.replace("/-/", "/");
1597    
1598                    if (locator.endsWith("/")) {
1599                            locator = locator.substring(0, locator.length() - 1);
1600                    }
1601    
1602                    if (!locator.equals("") && !locator.startsWith("link=") &&
1603                            !locator.startsWith("title=") && !locator.contains(".png")) {
1604    
1605                            try {
1606                                    XPathFactory xPathFactory = XPathFactory.newInstance();
1607    
1608                                    XPath xPath = xPathFactory.newXPath();
1609    
1610                                    xPath.compile(locator);
1611                            }
1612                            catch (Exception e) {
1613                                    throwValidationException(2003, fileName, locator);
1614                            }
1615                    }
1616    
1617                    Element keyElement = elements.get(0);
1618    
1619                    String key = keyElement.getText();
1620    
1621                    Element descriptionElement = elements.get(2);
1622    
1623                    String description = descriptionElement.getText();
1624    
1625                    if (!key.equals("") && !key.equals("EXTEND_ACTION_PATH") &&
1626                            !key.equals("PAGE_NAME") && !description.equals("")) {
1627    
1628                            if (description.endsWith(".")) {
1629                                    throwValidationException(2004, fileName, description);
1630                            }
1631    
1632                            Matcher statmentMatcher = _pathTrElementStatementPattern.matcher(
1633                                    description);
1634    
1635                            if (!statmentMatcher.find()) {
1636                                    throwValidationException(2004, fileName, description);
1637                            }
1638    
1639                            Matcher wordMatcher1 = _pathTrElementWordPattern1.matcher(
1640                                    description);
1641    
1642                            while (wordMatcher1.find()) {
1643                                    String word = wordMatcher1.group();
1644    
1645                                    if (word.equals("a") || word.equals("and") ||
1646                                            word.equals("as") || word.equals("at") ||
1647                                            word.equals("by") || word.equals("for") ||
1648                                            word.equals("from") || word.equals("in") ||
1649                                            word.equals("of") || word.equals("the") ||
1650                                            word.equals("to")) {
1651    
1652                                            continue;
1653                                    }
1654    
1655                                    Matcher wordMatcher2 = _pathTrElementWordPattern2.matcher(word);
1656    
1657                                    if (!wordMatcher2.find()) {
1658                                            throwValidationException(2004, fileName, description);
1659                                    }
1660                            }
1661                    }
1662            }
1663    
1664            protected void validatePropertyElement(
1665                    String fileName, Element propertyElement) {
1666    
1667                    List<Attribute> attributes = propertyElement.attributes();
1668    
1669                    String propertyName = propertyElement.attributeValue("name");
1670    
1671                    if (!_testCaseAvailablePropertyNames.contains(propertyName)) {
1672                            throwValidationException(
1673                                    3003, fileName, propertyElement, propertyName);
1674                    }
1675    
1676                    if (propertyName.equals("ignore.errors")) {
1677                            String propertyDelimiter = propertyElement.attributeValue(
1678                                    "delimiter");
1679    
1680                            String propertyValue = propertyElement.attributeValue("value");
1681    
1682                            if (propertyDelimiter != null) {
1683                                    if (!propertyValue.contains(propertyDelimiter)) {
1684                                            throwValidationException(
1685                                                    1006, fileName, propertyElement, "delimiter");
1686                                    }
1687                            }
1688    
1689                            if (Validator.isNull(propertyValue)) {
1690                                    throwValidationException(
1691                                            1006, fileName, propertyElement, "value");
1692                            }
1693                    }
1694    
1695                    for (Attribute attribute : attributes) {
1696                            String attributeName = attribute.getName();
1697    
1698                            if (attributeName.equals("delimiter") &&
1699                                    propertyName.equals("ignore.errors")) {
1700    
1701                                    continue;
1702                            }
1703                            else if (attributeName.equals("line-number") ||
1704                                             attributeName.equals("name") ||
1705                                             attributeName.equals("value")) {
1706    
1707                                    continue;
1708                            }
1709                            else {
1710                                    throwValidationException(
1711                                            1005, fileName, propertyElement, attributeName);
1712                            }
1713                    }
1714            }
1715    
1716            protected void validateSimpleElement(
1717                    String fileName, Element element, String[] neededAttributes) {
1718    
1719                    Map<String, Boolean> hasNeededAttributes = new HashMap<>();
1720    
1721                    for (String neededAttribute : neededAttributes) {
1722                            hasNeededAttributes.put(neededAttribute, false);
1723                    }
1724    
1725                    List<Attribute> attributes = element.attributes();
1726    
1727                    for (Attribute attribute : attributes) {
1728                            String attributeName = attribute.getName();
1729                            String attributeValue = attribute.getValue();
1730    
1731                            if (!_allowedNullAttributes.contains(attributeName) &&
1732                                    Validator.isNull(attributeValue)) {
1733    
1734                                    throwValidationException(
1735                                            1006, fileName, element, attributeName);
1736                            }
1737    
1738                            if (hasNeededAttributes.containsKey(attributeName)) {
1739                                    hasNeededAttributes.put(attributeName, true);
1740                            }
1741    
1742                            if (!attributeName.equals("line-number") &&
1743                                    !hasNeededAttributes.containsKey(attributeName)) {
1744    
1745                                    throwValidationException(
1746                                            1005, fileName, element, attributeName);
1747                            }
1748                    }
1749    
1750                    for (String neededAttribute : neededAttributes) {
1751                            if (!hasNeededAttributes.get(neededAttribute)) {
1752                                    throwValidationException(
1753                                            1004, fileName, element, neededAttributes);
1754                            }
1755                    }
1756    
1757                    List<Element> childElements = element.elements();
1758    
1759                    if (!childElements.isEmpty()) {
1760                            Element childElement = childElements.get(0);
1761    
1762                            String childElementName = childElement.getName();
1763    
1764                            throwValidationException(
1765                                    1002, fileName, childElement, childElementName);
1766                    }
1767            }
1768    
1769            protected void validateTestCaseDocument(
1770                    String fileName, Element rootElement) {
1771    
1772                    if (!Validator.equals(rootElement.getName(), "definition")) {
1773                            throwValidationException(1000, fileName, rootElement);
1774                    }
1775    
1776                    String extendedTestCase = rootElement.attributeValue("extends");
1777    
1778                    if (extendedTestCase != null) {
1779                            if (Validator.isNull(extendedTestCase)) {
1780                                    throwValidationException(
1781                                            1006, fileName, rootElement, "extends");
1782                            }
1783                    }
1784    
1785                    String componentName = rootElement.attributeValue("component-name");
1786    
1787                    if (componentName == null) {
1788                            throwValidationException(
1789                                    1003, fileName, rootElement, "component-name");
1790                    }
1791    
1792                    if ((componentName != null) &&
1793                            !_componentNames.contains(componentName)) {
1794    
1795                            throwValidationException(
1796                                    1006, fileName, rootElement, "component-name");
1797                    }
1798    
1799                    List<Element> elements = rootElement.elements();
1800    
1801                    if (Validator.isNull(extendedTestCase)) {
1802                            if (elements.isEmpty()) {
1803                                    throwValidationException(
1804                                            1001, fileName, rootElement, new String[] {"command"});
1805                            }
1806                    }
1807    
1808                    for (Element element : elements) {
1809                            String elementName = element.getName();
1810    
1811                            if (elementName.equals("command")) {
1812                                    String attributeValue = element.attributeValue("name");
1813    
1814                                    if (attributeValue == null) {
1815                                            throwValidationException(1003, fileName, element, "name");
1816                                    }
1817                                    else if (Validator.isNull(attributeValue)) {
1818                                            throwValidationException(1006, fileName, element, "name");
1819                                    }
1820    
1821                                    String priorityValue = element.attributeValue("priority");
1822    
1823                                    if (priorityValue == null) {
1824                                            throwValidationException(
1825                                                    1003, fileName, element, "priority");
1826                                    }
1827                                    else if (!(priorityValue.equals("1") ||
1828                                                       priorityValue.equals("2") ||
1829                                                       priorityValue.equals("3") ||
1830                                                       priorityValue.equals("4") ||
1831                                                       priorityValue.equals("5"))) {
1832    
1833                                            throwValidationException(
1834                                                    1006, fileName, element, "priority");
1835                                    }
1836    
1837                                    validateBlockElement(
1838                                            fileName, element,
1839                                            new String[] {
1840                                                    "description", "echo", "execute", "fail", "for", "if",
1841                                                    "property", "take-screenshot", "task", "var", "while"
1842                                            },
1843                                            new String[] {
1844                                                    "action", "function", "macro", "macro-desktop",
1845                                                    "macro-mobile", "test-case"
1846                                            },
1847                                            new String[] {"var"},
1848                                            new String[] {
1849                                                    "and", "condition", "contains", "equals", "isset",
1850                                                    "not", "or"
1851                                            });
1852                            }
1853                            else if (elementName.equals("property")) {
1854                                    validatePropertyElement(fileName, element);
1855                            }
1856                            else if (elementName.equals("set-up") ||
1857                                             elementName.equals("tear-down")) {
1858    
1859                                    List<Attribute> attributes = element.attributes();
1860    
1861                                    for (Attribute attribute : attributes) {
1862                                            String attributeName = attribute.getName();
1863    
1864                                            if (!attributeName.equals("line-number")) {
1865                                                    throwValidationException(
1866                                                            1005, fileName, element, attributeName);
1867                                            }
1868                                    }
1869    
1870                                    validateBlockElement(
1871                                            fileName, element,
1872                                            new String[] {
1873                                                    "description", "echo", "execute", "fail", "if",
1874                                                    "take-screenshot", "task", "var", "while"
1875                                            },
1876                                            new String[] {
1877                                                    "action", "function", "macro", "macro-desktop",
1878                                                    "macro-mobile", "test-case"
1879                                            },
1880                                            new String[] {"var"},
1881                                            new String[] {
1882                                                    "and", "condition", "contains", "equals", "isset",
1883                                                    "not", "or"
1884                                            });
1885                            }
1886                            else if (elementName.equals("var")) {
1887                                    validateVarElement(fileName, element);
1888                            }
1889                            else {
1890                                    throwValidationException(1002, fileName, element, elementName);
1891                            }
1892                    }
1893    
1894                    elements = getAllChildElements(rootElement, "property");
1895    
1896                    for (Element element : elements) {
1897                            String name = element.attributeValue("name");
1898                            String value = element.attributeValue("value");
1899    
1900                            if (name.equals("testray.component.names")) {
1901                                    List<String> testrayComponentNames = ListUtil.fromArray(
1902                                            StringUtil.split(value));
1903    
1904                                    for (String testrayComponentName : testrayComponentNames) {
1905                                            if (!_testrayAvailableComponentNames.contains(
1906                                                            testrayComponentName)) {
1907    
1908                                                    throwValidationException(
1909                                                            3001, fileName, element, name,
1910                                                            testrayComponentName);
1911                                            }
1912                                    }
1913                            }
1914                            else if (name.equals("testray.main.component.name")) {
1915                                    if (!_testrayAvailableComponentNames.contains(value)) {
1916                                            throwValidationException(
1917                                                    3001, fileName, element, name, value);
1918                                    }
1919                            }
1920                    }
1921    
1922                    elements = rootElement.elements("property");
1923    
1924                    boolean rootTestrayMainComponentNameFound = false;
1925    
1926                    for (Element element : elements) {
1927                            String name = element.attributeValue("name");
1928    
1929                            if (name.equals("testray.main.component.name")) {
1930                                    rootTestrayMainComponentNameFound = true;
1931    
1932                                    break;
1933                            }
1934                    }
1935    
1936                    if (!rootTestrayMainComponentNameFound) {
1937                            elements = rootElement.elements("command");
1938    
1939                            for (Element element : elements) {
1940                                    List<Element> propertyElements = getAllChildElements(
1941                                            element, "property");
1942    
1943                                    boolean commandTestrayMainComponentNameFound = false;
1944    
1945                                    for (Element propertyElement : propertyElements) {
1946                                            String propertyName = propertyElement.attributeValue(
1947                                                    "name");
1948    
1949                                            if (propertyName.equals("testray.main.component.name")) {
1950                                                    commandTestrayMainComponentNameFound = true;
1951    
1952                                                    break;
1953                                            }
1954                                    }
1955    
1956                                    if (!commandTestrayMainComponentNameFound) {
1957                                            throwValidationException(
1958                                                    3002, fileName, element, "testray.main.component.name");
1959                                    }
1960                            }
1961                    }
1962            }
1963    
1964            protected void validateVarElement(String fileName, Element element) {
1965                    List<Attribute> attributes = element.attributes();
1966    
1967                    Map<String, String> attributeMap = new HashMap<>();
1968    
1969                    for (Attribute attribute : attributes) {
1970                            String attributeName = attribute.getName();
1971                            String attributeValue = attribute.getValue();
1972    
1973                            if (!attributeName.equals("value") &&
1974                                    Validator.isNull(attributeValue)) {
1975    
1976                                    throwValidationException(
1977                                            1006, fileName, element, attributeName);
1978                            }
1979    
1980                            if (!_allowedVarAttributes.contains(attributeName)) {
1981                                    throwValidationException(
1982                                            1005, fileName, element, attributeName);
1983                            }
1984    
1985                            attributeMap.put(attributeName, attributeValue);
1986                    }
1987    
1988                    if (!attributeMap.containsKey("name")) {
1989                            throwValidationException(
1990                                    1004, fileName, element, new String[] {"name"});
1991                    }
1992                    else {
1993                            String nameValue = attributeMap.get("name");
1994    
1995                            if (Validator.isNull(nameValue)) {
1996                                    throwValidationException(1006, fileName, element, "name");
1997                            }
1998                    }
1999    
2000                    if (attributeMap.containsKey("locator")) {
2001                            String[] disallowedAttributes = {"locator-key", "path", "value"};
2002    
2003                            for (String disallowedAttribute : disallowedAttributes) {
2004                                    if (attributeMap.containsKey(disallowedAttribute)) {
2005                                            throwValidationException(
2006                                                    1005, fileName, element, disallowedAttribute);
2007                                    }
2008                            }
2009                    }
2010                    else if (attributeMap.containsKey("locator-key") &&
2011                                     attributeMap.containsKey("path")) {
2012    
2013                            if (attributeMap.containsKey("value")) {
2014                                    throwValidationException(1005, fileName, element, "value");
2015                            }
2016                    }
2017                    else if (attributeMap.containsKey("locator-key")) {
2018                            throwValidationException(
2019                                    1004, fileName, element, new String[] {"path"});
2020                    }
2021                    else if (attributeMap.containsKey("path")) {
2022                            throwValidationException(
2023                                    1004, fileName, element, new String[] {"locator-key"});
2024                    }
2025    
2026                    String varText = element.getText();
2027    
2028                    if (attributeMap.containsKey("locator") ||
2029                            attributeMap.containsKey("locator-key") ||
2030                            attributeMap.containsKey("path")) {
2031    
2032                            if (!Validator.isNull(varText)) {
2033                                    throwValidationException(1005, fileName, element, "value");
2034                            }
2035                    }
2036    
2037                    if (attributeMap.containsKey("method")) {
2038                            String methodValue = attributeMap.get("method");
2039    
2040                            if (!methodValue.startsWith("MathUtil") &&
2041                                    !methodValue.startsWith("selenium") &&
2042                                    !methodValue.startsWith("StringUtil") &&
2043                                    !methodValue.startsWith("TestPropsUtil")) {
2044    
2045                                    throwValidationException(1005, fileName, element, "method");
2046                            }
2047    
2048                            if (!methodValue.contains("#")) {
2049                                    throwValidationException(1005, fileName, element, "method");
2050                            }
2051                    }
2052    
2053                    if (!attributeMap.containsKey("property-value") &&
2054                            !attributeMap.containsKey("value") && Validator.isNull(varText)) {
2055    
2056                            if (!attributeMap.containsKey("group") &&
2057                                    !attributeMap.containsKey("input") &&
2058                                    !attributeMap.containsKey("locator") &&
2059                                    !attributeMap.containsKey("locator-key") &&
2060                                    !attributeMap.containsKey("method") &&
2061                                    !attributeMap.containsKey("path") &&
2062                                    !attributeMap.containsKey("pattern")) {
2063    
2064                                    throwValidationException(
2065                                            1004, fileName, element, new String[] {"value"});
2066                            }
2067                    }
2068                    else {
2069                            String varValue = attributeMap.get("value");
2070    
2071                            if (Validator.isNull(varValue)) {
2072                                    varValue = varText;
2073                            }
2074    
2075                            Matcher matcher = _varElementPattern.matcher(varValue);
2076    
2077                            while (matcher.find()) {
2078                                    String statement = matcher.group(1);
2079    
2080                                    Matcher statementMatcher = _varElementStatementPattern.matcher(
2081                                            statement);
2082    
2083                                    if (statementMatcher.find()) {
2084                                            String operand = statementMatcher.group(1);
2085    
2086                                            String method = statementMatcher.group(2);
2087    
2088                                            if (operand.equals("") || method.equals("")) {
2089                                                    throwValidationException(
2090                                                            1006, fileName, element, "value");
2091                                            }
2092    
2093                                            if (!_methodNames.contains(method)) {
2094                                                    throwValidationException(
2095                                                            1013, fileName, element, method);
2096                                            }
2097                                    }
2098                            }
2099                    }
2100    
2101                    List<Element> childElements = element.elements();
2102    
2103                    if (!childElements.isEmpty()) {
2104                            Element childElement = childElements.get(0);
2105    
2106                            String childElementName = childElement.getName();
2107    
2108                            throwValidationException(
2109                                    1002, fileName, childElement, childElementName);
2110                    }
2111            }
2112    
2113            private static final String _TPL_ROOT =
2114                    "com/liferay/portal/tools/seleniumbuilder/dependencies/";
2115    
2116            private static final List<String> _allowedNullAttributes =
2117                    ListUtil.fromArray(
2118                            new String[] {
2119                                    "arg1", "arg2", "delimiter", "message", "string", "substring",
2120                                    "value"
2121                            });
2122            private static final List<String> _allowedVarAttributes =
2123                    ListUtil.fromArray(
2124                            new String[] {
2125                                    "attribute", "group", "input", "line-number", "locator",
2126                                    "locator-key", "method", "name", "path", "pattern",
2127                                    "property-value", "value"
2128                            });
2129            private static final List<String> _methodNames = ListUtil.fromArray(
2130                    new String[] {
2131                            "getFirstNumber", "getIPAddress", "increment", "length",
2132                            "lowercase", "replace", "uppercase"
2133                    });
2134            private static final List<String> _reservedTags = ListUtil.fromArray(
2135                    new String[] {
2136                            "and", "case", "command", "condition", "contains", "default",
2137                            "definition", "delimiter", "description", "echo", "else", "elseif",
2138                            "equals", "execute", "fail", "for", "if", "isset", "not", "or",
2139                            "property", "set-up", "take-screenshot", "task", "td", "tear-down",
2140                            "then", "tr", "while", "var"
2141                    });
2142    
2143            private final String _baseDirName;
2144            private final List<String> _componentNames;
2145            private final Pattern _pathTrElementStatementPattern = Pattern.compile(
2146                    "[A-Z0-9].*");
2147            private final Pattern _pathTrElementWordPattern1 = Pattern.compile(
2148                    "[A-Za-z0-9\\-]+");
2149            private final Pattern _pathTrElementWordPattern2 = Pattern.compile(
2150                    "[A-Z0-9][A-Za-z0-9\\-]*");
2151            private final Properties _properties = new Properties();
2152            private final Pattern _tagPattern = Pattern.compile("<[a-z\\-]+");
2153            private final List<String> _testCaseAvailablePropertyNames;
2154            private final List<String> _testrayAvailableComponentNames;
2155            private final Pattern _varElementFunctionPattern = Pattern.compile(
2156                    "\\$\\{(locator|value)[0-9]+\\}");
2157            private final Pattern _varElementPattern = Pattern.compile(
2158                    "\\$\\{([^\\}]*?)\\}");
2159            private final Pattern _varElementStatementPattern = Pattern.compile(
2160                    "(.*)\\?(.*)\\(([^\\)]*?)\\)");
2161    
2162    }