com.google.testing.pogen.parser.template.TemplateParser.java Source code

Java tutorial

Introduction

Here is the source code for com.google.testing.pogen.parser.template.TemplateParser.java

Source

// Copyright 2011 The PageObjectGenerator Authors.
// Copyright 2011 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.testing.pogen.parser.template;

import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Range;
import com.google.common.collect.RangeSet;
import com.google.common.collect.TreeRangeSet;

/**
 * A class to parse a template to retrieve the {@link TemplateInfo} instance.
 * 
 * @author Kazunori Sakamoto
 */
public abstract class TemplateParser {

    /**
     * A name of the attribute to be assigned for tags containing template
     * variables.
     */
    protected final String attributeName;

    /**
     * Constructs the instance of {@link TemplateParser} with the specified
     * attribute name.
     * 
     * @param attributeName
     *            the name of the attribute to be assigned for tags containing
     *            template variables
     */
    public TemplateParser(String attributeName) {
        this.attributeName = attributeName;
    }

    /**
     * Retrieves the information of the specified template by parsing it.
     * 
     * @param template
     *            the string of the template to be parsed
     * @return the information of the specified template
     * @throws TemplateParseException
     *             if the specified template is in bad format
     */
    public TemplateInfo parse(String template) throws TemplateParseException {
        Preconditions.checkNotNull(template);

        RangeSet<Integer> repeatedParts = parseRepeatedPart(template);
        List<HtmlTagInfo> htmlTagInfos = parseTagsContainingVariables(template, repeatedParts);
        return new TemplateInfo(template, htmlTagInfos);
    }

    /**
     * Retrieves an information list of html tags which contain template
     * variables by parsing the specified template.
     * 
     * @param template
     *            the string of the template to be parsed
     * @param repeatedParts 
     * @return the information list of html tags which contain template
     *         variables
     * @throws TemplateParseException
     *             if the specified template is in bad format
     */
    protected abstract List<HtmlTagInfo> parseTagsContainingVariables(String template,
            RangeSet<Integer> repeatedParts) throws TemplateParseException;

    /**
     * Retrieves a {@link RangeSet} of indexes where the repeated part are
     * located in the specified template. Note that repeated part is inner part
     * of for/foreach tags and of template tags called from another repeated
     * part. For example, {template .t1}repeated part{/template}{foreach
     * ...}repeated part{call .t1}{/foreach}.
     * 
     * @param template
     *            the string of the template to be parsed
     * @return the {@link RangeSet} of the indexes where repeated part are
     *         located
     * @throws TemplateParseException
     *             if the specified template is in bad format
     */
    protected abstract RangeSet<Integer> parseRepeatedPart(String template) throws TemplateParseException;

    /**
     * Returns start indexes of strings that match the indicated regular
     * expression.
     * 
     * @param text
     *            the string to be parsed
     * @param pattern
     *            the regular expression to find
     * @return the start indexes of matched strings with the indicated pattern
     */
    protected static List<Integer> getMatchedIndexes(String text, Pattern pattern) {
        List<Integer> results = Lists.newArrayList();
        Matcher m = pattern.matcher(text);
        while (m.find()) {
            results.add(m.start());
        }
        return results;
    }

    /**
     * Returns a list of pairs containing matched strings and their start index
     * for the specified regular expression and the given group index.
     * 
     * @param text
     *            the string to be parsed
     * @param pattern
     *            the regular expression to find
     * @param groupIndex
     *            the index of the group whose matched string we want to
     *            retrieve
     * @return the start indexes of matched strings with the indicated pattern
     */
    protected static List<StringWithIndex> getMatchedStringAndIndexes(String text, Pattern pattern,
            int groupIndex) {
        List<StringWithIndex> results = Lists.newArrayList();
        Matcher m = pattern.matcher(text);
        while (m.find()) {
            results.add(new StringWithIndex(m.group(groupIndex), m.start()));
        }
        return results;
    }

