gt:         HashMap<String, Frequency> repeatTimes = new HashMap<>();
context: /**
   * Copyright (c) 2012 Todoroo Inc
   *
   * See the file "LICENSE" for the full license governing this code.
   */
  package com.todoroo.astrid.utility;
  
  import android.text.TextUtils;
  
  import com.google.ical.values.Frequency;
  import com.google.ical.values.RRule;
  import com.mdimension.jchronic.AstridChronic;
  import com.mdimension.jchronic.Chronic;
  import com.todoroo.astrid.data.Task;
  import com.todoroo.astrid.tags.TagService;
  
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import java.util.ArrayList;
  import java.util.Calendar;
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.Set;
  import java.util.regex.Matcher;
  import java.util.regex.Pattern;
  
  public class TitleParser {
  
      private static final Logger log = LoggerFactory.getLogger(TitleParser.class);
  
      public static boolean parse(TagService tagService, Task task, ArrayList<String> tags) {
          boolean markup;
          markup = repeatHelper(task);
          listHelper(tagService, task,tags); // Don't need to know if tags affected things since we don't show alerts for them
          markup = dayHelper(task) || markup;
          markup = priorityHelper(task) || markup;
          return markup;
      }
  
      public static String trimParenthesis(String pattern){
          if (pattern.charAt(0) == '#' || pattern.charAt(0) == '@') {
              pattern = pattern.substring(1);
          }
          if ('(' == pattern.charAt(0)) {
              return pattern.substring(1, pattern.length()-1);
          }
          return pattern;
      }
      public static void listHelper(TagService tagService, Task task, ArrayList<String> tags) {
          String inputText = task.getTitle();
          Pattern tagPattern = Pattern.compile("(\\s|^)#(\\(.*\\)|[^\\s]+)");
          Pattern contextPattern = Pattern.compile("(\\s|^)@(\\(.*\\)|[^\\s]+)");
  
          Set<String> addedTags = new HashSet<>();
  
          while(true) {
              Matcher m = tagPattern.matcher(inputText);
              if(m.find()) {
                  String tag = TitleParser.trimParenthesis(m.group(2));
                  String tagWithCase = tagService.getTagWithCase(tag);
                  if (!addedTags.contains(tagWithCase)) {
                      tags.add(tagWithCase);
                  }
                  addedTags.add(tagWithCase);
              } else {
                  m = contextPattern.matcher(inputText);
                  if(m.find()) {
                      String tag = TitleParser.trimParenthesis(m.group(2));
                      String tagWithCase = tagService.getTagWithCase(tag);
                      if (!addedTags.contains(tagWithCase)) {
                          tags.add(tagWithCase);
                      }
                      addedTags.add(tagWithCase);
                  } else{
                      break;
                  }
              }
              inputText = inputText.substring(0, m.start()) + inputText.substring(m.end());
          }
          task.setTitle(inputText.trim());
      }
  
      //helper method for priorityHelper. converts the string to a Task Importance
      private static int strToPriority(String priorityStr) {
          if (priorityStr!=null) {
              priorityStr.toLowerCase().trim();
          }
          int priority = Task.IMPORTANCE_DO_OR_DIE;
          if ("0".equals(priorityStr) || "!0".equals(priorityStr) || "least".equals(priorityStr) ||  "lowest".equals(priorityStr)) {
              priority = Task.IMPORTANCE_NONE;
          }
          if ("!".equals(priorityStr) || "!1".equals(priorityStr) || "bang".equals(priorityStr) || "1".equals(priorityStr) || "low".equals(priorityStr)) {
              priority = Task.IMPORTANCE_SHOULD_DO;
          }
          if ("!!".equals(priorityStr) || "!2".equals(priorityStr) || "bang bang".equals(priorityStr) || "2".equals(priorityStr) || "high".equals(priorityStr)) {
              priority = Task.IMPORTANCE_MUST_DO;
          }
          return priority;
      }
  
      //priorityHelper parses the string and sets the Task's importance
      private static boolean priorityHelper(Task task) {
          String inputText = task.getTitle();
          String[] importanceStrings = {
                  "()((^|[^\\w!])!+|(^|[^\\w!])!\\d)($|[^\\w!])",
                  "()(?i)((\\s?bang){1,})$",
                  "(?i)(\\spriority\\s?(\\d)$)",
                  "(?i)(\\sbang\\s?(\\d)$)",
                  "(?i)()(\\shigh(est)?|\\slow(est)?|\\stop|\\sleast) ?priority$"
          };
          boolean result = false;
          for (String importanceString:importanceStrings){
              Pattern importancePattern = Pattern.compile(importanceString);
              while (true){
                  Matcher m = importancePattern.matcher(inputText);
                  if(m.find()) {
                      result = true;
                      task.setImportance(strToPriority(m.group(2).trim()));
                      int start = m.start() == 0 ? 0 : m.start() + 1;
                      inputText = inputText.substring(0, start) + inputText.substring(m.end());
  
                  } else {
                      break;
                  }
              }
          }
          task.setTitle(inputText.trim());
          return result;
      }
  
      //helper for dayHelper. Converts am/pm to an int 0/1.
      private static int ampmToNumber(String amPmString) {
          int time = Calendar.PM;
          if (amPmString == null){
              return time;
          }
          String text = amPmString.toLowerCase().trim();
          if (text.equals ("am") || text.equals ("a.m") || text.equals("a")) {
              time = Calendar.AM;
          }
          if (text.equals ("pm") || text.equals ("p.m") || text.equals("p")) {
              time = Calendar.PM;
          }
          return time;
      }
  
      private static String removeIfParenthetical(Matcher m, String inputText) {
          String s = m.group();
          if (s.startsWith("(") && s.endsWith(")")) {
              return inputText.substring(0, m.start()) + inputText.substring(m.end());
          }
          return inputText;
      }
  
      private static String stripParens(String s) {
          if (s.startsWith("(")) {
              s = s.substring(1);
          }
          if (s.endsWith(")")) {
              s = s.substring(0, s.length() - 1);
          }
          return s;
      }
  
      //---------------------DATE--------------------------
      //Handles setting the task's date.
      //Day of week (e.g. Monday, Tuesday,..) is overridden by a set date (e.g. October 23 2013).
      //Vague times (e.g. breakfast, night) are overridden by a set time (9 am, at 10, 17:00)
      private static boolean dayHelper(Task task ) {
          if (task.containsNonNullValue(Task.DUE_DATE)) {
              return false;
          }
          String inputText = task.getTitle();
          Calendar cal = null;
          Boolean containsSpecificTime = false;
          String[] daysOfWeek = {
                  "(?i)(\\(|\\b)today(\\)|\\b)",
                  "(?i)(\\(|\\b)tomorrow(\\)|\\b)",
                  "(?i)(\\(|\\b)mon(day(\\)|\\b)|(\\)|\\.))",
                  "(?i)(\\(|\\b)tue(sday(\\)|\\b)|(\\)|\\.))",
                  "(?i)(\\(|\\b)wed(nesday(\\)|\\b)|(\\)|\\.))",
                  "(?i)(\\(|\\b)thu(rsday(\\)|\\b)|(\\)|\\.))",
                  "(?i)(\\(|\\b)fri(day(\\)|\\b)|(\\)|\\.))",
                  "(?i)(\\(|\\b)sat(urday(\\)|\\b)|(\\)|\\.))",
                  "(?i)(\\(|\\b)sun(day(\\)|\\b)|(\\)|\\.))"
          };
  
          for (String date : daysOfWeek){
              Pattern pattern = Pattern.compile(date);
              Matcher m = pattern.matcher(inputText);
              if (m.find()) {
                  String toParse = stripParens(m.group(0));
                  cal = AstridChronic.parse(toParse).getBeginCalendar();
                  inputText = removeIfParenthetical(m, inputText);
                  //then put it into task
              }
          }
  
          String[] dates = {
                  "(?i)(\\(|\\b)(jan(\\.|uary))(\\s(3[0-1]|[0-2]?[0-9])),?( (\\d{4}|\\d{2}))?(\\)|\\b)",
                  "(?i)(\\(|\\b)(feb(\\.|ruary))(\\s(3[0-1]|[0-2]?[0-9])),?( (\\d{4}|\\d{2}))?(\\)|\\b)",
                  "(?i)(\\(|\\b)(mar(\\.|ch))(\\s(3[0-1]|[0-2]?[0-9])),?( (\\d{4}|\\d{2}))?(\\)|\\b)",
                  "(?i)(\\(|\\b)(apr(\\.|il))(\\s(3[0-1]|[0-2]?[0-9])),?( (\\d{4}|\\d{2}))?(\\)|\\b)",
                  "(?i)(\\(|\\b)(may())(\\s(3[0-1]|[0-2]?[0-9])),?( (\\d{4}|\\d{2}))?(\\)|\\b)",
                  "(?i)(\\(|\\b)(jun(\\.|e))(\\s(3[0-1]|[0-2]?[0-9])),?( (\\d{4}|\\d{2}))?(\\)|\\b)",
                  "(?i)(\\(|\\b)(jul(\\.|y))(\\s(3[0-1]|[0-2]?[0-9])),?( (\\d{4}|\\d{2}))?(\\)|\\b)",
                  "(?i)(\\(|\\b)(aug(\\.|ust))(\\s(3[0-1]|[0-2]?[0-9])),?( (\\d{4}|\\d{2}))?(\\)|\\b)",
                  "(?i)(\\(|\\b)(sep(\\.|tember))(\\s(3[0-1]|[0-2]?[0-9])),?( (\\d{4}|\\d{2}))?(\\)|\\b)",
                  "(?i)(\\(|\\b)(oct(\\.|ober))(\\s(3[0-1]|[0-2]?[0-9])),?( (\\d{4}|\\d{2}))?(\\)|\\b)",
                  "(?i)(\\(|\\b)(nov(\\.|ember))(\\s(3[0-1]|[0-2]?[0-9])),?( (\\d{4}|\\d{2}))?(\\)|\\b)",
                  "(?i)(\\(|\\b)(dec(\\.|ember))(\\s(3[0-1]|[0-2]?[0-9])),?( (\\d{4}|\\d{2}))?(\\)|\\b)"
          };
  
          // m.group(2) = "month"
          //m.group(5) = "day"
          for (String date: dates) {
              Pattern pattern = Pattern.compile(date);
              Matcher m = pattern.matcher(inputText);
  
              if (m.find()){
                  Calendar dateCal = Chronic.parse(m.group(2)).getBeginCalendar();
                  if (m.group(5) != null) {
                      dateCal.set(Calendar.DAY_OF_MONTH, Integer.parseInt(m.group(5)));
                  }
                  Calendar today = Calendar.getInstance();
                  if (m.group(6) != null) {
                      dateCal.set(Calendar.YEAR, Integer.parseInt(m.group(6).trim()));
                  } else if (today.get(Calendar.MONTH) - dateCal.get(Calendar.MONTH) > 1) { //if more than a month in the past
                      dateCal.set(Calendar.YEAR, dateCal.get(Calendar.YEAR) + 1);
                  }
                  if (cal == null) {
                      cal = dateCal;
                  } else{
                      cal.set(Calendar.DAY_OF_MONTH, dateCal.get(Calendar.DAY_OF_MONTH));
                      cal.set(Calendar.MONTH,dateCal.get(Calendar.MONTH) );
                      cal.set(Calendar.YEAR, dateCal.get(Calendar.YEAR));
                  }
                  inputText = removeIfParenthetical(m, inputText);
              }
          }
  
          // for dates in the format MM/DD
          Pattern p = Pattern.compile("(?i)(\\(|\\b)(1[0-2]|0?[1-9])(\\/|-)(3[0-1]|[0-2]?[0-9])(\\/|-)?(\\d{4}|\\d{2})?(\\)|\\b)");
          Matcher match = p.matcher(inputText);
          if (match.find()){
              Calendar dCal = Calendar.getInstance();
              setCalendarToDefaultTime(dCal);
              dCal.set(Calendar.MONTH, Integer.parseInt(match.group(2).trim()) - 1);
              dCal.set(Calendar.DAY_OF_MONTH, Integer.parseInt(match.group(4)));
              if (match.group(6) != null && !(match.group(6).trim()).equals("")) {
                  String yearString = match.group(6);
                  if(match.group(6).length() == 2) {
                      yearString = "20" + match.group(6);
                  }
                  dCal.set(Calendar.YEAR, Integer.parseInt(yearString));
              }
  
              if (cal == null) {
                  cal = dCal;
              } else{
                  cal.set(Calendar.DAY_OF_MONTH, dCal.get(Calendar.DAY_OF_MONTH));
                  cal.set(Calendar.MONTH,dCal.get(Calendar.MONTH));
                  cal.set(Calendar.YEAR, dCal.get(Calendar.YEAR));
              }
              inputText = removeIfParenthetical(match, inputText);
          }
  
          HashMap<String, Integer> dayTimes = new HashMap<>();
          dayTimes.put("(?i)\\bbreakfast\\b", 8);
          dayTimes.put("(?i)\\blunch\\b", 12);
          dayTimes.put("(?i)\\bsupper\\b", 18);
          dayTimes.put("(?i)\\bdinner\\b", 18);
          dayTimes.put("(?i)\\bbrunch\\b", 10);
          dayTimes.put("(?i)\\bmorning\\b", 8);
          dayTimes.put("(?i)\\bafternoon\\b", 15);
          dayTimes.put("(?i)\\bevening\\b", 19);
          dayTimes.put("(?i)\\bnight\\b", 19);
          dayTimes.put("(?i)\\bmidnight\\b", 0);
          dayTimes.put("(?i)\\bnoon\\b", 12);
  
          Set<String> keys = dayTimes.keySet();
          for (String dayTime : keys) {
              Pattern pattern = Pattern.compile(dayTime);
              Matcher m = pattern.matcher(inputText);
              if (m.find()) {
                  containsSpecificTime = true;
                  int timeHour = dayTimes.get(dayTime);
                  Calendar dayTimesCal = Calendar.getInstance();
                  setCalendarToDefaultTime(dayTimesCal);
                  dayTimesCal.set(Calendar.HOUR, timeHour);
                  if (cal == null) {
                      cal = dayTimesCal;
                  } else {
                      setCalendarToDefaultTime(cal);
                      cal.set(Calendar.HOUR, timeHour);
                  }
              }
          }
  
          String[] times = {
                  //[time] am/pm
                  "(?i)(\\b)([01]?\\d):?([0-5]\\d)? ?([ap]\\.?m?\\.?)\\b",
                  //army time
                  "(?i)\\b(([0-2]?[0-9]):([0-5][0-9]))(\\b)",
                  //[int] o'clock
                  "(?i)\\b(([01]?\\d)() ?o'? ?clock) ?([ap]\\.?m\\.?)?\\b",
                  //at [int]
                  "(?i)(\\bat) ([01]?\\d)()($|\\D($|\\D))"
  
                  //m.group(2) holds the hour
                  //m.group(3) holds the minutes
                  //m.group(4) holds am/pm
          };
  
          for (String time : times){
              Pattern pattern = Pattern.compile(time);
              Matcher m = pattern.matcher(inputText);
              if (m.find()) {
                  containsSpecificTime = true;
                  Calendar today = Calendar.getInstance();
                  Calendar timeCal = Calendar.getInstance();
                  setCalendarToDefaultTime(timeCal);
                  timeCal.set(Calendar.HOUR, Integer.parseInt(m.group(2)));
  
                  if (m.group(3) != null && !m.group(3).trim().equals("")) {
                      timeCal.set(Calendar.MINUTE, Integer.parseInt(m.group(3)));
                  } else {
                      timeCal.set(Calendar.MINUTE, 0);
                  }
                  if (Integer.parseInt(m.group(2)) <= 12) {
                      timeCal.set(Calendar.AM_PM, ampmToNumber(m.group(4)));
                  }
  
                  //sets it to the next occurrence of that hour if no am/pm is provided. doesn't include military time
                  if (Integer.parseInt(m.group(2))<= 12 && (m.group(4)==null || (m.group(4).trim()).equals(""))) {
                      while (timeCal.getTime().getTime() < today.getTime().getTime()){
                          timeCal.set(Calendar.HOUR_OF_DAY, timeCal.get(Calendar.HOUR_OF_DAY)+12);
                      }
                  } else { //if am/pm is provided and the time is in the past, set it to the next day. Military time included.
                      if (timeCal.get(Calendar.HOUR) !=0 && (timeCal.getTime().getTime() < today.getTime().getTime())) {
                          timeCal.set(Calendar.DAY_OF_MONTH, timeCal.get(Calendar.DAY_OF_MONTH) + 1);
                      }
                      if (timeCal.get(Calendar.HOUR) == 0){
                          timeCal.set(Calendar.HOUR, 12);
                      }
                  }
  
                  if (cal == null){
                      cal = timeCal;
                  } else {
                      cal.set(Calendar.HOUR, timeCal.get(Calendar.HOUR));
                      cal.set(Calendar.MINUTE,timeCal.get(Calendar.MINUTE) );
                      cal.set(Calendar.SECOND, timeCal.get(Calendar.SECOND));
                      cal.set(Calendar.AM_PM, timeCal.get(Calendar.AM_PM));
                  }
                  break;
              }
          }
  
          if(cal != null) { //if at least one of the above has been called, write to task. else do nothing.
              if (!TextUtils.isEmpty(inputText)) {
                  task.setTitle(inputText);
              }
              if (containsSpecificTime) {
                  task.setDueDate(Task.createDueDate(Task.URGENCY_SPECIFIC_DAY_TIME, cal.getTime().getTime()));
              } else {
                  task.setDueDate(Task.createDueDate(Task.URGENCY_SPECIFIC_DAY, cal.getTime().getTime()));
              }
              return true;
          }
          return false;
      }
      //---------------------DATE--------------------------
  
      //Parses through the text and sets the frequency of the task.
      private static boolean repeatHelper(Task task) {
          if (task.containsNonNullValue(Task.RECURRENCE)) {
              return false;
          }
          String inputText = task.getTitle();
  