📄 tlvtable.java
字号:
/**
*
* <p>Title: Smgp协议TLV结构解析</p>
* <p>Description:存放TLV结构体</p>
* <p>Copyright: Copyright (c) 2007</p>
* <p>Company: 福富软件</p>
* @author chenxin
* @version 1.0 $Date 2007-07-03
*/
package ffcs.lbp.le.message.tlv;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import ffcs.lbp.le.util.LeIO;
/**
* 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
* @version $Id: TLVTable.java 267 2006-03-09 16:37:31Z 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
* <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;
/**
* 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(ByteBuffer buf) 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 len = enc.getValueLength(t, v);
byte[] buffer = new byte[8+len];
LeIO.intToBytes(t.intValue(), 4, buffer, 0);
LeIO.intToBytes(len, 4, buffer, 4);
enc.writeTo(t, v, buffer, 8);
buf.put(buffer,0,8+len);
// 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;
// int aa= LeIO.bytesToInt(opts, p, 4);
Tag t = Tag.getTag(LeIO.bytesToInt(opts, p, 4));//LeIO.bytesToInt(opts, p, 4));
// System.out.println("Tag名:"+Integer.toHexString(aa));
Encoder enc = t.getEncoder();
int l = LeIO.bytesToInt(opts, p + 4, 4);
val = enc.readFrom(t, opts, p + 8, l+1);
map.put(t, val);
p += 8 + 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 < 8) {
return null;
}
Encoder enc = tag.getEncoder();
Object val = null;
int p = 0;
while (true) {
int t = LeIO.bytesToInt(opts, p, 4);
int l = LeIO.bytesToInt(opts, p + 4, 4);
if (tag.equals(t)) {
val = enc.readFrom(tag, opts, p + 8, l+1);
synchronized (map) {
map.put(tag, val);
break;
}
}
p += 8 + 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() * 8;
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();
}
/**
* 类似于toString(),但是更简单,而且性能更高,毕竟toString里跑了循环
* @return 返回形式(15=abcxyz)
*/
public String getSimpleInfo(){
return map.toString();
}
public String toString() {
StringBuffer sb=new StringBuffer("TLVTable( ");
if (opts != null) {
parseAllOpts();
}
if(map.size()>0){
Tag tag;
Iterator key_i = map.keySet().iterator();
while (key_i.hasNext()) {
tag = (Tag) key_i.next();
sb.append(Tag.checkTag(tag.intValue()));
sb.append(map.get(tag)+" ");
}
sb.append(")");
return sb.toString();
}
return "";
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -