Repository: accumulo
Updated Branches:
  refs/heads/master bd515fceb -> 83f346b28


ACCUMULO-2493: Deprecated BinaryFormatter in favor of DefaultFormatter
with FormatterConfig.

* New class FormatterConfig handles configuration of Formatter objects
* FormatterConfig is taken by Formatter.initialize, which broke the
interface for many classes throughout the non-public API
* Added DateFormatSupplier to let Formatters use DateFormat in a
Thread-safe way
* Removed code from ScanCommand and Shell tied to BinaryFormatter to use
properly configured DefaultFormatter instead
* Fixed bug where `scan -f [num] -fm [class]` would ignore Formatter
class used in `-fm` and be overridden with BinaryFormatter


Project: http://git-wip-us.apache.org/repos/asf/accumulo/repo
Commit: http://git-wip-us.apache.org/repos/asf/accumulo/commit/83f346b2
Tree: http://git-wip-us.apache.org/repos/asf/accumulo/tree/83f346b2
Diff: http://git-wip-us.apache.org/repos/asf/accumulo/diff/83f346b2

Branch: refs/heads/master
Commit: 83f346b281ecf1ac01ca8af2e22f8cdd18a10b77
Parents: bd515fc
Author: Matt Dailey <matthew.dail...@gmail.com>
Authored: Fri Dec 4 21:50:58 2015 -0500
Committer: Josh Elser <els...@apache.org>
Committed: Wed Jan 13 17:56:43 2016 -0500

----------------------------------------------------------------------
 .../core/util/format/BinaryFormatter.java       |  41 ++-----
 .../core/util/format/DateFormatSupplier.java    |  95 +++++++++++++++
 .../core/util/format/DateStringFormatter.java   |  57 ++++-----
 .../core/util/format/DefaultFormatter.java      |  99 +++++++++-------
 .../accumulo/core/util/format/Formatter.java    |   2 +-
 .../core/util/format/FormatterConfig.java       | 115 +++++++++++++++++++
 .../core/util/format/FormatterFactory.java      |   8 +-
 .../accumulo/core/util/format/HexFormatter.java |   8 +-
 .../util/format/DateFormatSupplierTest.java     |  74 ++++++++++++
 .../util/format/DateStringFormatterTest.java    |  29 ++++-
 .../core/util/format/DefaultFormatterTest.java  |  62 +++++++++-
 .../core/util/format/FormatterConfigTest.java   |  81 +++++++++++++
 .../core/util/format/FormatterFactoryTest.java  |   5 +-
 .../core/util/format/HexFormatterTest.java      |   6 +-
 .../ShardedTableDistributionFormatterTest.java  |   4 +-
 .../format/StatisticsDisplayFormatterTest.java  |   4 +-
 .../server/replication/StatusFormatter.java     |  18 +--
 .../apache/accumulo/tracer/TraceFormatter.java  |  22 ++--
 .../java/org/apache/accumulo/shell/Shell.java   |  19 +--
 .../shell/commands/DeleteManyCommand.java       |   5 +-
 .../shell/commands/GetSplitsCommand.java        |   6 +-
 .../accumulo/shell/commands/GrepCommand.java    |   6 +-
 .../accumulo/shell/commands/ScanCommand.java    |  31 ++---
 .../accumulo/shell/format/DeleterFormatter.java |  11 +-
 .../org/apache/accumulo/shell/ShellTest.java    |  96 +++++++++++++++-
 .../shell/format/DeleterFormatterTest.java      |  20 ++--
 .../org/apache/accumulo/test/ShellServerIT.java |   9 +-
 27 files changed, 719 insertions(+), 214 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/accumulo/blob/83f346b2/core/src/main/java/org/apache/accumulo/core/util/format/BinaryFormatter.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/accumulo/core/util/format/BinaryFormatter.java 
b/core/src/main/java/org/apache/accumulo/core/util/format/BinaryFormatter.java
index a5f6a8d..f5cbe39 100644
--- 
a/core/src/main/java/org/apache/accumulo/core/util/format/BinaryFormatter.java
+++ 
b/core/src/main/java/org/apache/accumulo/core/util/format/BinaryFormatter.java
@@ -17,67 +17,50 @@
 package org.apache.accumulo.core.util.format;
 
 import java.util.Map.Entry;
-
 import org.apache.accumulo.core.data.Key;
 import org.apache.accumulo.core.data.Value;
 import org.apache.accumulo.core.security.ColumnVisibility;
-import org.apache.hadoop.io.Text;
 
