/* * * 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. *

* 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). *

* 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 *

* 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()) *

* The following diagram represents the QmfData class hierarchy. *

* * * @author Fraser Adams */ public class QmfData { protected Map _values = null; protected Map _subtypes = null; /** * The default constructor, initialises the QmfData with an empty Map. */ public QmfData() { _values = new HashMap(); } /** * 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(); } else { Map values = (Map)m.get("_values"); _values = (values == null) ? m : values; Map subtypes = (Map)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 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 best String representation of the given Object. *

* 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. *

* 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 best long representation of the given Object. *

* 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. *

* 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 best boolean representation of the given Object. *

* 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. *

* 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 best double representation of the given Object. *

* 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. *

* 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 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(); } _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. *

* 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. *

* 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. *

* 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 mapEncode() { return _values; } /** * Helper/debug method to list the properties and their type. */ public void listValues() { for (Map.Entry 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)); } } } }