📄 tlvtable.java
字号:
/* * Java SMPP API * Copyright (C) 1998 - 2002 by Oran Kelly * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * A copy of the LGPL can be viewed at http://www.gnu.org/copyleft/lesser.html * Java SMPP API author: orank@users.sf.net * Java SMPP API Homepage: http://smppapi.sourceforge.net/ * * $Id: TLVTable.java,v 1.7 2004/10/09 22:02:42 orank Exp $ */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 org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;/** Table of optional parameters (TLVs). <p>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.</p> * <p>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:</p> * <pre> * +-------------------------+ * | SMPP Packet | * | +----------------------+| * | | SMPP Header || * | +----------------------+| * | | || * | | || * | | Mandatory parameters || * | | || * | | || * | +----------------------+| * | | Optional parameters || * | | +------------------+ || * | | | Tag/Length/Value | || * | | +------------------+ || * | | | ... | || * | | +------------------+ || * | +----------------------+| * +-------------------------+ * </pre> * @author Oran Kelly <orank@users.sf.net> */public class TLVTable implements java.io.Serializable { private static final Log logger = LogFactory.getLog(TLVTable.class); /** Map of tag to values. */ private HashMap 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 <code>get</code> * method. Certain method calls, however, will force * TLVTable to parse the entire set of options (using * <code>parseAllOpts</code>). When that happens, this * array is released for garbage collection. */ private byte[] opts = null; /** 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) throws ArrayIndexOutOfBoundsException { 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) { int len = 0, offset = 0; 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 <code>tag</code>, 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) { 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. * <p>It is not normally needed for an application to call this method. * <code>TLVTable</code> 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 <code>opts</code> * byte array. * @param tag The tag to get the value for. * @return The value object for tag <code>tag</code>. <code>null</code> * 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:<br> * <code>sizeof (smpp_header) + sizeof (mandatory_parameters) * + sizeof (optional_parameters).</code><br> * 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()); }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -