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}