1 /*******************************************************************************
2  * Copyright (c) 2005, 2015 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *******************************************************************************/
14 package org.eclipse.core.commands;
15 
16 import java.util.ArrayList;
17 import java.util.Collection;
18 import java.util.Iterator;
19 import java.util.Objects;
20 
21 import org.eclipse.core.commands.common.NamedHandleObject;
22 
23 /**
24  * <p>
25  * A logical group for a set of commands. A command belongs to exactly one
26  * category. The category has no functional effect, but may be used in graphical
27  * tools that want to group the set of commands somehow.
28  * </p>
29  *
30  * @since 3.1
31  */
32 public final class Category extends NamedHandleObject {
33 
34 	/**
35 	 * A collection of objects listening to changes to this category. This
36 	 * collection is <code>null</code> if there are no listeners.
37 	 */
38 	private Collection<ICategoryListener> categoryListeners;
39 
40 	/**
41 	 * Constructs a new instance of <code>Category</code> based on the given
42 	 * identifier. When a category is first constructed, it is undefined.
43 	 * Category should only be constructed by the <code>CommandManager</code>
44 	 * to ensure that identifier remain unique.
45 	 *
46 	 * @param id
47 	 *            The identifier for the category. This value must not be
48 	 *            <code>null</code>, and must be unique amongst all
49 	 *            categories.
50 	 */
Category(final String id)51 	Category(final String id) {
52 		super(id);
53 	}
54 
55 	/**
56 	 * Adds a listener to this category that will be notified when this
57 	 * category's state changes.
58 	 *
59 	 * @param categoryListener
60 	 *            The listener to be added; must not be <code>null</code>.
61 	 */
addCategoryListener( final ICategoryListener categoryListener)62 	public final void addCategoryListener(
63 			final ICategoryListener categoryListener) {
64 		if (categoryListener == null) {
65 			throw new NullPointerException();
66 		}
67 		if (categoryListeners == null) {
68 			categoryListeners = new ArrayList<>();
69 		}
70 		if (!categoryListeners.contains(categoryListener)) {
71 			categoryListeners.add(categoryListener);
72 		}
73 	}
74 
75 	/**
76 	 * <p>
77 	 * Defines this category by giving it a name, and possibly a description as
78 	 * well. The defined property automatically becomes <code>true</code>.
79 	 * </p>
80 	 * <p>
81 	 * Notification is sent to all listeners that something has changed.
82 	 * </p>
83 	 *
84 	 * @param name
85 	 *            The name of this command; must not be <code>null</code>.
86 	 * @param description
87 	 *            The description for this command; may be <code>null</code>.
88 	 */
define(final String name, final String description)89 	public final void define(final String name, final String description) {
90 		if (name == null) {
91 			throw new NullPointerException("The name of a command cannot be null"); //$NON-NLS-1$
92 		}
93 
94 		final boolean definedChanged = !this.defined;
95 		this.defined = true;
96 
97 		final boolean nameChanged = !Objects.equals(this.name, name);
98 		this.name = name;
99 
100 		final boolean descriptionChanged = !Objects.equals(this.description, description);
101 		this.description = description;
102 
103 		fireCategoryChanged(new CategoryEvent(this, definedChanged, descriptionChanged, nameChanged));
104 	}
105 
106 	/**
107 	 * Notifies the listeners for this category that it has changed in some way.
108 	 *
109 	 * @param categoryEvent
110 	 *            The event to send to all of the listener; must not be
111 	 *            <code>null</code>.
112 	 */
fireCategoryChanged(final CategoryEvent categoryEvent)113 	private final void fireCategoryChanged(final CategoryEvent categoryEvent) {
114 		if (categoryEvent == null) {
115 			throw new NullPointerException();
116 		}
117 		if (categoryListeners != null) {
118 			final Iterator<ICategoryListener> listenerItr = categoryListeners.iterator();
119 			while (listenerItr.hasNext()) {
120 				final ICategoryListener listener = listenerItr.next();
121 				listener.categoryChanged(categoryEvent);
122 			}
123 		}
124 	}
125 
126 	/**
127 	 * Removes a listener from this category.
128 	 *
129 	 * @param categoryListener
130 	 *            The listener to be removed; must not be <code>null</code>.
131 	 *
132 	 */
removeCategoryListener( final ICategoryListener categoryListener)133 	public final void removeCategoryListener(
134 			final ICategoryListener categoryListener) {
135 		if (categoryListener == null) {
136 			throw new NullPointerException();
137 		}
138 
139 		if (categoryListeners != null) {
140 			categoryListeners.remove(categoryListener);
141 		}
142 	}
143 
144 	@Override
toString()145 	public String toString() {
146 		if (string == null) {
147 			final StringBuilder stringBuffer = new StringBuilder("Category("); //$NON-NLS-1$
148 			stringBuffer.append(id);
149 			stringBuffer.append(',');
150 			stringBuffer.append(name);
151 			stringBuffer.append(',');
152 			stringBuffer.append(description);
153 			stringBuffer.append(',');
154 			stringBuffer.append(defined);
155 			stringBuffer.append(')');
156 			string = stringBuffer.toString();
157 		}
158 		return string;
159 	}
160 
161 	@Override
undefine()162 	public void undefine() {
163 		string = null;
164 
165 		final boolean definedChanged = defined;
166 		defined = false;
167 
168 		final boolean nameChanged = name != null;
169 		name = null;
170 
171 		final boolean descriptionChanged = description != null;
172 		description = null;
173 
174 		fireCategoryChanged(new CategoryEvent(this, definedChanged, descriptionChanged, nameChanged));
175 	}
176 
177 }
178