FormatPattern.java

/*
 * 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.calcite.util.format.postgresql.format;

import org.apache.calcite.util.format.postgresql.PatternModifier;
import org.apache.calcite.util.format.postgresql.format.compiled.CompiledPattern;

import com.google.common.collect.ImmutableSet;

import java.text.ParseException;
import java.text.ParsePosition;
import java.util.Set;

/**
 * A single component of a date/time format pattern such as YYYY or MI. Each
 * component may have some flags applied. If the flags are not used by a component,
 * then they are ignored.
 */
public abstract class FormatPattern {
  protected final String pattern;

  /**
   * Base class constructor that stores the pattern.
   *
   * @param pattern the date/time component taht this FormatPattern represents
   */
  protected FormatPattern(String pattern) {
    this.pattern = pattern;
  }

  /**
   * Creates the compiled version of the parsed date/time component along with
   * any flags that it had. It is expected that the formatString has the date/time
   * component (with its flags) at the position indicated in formatParsePosition.
   *
   * @param formatString the full date/time format string
   * @param formatParsePosition starting position in formatString with this
   *                            pattern is located
   * @return the compiled version of the parsed date/time component
   * @throws ParseException If the date/time format component was not found
   */
  public CompiledPattern compilePattern(String formatString,
      ParsePosition formatParsePosition) throws ParseException {
    String formatTrimmed = formatString.substring(formatParsePosition.getIndex());
    int charsConsumed = 0;
    final ImmutableSet.Builder<PatternModifier> modifiers = ImmutableSet.builder();

    // Find the prefix modifiers
    boolean modifierFound = true;
    while (modifierFound) {
      modifierFound = false;

      for (PatternModifier modifier : PatternModifier.values()) {
        if (modifier.isPrefix() && formatTrimmed.startsWith(modifier.getModifierString())) {
          modifiers.add(modifier);
          formatTrimmed = formatTrimmed.substring(modifier.getModifierString().length());
          charsConsumed += modifier.getModifierString().length();
          modifierFound = true;
          break;
        }
      }
    }

    if (formatTrimmed.startsWith(pattern)) {
      charsConsumed += pattern.length();
      formatTrimmed = formatString.substring(formatParsePosition.getIndex() + charsConsumed);

      // Find the suffix modifiers
      modifierFound = true;
      while (modifierFound) {
        modifierFound = false;

        for (PatternModifier modifier : PatternModifier.values()) {
          if (!modifier.isPrefix() && formatTrimmed.startsWith(modifier.getModifierString())) {
            modifiers.add(modifier);
            formatTrimmed = formatTrimmed.substring(modifier.getModifierString().length());
            modifierFound = true;
            break;
          }
        }
      }

      return buildCompiledPattern(modifiers.build());
    }

    throw new ParseException("Pattern not found", formatParsePosition.getIndex());
  }

  /**
   * Creates a new instance of the compiled version of this date/time component.
   *
   * @param modifiers the set of flags that were parsed
   * @return a new instance of the compiled version of this date/time component
   */
  protected abstract CompiledPattern buildCompiledPattern(Set<PatternModifier> modifiers);

  public String getPattern() {
    return pattern;
  }
}