summaryrefslogtreecommitdiff
path: root/tools/src/java/qpid-qmf2/src/main/java/org/apache/qpid/qmf2/common/QmfData.java
blob: c634564ba40aa6418f42c553ca3453321e16b358 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
/*
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
 */
package org.apache.qpid.qmf2.common;

// Misc Imports
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;

/**
 * QMF defines the QmfData class to represent an atomic unit of managment data.
 * <p>
 * The QmfData class defines a collection of named data values. Optionally, a string tag, or "sub-type" may be
 * associated with each data item. This tag may be used by an application to annotate the data item.
 * (Note the tag implementation is TBD).
 * <p>
 * In QMFv2, message bodies are AMQP maps and are therefore easily extended without causing backward
 * compatibility problems. Another benefit of the map-message format is that all messages can be fully
 * parsed when received without the need for external schema data
 * <p>
 * This class is the base class for all QMF2 Data Types in this implementation. It is intended to provide a
 * useful wrapper for the underlying Map, supplying accessor and mutator methods that give an element of type
 * safety (for example strings appear to be encoded as a mixture of byte[] and String depending of the agent, so
 * this class checks for this in its getString())
 * <p>
 * The following diagram represents the QmfData class hierarchy.
 * <p>
 * <img alt="" src="doc-files/QmfData.png">
 *
 * @author Fraser Adams
 */
public class QmfData
{
    protected Map<String, Object> _values = null;
    protected Map<String, String> _subtypes = null;

    /**
     * The default constructor, initialises the QmfData with an empty Map.
     */
    public QmfData()
    {
        _values = new HashMap<String, Object>();
    }

    /**
     * The main constructor, taking a java.util.Map as a parameter. In essence it "deserialises" its state from the Map.
     *
     * @param m the Map used to construct the QmfData.
     */
    @SuppressWarnings("unchecked")
    public QmfData(final Map m)
    {
        if (m == null)
        { // This branch is just a safety precaution and shouldn't generally occur in normal usage scenarios.
            // Initialise with a new HashMap. Should then behave the same as the default Constructor.
            _values = new HashMap<String, Object>();
        }
        else
        {
            Map<String, Object> values = (Map<String, Object>)m.get("_values");
            _values = (values == null) ? m : values;

            Map<String, String> subtypes = (Map<String, String>)m.get("_subtypes");
             _subtypes = subtypes;
        }   
    }

    /**
     * Get the state of the _subtypes Map, (generally used when serialising method request/response arguments.
     *
     * @return the value of the _subtypes Map.
     */
    public Map<String, String> getSubtypes()
    {
        return _subtypes;
    }


    /**
     * Set the state of the _subtypes Map, (generally used when deserialising method request/response arguments.
     *
     * @param subtypes the new value of the _subtypes Map.
     */
    @SuppressWarnings("unchecked")
    public void setSubtypes(Map subtypes)
    {
        _subtypes = subtypes;
    }

    /**
     * Helper method to return the <i>best</i> String representation of the given Object.
     * <p>
     * There seem to be some inconsistencies where string properties are sometimes returned as byte[] and
     * sometimes as Strings. It seems to vary depending on the Agent and is due to strings being encoded as a mix of
     * binary strings and UTF-8 strings by C++ Agent classes. Getting it wrong results in ClassCastExceptions, which
     * is clearly unfortunate.
     * <p>
     * This is basically a helper method to check the type of a property and return the most "appropriate"
     * String representation for it.
     *
     * @param p a property in Object form
     * @return the most appropriate String representation of the property
     */
    public static final String getString(final Object p)
    {
        if (p == null)
        {
            return "";
        }
        else if (p instanceof String)
        {
            return (String)p;
        }
        else if (p instanceof byte[])
        {
            return new String((byte[])p);
        }
        else return p.toString();
    }

    /**
     * Helper method to return the <i>best</i> long representation of the given Object.
     * <p>
     * There seem to be some inconsistencies where properties are sometimes returned as Integer and
     * sometimes as Long. It seems to vary depending on the Agent and getting it wrong results in
     * ClassCastExceptions, which is clearly unfortunate.
     * <p>
     * This is basically a helper method to check the type of a property and return a long representation for it.
     *
     * @param p a property in Object form
     * @return the long representation for it.
     */
    public static final long getLong(final Object p)
    {
        if (p == null)
        {
            return 0;
        }
        else if (p instanceof Long)
        {
            return ((Long)p).longValue();
        }
        else if (p instanceof Integer)
        {
            return ((Integer)p).intValue();
        }
        else if (p instanceof Short)
        {
            return ((Short)p).shortValue();
        }
        else return 0;
    }

    /**
     * Helper method to return the <i>best</i> boolean representation of the given Object.
     * <p>
     * There seem to be some inconsistencies where boolean properties are sometimes returned as Boolean and
     * sometimes as Long/Integer/Short. It seems to vary depending on the Agent and getting it wrong results in
     * ClassCastExceptions, which is clearly unfortunate.
     * <p>
     * This is basically a helper method to check the type of a property and return a boolean representation for it.
     *
     * @param p a property in Object form
     * @return the boolean representation for it.
     */
    public static final boolean getBoolean(final Object p)
    {
        if (p == null)
        {
            return false;
        }
        else if (p instanceof Boolean)
        {
            return ((Boolean)p).booleanValue();
        }
        else if (p instanceof Long)
        {
            return ((Long)p).longValue() > 0;
        }
        else if (p instanceof Integer)
        {
            return ((Integer)p).intValue() > 0;
        }
        else if (p instanceof Short)
        {
            return ((Short)p).shortValue() > 0;
        }
        else if (p instanceof String)
        {
            return Boolean.parseBoolean((String)p);
        }
        else return false;
    }

