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

com.hazelcast.org.apache.calcite.runtime.Matcher Maven / Gradle / Ivy

There is a newer version: 5.5.0
Show newest version
/*
 * 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 com.hazelcast.org.apache.calcite.runtime;

import com.hazelcast.org.apache.calcite.linq4j.MemoryFactory;
import com.hazelcast.org.apache.calcite.util.ImmutableBitSet;

import com.hazelcast.com.google.common.collect.ImmutableList;
import com.hazelcast.com.google.common.collect.ImmutableMap;
import com.hazelcast.com.google.common.collect.ImmutableSet;
import com.hazelcast.com.google.common.collect.Sets;

import com.hazelcast.org.checkerframework.checker.nullness.qual.Nullable;

import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * Workspace that partialMatches patterns against an automaton.
 * @param  Type of rows matched by this automaton
 */
public class Matcher {
  private final DeterministicAutomaton dfa;
  private final ImmutableMap>> predicates;

  // The following members are work space. They can be shared among partitions,
  // but only one thread can use them at a time. Putting them here saves the
  // expense of creating a fresh object each call to "match".

  @SuppressWarnings("unused")
  private final ImmutableBitSet startSet;

  /**
   * Creates a Matcher; use {@link #builder}.
   */
  private Matcher(Automaton automaton,
      ImmutableMap>> predicates) {
    this.predicates = Objects.requireNonNull(predicates, "predicates");
    final ImmutableBitSet.Builder startSetBuilder =
        ImmutableBitSet.builder();
    startSetBuilder.set(automaton.startState.id);
    automaton.epsilonSuccessors(automaton.startState.id, startSetBuilder);
    startSet = startSetBuilder.build();
    // Build the DFA
    dfa = new DeterministicAutomaton(automaton);
  }

  public static  Builder builder(Automaton automaton) {
    return new Builder<>(automaton);
  }

  public List> match(E... rows) {
    return match(Arrays.asList(rows));
  }

  public List> match(Iterable rows) {
    final ImmutableList.Builder> resultMatchBuilder =
        ImmutableList.builder();
    final Consumer> resultMatchConsumer = resultMatchBuilder::add;
    final PartitionState partitionState = createPartitionState(0, 0);
    for (E row : rows) {
      partitionState.getMemoryFactory().add(row);
      matchOne(partitionState.getRows(), partitionState, resultMatchConsumer);
    }
    return resultMatchBuilder.build();
  }

  public PartitionState createPartitionState(int history, int future) {
    return new PartitionState<>(history, future);
  }

  /**
   * Feeds a single input row into the given partition state,
   * and writes the resulting output rows (if any).
   * This method ignores the symbols that caused a transition.
   */
  protected void matchOne(MemoryFactory.Memory rows,
      PartitionState partitionState, Consumer> resultMatches) {
    List> matches = matchOneWithSymbols(rows, partitionState);
    for (PartialMatch pm : matches) {
      resultMatches.accept(pm);
    }
  }

  protected List> matchOneWithSymbols(MemoryFactory.Memory rows,
      PartitionState partitionState) {
    final HashSet> newMatches = new HashSet<>();
    for (Map.Entry>> predicate
        : predicates.entrySet()) {
      for (PartialMatch pm : partitionState.getPartialMatches()) {
        // Remove this match
        if (predicate.getValue().test(rows)) {
          // Check if we have transitions from here
          final List transitions =
              dfa.getTransitions().stream()
                  .filter(t -> predicate.getKey().equals(t.symbol))
                  .filter(t -> pm.currentState.equals(t.fromState))
                  .collect(Collectors.toList());

          for (DeterministicAutomaton.Transition transition : transitions) {
            // System.out.println("Append new transition to ");
            final PartialMatch newMatch = pm.append(transition.symbol,
                rows.get(), transition.toState);
            newMatches.add(newMatch);
          }
        }
      }
      // Check if a new Match starts here
      if (predicate.getValue().test(rows)) {
        final List transitions =
            dfa.getTransitions().stream()
                .filter(t -> predicate.getKey().equals(t.symbol))
                .filter(t -> dfa.startState.equals(t.fromState))
                .collect(Collectors.toList());

        for (DeterministicAutomaton.Transition transition : transitions) {
          final PartialMatch newMatch = new PartialMatch<>(-1L,
              ImmutableList.of(transition.symbol), ImmutableList.of(rows.get()),
              transition.toState);
          newMatches.add(newMatch);
        }
      }
    }

    // Remove all current partitions
    partitionState.clearPartitions();
    // Add all partial matches
    partitionState.addPartialMatches(newMatches);
    // Check if one of the new Matches is in a final state, otherwise add them
    // and go on
    final ImmutableList.Builder> builder =
        ImmutableList.builder();
    for (PartialMatch match : newMatches) {
      if (dfa.getEndStates().contains(match.currentState)) {
        // This is the match, handle all "open" partial matches with a suitable
        // strategy
        // TODO add strategy
        // Return it!
        builder.add(match);
      }
    }
    return builder.build();
  }

