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}