All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ie.omk.smpp.message.tlv.TLVTable Maven / Gradle / Ivy

The newest version!
package ie.omk.smpp.message.tlv;

import ie.omk.smpp.util.SMPPIO;

import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * Table of optional parameters (TLVs).
 * 

* TLV stands for Tag/Length/Value and was a capability added to SMPP version * 3.4. It is an extensible means of adding new parameter types to SMPP packets. * Each optional parameter has a 2-byte tag, which is a unique identifier of * that parameter, a 2-byte length, which is an integer value representing the * length of the value of the parameter and a value. The value may be of various * types including integers, C Strings, octet strings, bit masks etc. The tag * defines the type of the value. *

*

* This class holds a mapping of tags to values. Each SMPP packet holds a TLV * table which holds that packet's set of current optional parameters. Upon * serializing the packet to an output stream or byte array, the format of the * serialized packet is: *

* *
 * 
 *    +-------------------------+
 *    | SMPP Packet             |
 *    | +----------------------+|
 *    | | SMPP Header          ||
 *    | +----------------------+|
 *    | |                      ||
 *    | |                      ||
 *    | | Mandatory parameters ||
 *    | |                      ||
 *    | |                      ||
 *    | +----------------------+|
 *    | | Optional parameters  ||
 *    | | +------------------+ ||
 *    | | | Tag/Length/Value | ||
 *    | | +------------------+ ||
 *    | | |     ...          | ||
 *    | | +------------------+ ||
 *    | +----------------------+|
 *    +-------------------------+
 *  
 * 
