class DoubleParser extends AbstractColumnParser<Double> {

  public DoubleParser(ColumnType columnType) {
    super(columnType);
  }

  public DoubleParser(DoubleColumnType doubleColumnType, ReadOptions readOptions) {
    super(doubleColumnType);
    if (readOptions.missingValueIndicators().length > 0) {
      missingValueStrings = Lists.newArrayList(readOptions.missingValueIndicators());
    }
  }

  @Override
  public boolean canParse(String s) {
    if (isMissing(s)) {
      return true;
    }
    try {
      if (isPercent(AbstractColumnParser.remove(s, ','))) {
        s = AbstractColumnParser.remove(s, ',');
        Number number = NumberFormat.getPercentInstance().parse(s);
      } else {
        Double.parseDouble(AbstractColumnParser.remove(s, ','));
      }
      return true;
    } catch (NumberFormatException | ParseException | IndexOutOfBoundsException e) {
      // it's all part of the plan
      return false;
    }
  }

  @Override
  public Double parse(String s) {
    return parseDouble(s);
  }

  @Override
  public double parseDouble(String s) {
    if (isMissing(s)) {
      return DoubleColumnType.missingValueIndicator();
    }
    if (isPercent(AbstractColumnParser.remove(s, ','))) {
      s = AbstractColumnParser.remove(s, ',').substring(0, s.length() - 1);
      return Double.parseDouble(s) / 100.0;
    }
    return Double.parseDouble(AbstractColumnParser.remove(s, ','));
  }

  /**
   * A number is percent when it ends with %
   * - We can trim off the last occurrence of '%'
   * - The remaining string should then be parsable as a number(double)
   *
   * @param s Value
   * @return Flag returning whether the input is a percent or not
   */
  private boolean isPercent(String s) {<FILL_FUNCTION_BODY>}
}

    boolean containsPercentSymbol = s.charAt(s.length() - 1) == '%';
    if (containsPercentSymbol) {
      String percentageValue = s.substring(0, s.length() - 1);
      try {
        double value = Double.parseDouble(percentageValue);
        return !Double.isNaN(value);
      } catch (NumberFormatException e) {
        // it's all part of the plan
        return false;
      }
    }
    return false;
  
<methods>public void <init>(tech.tablesaw.api.ColumnType) ,public abstract boolean canParse(java.lang.String) ,public tech.tablesaw.api.ColumnType columnType() ,public boolean isMissing(java.lang.String) ,public abstract java.lang.Double parse(java.lang.String) ,public byte parseByte(java.lang.String) ,public double parseDouble(java.lang.String) ,public float parseFloat(java.lang.String) ,public int parseInt(java.lang.String) ,public long parseLong(java.lang.String) ,public short parseShort(java.lang.String) ,public void setMissingValueStrings(List<java.lang.String>) <variables>private final non-sealed tech.tablesaw.api.ColumnType columnType,protected List<java.lang.String> missingValueStrings