001 /* CompositeType.java -- Type descriptor for CompositeData instances.
002 Copyright (C) 2006, 2007 Free Software Foundation, Inc.
003
004 This file is part of GNU Classpath.
005
006 GNU Classpath is free software; you can redistribute it and/or modify
007 it under the terms of the GNU General Public License as published by
008 the Free Software Foundation; either version 2, or (at your option)
009 any later version.
010
011 GNU Classpath is distributed in the hope that it will be useful, but
012 WITHOUT ANY WARRANTY; without even the implied warranty of
013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014 General Public License for more details.
015
016 You should have received a copy of the GNU General Public License
017 along with GNU Classpath; see the file COPYING. If not, write to the
018 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019 02110-1301 USA.
020
021 Linking this library statically or dynamically with other modules is
022 making a combined work based on this library. Thus, the terms and
023 conditions of the GNU General Public License cover the whole
024 combination.
025
026 As a special exception, the copyright holders of this library give you
027 permission to link this library with independent modules to produce an
028 executable, regardless of the license terms of these independent
029 modules, and to copy and distribute the resulting executable under
030 terms of your choice, provided that you also meet, for each linked
031 independent module, the terms and conditions of the license of that
032 module. An independent module is a module which is not derived from
033 or based on this library. If you modify this library, you may extend
034 this exception to your version of the library, but you are not
035 obligated to do so. If you do not wish to do so, delete this
036 exception statement from your version. */
037
038 package javax.management.openmbean;
039
040 import java.util.Collections;
041 import java.util.Iterator;
042 import java.util.Map;
043 import java.util.Set;
044 import java.util.TreeMap;
045
046 /**
047 * The open type descriptor for instances of the
048 * {@link CompositeData} class.
049 *
050 * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
051 * @since 1.5
052 */
053 public class CompositeType
054 extends OpenType<CompositeData>
055 {
056
057 /**
058 * Compatible with JDK 1.5
059 */
060 private static final long serialVersionUID = -5366242454346948798L;
061
062 /**
063 * A map of item names to their descriptions.
064 */
065 private TreeMap<String,String> nameToDescription;
066
067 /**
068 * A map of item names to their types.
069 */
070 private TreeMap<String,OpenType<?>> nameToType;
071
072 /**
073 * The hash code of this instance.
074 */
075 private transient Integer hashCode;
076
077 /**
078 * The <code>toString()</code> result of this instance.
079 */
080 private transient String string;
081
082 /**
083 * <p>
084 * Constructs a new {@link CompositeType} instance for the given
085 * type name with the specified field names, descriptions and types.
086 * All parameters, and the elements of the array parameters, must be
087 * non-null and {@link java.lang.String} values must be something other
088 * than the empty string. The arrays must be non-empty, and be of
089 * equal size.
090 * </p>
091 * <p>
092 * The result of <code>CompositeData.class.getName()</code> is adopted
093 * as the class name (see {@link OpenType}) and changes to the array
094 * elements following construction of the {@link CompositeType} instance
095 * will <strong>not</strong> affect the values used by the instance.
096 * The field names are sorted in to ascending alphanumeric order internally,
097 * and so ordering can not be used to differentiate between two instances.
098 * </p>
099 *
100 * @param name the name of this composite type.
101 * @param desc a description of this composite type.
102 * @param names the names of each field within the composite type.
103 * @param descs the descriptions of each field within the composite type.
104 * @param types the types of each field within the composite type.
105 * @throws IllegalArgumentException if any validity constraint listed above
106 * is broken.
107 * @throws OpenDataException if duplicate item names are provided. Item names
108 * are case-sensitive, but whitespace is removed
109 * before comparison.
110 */
111 public CompositeType(String name, String desc, String[] names,
112 String[] descs, OpenType<?>[] types)
113 throws OpenDataException
114 {
115 super(CompositeData.class.getName(), name, desc);
116 if (names.length == 0
117 || names.length != descs.length
118 || names.length != types.length)
119 throw new IllegalArgumentException("Arrays must be non-empty " +
120 "and of equal size.");
121 nameToDescription = new TreeMap<String,String>();
122 for (int a = 0; a < names.length; ++a)
123 {
124 if (names[a] == null)
125 throw new IllegalArgumentException("Name " + a + " is null.");
126 if (descs[a] == null)
127 throw new IllegalArgumentException("Description " + a +
128 " is null.");
129 String fieldName = names[a].trim();
130 if (fieldName.length() == 0)
131 throw new IllegalArgumentException("Name " + a + " is " +
132 "the empty string.");
133 if (descs[a].length() == 0)
134 throw new IllegalArgumentException("Description " + a + " is " +
135 "the empty string.");
136 if (nameToDescription.containsKey(fieldName))
137 throw new OpenDataException(fieldName + " appears more " +
138 "than once.");
139 nameToDescription.put(fieldName, descs[a]);
140 }
141 nameToType = new TreeMap<String,OpenType<?>>();
142 for (int a = 0; a < names.length; ++a)
143 nameToType.put(names[a].trim(), types[a]);
144 }
145
146 /**
147 * Returns true if this composite data type has a field
148 * with the given name.
149 *
150 * @param name the name of the field to check for.
151 * @return true if a field of that name exists.
152 */
153 public boolean containsKey(String name)
154 {
155 return nameToDescription.containsKey(name);
156 }
157
158 /**
159 * <p>
160 * Compares this composite data type with another object
161 * for equality. The objects are judged to be equal if:
162 * </p>
163 * <ul>
164 * <li><code>obj</code> is not null.</li>
165 * <li><code>obj</code> is an instance of
166 * {@link CompositeType}.</li>
167 * <li>The type names are equal.</li>
168 * <li>The fields and their types match.</li>
169 * </ul>
170 *
171 * @param obj the object to compare with.
172 * @return true if the conditions above hold.
173 */
174 public boolean equals(Object obj)
175 {
176 if (!(obj instanceof CompositeType))
177 return false;
178 CompositeType ctype = (CompositeType) obj;
179 if (!(ctype.getTypeName().equals(getTypeName())))
180 return false;
181 Set<String> keys = keySet();
182 if (!(ctype.keySet().equals(keys)))
183 return false;
184 for (String key : keys)
185 {
186 if (!(ctype.getType(key).equals(getType(key))))
187 return false;
188 }
189 return true;
190 }
191
192 /**
193 * Returns the description for the given field name,
194 * or <code>null</code> if the field name does not
195 * exist within this composite data type.
196 *
197 * @param name the name of the field whose description
198 * should be returned.
199 * @return the description, or <code>null</code> if the
200 * field doesn't exist.
201 */
202 public String getDescription(String name)
203 {
204 return nameToDescription.get(name);
205 }
206
207 /**
208 * Returns the type for the given field name,
209 * or <code>null</code> if the field name does not
210 * exist within this composite data type.
211 *
212 * @param name the name of the field whose type
213 * should be returned.
214 * @return the type, or <code>null</code> if the
215 * field doesn't exist.
216 */
217 public OpenType<?> getType(String name)
218 {
219 return nameToType.get(name);
220 }
221
222 /**
223 * <p>
224 * Returns the hash code of the composite data type.
225 * This is computed as the sum of the hash codes of
226 * each field name and its type, together with the hash
227 * code of the type name. These are the same elements
228 * of the type that are compared as part of the
229 * {@link #equals(java.lang.Object)} method, thus ensuring
230 * that the hashcode is compatible with the equality
231 * test.
232 * </p>
233 * <p>
234 * As instances of this class are immutable, the hash code
235 * is computed just once for each instance and reused
236 * throughout its life.
237 * </p>
238 *
239 * @return the hash code of this instance.
240 */
241 public int hashCode()
242 {
243 if (hashCode == null)
244 {
245 int elementTotal = 0;
246 for (Map.Entry<String,OpenType<?>> entry : nameToType.entrySet())
247 {
248 elementTotal += (entry.getKey().hashCode() +
249 entry.getValue().hashCode());
250 }
251 hashCode = Integer.valueOf(elementTotal
252 + getTypeName().hashCode());
253 }
254 return hashCode.intValue();
255 }
256
257 /**
258 * Returns true if the specified object is a member of this
259 * composite type. The object is judged to be so if it is
260 * an instance of {@link CompositeData} with an equivalent
261 * type, according to the definition of
262 * {@link #equals(java.lang.Object)} for {@link CompositeType}.
263 *
264 * @param obj the object to test for membership.
265 * @return true if the object is a member of this type.
266 */
267 public boolean isValue(Object obj)
268 {
269 if (obj instanceof CompositeData)
270 {
271 CompositeData data = (CompositeData) obj;
272 return equals(data.getCompositeType());
273 }
274 return false;
275 }
276
277 /**
278 * Returns an unmodifiable {@link java.util.Set}-based
279 * view of the field names that form part of this
280 * {@link CompositeType} instance. The names are stored
281 * in ascending alphanumeric order.
282 *
283 * @return a unmodifiable set containing the field
284 * name {@link java.lang.String}s.
285 */
286 public Set<String> keySet()
287 {
288 return Collections.unmodifiableSet(nameToDescription.keySet());
289 }
290
291 /**
292 * <p>
293 * Returns a textual representation of this instance. This
294 * is constructed using the class name
295 * (<code>javax.management.openmbean.CompositeType</code>)
296 * and each element of the instance which is relevant to
297 * the definition of {@link equals(java.lang.Object)} and
298 * {@link hashCode()} (i.e. the type name, and the name
299 * and type of each field).
300 * </p>
301 * <p>
302 * As instances of this class are immutable, the return value
303 * is computed just once for each instance and reused
304 * throughout its life.
305 * </p>
306 *
307 * @return a @link{java.lang.String} instance representing
308 * the instance in textual form.
309 */
310 public String toString()
311 {
312 if (string == null)
313 string = getClass().getName()
314 + "[name=" + getTypeName()
315 + ", fields=" + nameToType
316 + "]";
317 return string;
318 }
319
320 }