    /**
     * Helper method to return the <i>best</i> double representation of the given Object.
     * <p>
     * There seem to be some inconsistencies where properties are sometimes returned as Float and
     * sometimes as Double. It seems to vary depending on the Agent and getting it wrong results in
     * ClassCastExceptions, which is clearly unfortunate.
     * <p>
     * This is basically a helper method to check the type of a property and return a Double representation for it.
     *
     * @param p a property in Object form
     * @return the Double representation for it.
     */
    public static final double getDouble(final Object p)
    {
        if (p == null)
        {
            return 0.0d;
        }
        else if (p instanceof Float)
        {
            return ((Float)p).floatValue();
        }
        else if (p instanceof Double)
        {
            return ((Double)p).doubleValue();
        }
        else return 0.0d;
    }

    /**
     * Determines if the named property exists.
     *
     * @param name of the property to check.
     * @return true if the property exists otherwise false.
     */
    public final boolean hasValue(final String name)
    {
        return _values.containsKey(name);
    }

    /**
     * Accessor method to return a named property as an Object.
     *
     * @param name of the property to return as an Object.
     * @return value of property as an Object.
     */
    @SuppressWarnings("unchecked")
    public final <T> T getValue(final String name)
    {
        return (T)_values.get(name);
    }

    /**
     * Mutator method to set a named Object property.
     *
     * @param name the name of the property to set.
     * @param value the value of the property to set.
     */
    public final void setValue(final String name, final Object value)
    {
        _values.put(name, value);
    }

    /**
     * Mutator method to set a named Object property.
     *
     * @param name the name of the property to set.
     * @param value the value of the property to set.
     * @param subtype the subtype of the property.
     */
    public final void setValue(final String name, final Object value, final String subtype)
    {
        setValue(name, value);
        setSubtype(name, subtype);
    }

    /**
     * Mutator to set or modify the subtype associated with name.
     *
     * @param name the name of the property to set the subtype for.
     * @param subtype the subtype of the property.
     */
    public final void setSubtype(final String name, final String subtype)
    {
        if (_subtypes == null)
        {
            _subtypes = new HashMap<String, String>();
        }
        _subtypes.put(name, subtype);
    }

    /**
     * Accessor to return the subtype associated with named property.
     *
     * @param name the name of the property to get the subtype for.
     * @return the subtype of the named property or null if not present.
     */
    public final String getSubtype(final String name)
    {
        if (_subtypes == null)
        {
            return null;
        }
        return _subtypes.get(name);
    }

    /**
     * Accessor method to return a named property as a boolean.
     *
     * @param name of the property to return as a boolean.
     * @return value of property as a boolean.
     */
    public final boolean getBooleanValue(final String name)
    {
        return getBoolean(getValue(name));
    }

    /**
     * Accessor method to return a named property as a long.
     *
     * @param name of the property to return as a long.
     * @return value of property as a long.
     */
    public final long getLongValue(final String name)
    {
        return getLong(getValue(name));
    }

    /**
     * Accessor method to return a named property as a double.
     *
     * @param name of the property to return as a double.
     * @return value of property as a double.
     */
    public final double getDoubleValue(final String name)
    {
        return getDouble(getValue(name));
    }

    /**
     * Accessor method to return a named property as a String.
     *
     * @param name of the property to return as a String.
     * @return value of property as a String.
     */
    public final String getStringValue(final String name)
    {
        return getString(getValue(name));
    }

    /**
     * Accessor method to return a reference property.
     * <p>
     * Many QMF Objects contain reference properties, e.g. references to other QMF Objects.
     * This method allows these to be obtained as ObjectId objects to enable much easier
     * comparison and rendering.
     * @return the retrieved value as an ObjectId instance.
     */
    public final ObjectId getRefValue(final String name)
    {
        return new ObjectId((Map)getValue(name));
    }

    /**
     * Mutator method to set a named reference property.
     * <p>
     * Many QMF Objects contain reference properties, e.g. references to other QMF Objects.
     * This method allows these to be set as ObjectId objects.
     *
     * @param name the name of the property to set.
     * @param value the value of the property to set.
     */
    public final void setRefValue(final String name, final ObjectId value)
    {
        setValue(name, value.mapEncode());
    }

    /**
     * Mutator method to set a named reference property.
     * <p>
     * Many QMF Objects contain reference properties, e.g. references to other QMF Objects.
     * This method allows these to be set as ObjectId objects.
     *
     * @param name the name of the property to set.
     * @param value the value of the property to set.
     * @param subtype the subtype of the property.
     */
    public final void setRefValue(final String name, final ObjectId value, final String subtype)
    {
        setRefValue(name, value);
        setSubtype(name, subtype);
    }

    /**
     * Return the underlying Map representation of this QmfData.
     * @return the underlying Map. 
     */
    public Map<String, Object> mapEncode()
    {
        return _values;
    }

    /**
     * Helper/debug method to list the properties and their type.
     */
    public void listValues()
    {
        for (Map.Entry<String, Object> entry : _values.entrySet())
        {
            Object key = entry.getKey();
            Object value = entry.getValue();
            if (value instanceof Map)
            { // Check if the value part is an ObjectId and display appropriately
                Map map = (Map)value;
                if (map.containsKey("_object_name"))
                {
                    System.out.println(key + ": " + new ObjectId(map));
                }
                else
                {
                    System.out.println(key + ": " + getString(value));
                }
            }
            else
            {
                System.out.println(key + ": " + getString(value));
            }
        }
    }
}