001package jmri; 002 003import java.util.*; 004import java.util.concurrent.CopyOnWriteArrayList; 005 006/** 007 * A category of something. 008 * <P> 009 * Category was created for LogixNG actions and expressions but it can be used 010 * for everything in JMRI that needs "extendable enums". 011 * <P> 012 * This class is intended to be an Enum, but implemented as an abstract class 013 * to allow adding more categories later without needing to change this class. 014 * For example, external programs using JMRI as a lib might want to add their 015 * own categories. 016 * 017 * @author Daniel Bergqvist Copyright 2025 018 */ 019public abstract class Category implements Comparable<Category> { 020 021 /** 022 * Other things. 023 */ 024 public static final Other OTHER = new Other(); 025 026 static { 027 // It's not often any item is added to this list so we use CopyOnWriteArrayList 028 _categories = new CopyOnWriteArrayList<>(); 029 registerCategory(OTHER); 030 } 031 032 /** 033 * Get all the registered Categories 034 * @return a list of categories 035 */ 036 public static List<Category> values() { 037 return Collections.unmodifiableList(_categories); 038 } 039 040 /** 041 * Register a category. 042 * There must not exist any category with either the name or the description 043 * of this category. Otherwise an IllegalArgumentException will be thrown. 044 * @param category the category 045 * @return the new category 046 * @throws IllegalArgumentException if the category already is registered. 047 */ 048 public static Category registerCategory(Category category) 049 throws IllegalArgumentException { 050 for (Category c : _categories) { 051 if (c.equals(category)) { 052 throw new IllegalArgumentException(String.format( 053 "Category '%s' with description '%s' is already registered", 054 category._name, category._description)); 055 } 056 } 057 _categories.add(category); 058 return category; 059 } 060 061 062 private static final List<Category> _categories; 063 064 private final String _name; 065 private final String _description; 066 private final int _order; 067 068 069 protected Category(String name, String description, int order) { 070 _name = name; 071 _description = description; 072 _order = order; 073 } 074 075 public final String name() { 076 return _name; 077 } 078 079 @Override 080 public final String toString() { 081 return _description; 082 } 083 084 public final int order() { 085 return _order; 086 } 087 088 @Override 089 public final boolean equals(Object o) { 090 if (o instanceof Category) { 091 Category c = (Category)o; 092 // We check during initialization that two categories isn't equal. 093 return _name.equals(c._name) || _description.equals(c._description); 094 } 095 return false; 096 } 097 098 @Override 099 public final int hashCode() { 100 return _description.hashCode(); 101 } 102 103 @Override 104 public final int compareTo(Category c) { 105 if (_order < c.order()) return -1; 106 if (_order > c.order()) return 1; 107 return toString().compareTo(c.toString()); 108 } 109 110 111 public static final class Other extends Category { 112 113 public Other() { 114 super("OTHER", Bundle.getMessage("CategoryOther"), Integer.MAX_VALUE); 115 } 116 } 117 118}