001package jmri;
002
003import java.beans.PropertyChangeEvent;
004import java.beans.PropertyChangeListener;
005import java.beans.PropertyVetoException;
006import java.util.ArrayList;
007import java.util.List;
008import java.util.Locale;
009import java.util.Objects;
010
011import javax.annotation.CheckForNull;
012import javax.annotation.CheckReturnValue;
013import javax.annotation.Nonnull;
014
015import jmri.beans.PropertyChangeProvider;
016
017/**
018 * Provides common services for classes representing objects on the layout, and
019 * allows a common form of access by their Managers.
020 * <p>
021 * Each object has two types of names:
022 * <p>
023 * The "system" name is provided by the system-specific implementations, and
024 * provides a unique mapping to the layout control system (for example LocoNet
025 * or NCE) and address within that system. It must be present and unique across
026 * the JMRI instance. Two beans are identical if they have the same system name;
027 * if not, not.
028 * <p>
029 * The "user" name is optional. It's free form text except for two restrictions:
030 * <ul>
031 * <li>It can't be the empty string "". (A non-existant user name is coded as a
032 * null)
033 * <li>And eventually, we may insist on normalizing user names to a specific
034 * form, e.g. remove leading and trailing white space; see the
035 * {@link #normalizeUserName(java.lang.String)} method
036 * </ul>
037 * <p>
038 * Each of these two names must be unique for every NamedBean of the same type
039 * on the layout and a single NamedBean cannot have a user name that is the same
040 * as the system name of another NamedBean of the same type. (The complex
041 * wording is saying that a single NamedBean object is allowed to have its
042 * system name and user name be the same, but that's the only non-uniqueness
043 * that's allowed within a specific type). Note that the uniqueness restrictions
044 * are currently not completely enforced, only warned about; a future version of
045 * JMRI will enforce this restriction.
046 * <p>
047 * For more information, see the
048 * <a href="http://jmri.org/help/en/html/doc/Technical/Names.shtml">Names and
049 * Naming</a> page in the
050 * <a href="http://jmri.org/help/en/html/doc/Technical/index.shtml">Technical
051 * Info</a> pages.
052 * <hr>
053 * This file is part of JMRI.
054 * <p>
055 * JMRI is free software; you can redistribute it and/or modify it under the
056 * terms of version 2 of the GNU General Public License as published by the Free
057 * Software Foundation. See the "COPYING" file for a copy of this license.
058 * <p>
059 * JMRI is distributed in the hope that it will be useful, but WITHOUT ANY
060 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
061 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
062 *
063 * @author Bob Jacobsen Copyright (C) 2001, 2002, 2003, 2004
064 * @see jmri.Manager
065 */
066public interface NamedBean extends Comparable<NamedBean>, PropertyChangeProvider {
067
068    /**
069     * Constant representing an "unknown" state, indicating that the object's
070     * state is not necessarily that of the actual layout hardware. This is the
071     * initial state of a newly created object before communication with the
072     * layout.
073     */
074    public static final int UNKNOWN = 0x01;
075
076    /**
077     * Constant representing an "inconsistent" state, indicating that some
078     * inconsistency has been detected in the hardware readback.
079     */
080    public static final int INCONSISTENT = 0x08;
081
082    /**
083     * Format used for {@link #getDisplayName(DisplayOptions)} when displaying
084     * the user name and system name without quoation marks around the user
085     * name.
086     */
087    public static final String DISPLAY_NAME_FORMAT = "%s (%s)";
088
089    /**
090     * Format used for {@link #getDisplayName(DisplayOptions)} when displaying
091     * the user name and system name with quoation marks around the user name.
092     */
093    public static final String QUOTED_NAME_FORMAT = "\"%s\" (%s)";
094
095    /**
096     * Property of changed state.
097     */
098    public static final String PROPERTY_STATE = "state";
099
100    /**
101     * User's identification for the item. Bound parameter so manager(s) can
102     * listen to changes. Any given user name must be unique within the layout.
103     * Must not match the system name.
104     *
105     * @return null if not set
106     */
107    @CheckReturnValue
108    @CheckForNull
109    public String getUserName();
110
111    /**
112     * Set the user name, normalizing it if needed.
113     *
114     * @param s the new user name
115     * @throws jmri.NamedBean.BadUserNameException if the user name can not be
116     *                                                 normalized
117     */
118    public void setUserName(@CheckForNull String s) throws BadUserNameException;
119
120    /**
121     * Get a system-specific name. This encodes the hardware addressing
122     * information. Any given system name must be unique within the layout.
123     *
124     * @return the system-specific name
125     */
126    @CheckReturnValue
127    @Nonnull
128    public String getSystemName();
129
130    /**
131     * Display the system-specific name.
132     * <p>Note that this is a firm contract:  toString() in
133     * all implementing classes must return the system name
134     * followed by optional additional information.
135     * Using code can assume that the result of toString() will always be 
136     * or start with the system name followed by some kind of separator character.
137     *
138     * @return the system-specific name
139     */
140    @Nonnull
141    @Override
142    public String toString();
143
144    /**
145     * Get user name if it exists, otherwise return System name.
146     *
147     * @return the user name or system-specific name
148     */
149    @CheckReturnValue
150    @Nonnull
151    public default String getDisplayName() {
152        return getDisplayName(DisplayOptions.DISPLAYNAME);
153    }
154
155    /**
156     * Get the name to display, formatted per {@link NamedBean.DisplayOptions}.
157     *
158     * @param options the DisplayOptions to use
159     * @return the display name formatted per options
160     */
161    @CheckReturnValue
162    @Nonnull
163    public default String getDisplayName(DisplayOptions options) {
164        String userName = getUserName();
165        String systemName = getSystemName();
166        // since there are two undisplayable states for the user name,
167        // empty or null, if user name is empty, make it null to avoid
168        // repeatedly checking for both those states later
169        if (userName != null && userName.isEmpty()) {
170            userName = null;
171        }
172        switch (options) {
173            case USERNAME_SYSTEMNAME:
174                return userName != null ? String.format(DISPLAY_NAME_FORMAT, userName, systemName) : systemName;
175            case QUOTED_USERNAME_SYSTEMNAME:
176                return userName != null ? String.format(QUOTED_NAME_FORMAT, userName, systemName) : getDisplayName(DisplayOptions.QUOTED_SYSTEMNAME);
177            case SYSTEMNAME:
178                return systemName;
179            case QUOTED_SYSTEMNAME:
180                return String.format("\"%s\"", systemName);
181            case QUOTED_USERNAME:
182            case QUOTED_DISPLAYNAME:
183                return String.format("\"%s\"", userName != null ? userName : systemName);
184            case USERNAME:
185            case DISPLAYNAME:
186            default:
187                return userName != null ? userName : systemName;
188        }
189    }
190
191    /**
192     * Get a fully formatted display that includes the SystemName and, if set,
193     * the UserName.
194     * <p>
195     * This is the same as calling {@link #getDisplayName(DisplayOptions)} with
196     * the parameter {@link DisplayOptions#USERNAME_SYSTEMNAME}.
197     *
198     * @return {@code UserName (SystemName)} or {@code SystemName} if the
199     *         UserName is null, empty, or matches the SystemName
200     * @deprecated since 4.17.2; use {@link #getDisplayName(DisplayOptions)}
201     *             with {@link DisplayOptions#USERNAME_SYSTEMNAME} instead
202     */
203    @CheckReturnValue
204    @Nonnull
205    @Deprecated
206    public default String getFullyFormattedDisplayName() {
207        return getDisplayName(DisplayOptions.USERNAME_SYSTEMNAME);
208    }
209
210    /**
211     * Returns a fully formatted display that includes the SystemName and
212     * UserName if set. This uses the format {@value #DISPLAY_NAME_FORMAT}
213     *
214     * @param userNameFirst ignored; retained for compatibility until removed
215     * @return {@code UserName (SystemName)} or {@code SystemName} if the
216     *         UserName is null, empty, or matches the SystemName
217     * @deprecated since 4.17.2; use {@link #getDisplayName(DisplayOptions)}
218     *             with {@link DisplayOptions#USERNAME_SYSTEMNAME} instead
219     */
220    @CheckReturnValue
221    @Nonnull
222    @Deprecated
223    public default String getFullyFormattedDisplayName(boolean userNameFirst) {
224        return getDisplayName(DisplayOptions.USERNAME_SYSTEMNAME);
225    }
226
227    /**
228     * Request a call-back when a bound property changes. Bound properties are
229     * the known state, commanded state, user and system names.
230     *
231     * @param listener    The listener. This may change in the future to be a
232     *                        subclass of NamedProprtyChangeListener that
233     *                        carries the name and listenerRef values internally
234     * @param name        The name (either system or user) that the listener
235     *                        uses for this namedBean, this parameter is used to
236     *                        help determine when which listeners should be
237     *                        moved when the username is moved from one bean to
238     *                        another
239     * @param listenerRef A textual reference for the listener, that can be
240     *                        presented to the user when a delete is called
241     */
242    public void addPropertyChangeListener(@Nonnull PropertyChangeListener listener, String name, String listenerRef);
243
244    /**
245     * Request a call-back when a bound property changes. Bound properties are
246     * the known state, commanded state, user and system names.
247     *
248     * @param propertyName The name of the property to listen to
249     * @param listener     The listener. This may change in the future to be a
250     *                         subclass of NamedProprtyChangeListener that
251     *                         carries the name and listenerRef values
252     *                         internally
253     * @param name         The name (either system or user) that the listener
254     *                         uses for this namedBean, this parameter is used
255     *                         to help determine when which listeners should be
256     *                         moved when the username is moved from one bean to
257     *                         another
258     * @param listenerRef  A textual reference for the listener, that can be
259     *                         presented to the user when a delete is called
260     */
261    public void addPropertyChangeListener(@Nonnull String propertyName, @Nonnull PropertyChangeListener listener,
262            String name, String listenerRef);
263
264    public void updateListenerRef(@Nonnull PropertyChangeListener l, String newName);
265
266    public void vetoableChange(@Nonnull PropertyChangeEvent evt) throws PropertyVetoException;
267
268    /**
269     * Get the textual reference for the specific listener
270     *
271     * @param l the listener of interest
272     * @return the textual reference
273     */
274    @CheckReturnValue
275    public String getListenerRef(@Nonnull PropertyChangeListener l);
276
277    /**
278     * Returns a list of all the listeners references
279     *
280     * @return a list of textual references
281     */
282    @CheckReturnValue
283    public ArrayList<String> getListenerRefs();
284
285    /**
286     * Number of current listeners. May return -1 if the information is not
287     * available for some reason.
288     *
289     * @return the number of listeners.
290     */
291    @CheckReturnValue
292    public int getNumPropertyChangeListeners();
293
294    /**
295     * Get a list of all the property change listeners that are registered using
296     * a specific name
297     *
298     * @param name The name (either system or user) that the listener has
299     *                 registered as referencing this namedBean
300     * @return empty list if none
301     */
302    @CheckReturnValue
303    @Nonnull
304    public PropertyChangeListener[] getPropertyChangeListenersByReference(@Nonnull String name);
305
306    /**
307     * Deactivate this object, so that it releases as many resources as possible
308     * and no longer effects others.
309     * <p>
310     * For example, if this object has listeners, after a call to this method it
311     * should no longer notify those listeners. Any native or system-wide
312     * resources it maintains should be released, including threads, files, etc.
313     * <p>
314     * It is an error to invoke any other methods on this object once dispose()
315     * has been called. Note, however, that there is no guarantee about behavior
316     * in that case.
317     * <p>
318     * Afterwards, references to this object may still exist elsewhere,
319     * preventing its garbage collection. But it's formally dead, and shouldn't
320     * be keeping any other objects alive. Therefore, this method should null
321     * out any references to other objects that this NamedBean contained.
322     */
323    public void dispose(); // remove _all_ connections!
324
325    /**
326     * Provide generic access to internal state.
327     * <p>
328     * This generally shouldn't be used by Java code; use the class-specific
329     * form instead (e.g. setCommandedState in Turnout). This is provided to
330     * make scripts access easier to read.
331     *
332     * @param s the state
333     * @throws JmriException general error when setting the state fails
334     */
335    @InvokeOnLayoutThread
336    public void setState(int s) throws JmriException;
337
338    /**
339     * Provide generic access to internal state.
340     * <p>
341     * This generally shouldn't be used by Java code; use the class-specific
342     * form instead (e.g. getCommandedState in Turnout). This is provided to
343     * make scripts easier to read.
344     *
345     * @return the state
346     */
347    @CheckReturnValue
348    public int getState();
349
350    /**
351     * Provide human-readable, localized version of state value.
352     * <p>
353     * This method is intended for use when presenting to a human operator.
354     *
355     * @param state the state to describe
356     * @return the state in localized form
357     */
358    @CheckReturnValue
359    public String describeState(int state);
360
361    /**
362     * Get associated comment text.
363     *
364     * @return the comment or null
365     */
366    @CheckReturnValue
367    @CheckForNull
368    public String getComment();
369
370    /**
371     * Set associated comment text.
372     * <p>
373     * Comments can be any valid text.
374     *
375     * @param comment the comment or null to remove an existing comment
376     */
377    public void setComment(@CheckForNull String comment);
378
379    /**
380     * Get a list of references for the specified bean.
381     *
382     * @param bean The bean to be checked.
383     * @return a list of NamedBeanUsageReports or an empty ArrayList.
384     */
385    default List<NamedBeanUsageReport> getUsageReport(@CheckForNull NamedBean bean) { return (new ArrayList<>()); }
386
387    /**
388     * Attach a key/value pair to the NamedBean, which can be retrieved later.
389     * These are not bound properties as yet, and don't throw events on
390     * modification. Key must not be null.
391     * <p>
392     * Prior to JMRI 4.3, the key was of Object type. It was constrained to
393     * String to make these more like normal Java Beans.
394     *
395     * @param key   the property to set
396     * @param value the value of the property
397     */
398    public void setProperty(@Nonnull String key, Object value);
399
400    /**
401     * Retrieve the value associated with a key. If no value has been set for
402     * that key, returns null.
403     *
404     * @param key the property to get
405     * @return The value of the property or null.
406     */
407    @CheckReturnValue
408    @CheckForNull
409    public Object getProperty(@Nonnull String key);
410
411    /**
412     * Remove the key/value pair against the NamedBean.
413     *
414     * @param key the property to remove
415     */
416    public void removeProperty(@Nonnull String key);
417
418    /**
419     * Retrieve the complete current set of keys.
420     *
421     * @return empty set if none
422     */
423    @CheckReturnValue
424    @Nonnull
425    public java.util.Set<String> getPropertyKeys();
426
427    /**
428     * For instances in the code where we are dealing with just a bean and a
429     * message needs to be passed to the user or in a log.
430     *
431     * @return a string of the bean type, eg Turnout, Sensor etc
432     */
433    @CheckReturnValue
434    @Nonnull
435    public String getBeanType();
436
437    /**
438     * Enforces, and as a user convenience converts to, the standard form for a
439     * user name.
440     * <p>
441     * This implementation just does a trim(), but later versions might e.g. do
442     * more extensive things.
443     *
444     * @param inputName User name to be normalized
445     * @throws BadUserNameException If the inputName can't be converted to
446     *                                  normalized form
447     * @return A user name in standard normalized form or null if inputName was
448     *         null
449     */
450    @CheckReturnValue
451    @CheckForNull
452    static public String normalizeUserName(@CheckForNull String inputName) throws BadUserNameException {
453        String result = inputName;
454        if (result != null) {
455            result = result.trim();
456        }
457        return result;
458    }
459
460    /**
461     * Provide a comparison between the system names of two beans. This provides
462     * a implementation for e.g. {@link java.util.Comparator}. Returns 0 if the
463     * names are the same, -1 if the first argument orders before the second
464     * argument's name, +1 if the first argument's name orders after the second
465     * argument's name. The comparison is alphanumeric on the system prefix,
466     * then alphabetic on the type letter, then system-specific comparison on
467     * the two suffix parts via the {@link #compareSystemNameSuffix} method.
468     *
469     * @param n2 The second NamedBean in the comparison ("this" is the first
470     *               one)
471     * @return -1,0,+1 for ordering if the names are well-formed; may not
472     *         provide proper ordering if the names are not well-formed.
473     */
474    @CheckReturnValue
475    @Override
476    public default int compareTo(NamedBean n2) {
477        Objects.requireNonNull(n2);
478        jmri.util.AlphanumComparator ac = new jmri.util.AlphanumComparator();
479        String o1 = this.getSystemName();
480        String o2 = n2.getSystemName();
481
482        int p1len = Manager.getSystemPrefixLength(o1);
483        int p2len = Manager.getSystemPrefixLength(o2);
484
485        int comp = ac.compare(o1.substring(0, p1len), o2.substring(0, p2len));
486        if (comp != 0)
487            return comp;
488
489        char c1 = o1.charAt(p1len);
490        char c2 = o2.charAt(p2len);
491
492        if (c1 != c2) {
493            return (c1 > c2) ? +1 : -1;
494        } else {
495            return this.compareSystemNameSuffix(o1.substring(p1len + 1), o2.substring(p2len + 1), n2);
496        }
497    }
498
499    /**
500     * Compare the suffix of this NamedBean's name with the suffix of the
501     * argument NamedBean's name for the {@link #compareTo} operation. This is
502     * intended to be a system-specific comparison that understands the various
503     * formats, etc.
504     *
505     * @param suffix1 The suffix for the 1st bean in the comparison
506     * @param suffix2 The suffix for the 2nd bean in the comparison
507     * @param n2      The other (second) NamedBean in the comparison
508     * @return -1,0,+1 for ordering if the names are well-formed; may not
509     *         provide proper ordering if the names are not well-formed.
510     */
511    @CheckReturnValue
512    public int compareSystemNameSuffix(@Nonnull String suffix1, @Nonnull String suffix2, @Nonnull NamedBean n2);
513
514    /**
515     * Parent class for a set of classes that describe if a user name or system
516     * name is a bad name.
517     */
518    public class BadNameException extends IllegalArgumentException {
519
520        private final String localizedMessage;
521
522        /**
523         * Create an exception with no message to the user or for logging.
524         */
525        protected BadNameException() {
526            super();
527            localizedMessage = super.getMessage();
528        }
529
530        /**
531         * Create a localized exception, suitable for display to the user.This
532         * takes the non-localized message followed by the localized message.
533         * <p>
534         * Use {@link #getLocalizedMessage()} to display the message to the
535         * user, and use {@link #getMessage()} to record the message in logs.
536         *
537         * @param logging the English message for logging
538         * @param display the localized message for display
539         */
540        protected BadNameException(String logging, String display) {
541            super(logging);
542            localizedMessage = display;
543        }
544
545        @Override
546        public String getLocalizedMessage() {
547            return localizedMessage;
548        }
549
550    }
551
552    public class BadUserNameException extends BadNameException {
553
554        /**
555         * Create an exception with no message to the user or for logging. Use
556         * only when calling methods likely have alternate mechanism for
557         * allowing user to understand why exception was thrown.
558         */
559        public BadUserNameException() {
560            super();
561        }
562
563        /**
564         * Create a localized exception, suitable for display to the user. This
565         * takes the same arguments as
566         * {@link jmri.Bundle#getMessage(java.util.Locale, java.lang.String, java.lang.Object...)}
567         * as it uses that method to create both the localized and loggable
568         * messages.
569         * <p>
570         * Use {@link #getLocalizedMessage()} to display the message to the
571         * user, and use {@link #getMessage()} to record the message in logs.
572         * <p>
573         * <strong>Note</strong> the message must be accessible by
574         * {@link jmri.Bundle}.
575         *
576         * @param locale  the locale to be used
577         * @param message bundle key to be translated
578         * @param subs    One or more objects to be inserted into the message
579         */
580        public BadUserNameException(Locale locale, String message, Object... subs) {
581            super(Bundle.getMessage(Locale.ENGLISH, message, subs),
582                    Bundle.getMessage(locale, message, subs));
583        }
584
585        /**
586         * Create a localized exception, suitable for display to the user. This
587         * takes the non-localized message followed by the localized message.
588         * <p>
589         * Use {@link #getLocalizedMessage()} to display the message to the
590         * user, and use {@link #getMessage()} to record the message in logs.
591         *
592         * @param logging the English message for logging
593         * @param display the localized message for display
594         */
595        public BadUserNameException(String logging, String display) {
596            super(logging, display);
597        }
598    }
599
600    public class BadSystemNameException extends BadNameException {
601
602        /**
603         * Create an exception with no message to the user or for logging. Use
604         * only when calling methods likely have alternate mechanism for
605         * allowing user to understand why exception was thrown.
606         */
607        public BadSystemNameException() {
608            super();
609        }
610
611        /**
612         * Create a localized exception, suitable for display to the user. This
613         * takes the same arguments as
614         * {@link jmri.Bundle#getMessage(java.util.Locale, java.lang.String, java.lang.Object...)}
615         * as it uses that method to create both the localized and loggable
616         * messages.
617         * <p>
618         * Use {@link #getLocalizedMessage()} to display the message to the
619         * user, and use {@link #getMessage()} to record the message in logs.
620         * <p>
621         * <strong>Note</strong> the message must be accessible by
622         * {@link jmri.Bundle}.
623         *
624         * @param locale  the locale to be used
625         * @param message bundle key to be translated
626         * @param subs    One or more objects to be inserted into the message
627         */
628        public BadSystemNameException(Locale locale, String message, Object... subs) {
629            this(Bundle.getMessage(Locale.ENGLISH, message, subs),
630                    Bundle.getMessage(locale, message, subs));
631        }
632
633        /**
634         * Create a localized exception, suitable for display to the user. This
635         * takes the non-localized message followed by the localized message.
636         * <p>
637         * Use {@link #getLocalizedMessage()} to display the message to the
638         * user, and use {@link #getMessage()} to record the message in logs.
639         *
640         * @param logging the English message for logging
641         * @param display the localized message for display
642         */
643        public BadSystemNameException(String logging, String display) {
644            super(logging, display);
645        }
646    }
647
648    public class DuplicateSystemNameException extends IllegalArgumentException {
649
650        private final String localizedMessage;
651
652        /**
653         * Create an exception with no message to the user or for logging. Use
654         * only when calling methods likely have alternate mechanism for
655         * allowing user to understand why exception was thrown.
656         */
657        public DuplicateSystemNameException() {
658            super();
659            localizedMessage = super.getMessage();
660        }
661
662        /**
663         * Create a exception.
664         *
665         * @param message bundle key to be translated
666         */
667        public DuplicateSystemNameException(String message) {
668            super(message);
669            localizedMessage = super.getMessage();
670        }
671
672        /**
673         * Create a localized exception, suitable for display to the user. This
674         * takes the same arguments as
675         * {@link jmri.Bundle#getMessage(java.util.Locale, java.lang.String, java.lang.Object...)}
676         * as it uses that method to create both the localized and loggable
677         * messages.
678         * <p>
679         * Use {@link #getLocalizedMessage()} to display the message to the
680         * user, and use {@link #getMessage()} to record the message in logs.
681         * <p>
682         * <strong>Note</strong> the message must be accessible by
683         * {@link jmri.Bundle}.
684         *
685         * @param locale  the locale to be used
686         * @param message bundle key to be translated
687         * @param subs    One or more objects to be inserted into the message
688         */
689        public DuplicateSystemNameException(Locale locale, String message, Object... subs) {
690            this(Bundle.getMessage(locale, message, subs),
691                    Bundle.getMessage(locale, message, subs));
692        }
693
694        /**
695         * Create a localized exception, suitable for display to the user. This
696         * takes the non-localized message followed by the localized message.
697         * <p>
698         * Use {@link #getLocalizedMessage()} to display the message to the
699         * user, and use {@link #getMessage()} to record the message in logs.
700         *
701         * @param logging the English message for logging
702         * @param display the localized message for display
703         */
704        public DuplicateSystemNameException(String logging, String display) {
705            super(logging);
706            localizedMessage = display;
707        }
708
709        @Override
710        public String getLocalizedMessage() {
711            return localizedMessage;
712        }
713    }
714
715    /**
716     * Display options for {@link #getDisplayName(DisplayOptions)}. The quoted
717     * forms are intended to be used in sentences and messages, while the
718     * unquoted forms are intended for use in user interface elements like lists
719     * and combo boxes.
720     */
721    public enum DisplayOptions {
722        /**
723         * Display the user name; if the user name is null or empty, display the
724         * system name.
725         */
726        DISPLAYNAME,
727        /**
728         * Display the user name in quotes; if the user name is null or empty,
729         * display the system name in quotes.
730         */
731        QUOTED_DISPLAYNAME,
732        /**
733         * Display the user name; if the user name is null or empty, display the
734         * system name.
735         */
736        USERNAME,
737        /**
738         * Display the user name in quotes; if the user name is null or empty,
739         * display the system name in quotes.
740         */
741        QUOTED_USERNAME,
742        /**
743         * Display the system name. This should be used only when the context
744         * would cause displaying the user name to be more confusing than not or
745         * in text input fields for editing the system name.
746         */
747        SYSTEMNAME,
748        /**
749         * Display the system name in quotes. This should be used only when the
750         * context would cause displaying the user name to be more confusing
751         * than not or in text input fields for editing the system name.
752         */
753        QUOTED_SYSTEMNAME,
754        /**
755         * Display the user name followed by the system name in parenthesis. If
756         * the user name is null or empty, display the system name without
757         * parenthesis.
758         */
759        USERNAME_SYSTEMNAME,
760        /**
761         * Display the user name in quotes followed by the system name in
762         * parenthesis. If the user name is null or empty, display the system
763         * name in quotes.
764         */
765        QUOTED_USERNAME_SYSTEMNAME;
766    }
767
768}