* * @author Oran Kelly * @version $Id: TLVTable.java 466 2009-06-16 12:26:57Z orank $ */ public class TLVTable implements java.io.Serializable { static final long serialVersionUID = -4113000096792513355L; /** * Map of tag to values. */ private Map map = new HashMap(); /** * Undecoded options. TLVTable is lazy about decoding optional parameters. * It will decode a TLV param only when it is requested using the * get method. Certain method calls, however, will force * TLVTable to parse the entire set of options (using * parseAllOpts). When that happens, this array is released * for garbage collection. */ private byte[] opts; /** * Create a new, empty, TLVTable. */ public TLVTable() { } /** * Decode a full set of optional parameters from a byte array. * * @param b * The byte array to decode from. * @param offset * The first byte of the tag of the first optional parameter. * @param len * The length in the byte array of all the optional parameters. */ public void readFrom(byte[] b, int offset, int len) { synchronized (map) { opts = new byte[len]; System.arraycopy(b, offset, opts, 0, len); } } /** * Encode all the optional parameters in this table to an output stream. * * @param out * The output stream to encode the parameters to. * @throws java.io.IOException * If an error occurs writing to the output stream. */ public void writeTo(OutputStream out) throws IOException { synchronized (map) { byte[] buffer = new byte[1024]; Iterator i = map.keySet().iterator(); while (i.hasNext()) { Tag t = (Tag) i.next(); Encoder enc = t.getEncoder(); Object v = map.get(t); int l = enc.getValueLength(t, v); if (buffer.length < (l + 4)) { buffer = new byte[l + 4]; } SMPPIO.intToBytes(t.intValue(), 2, buffer, 0); SMPPIO.intToBytes(l, 2, buffer, 2); enc.writeTo(t, v, buffer, 4); // write the buffer out. out.write(buffer, 0, l + 4); } } } /** * Get the value for a tag. Note that this method can return null in two * cases: if the parameter is not set or if the value is null, which can * occur if the particular tag type has no value. To check if a parameter * which has no value is set, use {@link #isSet}. * * @param tag * The tag to get the value for. * @return The currently set value for tag, or null if it is * not set. */ public Object get(Tag tag) { Object v = map.get(tag); if (v == null) { v = getValueFromBytes(tag); } return v; } /** * Get the value for a tag. * * @see #get(ie.omk.smpp.message.tlv.Tag) */ public Object get(int tag) { Tag tagObj = Tag.getTag(tag); Object v = map.get(tagObj); if (v == null) { v = getValueFromBytes(tagObj); } return v; } /** * Check if an optional parameter currently has a value set. * * @param tag * The tag of the parameter to check is set. * @return true if the parameter is set, false if not. */ public boolean isSet(Tag tag) { if (opts != null) { parseAllOpts(); } return map.containsKey(tag); } /** * Set a value for an optional parameter. * * @param tag * The tag of the parameter to set. * @param value * The value of the parameter to set. * @return The previous value for the parameter, or null if there was none. * @throws ie.omk.smpp.message.tlv.BadValueTypeException * if an attempt is made to set a value using a Java type that * is not allowed for that parameter type. * @throws ie.omk.smpp.message.tlv.InvalidSizeForValueException * if the value's encoded length is outside the bounds allowed * for that parameter. */ public Object set(Tag tag, Object value) throws BadValueTypeException, InvalidSizeForValueException { synchronized (map) { if (opts != null) { parseAllOpts(); } if (tag.getType() == null) { if (value != null) { throw new BadValueTypeException("Tag " + Integer.toHexString(tag.intValue()) + " does not accept a value."); } } else if (!tag.getType().isAssignableFrom(value.getClass())) { throw new BadValueTypeException("Tag " + Integer.toHexString(tag.intValue()) + " expects a value of type " + tag.getType()); } // Enforce the length restrictions on the Value specified by the // Tag. int min = tag.getMinLength(); int max = tag.getMaxLength(); int actual = tag.getEncoder().getValueLength(tag, value); boolean illegal = min > -1 && actual < min; if (!illegal) { illegal = max > -1 && actual > max; } if (illegal) { throw new InvalidSizeForValueException("Tag " + Integer.toHexString(tag.intValue()) + " must have a length in the range " + min + " <= len <= " + max); } return map.put(tag, value); } } /** * Clear all optional parameters out of this table. */ public void clear() { synchronized (map) { map.clear(); } } /** * Force the TLVTable to parse all the optional parameters from the internal * byte array and place them in the map. Normally, TLVTable is lazy about * parsing parameters. It will only decode them and place them in the * internal map when they are requested using {@link #get}. Calling this * method causes all the parameters to be parsed and placed in the internal * map and the byte array containing the parameter's bytes to be released * for garbage collection. *

* It is not normally needed for an application to call this method. * TLVTable uses it internally when necessary to ensure there * is no loss of synchronization between the internal map and the byte * array. */ public final void parseAllOpts() { synchronized (map) { int p = 0; while (p < opts.length) { Object val = null; Tag t = Tag.getTag(SMPPIO.bytesToInt(opts, p, 2)); Encoder enc = t.getEncoder(); int l = SMPPIO.bytesToInt(opts, p + 2, 2); val = enc.readFrom(t, opts, p + 4, l); map.put(t, val); p += 4 + l; } opts = null; } } /** * Get the value of an option from the opts byte array. * * @param tag * The tag to get the value for. * @return The value object for tag tag.null * if it is not set. */ private Object getValueFromBytes(Tag tag) { if (opts == null || opts.length < 4) { return null; } Encoder enc = tag.getEncoder(); Object val = null; int p = 0; while (true) { int t = SMPPIO.bytesToInt(opts, p, 2); int l = SMPPIO.bytesToInt(opts, p + 2, 2); if (tag.equals(t)) { val = enc.readFrom(tag, opts, p + 4, l); synchronized (map) { map.put(tag, val); break; } } p += 4 + l; if (p >= opts.length) { break; } } return val; } /** * Get the length the parameters in the table would encode as. The length of * an SMPP packet is determined by:
* sizeof (smpp_header) + sizeof (mandatory_parameters) * + sizeof (optional_parameters). *
* The value returned for this method is the last clause in this equation. * * @return The full length that the optional parameters would encode as. */ public int getLength() { if (opts != null) { parseAllOpts(); } // Length is going to be (number of options) * (2 bytes for tag) * (2 // bytes for length) + (size of all encoded values) int length = map.size() * 4; Tag tag; Encoder enc; Iterator i = map.keySet().iterator(); while (i.hasNext()) { tag = (Tag) i.next(); enc = tag.getEncoder(); length += enc.getValueLength(tag, map.get(tag)); } return length; } /** * Get the set of tags in this TLVTable. * * @return A java.util.Set containing all the Tags in this TLVTable. */ public java.util.Set tagSet() { if (opts != null) { parseAllOpts(); } return map.keySet(); } /** * Get a Collection view of the set of values in this TLVTable. * * @return A java.util.Collection view of all the values in this TLVTable. */ public java.util.Collection values() { if (opts != null) { parseAllOpts(); } return map.values(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy