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}