content: /**
   * Licensed 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.aurora.scheduler.cron.quartz;
  
  import java.text.ParseException;
  import java.util.List;
  import java.util.TimeZone;
  
  import com.google.common.base.Joiner;
  import com.google.common.collect.ContiguousSet;
  import com.google.common.collect.DiscreteDomain;
  import com.google.common.collect.Lists;
  import com.google.common.collect.Range;
  
  import org.apache.aurora.scheduler.base.JobKeys;
  import org.apache.aurora.scheduler.cron.CrontabEntry;
  import org.apache.aurora.scheduler.storage.entities.IJobKey;
  import org.quartz.CronExpression;
  import org.quartz.CronScheduleBuilder;
  import org.quartz.CronTrigger;
  import org.quartz.Job;
  import org.quartz.JobBuilder;
  import org.quartz.JobDetail;
  import org.quartz.JobKey;
  import org.quartz.TriggerBuilder;
  
  import static java.util.Objects.requireNonNull;
  
  /**
   * Utilities for converting Aurora datatypes to Quartz datatypes.
   */
  final class Quartz {
    private Quartz() {
      // Utility class.
    }
  
    /**
     * Convert an Aurora CrontabEntry to a Quartz CronExpression.
     */
    static CronExpression cronExpression(CrontabEntry entry, TimeZone timeZone) {
      String dayOfMonth;
      if (entry.hasWildcardDayOfMonth()) {
        dayOfMonth = "?"; // special quartz token meaning "don't care"
      } else {
        dayOfMonth = entry.getDayOfMonthAsString();
      }
      String dayOfWeek;
      if (entry.hasWildcardDayOfWeek() && !entry.hasWildcardDayOfMonth()) {
        dayOfWeek = "?";
      } else {
        List<Integer> daysOfWeek = Lists.newArrayList();
        for (Range<Integer> range : entry.getDayOfWeek().asRanges()) {
          for (int i : ContiguousSet.create(range, DiscreteDomain.integers())) {
            daysOfWeek.add(i + 1); // Quartz has an off-by-one with what the "standard" defines.
          }
        }
        dayOfWeek = Joiner.on(",").join(daysOfWeek);
      }
  
      String rawCronExpresion = String.join(
          " ",
          "0",
          entry.getMinuteAsString(),
          entry.getHourAsString(),
          dayOfMonth,
          entry.getMonthAsString(),
          dayOfWeek);
      CronExpression cronExpression;
      try {
        cronExpression = new CronExpression(rawCronExpresion);
      } catch (ParseException e) {
        throw new RuntimeException(e);
      }
      cronExpression.setTimeZone(timeZone);
      return cronExpression;
    }
  
    /**
     * Convert a Quartz JobKey to an Aurora IJobKey.
     */
    static IJobKey auroraJobKey(JobKey jobKey) {
      return JobKeys.parse(jobKey.getName());
    }
  
    /**
     * Convert an Aurora IJobKey to a Quartz JobKey.
     */
    static JobKey jobKey(IJobKey jobKey) {
      return JobKey.jobKey(JobKeys.canonicalString(jobKey));
    }
  
    static CronTrigger cronTrigger(CrontabEntry schedule, TimeZone timeZone) {
      return TriggerBuilder.newTrigger()
          .withSchedule(CronScheduleBuilder.cronSchedule(cronExpression(schedule, timeZone)))
          .withDescription(schedule.toString())
          .build();
    }
  
    static JobDetail jobDetail(IJobKey jobKey, Class<? extends Job> jobClass) {
      requireNonNull(jobKey);
      requireNonNull(jobClass);
  
      return JobBuilder.newJob(jobClass)
          .withIdentity(jobKey(jobKey))
          .build();
    }
  
    static CrontabEntry crontabEntry(CronTrigger cronTrigger) {
      return CrontabEntry.parse(cronTrigger.getDescription());
    }
  }
  