1 /* CompositeType.java -- Type descriptor for CompositeData instances.
2    Copyright (C) 2006, 2007 Free Software Foundation, Inc.
3 
4 This file is part of GNU Classpath.
5 
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10 
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20 
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25 
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37 
38 package javax.management.openmbean;
39 
40 import java.util.Collections;
41 import java.util.Iterator;
42 import java.util.Map;
43 import java.util.Set;
44 import java.util.TreeMap;
45 
46 /**
47  * The open type descriptor for instances of the
48  * {@link CompositeData} class.
49  *
50  * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
51  * @since 1.5
52  */
53 public class CompositeType
54   extends OpenType<CompositeData>
55 {
56 
57   /**
58    * Compatible with JDK 1.5
59    */
60   private static final long serialVersionUID = -5366242454346948798L;
61 
62   /**
63    * A map of item names to their descriptions.
64    */
65   private TreeMap<String,String> nameToDescription;
66 
67   /**
68    * A map of item names to their types.
69    */
70   private TreeMap<String,OpenType<?>> nameToType;
71 
72   /**
73    * The hash code of this instance.
74    */
75   private transient Integer hashCode;
76 
77   /**
78    * The <code>toString()</code> result of this instance.
79    */
80   private transient String string;
81 
82   /**
83    * <p>
84    * Constructs a new {@link CompositeType} instance for the given
85    * type name with the specified field names, descriptions and types.
86    * All parameters, and the elements of the array parameters, must be
87    * non-null and {@link java.lang.String} values must be something other
88    * than the empty string.  The arrays must be non-empty, and be of
89    * equal size.
90    * </p>
91    * <p>
92    * The result of <code>CompositeData.class.getName()</code> is adopted
93    * as the class name (see {@link OpenType}) and changes to the array
94    * elements following construction of the {@link CompositeType} instance
95    * will <strong>not</strong> affect the values used by the instance.
96    * The field names are sorted in to ascending alphanumeric order internally,
97    * and so ordering can not be used to differentiate between two instances.
98    * </p>
99    *
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    */
CompositeType(String name, String desc, String[] names, String[] descs, OpenType<?>[] types)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    */
containsKey(String name)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    */
equals(Object obj)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    */
getDescription(String name)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    */
getType(String name)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    */
hashCode()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    */
isValue(Object obj)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    */
keySet()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    */
toString()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 }
321