Use isEmpty().

Add ShutdownHookProcessDestroyer.isEmpty().
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 2e5af39..06429ab 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -46,6 +46,9 @@
            <action dev="ggregory" type="update" due-to="nullptr">
                 Java-style Array declaration and remove empty finally block #26.
             </action>
+           <action dev="ggregory" type="add" due-to="Gary Gregory">
+                Add ShutdownHookProcessDestroyer.isEmpty().
+            </action>
         </release>
         <release version="1.3" date="2014-11-02" description="Maintenance and feature Release">
             <action issue="EXEC-69" dev="ggregory" type="add" due-to="Richard Atkins, Michael Vorburger">
diff --git a/src/main/java/org/apache/commons/exec/LogOutputStream.java b/src/main/java/org/apache/commons/exec/LogOutputStream.java
index eb606d0..80f78b8 100644
--- a/src/main/java/org/apache/commons/exec/LogOutputStream.java
+++ b/src/main/java/org/apache/commons/exec/LogOutputStream.java
@@ -21,6 +21,7 @@
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
+import java.nio.charset.Charset;
 
 /**
  * Base class to connect a logging system to the output and/or
@@ -32,6 +33,16 @@
 public abstract class LogOutputStream
   extends OutputStream {
 
+    private static final class ByteArrayOutputStreamX extends ByteArrayOutputStream {
+        private ByteArrayOutputStreamX(final int size) {
+            super(size);
+        }
+
+        public synchronized String toString(final Charset charset) {
+            return new String(buf, 0, count, charset);
+        }
+    }
+
     /** Initial buffer size. */
     private static final int INTIAL_SIZE = 132;
 
@@ -42,13 +53,14 @@
     private static final int LF = 0x0a;
 
     /** the internal buffer */
-    private final ByteArrayOutputStream buffer = new ByteArrayOutputStream(
-      INTIAL_SIZE);
+    private final ByteArrayOutputStreamX buffer = new ByteArrayOutputStreamX(INTIAL_SIZE);
 
     private boolean skip = false;
 
     private final int level;
 
+    private final Charset charset;
+
     /**
      * Creates a new instance of this class.
      * Uses the default level of 999.
@@ -63,7 +75,19 @@
      * @param level loglevel used to log data written to this stream.
      */
     public LogOutputStream(final int level) {
+        this(level, null);
+    }
+
+    /**
+     * Creates a new instance of this class, specifying the character set that should be used for
+     * outputting the string for each line
+     *
+     * @param level loglevel used to log data written to this stream
+     * @param charset Character Set to use when processing lines
+     */
+    public LogOutputStream(final int level, final Charset charset) {
         this.level = level;
+        this.charset = charset == null ? Charset.defaultCharset() : charset;
     }
 
     /**
@@ -157,7 +181,7 @@
      * Converts the buffer to a string and sends it to {@code processLine}.
      */
     protected void processBuffer() {
-        processLine(buffer.toString());
+        processLine(buffer.toString(charset));
         buffer.reset();
     }
 
diff --git a/src/main/java/org/apache/commons/exec/ShutdownHookProcessDestroyer.java b/src/main/java/org/apache/commons/exec/ShutdownHookProcessDestroyer.java
index 13d94a7..c47d807 100644
--- a/src/main/java/org/apache/commons/exec/ShutdownHookProcessDestroyer.java
+++ b/src/main/java/org/apache/commons/exec/ShutdownHookProcessDestroyer.java
@@ -27,20 +27,6 @@
  */
 public class ShutdownHookProcessDestroyer implements ProcessDestroyer, Runnable {
 
-    /** the list of currently running processes */
-    private final Vector<Process> processes = new Vector<>();
-
-    /** The thread registered at the JVM to execute the shutdown handler */
-    private ProcessDestroyerImpl destroyProcessThread = null;
-
-    /** Whether or not this ProcessDestroyer has been registered as a shutdown hook */
-    private boolean added = false;
-
-    /**
-     * Whether or not this ProcessDestroyer is currently running as shutdown hook
-     */
-    private volatile boolean running = false;
-
     private class ProcessDestroyerImpl extends Thread {
 
         private boolean shouldDestroy = true;
@@ -61,22 +47,51 @@
         }
     }
 
