/*
* Created on Jan 28, 2005
*
* To change the template for this generated file go to
* Window>Preferences>Java>Code Generation>Code and Comments
*/
package de.desy.tine.structUtils;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import de.desy.tine.dataUtils.TDataType;
import de.desy.tine.definitions.TErrorList;
import de.desy.tine.definitions.TFormat;
import de.desy.tine.endianUtils.*;
import de.desy.tine.server.logger.MsgLog;
import de.desy.tine.types.*;
/**
* \internal
*
* This class describes a tagged structure, or rather an array of tagged
* structures. A TStructDescription contains a set of TStructDefinition.Field
* which are referenced by tagName. It also contains an arraySize which
* indicates how often this structure occurs in a sequence. Replaces
* TStruct.StructStruct TODO Rename to TStructArrayDescription?
*
* @author jwlg
* @version 2005-02-01
*/
public class TStructDescription
{
// add cross-reference back to a registered 'TTaggedStructure'
private TTaggedStructure tts = null;
public TTaggedStructure getTaggedStructure() { return tts; }
public void setTaggedStructure(TTaggedStructure struct) { tts = struct; }
private LinkedList<String> srvKeyLst = new LinkedList<String>();
/**
* @param key is the input key to examine and validate
* @return null if input is not valid or cannot be made into a valid key,
* otherwise returns a valid key
*/
private String makeValidSrvKey(String key)
{
if (key == null || key.length() == 0) return null;
if (!key.startsWith("/")) return null;
int idx;
if ((idx=key.indexOf("/", 1)) < 0) return null;
if (key.indexOf("/",idx+1) > 0)
{
String[] parts = key.split("/");
if (parts.length < 3) return null;
key = "/"+parts[1]+"/"+parts[2];
}
return key;
}
public boolean containsSrvKey(String key)
{
if (srvKeyLst.size() == 0) return true;
String srvkey = makeValidSrvKey(key);
if (srvkey == null) return false;
return srvKeyLst.contains(srvkey);
}
public boolean containsSrvKey(String context,String server)
{
if (context == null || context.length() == 0) return false;
if (server == null || server.length() == 0) return false;
return containsSrvKey("/"+context+"/"+server);
}
public void addSrvKey(String key)
{
String srvkey = makeValidSrvKey(key);
if (srvkey == null) return;
if (srvKeyLst.size() > 0 && srvKeyLst.contains(srvkey)) return;
srvKeyLst.add(srvkey);
}
public void addSrvKey(String context, String server)
{
if (context == null || context.length() == 0) return;
if (server == null || server.length() == 0) return;
addSrvKey("/"+context+"/"+server);
}
public String getTagDecoration()
{
if (srvKeyLst.size() == 0) return "";
return "@"+srvKeyLst.get(0);
}
//
private String tagName = ""; // structure tag
private int rawLength = 0; // (packed) network data size in bytes
private int size = 0; // local structure size -> always identical to rawLength for java
private int arraySize; // array-of buffer capacity (not relevant in java)
private ArrayList<Field> myFields = new ArrayList<Field>();
boolean fieldsComplete = false; // True if all fields are added.
boolean hasExtendedSpace = false;
public boolean hasExtendedSpace() { return hasExtendedSpace; }
/**
* \internal
*
* Describes a field of a tagged structure. Replaces TStruct.StructFormat TODO
* Document attributes
*/
public class Field
{
int arraySize; /* Number of subsequent occurrences in the structure */
int format; /* TODO TFormat (short) casted to int intentionally(?) */
int offset; /* Same as address? */
int address; // TODO Seems to be unused - always identical to offset
String name;
/**
* Default Constructor. Creates a Field of Format CF_NULL.
*/
public Field()
{
this("",0, TFormat.CF_NULL, 0, 0);
}
/**
* Constructs a Field.
*
* @param newSize
* The number of following elements of this type.
* @param newFormat
* The format of the field.
* @param newOffset
* The offset.
* @param newAddress
* The address.
*/
public Field(int newSize, short newFormat, int newOffset, int newAddress)
{
this("",newSize,newFormat,newOffset,newAddress);
}
public Field(String name, int newSize, short newFormat, int newOffset, int newAddress)
{
arraySize = newSize;
format = (int) newFormat;
offset = newOffset;
address = newAddress;
this.name = name;
}
/**
* @return Returns the address.
*/
public int getAddress()
{
return address;
}
/**
* Sets the address.
*
* @param address
* The address to set.
*/
public void setAddress(int address)
{
this.address = address;
}
/**
* @return Returns the format.
*/
public short getFormat()
{
return (short) format;
}
/**
* Sets the format.
*
* @param format
* The format to set.
*/
public void setFormat(short format)
{
this.format = (int) format;
}
/**
* @return Returns the offset.
*/
public int getOffset()
{
return offset;
}
public String getName()
{
return name;
}
/**
* Sets the offset.
*
* @param offset
* The offset to set.
*/
public void setOffset(int offset)
{
this.offset = offset;
}
/**
* @return Returns the size.
*/
public int getArraySize()
{
return arraySize;
}
/**
* Sets the size.
*
* @param size
* The size to set.
*/
public void setArraySize(int size)
{
this.arraySize = size;
}
}
/**
* Constructs a TStructDescription
*
* @param newTagName
* Name of the new structure
*/
public TStructDescription(String newTagName)
{
this.tagName = newTagName;
}
/**
* Add a Field
*
* @param format
* The TFormat as specified in TFormat
* @param arraySize
* The number of sequential occurrences of this field
* @see TFormat
*/
public void addField(short format, int arraySize)
{
addField("",format,arraySize);
}
public void addField(String name, short format, int arraySize)
{
if (!fieldsComplete)
{ // TODO Is this necessary?
Field newField = new Field(name, arraySize, format, this.rawLength, this.rawLength);
int hsiz = TFormat.getFormatHeaderSize(format);
int dsiz;
if (TFormat.isVariableLength(format))
{ // CF_STRING, CF_AIMAGE, CF_ASPECTRUM (and CF_IMAGE)
if (format == TFormat.CF_IMAGE) arraySize = 1; // special case
dsiz = arraySize * (hsiz + 8); // 2 int32 values specify the position and offset
hasExtendedSpace = true;
}
else
{ // CF_SPECTRUM is not 'variableLength'
dsiz = arraySize * TFormat.formatSizeOf(format) + hsiz;
}
if (format == TFormat.CF_STRUCT)
{
String stag = name.substring(1, name.indexOf('>'));
dsiz *= TStructRegistry.getSizeInBytes(stag);
if (dsiz < 0)
{
MsgLog.log("addField", "nested structure "+stag+" not properly registered!",TErrorList.invalid_structure_tag,null,0);
return;
}
}
this.rawLength += dsiz;
// in java, we're only ever dealing with a byte stream, so
// size = rawLength = sizeInBytes always
this.size = this.rawLength;
myFields.add(newField);
}
}
/**
* Add a field multiple times.
*
* @param num
* Multiplicity of the field
* @param format
* The TFormat as specified in TFormat
* @param arraySize
* The number of sequential occurrences of this field
*/
public void addField(int num, short format, int arraySize)
{
for (int i = 0; i < num; i++)
{
addField(format, arraySize);
}
}
/**
* Erases the contained information
*/
private void clear()
{
this.arraySize = 0;
this.rawLength = 0;
this.fieldsComplete = false;
this.size = 0;
this.myFields.clear();
}
/**
* Clears everything and permits definition of fields. TODO Is this necessary?
*/
public void beginDefinition()
{
clear();
}
/**
* Closes definition of fields. TODO Is this necessary?
*/
public void endDefinition()
{
// addField(TFormat.CF_NULL,1);
fieldsComplete = true;
}
/**
* @return Returns the number of sequential occurrences of this structure.
*/
public int getArraySize()
{
return arraySize;
}
/**
* Sets the number of sequential occurrences of this structure.
*
* @param arraySize
* The arraySize to set.
*/
public void setArraySize(int arraySize)
{
this.arraySize = arraySize;
}
/**
* @return Returns the tagName.
*/
public String getTagName()
{
return tagName;
}
public String getDecoratedTagName()
{
return tagName + getTagDecoration();
}
/**
* @param tagName
* The tagName to set.
*/
public void setTagName(String name)
{
this.tagName = name;
}
/**
* @return Returns the rawLength.
*/
public int getRawLength()
{
return rawLength;
}
/**
* @return Returns the registered.
*/
public boolean isFieldsComplete()
{
return fieldsComplete;
}
/**
* @return Returns the size.
*/
public int getSize()
{
return size;
}
/**
* Returns a HasMap of all contained fields. The hash map contains keys of
* type String and entries of type Field.
*/
public ArrayList<Field> getFields()
{
return myFields;
}
/**
* Returns an iterator over all fields. The iterator refers to an entry of
* type Field.
* <P>
* <B>Note: </B>Don't use the iterator to remove Fields!
* </P>
*
* @return
*/
public Iterator<Field> getFieldIterator()
{
return myFields.iterator();
}
public int getNFields()
{
return myFields.size();
}
public Field getField(int i)
{
Field rv = null;
if ((i >= 0) && (i < myFields.size()))
{
rv = (Field) myFields.get(i);
}
return rv;
}
public Field getField(String field)
{
Field rv = null;
Iterator<Field> f = myFields.iterator();
while (f.hasNext())
{
rv = f.next();
if (rv.getName().compareToIgnoreCase(field) == 0) break;
}
return rv;
}
public boolean hasField(String field)
{
Iterator<Field> f = myFields.iterator();
while (f.hasNext()) if (f.next().getName().compareToIgnoreCase(field) == 0) return true;
return false;
}
/**
* Returns the field sizes and formats in a differently coded form used by
* de.desy.tine.client.
*
* @return LongInt Array containing type/format pairs.
*/
public INTINT[] toLongIntArray()
{
int nFields = myFields.size();
INTINT[] li = new INTINT[nFields + 1];
Field currentField;
for (int i = 0; i < nFields; i++)
{
currentField = getField(i);
li[i] = new INTINT(currentField.getArraySize(), currentField.getFormat() + 512);
}
// append terminating NULL
li[nFields] = new INTINT(size, TFormat.CF_NULL + 512);
return li;
}
public NAME16II[] toNameIntIntArray()
{
int nFields = myFields.size();
NAME16II[] li = new NAME16II[nFields + 1];
Field currentField;
for (int i = 0; i < nFields; i++)
{
currentField = getField(i);
li[i] = new NAME16II(currentField.getName(),currentField.getArraySize(), currentField.getFormat() + 512);
}
// append terminating NULL
li[nFields] = new NAME16II("",size, TFormat.CF_NULL + 512);
return li;
}
public NAME64DBLDBL[] toNameDblDblArray()
{
int nFields = myFields.size();
NAME64DBLDBL[] li = new NAME64DBLDBL[nFields + 1];
Field currentField;
for (int i = 0; i < nFields; i++)
{
currentField = getField(i);
li[i] = new NAME64DBLDBL(currentField.getName(),currentField.getArraySize(), currentField.getFormat() + 512);
}
// append terminating NULL
li[nFields] = new NAME64DBLDBL("",size, TFormat.CF_NULL + 512);
return li;
}
private static boolean isInside = false;
private static String tagInside = null;
/**
* Converts an array of bytes into a string. This method uses the field
* descriptions to convert an array of bytes into string using a special
* format. TODO Describe the special format.
*
* @param data
* A byte array containing the data
* @param offset
* Offset in the byte array.
* @return Result of conversion.
*/
public String convertBytesToString(byte[] data, int offset)
{
Field currentField;
int fmt, len;
byte[] bstr;
String tag = null;
StringBuffer buffer = new StringBuffer();
try
{
ByteArrayInputStream dis = new ByteArrayInputStream(data, offset, this.rawLength);
DataInputStream ds = new DataInputStream(dis);
for (int n = 0; n < myFields.size(); n++)
{
currentField = getField(n);
len = currentField.getArraySize();
fmt = (currentField.getFormat()) % 512;
if (len > 0)
{
if (isInside)
{
tag = tagInside + currentField.getName() + "] ->";
}
else
{
tag = "[" + currentField.getName() + "] ->";
}
switch (fmt)
{
case TFormat.CF_BYTE:
buffer.append(tag);
for (int i = 0; i < len; i++)
{
buffer.append(i > 0 ? "," : " ");
buffer.append(ds.readByte());
}
buffer.append("\n");
break;
case TFormat.CF_TEXT:
buffer.append(tag);
bstr = new byte[len];
ds.read(bstr, 0, len);
int nlen;
for (nlen = 0; nlen < len; nlen++) if (bstr[nlen] == 0) break;
buffer.append(new String(bstr,0,nlen).trim());
buffer.append("\n");
break;
case TFormat.CF_SHORT:
buffer.append(tag);
for (int i = 0; i < len; i++)
{
buffer.append(i > 0 ? "," : " ");
buffer.append(SwapUtil.swapShort(ds.readShort()));
}
buffer.append("\n");
break;
case TFormat.CF_LONG:
buffer.append(tag);
for (int i = 0; i < len; i++)
{
buffer.append(i > 0 ? "," : " ");
buffer.append(SwapUtil.swapInt(ds.readInt()));
}
buffer.append("\n");
break;
case TFormat.CF_DLONG:
buffer.append(tag);
for (int i = 0; i < len; i++)
{
buffer.append(i > 0 ? "," : " ");
buffer.append(SwapUtil.swapLong(ds.readLong()));
}
buffer.append("\n");
break;
case TFormat.CF_FLOAT:
buffer.append(tag);
for (int i = 0; i < len; i++)
{
buffer.append(i > 0 ? "," : " ");
// avoid NaN !
buffer.append(SwapUtil.swapFloat(ds.readInt()));
}
buffer.append("\n");
break;
case TFormat.CF_DOUBLE:
buffer.append(tag);
for (int i = 0; i < len; i++)
{
buffer.append(i > 0 ? "," : " ");
// avoid NaN !
buffer.append(SwapUtil.swapDouble(ds.readLong()));
}
buffer.append("\n");
break;
case TFormat.CF_STRUCT:
String nam = currentField.getName();
String stag = nam.substring(1, nam.indexOf('>'));
for (int i=0; i<len; i++)
{
byte[] sb = new byte[TFormat.formatSizeOf((short)fmt)*TStructRegistry.getSizeInBytes(stag)];
ds.read(sb);
isInside = true;
if (len > 1) tagInside = "[" + currentField.getName()+"("+i+").";
else tagInside = "[" + currentField.getName()+".";
buffer.append(TStructRegistry.toString(stag, sb));
isInside = false;
}
break;
default:
boolean skip = false;
int fmtsiz = TFormat.formatSizeOf((short)fmt);
int hsiz = TFormat.getFormatHeaderSize((short)fmt);
if (TFormat.isAdjustableLength((short)fmt))
{
hsiz += 8;
fmtsiz = hsiz;
skip = true;
}
buffer.append(tag);
byte[] b = new byte[len*fmtsiz];
ds.read(b);
if (skip)
{
buffer.append("variable length format string space unavailable\n");
break;
}
TDataType dt = new TDataType(len,(short)fmt);
dt.pushBytes(b);
dt.dCompletionLength = len;
dt.getData();
buffer.append(dt.toString());
break;
} // Type Switch
} // if len > 0
} // for
}
catch (IOException e)
{
e.printStackTrace();
MsgLog.log("convertBytesToString",e.getMessage(),TErrorList.code_failure,e,0);
}
return buffer.toString();
}
/**
* Overloaded for Convenience.
*
* @param data
* @see convertBytesToString(byte[],int)
*/
public String convertBytesToString(byte[] data)
{
return convertBytesToString(data, 0);
}
}