All Downloads are FREE. Search and download functionalities are using the official Maven repository.

reader-pdd-1.1.src.main.java.com.rempl.readers.pdd.FilePuzzles Maven / Gradle / Ivy

There is a newer version: 1.1.3
Show newest version
/**
 * Copyright (c) 2011, REMPL.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1) Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * 2) Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following
 * disclaimer in the documentation and/or other materials provided
 * with the distribution.
 * 3) Neither the name of the REMPL.com nor the names of its
 * contributors may be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
 * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
 * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.rempl.readers.pdd;

// REMPL API classes
import com.rempl.api.Logger;

// antlr parser
import com.rempl.readers.pdd.puzzle.PuzzleLexer;
import com.rempl.readers.pdd.puzzle.PuzzleParser;

// utils
import com.rempl.utils.VelocityWriter;

// file management
import java.io.File;

// collections
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

// regular expressions
import java.util.regex.Matcher;
import java.util.regex.Pattern;

// ANTLR3 runtime for grammar/lexical processing
import org.antlr.runtime.ANTLRStringStream;
import org.antlr.runtime.CharStream;
import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.TokenStream;

// file management
import org.apache.commons.io.FileUtils;

/**
 * Read puzzles from a file.
 *
 * @author Yegor Bugayenko ([email protected])
 * @version $Id: FilePuzzles.java 795 2011-03-19 10:17:23Z [email protected] $
 */
public final class FilePuzzles {

    /**
     * The {@link File} to work with.
     * @see #FilePuzzles(File)
     */
    private final File file;

    /**
     * Public ctor.
     * @param pfile The file to work with
     */
    public FilePuzzles(final File pfile) {
        this.file = pfile;
    }

    /**
     * Convert file information to the UML.
     * @return UML in string
     * @throws java.io.IOException If I/O problem occurs
     */
    public String uml() throws java.io.IOException {
        if (Logger.isTraceEnabled(this)) {
            Logger.trace(this, "#uml()");
        }
        final List lines = FileUtils.readLines(this.file);
        final List puzzles = this.puzzles(lines);
        if (Logger.isDebugEnabled(this)) {
            for (Puzzle puzzle : puzzles) {
                Logger.debug(
                    this,
                    "#uml(%s): puzzle found (#%s:%dmin by %s)",
                    this.file.getName(),
                    puzzle.getTicket(),
                    puzzle.getEstimate(),
                    puzzle.getOwnerTitle()
                );
            }
        }
        final Map ctx = new HashMap();
        ctx.put("file", this.file);
        ctx.put("puzzles", puzzles);
        final String uml = VelocityWriter.INSTANCE
            .write("vm/file-puzzles.vm", ctx);
        if (Logger.isDebugEnabled(this)) {
            Logger.debug(
                this,
                "#uml(): %d puzzles discovered in %s:\n%s",
                puzzles.size(),
                this.file.getName(),
                uml
            );
        }
        return uml;
    }

    /**
     * Retrieve puzzles from the lines.
     * @param lines List of input lines
     * @return List of puzzles discovered
     */
    public List puzzles(final List lines) {
        final List puzzles = new ArrayList();
        Puzzle puzzle = null;
        Integer num = 0;
        Integer started = num;
        final Matcher matcher = this.matcher();
        for (String line : lines) {
            num += 1;
            matcher.reset(line);
            if (matcher.matches()) {
                // the puzzle start is found, let's create a new object
                // and add it immediately to the collection
                puzzle = this.parse(line);
                if (puzzle != null) {
                    // yes, it's not a mistake, the puzzle is found and
                    // perfectly parsed, now it goes to the collection
                    puzzles.add(puzzle);
                    started = num;
                    puzzle.setLines(started, started);
                }
            } else if (puzzle != null) {
                // we're somewhere inside the puzzle found before,
                // and this particular line has to go into it
                // or has to stop the puzzle
                if (line.startsWith(puzzle.getPrefix())) {
                    // yes, this line goes into the puzzle, and we
                    // continue working with the puzzle
                    puzzle.appendBody(
                        line.substring(puzzle.getPrefix().length())
                    );
                } else {
                    // no, the line is not for this puzzle, we have
                    // to stop working with the puzzle and register
                    // it's location in the files (the lines)
                    puzzle.setLines(started, num - 1);
                    puzzle = null;
                }
            }
        }
        return puzzles;
    }

    /**
     * Get RegEx matcher to match every line for puzzles.
     * @return The matcher
     * @see #puzzles(List)
     */
    private Matcher matcher() {
        final Pattern pattern = Pattern.compile(
            // start of the line
            "^"
            // something before the @todo tag
            + "([\\W\\D]+\\s+|)"
            // the tag itself
            + "@todo "
            // the number of the ticket
            + "#(\\d+)"
            // one of the following, followed by a space-char
            + "(/\\d+|:\\d+(\\.\\d+)?(hrs|hr|h|)|\\?|\\!)* "
            // rest of the line
            + ".*"
            // end of the line
            + "$"
        );
        return pattern.matcher("");
    }

    /**
     * Make puzzle out of string.
     * @param line One line in the input file
     * @return The puzzle discovered or NULL if nothing found
     */
    private Puzzle parse(final String line) {
        final CharStream input = new ANTLRStringStream(line);
        final PuzzleLexer lexer = new PuzzleLexer(input);
        final TokenStream tokens = new CommonTokenStream(lexer);
        final PuzzleParser parser = new PuzzleParser(tokens);
        Puzzle puzzle;
        try {
            puzzle = parser.puzzle();
        } catch (org.antlr.runtime.RecognitionException ex) {
            puzzle = null;
        } catch (PuzzleParsingException ex) {
            puzzle = null;
        }
        if (Logger.isDebugEnabled(this)) {
            Logger.debug(this, "#parse(%s)", line);
        }
        return puzzle;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy