001/* CompositeType.java -- Type descriptor for CompositeData instances. 002 Copyright (C) 2006, 2007 Free Software Foundation, Inc. 003 004This file is part of GNU Classpath. 005 006GNU Classpath is free software; you can redistribute it and/or modify 007it under the terms of the GNU General Public License as published by 008the Free Software Foundation; either version 2, or (at your option) 009any later version. 010 011GNU Classpath is distributed in the hope that it will be useful, but 012WITHOUT ANY WARRANTY; without even the implied warranty of 013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014General Public License for more details. 015 016You should have received a copy of the GNU General Public License 017along with GNU Classpath; see the file COPYING. If not, write to the 018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 01902110-1301 USA. 020 021Linking this library statically or dynamically with other modules is 022making a combined work based on this library. Thus, the terms and 023conditions of the GNU General Public License cover the whole 024combination. 025 026As a special exception, the copyright holders of this library give you 027permission to link this library with independent modules to produce an 028executable, regardless of the license terms of these independent 029modules, and to copy and distribute the resulting executable under 030terms of your choice, provided that you also meet, for each linked 031independent module, the terms and conditions of the license of that 032module. An independent module is a module which is not derived from 033or based on this library. If you modify this library, you may extend 034this exception to your version of the library, but you are not 035obligated to do so. If you do not wish to do so, delete this 036exception statement from your version. */ 037 038package javax.management.openmbean; 039 040import java.util.Collections; 041import java.util.Iterator; 042import java.util.Map; 043import java.util.Set; 044import 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 */ 053public 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}