001    /*
002     * Copyright 2002-2004 the original author or authors.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005     * use this file except in compliance with the License. You may obtain a copy of
006     * the License at
007     *
008     * http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012     * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013     * License for the specific language governing permissions and limitations under
014     * the License.
015     */
016    package org.springframework.richclient.command.config;
017    
018    import javax.swing.AbstractButton;
019    import javax.swing.JMenu;
020    import javax.swing.JMenuItem;
021    import javax.swing.KeyStroke;
022    
023    import org.springframework.core.style.ToStringCreator;
024    import org.springframework.richclient.core.LabelInfo;
025    import org.springframework.richclient.factory.ButtonConfigurer;
026    import org.springframework.richclient.util.Assert;
027    import org.springframework.util.ObjectUtils;
028    import org.springframework.util.StringUtils;
029    
030    
031    /**
032     * An immutable parameter object consisting of the text, mnemonic character, mnemonic character
033     * index and keystroke accelerator that may be associated with a labeled command button.
034     *
035     * <p>
036     * This class also acts as a factory for creating instances of itself based on a string descriptor.
037     * The syntax used for this descriptor is described in the javadoc for the {@link #valueOf(String)}
038     * method.
039     *
040     *
041     * @author Keith Donald
042     * @author Kevin Stembridge
043     *
044     * @see LabelInfo
045     * @see KeyStroke
046     */
047    public final class CommandButtonLabelInfo implements ButtonConfigurer {
048    
049        /** A default instance to be used for command buttons with no label information. */
050        public static final CommandButtonLabelInfo BLANK_BUTTON_LABEL = new CommandButtonLabelInfo("");
051    
052        private final LabelInfo labelInfo;
053    
054        private final KeyStroke accelerator;
055    
056        /**
057         * Return an instance of this class, created by parsing the information in the given label
058         * descriptor string. The expected format of this descriptor is the same as that used by
059         * the {@link LabelInfo} class, with the following additions:
060         *
061         * <ul>
062         * <li>The @ symbol is an escapable character.</li>
063         * <li>Everything after the first unescaped @ symbol will be treated as the textual representation
064         * of the keystroke accelerator.</li>
065         * </ul>
066         *
067         * The expected format of the keystroke accelerator string is as described in the javadocs for the
068         * {@link KeyStroke#getKeyStroke(String)} method.
069         *
070         *
071         * @param labelDescriptor The label descriptor. May be null or empty, in which case, a default
072         * blank label info will be returned.
073         * @return A CommandButtonLabelInfo instance, never null.
074         *
075         * @throws IllegalArgumentException if {@code labelDescriptor} contains invalid syntax.
076         *
077         * @see LabelInfo
078         * @see KeyStroke
079         */
080        public static CommandButtonLabelInfo valueOf(String labelDescriptor) {
081    
082            if (!StringUtils.hasText(labelDescriptor)) {
083                return BLANK_BUTTON_LABEL;
084            }
085    
086            StringBuffer labelInfoBuffer = new StringBuffer();
087            char currentChar;
088            KeyStroke keyStroke = null;
089    
090            for (int i = 0, textCharsIndex = 0; i < labelDescriptor.length(); i++, textCharsIndex++) {
091                currentChar = labelDescriptor.charAt(i);
092    
093                if (currentChar == '\\') {
094                    int nextCharIndex = i + 1;
095                    //if this backslash is escaping an @ symbol, we remove the backslash and
096                    //continue with the next char
097                    if (nextCharIndex < labelDescriptor.length()) {
098                        char nextChar = labelDescriptor.charAt(nextCharIndex);
099    
100                        if (nextChar == '@') {
101                            labelInfoBuffer.append(nextChar);
102                            i++;
103                        } else {
104                            labelInfoBuffer.append(currentChar);
105                            labelInfoBuffer.append(nextChar);
106                            i++;
107                        }
108    
109                    }
110    
111                }
112                else if (currentChar == '@') {
113                    //we've found the accelerator indicator
114    
115                    if (i + 1 == labelDescriptor.length()) {
116                        throw new IllegalArgumentException(
117                                "The label descriptor ["
118                                + labelDescriptor
119                                + "] does not specify a valid accelerator after the last "
120                                + "non-espaced @ symbol.");
121                    }
122    
123                    String acceleratorStr = labelDescriptor.substring(i + 1);
124                    keyStroke = KeyStroke.getKeyStroke(acceleratorStr);
125    
126                    if (keyStroke == null) {
127                        throw new IllegalArgumentException(
128                                "The keystroke accelerator string ["
129                                + acceleratorStr
130                                + "] from the label descriptor ["
131                                + labelDescriptor
132                                + "] is not a valid keystroke format.");
133                    }
134    
135                    break;
136    
137                }
138                else {
139                    labelInfoBuffer.append(currentChar);
140                }
141    
142            }
143    
144            LabelInfo info = LabelInfo.valueOf(labelInfoBuffer.toString());
145    
146            return new CommandButtonLabelInfo(info, keyStroke);
147    
148        }
149    
150        /**
151         * Creates a new {@code CommandButtonLabelInfo} that will display the given text on its label.
152         * There will be no associated mnemonic character and no keystroke accelerator.
153         *
154         * @param text The label text to be displayed. Must not be null.
155         *
156         * @throws IllegalArgumentException if {@code text} is null.
157         */
158        public CommandButtonLabelInfo(String text) {
159            this(new LabelInfo(text), null);
160        }
161    
162        /**
163         * Creates a new {@code CommandButtonLabelInfo} with the given label information and keystroke
164         * accelerator.
165         *
166         * @param labelInfo The label information. Must not be null.
167         * @param accelerator The keystroke accelerator. May be null.
168         *
169         * @throws IllegalArgumentException if {@code labelInfo} is null.
170         */
171        public CommandButtonLabelInfo(LabelInfo labelInfo, KeyStroke accelerator) {
172            Assert.required(labelInfo, "labelInfo");
173            this.labelInfo = labelInfo;
174            this.accelerator = accelerator;
175        }
176    
177        /**
178         * Returns the displayable text.
179         *
180         * @return The label text. Maybe an empty string but never null.
181         */
182        public String getText() {
183            return this.labelInfo.getText();
184        }
185    
186        /**
187         * Returns the mnemonic for the label.
188         *
189         * @return The mnemonic for the label.
190         */
191        public int getMnemonic() {
192            return this.labelInfo.getMnemonic();
193        }
194    
195        /**
196         * Returns the zero-based index for the mnemonic character within the label text.
197         *
198         * @return The mnemonic index or -1 to indicate that there is no associated mnemonic character.
199         */
200        public int getMnemonicIndex() {
201            return this.labelInfo.getMnemonicIndex();
202        }
203    
204        /**
205         * Returns the keystroke accelerator for the label.
206         *
207         * @return The keystroke accelerator, or null.
208         */
209        public KeyStroke getAccelerator() {
210            return this.accelerator;
211        }
212    
213        /**
214         * {@inheritDoc}
215         */
216        public int hashCode() {
217            return this.labelInfo.hashCode() + (this.accelerator != null ? this.accelerator.hashCode() : 0);
218        }
219    
220        /**
221         * {@inheritDoc}
222         */
223        public boolean equals(Object obj) {
224    
225            if (obj == this) {
226                return true;
227            }
228    
229            if (!(obj instanceof CommandButtonLabelInfo)) {
230                return false;
231            }
232    
233            CommandButtonLabelInfo other = (CommandButtonLabelInfo)obj;
234    
235            if (!this.labelInfo.equals(other.labelInfo)) {
236                return false;
237            }
238    
239            if (!ObjectUtils.nullSafeEquals(this.accelerator, other.accelerator)) {
240                return false;
241            }
242    
243            return true;
244    
245        }
246    
247        /**
248         * Configures an existing button appropriately based on this label info's
249         * properties.
250         *
251         * @param button
252         */
253        public AbstractButton configure(AbstractButton button) {
254            Assert.notNull(button);
255            button.setText(this.labelInfo.getText());
256            button.setMnemonic(this.labelInfo.getMnemonic());
257            button.setDisplayedMnemonicIndex(this.labelInfo.getMnemonicIndex());
258            configureAccelerator(button, getAccelerator());
259            return button;
260        }
261    
262        /**
263         * Sets the given keystroke accelerator on the given button.
264         *
265         * @param button The button. May be null.
266         * @param keystrokeAccelerator The accelerator. May be null.
267         */
268        protected void configureAccelerator(AbstractButton button, KeyStroke keystrokeAccelerator) {
269            if ((button instanceof JMenuItem) && !(button instanceof JMenu)) {
270                ((JMenuItem)button).setAccelerator(keystrokeAccelerator);
271            }
272        }
273    
274        /**
275         * {@inheritDoc}
276         */
277        public String toString() {
278            return new ToStringCreator(this)
279                    .append("labelInfo", this.labelInfo)
280                    .append("accelerator", this.accelerator)
281                    .toString();
282        }
283    
284    }