org.apache.struts.maven.snippetextractor.parser.SnippetParser.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.struts.maven.snippetextractor.parser.SnippetParser.java

Source

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you 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 org.apache.struts.maven.snippetextractor.parser;

import org.apache.struts.maven.snippetextractor.parser.java.InvalidSnippetException;

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

import static org.apache.commons.lang.StringUtils.isBlank;

/**
 * Parser handling the logic for finding start, name and end of a snippet.
 * </p>
 * The pattern for a snippet start is: {@code <!-- START SNIPPET: snippet name -->}.
 * </p>
 * The pattern for a snippet end is : {@code <!-- END SNIPPET: snippet name -->}.
 * </p>
 * Every snippet must have a name. You cannot nest snippets.
 * </p>
 * The snippet start and end can be escaped with a double backslash: {@literal \\}.
 */
public class SnippetParser {
    private static final String SNIPPET_START_REGEX = "(?<!\\\\)(<!--\\s*START SNIPPET:)(.*?)(-->)";
    private static final Pattern SNIPPET_START_PATTERN = Pattern.compile(SNIPPET_START_REGEX);
    private static final String SNIPPET_STOP_REGEX = "(?<!\\\\)(<!--\\s*END SNIPPET:)(.*?)(-->)";
    private static final Pattern SNIPPET_END_PATTERN = Pattern.compile(SNIPPET_STOP_REGEX);
    private String snippetName = null;
    private Matcher snippetStartMatcher;
    private Matcher snippetEndMatcher;

    /**
     * Constructor for a statefull SnippetParser. Before making the effort to instantiate one, check if there is a
     * snippet start in your string using the static method {@link #isSnippetStart(String)}. As every snippet must have
     * a name a {@link InvalidSnippetException} will be thrown if there is a unnamed snippet.
     *
     * @param string containing the snippet start to be parsed
     * @throws IllegalArgumentException if you provide a string without a snippet start,
     *                                  use {@link #isSnippetStart(String)} before
     * @throws InvalidSnippetException  if the snippet is unnamed
     */
    public SnippetParser(String string) throws IllegalArgumentException, InvalidSnippetException {
        snippetStartMatcher = SNIPPET_START_PATTERN.matcher(string);
        if (isSnippetStart(string, snippetStartMatcher)) {
            snippetName = snippetStartMatcher.group(2);
        }
        if (snippetName == null) {
            throw new IllegalArgumentException(
                    "No snippet found. Use isSnippetStart first before creating instance.");
        }
        snippetName = snippetName.trim();
        if (isBlank(snippetName)) {
            throw new InvalidSnippetException("Snippet without name found. This is an invalid snippet.");
        }
    }

    /**
     * Check wether the provided string contains a snippet start which should look like
     * {@code <!-- START SNIPPET: snippet name -->}.
     *
     * @param string to be parsed for a snippet start
     * @return {@code true} if a snippet start is found, otherwise {@code false}
     */
    public static boolean isSnippetStart(String string) {
        return isSnippetStart(string, null);
    }

    private static boolean isSnippetStart(String string, Matcher matcher) {
        if (matcher == null) {
            matcher = SNIPPET_START_PATTERN.matcher(string);
        }
        return matcher.find() && matcher.groupCount() == 3;
    }

    /**
     * Get the name of the snippet the SnippetParser was instantiated with.
     *
     * @return snippet name without surrounding whitespaces
     */
    public String getSnippetName() {
        return snippetName;
    }

    /**
     * Get the complete snippet start string without any clutter that is not part of the snippet start pattern.
     *
     * @return complete snippet start
     */
    public String getSnippetStart() {
        return snippetStartMatcher.group();
    }

    /**
     * Checks whether the provided string contains a matching snippet end pattern for this snippet. A snippet end looks
     * like {@code <!-- END SNIPPET: snippet name -->}. The name must match to the snippet start, otherwise a
     * {@link InvalidSnippetException} is thrown.
     *
     * @param string to parse for a matching snippet end
     * @return {@code true} if a matching snippet is found, otherwise {@code false}
     * @throws InvalidSnippetException is thrown if the name of the snippet end does not match the name the snippet
     *                                 start this SnippetParser was instatiated with.
     */
    public boolean isSnippetEnd(String string) throws InvalidSnippetException {
        snippetEndMatcher = SNIPPET_END_PATTERN.matcher(string);
        if (!doesContainSnippetEnd(snippetEndMatcher)) {
            snippetEndMatcher = null;
            return false;
        }
        String snippetEndName = snippetEndMatcher.group(2).trim();
        if (!snippetName.equals(snippetEndName)) {
            snippetEndMatcher = null;
            throw new InvalidSnippetException("End of snippet with name " + snippetEndName + " found, but "
                    + "snippet " + snippetName + " still open. Cannot handle nested snippets.");
        }
        return true;
    }

    /**
     * Get the index of where the snippet end starts. Be sure you have looked for the snippet end before with the method
     * {@link #isSnippetEnd(String)}, otherwise a {@link IllegalStateException} will be thrown.
     *
     * @return position where the snippet end pattern starts
     * @throws IllegalStateException if {@link #isSnippetEnd(String)} was not called before once
     */
    public int indexOfSnippetEnd() throws IllegalStateException {
        if (snippetEndMatcher == null) {
            throw new IllegalStateException(
                    "Snippet end not yet found. Use isSnippetEnd() for finding it before " + "using this method.");
        }
        return snippetEndMatcher.start();
    }

    private boolean doesContainSnippetEnd(Matcher matcher) {
        return matcher.find() && matcher.groupCount() == 3;
    }
}