001 /*
002 * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025
026 package javax.management.openmbean;
027
028 // java import
029 //
030 import java.util.ArrayList;
031 import java.util.Collections;
032 import java.util.Iterator;
033 import java.util.List;
034
035 // jmx import
036 //
037
038 /**
039 * The <code>TabularType</code> class is the <i> open type</i> class
040 * whose instances describe the types of {@link TabularData <code>TabularData</code>} values.
041 *
042 * @since 1.5
043 */
044 public class TabularType extends OpenType<TabularData> {
045
046 /* Serial version */
047 static final long serialVersionUID = 6554071860220659261L;
048
049 /**
050 * @serial The composite type of rows
051 */
052 private CompositeType rowType;
053
054 /**
055 * @serial The items used to index each row element, kept in the order the user gave
056 * This is an unmodifiable {@link ArrayList}
057 */
058 private List<String> indexNames;
059
060 private transient Integer myHashCode = null; // As this instance is immutable, these two values
061 private transient String myToString = null; // need only be calculated once.
062
063 /* *** Constructor *** */
064
065 /**
066 * Constructs a <code>TabularType</code> instance, checking for the validity of the given parameters.
067 * The validity constraints are described below for each parameter.
068 * <p>
069 * The Java class name of tabular data values this tabular type represents
070 * (ie the class name returned by the {@link OpenType#getClassName() getClassName} method)
071 * is set to the string value returned by <code>TabularData.class.getName()</code>.
072 * <p>
073 * @param typeName The name given to the tabular type this instance represents; cannot be a null or empty string.
074 * <br>
075 * @param description The human readable description of the tabular type this instance represents;
076 * cannot be a null or empty string.
077 * <br>
078 * @param rowType The type of the row elements of tabular data values described by this tabular type instance;
079 * cannot be null.
080 * <br>
081 * @param indexNames The names of the items the values of which are used to uniquely index each row element in the
082 * tabular data values described by this tabular type instance;
083 * cannot be null or empty. Each element should be an item name defined in <var>rowType</var>
084 * (no null or empty string allowed).
085 * It is important to note that the <b>order</b> of the item names in <var>indexNames</var>
086 * is used by the methods {@link TabularData#get(java.lang.Object[]) <code>get</code>} and
087 * {@link TabularData#remove(java.lang.Object[]) <code>remove</code>} of class
088 * <code>TabularData</code> to match their array of values parameter to items.
089 * <br>
090 * @throws IllegalArgumentException if <var>rowType</var> is null,
091 * or <var>indexNames</var> is a null or empty array,
092 * or an element in <var>indexNames</var> is a null or empty string,
093 * or <var>typeName</var> or <var>description</var> is a null or empty string.
094 * <br>
095 * @throws OpenDataException if an element's value of <var>indexNames</var>
096 * is not an item name defined in <var>rowType</var>.
097 */
098 public TabularType(String typeName, String description,
099 CompositeType rowType, String[] indexNames)
100 throws OpenDataException {
101
102 // Check and initialize state defined by parent.
103 //
104 super (TabularData.class.getName(), typeName, description, false);
105
106 // Check rowType is not null
107 //
108 if (rowType == null) {
109 throw new IllegalArgumentException(
110 "Argument rowType cannot be null.");
111 }
112
113 // Check indexNames is neither null nor empty and does not contain any null element or empty string
114 //
115 checkForNullElement(indexNames, "indexNames");
116 checkForEmptyString(indexNames, "indexNames");
117
118 // Check all indexNames values are valid item names for rowType
119 //
120 for (int i = 0; i < indexNames.length; i++) {
121 if (!rowType.containsKey(indexNames[i])) {
122 throw new OpenDataException(
123 "Argument's element value indexNames["
124 + i
125 + "]=\""
126 + indexNames[i]
127 + "\" is not a valid item name for rowType.");
128 }
129 }
130
131 // initialize rowType
132 //
133 this .rowType = rowType;
134
135 // initialize indexNames (copy content so that subsequent
136 // modifs to the array referenced by the indexNames parameter
137 // have no impact)
138 //
139 List<String> tmpList = new ArrayList<String>(
140 indexNames.length + 1);
141 for (int i = 0; i < indexNames.length; i++) {
142 tmpList.add(indexNames[i]);
143 }
144 this .indexNames = Collections.unmodifiableList(tmpList);
145 }
146
147 /**
148 * Checks that Object[] arg is neither null nor empty (ie length==0)
149 * and that it does not contain any null element.
150 */
151 private static void checkForNullElement(Object[] arg, String argName) {
152 if ((arg == null) || (arg.length == 0)) {
153 throw new IllegalArgumentException("Argument " + argName
154 + "[] cannot be null or empty.");
155 }
156 for (int i = 0; i < arg.length; i++) {
157 if (arg[i] == null) {
158 throw new IllegalArgumentException(
159 "Argument's element " + argName + "[" + i
160 + "] cannot be null.");
161 }
162 }
163 }
164
165 /**
166 * Checks that String[] does not contain any empty (or blank characters only) string.
167 */
168 private static void checkForEmptyString(String[] arg, String argName) {
169 for (int i = 0; i < arg.length; i++) {
170 if (arg[i].trim().equals("")) {
171 throw new IllegalArgumentException(
172 "Argument's element " + argName + "[" + i
173 + "] cannot be an empty string.");
174 }
175 }
176 }
177
178 /* *** Tabular type specific information methods *** */
179
180 /**
181 * Returns the type of the row elements of tabular data values
182 * described by this <code>TabularType</code> instance.
183 *
184 * @return the type of each row.
185 */
186 public CompositeType getRowType() {
187
188 return rowType;
189 }
190
191 /**
192 * <p>Returns, in the same order as was given to this instance's
193 * constructor, an unmodifiable List of the names of the items the
194 * values of which are used to uniquely index each row element of
195 * tabular data values described by this <code>TabularType</code>
196 * instance.</p>
197 *
198 * @return a List of String representing the names of the index
199 * items.
200 *
201 */
202 public List<String> getIndexNames() {
203
204 return indexNames;
205 }
206
207 /**
208 * Tests whether <var>obj</var> is a value which could be
209 * described by this <code>TabularType</code> instance.
210 *
211 * <p>If <var>obj</var> is null or is not an instance of
212 * <code>javax.management.openmbean.TabularData</code>,
213 * <code>isValue</code> returns <code>false</code>.</p>
214 *
215 * <p>If <var>obj</var> is an instance of
216 * <code>javax.management.openmbean.TabularData</code>, say {@code
217 * td}, the result is true if this {@code TabularType} is
218 * <em>assignable from</em> {@link TabularData#getTabularType()
219 * td.getTabularType()}, as defined in {@link
220 * CompositeType#isValue CompositeType.isValue}.</p>
221 *
222 * @param obj the value whose open type is to be tested for
223 * compatibility with this <code>TabularType</code> instance.
224 *
225 * @return <code>true</code> if <var>obj</var> is a value for this
226 * tabular type, <code>false</code> otherwise.
227 */
228 public boolean isValue(Object obj) {
229
230 // if obj is null or not a TabularData, return false
231 //
232 if (!(obj instanceof TabularData))
233 return false;
234
235 // if obj is not a TabularData, return false
236 //
237 TabularData value = (TabularData) obj;
238 TabularType valueType = value.getTabularType();
239 return isAssignableFrom(valueType);
240 }
241
242 @Override
243 boolean isAssignableFrom(OpenType ot) {
244 if (!(ot instanceof TabularType))
245 return false;
246 TabularType tt = (TabularType) ot;
247 if (!getTypeName().equals(tt.getTypeName())
248 || !getIndexNames().equals(tt.getIndexNames()))
249 return false;
250 return getRowType().isAssignableFrom(tt.getRowType());
251 }
252
253 /* *** Methods overriden from class Object *** */
254
255 /**
256 * Compares the specified <code>obj</code> parameter with this <code>TabularType</code> instance for equality.
257 * <p>
258 * Two <code>TabularType</code> instances are equal if and only if all of the following statements are true:
259 * <ul>
260 * <li>their type names are equal</li>
261 * <li>their row types are equal</li>
262 * <li>they use the same index names, in the same order</li>
263 * </ul>
264 * <br>
265 * @param obj the object to be compared for equality with this <code>TabularType</code> instance;
266 * if <var>obj</var> is <code>null</code>, <code>equals</code> returns <code>false</code>.
267 *
268 * @return <code>true</code> if the specified object is equal to this <code>TabularType</code> instance.
269 */
270 public boolean equals(Object obj) {
271
272 // if obj is null, return false
273 //
274 if (obj == null) {
275 return false;
276 }
277
278 // if obj is not a TabularType, return false
279 //
280 TabularType other;
281 try {
282 other = (TabularType) obj;
283 } catch (ClassCastException e) {
284 return false;
285 }
286
287 // Now, really test for equality between this TabularType instance and the other:
288 //
289
290 // their names should be equal
291 if (!this .getTypeName().equals(other.getTypeName())) {
292 return false;
293 }
294
295 // their row types should be equal
296 if (!this .rowType.equals(other.rowType)) {
297 return false;
298 }
299
300 // their index names should be equal and in the same order (ensured by List.equals())
301 if (!this .indexNames.equals(other.indexNames)) {
302 return false;
303 }
304
305 // All tests for equality were successfull
306 //
307 return true;
308 }
309
310 /**
311 * Returns the hash code value for this <code>TabularType</code> instance.
312 * <p>
313 * The hash code of a <code>TabularType</code> instance is the sum of the hash codes
314 * of all elements of information used in <code>equals</code> comparisons
315 * (ie: name, row type, index names).
316 * This ensures that <code> t1.equals(t2) </code> implies that <code> t1.hashCode()==t2.hashCode() </code>
317 * for any two <code>TabularType</code> instances <code>t1</code> and <code>t2</code>,
318 * as required by the general contract of the method
319 * {@link Object#hashCode() Object.hashCode()}.
320 * <p>
321 * As <code>TabularType</code> instances are immutable, the hash code for this instance is calculated once,
322 * on the first call to <code>hashCode</code>, and then the same value is returned for subsequent calls.
323 *
324 * @return the hash code value for this <code>TabularType</code> instance
325 */
326 public int hashCode() {
327
328 // Calculate the hash code value if it has not yet been done (ie 1st call to hashCode())
329 //
330 if (myHashCode == null) {
331 int value = 0;
332 value += this .getTypeName().hashCode();
333 value += this .rowType.hashCode();
334 for (Iterator k = indexNames.iterator(); k.hasNext();) {
335 value += k.next().hashCode();
336 }
337 myHashCode = new Integer(value);
338 }
339
340 // return always the same hash code for this instance (immutable)
341 //
342 return myHashCode.intValue();
343 }
344
345 /**
346 * Returns a string representation of this <code>TabularType</code> instance.
347 * <p>
348 * The string representation consists of the name of this class (ie <code>javax.management.openmbean.TabularType</code>),
349 * the type name for this instance, the row type string representation of this instance,
350 * and the index names of this instance.
351 * <p>
352 * As <code>TabularType</code> instances are immutable, the string representation for this instance is calculated once,
353 * on the first call to <code>toString</code>, and then the same value is returned for subsequent calls.
354 *
355 * @return a string representation of this <code>TabularType</code> instance
356 */
357 public String toString() {
358
359 // Calculate the string representation if it has not yet been done (ie 1st call to toString())
360 //
361 if (myToString == null) {
362 final StringBuilder result = new StringBuilder().append(
363 this .getClass().getName()).append("(name=").append(
364 getTypeName()).append(",rowType=").append(
365 rowType.toString()).append(",indexNames=(");
366 int i = 0;
367 Iterator k = indexNames.iterator();
368 while (k.hasNext()) {
369 if (i > 0)
370 result.append(",");
371 result.append(k.next().toString());
372 i++;
373 }
374 result.append("))");
375 myToString = result.toString();
376 }
377
378 // return always the same string representation for this instance (immutable)
379 //
380 return myToString;
381 }
382
383 }
|