+/**
+ * @deprecated Use {@link DefaultFormatter} providing showLength and 
printTimestamps via {@link FormatterConfig}.
+ */
+@Deprecated
 public class BinaryFormatter extends DefaultFormatter {
-  private static int showLength;
-
+  // this class can probably be replaced by DefaultFormatter since 
DefaultFormatter has the max length stuff
   @Override
   public String next() {
     checkState(true);
-    return formatEntry(getScannerIterator().next(), isDoTimestamps());
+    return formatEntry(getScannerIterator().next(), 
config.willPrintTimestamps(), config.getShownLength());
   }
 
-  // this should be replaced with something like Record.toString();
-  // it would be great if we were able to combine code with 
DefaultFormatter.formatEntry, but that currently does not respect the 
showLength option.
-  public static String formatEntry(Entry<Key,Value> entry, boolean 
showTimestamps) {
+  public static String formatEntry(Entry<Key,Value> entry, boolean 
printTimestamps, int shownLength) {
     StringBuilder sb = new StringBuilder();
 
     Key key = entry.getKey();
 
     // append row
-    appendText(sb, key.getRow()).append(" ");
+    appendText(sb, key.getRow(), shownLength).append(" ");
 
     // append column family
-    appendText(sb, key.getColumnFamily()).append(":");
+    appendText(sb, key.getColumnFamily(), shownLength).append(":");
 
     // append column qualifier
-    appendText(sb, key.getColumnQualifier()).append(" ");
+    appendText(sb, key.getColumnQualifier(), shownLength).append(" ");
 
     // append visibility expression
     sb.append(new ColumnVisibility(key.getColumnVisibility()));
 
     // append timestamp
-    if (showTimestamps)
+    if (printTimestamps)
       sb.append(" ").append(entry.getKey().getTimestamp());
 
     // append value
     Value value = entry.getValue();
     if (value != null && value.getSize() > 0) {
       sb.append("\t");
-      appendValue(sb, value);
+      appendValue(sb, value, shownLength);
     }
     return sb.toString();
   }
 
-  public static StringBuilder appendText(StringBuilder sb, Text t) {
-    return appendBytes(sb, t.getBytes(), 0, t.getLength());
-  }
-
-  static StringBuilder appendValue(StringBuilder sb, Value value) {
-    return appendBytes(sb, value.get(), 0, value.get().length);
-  }
-
-  static StringBuilder appendBytes(StringBuilder sb, byte ba[], int offset, 
int len) {
-    int length = Math.min(len, showLength);
-    return DefaultFormatter.appendBytes(sb, ba, offset, length);
-  }
-
-  public static void getlength(int length) {
-    showLength = length;
-  }
 }

http://git-wip-us.apache.org/repos/asf/accumulo/blob/83f346b2/core/src/main/java/org/apache/accumulo/core/util/format/DateFormatSupplier.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/accumulo/core/util/format/DateFormatSupplier.java
 
b/core/src/main/java/org/apache/accumulo/core/util/format/DateFormatSupplier.java
new file mode 100644
index 0000000..9cf50e0
--- /dev/null
+++ 
b/core/src/main/java/org/apache/accumulo/core/util/format/DateFormatSupplier.java
@@ -0,0 +1,95 @@
+/*
+ * 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.accumulo.core.util.format;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.TimeZone;
+
+import com.google.common.base.Supplier;
+
+/**
+ * DateFormatSupplier is a {@code ThreadLocal<DateFormat>} that will set the 
correct TimeZone when the object is retrieved by {@link #get()}.
+ *
+ * This exists as a way to get around thread safety issues in {@link 
DateFormat}. This class also contains helper methods that create some useful
+ * DateFormatSuppliers.
+ *
+ * Instances of DateFormatSuppliers can be shared, but note that a DateFormat 
generated from it will be shared by all classes within a Thread.
+ *
+ * In general, the state of a retrieved DateFormat should not be changed, 
unless it makes sense to only perform a state change within that Thread.
+ */
+public abstract class DateFormatSupplier extends ThreadLocal<DateFormat> 
implements Supplier<DateFormat> {
+  private TimeZone timeZone;
+
+  public DateFormatSupplier() {
+    timeZone = TimeZone.getDefault();
+  }
+
+  public DateFormatSupplier(TimeZone timeZone) {
+    this.timeZone = timeZone;
+  }
+
+  public TimeZone getTimeZone() {
+    return timeZone;
+  }
+
+  public void setTimeZone(TimeZone timeZone) {
+    this.timeZone = timeZone;
+  }
+
+  /** Always sets the TimeZone, which is a fast operation */
+  @Override
+  public DateFormat get() {
+    final DateFormat df = super.get();
+    df.setTimeZone(timeZone);
+    return df;
+  }
+
+  public static final String HUMAN_READABLE_FORMAT = "yyyy/MM/dd HH:mm:ss.SSS";
+
+  /**
+   * Create a Supplier for {@link FormatterConfig.DefaultDateFormat}s
+   */
+  public static DateFormatSupplier createDefaultFormatSupplier() {
+    return new DateFormatSupplier() {
+      @Override
+      protected DateFormat initialValue() {
+        return new FormatterConfig.DefaultDateFormat();
+      }
+    };
+  }
+
+  /** Create a generator for SimpleDateFormats accepting a dateFormat */
+  public static DateFormatSupplier createSimpleFormatSupplier(final String 
dateFormat) {
+    return new DateFormatSupplier() {
+      @Override
+      protected SimpleDateFormat initialValue() {
+        return new SimpleDateFormat(dateFormat);
+      }
+    };
+  }
+
+  /** Create a generator for SimpleDateFormats accepting a dateFormat */
+  public static DateFormatSupplier createSimpleFormatSupplier(final String 
dateFormat, final TimeZone timeZone) {
+    return new DateFormatSupplier(timeZone) {
+      @Override
+      protected SimpleDateFormat initialValue() {
+        return new SimpleDateFormat(dateFormat);
+      }
+    };
+  }
+}

http://git-wip-us.apache.org/repos/asf/accumulo/blob/83f346b2/core/src/main/java/org/apache/accumulo/core/util/format/DateStringFormatter.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/accumulo/core/util/format/DateStringFormatter.java
 
b/core/src/main/java/org/apache/accumulo/core/util/format/DateStringFormatter.java
index 5bcd4a3..63bd536 100644
--- 
a/core/src/main/java/org/apache/accumulo/core/util/format/DateStringFormatter.java
+++ 
b/core/src/main/java/org/apache/accumulo/core/util/format/DateStringFormatter.java
@@ -16,31 +16,45 @@
  */
 package org.apache.accumulo.core.util.format;
 
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
 import java.util.Map.Entry;
 import java.util.TimeZone;
-
 import org.apache.accumulo.core.data.Key;
 import org.apache.accumulo.core.data.Value;
 
+/**
+ * This class is <strong>not</strong> recommended because {@link 
#initialize(Iterable, FormatterConfig)} replaces parameters in {@link 
FormatterConfig}, which
+ * could surprise users.
+ *
+ * This class can be replaced by {@link DefaultFormatter} where 
FormatterConfig is initialized with a DateFormat set to {@link #DATE_FORMAT}. 
See
+ * {@link DateFormatSupplier#createSimpleFormatSupplier(String, 
java.util.TimeZone)}.
+ *
+ * <pre>
+ * final DateFormatSupplier dfSupplier = 
DateFormatSupplier.createSimpleFormatSupplier(DateFormatSupplier.HUMAN_READABLE_FORMAT,
 TimeZone.getTimeZone(&quot;UTC&quot;));
+ * final FormatterConfig config = new 
FormatterConfig().setPrintTimestamps(true).setDateFormatSupplier(dfSupplier);
+ * </pre>
+ */
+@Deprecated
 public class DateStringFormatter implements Formatter {
-  private boolean printTimestamps = false;
-  private DefaultFormatter defaultFormatter = new DefaultFormatter();
 
-  public static final String DATE_FORMAT = "yyyy/MM/dd HH:mm:ss.SSS";
-  // SimpleDataFormat is not thread safe
-  private static final ThreadLocal<DateFormat> formatter = new 
ThreadLocal<DateFormat>() {
-    @Override
-    protected SimpleDateFormat initialValue() {
-      return new SimpleDateFormat(DATE_FORMAT);
-    }
-  };
+  private DefaultFormatter defaultFormatter;
+  private TimeZone timeZone;
+
+  public static final String DATE_FORMAT = 
DateFormatSupplier.HUMAN_READABLE_FORMAT;
+
+  public DateStringFormatter() {
+    this(TimeZone.getDefault());
+  }
+
+  public DateStringFormatter(TimeZone timeZone) {
+    this.defaultFormatter = new DefaultFormatter();
+    this.timeZone = timeZone;
+  }
 
   @Override
-  public void initialize(Iterable<Entry<Key,Value>> scanner, boolean 
printTimestamps) {
-    this.printTimestamps = printTimestamps;
-    defaultFormatter.initialize(scanner, printTimestamps);
+  public void initialize(Iterable<Entry<Key,Value>> scanner, FormatterConfig 
config) {
+    FormatterConfig newConfig = new FormatterConfig(config);
+    
newConfig.setDateFormatSupplier(DateFormatSupplier.createSimpleFormatSupplier(DATE_FORMAT,
 timeZone));
+    defaultFormatter.initialize(scanner, newConfig);
   }
 
   @Override
@@ -50,13 +64,7 @@ public class DateStringFormatter implements Formatter {
 
   @Override
   public String next() {
-    DateFormat timestampformat = null;
-
-    if (printTimestamps) {
-      timestampformat = formatter.get();
-    }
-
-    return defaultFormatter.next(timestampformat);
+    return defaultFormatter.next();
   }
 
   @Override
@@ -64,7 +72,4 @@ public class DateStringFormatter implements Formatter {
     defaultFormatter.remove();
   }
 
-  public void setTimeZone(TimeZone zone) {
-    formatter.get().setTimeZone(zone);
-  }
 }

http://git-wip-us.apache.org/repos/asf/accumulo/blob/83f346b2/core/src/main/java/org/apache/accumulo/core/util/format/DefaultFormatter.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/accumulo/core/util/format/DefaultFormatter.java 
b/core/src/main/java/org/apache/accumulo/core/util/format/DefaultFormatter.java
index 5a2f43f..5517b78 100644
--- 
a/core/src/main/java/org/apache/accumulo/core/util/format/DefaultFormatter.java
+++ 
b/core/src/main/java/org/apache/accumulo/core/util/format/DefaultFormatter.java
@@ -17,12 +17,9 @@
 package org.apache.accumulo.core.util.format;
 
 import java.text.DateFormat;
-import java.text.FieldPosition;
-import java.text.ParsePosition;
 import java.util.Date;
 import java.util.Iterator;
 import java.util.Map.Entry;
-
 import org.apache.accumulo.core.data.Key;
 import org.apache.accumulo.core.data.Value;
 import org.apache.accumulo.core.security.ColumnVisibility;
@@ -30,35 +27,16 @@ import org.apache.hadoop.io.Text;
 
 public class DefaultFormatter implements Formatter {
   private Iterator<Entry<Key,Value>> si;
-  private boolean doTimestamps;
-
-  public static class DefaultDateFormat extends DateFormat {
-    private static final long serialVersionUID = 1L;
-
-    @Override
-    public StringBuffer format(Date date, StringBuffer toAppendTo, 
FieldPosition fieldPosition) {
-      toAppendTo.append(Long.toString(date.getTime()));
-      return toAppendTo;
-    }
-
-    @Override
-    public Date parse(String source, ParsePosition pos) {
-      return new Date(Long.parseLong(source));
-    }
-  }
+  protected FormatterConfig config;
 
-  private static final ThreadLocal<DateFormat> formatter = new 
ThreadLocal<DateFormat>() {
-    @Override
-    protected DateFormat initialValue() {
-      return new DefaultDateFormat();
-    }
-  };
+  /** Used as default DateFormat for some static methods */
+  private static final ThreadLocal<DateFormat> formatter = 
DateFormatSupplier.createDefaultFormatSupplier();
 
   @Override
-  public void initialize(Iterable<Entry<Key,Value>> scanner, boolean 
printTimestamps) {
+  public void initialize(Iterable<Entry<Key,Value>> scanner, FormatterConfig 
config) {
     checkState(false);
     si = scanner.iterator();
-    doTimestamps = printTimestamps;
+    this.config = new FormatterConfig(config);
   }
 
   @Override
@@ -69,18 +47,8 @@ public class DefaultFormatter implements Formatter {
 
   @Override
   public String next() {
-    DateFormat timestampFormat = null;
-
-    if (doTimestamps) {
-      timestampFormat = formatter.get();
-    }
-
-    return next(timestampFormat);
-  }
-
-  protected String next(DateFormat timestampFormat) {
     checkState(true);
-    return formatEntry(si.next(), timestampFormat);
+    return formatEntry(si.next());
   }
 
   @Override
@@ -96,7 +64,10 @@ public class DefaultFormatter implements Formatter {
       throw new IllegalStateException("Already initialized");
   }
 
-  // this should be replaced with something like Record.toString();
+  /**
+   * if showTimestamps, will use {@link 
org.apache.accumulo.core.util.format.FormatterConfig.DefaultDateFormat}. 
Preferably, use
+   * {@link #formatEntry(Entry, FormatterConfig)}
+   */
   public static String formatEntry(Entry<Key,Value> entry, boolean 
showTimestamps) {
     DateFormat timestampFormat = null;
 
@@ -115,6 +86,7 @@ public class DefaultFormatter implements Formatter {
     }
   };
 
+  /** Does not show timestamps if timestampFormat is null */
   public static String formatEntry(Entry<Key,Value> entry, DateFormat 
timestampFormat) {
     StringBuilder sb = new StringBuilder();
     Key key = entry.getKey();
@@ -149,14 +121,55 @@ public class DefaultFormatter implements Formatter {
     return sb.toString();
   }
 
+  public String formatEntry(Entry<Key,Value> entry) {
+    return formatEntry(entry, this.config);
+  }
+
+  public static String formatEntry(Entry<Key,Value> entry, FormatterConfig 
config) {
+    // originally from BinaryFormatter
+    StringBuilder sb = new StringBuilder();
+    Key key = entry.getKey();
+    Text buffer = new Text();
+
+    final int shownLength = config.getShownLength();
+
+    appendText(sb, key.getRow(buffer), shownLength).append(" ");
+    appendText(sb, key.getColumnFamily(buffer), shownLength).append(":");
+    appendText(sb, key.getColumnQualifier(buffer), shownLength).append(" ");
+    sb.append(new ColumnVisibility(key.getColumnVisibility(buffer)));
+
+    // append timestamp
+    if (config.willPrintTimestamps() && config.getDateFormatSupplier() != 
null) {
+      tmpDate.get().setTime(entry.getKey().getTimestamp());
+      sb.append(" 
").append(config.getDateFormatSupplier().get().format(tmpDate.get()));
+    }
+
+    // append value
+    Value value = entry.getValue();
+    if (value != null && value.getSize() > 0) {
+      sb.append("\t");
+      appendValue(sb, value, shownLength);
+    }
+    return sb.toString();
+
+  }
+
   static StringBuilder appendText(StringBuilder sb, Text t) {
     return appendBytes(sb, t.getBytes(), 0, t.getLength());
   }
 
+  public static StringBuilder appendText(StringBuilder sb, Text t, int 
shownLength) {
+    return appendBytes(sb, t.getBytes(), 0, t.getLength(), shownLength);
+  }
+
   static StringBuilder appendValue(StringBuilder sb, Value value) {
     return appendBytes(sb, value.get(), 0, value.get().length);
   }
 
+  static StringBuilder appendValue(StringBuilder sb, Value value, int 
shownLength) {
+    return appendBytes(sb, value.get(), 0, value.get().length, shownLength);
+  }
+
   static StringBuilder appendBytes(StringBuilder sb, byte ba[], int offset, 
int len) {
     for (int i = 0; i < len; i++) {
       int c = 0xff & ba[offset + i];
@@ -170,11 +183,17 @@ public class DefaultFormatter implements Formatter {
     return sb;
   }
 
+  static StringBuilder appendBytes(StringBuilder sb, byte ba[], int offset, 
int len, int shownLength) {
+    int length = Math.min(len, shownLength);
+    return DefaultFormatter.appendBytes(sb, ba, offset, length);
+  }
+
   public Iterator<Entry<Key,Value>> getScannerIterator() {
     return si;
   }
 
   protected boolean isDoTimestamps() {
-    return doTimestamps;
+    return config.willPrintTimestamps();
   }
+
 }

http://git-wip-us.apache.org/repos/asf/accumulo/blob/83f346b2/core/src/main/java/org/apache/accumulo/core/util/format/Formatter.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/accumulo/core/util/format/Formatter.java 
b/core/src/main/java/org/apache/accumulo/core/util/format/Formatter.java
index 497cc74..136b252 100644
--- a/core/src/main/java/org/apache/accumulo/core/util/format/Formatter.java
+++ b/core/src/main/java/org/apache/accumulo/core/util/format/Formatter.java
@@ -23,5 +23,5 @@ import org.apache.accumulo.core.data.Key;
 import org.apache.accumulo.core.data.Value;
 
 public interface Formatter extends Iterator<String> {
-  void initialize(Iterable<Entry<Key,Value>> scanner, boolean printTimestamps);
+  void initialize(Iterable<Entry<Key,Value>> scanner, FormatterConfig config);
 }

http://git-wip-us.apache.org/repos/asf/accumulo/blob/83f346b2/core/src/main/java/org/apache/accumulo/core/util/format/FormatterConfig.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/accumulo/core/util/format/FormatterConfig.java 
b/core/src/main/java/org/apache/accumulo/core/util/format/FormatterConfig.java
new file mode 100644
index 0000000..c70b1e4
--- /dev/null
+++ 
b/core/src/main/java/org/apache/accumulo/core/util/format/FormatterConfig.java
@@ -0,0 +1,115 @@
+/*
+ * 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.accumulo.core.util.format;
+
+import java.text.DateFormat;
+import java.text.FieldPosition;
+import java.text.ParsePosition;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Supplier;
+
+/**
+ * Holds configuration settings for a {@link Formatter}
+ */
+public class FormatterConfig {
+
+  private boolean printTimestamps;
+  private int shownLength;
+  private Supplier<DateFormat> dateFormatSupplier;
+
+  /** Formats with milliseconds since epoch */
+  public static class DefaultDateFormat extends SimpleDateFormat {
+    private static final long serialVersionUID = 1L;
+
+    @Override
+    public StringBuffer format(Date date, StringBuffer toAppendTo, 
FieldPosition fieldPosition) {
+      toAppendTo.append(Long.toString(date.getTime()));
+      return toAppendTo;
+    }
+
+    @Override
+    public Date parse(String source, ParsePosition pos) {
+      return new Date(Long.parseLong(source));
+    }
+  }
+
+  public FormatterConfig() {
+    this.setPrintTimestamps(false);
+    this.doNotLimitShowLength();
+    this.dateFormatSupplier = DateFormatSupplier.createDefaultFormatSupplier();
+  }
+
+  /**
+   * Copies most fields, but still points to other.dateFormatSupplier.
+   */
+  public FormatterConfig(FormatterConfig other) {
+    this.printTimestamps = other.printTimestamps;
+    this.shownLength = other.shownLength;
+    this.dateFormatSupplier = other.dateFormatSupplier;
+  }
+
+  public boolean willPrintTimestamps() {
+    return printTimestamps;
+  }
+
+  public FormatterConfig setPrintTimestamps(boolean printTimestamps) {
+    this.printTimestamps = printTimestamps;
+    return this;
+  }
+
+  public int getShownLength() {
+    return shownLength;
+  }
+
+  public boolean willLimitShowLength() {
+    return this.shownLength != Integer.MAX_VALUE;
+  }
+
+  /**
+   * If given a negative number, throws an {@link IllegalArgumentException}
+   *
+   * @param shownLength
+   *          maximum length of formatted output
+   * @return {@code this} to allow chaining of set methods
+   */
+  public FormatterConfig setShownLength(int shownLength) {
+    Preconditions.checkArgument(shownLength >= 0, "Shown length cannot be 
negative");
+    this.shownLength = shownLength;
+    return this;
+  }
+
+  public FormatterConfig doNotLimitShowLength() {
+    this.shownLength = Integer.MAX_VALUE;
+    return this;
+  }
+
+  public Supplier<DateFormat> getDateFormatSupplier() {
+    return dateFormatSupplier;
+  }
+
+  /**
+   * this.dateFormatSupplier points to dateFormatSupplier, so it is 
recommended that you create a new {@code Supplier} when calling this function 
if your
+   * {@code Supplier} maintains some kind of state (see {@link 
DateFormatSupplier}.
+   */
+  public FormatterConfig setDateFormatSupplier(Supplier<DateFormat> 
dateFormatSupplier) {
+    this.dateFormatSupplier = dateFormatSupplier;
+    return this;
+  }
+}

http://git-wip-us.apache.org/repos/asf/accumulo/blob/83f346b2/core/src/main/java/org/apache/accumulo/core/util/format/FormatterFactory.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/accumulo/core/util/format/FormatterFactory.java 
b/core/src/main/java/org/apache/accumulo/core/util/format/FormatterFactory.java
index 7eb542f..9ae1a6c 100644
--- 
a/core/src/main/java/org/apache/accumulo/core/util/format/FormatterFactory.java
+++ 
b/core/src/main/java/org/apache/accumulo/core/util/format/FormatterFactory.java
@@ -26,7 +26,7 @@ import org.slf4j.LoggerFactory;
 public class FormatterFactory {
   private static final Logger log = 
LoggerFactory.getLogger(FormatterFactory.class);
 
-  public static Formatter getFormatter(Class<? extends Formatter> 
formatterClass, Iterable<Entry<Key,Value>> scanner, boolean printTimestamps) {
+  public static Formatter getFormatter(Class<? extends Formatter> 
formatterClass, Iterable<Entry<Key,Value>> scanner, FormatterConfig config) {
     Formatter formatter = null;
     try {
       formatter = formatterClass.newInstance();
@@ -34,12 +34,12 @@ public class FormatterFactory {
       log.warn("Unable to instantiate formatter. Using default formatter.", e);
       formatter = new DefaultFormatter();
     }
-    formatter.initialize(scanner, printTimestamps);
+    formatter.initialize(scanner, config);
     return formatter;
   }
 
-  public static Formatter getDefaultFormatter(Iterable<Entry<Key,Value>> 
scanner, boolean printTimestamps) {
-    return getFormatter(DefaultFormatter.class, scanner, printTimestamps);
+  public static Formatter getDefaultFormatter(Iterable<Entry<Key,Value>> 
scanner, FormatterConfig config) {
+    return getFormatter(DefaultFormatter.class, scanner, config);
   }
 
   private FormatterFactory() {

http://git-wip-us.apache.org/repos/asf/accumulo/blob/83f346b2/core/src/main/java/org/apache/accumulo/core/util/format/HexFormatter.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/accumulo/core/util/format/HexFormatter.java 
b/core/src/main/java/org/apache/accumulo/core/util/format/HexFormatter.java
index 65e52d3..54e2598 100644
--- a/core/src/main/java/org/apache/accumulo/core/util/format/HexFormatter.java
+++ b/core/src/main/java/org/apache/accumulo/core/util/format/HexFormatter.java
@@ -31,7 +31,7 @@ public class HexFormatter implements Formatter, 
ScanInterpreter {
 
   private char chars[] = new char[] {'0', '1', '2', '3', '4', '5', '6', '7', 
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
   private Iterator<Entry<Key,Value>> iter;
-  private boolean printTimestamps;
+  private FormatterConfig config;
 
   private void toHex(StringBuilder sb, byte[] bin) {
 
@@ -88,7 +88,7 @@ public class HexFormatter implements Formatter, 
ScanInterpreter {
     sb.append(" [");
     sb.append(entry.getKey().getColumnVisibilityData().toString());
     sb.append("] ");
-    if (printTimestamps) {
+    if (config.willPrintTimestamps()) {
       sb.append(Long.toString(entry.getKey().getTimestamp()));
       sb.append("  ");
     }
@@ -103,9 +103,9 @@ public class HexFormatter implements Formatter, 
ScanInterpreter {
   }
 
   @Override
-  public void initialize(Iterable<Entry<Key,Value>> scanner, boolean 
printTimestamps) {
+  public void initialize(Iterable<Entry<Key,Value>> scanner, FormatterConfig 
config) {
     this.iter = scanner.iterator();
-    this.printTimestamps = printTimestamps;
+    this.config = new FormatterConfig(config);
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/accumulo/blob/83f346b2/core/src/test/java/org/apache/accumulo/core/util/format/DateFormatSupplierTest.java
----------------------------------------------------------------------
diff --git 
a/core/src/test/java/org/apache/accumulo/core/util/format/DateFormatSupplierTest.java
 
b/core/src/test/java/org/apache/accumulo/core/util/format/DateFormatSupplierTest.java
new file mode 100644
index 0000000..b095b04
--- /dev/null
+++ 
b/core/src/test/java/org/apache/accumulo/core/util/format/DateFormatSupplierTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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.accumulo.core.util.format;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import java.text.DateFormat;
+import java.util.Date;
+import java.util.TimeZone;
+import org.junit.Test;
+
+public class DateFormatSupplierTest {
+
+  /** Asserts two supplier instance create independent objects */
+  private void assertSuppliersIndependent(ThreadLocal<DateFormat> supplierA, 
ThreadLocal<DateFormat> supplierB) {
+    DateFormat getA1 = supplierA.get();
+    DateFormat getA2 = supplierA.get();
+    assertSame(getA1, getA2);
+
+    DateFormat getB1 = supplierB.get();
+    DateFormat getB2 = supplierB.get();
+
+    assertSame(getB1, getB2);
+    assertNotSame(getA1, getB1);
+  }
+
+  @Test
+  public void testCreateDefaultFormatSupplier() throws Exception {
+    ThreadLocal<DateFormat> supplierA = 
DateFormatSupplier.createDefaultFormatSupplier();
+    ThreadLocal<DateFormat> supplierB = 
DateFormatSupplier.createDefaultFormatSupplier();
+    assertSuppliersIndependent(supplierA, supplierB);
+  }
+
+  @Test
+  public void testCreateSimpleFormatSupplier() throws Exception {
+    final String format = DateFormatSupplier.HUMAN_READABLE_FORMAT;
+    DateFormatSupplier supplierA = 
DateFormatSupplier.createSimpleFormatSupplier(format);
+    DateFormatSupplier supplierB = 
DateFormatSupplier.createSimpleFormatSupplier(format);
+    assertSuppliersIndependent(supplierA, supplierB);
+
+    // since dfA and dfB come from different suppliers, altering the TimeZone 
on one does not affect the other
+    supplierA.setTimeZone(TimeZone.getTimeZone("UTC"));
+    final DateFormat dfA = supplierA.get();
+
+    supplierB.setTimeZone(TimeZone.getTimeZone("EST"));
+    final DateFormat dfB = supplierB.get();
+
+    final String resultA = dfA.format(new Date(0));
+    assertEquals("1970/01/01 00:00:00.000", resultA);
+
+    final String resultB = dfB.format(new Date(0));
+    assertEquals("1969/12/31 19:00:00.000", resultB);
+
+    assertTrue(!resultA.equals(resultB));
+
+  }
+}

http://git-wip-us.apache.org/repos/asf/accumulo/blob/83f346b2/core/src/test/java/org/apache/accumulo/core/util/format/DateStringFormatterTest.java
----------------------------------------------------------------------
diff --git 
a/core/src/test/java/org/apache/accumulo/core/util/format/DateStringFormatterTest.java
 
b/core/src/test/java/org/apache/accumulo/core/util/format/DateStringFormatterTest.java
index 1b121f3..505c198 100644
--- 
a/core/src/test/java/org/apache/accumulo/core/util/format/DateStringFormatterTest.java
+++ 
b/core/src/test/java/org/apache/accumulo/core/util/format/DateStringFormatterTest.java
@@ -22,12 +22,12 @@ import static org.junit.Assert.assertTrue;
 import java.util.Map;
 import java.util.TimeZone;
 import java.util.TreeMap;
-
 import org.apache.accumulo.core.data.Key;
 import org.apache.accumulo.core.data.Value;
 import org.junit.Before;
 import org.junit.Test;
 
+@SuppressWarnings("deprecation")
 public class DateStringFormatterTest {
   DateStringFormatter formatter;
 
@@ -40,13 +40,30 @@ public class DateStringFormatterTest {
     data.put(new Key("", "", "", 0), new Value());
   }
 
+  private void testFormatterIgnoresConfig(FormatterConfig config, 
DateStringFormatter formatter) {
+    // ignores config's DateFormatSupplier and substitutes its own
+    formatter.initialize(data.entrySet(), config);
+
+    assertTrue(formatter.hasNext());
+    final String next = formatter.next();
+    assertTrue(next, next.endsWith("1970/01/01 00:00:00.000"));
+  }
+
   @Test
   public void testTimestamps() {
-    formatter.initialize(data.entrySet(), true);
-    formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
+    final TimeZone utc = TimeZone.getTimeZone("UTC");
+    final TimeZone est = TimeZone.getTimeZone("EST");
+    final FormatterConfig config = new 
FormatterConfig().setPrintTimestamps(true);
+    DateStringFormatter formatter;
 
-    assertTrue(formatter.hasNext());
-    assertTrue(formatter.next().endsWith("1970/01/01 00:00:00.000"));
+    formatter = new DateStringFormatter(utc);
+    testFormatterIgnoresConfig(config, formatter);
+
+    // even though config says to use EST and only print year, the Formatter 
will override these
+    formatter = new DateStringFormatter(utc);
+    DateFormatSupplier dfSupplier = 
DateFormatSupplier.createSimpleFormatSupplier("YYYY", est);
+    config.setDateFormatSupplier(dfSupplier);
+    testFormatterIgnoresConfig(config, formatter);
   }
 
   @Test
@@ -55,7 +72,7 @@ public class DateStringFormatterTest {
 
     assertEquals(2, data.size());
 
-    formatter.initialize(data.entrySet(), false);
+    formatter.initialize(data.entrySet(), new FormatterConfig());
 
     assertEquals(formatter.next(), formatter.next());
   }

http://git-wip-us.apache.org/repos/asf/accumulo/blob/83f346b2/core/src/test/java/org/apache/accumulo/core/util/format/DefaultFormatterTest.java
----------------------------------------------------------------------
diff --git 
a/core/src/test/java/org/apache/accumulo/core/util/format/DefaultFormatterTest.java
 
b/core/src/test/java/org/apache/accumulo/core/util/format/DefaultFormatterTest.java
index 7b654d0..eed455c 100644
--- 
a/core/src/test/java/org/apache/accumulo/core/util/format/DefaultFormatterTest.java
+++ 
b/core/src/test/java/org/apache/accumulo/core/util/format/DefaultFormatterTest.java
@@ -19,8 +19,10 @@ package org.apache.accumulo.core.util.format;
 import static org.junit.Assert.assertEquals;
 
 import java.util.Collections;
+import java.util.Map;
 import java.util.Map.Entry;
-
+import java.util.TimeZone;
+import java.util.TreeMap;
 import org.apache.accumulo.core.data.Key;
 import org.apache.accumulo.core.data.Value;
 import org.apache.hadoop.io.Text;
@@ -29,6 +31,8 @@ import org.junit.Test;
 
 public class DefaultFormatterTest {
 
+  public static final TimeZone UTC = TimeZone.getTimeZone("UTC");
+  public static final TimeZone EST = TimeZone.getTimeZone("EST");
   DefaultFormatter df;
   Iterable<Entry<Key,Value>> empty = Collections.<Key,Value> 
emptyMap().entrySet();
 
@@ -39,8 +43,9 @@ public class DefaultFormatterTest {
 
   @Test(expected = IllegalStateException.class)
   public void testDoubleInitialize() {
-    df.initialize(empty, true);
-    df.initialize(empty, true);
+    final FormatterConfig timestampConfig = new 
FormatterConfig().setPrintTimestamps(true);
+    df.initialize(empty, timestampConfig);
+    df.initialize(empty, timestampConfig);
   }
 
   @Test(expected = IllegalStateException.class)
@@ -59,4 +64,55 @@ public class DefaultFormatterTest {
     DefaultFormatter.appendText(sb, new Text(data));
     assertEquals("\\x00\\\\x\\xFF", sb.toString());
   }
+
+  @Test
+  public void testFormatEntry() {
+    final long timestamp = 0;
+    Map<Key,Value> map = new TreeMap<Key,Value>();
+    map.put(new Key("a", "ab", "abc", timestamp), new 
Value("abcd".getBytes()));
+
+    FormatterConfig config;
+    String answer;
+
+    // no timestamp, no max
+    config = new FormatterConfig();
+    df = new DefaultFormatter();
+    df.initialize(map.entrySet(), config);
+    answer = df.next();
+    assertEquals("a ab:abc []\tabcd", answer);
+
+    // yes timestamp, no max
+    config.setPrintTimestamps(true);
+    df = new DefaultFormatter();
+    df.initialize(map.entrySet(), config);
+    answer = df.next();
+    assertEquals("a ab:abc [] " + timestamp + "\tabcd", answer);
+
+    // yes timestamp, max of 1
+    config.setPrintTimestamps(true).setShownLength(1);
+    df = new DefaultFormatter();
+    df.initialize(map.entrySet(), config);
+    answer = df.next();
+    assertEquals("a a:a [] " + timestamp + "\ta", answer);
+
+    // yes timestamp, no max, new DateFormat
+    
config.setPrintTimestamps(true).doNotLimitShowLength().setDateFormatSupplier(DateFormatSupplier.createSimpleFormatSupplier("YYYY"));
+    df = new DefaultFormatter();
+    df.initialize(map.entrySet(), config);
+    answer = df.next();
+    assertEquals("a ab:abc [] 1970\tabcd", answer);
+
+    // yes timestamp, no max, new DateFormat, different TimeZone
+    
config.setPrintTimestamps(true).doNotLimitShowLength().setDateFormatSupplier(DateFormatSupplier.createSimpleFormatSupplier("HH",
 UTC));
+    df = new DefaultFormatter();
+    df.initialize(map.entrySet(), config);
+    answer = df.next();
+    assertEquals("a ab:abc [] 00\tabcd", answer);
+
+    
config.setPrintTimestamps(true).doNotLimitShowLength().setDateFormatSupplier(DateFormatSupplier.createSimpleFormatSupplier("HH",
 EST));
+    df = new DefaultFormatter();
+    df.initialize(map.entrySet(), config);
+    answer = df.next();
+    assertEquals("a ab:abc [] 19\tabcd", answer);
+  }
 }

http://git-wip-us.apache.org/repos/asf/accumulo/blob/83f346b2/core/src/test/java/org/apache/accumulo/core/util/format/FormatterConfigTest.java
----------------------------------------------------------------------
diff --git 
a/core/src/test/java/org/apache/accumulo/core/util/format/FormatterConfigTest.java
 
b/core/src/test/java/org/apache/accumulo/core/util/format/FormatterConfigTest.java
new file mode 100644
index 0000000..aa88e03
--- /dev/null
+++ 
b/core/src/test/java/org/apache/accumulo/core/util/format/FormatterConfigTest.java
@@ -0,0 +1,81 @@
+/*
+ * 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.accumulo.core.util.format;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.fail;
+
+import java.text.DateFormat;
+import org.junit.Test;
+
+public class FormatterConfigTest {
+
+  @Test
+  public void testConstructor() {
+    FormatterConfig config = new FormatterConfig();
+    assertEquals(false, config.willLimitShowLength());
+    assertEquals(false, config.willPrintTimestamps());
+  }
+
+  @Test
+  public void testSetShownLength() throws Exception {
+    FormatterConfig config = new FormatterConfig();
+    try {
+      config.setShownLength(-1);
+      fail("Should throw on negative length.");
+    } catch (IllegalArgumentException e) {}
+
+    config.setShownLength(0);
+    assertEquals(0, config.getShownLength());
+    assertEquals(true, config.willLimitShowLength());
+
+    config.setShownLength(1);
+    assertEquals(1, config.getShownLength());
+    assertEquals(true, config.willLimitShowLength());
+  }
+
+  @Test
+  public void testDoNotLimitShowLength() {
+    FormatterConfig config = new FormatterConfig();
+    assertEquals(false, config.willLimitShowLength());
+
+    config.setShownLength(1);
+    assertEquals(true, config.willLimitShowLength());
+
+    config.doNotLimitShowLength();
+    assertEquals(false, config.willLimitShowLength());
+  }
+
+  @Test
+  public void testGetDateFormat() {
+    FormatterConfig config1 = new FormatterConfig();
+    DateFormat df1 = config1.getDateFormatSupplier().get();
+
+    FormatterConfig config2 = new FormatterConfig();
+    assertNotSame(df1, config2.getDateFormatSupplier().get());
+
+    config2.setDateFormatSupplier(config1.getDateFormatSupplier());
+    assertSame(df1, config2.getDateFormatSupplier().get());
+
+    // even though copying, it can't copy the Generator, so will pull out the 
same DateFormat
+    FormatterConfig configCopy = new FormatterConfig(config1);
+    assertSame(df1, configCopy.getDateFormatSupplier().get());
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/accumulo/blob/83f346b2/core/src/test/java/org/apache/accumulo/core/util/format/FormatterFactoryTest.java
----------------------------------------------------------------------
diff --git 
a/core/src/test/java/org/apache/accumulo/core/util/format/FormatterFactoryTest.java
 
b/core/src/test/java/org/apache/accumulo/core/util/format/FormatterFactoryTest.java
index d379dee..b6b91d3 100644
--- 
a/core/src/test/java/org/apache/accumulo/core/util/format/FormatterFactoryTest.java
+++ 
b/core/src/test/java/org/apache/accumulo/core/util/format/FormatterFactoryTest.java
@@ -37,8 +37,9 @@ public class FormatterFactoryTest {
 
   @Test
   public void testGetDefaultFormatter() {
-    Formatter defaultFormatter = FormatterFactory.getDefaultFormatter(scanner, 
true);
-    Formatter bogusFormatter = FormatterFactory.getFormatter(Formatter.class, 
scanner, true);
+    final FormatterConfig timestampConfig = new 
FormatterConfig().setPrintTimestamps(true);
+    Formatter defaultFormatter = FormatterFactory.getDefaultFormatter(scanner, 
timestampConfig);
+    Formatter bogusFormatter = FormatterFactory.getFormatter(Formatter.class, 
scanner, timestampConfig);
     assertEquals(defaultFormatter.getClass(), bogusFormatter.getClass());
   }
 

http://git-wip-us.apache.org/repos/asf/accumulo/blob/83f346b2/core/src/test/java/org/apache/accumulo/core/util/format/HexFormatterTest.java
----------------------------------------------------------------------
diff --git 
a/core/src/test/java/org/apache/accumulo/core/util/format/HexFormatterTest.java 
b/core/src/test/java/org/apache/accumulo/core/util/format/HexFormatterTest.java
index 4745ad3..7f20dfc 100644
--- 
a/core/src/test/java/org/apache/accumulo/core/util/format/HexFormatterTest.java
+++ 
b/core/src/test/java/org/apache/accumulo/core/util/format/HexFormatterTest.java
@@ -42,7 +42,7 @@ public class HexFormatterTest {
   @Test
   public void testInitialize() {
     data.put(new Key(), new Value());
-    formatter.initialize(data.entrySet(), false);
+    formatter.initialize(data.entrySet(), new FormatterConfig());
 
     assertTrue(formatter.hasNext());
     assertEquals("  " + "  " + " [" + "] ", formatter.next());
@@ -59,7 +59,7 @@ public class HexFormatterTest {
     Text bytes = new Text(new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 
12, 13, 14, 15});
     data.put(new Key(bytes), new Value());
 
-    formatter.initialize(data.entrySet(), false);
+    formatter.initialize(data.entrySet(), new FormatterConfig());
 
     String row = formatter.next().split(" ")[0];
     assertEquals("0001-0203-0405-0607-0809-0a0b-0c0d-0e0f", row);
@@ -80,7 +80,7 @@ public class HexFormatterTest {
   public void testTimestamps() {
     long now = System.currentTimeMillis();
     data.put(new Key("", "", "", now), new Value());
-    formatter.initialize(data.entrySet(), true);
+    formatter.initialize(data.entrySet(), new 
FormatterConfig().setPrintTimestamps(true));
     String entry = formatter.next().split("\\s+")[2];
     assertEquals(now, Long.parseLong(entry));
   }

http://git-wip-us.apache.org/repos/asf/accumulo/blob/83f346b2/core/src/test/java/org/apache/accumulo/core/util/format/ShardedTableDistributionFormatterTest.java
----------------------------------------------------------------------
diff --git 
a/core/src/test/java/org/apache/accumulo/core/util/format/ShardedTableDistributionFormatterTest.java
 
b/core/src/test/java/org/apache/accumulo/core/util/format/ShardedTableDistributionFormatterTest.java
index e8879a5..d6afe99 100644
--- 
a/core/src/test/java/org/apache/accumulo/core/util/format/ShardedTableDistributionFormatterTest.java
+++ 
b/core/src/test/java/org/apache/accumulo/core/util/format/ShardedTableDistributionFormatterTest.java
@@ -47,7 +47,7 @@ public class ShardedTableDistributionFormatterTest {
   public void testInitialize() {
     data.put(new Key(), new Value());
     data.put(new Key("r", "~tab"), new Value());
-    formatter.initialize(data.entrySet(), false);
+    formatter.initialize(data.entrySet(), new FormatterConfig());
 
     assertTrue(formatter.hasNext());
     formatter.next();
@@ -60,7 +60,7 @@ public class ShardedTableDistributionFormatterTest {
     data.put(new Key("t;19700101", "~tab", "loc", 0), new 
Value("srv1".getBytes(UTF_8)));
     data.put(new Key("t;19700101", "~tab", "loc", 1), new 
Value("srv2".getBytes(UTF_8)));
 
-    formatter.initialize(data.entrySet(), false);
+    formatter.initialize(data.entrySet(), new FormatterConfig());
 
     String[] resultLines = formatter.next().split("\n");
     List<String> results = Arrays.asList(resultLines).subList(2, 4);

http://git-wip-us.apache.org/repos/asf/accumulo/blob/83f346b2/core/src/test/java/org/apache/accumulo/core/util/format/StatisticsDisplayFormatterTest.java
----------------------------------------------------------------------
diff --git 
a/core/src/test/java/org/apache/accumulo/core/util/format/StatisticsDisplayFormatterTest.java
 
b/core/src/test/java/org/apache/accumulo/core/util/format/StatisticsDisplayFormatterTest.java
index 93c948c..69342d7 100644
--- 
a/core/src/test/java/org/apache/accumulo/core/util/format/StatisticsDisplayFormatterTest.java
+++ 
b/core/src/test/java/org/apache/accumulo/core/util/format/StatisticsDisplayFormatterTest.java
@@ -42,7 +42,7 @@ public class StatisticsDisplayFormatterTest {
   @Test
   public void testInitialize() {
     data.put(new Key(), new Value());
-    formatter.initialize(data.entrySet(), false);
+    formatter.initialize(data.entrySet(), new FormatterConfig());
 
     assertTrue(formatter.hasNext());
   }
@@ -51,7 +51,7 @@ public class StatisticsDisplayFormatterTest {
   public void testAggregate() {
     data.put(new Key("", "", "", 1), new Value());
     data.put(new Key("", "", "", 2), new Value());
-    formatter.initialize(data.entrySet(), false);
+    formatter.initialize(data.entrySet(), new FormatterConfig());
 
     String[] output = formatter.next().split("\n");
     assertTrue(output[2].endsWith(": 1"));

http://git-wip-us.apache.org/repos/asf/accumulo/blob/83f346b2/server/base/src/main/java/org/apache/accumulo/server/replication/StatusFormatter.java
----------------------------------------------------------------------
diff --git 
a/server/base/src/main/java/org/apache/accumulo/server/replication/StatusFormatter.java
 
b/server/base/src/main/java/org/apache/accumulo/server/replication/StatusFormatter.java
index a674802..6a2d66a 100644
--- 
a/server/base/src/main/java/org/apache/accumulo/server/replication/StatusFormatter.java
+++ 
b/server/base/src/main/java/org/apache/accumulo/server/replication/StatusFormatter.java
@@ -22,7 +22,6 @@ import java.util.Date;
 import java.util.Iterator;
 import java.util.Map.Entry;
 import java.util.Set;
-
 import org.apache.accumulo.core.data.Key;
 import org.apache.accumulo.core.data.Value;
 import 
org.apache.accumulo.core.metadata.schema.MetadataSchema.ReplicationSection;
@@ -32,8 +31,8 @@ import 
org.apache.accumulo.core.replication.ReplicationSchema.StatusSection;
 import org.apache.accumulo.core.replication.ReplicationSchema.WorkSection;
 import org.apache.accumulo.core.security.ColumnVisibility;
 import org.apache.accumulo.core.util.format.DefaultFormatter;
-import org.apache.accumulo.core.util.format.DefaultFormatter.DefaultDateFormat;
 import org.apache.accumulo.core.util.format.Formatter;
+import org.apache.accumulo.core.util.format.FormatterConfig;
 import org.apache.accumulo.server.replication.proto.Replication.Status;
 import org.apache.hadoop.io.Text;
 import org.slf4j.Logger;
@@ -52,7 +51,7 @@ public class StatusFormatter implements Formatter {
       WorkSection.NAME, OrderSection.NAME));
 
   private Iterator<Entry<Key,Value>> iterator;
-  private boolean printTimestamps;
+  private FormatterConfig config;
 
   /* so a new date object doesn't get created for every record in the scan 
result */
   private static ThreadLocal<Date> tmpDate = new ThreadLocal<Date>() {
@@ -62,13 +61,6 @@ public class StatusFormatter implements Formatter {
     }
   };
 
-  private static final ThreadLocal<DateFormat> formatter = new 
ThreadLocal<DateFormat>() {
-    @Override
-    protected DateFormat initialValue() {
-      return new DefaultDateFormat();
-    }
-  };
-
   @Override
   public boolean hasNext() {
     return iterator.hasNext();
@@ -77,7 +69,7 @@ public class StatusFormatter implements Formatter {
   @Override
   public String next() {
     Entry<Key,Value> entry = iterator.next();
-    DateFormat timestampFormat = printTimestamps ? formatter.get() : null;
+    DateFormat timestampFormat = config.willPrintTimestamps() ? 
config.getDateFormatSupplier().get() : null;
 
     // If we expected this to be a protobuf, try to parse it, adding a message 
when it fails to parse
     if (REPLICATION_COLFAMS.contains(entry.getKey().getColumnFamily())) {
@@ -157,9 +149,9 @@ public class StatusFormatter implements Formatter {
   }
 
   @Override
-  public void initialize(Iterable<Entry<Key,Value>> scanner, boolean 
printTimestamps) {
+  public void initialize(Iterable<Entry<Key,Value>> scanner, FormatterConfig 
config) {
     this.iterator = scanner.iterator();
-    this.printTimestamps = printTimestamps;
+    this.config = new FormatterConfig(config);
   }
 
 }

http://git-wip-us.apache.org/repos/asf/accumulo/blob/83f346b2/server/tracer/src/main/java/org/apache/accumulo/tracer/TraceFormatter.java
----------------------------------------------------------------------
diff --git 
a/server/tracer/src/main/java/org/apache/accumulo/tracer/TraceFormatter.java 
b/server/tracer/src/main/java/org/apache/accumulo/tracer/TraceFormatter.java
index 48ec8cf..775e6aa 100644
--- a/server/tracer/src/main/java/org/apache/accumulo/tracer/TraceFormatter.java
+++ b/server/tracer/src/main/java/org/apache/accumulo/tracer/TraceFormatter.java
@@ -20,11 +20,12 @@ import java.text.SimpleDateFormat;
 import java.util.Date;
 import java.util.Iterator;
 import java.util.Map.Entry;
-
 import org.apache.accumulo.core.data.Key;
 import org.apache.accumulo.core.data.Value;
+import org.apache.accumulo.core.util.format.DateFormatSupplier;
 import org.apache.accumulo.core.util.format.DefaultFormatter;
 import org.apache.accumulo.core.util.format.Formatter;
+import org.apache.accumulo.core.util.format.FormatterConfig;
 import org.apache.accumulo.tracer.thrift.Annotation;
 import org.apache.accumulo.tracer.thrift.RemoteSpan;
 import org.apache.commons.lang.NotImplementedException;
@@ -38,14 +39,9 @@ import org.apache.thrift.transport.TMemoryInputTransport;
  *
  */
 public class TraceFormatter implements Formatter {
-  public static final String DATE_FORMAT = "yyyy/MM/dd HH:mm:ss.SSS";
+  public static final String DATE_FORMAT = 
DateFormatSupplier.HUMAN_READABLE_FORMAT;
   // ugh... SimpleDataFormat is not thread safe
-  private static final ThreadLocal<SimpleDateFormat> formatter = new 
ThreadLocal<SimpleDateFormat>() {
-    @Override
-    protected SimpleDateFormat initialValue() {
-      return new SimpleDateFormat(DATE_FORMAT);
-    }
-  };
+  private static final DateFormatSupplier formatter = 
DateFormatSupplier.createSimpleFormatSupplier(DATE_FORMAT);
 
   public static String formatDate(final Date date) {
     return formatter.get().format(date);
@@ -54,7 +50,7 @@ public class TraceFormatter implements Formatter {
   private final static Text SPAN_CF = new Text("span");
 
   private Iterator<Entry<Key,Value>> scanner;
-  private boolean printTimeStamps;
+  private FormatterConfig config;
 
   public static RemoteSpan getRemoteSpan(Entry<Key,Value> entry) {
     TMemoryInputTransport transport = new 
TMemoryInputTransport(entry.getValue().get());
@@ -99,12 +95,12 @@ public class TraceFormatter implements Formatter {
         }
       }
 
-      if (printTimeStamps) {
+      if (config.willPrintTimestamps()) {
         result.append(String.format(" %-12s:%d%n", "timestamp", 
next.getKey().getTimestamp()));
       }
       return result.toString();
     }
-    return DefaultFormatter.formatEntry(next, printTimeStamps);
+    return DefaultFormatter.formatEntry(next, config.willPrintTimestamps());
   }
 
   @Override
@@ -113,8 +109,8 @@ public class TraceFormatter implements Formatter {
   }
 
   @Override
-  public void initialize(Iterable<Entry<Key,Value>> scanner, boolean 
printTimestamps) {
+  public void initialize(Iterable<Entry<Key,Value>> scanner, FormatterConfig 
config) {
     this.scanner = scanner.iterator();
-    this.printTimeStamps = printTimestamps;
+    this.config = new FormatterConfig(config);
   }
 }

http://git-wip-us.apache.org/repos/asf/accumulo/blob/83f346b2/shell/src/main/java/org/apache/accumulo/shell/Shell.java
----------------------------------------------------------------------
diff --git a/shell/src/main/java/org/apache/accumulo/shell/Shell.java 
b/shell/src/main/java/org/apache/accumulo/shell/Shell.java
index 2373437..5c95129 100644
--- a/shell/src/main/java/org/apache/accumulo/shell/Shell.java
+++ b/shell/src/main/java/org/apache/accumulo/shell/Shell.java
@@ -67,9 +67,9 @@ import 
org.apache.accumulo.core.tabletserver.thrift.ConstraintViolationException
 import org.apache.accumulo.core.trace.DistributedTrace;
 import org.apache.accumulo.core.util.BadArgumentException;
 import org.apache.accumulo.core.util.DeprecationUtil;
-import org.apache.accumulo.core.util.format.BinaryFormatter;
 import org.apache.accumulo.core.util.format.DefaultFormatter;
 import org.apache.accumulo.core.util.format.Formatter;
+import org.apache.accumulo.core.util.format.FormatterConfig;
 import org.apache.accumulo.core.util.format.FormatterFactory;
 import org.apache.accumulo.core.volume.VolumeConfiguration;
 import org.apache.accumulo.core.zookeeper.ZooUtil;
@@ -206,7 +206,6 @@ public class Shell extends ShellOptions implements 
KeywordExecutable {
   protected ConsoleReader reader;
   private AuthenticationToken token;
   private final Class<? extends Formatter> defaultFormatterClass = 
DefaultFormatter.class;
-  private final Class<? extends Formatter> binaryFormatterClass = 
BinaryFormatter.class;
   public Map<String,List<IteratorSetting>> scanIteratorOptions = new 
HashMap<String,List<IteratorSetting>>();
   public Map<String,List<IteratorSetting>> iteratorProfiles = new 
HashMap<String,List<IteratorSetting>>();
 
@@ -1102,22 +1101,14 @@ public class Shell extends ShellOptions implements 
KeywordExecutable {
     }
   }
 
-  public final void printRecords(Iterable<Entry<Key,Value>> scanner, boolean 
printTimestamps, boolean paginate, Class<? extends Formatter> formatterClass,
+  public final void printRecords(Iterable<Entry<Key,Value>> scanner, 
FormatterConfig config, boolean paginate, Class<? extends Formatter> 
formatterClass,
       PrintLine outFile) throws IOException {
-    printLines(FormatterFactory.getFormatter(formatterClass, scanner, 
printTimestamps), paginate, outFile);
+    printLines(FormatterFactory.getFormatter(formatterClass, scanner, config), 
paginate, outFile);
   }
 
-  public final void printRecords(Iterable<Entry<Key,Value>> scanner, boolean 
printTimestamps, boolean paginate, Class<? extends Formatter> formatterClass)
+  public final void printRecords(Iterable<Entry<Key,Value>> scanner, 
FormatterConfig config, boolean paginate, Class<? extends Formatter> 
formatterClass)
       throws IOException {
-    printLines(FormatterFactory.getFormatter(formatterClass, scanner, 
printTimestamps), paginate);
-  }
-
-  public final void printBinaryRecords(Iterable<Entry<Key,Value>> scanner, 
boolean printTimestamps, boolean paginate, PrintLine outFile) throws 
IOException {
-    printLines(FormatterFactory.getFormatter(binaryFormatterClass, scanner, 
printTimestamps), paginate, outFile);
-  }
-
-  public final void printBinaryRecords(Iterable<Entry<Key,Value>> scanner, 
boolean printTimestamps, boolean paginate) throws IOException {
-    printLines(FormatterFactory.getFormatter(binaryFormatterClass, scanner, 
printTimestamps), paginate);
+    printLines(FormatterFactory.getFormatter(formatterClass, scanner, config), 
paginate);
   }
 
   public static String repeat(String s, int c) {

http://git-wip-us.apache.org/repos/asf/accumulo/blob/83f346b2/shell/src/main/java/org/apache/accumulo/shell/commands/DeleteManyCommand.java
----------------------------------------------------------------------
diff --git 
a/shell/src/main/java/org/apache/accumulo/shell/commands/DeleteManyCommand.java 
b/shell/src/main/java/org/apache/accumulo/shell/commands/DeleteManyCommand.java
index 3400680..b8782f0 100644
--- 
a/shell/src/main/java/org/apache/accumulo/shell/commands/DeleteManyCommand.java
+++ 
b/shell/src/main/java/org/apache/accumulo/shell/commands/DeleteManyCommand.java
@@ -24,6 +24,7 @@ import org.apache.accumulo.core.client.IteratorSetting;
 import org.apache.accumulo.core.client.Scanner;
 import org.apache.accumulo.core.iterators.SortedKeyIterator;
 import org.apache.accumulo.core.security.Authorizations;
+import org.apache.accumulo.core.util.format.FormatterConfig;
 import org.apache.accumulo.core.util.interpret.ScanInterpreter;
 import org.apache.accumulo.shell.Shell;
 import org.apache.accumulo.shell.format.DeleterFormatter;
@@ -61,7 +62,9 @@ public class DeleteManyCommand extends ScanCommand {
     // output / delete the records
     final BatchWriter writer = shellState.getConnector()
         .createBatchWriter(tableName, new 
BatchWriterConfig().setTimeout(getTimeout(cl), TimeUnit.MILLISECONDS));
-    shellState.printLines(new DeleterFormatter(writer, scanner, 
cl.hasOption(timestampOpt.getOpt()), shellState, 
cl.hasOption(forceOpt.getOpt())), false);
+    FormatterConfig config = new FormatterConfig();
+    config.setPrintTimestamps(cl.hasOption(timestampOpt.getOpt()));
+    shellState.printLines(new DeleterFormatter(writer, scanner, config, 
shellState, cl.hasOption(forceOpt.getOpt())), false);
 
     return 0;
   }

http://git-wip-us.apache.org/repos/asf/accumulo/blob/83f346b2/shell/src/main/java/org/apache/accumulo/shell/commands/GetSplitsCommand.java
----------------------------------------------------------------------
diff --git 
a/shell/src/main/java/org/apache/accumulo/shell/commands/GetSplitsCommand.java 
b/shell/src/main/java/org/apache/accumulo/shell/commands/GetSplitsCommand.java
index 9d82269..17b7db4 100644
--- 
a/shell/src/main/java/org/apache/accumulo/shell/commands/GetSplitsCommand.java
+++ 
b/shell/src/main/java/org/apache/accumulo/shell/commands/GetSplitsCommand.java
@@ -36,7 +36,7 @@ import 
org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection;
 import org.apache.accumulo.core.security.Authorizations;
 import org.apache.accumulo.core.util.Base64;
 import org.apache.accumulo.core.util.TextUtil;
-import org.apache.accumulo.core.util.format.BinaryFormatter;
+import org.apache.accumulo.core.util.format.DefaultFormatter;
 import org.apache.accumulo.shell.Shell;
 import org.apache.accumulo.shell.Shell.Command;
 import org.apache.accumulo.shell.Shell.PrintFile;
@@ -102,8 +102,8 @@ public class GetSplitsCommand extends Command {
     if (text == null) {
       return null;
     }
-    BinaryFormatter.getlength(text.getLength());
-    return encode ? Base64.encodeBase64String(TextUtil.getBytes(text)) : 
BinaryFormatter.appendText(new StringBuilder(), text).toString();
+    final int length = text.getLength();
+    return encode ? Base64.encodeBase64String(TextUtil.getBytes(text)) : 
DefaultFormatter.appendText(new StringBuilder(), text, length).toString();
   }
 
   private static String obscuredTabletName(final KeyExtent extent) {

http://git-wip-us.apache.org/repos/asf/accumulo/blob/83f346b2/shell/src/main/java/org/apache/accumulo/shell/commands/GrepCommand.java
----------------------------------------------------------------------
diff --git 
a/shell/src/main/java/org/apache/accumulo/shell/commands/GrepCommand.java 
b/shell/src/main/java/org/apache/accumulo/shell/commands/GrepCommand.java
index 44ee93c..70c5db2 100644
--- a/shell/src/main/java/org/apache/accumulo/shell/commands/GrepCommand.java
+++ b/shell/src/main/java/org/apache/accumulo/shell/commands/GrepCommand.java
@@ -19,12 +19,12 @@ package org.apache.accumulo.shell.commands;
 import java.io.IOException;
 import java.util.Collections;
 import java.util.concurrent.TimeUnit;
-
 import org.apache.accumulo.core.client.BatchScanner;
 import org.apache.accumulo.core.client.IteratorSetting;
 import org.apache.accumulo.core.iterators.user.GrepIterator;
 import org.apache.accumulo.core.security.Authorizations;
 import org.apache.accumulo.core.util.format.Formatter;
+import org.apache.accumulo.core.util.format.FormatterConfig;
 import org.apache.accumulo.core.util.interpret.ScanInterpreter;
 import org.apache.accumulo.shell.Shell;
 import org.apache.accumulo.shell.Shell.PrintFile;
@@ -71,7 +71,9 @@ public class GrepCommand extends ScanCommand {
       fetchColumns(cl, scanner, interpeter);
 
       // output the records
-      printRecords(cl, shellState, scanner, formatter, printFile);
+      final FormatterConfig config = new FormatterConfig();
+      config.setPrintTimestamps(cl.hasOption(timestampOpt.getOpt()));
+      printRecords(cl, shellState, config, scanner, formatter, printFile);
     } finally {
       scanner.close();
     }

http://git-wip-us.apache.org/repos/asf/accumulo/blob/83f346b2/shell/src/main/java/org/apache/accumulo/shell/commands/ScanCommand.java
----------------------------------------------------------------------
diff --git 
a/shell/src/main/java/org/apache/accumulo/shell/commands/ScanCommand.java 
b/shell/src/main/java/org/apache/accumulo/shell/commands/ScanCommand.java
index c7ab1ff..85cd8e7 100644
--- a/shell/src/main/java/org/apache/accumulo/shell/commands/ScanCommand.java
+++ b/shell/src/main/java/org/apache/accumulo/shell/commands/ScanCommand.java
@@ -22,7 +22,6 @@ import java.io.UnsupportedEncodingException;
 import java.util.List;
 import java.util.Map.Entry;
 import java.util.concurrent.TimeUnit;
-
 import org.apache.accumulo.core.client.AccumuloException;
 import org.apache.accumulo.core.client.AccumuloSecurityException;
 import org.apache.accumulo.core.client.IteratorSetting;
@@ -37,8 +36,8 @@ import org.apache.accumulo.core.data.Range;
 import org.apache.accumulo.core.data.Value;
 import org.apache.accumulo.core.iterators.SortedKeyValueIterator;
 import org.apache.accumulo.core.security.Authorizations;
-import org.apache.accumulo.core.util.format.BinaryFormatter;
 import org.apache.accumulo.core.util.format.Formatter;
+import org.apache.accumulo.core.util.format.FormatterConfig;
 import org.apache.accumulo.core.util.interpret.DefaultScanInterpreter;
 import org.apache.accumulo.core.util.interpret.ScanInterpreter;
 import org.apache.accumulo.shell.Shell;
@@ -111,24 +110,21 @@ public class ScanCommand extends Command {
     setupSampling(tableName, cl, shellState, scanner);
 
     // output the records
+
+    final FormatterConfig config = new FormatterConfig();
+    config.setPrintTimestamps(cl.hasOption(timestampOpt.getOpt()));
     if (cl.hasOption(showFewOpt.getOpt())) {
       final String showLength = cl.getOptionValue(showFewOpt.getOpt());
       try {
         final int length = Integer.parseInt(showLength);
-        if (length < 1) {
-          throw new IllegalArgumentException();
-        }
-        BinaryFormatter.getlength(length);
-        printBinaryRecords(cl, shellState, scanner, printFile);
+        config.setShownLength(length);
       } catch (NumberFormatException nfe) {
         shellState.getReader().println("Arg must be an integer.");
       } catch (IllegalArgumentException iae) {
         shellState.getReader().println("Arg must be greater than one.");
       }
-
-    } else {
-      printRecords(cl, shellState, scanner, formatter, printFile);
     }
+    printRecords(cl, shellState, config, scanner, formatter, printFile);
     if (printFile != null) {
       printFile.close();
     }
@@ -190,21 +186,12 @@ public class ScanCommand extends Command {
     }
   }
 
-  protected void printRecords(final CommandLine cl, final Shell shellState, 
final Iterable<Entry<Key,Value>> scanner,
+  protected void printRecords(final CommandLine cl, final Shell shellState, 
FormatterConfig config, final Iterable<Entry<Key,Value>> scanner,
       final Class<? extends Formatter> formatter, PrintFile outFile) throws 
IOException {
     if (outFile == null) {
-      shellState.printRecords(scanner, cl.hasOption(timestampOpt.getOpt()), 
!cl.hasOption(disablePaginationOpt.getOpt()), formatter);
-    } else {
-      shellState.printRecords(scanner, cl.hasOption(timestampOpt.getOpt()), 
!cl.hasOption(disablePaginationOpt.getOpt()), formatter, outFile);
-    }
-  }
-
-  protected void printBinaryRecords(final CommandLine cl, final Shell 
shellState, final Iterable<Entry<Key,Value>> scanner, PrintFile outFile)
-      throws IOException {
-    if (outFile == null) {
-      shellState.printBinaryRecords(scanner, 
cl.hasOption(timestampOpt.getOpt()), 
!cl.hasOption(disablePaginationOpt.getOpt()));
+      shellState.printRecords(scanner, config, 
!cl.hasOption(disablePaginationOpt.getOpt()), formatter);
     } else {
-      shellState.printBinaryRecords(scanner, 
cl.hasOption(timestampOpt.getOpt()), 
!cl.hasOption(disablePaginationOpt.getOpt()), outFile);
+      shellState.printRecords(scanner, config, 
!cl.hasOption(disablePaginationOpt.getOpt()), formatter, outFile);
     }
   }
 

http://git-wip-us.apache.org/repos/asf/accumulo/blob/83f346b2/shell/src/main/java/org/apache/accumulo/shell/format/DeleterFormatter.java
----------------------------------------------------------------------
diff --git 
a/shell/src/main/java/org/apache/accumulo/shell/format/DeleterFormatter.java 
b/shell/src/main/java/org/apache/accumulo/shell/format/DeleterFormatter.java
index 1dd2234..275592e 100644
--- a/shell/src/main/java/org/apache/accumulo/shell/format/DeleterFormatter.java
+++ b/shell/src/main/java/org/apache/accumulo/shell/format/DeleterFormatter.java
@@ -18,7 +18,6 @@ package org.apache.accumulo.shell.format;
 
 import java.io.IOException;
 import java.util.Map.Entry;
-
 import org.apache.accumulo.core.client.BatchWriter;
 import org.apache.accumulo.core.client.MutationsRejectedException;
 import org.apache.accumulo.core.data.ConstraintViolationSummary;
@@ -27,7 +26,9 @@ import org.apache.accumulo.core.data.Mutation;
 import org.apache.accumulo.core.data.Value;
 import org.apache.accumulo.core.security.ColumnVisibility;
 import org.apache.accumulo.core.util.format.DefaultFormatter;
+import org.apache.accumulo.core.util.format.FormatterConfig;
 import org.apache.accumulo.shell.Shell;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -36,15 +37,13 @@ public class DeleterFormatter extends DefaultFormatter {
   private static final Logger log = 
LoggerFactory.getLogger(DeleterFormatter.class);
   private BatchWriter writer;
   private Shell shellState;
-  private boolean printTimestamps;
   private boolean force;
   private boolean more;
 
-  public DeleterFormatter(BatchWriter writer, Iterable<Entry<Key,Value>> 
scanner, boolean printTimestamps, Shell shellState, boolean force) {
-    super.initialize(scanner, printTimestamps);
+  public DeleterFormatter(BatchWriter writer, Iterable<Entry<Key,Value>> 
scanner, FormatterConfig config, Shell shellState, boolean force) {
+    super.initialize(scanner, config);
     this.writer = writer;
     this.shellState = shellState;
-    this.printTimestamps = printTimestamps;
     this.force = force;
     this.more = true;
   }
@@ -73,7 +72,7 @@ public class DeleterFormatter extends DefaultFormatter {
     Entry<Key,Value> next = getScannerIterator().next();
     Key key = next.getKey();
     Mutation m = new Mutation(key.getRow());
-    String entryStr = formatEntry(next, printTimestamps);
+    String entryStr = formatEntry(next, isDoTimestamps());
     boolean delete = force;
     try {
       if (!force) {

http://git-wip-us.apache.org/repos/asf/accumulo/blob/83f346b2/shell/src/test/java/org/apache/accumulo/shell/ShellTest.java
----------------------------------------------------------------------
diff --git a/shell/src/test/java/org/apache/accumulo/shell/ShellTest.java 
b/shell/src/test/java/org/apache/accumulo/shell/ShellTest.java
index 95fdc5a..dc902ce 100644
--- a/shell/src/test/java/org/apache/accumulo/shell/ShellTest.java
+++ b/shell/src/test/java/org/apache/accumulo/shell/ShellTest.java
@@ -31,9 +31,6 @@ import java.util.Date;
 import java.util.List;
 import java.util.TimeZone;
 
-import jline.console.ConsoleReader;
-
-import org.apache.accumulo.core.util.format.DateStringFormatter;
 import org.apache.log4j.Level;
 import org.junit.After;
 import org.junit.Before;
@@ -41,6 +38,8 @@ import org.junit.Test;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import jline.console.ConsoleReader;
+
 public class ShellTest {
   private static final Logger log = LoggerFactory.getLogger(ShellTest.class);
 
@@ -174,6 +173,8 @@ public class ShellTest {
     exec("createtable test", true);
     exec("addsplits 1 \\x80", true);
     exec("getsplits", true, "1\n\\x80");
+    exec("getsplits -m 1", true, "1");
+    exec("getsplits -b64", true, "MQ==\ngA==");
     exec("deletetable test -f", true, "Table: [test] has been deleted");
   }
 
@@ -203,6 +204,36 @@ public class ShellTest {
   }
 
   @Test
+  public void deleteManyTest() throws IOException {
+    exec("deletemany", false, "java.lang.IllegalStateException: Not in a table 
context");
+    exec("createtable test", true);
+    exec("deletemany", true, "\n");
+
+    exec("insert 0 0 0 0 -ts 0");
+    exec("insert 0 0 0 0 -l 0 -ts 0");
+    exec("insert 1 1 1 1 -ts 1");
+    exec("insert 2 2 2 2 -ts 2");
+
+    // prompts for delete, and rejects by default
+    exec("deletemany", true, "[SKIPPED] 0 0:0 []");
+    exec("deletemany -r 0", true, "[SKIPPED] 0 0:0 []");
+    exec("deletemany -r 0 -f", true, "[DELETED] 0 0:0 []");
+
+    // with auths, can delete the other record
+    exec("setauths -s 0");
+    exec("deletemany -r 0 -f", true, "[DELETED] 0 0:0 [0]");
+
+    // delete will show the timestamp
+    exec("deletemany -r 1 -f -st", true, "[DELETED] 1 1:1 [] 1");
+
+    // DeleteManyCommand has its own Formatter (DeleterFormatter), so it does 
not honor the -fm flag
+    exec("deletemany -r 2 -f -st -fm 
org.apache.accumulo.core.util.format.DateStringFormatter", true, "[DELETED] 2 
2:2 [] 2");
+
+    exec("setauths -c ", true);
+    exec("deletetable test -f", true, "Table: [test] has been deleted");
+  }
+
+  @Test
   public void authsTest() throws Exception {
     Shell.log.debug("Starting auths test --------------------------");
     exec("setauths x,y,z", false, "Missing required option");
@@ -251,13 +282,70 @@ public class ShellTest {
   }
 
   @Test
+  public void scanTimestampTest() throws IOException {
+    Shell.log.debug("Starting scanTimestamp test ------------------------");
+    exec("createtable test", true);
+    exec("insert r f q v -ts 0", true);
+    exec("scan -st", true, "r f:q [] 0    v");
+    exec("scan -st -f 0", true, " : [] 0   ");
+    exec("deletemany -f", true);
+    exec("deletetable test -f", true, "Table: [test] has been deleted");
+  }
+
+  @Test
+  public void scanFewTest() throws IOException {
+    Shell.log.debug("Starting scanFew test ------------------------");
+    exec("createtable test", true);
+    // historically, showing few did not pertain to ColVis or Timestamp
+    exec("insert 1 123 123456 -l '12345678' -ts 123456789 1234567890", true);
+    exec("setauths -s 12345678", true);
+    String expected = "1 123:123456 [12345678] 123456789    1234567890";
+    String expectedFew = "1 123:12345 [12345678] 123456789    12345";
+    exec("scan -st", true, expected);
+    exec("scan -st -f 5", true, expectedFew);
+    // also prove that BinaryFormatter behaves same as the default
+    exec("scan -st -fm org.apache.accumulo.core.util.format.BinaryFormatter", 
true, expected);
+    exec("scan -st -f 5 -fm 
org.apache.accumulo.core.util.format.BinaryFormatter", true, expectedFew);
+    exec("setauths -c", true);
+    exec("deletetable test -f", true, "Table: [test] has been deleted");
+  }
+
+  @Test
   public void scanDateStringFormatterTest() throws IOException {
     Shell.log.debug("Starting scan dateStringFormatter test 
--------------------------");
     exec("createtable t", true);
     exec("insert r f q v -ts 0", true);
-    DateFormat dateFormat = new 
SimpleDateFormat(DateStringFormatter.DATE_FORMAT);
+    @SuppressWarnings("deprecation")
+    DateFormat dateFormat = new 
SimpleDateFormat(org.apache.accumulo.core.util.format.DateStringFormatter.DATE_FORMAT);
     String expected = String.format("r f:q [] %s    v", dateFormat.format(new 
Date(0)));
+    // historically, showing few did not pertain to ColVis or Timestamp
+    String expectedFew = expected;
+    String expectedNoTimestamp = String.format("r f:q []    v");
     exec("scan -fm org.apache.accumulo.core.util.format.DateStringFormatter 
-st", true, expected);
+    exec("scan -fm org.apache.accumulo.core.util.format.DateStringFormatter 
-st -f 1000", true, expected);
+    exec("scan -fm org.apache.accumulo.core.util.format.DateStringFormatter 
-st -f 5", true, expectedFew);
+    exec("scan -fm org.apache.accumulo.core.util.format.DateStringFormatter", 
true, expectedNoTimestamp);
+    exec("deletetable t -f", true, "Table: [t] has been deleted");
+  }
+
+  @Test
+  public void grepTest() throws IOException {
+    Shell.log.debug("Starting grep test --------------------------");
+    exec("grep", false, "java.lang.IllegalStateException: Not in a table 
context");
+    exec("createtable t", true);
+    exec("setauths -s vis", true);
+    exec("insert r f q v -ts 0 -l vis", true);
+
+    String expected = "r f:q [vis]    v";
+    String expectedTimestamp = "r f:q [vis] 0    v";
+    exec("grep", false, "No terms specified");
+    exec("grep non_matching_string", true, "");
+    // historically, showing few did not pertain to ColVis or Timestamp
+    exec("grep r", true, expected);
+    exec("grep r -f 1", true, expected);
+    exec("grep r -st", true, expectedTimestamp);
+    exec("grep r -st -f 1", true, expectedTimestamp);
+    exec("setauths -c", true);
     exec("deletetable t -f", true, "Table: [t] has been deleted");
   }
 

http://git-wip-us.apache.org/repos/asf/accumulo/blob/83f346b2/shell/src/test/java/org/apache/accumulo/shell/format/DeleterFormatterTest.java
----------------------------------------------------------------------
diff --git 
a/shell/src/test/java/org/apache/accumulo/shell/format/DeleterFormatterTest.java
 
b/shell/src/test/java/org/apache/accumulo/shell/format/DeleterFormatterTest.java
index d99c0f2..0698beb 100644
--- 
a/shell/src/test/java/org/apache/accumulo/shell/format/DeleterFormatterTest.java
+++ 
b/shell/src/test/java/org/apache/accumulo/shell/format/DeleterFormatterTest.java
@@ -34,16 +34,16 @@ import java.io.InputStream;
 import java.util.Collections;
 import java.util.Map;
 import java.util.TreeMap;
-
-import jline.UnsupportedTerminal;
-import jline.console.ConsoleReader;
-
 import org.apache.accumulo.core.client.BatchWriter;
 import org.apache.accumulo.core.client.MutationsRejectedException;
 import org.apache.accumulo.core.data.Key;
 import org.apache.accumulo.core.data.Mutation;
 import org.apache.accumulo.core.data.Value;
+import org.apache.accumulo.core.util.format.FormatterConfig;
 import org.apache.accumulo.shell.Shell;
+
+import jline.UnsupportedTerminal;
+import jline.console.ConsoleReader;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -99,13 +99,13 @@ public class DeleterFormatterTest {
 
   @Test
   public void testEmpty() {
-    formatter = new DeleterFormatter(writer, Collections.<Key,Value> 
emptyMap().entrySet(), true, shellState, true);
+    formatter = new DeleterFormatter(writer, Collections.<Key,Value> 
emptyMap().entrySet(), new FormatterConfig().setPrintTimestamps(true), 
shellState, true);
     assertFalse(formatter.hasNext());
   }
 
   @Test
   public void testSingle() throws IOException {
-    formatter = new DeleterFormatter(writer, data.entrySet(), true, 
shellState, true);
+    formatter = new DeleterFormatter(writer, data.entrySet(), new 
FormatterConfig().setPrintTimestamps(true), shellState, true);
 
     assertTrue(formatter.hasNext());
     assertNull(formatter.next());
@@ -117,7 +117,7 @@ public class DeleterFormatterTest {
   public void testNo() throws IOException {
     input.set("no\n");
     data.put(new Key("z"), new Value("v2".getBytes(UTF_8)));
-    formatter = new DeleterFormatter(writer, data.entrySet(), true, 
shellState, false);
+    formatter = new DeleterFormatter(writer, data.entrySet(), new 
FormatterConfig().setPrintTimestamps(true), shellState, false);
 
     assertTrue(formatter.hasNext());
     assertNull(formatter.next());
@@ -131,7 +131,7 @@ public class DeleterFormatterTest {
   public void testNoConfirmation() throws IOException {
     input.set("");
     data.put(new Key("z"), new Value("v2".getBytes(UTF_8)));
-    formatter = new DeleterFormatter(writer, data.entrySet(), true, 
shellState, false);
+    formatter = new DeleterFormatter(writer, data.entrySet(), new 
FormatterConfig().setPrintTimestamps(true), shellState, false);
 
     assertTrue(formatter.hasNext());
     assertNull(formatter.next());
@@ -145,7 +145,7 @@ public class DeleterFormatterTest {
   public void testYes() throws IOException {
     input.set("y\nyes\n");
     data.put(new Key("z"), new Value("v2".getBytes(UTF_8)));
-    formatter = new DeleterFormatter(writer, data.entrySet(), true, 
shellState, false);
+    formatter = new DeleterFormatter(writer, data.entrySet(), new 
FormatterConfig().setPrintTimestamps(true), shellState, false);
 
     assertTrue(formatter.hasNext());
     assertNull(formatter.next());
@@ -158,7 +158,7 @@ public class DeleterFormatterTest {
 
   @Test
   public void testMutationException() {
-    formatter = new DeleterFormatter(exceptionWriter, data.entrySet(), true, 
shellState, true);
+    formatter = new DeleterFormatter(exceptionWriter, data.entrySet(), new 
FormatterConfig().setPrintTimestamps(true), shellState, true);
 
     assertTrue(formatter.hasNext());
     assertNull(formatter.next());

http://git-wip-us.apache.org/repos/asf/accumulo/blob/83f346b2/test/src/main/java/org/apache/accumulo/test/ShellServerIT.java
----------------------------------------------------------------------
diff --git a/test/src/main/java/org/apache/accumulo/test/ShellServerIT.java 
b/test/src/main/java/org/apache/accumulo/test/ShellServerIT.java
index f2dcd4b..28112ac 100644
--- a/test/src/main/java/org/apache/accumulo/test/ShellServerIT.java
+++ b/test/src/main/java/org/apache/accumulo/test/ShellServerIT.java
@@ -62,6 +62,7 @@ import org.apache.accumulo.core.file.FileSKVWriter;
 import org.apache.accumulo.core.metadata.MetadataTable;
 import org.apache.accumulo.core.security.Authorizations;
 import org.apache.accumulo.core.util.format.Formatter;
+import org.apache.accumulo.core.util.format.FormatterConfig;
 import org.apache.accumulo.harness.SharedMiniClusterBase;
 import org.apache.accumulo.shell.Shell;
 import org.apache.accumulo.test.functional.SlowIterator;
@@ -1212,7 +1213,7 @@ public class ShellServerIT extends SharedMiniClusterBase {
    */
   public static class HexFormatter implements Formatter {
     private Iterator<Entry<Key,Value>> iter = null;
-    private boolean printTs = false;
+    private FormatterConfig config;
 
     private final static String tab = "\t";
     private final static String newline = "\n";
@@ -1231,7 +1232,7 @@ public class ShellServerIT extends SharedMiniClusterBase {
       String key;
 
       // Observe the timestamps
-      if (printTs) {
+      if (config.willPrintTimestamps()) {
         key = entry.getKey().toString();
       } else {
         key = entry.getKey().toStringNoTime();
@@ -1257,9 +1258,9 @@ public class ShellServerIT extends SharedMiniClusterBase {
     public void remove() {}
 
     @Override
-    public void initialize(final Iterable<Entry<Key,Value>> scanner, final 
boolean printTimestamps) {
+    public void initialize(final Iterable<Entry<Key,Value>> scanner, final 
FormatterConfig config) {
       this.iter = scanner.iterator();
-      this.printTs = printTimestamps;
+      this.config = new FormatterConfig(config);
     }
   }
 

Reply via email to