+    /** the list of currently running processes */
+    private final Vector<Process> processes = new Vector<>();
+
+    /** The thread registered at the JVM to execute the shutdown handler */
+    private ProcessDestroyerImpl destroyProcessThread = null;
+
+    /** Whether or not this ProcessDestroyer has been registered as a shutdown hook */
+    private boolean added = false;
+
     /**
-     * Constructs a {@code ProcessDestroyer} and obtains
-     * {@code Runtime.addShutdownHook()} and
-     * {@code Runtime.removeShutdownHook()} through reflection. The
-     * ProcessDestroyer manages a list of processes to be destroyed when the VM
-     * exits. If a process is added when the list is empty, this
-     * {@code ProcessDestroyer} is registered as a shutdown hook. If
-     * removing a process results in an empty list, the
-     * {@code ProcessDestroyer} is removed as a shutdown hook.
+     * Whether or not this ProcessDestroyer is currently running as shutdown hook
+     */
+    private volatile boolean running = false;
+
+    /**
+     * Constructs a {@code ProcessDestroyer} and obtains {@code Runtime.addShutdownHook()} and
+     * {@code Runtime.removeShutdownHook()} through reflection. The ProcessDestroyer manages a list of processes to be
+     * destroyed when the VM exits. If a process is added when the list is empty, this {@code ProcessDestroyer} is
+     * registered as a shutdown hook. If removing a process results in an empty list, the {@code ProcessDestroyer} is
+     * removed as a shutdown hook.
      */
     public ShutdownHookProcessDestroyer() {
     }
 
     /**
-     * Registers this {@code ProcessDestroyer} as a shutdown hook, uses
-     * reflection to ensure pre-JDK 1.3 compatibility.
+     * Returns {@code true} if the specified {@code Process} was successfully added to the list of processes to destroy
+     * upon VM exit.
+     *
+     * @param process the process to add
+     * @return {@code true} if the specified {@code Process} was successfully added
+     */
+    @Override
+    public boolean add(final Process process) {
+        synchronized (processes) {
+            // if this list is empty, register the shutdown hook
+            if (processes.isEmpty()) {
+                addShutdownHook();
+            }
+            processes.addElement(process);
+            return processes.contains(process);
+        }
+    }
+
+    /**
+     * Registers this {@code ProcessDestroyer} as a shutdown hook, uses reflection to ensure pre-JDK 1.3 compatibility.
      */
     private void addShutdownHook() {
         if (!running) {
@@ -87,20 +102,54 @@
     }
 
     /**
-     * Removes this {@code ProcessDestroyer} as a shutdown hook, uses
-     * reflection to ensure pre-JDK 1.3 compatibility
+     * Returns whether or not the ProcessDestroyer is registered as as shutdown hook
+     *
+     * @return true if this is currently added as shutdown hook
+     */
+    public boolean isAddedAsShutdownHook() {
+        return added;
+    }
+
+    /**
+     * Tests emptiness (size == 0).
+     * 
+     * @return Whether or not this is empty.
+     * @since 1.4.0
+     */
+    public boolean isEmpty() {
+        return size() == 0;
+    }
+
+    /**
+     * Returns {@code true} if the specified {@code Process} was successfully removed from the list of processes to
+     * destroy upon VM exit.
+     *
+     * @param process the process to remove
+     * @return {@code true} if the specified {@code Process} was successfully removed
+     */
+    @Override
+    public boolean remove(final Process process) {
+        synchronized (processes) {
+            final boolean processRemoved = processes.removeElement(process);
+            if (processRemoved && processes.isEmpty()) {
+                removeShutdownHook();
+            }
+            return processRemoved;
+        }
+    }
+    
+    /**
+     * Removes this {@code ProcessDestroyer} as a shutdown hook, uses reflection to ensure pre-JDK 1.3 compatibility
      */
     private void removeShutdownHook() {
         if (added && !running) {
-            final boolean removed = Runtime.getRuntime().removeShutdownHook(
-                    destroyProcessThread);
+            final boolean removed = Runtime.getRuntime().removeShutdownHook(destroyProcessThread);
             if (!removed) {
                 System.err.println("Could not remove shutdown hook");
             }
             /*
-             * start the hook thread, a unstarted thread may not be eligible for
-             * garbage collection Cf.: http://developer.java.sun.com/developer/
-             * bugParade/bugs/4533087.html
+             * start the hook thread, a unstarted thread may not be eligible for garbage collection Cf.:
+             * http://developer.java.sun.com/developer/ bugParade/bugs/4533087.html
              */
 
             destroyProcessThread.setShouldDestroy(false);
@@ -118,83 +167,31 @@
     }
 
     /**
-     * Returns whether or not the ProcessDestroyer is registered as as shutdown
-     * hook
-     *
-     * @return true if this is currently added as shutdown hook
-     */
-    public boolean isAddedAsShutdownHook() {
-        return added;
-    }
-
-    /**
-     * Returns {@code true} if the specified {@code Process} was
-     * successfully added to the list of processes to destroy upon VM exit.
-     *
-     * @param process
-     *            the process to add
-     * @return {@code true} if the specified {@code Process} was
-     *         successfully added
-     */
-    @Override
-    public boolean add(final Process process) {
-        synchronized (processes) {
-            // if this list is empty, register the shutdown hook
-            if (processes.size() == 0) {
-                addShutdownHook();
-            }
-            processes.addElement(process);
-            return processes.contains(process);
-        }
-    }
-
-    /**
-     * Returns {@code true} if the specified {@code Process} was
-     * successfully removed from the list of processes to destroy upon VM exit.
-     *
-     * @param process
-     *            the process to remove
-     * @return {@code true} if the specified {@code Process} was
-     *         successfully removed
-     */
-    @Override
-    public boolean remove(final Process process) {
-        synchronized (processes) {
-            final boolean processRemoved = processes.removeElement(process);
-            if (processRemoved && processes.size() == 0) {
-                removeShutdownHook();
-            }
-            return processRemoved;
-        }
-    }
-
-  /**
-   * Returns the number of registered processes.
-   *
-   * @return the number of register process
-   */
-  @Override
-public int size() {
-    return processes.size();
-  }
-
-  /**
      * Invoked by the VM when it is exiting.
      */
-  @Override
-public void run() {
-      synchronized (processes) {
-          running = true;
-          final Enumeration<Process> e = processes.elements();
-          while (e.hasMoreElements()) {
-              final Process process = e.nextElement();
-              try {
-                  process.destroy();
-              }
-              catch (final Throwable t) {
-                  System.err.println("Unable to terminate process during process shutdown");
-              }
-          }
-      }
-  }
+    @Override
+    public void run() {
+        synchronized (processes) {
+            running = true;
+            final Enumeration<Process> e = processes.elements();
+            while (e.hasMoreElements()) {
+                final Process process = e.nextElement();
+                try {
+                    process.destroy();
+                } catch (final Throwable t) {
+                    System.err.println("Unable to terminate process during process shutdown");
+                }
+            }
+        }
+    }
+
+    /**
+     * Returns the number of registered processes.
+     *
+     * @return the number of register process
+     */
+    @Override
+    public int size() {
+        return processes.size();
+    }
 }
diff --git a/src/main/java/org/apache/commons/exec/util/MapUtils.java b/src/main/java/org/apache/commons/exec/util/MapUtils.java
index 57901c8..dbe3547 100644
--- a/src/main/java/org/apache/commons/exec/util/MapUtils.java
+++ b/src/main/java/org/apache/commons/exec/util/MapUtils.java
@@ -98,10 +98,10 @@
 
         Map<K, V> result = null;
 
-        if (lhs == null || lhs.size() == 0) {
+        if (lhs == null || lhs.isEmpty()) {
             result = copy(rhs);
         }
-        else if (rhs == null || rhs.size() == 0) {
+        else if (rhs == null || rhs.isEmpty()) {
             result = copy(lhs);
         }
         else {
diff --git a/src/main/java/org/apache/commons/exec/util/StringUtils.java b/src/main/java/org/apache/commons/exec/util/StringUtils.java
index 7eaeb7d..b8fb1e9 100644
--- a/src/main/java/org/apache/commons/exec/util/StringUtils.java
+++ b/src/main/java/org/apache/commons/exec/util/StringUtils.java
@@ -74,7 +74,7 @@
             return argBuf;
         }
 
-        if (vars == null || vars.size() == 0) {
+        if (vars == null || vars.isEmpty()) {
             return argBuf.append(argStr);
         }
 
diff --git a/src/site/apt/tutorial.apt b/src/site/apt/tutorial.apt
index c059ada..6038063 100644
--- a/src/site/apt/tutorial.apt
+++ b/src/site/apt/tutorial.apt
@@ -54,7 +54,7 @@
 
   You successfully printed your first PDF document but at the end an exception is thrown - what
   happend? Oops, Acrobat Reader returned an exit value of '1' on success which is usually
-  considered as an execution failure. So we have to tweak our code to fix this odd behaviour -
+  considered as an execution failure. So we have to tweak our code to fix this odd behavior -
   we define the exit value of "1" to be considered as successful execution.
 
 +----------------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/exec/DefaultExecutorTest.java b/src/test/java/org/apache/commons/exec/DefaultExecutorTest.java
index 787393f..896de1b 100644
--- a/src/test/java/org/apache/commons/exec/DefaultExecutorTest.java
+++ b/src/test/java/org/apache/commons/exec/DefaultExecutorTest.java
@@ -472,14 +472,14 @@
       final ShutdownHookProcessDestroyer processDestroyer = new ShutdownHookProcessDestroyer();
       exec.setProcessDestroyer(processDestroyer);
 
-      assertTrue(processDestroyer.size() == 0);
+      assertTrue(processDestroyer.isEmpty());
       assertTrue(processDestroyer.isAddedAsShutdownHook() == false);
 
       final int exitValue = exec.execute(cl);
 
       assertEquals("FOO..", baos.toString().trim());
       assertFalse(exec.isFailure(exitValue));
-      assertTrue(processDestroyer.size() == 0);
+      assertTrue(processDestroyer.isEmpty());
       assertTrue(processDestroyer.isAddedAsShutdownHook() == false);
     }
 
@@ -499,7 +499,7 @@
       final ExecuteWatchdog watchdog = new ExecuteWatchdog(Integer.MAX_VALUE);
 
       assertTrue(exec.getProcessDestroyer() == null);
-      assertTrue(processDestroyer.size() == 0);
+      assertTrue(processDestroyer.isEmpty());
       assertTrue(processDestroyer.isAddedAsShutdownHook() == false);
 
       exec.setWatchdog(watchdog);
diff --git a/src/test/java/org/apache/commons/exec/LogOutputStreamTest.java b/src/test/java/org/apache/commons/exec/LogOutputStreamTest.java
index 8e7403e..8f578d2 100644
--- a/src/test/java/org/apache/commons/exec/LogOutputStreamTest.java
+++ b/src/test/java/org/apache/commons/exec/LogOutputStreamTest.java
@@ -17,10 +17,12 @@
  */
 package org.apache.commons.exec;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 
 import java.io.File;
 import java.io.OutputStream;
+import java.nio.charset.Charset;
 
 import org.junit.After;
 import org.junit.Before;
@@ -37,6 +39,7 @@
     private final File testDir = new File("src/test/scripts");
     private OutputStream systemOut;
     private final File environmentScript = TestUtil.resolveScriptForOS(testDir + "/environment");
+    private final File utf8CharacterScript = TestUtil.resolveScriptForOS(testDir + "/utf8Characters");
 
     @BeforeClass
     public static void classSetUp() {
@@ -47,13 +50,14 @@
 
     @Before
     public void setUp() throws Exception {
-        this.systemOut = new SystemLogOutputStream(1);
-        this.exec.setStreamHandler(new PumpStreamHandler(systemOut, systemOut));
+
     }
 
     @After
     public void tearDown() throws Exception {
-        this.systemOut.close();
+        if (this.systemOut != null) {
+            this.systemOut.close();
+        }
     }
 
     // ======================================================================
@@ -62,24 +66,49 @@
 
     @Test
     public void testStdout() throws Exception {
+        this.systemOut = new SystemLogOutputStream(1);
+        this.exec.setStreamHandler(new PumpStreamHandler(systemOut, systemOut));
+
         final CommandLine cl = new CommandLine(environmentScript);
         final int exitValue = exec.execute(cl);
         assertFalse(exec.isFailure(exitValue));
     }
 
+    @Test
+    public void testStdoutWithUTF8Characters() throws Exception {
+        this.systemOut = new SystemLogOutputStream(1, Charset.forName("UTF-8"));
+        this.exec.setStreamHandler(new PumpStreamHandler(systemOut, systemOut));
+
+        final CommandLine cl = new CommandLine(utf8CharacterScript);
+        final int exitValue = exec.execute(cl);
+        assertFalse(exec.isFailure(exitValue));
+        assertEquals("This string contains UTF-8 characters like the see no evil monkey \uD83D\uDE48 and the right single quotation mark \u2019", ((SystemLogOutputStream) systemOut).getOutput());
+    }
+
     // ======================================================================
     // Helper classes
     // ======================================================================
 
     private class SystemLogOutputStream extends LogOutputStream {
 
+        StringBuffer output = new StringBuffer();
+
         private SystemLogOutputStream(final int level) {
             super(level);
         }
 
+        private SystemLogOutputStream(final int level, final Charset charset) {
+            super(level, charset);
+        }
+
         @Override
         protected void processLine(final String line, final int level) {
             System.out.println(line);
+            output.append(line);
+        }
+
+        private String getOutput() {
+            return output.toString();
         }
     }
 
diff --git a/src/test/java/org/apache/commons/exec/util/MapUtilTest.java b/src/test/java/org/apache/commons/exec/util/MapUtilTest.java
index af1a54d..053e7b6 100644
--- a/src/test/java/org/apache/commons/exec/util/MapUtilTest.java
+++ b/src/test/java/org/apache/commons/exec/util/MapUtilTest.java
@@ -45,7 +45,7 @@
         assertEquals("/usr/opt/java", result.get("JAVA_HOME"));
 
         result.remove("JAVA_HOME");
-        assertTrue(result.size() == 0);
+        assertTrue(result.isEmpty());
         assertTrue(procEnvironment.size() == 1);
     }