001/*
002 * Sonar, open source software quality management tool.
003 * Copyright (C) 2008-2012 SonarSource
004 * mailto:contact AT sonarsource DOT com
005 *
006 * Sonar is free software; you can redistribute it and/or
007 * modify it under the terms of the GNU Lesser General Public
008 * License as published by the Free Software Foundation; either
009 * version 3 of the License, or (at your option) any later version.
010 *
011 * Sonar is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014 * Lesser General Public License for more details.
015 *
016 * You should have received a copy of the GNU Lesser General Public
017 * License along with Sonar; if not, write to the Free Software
018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
019 */
020package org.sonar.duplications.statement;
021
022import java.util.ArrayList;
023import java.util.List;
024
025import org.sonar.duplications.DuplicationsException;
026import org.sonar.duplications.statement.matcher.TokenMatcher;
027import org.sonar.duplications.token.TokenQueue;
028
029public final class StatementChunker {
030
031  private final StatementChannelDisptacher channelDispatcher;
032
033  public static Builder builder() {
034    return new Builder();
035  }
036
037  private StatementChunker(Builder builder) {
038    this.channelDispatcher = builder.getChannelDispatcher();
039  }
040
041  public List<Statement> chunk(TokenQueue tokenQueue) {
042    if (tokenQueue == null) {
043      throw new IllegalArgumentException();
044    }
045    List<Statement> statements = new ArrayList<Statement>();
046    try {
047      channelDispatcher.consume(tokenQueue, statements);
048      return statements;
049    } catch (Exception e) {
050      throw new DuplicationsException("Unable to build statement from token : " + tokenQueue.peek(), e);
051    }
052  }
053
054  /**
055   * Note that order is important, e.g.
056   * <code>statement(token(A)).ignore(token(A))</code> for the input sequence "A" will produce statement, whereas
057   * <code>ignore(token(A)).statement(token(A))</code> will not.
058   */
059  public static final class Builder {
060
061    private List<StatementChannel> channels = new ArrayList<StatementChannel>();
062
063    private Builder() {
064    }
065
066    public StatementChunker build() {
067      return new StatementChunker(this);
068    }
069
070    /**
071     * Defines that sequence of tokens must be ignored, if it matches specified list of matchers.
072     * 
073     * @see TokenMatcherFactory
074     */
075    public Builder ignore(TokenMatcher... matchers) {
076      channels.add(StatementChannel.createBlackHole(matchers));
077      return this;
078    }
079
080    /**
081     * Defines that sequence of tokens, which is matched specified list of matchers, is a statement.
082     * 
083     * @see TokenMatcherFactory
084     */
085    public Builder statement(TokenMatcher... matchers) {
086      channels.add(StatementChannel.create(matchers));
087      return this;
088    }
089
090    private StatementChannelDisptacher getChannelDispatcher() {
091      return new StatementChannelDisptacher(channels);
092    }
093  }
094
095}