    /**
     * Returns a {@link RangeSet} of indexes between the start and the end tags
     * for non-nested tags.
     * 
     * @param text
     *            the string to be parsed
     * @param startPattern
     *            the regular expression of start tags to find
     * @param endPattern
     *            the regular expression of end tags to find
     * @return the {@link RangeSet} of the indexes between the start and the end
     *         tags
     * @throws TemplateParseException
     *             if the specified template is in bad format where broken pairs
     *             of start and end tags appear
     */
    protected static RangeSet<Integer> getIndexRangesOfNonNestedTags(String text, Pattern startPattern,
            Pattern endPattern) throws TemplateParseException {

        RangeSet<Integer> rangeSet = TreeRangeSet.create();

        List<Integer> startIndexes = getMatchedIndexes(text, startPattern);
        List<Integer> endIndexes = getMatchedIndexes(text, endPattern);
        // Check whether all start tags and end tags are paired correctly
        if (startIndexes.size() != endIndexes.size()) {
            throw new TemplateParseException(
                    String.format("There are broken pairs of start and end tags (#start tags: %d, #end tags: %d)",
                            startIndexes.size(), endIndexes.size()));
        }

        for (int i = 0; i < startIndexes.size(); i++) {
            rangeSet.add(Range.closedOpen(startIndexes.get(i), endIndexes.get(i)));
        }
        return rangeSet;
    }

    /**
     * Returns a {@link RangeSet} of indexes between the start and the end tags
     * for nested tags. If there're nested tags, returns the outermost ranges.
     * 
     * @param text
     *            the string to be parsed
     * @param startPattern
     *            the regular expression of start tags to find
     * @param endPattern
     *            the regular expression of end tags to find
     * @return the {@link RangeSet} of the indexes between the start and the end
     *         tags
     * @throws TemplateParseException
     *             if the specified template is in bad format where broken pairs
     *             of start and end tags appear
     */
    protected static RangeSet<Integer> getIndexRangesOfNestedTags(String text, Pattern startPattern,
            Pattern endPattern) throws TemplateParseException {
        RangeSet<Integer> rangeSet = TreeRangeSet.create();

        List<Integer> startIndexes = getMatchedIndexes(text, startPattern);
        List<Integer> endIndexes = getMatchedIndexes(text, endPattern);

        int startIndex = 0, endIndex = 0;
        // Check whether the sizes of start tags and end tags are equal
        if (startIndexes.size() != endIndexes.size()) {
            throw new TemplateParseException(String.format(
                    "The sizes of start tags and end tags are not equal (#start tags: %d, #end tags: %d).",
                    startIndexes.size(), endIndexes.size()));
        }

        int depth = 0, lastStartIndex = 0;
        while (endIndex < endIndexes.size()) {
            boolean consumedStartIndexes = startIndex == startIndexes.size();
            // Process a previous index before a next index
            if (consumedStartIndexes || endIndexes.get(endIndex) < startIndexes.get(startIndex)) {
                if (--depth <= 0) {
                    if (depth < 0) {
                        // Check whether all start tags and end tags are paired
                        // correctly
                        throw new TemplateParseException(
                                String.format("Broken pairs of start and end tags are found."));
                    }
                    rangeSet.add(Range.closedOpen(lastStartIndex, endIndexes.get(endIndex)));
                }
                endIndex++;
            } else {
                if (depth++ == 0) {
                    lastStartIndex = startIndexes.get(startIndex);
                }
                startIndex++;
            }
        }
        return rangeSet;
    }

    /**
     * Returns a {@link Map} of names and indexes between the start and the end
     * tags for non-nested tags.
     * 
     * @param text
     *            the string to be parsed
     * @param startPattern
     *            the regular expression of start tags to find
     * @param endPattern
     *            the regular expression of end tags to find
     * @param groupIndex
     *            the index of the group in matched parts to retrieve a name
     * @return the {@link Map} of the names and the indexes between the start
     *         and the end tags
     * @throws TemplateParseException
     *             if the specified template is in bad format where broken pairs
     *             of start and end tags appear
     */
    protected static Map<String, Range<Integer>> getNamedIndexRangesOfNonNestedTags(String text,
            Pattern startPattern, Pattern endPattern, int groupIndex) throws TemplateParseException {
        Map<String, Range<Integer>> templates = Maps.newHashMap();

        List<StringWithIndex> starts = getMatchedStringAndIndexes(text, startPattern, groupIndex);
        List<Integer> ends = getMatchedIndexes(text, endPattern);
        // Check whether the sizes of start tags and end tags are equal
        if (starts.size() != ends.size()) {
            throw new TemplateParseException(
                    String.format("There are broken pairs of start and end tags (#start tags: %d, #end tags: %d)",
                            starts.size(), ends.size()));
        }

        int endIndexesIndex = 0;
        for (StringWithIndex nameAndIndex : starts) {
            int endIndex = ends.get(endIndexesIndex++);
            // Check whether all start tags and end tags are paired correctly
            if (nameAndIndex.getIndex() >= endIndex) {
                throw new TemplateParseException(String.format("Broken pairs of start and end tags are found."));
            }
            templates.put(nameAndIndex.getString(), Range.closedOpen(nameAndIndex.getIndex(), endIndex));
        }
        return templates;
    }
}