title: Limiting character count of JavaFX TextField causes IndexOutOfBounds on Undo
body: <p>I have the requirement to limit the number of characters a user can input into a <code>TextField</code> JavaFX control. I have extended <code>TextField</code> like so</p>
  
  <pre><code>public class LengthLimitedTextField extends TextField {
      /**
       * @param maxCharacters The max allowed characters that can be entered into this {@link TextField}.
       */
      public LengthLimitedTextField(final int maxCharacters) {
          final TextField thisField = this;
          this.textProperty().addListener(new ChangeListener&lt;String&gt;() {
              @Override
              public void changed(ObservableValue&lt;? extends String&gt; observable,
                                  String oldValue, String newValue) {
                  // Force correct length by deleting the last entered character if text is longer than maxCharacters
                  if (newValue.length() &gt; maxCharacters) {
                      thisField.deleteNextChar();
                  }
              }
          });
      }
  }
  </code></pre>
  
  <p>This does work as intended. However, lets say that for a particular <code>LengthLimitedTextField</code> the <code>maxCharacters</code> is set to 3. If the user enters 4 or more characters, and attempts to Undo (either via CTRL+Z or mouse context menu), I receive the following <code>Exception</code> and the text is left unchanged.</p>
  
  <pre><code>java.lang.IndexOutOfBoundsException
      at javafx.scene.control.TextInputControl.replaceText(TextInputControl.java:368)
      at com.sun.javafx.scene.control.skin.TextFieldSkin.replaceText(TextFieldSkin.java:572)
      at com.sun.javafx.scene.control.behavior.TextFieldBehavior.replaceText(TextFieldBehavior.java:159)
      at com.sun.javafx.scene.control.behavior.TextInputControlBehavior$UndoManager.undo(TextInputControlBehavior.java:442)
      at com.sun.javafx.scene.control.behavior.TextInputControlBehavior.callAction(TextInputControlBehavior.java:137)
      at com.sun.javafx.scene.control.skin.TextInputControlSkin$ContextMenuItem$1.handle(TextInputControlSkin.java:595)
      at com.sun.javafx.scene.control.skin.TextInputControlSkin$ContextMenuItem$1.handle(TextInputControlSkin.java:593)
      at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:69)
      at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:217)
      at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:170)
      at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:37)
      at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92)
      at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:53)
      at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:28)
      at javafx.event.Event.fireEvent(Event.java:171)
      at javafx.scene.control.MenuItem.fire(MenuItem.java:456)
      at com.sun.javafx.scene.control.skin.ContextMenuContent$MenuItemContainer.doSelect(ContextMenuContent.java:1197)
      at com.sun.javafx.scene.control.skin.ContextMenuContent$MenuItemContainer$6.handle(ContextMenuContent.java:1148)
      at com.sun.javafx.scene.control.skin.ContextMenuContent$MenuItemContainer$6.handle(ContextMenuContent.java:1146)
      at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:69)
      at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:217)
      at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:170)
      at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:38)
      at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:37)
      at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92)
      at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:35)
      at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92)
      at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:35)
      at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92)
      at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:53)
      at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:33)
      at javafx.event.Event.fireEvent(Event.java:171)
      at javafx.scene.Scene$MouseHandler.process(Scene.java:3328)
      at javafx.scene.Scene$MouseHandler.process(Scene.java:3168)
      at javafx.scene.Scene$MouseHandler.access$1900(Scene.java:3123)
      at javafx.scene.Scene.impl_processMouseEvent(Scene.java:1563)
      at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2265)
      at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:250)
      at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:173)
      at java.security.AccessController.doPrivileged(Native Method)
      at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:292)
      at com.sun.glass.ui.View.handleMouseEvent(View.java:528)
      at com.sun.glass.ui.View.notifyMouse(View.java:922)
      at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
      at com.sun.glass.ui.win.WinApplication.access$100(WinApplication.java:29)
      at com.sun.glass.ui.win.WinApplication$3$1.run(WinApplication.java:73)
      at java.lang.Thread.run(Thread.java:724)
  </code></pre>
  
  <p>I'm not sure how I can resolve this. A possible (but not ideal) solution was to disable Undo/Redo completely, but that doesn't appear possible without completely override the context menu (which according to this <a href="https://stackoverflow.com/questions/13562712/how-to-disable-the-default-context-menu-on-a-text-field">SO answer</a>, isn't easy) and the default keyboard shortcuts. </p>
  
  <p>So ultimately, my question is twofold:</p>
  
  <p>Is there possibly another way I can limit the number of characters of a <code>TextField</code> without throwing an exception on Undo? Or is there a clean way to completely disable Undo in the application?</p>
  
  <p><strong>Edit</strong>: I did some more research and according to <a href="https://javafx-jira.kenai.com/browse/RT-30881" rel="noreferrer">https://javafx-jira.kenai.com/browse/RT-30881</a> this appears to be a bug. See <a href="https://javafx-jira.kenai.com/browse/RT-30881?focusedCommentId=345660&amp;page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-345660" rel="noreferrer">this comment</a>. So perhaps this isn't possible to achieve then? I'm going to leave the question open in hopes someone does have a possible workaround. </p>