UNPKG

epubjs

Version:

Render ePub documents in the browser, across many devices

370 lines (350 loc) 83.5 kB
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"><head><title>Text Components</title><link rel="stylesheet" href="core.css" type="text/css"/><meta name="generator" content="DocBook XSL Stylesheets V1.74.0"/></head><body><div class="sect1" title="Text Components"><div class="titlepage"><div><div><h1 class="title"><a id="learnjava3-CHP-18-SECT-1"/>Text Components</h1></div></div></div><p><a id="idx11027" class="indexterm"/>Swing offers sophisticated text components, from plain-text entry boxes to HTML renderers. For full coverage of Swing’s text capabilities, see O’Reilly’s <span class="emphasis"><em>Java Swing</em></span>. In that encyclopedic book, several meaty chapters are devoted to text. It’s a huge subject; we’ll just scratch the surface here.</p><p>Let’s begin by examining the simpler text components. <a id="I_indexterm18_id798841" class="indexterm"/><code class="literal">JTextField</code> is a single-line text editor and <a id="I_indexterm18_id798852" class="indexterm"/><code class="literal">JTextArea</code> is a simple, multiline text editor. Both <code class="literal">JTextField</code> and <code class="literal">JTextArea</code> derive from the <a id="I_indexterm18_id798874" class="indexterm"/><code class="literal">JTextComponent</code> class, which provides the functionality they have in common. This includes methods for setting and retrieving the displayed text, specifying whether the text is “editable” or read-only, manipulating the cursor position within the text, and manipulating text selections.</p><p>Observing changes in text components requires an understanding of how the components implement the Model-View-Controller (MVC) architecture. You may recall from the last chapter that Swing components implement a true MVC architecture. It’s in the text components that you first get an inkling of a clear separation between the M and VC parts of the MVC architecture. The model for text components is an object called a <code class="literal">Document</code>. When you add or remove text from a <code class="literal">JTextField</code> or a <code class="literal">JTextArea</code>, the corresponding <code class="literal">Document</code> is changed. It’s the document itself, not the visual components, that generates text-related events when something changes. To receive notification of <code class="literal">JTextArea</code> changes, therefore, you register with the underlying <code class="literal">Document</code>, not with the <code class="literal">JTextArea</code> component itself:</p><a id="I_18_tt1053"/><pre class="programlisting"> <code class="n">JTextArea</code> <code class="n">textArea</code> <code class="o">=</code> <code class="k">new</code> <code class="n">JTextArea</code><code class="o">();</code> <code class="n">Document</code> <code class="n">doc</code> <code class="o">=</code> <code class="n">textArea</code><code class="o">.</code><code class="na">getDocument</code><code class="o">();</code> <code class="n">doc</code><code class="o">.</code><code class="na">addDocumentListener</code><code class="o">(</code><code class="n">someListener</code><code class="o">);</code></pre><p>As you’ll see in an upcoming example, you can easily have more than one visual text component use the same underlying <code class="literal">Document</code> data model.</p><p>In addition, <code class="literal">JTextField</code> components generate <code class="literal">ActionEvent</code>s whenever the user presses the Return key within the field. To get these events, just implement the <code class="literal">ActionListener</code> interface and register your listener using the <code class="literal">addActionListener()</code> method.</p><p>The next sections contain a couple of simple applications that show you how to work with text areas and fields.</p><div class="sect2" title="The TextEntryBox Application"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-18-SECT-1.1"/>The TextEntryBox Application</h2></div></div></div><p><a id="idx11038" class="indexterm"/> <a id="idx11040" class="indexterm"/>Our first example, <code class="literal">TextEntryBox</code>, creates a <a id="I_indexterm18_id799024" class="indexterm"/><code class="literal">JTextArea</code> and ties it to a <a id="I_indexterm18_id799035" class="indexterm"/><code class="literal">JTextField</code>, as you can see in <a class="xref" href="ch18s01.html#learnjava3-CHP-18-FIG-1" title="Figure 18-1. The TextEntryBox application">Figure 18-1</a>.</p><div class="figure"><a id="learnjava3-CHP-18-FIG-1"/><div class="figure-contents"><div class="mediaobject"><a id="I_18_tt1054"/><img src="httpatomoreillycomsourceoreillyimages1707667.png" alt="The TextEntryBox application"/></div></div><p class="title">Figure 18-1. The TextEntryBox application</p></div><p>When the user hits Return in the <code class="literal">JTextField</code>, we receive an <code class="literal">ActionEvent</code> and add the line to the <code class="literal">JTextArea</code>’s display. Try it out. You may have to click your mouse in the <code class="literal">JTextField</code> to give it focus before typing in it. If you fill up the display with lines, you can test-drive the scroll bar:</p><a id="I_18_tt1055"/><pre class="programlisting"> <code class="c1">//file: TextEntryBox.java</code> <code class="kn">import</code> <code class="nn">java.awt.*</code><code class="o">;</code> <code class="kn">import</code> <code class="nn">java.awt.event.*</code><code class="o">;</code> <code class="kn">import</code> <code class="nn">javax.swing.*</code><code class="o">;</code> <code class="kd">public</code> <code class="kd">class</code> <code class="nc">TextEntryBox</code> <code class="o">{</code> <code class="kd">public</code> <code class="kd">static</code> <code class="kt">void</code> <code class="nf">main</code><code class="o">(</code><code class="n">String</code><code class="o">[]</code> <code class="n">args</code><code class="o">)</code> <code class="o">{</code> <code class="n">JFrame</code> <code class="n">frame</code> <code class="o">=</code> <code class="k">new</code> <code class="n">JFrame</code><code class="o">(</code><code class="s">"Text Entry Box"</code><code class="o">);</code> <code class="kd">final</code> <code class="n">JTextArea</code> <code class="n">area</code> <code class="o">=</code> <code class="k">new</code> <code class="n">JTextArea</code><code class="o">();</code> <code class="n">area</code><code class="o">.</code><code class="na">setFont</code><code class="o">(</code><code class="k">new</code> <code class="n">Font</code><code class="o">(</code><code class="s">"Serif"</code><code class="o">,</code> <code class="n">Font</code><code class="o">.</code><code class="na">BOLD</code><code class="o">,</code> <code class="mi">18</code><code class="o">));</code> <code class="n">area</code><code class="o">.</code><code class="na">setText</code><code class="o">(</code><code class="s">"Howdy!\n"</code><code class="o">);</code> <code class="kd">final</code> <code class="n">JTextField</code> <code class="n">field</code> <code class="o">=</code> <code class="k">new</code> <code class="n">JTextField</code><code class="o">();</code> <code class="n">frame</code><code class="o">.</code><code class="na">add</code><code class="o">(</code><code class="k">new</code> <code class="n">JScrollPane</code><code class="o">(</code><code class="n">area</code><code class="o">),</code> <code class="n">BorderLayout</code><code class="o">.</code><code class="na">CENTER</code><code class="o">);</code> <code class="n">frame</code><code class="o">.</code><code class="na">add</code><code class="o">(</code><code class="n">field</code><code class="o">,</code> <code class="n">BorderLayout</code><code class="o">.</code><code class="na">SOUTH</code><code class="o">);</code> <code class="n">field</code><code class="o">.</code><code class="na">requestFocus</code><code class="o">();</code> <code class="n">field</code><code class="o">.</code><code class="na">addActionListener</code><code class="o">(</code><code class="k">new</code> <code class="n">ActionListener</code><code class="o">()</code> <code class="o">{</code> <code class="kd">public</code> <code class="kt">void</code> <code class="nf">actionPerformed</code><code class="o">(</code><code class="n">ActionEvent</code> <code class="n">ae</code><code class="o">)</code> <code class="o">{</code> <code class="n">area</code><code class="o">.</code><code class="na">append</code><code class="o">(</code><code class="n">field</code><code class="o">.</code><code class="na">getText</code><code class="o">()</code> <code class="o">+</code> <code class="sc">'\n'</code><code class="o">);</code> <code class="n">field</code><code class="o">.</code><code class="na">setText</code><code class="o">(</code><code class="s">""</code><code class="o">);</code> <code class="o">}</code> <code class="o">});</code> <code class="n">frame</code><code class="o">.</code><code class="na">setDefaultCloseOperation</code><code class="o">(</code> <code class="n">JFrame</code><code class="o">.</code><code class="na">EXIT_ON_CLOSE</code> <code class="o">);</code> <code class="n">frame</code><code class="o">.</code><code class="na">setSize</code><code class="o">(</code><code class="mi">200</code><code class="o">,</code> <code class="mi">300</code><code class="o">);</code> <code class="n">frame</code><code class="o">.</code><code class="na">setVisible</code><code class="o">(</code><code class="kc">true</code><code class="o">);</code> <code class="o">}</code> <code class="o">}</code></pre><p><code class="literal">TextEntryBox</code> is exceedingly simple; we’ve done a few things to make it more interesting. We give the text area a bigger font using <code class="literal">Component</code>’s <a id="I_indexterm18_id799126" class="indexterm"/><code class="literal">setFont()</code> method; fonts are discussed in <a class="xref" href="ch20.html" title="Chapter 20. Drawing with the 2D API">Chapter 20</a>. Finally, we want to be notified whenever the user presses Return in the text field, so we register an anonymous inner class as a listener for action events.</p><p>Pressing Return in the <code class="literal">JTextField</code> generates an action event, and that’s where the fun begins. We handle the event in the <code class="literal">actionPerformed()</code> method of our inner <code class="literal">ActionListener</code> implementation. Then, we use the <a id="I_indexterm18_id799163" class="indexterm"/><code class="literal">getText()</code> and <a id="I_indexterm18_id799174" class="indexterm"/><code class="literal">setText()</code> methods to manipulate the text that the user has typed. These methods can be used for <code class="literal">JTextField</code> and <code class="literal">JTextArea</code>, as these components are both derived from the <code class="literal">JTextComponent</code> class and, therefore, have some common functionality.</p><p>The event handler, <code class="literal">actionPerformed()</code>, calls <code class="literal">field.getText()</code> to read the text that the user typed into our <code class="literal">JTextField</code>. It then adds this text to the <code class="literal">JTextArea</code> by calling <code class="literal">area.append()</code>. Finally, we clear the text field by calling the method <code class="literal">field.setText("")</code>, preparing it for more input.</p><p>Remember, the text components really are distinct from the text data model, the <code class="literal">Document</code>. When you call <code class="literal">setText()</code>, <code class="literal">getText()</code>, or <a id="I_indexterm18_id799258" class="indexterm"/><code class="literal">append()</code>, these methods are shorthand for operations on an underlying <code class="literal">Document</code>.</p><p>By default, <code class="literal">JTextField</code> and <code class="literal">JTextArea</code> are editable; you can type and edit in both text components. They can be changed to output-only areas by calling <a id="I_indexterm18_id799288" class="indexterm"/><code class="literal">setEditable(false)</code>. Both text components also support <a id="I_indexterm18_id799299" class="indexterm"/><span class="emphasis"><em>selections</em></span>. A selection is a range of text that is highlighted for copying, cutting, or pasting in your windowing system. You select text by dragging the mouse over it; you can then cut, copy, and paste it into other text windows using the default keyboard gestures. On most systems, these are Ctrl-C for copy, Ctrl-V for paste, and Ctrl-X for cut (on the Mac it’s Command-C, Command-V, and Command-X). You can also programmatically manage these operations using the <code class="literal">JTextComponent</code>’s <code class="literal">cut()</code> , <code class="literal">copy()</code>, <code class="literal">and paste()</code> methods. You could, for example, create a pop-up menu with the standard cut, copy, and paste options using these methods. The current text selection is returned by <a id="I_indexterm18_id799339" class="indexterm"/><code class="literal">getSelectedText()</code>, and you can set the selection using <a id="I_indexterm18_id799351" class="indexterm"/><code class="literal">selectText()</code>, which takes an index range or <a id="I_indexterm18_id799362" class="indexterm"/><code class="literal">selectAll()</code>.</p><p>Notice how <code class="literal">JTextArea</code> fits neatly inside a <code class="literal">JScrollPane</code>. The scroll pane gives us the expected scrollbars and scrolling behavior if the text in the <code class="literal">JTextArea</code> becomes too large for the available space.<a id="I_indexterm18_id799392" class="indexterm"/><a id="I_indexterm18_id799399" class="indexterm"/></p></div><div class="sect2" title="Formatted Text"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-18-SECT-1.2"/>Formatted Text</h2></div></div></div><p><a id="idx11015" class="indexterm"/> <a id="idx11033" class="indexterm"/>The <a id="I_indexterm18_id799436" class="indexterm"/><code class="literal">JFormattedTextField</code> component provides explicit support for editing complex formatted values such as numbers and dates. <code class="literal">JFormattedTextField</code> acts somewhat like a <code class="literal">JTextField</code>, except that it accepts a format-specifying object in its constructor and manages a complex object type (such as <code class="literal">Date</code> or <code class="literal">Integer</code>) through its <a id="I_indexterm18_id799471" class="indexterm"/><code class="literal">setValue()</code> and <a id="I_indexterm18_id799481" class="indexterm"/><code class="literal">getValue()</code> methods. The following example shows the construction of a simple form with different types of formatted fields:</p><a id="I_18_tt1056"/><pre class="programlisting"> <code class="kn">import</code> <code class="nn">java.text.*</code><code class="o">;</code> <code class="kn">import</code> <code class="nn">javax.swing.*</code><code class="o">;</code> <code class="kn">import</code> <code class="nn">javax.swing.text.*</code><code class="o">;</code> <code class="kn">import</code> <code class="nn">java.util.Date</code><code class="o">;</code> <code class="kd">public</code> <code class="kd">class</code> <code class="nc">FormattedFields</code> <code class="o">{</code> <code class="kd">public</code> <code class="kd">static</code> <code class="kt">void</code> <code class="nf">main</code><code class="o">(</code> <code class="n">String</code><code class="o">[]</code> <code class="n">args</code> <code class="o">)</code> <code class="kd">throws</code> <code class="n">Exception</code> <code class="o">{</code> <code class="n">Box</code> <code class="n">form</code> <code class="o">=</code> <code class="n">Box</code><code class="o">.</code><code class="na">createVerticalBox</code><code class="o">();</code> <code class="n">form</code><code class="o">.</code><code class="na">add</code><code class="o">(</code> <code class="k">new</code> <code class="n">JLabel</code><code class="o">(</code><code class="s">"Name:"</code><code class="o">)</code> <code class="o">);</code> <code class="n">form</code><code class="o">.</code><code class="na">add</code><code class="o">(</code> <code class="k">new</code> <code class="n">JTextField</code><code class="o">(</code><code class="s">"Joe User"</code><code class="o">)</code> <code class="o">);</code> <code class="n">form</code><code class="o">.</code><code class="na">add</code><code class="o">(</code> <code class="k">new</code> <code class="n">JLabel</code><code class="o">(</code><code class="s">"Birthday:"</code><code class="o">)</code> <code class="o">);</code> <code class="n">JFormattedTextField</code> <code class="n">birthdayField</code> <code class="o">=</code> <code class="k">new</code> <code class="nf">JFormattedTextField</code><code class="o">(</code><code class="k">new</code> <code class="n">SimpleDateFormat</code><code class="o">(</code><code class="s">"MM/dd/yy"</code><code class="o">));</code> <code class="n">birthdayField</code><code class="o">.</code><code class="na">setValue</code><code class="o">(</code> <code class="k">new</code> <code class="n">Date</code><code class="o">()</code> <code class="o">);</code> <code class="n">form</code><code class="o">.</code><code class="na">add</code><code class="o">(</code> <code class="n">birthdayField</code> <code class="o">);</code> <code class="n">form</code><code class="o">.</code><code class="na">add</code><code class="o">(</code> <code class="k">new</code> <code class="n">JLabel</code><code class="o">(</code><code class="s">"Age:"</code><code class="o">)</code> <code class="o">);</code> <code class="n">form</code><code class="o">.</code><code class="na">add</code><code class="o">(</code><code class="k">new</code> <code class="n">JFormattedTextField</code><code class="o">(</code><code class="k">new</code> <code class="n">Integer</code><code class="o">(</code><code class="mi">32</code><code class="o">)));</code> <code class="n">form</code><code class="o">.</code><code class="na">add</code><code class="o">(</code> <code class="k">new</code> <code class="n">JLabel</code><code class="o">(</code><code class="s">"Hairs on Body:"</code><code class="o">)</code> <code class="o">);</code> <code class="n">JFormattedTextField</code> <code class="n">hairsField</code> <code class="o">=</code> <code class="k">new</code> <code class="n">JFormattedTextField</code><code class="o">(</code> <code class="k">new</code> <code class="n">DecimalFormat</code><code class="o">(</code><code class="s">"###,###"</code><code class="o">)</code> <code class="o">);</code> <code class="n">hairsField</code><code class="o">.</code><code class="na">setValue</code><code class="o">(</code><code class="k">new</code> <code class="n">Integer</code><code class="o">(</code><code class="mi">100000</code><code class="o">));</code> <code class="n">form</code><code class="o">.</code><code class="na">add</code><code class="o">(</code> <code class="n">hairsField</code> <code class="o">);</code> <code class="n">form</code><code class="o">.</code><code class="na">add</code><code class="o">(</code> <code class="k">new</code> <code class="n">JLabel</code><code class="o">(</code><code class="s">"Phone Number:"</code><code class="o">)</code> <code class="o">);</code> <code class="n">JFormattedTextField</code> <code class="n">phoneField</code> <code class="o">=</code> <code class="k">new</code> <code class="nf">JFormattedTextField</code><code class="o">(</code> <code class="k">new</code> <code class="n">MaskFormatter</code><code class="o">(</code><code class="s">"(###)###-####"</code><code class="o">)</code> <code class="o">);</code> <code class="n">phoneField</code><code class="o">.</code><code class="na">setValue</code><code class="o">(</code><code class="s">"(314)555-1212"</code><code class="o">);</code> <code class="n">form</code><code class="o">.</code><code class="na">add</code><code class="o">(</code> <code class="n">phoneField</code> <code class="o">);</code> <code class="n">JFrame</code> <code class="n">frame</code> <code class="o">=</code> <code class="k">new</code> <code class="n">JFrame</code><code class="o">(</code><code class="s">"User Information"</code><code class="o">);</code> <code class="n">frame</code><code class="o">.</code><code class="na">getContentPane</code><code class="o">().</code><code class="na">add</code><code class="o">(</code><code class="n">form</code><code class="o">);</code> <code class="n">frame</code><code class="o">.</code><code class="na">pack</code><code class="o">();</code> <code class="n">frame</code><code class="o">.</code><code class="na">setDefaultCloseOperation</code><code class="o">(</code> <code class="n">JFrame</code><code class="o">.</code><code class="na">EXIT_ON_CLOSE</code> <code class="o">);</code> <code class="n">frame</code><code class="o">.</code><code class="na">setVisible</code><code class="o">(</code><code class="kc">true</code><code class="o">);</code> <code class="o">}</code> <code class="o">}</code></pre><p><code class="literal">JFormattedTextField</code> can be constructed in a variety of ways. You can use a plain instance of <code class="literal">java.lang.Number</code> (e.g., <code class="literal">Integer</code> and <code class="literal">Float</code>) as a prototype or set the layout explicitly using a formatting object from the <a id="I_indexterm18_id799538" class="indexterm"/><code class="literal">java.text</code> package: <code class="literal">java.text.NumberFormat</code>, <code class="literal">java.text.DateFormat</code>, or the more arbitrary <code class="literal">java.text.MaskFormatter</code>. The <a id="I_indexterm18_id799566" class="indexterm"/><code class="literal">NumberFormat</code> and <a id="I_indexterm18_id799577" class="indexterm"/><code class="literal">DateFormat</code> classes of the <code class="literal">java.text</code> package are discussed in Chapters <a class="xref" href="ch10.html" title="Chapter 10. Working with Text">10</a> and <a class="xref" href="ch11.html" title="Chapter 11. Core Utilities">11</a>. <a id="I_indexterm18_id799607" class="indexterm"/><code class="literal">MaskFormatter</code> allows you to construct arbitrary physical layout conventions. In a moment, we’ll discuss input filtering and component validation, which also allow you to restrict the kinds of characters that could fill the fields or perform arbitrary checks on the data. Finally, we should mention that in this example, we’ve used a <a id="I_indexterm18_id799623" class="indexterm"/><code class="literal">Box</code> container. A <code class="literal">Box</code> is just a Swing container that uses a <code class="literal">BoxLayout</code>, which we’ll discuss more in <a class="xref" href="ch19.html" title="Chapter 19. Layout Managers">Chapter 19</a>.</p><p>After construction, you can set a valid value using <code class="literal">setValue()</code> and retrieve the last valid value with <code class="literal">getValue()</code>. To do this, you’ll have to cast the value back to the correct type based on the format you are using. For example, this statement retrieves the date from our birthday field:</p><a id="I_18_tt1057"/><pre class="programlisting"> <code class="n">Date</code> <code class="n">bday</code> <code class="o">=</code> <code class="o">(</code><code class="n">Date</code><code class="o">)</code><code class="n">birthdayField</code><code class="o">.</code><code class="na">getValue</code><code class="o">();</code></pre><p><code class="literal">JFormattedTextField</code> validates its text when the user attempts to shift focus to a new field (either by clicking with the mouse outside of the field or using keyboard navigation). By default, <code class="literal">JFormattedTextField</code> handles invalid input by simply reverting to the last valid value. If you wish to allow invalid input to remain in the field for further editing, you can set the <code class="literal">setFocusLostBehavior()</code> method with the value <code class="literal">JFormattedTextField.COMMIT</code> (the default is <code class="literal">COMMIT_OR_REVERT</code>). In any case, invalid input does not change the value property retrieved by <code class="literal">getValue()</code>.<a id="I_indexterm18_id799714" class="indexterm"/><a id="I_indexterm18_id799722" class="indexterm"/></p></div><div class="sect2" title="Filtering Input"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-18-SECT-1.3"/>Filtering Input</h2></div></div></div><p><a id="idx11009" class="indexterm"/> <a id="idx11032" class="indexterm"/> <code class="literal">JFormattedTextField</code> does not know about all format types itself; instead, it uses <a id="I_indexterm18_id799766" class="indexterm"/><code class="literal">AbstractFormatter</code> objects that know about particular format types. The <code class="literal">AbstractFormatter</code>s, in turn, provide implementations of two interfaces: <code class="literal">DocumentFilter</code> and <a id="I_indexterm18_id799793" class="indexterm"/><code class="literal">NavigationFilter</code>. A <code class="literal">DocumentFilter</code> attaches to implementations of <code class="literal">Document</code> and allows you to intercept editing commands, modifying them as you wish. A <code class="literal">NavigationFilter</code> can be attached to <code class="literal">JTextComponent</code>s to control the movement of the cursor (as in a mask-formatted field). You can implement your own <code class="literal">AbstractFormatter</code>s for use with <code class="literal">JFormattedTextField</code>, and, more generally, you can use the <code class="literal">DocumentFilter</code> interface to control how documents are edited in any type of text component. For example, you could create a <code class="literal">DocumentFilter</code> that maps characters to uppercase or strange symbols. <code class="literal">DocumentFilter</code> provides a low-level, edit-by-edit means of controlling or mapping user input. We will show an example of this now. In the following section, we discuss how to implement higher-level field <span class="emphasis"><em>validation</em></span> that ensures the correctness of data after it is entered, in the same way that the formatted text field did for us earlier.</p><div class="sect3" title="DocumentFilter"><div class="titlepage"><div><div><h3 class="title"><a id="learnjava3-CHP-18-SECT-1.3.1"/>DocumentFilter</h3></div></div></div><p><a id="idx11008" class="indexterm"/>The following example, <code class="literal">DocFilter</code>, applies a document filter to a <code class="literal">JTextField</code>. Our <code class="literal">DocumentFilter</code> simply maps any input to uppercase. Here is the code:</p><a id="I_18_tt1058"/><pre class="programlisting"> <code class="kn">import</code> <code class="nn">java.text.*</code><code class="o">;</code> <code class="kn">import</code> <code class="nn">javax.swing.*</code><code class="o">;</code> <code class="kn">import</code> <code class="nn">javax.swing.text.*</code><code class="o">;</code> <code class="kd">public</code> <code class="kd">class</code> <code class="nc">DocFilter</code> <code class="o">{</code> <code class="kd">public</code> <code class="kd">static</code> <code class="kt">void</code> <code class="nf">main</code><code class="o">(</code> <code class="n">String</code><code class="o">[]</code> <code class="n">args</code> <code class="o">)</code> <code class="kd">throws</code> <code class="n">Exception</code> <code class="o">{</code> <code class="n">JTextField</code> <code class="n">field</code> <code class="o">=</code> <code class="k">new</code> <code class="n">JTextField</code><code class="o">(</code><code class="mi">30</code><code class="o">);</code> <code class="o">((</code><code class="n">AbstractDocument</code><code class="o">)(</code><code class="n">field</code><code class="o">.</code><code class="na">getDocument</code><code class="o">())).</code><code class="na">setDocumentFilter</code><code class="o">(</code> <code class="k">new</code> <code class="nf">DocumentFilter</code><code class="o">()</code> <code class="o">{</code> <code class="kd">public</code> <code class="kt">void</code> <code class="nf">insertString</code><code class="o">(</code> <code class="n">FilterBypass</code> <code class="n">fb</code><code class="o">,</code> <code class="kt">int</code> <code class="n">offset</code><code class="o">,</code> <code class="n">String</code> <code class="n">string</code><code class="o">,</code> <code class="n">AttributeSet</code> <code class="n">attr</code><code class="o">)</code> <code class="kd">throws</code> <code class="n">BadLocationException</code> <code class="o">{</code> <code class="n">fb</code><code class="o">.</code><code class="na">insertString</code><code class="o">(</code> <code class="n">offset</code><code class="o">,</code> <code class="n">string</code><code class="o">.</code><code class="na">toUpperCase</code><code class="o">(),</code> <code class="n">attr</code> <code class="o">);</code> <code class="o">}</code> <code class="kd">public</code> <code class="kt">void</code> <code class="nf">replace</code><code class="o">(</code> <code class="n">FilterBypass</code> <code class="n">fb</code><code class="o">,</code> <code class="kt">int</code> <code class="n">offset</code><code class="o">,</code> <code class="kt">int</code> <code class="n">length</code><code class="o">,</code> <code class="n">String</code> <code class="n">string</code><code class="o">,</code> <code class="n">AttributeSet</code> <code class="n">attr</code><code class="o">)</code> <code class="kd">throws</code> <code class="n">BadLocationException</code> <code class="o">{</code> <code class="n">fb</code><code class="o">.</code><code class="na">replace</code><code class="o">(</code> <code class="n">offset</code><code class="o">,</code> <code class="n">length</code><code class="o">,</code> <code class="n">string</code><code class="o">.</code><code class="na">toUpperCase</code><code class="o">(),</code> <code class="n">attr</code> <code class="o">);</code> <code class="o">}</code> <code class="o">}</code> <code class="o">);</code> <code class="n">JFrame</code> <code class="n">frame</code> <code class="o">=</code> <code class="k">new</code> <code class="n">JFrame</code><code class="o">(</code><code class="s">"User Information"</code><code class="o">);</code> <code class="n">frame</code><code class="o">.</code><code class="na">add</code><code class="o">(</code> <code class="n">field</code> <code class="o">);</code> <code class="n">frame</code><code class="o">.</code><code class="na">pack</code><code class="o">();</code> <code class="n">frame</code><code class="o">.</code><code class="na">setDefaultCloseOperation</code><code class="o">(</code> <code class="n">JFrame</code><code class="o">.</code><code class="na">EXIT_ON_CLOSE</code> <code class="o">);</code> <code class="n">frame</code><code class="o">.</code><code class="na">setVisible</code><code class="o">(</code><code class="kc">true</code><code class="o">);</code> <code class="o">}</code> <code class="o">}</code></pre><p>The methods <a id="I_indexterm18_id799923" class="indexterm"/><code class="literal">insertString()</code> and <a id="I_indexterm18_id799934" class="indexterm"/><code class="literal">replace()</code> of the <code class="literal">DocumentFilter</code> are called when text is added to the document or modified. Within them, we have an opportunity to filter the text before passing it on. When we are ready to apply the text, we use the <a id="I_indexterm18_id799954" class="indexterm"/><code class="literal">FilterBypass</code> reference to pass it along. <code class="literal">FilterBypass</code> has the same set of methods, which apply the changes directly to the document. The <code class="literal">DocumentFilter remove()</code> method can also be used to intercept edits to the document that remove characters. One thing to note in our example is that not all <code class="literal">Document</code>s have a <a id="I_indexterm18_id799984" class="indexterm"/><code class="literal">setDocumentFilter()</code> method. Instead, we have to cast our document to an <code class="literal">AbstractDocument</code>. Only document implementations that extend <code class="literal">AbstractDocument</code> accept filters (unless you implement your own). This sad state of affairs is because the Document Filter API was added in Java 1.4, and it was decided that changes could not be made to the original <code class="literal">Document</code> interface.<a id="I_indexterm18_id800014" class="indexterm"/><a id="I_indexterm18_id800021" class="indexterm"/><a id="I_indexterm18_id800028" class="indexterm"/></p></div></div><div class="sect2" title="Validating Data"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-18-SECT-1.4"/>Validating Data</h2></div></div></div><p><a id="idx11039" class="indexterm"/> <a id="idx11042" class="indexterm"/>Low-level input filtering prevents you from doing such things as entering a number where a character should be. In this section, we’re going to talk about high-level validation, which accounts for things like February having only 28 days or a credit card number being for a Visa or MasterCard. Whereas character filtering prevents you from entering incorrect data, field validation happens after data has been entered. Normally, validation occurs when the user tries to change focus and leave the field, either by clicking the mouse or through keyboard navigation. Java 1.4 added the <code class="literal">InputVerifier</code> API, which allows you to validate the contents of a component before focus is transferred. Although we are going to talk about this in the context of text fields, an <a id="I_indexterm18_id800081" class="indexterm"/><code class="literal">InputVerifier</code> can actually be attached to any <code class="literal">JComponent</code> to validate its state in this way.</p><p>The following example creates a pair of text fields. The first allows any value to be entered, while the second accepts only numbers between 0 and 100. When both fields are happy, you can freely move between them. However, when you enter an invalid value in the second field and try to leave, the program just beeps and selects the text. The focus remains trapped until you correct the problem.</p><a id="I_18_tt1059"/><pre class="programlisting"> <code class="kn">import</code> <code class="nn">javax.swing.*</code><code class="o">;</code> <code class="kd">public</code> <code class="kd">class</code> <code class="nc">Validator</code> <code class="o">{</code> <code class="kd">public</code> <code class="kd">static</code> <code class="kt">void</code> <code class="nf">main</code><code class="o">(</code> <code class="n">String</code><code class="o">[]</code> <code class="n">args</code> <code class="o">)</code> <code class="kd">throws</code> <code class="n">Exception</code> <code class="o">{</code> <code class="n">Box</code> <code class="n">form</code> <code class="o">=</code> <code class="n">Box</code><code class="o">.</code><code class="na">createVerticalBox</code><code class="o">();</code> <code class="n">form</code><code class="o">.</code><code class="na">add</code><code class="o">(</code> <code class="k">new</code> <code class="n">JLabel</code><code class="o">(</code><code class="s">"Any Value"</code><code class="o">)</code> <code class="o">);</code> <code class="n">form</code><code class="o">.</code><code class="na">add</code><code class="o">(</code> <code class="k">new</code> <code class="n">JTextField</code><code class="o">(</code><code class="s">"5000"</code><code class="o">)</code> <code class="o">);</code> <code class="n">form</code><code class="o">.</code><code class="na">add</code><code class="o">(</code> <code class="k">new</code> <code class="n">JLabel</code><code class="o">(</code><code class="s">"Only 0-100"</code><code class="o">)</code> <code class="o">);</code> <code class="n">JTextField</code> <code class="n">rangeField</code> <code class="o">=</code> <code class="k">new</code> <code class="n">JTextField</code><code class="o">(</code><code class="s">"50"</code><code class="o">);</code> <code class="n">rangeField</code><code class="o">.</code><code class="na">setInputVerifier</code><code class="o">(</code> <code class="k">new</code> <code class="n">InputVerifier</code><code class="o">()</code> <code class="o">{</code> <code class="kd">public</code> <code class="kt">boolean</code> <code class="nf">verify</code><code class="o">(</code> <code class="n">JComponent</code> <code class="n">comp</code> <code class="o">)</code> <code class="o">{</code> <code class="n">JTextField</code> <code class="n">field</code> <code class="o">=</code> <code class="o">(</code><code class="n">JTextField</code><code class="o">)</code><code class="n">comp</code><code class="o">;</code> <code class="kt">boolean</code> <code class="n">passed</code> <code class="o">=</code> <code class="kc">false</code><code class="o">;</code> <code class="k">try</code> <code class="o">{</code> <code class="kt">int</code> <code class="n">n</code> <code class="o">=</code> <code class="n">Integer</code><code class="o">.</code><code class="na">parseInt</code><code class="o">(</code><code class="n">field</code><code class="o">.</code><code class="na">getText</code><code class="o">());</code> <code class="n">passed</code> <code class="o">=</code> <code class="o">(</code> <code class="mi">0</code> <code class="o">&lt;=</code> <code class="n">n</code> <code class="o">&amp;&amp;</code> <code class="n">n</code> <code class="o">&lt;=</code> <code class="mi">100</code> <code class="o">);</code> <code class="o">}</code> <code class="k">catch</code> <code class="o">(</code><code class="n">NumberFormatException</code> <code class="n">e</code><code class="o">)</code> <code class="o">{</code> <code class="o">}</code> <code class="k">if</code> <code class="o">(</code> <code class="o">!</code><code class="n">passed</code> <code class="o">)</code> <code class="o">{</code> <code class="n">comp</code><code class="o">.</code><code class="na">getToolkit</code><code class="o">().</code><code class="na">beep</code><code class="o">();</code> <code class="n">field</code><code class="o">.</code><code class="na">selectAll</code><code class="o">();</code> <code class="o">}</code> <code class="k">return</code> <code class="n">passed</code><code class="o">;</code> <code class="o">}</code> <code class="o">}</code> <code class="o">);</code> <code class="n">form</code><code class="o">.</code><code class="na">add</code><code class="o">(</code> <code class="n">rangeField</code> <code class="o">);</code> <code class="n">JFrame</code> <code class="n">frame</code> <code class="o">=</code> <code class="k">new</code> <code class="n">JFrame</code><code class="o">(</code><code class="s">"User Information"</code><code class="o">);</code> <code class="n">frame</code><code class="o">.</code><code class="na">add</code><code class="o">(</code><code class="n">form</code><code class="o">);</code> <code class="n">frame</code><code class="o">.</code><code class="na">setDefaultCloseOperation</code><code class="o">(</code> <code class="n">JFrame</code><code class="o">.</code><code class="na">EXIT_ON_CLOSE</code> <code class="o">);</code> <code class="n">frame</code><code class="o">.</code><code class="na">pack</code><code class="o">();</code> <code class="n">frame</code><code class="o">.</code><code class="na">setVisible</code><code class="o">(</code><code class="kc">true</code><code class="o">);</code> <code class="o">}</code> <code class="o">}</code></pre><p>We’ve created an anonymous inner class extending <code class="literal">InputVerifier</code> with this code. The API is very simple; at validation time, our <a id="I_indexterm18_id800133" class="indexterm"/><code class="literal">verify()</code> method is called, and we are passed a reference to the component needing checking. Here we cast to the correct type (we know what we are verifying, of course) and parse the number. If it is out of range, we beep and select the text. We then return <code class="literal">true</code> or <code class="literal">false</code> indicating whether the value passes validation.</p><p>You can use an <code class="literal">InputVerifier</code> in combination with a <code class="literal">JFormattedTextField</code> to both guide user input into the correct format and validate the semantics of what the user entered.<a id="I_indexterm18_id800171" class="indexterm"/><a id="I_indexterm18_id800178" class="indexterm"/></p></div><div class="sect2" title="Say the Magic Word"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-18-SECT-1.5"/>Say the Magic Word</h2></div></div></div><p><a id="idx11017" class="indexterm"/> <a id="idx11036" class="indexterm"/>Before we move on from our discussion of formatted text, we should mention that Swing includes a class just for typing passwords, called <a id="I_indexterm18_id800217" class="indexterm"/><code class="literal">JPasswordField</code>. A <code class="literal">JPasswordField</code> behaves just like a <code class="literal">JTextField</code> (it’s a subclass), except every character typed is echoed as the same, obfuscating character, typically an asterisk. <a class="xref" href="ch18s01.html#learnjava3-CHP-18-FIG-2" title="Figure 18-2. Using a JPasswordField in a dialog">Figure 18-2</a> shows the option dialog example that was presented in <a class="xref" href="ch17.html" title="Chapter 17. Using Swing Components">Chapter 17</a>. The example includes a <code class="literal">JTextField</code> and a <code class="literal">JPasswordField</code>.</p><p>The creation and use of <code class="literal">JPasswordField</code> is basically the same as for <code class="literal">JTextField</code>. If you find asterisks distasteful, you can tell the <code class="literal">JPasswordField</code> to use a different character using the <a id="I_indexterm18_id800281" class="indexterm"/><code class="literal">setEchoChar()</code> method.</p><p>Normally, you would use <code class="literal">getText()</code> to retrieve the text typed into the <code class="literal">JPasswordField</code>. This method, however, is deprecated; you should use <a id="I_indexterm18_id800305" class="indexterm"/><code class="literal">getPassword()</code> instead. The <code class="literal">getPassword()</code> method returns a character array rather than a <code class="literal">String</code> object. This is done because character arrays are a little less vulnerable than <code class="literal">String</code>s to discover by memory-snooping password sniffer programs and they can be erased directly and easily. If you’re not that concerned, you can simply create a new <code class="literal">String</code> from the character array. Note that methods in the Java cryptographic classes accept passwords as character arrays, not strings, so you can pass the results of a <code class="literal">getPassword()</code> call directly to methods in the cryptographic classes without ever creating a <code class="literal">String</code>.<a id="I_indexterm18_id800355" class="indexterm"/><a id="I_indexterm18_id800362" class="indexterm"/></p><div class="figure"><a id="learnjava3-CHP-18-FIG-2"/><div class="figure-contents"><div class="mediaobject"><a id="I_18_tt1060"/><img src="httpatomoreillycomsourceoreillyimages1707668.png" alt="Using a JPasswordField in a dialog"/></div></div><p class="title">Figure 18-2. Using a JPasswordField in a dialog</p></div></div><div class="sect2" title="Sharing a Data Model"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-18-SECT-1.6"/>Sharing a Data Model</h2></div></div></div><p><a id="idx11005" class="indexterm"/> <a id="idx11019" class="indexterm"/> <a id="idx11037" class="indexterm"/>Our next example shows how easy it is to make two or more text components share the same <code class="literal">Document</code>; <a class="xref" href="ch18s01.html#learnjava3-CHP-18-FIG-3" title="Figure 18-3. Three views of the same data model">Figure 18-3</a> shows what the application looks like.</p><div class="figure"><a id="learnjava3-CHP-18-FIG-3"/><div class="figure-contents"><div class="mediaobject"><a id="I_18_tt1061"/><img src="httpatomoreillycomsourceoreillyimages1707669.png" alt="Three views of the same data model"/></div></div><p class="title">Figure 18-3. Three views of the same data model</p></div><p>Anything the user types into any text area is reflected in all of them. All we had to do is make all the text areas use the same data model, like this:</p><a id="I_18_tt1062"/><pre class="programlisting"> <code class="n">JTextArea</code> <code class="n">areaFiftyOne</code> <code class="o">=</code> <code class="k">new</code> <code class="n">JTextArea</code><code class="o">();</code> <code class="n">JTextArea</code> <code class="n">areaFiftyTwo</code> <code class="o">=</code> <code class="k">new</code> <code class="n">JTextArea</code><code class="o">();</code> <code class="n">areaFiftyTwo</code><code class="o">.</code><code class="na">setDocument</code><code class="o">(</code><code class="n">areaFiftyOne</code><code class="o">.</code><code class="na">getDocument</code><code class="o">());</code> <code class="n">JTextArea</code> <code class="n">areaFiftyThree</code> <code class="o">=</code> <code class="k">new</code> <code class="n">JTextArea</code><code class="o">();</code> <code class="n">areaFiftyThree</code><code class="o">.</code><code class="na">setDocument</code><code class="o">(</code><code class="n">areaFiftyOne</code><code class="o">.</code><code class="na">getDocument</code><code class="o">());</code></pre><p>We could just as easily make seven text areas sharing the same document—or seventy. While this example may not look very useful, keep in mind that you can scroll different text areas to different places in the same document. That’s one of the beauties of putting multiple views on the same data; you get to examine different parts of it. Another useful technique is viewing the same data in different ways. You could, for example, view some tabular numerical data as both a spreadsheet and a pie chart. The MVC architecture that Swing uses means that it’s possible to do this in an intelligent way so that if numbers in a spreadsheet are updated, a pie chart that uses the same data is automatically updated, too.</p><p>This example works because, behind the scenes, there are a lot of events flying around. When you type in one of the text areas, the text area receives the keyboard events. It calls methods in the document to update its data. In turn, the document sends events to the other text areas telling them about the updates so that they can correctly display the document’s new data. But don’t worry about any of this; you just tell the text areas to use the same data, and Swing takes care of the rest:</p><a id="I_18_tt1063"/><pre class="programlisting"> <code class="c1">//file: SharedModel.java<