  /**
   * State for each partition.
   *
   * @param  Row type
   */
  static class PartitionState {
    private final Set> partialMatches = new HashSet<>();
    private final MemoryFactory memoryFactory;

    PartitionState(int history, int future) {
      this.memoryFactory = new MemoryFactory<>(history, future);
    }

    public void addPartialMatches(Collection> matches) {
      partialMatches.addAll(matches);
    }

    public Set> getPartialMatches() {
      return ImmutableSet.copyOf(partialMatches);
    }

    public void removePartialMatch(PartialMatch pm) {
      partialMatches.remove(pm);
    }

    public void clearPartitions() {
      partialMatches.clear();
    }

    public MemoryFactory.Memory getRows() {
      return memoryFactory.create();
    }

    public MemoryFactory getMemoryFactory() {
      return this.memoryFactory;
    }
  }

  /**
   * Partial match of the NFA.
   *
   * 

This class is immutable; the {@link #copy()} and * {@link #append(String, Object, DeterministicAutomaton.MultiState)} * methods generate new instances. * * @param Row type */ static class PartialMatch { final long startRow; final ImmutableList symbols; final ImmutableList rows; final DeterministicAutomaton.MultiState currentState; PartialMatch(long startRow, ImmutableList symbols, ImmutableList rows, DeterministicAutomaton.MultiState currentState) { this.startRow = startRow; this.symbols = symbols; this.rows = rows; this.currentState = currentState; } public PartialMatch copy() { return new PartialMatch<>(startRow, symbols, rows, currentState); } public PartialMatch append(String symbol, E row, DeterministicAutomaton.MultiState toState) { ImmutableList symbols = ImmutableList.builder() .addAll(this.symbols) .add(symbol) .build(); ImmutableList rows = ImmutableList.builder() .addAll(this.rows) .add(row) .build(); return new PartialMatch<>(startRow, symbols, rows, toState); } @Override public boolean equals(@Nullable Object o) { return o == this || o instanceof PartialMatch && startRow == ((PartialMatch) o).startRow && Objects.equals(symbols, ((PartialMatch) o).symbols) && Objects.equals(rows, ((PartialMatch) o).rows) && Objects.equals(currentState, ((PartialMatch) o).currentState); } @Override public int hashCode() { return Objects.hash(startRow, symbols, rows, currentState); } @Override public String toString() { final StringBuilder sb = new StringBuilder(); sb.append("["); for (int i = 0; i < rows.size(); i++) { if (i > 0) { sb.append(", "); } sb.append("("); sb.append(symbols.get(i)); sb.append(", "); sb.append(rows.get(i)); sb.append(")"); } sb.append("]"); return sb.toString(); } } /** * Builds a Matcher. * * @param Type of rows matched by this automaton */ public static class Builder { final Automaton automaton; final Map>> symbolPredicates = new HashMap<>(); Builder(Automaton automaton) { this.automaton = automaton; } /** * Associates a predicate with a symbol. */ public Builder add(String symbolName, Predicate> predicate) { symbolPredicates.put(symbolName, predicate); return this; } public Matcher build() { final Set predicateSymbolsNotInGraph = Sets.newTreeSet(symbolPredicates.keySet()); predicateSymbolsNotInGraph.removeAll(automaton.symbolNames); if (!predicateSymbolsNotInGraph.isEmpty()) { throw new IllegalArgumentException("not all predicate symbols [" + predicateSymbolsNotInGraph + "] are in graph [" + automaton.symbolNames + "]"); } final ImmutableMap.Builder>> builder = ImmutableMap.builder(); for (String symbolName : automaton.symbolNames) { // If a symbol does not have a predicate, it defaults to true. // By convention, "STRT" is used for the start symbol, but it could be // anything. builder.put(symbolName, symbolPredicates.getOrDefault(symbolName, e -> true)); } return new Matcher<>(automaton, builder.build()); } } /** * A 2-tuple consisting of a symbol and a row. * * @param Type of Row */ static class Tuple { final String symbol; final E row; Tuple(String symbol, E row) { this.symbol = symbol; this.row = row; } @Override public boolean equals(@Nullable Object o) { return o == this || o instanceof Tuple && ((Tuple) o).symbol.equals(symbol) && Objects.equals(row, ((Tuple) o).row); } @Override public int hashCode() { return Objects.hash(symbol, row); } @Override public String toString() { return "(" + symbol + ", " + row + ")"; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy