/*
i-net software provides programming examples for illustration only, without warranty
either expressed or implied, including, but not limited to, the implied warranties
of merchantability and/or fitness for a particular purpose. This programming example
assumes that you are familiar with the programming language being demonstrated and
the tools used to create and debug procedures. i-net software support professionals
can help explain the functionality of a particular procedure, but they will not modify
these examples to provide added functionality or construct procedures to meet your
specific needs.
© i-net software 1998-2013
*/
using System.Drawing.Drawing2D;
using System.Drawing;
using System;
using Inet.Viewer.Resources;
using System.Globalization;
using System.IO;
namespace Inet.Viewer.Data
{
///
/// Loader interface that is to fetch the data, to interpret the data and to give it back to the Viewer.
/// This class is the link between the Viewer and IReportData . IReportData is the
/// low level IO communication to the server.
/// After the data are fetched they should be stored in and than invoked through
///
/// The implemented Loader should override as this function is invoked for each token that is read.
///
public class Loader
{
///
/// for the Property Data
///
private byte[] data;
///
///
///
protected int Offset { get; set; }
///
///
///
protected int TokenSize { get; set; }
///
///
///
protected int OldOffset { get; set; }
///
/// Contains the byte data that will be processed
///
public byte[] Data
{
get { return data; }
set { data = value; }
}
///
/// Checks the byte data for the correct signature and checksum, and then goes through and
/// interprets each individual token using load(token).
///
internal void ReadTokens()
{
Offset = 0;
if (data == null || data.Length < 4 || data[Offset++] != 26 || data[Offset++] != 88 || data[Offset++] != 77 || data[Offset++] != 76)
{
// wrong signature, empty or no data
if (data == null)
{
data = new byte[0];
}
string msg = GetHexDump(data, 0, System.Math.Min(data.Length, 200));
if (data.Length > 200)
{
msg += "\n................";
}
else if (data.Length == 0)
{
msg = "";
}
throw new ViewerException(0, strings.ErrorMessage_WrongDataReceived, null, null, null, null, 0, msg);
}
int length = Read4ByteInt();
if (length != data.Length)
{
throw new ViewerException(0, strings.ErrorMessage_DataLength, null, null, null, null, 0, "Header length:" + length + "\nPackage length:" + data.Length);
}
// check Checksum: at the end, Adler32 Checksumme for the whole byte arrays
Offset = data.Length - 4;
int checksum = Read4ByteInt();
Adler32 checker = new Adler32();
checker.Update(data, 0, data.Length - 4);
int value = checker.Checksum;
if (checksum != value)
{
throw new ViewerException(0, "There was an error in the data transmission: checksums differ.", null, null, null, null, 0, "expected: " + checksum + ", got: " + value);
}
// reset (after signature - 4 byte and lenght - 4 byte)
Offset = 8;
int size = data.Length - 4;
// 4 Byte Checksumme
while (Offset < size)
{
int token = ReadInt();
TokenSize = ReadInt();
OldOffset = Offset;
if (!Load(token))
{
return; // load(int) will be overriden by the implementation of this Loader
}
Offset = OldOffset + TokenSize;
}
}
///
/// Reads a single token. Will be overridden by the implementations of this Loader.
/// The lengt of a token ist handled one level up.
/// ID of the token
/// true: continue loading; false: stop loading
protected internal virtual bool Load(int token)
{
switch (token)
{
case ViewerTokenConstants.TokenVersion:
string engineVersion = ReadString();
string protocolVersion = ReadString();
try
{
double versionAsNumber = double.Parse(protocolVersion, CultureInfo.InvariantCulture);
}
catch (System.FormatException e)
{
ViewerUtils.PrintStackTrace(e);
}
break;
case ViewerTokenConstants.TokenErrorMessage:
ViewerException error = ReadError();
if (error != null)
{
throw error;
}
break;
case ViewerTokenConstants.TokenPrompts:
PromptData[] prompts = ReadPrompts();
if (prompts == null)
{
break;
}
throw new ViewerException(prompts);
default:
if (GetType() != typeof(Loader))
{
ViewerUtils.Error("Loader.Load() Unknown token: " + token);
}
break;
}
return true;
}
///
/// Read the prompts
///
/// an array or null
protected internal PromptData[] ReadPrompts()
{
int numberOfPrompts = ReadInt();
if (numberOfPrompts == 0)
{
return null;
}
PromptData[] prompts = new PromptData[numberOfPrompts];
for (int i = 0; i < numberOfPrompts; i++)
{
bool needed = ReadInt() == 1;
if (needed)
{
prompts[i] = new PromptData(ReadString(), ReadString(), ReadString(), ReadStringArray(), ReadStringArray(),
ReadInt(), ReadInt() == 1, ReadInt() == 1, ReadInt() == 1, ReadInt() == 1,
// prompt parameter name
// prompt's sub report name
// prompting parameter text
// prompt parameter default value
// default value descriptions
// type (NO flags like range, etc.!)
// discrete?
// range?
// multi?
// default Values editable?
// only show descriptions?
// Informix SP?
// edit mask
// min length
ReadInt() == 1, ReadInt() == 1, ReadString(), ReadString(), ReadString());
// max length
prompts[i].CascadingParent = ReadString();
prompts[i].DisplayName = ReadString();
}
else
{
prompts[i] = new PromptData(true);
}
}
return prompts;
}
///
/// Reads the Error message from the byte Data and wraps it into a
/// ViewerExeption class
///
///
protected internal ViewerException ReadError()
{
int errorCode = Read4ByteInt();
string msg = ReadString();
string format = ReadString();
string srvVersion = ReadString();
string srvJVM = ReadString();
string srvOS = ReadString();
int srvCache = ReadInt();
string stacktrace = ReadString();
if (errorCode >= 0)
{
return new ViewerException(errorCode, msg, format, srvVersion, srvJVM, srvOS, srvCache, stacktrace);
}
return null;
}
///
/// String[]: [NUMBER_OF_STRINGS (int)] [STRING] [STRING] [STRING] ...
/// String array encoded in the byte array
protected internal string[] ReadStringArray()
{
string[] resultArray = new string[ReadInt()];
for (int i = 0; i < resultArray.Length; i++)
{
resultArray[i] = ReadString();
}
return resultArray;
}
///
/// float[]: [NUMBER_OF_FLOATS (int)] [FLOAT] [FLOAT] [FLOAT] ...
/// Float array encoded in the byte array.
protected internal float[] ReadFloatArray()
{
int count = ReadInt();
if (count < 0)
{
return null;
}
float[] result = new float[count];
for (int i = 0; i < result.Length; i++)
{
result[i] = ReadFloat();
}
return result;
}
///
/// Reads an encoded int
/// Int encoded in the byte array
protected internal int ReadInt()
{
byte b1 = data[Offset];
int result = data[Offset++] & 255;
if (result == 0)
{
return -1;
}
int size;
int bitmask = 128;
// single bit on the left
for (size = 1; size < 8; size++)
{
if ((result & bitmask) == bitmask)
{
break;
}
bitmask >>= 1;
}
if (size > 4)
{
// negative number
size -= 4;
result ^= bitmask;
result -= bitmask;
}
else
{
result ^= bitmask;
}
for (int i = 1; i < size; i++)
{
result = (result << 8) | (data[Offset++] & 255);
}
return result;
}
///
/// Reads a boolean Flag
///
protected internal bool ReadBoolean()
{
return ReadInt() != 0;
}
///
/// Reads an int encoded in classic 4-byte mode
/// Int encoded as 4-byte int from the byte array
protected internal int Read4ByteInt()
{
return (data[Offset++] << 24) + ((data[Offset++] & 255) << 16) + ((data[Offset++] & 255) << 8) + (data[Offset++] & 255);
}
///
/// Reads a float encoded as a classic 4-byte float
/// Float encoded as 4-byte
protected internal float ReadFloat()
{
// Read4ByteInt inversive
byte part1 = data[Offset++];
byte part2 = data[Offset++];
byte part3 = data[Offset++];
byte part4 = data[Offset++];
byte[] intReverse = { part4, part3, part2, part1 };
float f = BitConverter.ToSingle(intReverse, 0);
return f;
}
///
/// Reads a long encoded as a classic 8-byte long
/// Long encoded as 8-byte
protected internal long Read8ByteLong()
{
return ((long)Read4ByteInt()) << 32 | Read4ByteInt() & 0xFFFFFFFFL;
}
///
/// Reads a double encoded as a classic 8-byte double
/// double encoded as 8-byte
private double ReadDouble()
{
long l = Read8ByteLong();
double d = BitConverter.Int64BitsToDouble(l);
return d;
}
///
/// String: [NUMBEROFCHARS] [CHAR (2-byte)] [CHAR (2-byte)] [CHAR (2-byte)] ...
///
protected internal string ReadString()
{
int length = ReadInt();
if (length < 0)
{
return null;
}
char[] chars = new char[length];
for (int i = 0; i < length; i++)
{
chars[i] = (char)((data[Offset++] & 255) << 8 | (data[Offset++] & 255));
}
return new string(chars);
}
///
/// Equivalent to readInt.
/// Int
protected internal int ReadTwip()
{
return ReadInt();
}
///
/// Polyon: [COUNT_OF_POINTS] [X (int)] [Y (int)] [X (int)] [Y (int)] ...
/// A Ploygon with a Point array
protected internal Point[] ReadPolygon()
{
int count = ReadInt();
int[] xpoints = new int[count];
int[] ypoints = new int[count];
for (int i = 0; i < count; i++)
{
xpoints[i] = ReadTwip();
ypoints[i] = ReadTwip();
}
return new Point[3];
}
///
/// Shape: [NUMBER_OF_POINTS] [TYPE_OF_MOVEMENT] [PARAMS] [TYPE_OF_MOVEMENT] [PARAMS] ...
/// Shape encoded in the byte array
protected internal GraphicsPath ReadShape()
{
GraphicsPath path = new GraphicsPath();
path.FillMode = (FillMode)ReadInt();
float xPrev = 0;
float yPrev = 0;
while (true)
{
int type = ReadInt();
switch (type)
{
case -1:
return path;
case 0: // SEG_MOVETO
xPrev = ReadFloat();
yPrev = ReadFloat();
path.StartFigure();
break;
case 1: // SEG_LINETO
float x = ReadFloat();
float y = ReadFloat();
path.AddLine(xPrev, yPrev, x, y);
xPrev = x;
yPrev = y;
break;
case 2: // SEG_QUADTO:
float x1 = ReadFloat();
float y1 = ReadFloat();
float x2 = ReadFloat();
float y2 = ReadFloat();
path.AddBezier(xPrev, yPrev, (xPrev + x1 * 2f) / 3f, (yPrev + y1 * 2f) / 3f, (x2 + x1 * 2f) / 3f, (y2 + y1 * 2f) / 3f, x2, y2);
xPrev = x2;
yPrev = y2;
break;
case 3: // SEG_CUBICTO:
x1 = ReadFloat();
y1 = ReadFloat();
x2 = ReadFloat();
y2 = ReadFloat();
float x3 = ReadFloat();
float y3 = ReadFloat();
path.AddBezier(xPrev, yPrev, x1, y1, x2, y2, x3, y3);
xPrev = x3;
yPrev = y3;
break;
case 4: // SEG_CLOSE:
path.CloseFigure();
break;
}
}
}
///
/// Redas a Gradient Brush from the byte data
///
protected internal LinearGradientBrush ReadGradientPaint()
{
PointF p1 = new PointF(ReadFloat(), ReadFloat());
Color c1 = Color.FromArgb(Read4ByteInt());
PointF p2 = new PointF(ReadFloat(), ReadFloat());
Color c2 = Color.FromArgb(Read4ByteInt());
// Cyclic is not used at the moment
bool isCyclic = ReadBoolean();
LinearGradientBrush linear = new LinearGradientBrush(p1, p2, c1, c2);
return linear;
}
///
/// Reads a TextureBrush from the byte data
///
protected internal TextureBrush ReadTexturePaint(float alphaComposite)
{
RectangleF anchor = new Rectangle();
anchor.X = ReadFloat();
anchor.Y = ReadFloat();
anchor.Width = ReadFloat();
anchor.Height = ReadFloat();
byte[] imgBytes = ReadByteArray();
Image img = Image.FromStream(new MemoryStream(imgBytes));
System.Drawing.Imaging.ImageAttributes attr = new System.Drawing.Imaging.ImageAttributes();
TextureBrush brush = new TextureBrush(img, new Rectangle(0, 0, img.Width, img.Height), attr);
brush.TranslateTransform(anchor.X, anchor.Y);
brush.ScaleTransform(anchor.Width / img.Width, anchor.Height / img.Height);
brush.WrapMode = WrapMode.Tile;
return brush;
}
///
/// Redas a Gradient Brush from the byte data
/// And adds the alphaComposite as alpha value
///
protected internal LinearGradientBrush ReadGradientPaint(float alphaComposite)
{
PointF p1 = new PointF(ReadFloat(), ReadFloat());
Color c1 = Color.FromArgb(Read4ByteInt());
PointF p2 = new PointF(ReadFloat(), ReadFloat());
Color c2 = Color.FromArgb(Read4ByteInt());
// if alpha is set to transparency
if (alphaComposite < 1)
{
c1 = Color.FromArgb((int)(alphaComposite * 256), c1);
c2 = Color.FromArgb((int)(alphaComposite * 256), c2);
}
bool isCyclic = ReadBoolean();
if (isCyclic)
{
LinearGradientBrush linear = new LinearGradientBrush(p1, p2, c1, c2);
linear.WrapMode = WrapMode.TileFlipXY;
return linear;
}
else
{
// code from IKVM graphics.setPaint()
// a exact solution will calculate the size of the Graphics with the current transform
float diffX = p2.X - p1.X;
float diffY = p2.Y - p1.Y;
const float Z = 60; // HACK zoom factor, with a larger factor .NET will make the gradient wider.
LinearGradientBrush linear = new LinearGradientBrush(
new PointF(p1.X - Z * diffX, p1.Y - Z * diffY),
new PointF(p2.X + Z * diffX, p2.Y + Z * diffY),
c1,
c2);
ColorBlend colorBlend = new ColorBlend(4);
Color[] colors = colorBlend.Colors;
colors[0] = colors[1] = c1;
colors[2] = colors[3] = c2;
float[] positions = colorBlend.Positions;
positions[1] = Z / (2 * Z + 1);
positions[2] = (Z + 1) / (2 * Z + 1);
positions[3] = 1.0f;
linear.InterpolationColors = colorBlend;
return linear;
}
}
///
/// Byte Array: [NUMBER OF BYTES] [BYTE] [BYTE] [BYTE] ...
/// Byte array encoded in the data, or null if no byte array could be read
protected internal byte[] ReadByteArray()
{
int count = ReadInt();
// data length
if (count < 1 || count > data.Length - Offset)
{
ViewerUtils.PrintStackTrace(new System.Exception("Invalid byte array size: " + count + ". Amount of available bytes: " + (data.Length - Offset)));
return null;
}
else
{
byte[] byteData = new byte[count];
// Image-Data
System.Array.Copy(data, Offset, byteData, 0, count);
int code = 0;
// Image img = readImage(imgBytes); // -> readIndexImage oder readDirectImage
for (int i = 0; i < count; i++)
{
code += byteData[i];
}
return byteData;
}
}
///
/// AffineTransform: m00, m10, m20, m01, m11, m21
/// AffineTransform encoded in the byte array
protected internal Matrix ReadTransform()
{
return new Matrix((float)ReadDouble(), (float)ReadDouble(), (float)ReadDouble(), (float)ReadDouble(), (float)ReadDouble(), (float)ReadDouble());
}
///
/// Generates a hex dump as a string in error cases
/// Buffer to dump
/// Start offset to dump
/// How many bytes to dump
/// Hex dump as String
protected static internal string GetHexDump(byte[] buf, int start, int count)
{
System.Text.StringBuilder sb = new System.Text.StringBuilder(count);
byte[] buffer = (byte[])buf.Clone();
count += start;
for (int i = start; i < count; i += 16)
{
sb.Append(Hex((byte)(i >> 8))).Append(Hex((byte)i)).Append(" ");
FormatHexDump(buffer, i, count - i > 16 ? 16 : count - i, sb);
}
sb.Append("\r\n");
return sb.ToString();
}
///
/// Format the byte buffer as hex dump.
///
/// the byte buffer to dump
/// the first index of the byte array
/// the length
/// the string builder to write to
private static void FormatHexDump(byte[] buffer, int offset, int count, System.Text.StringBuilder sb)
{
int i;
for (i = offset; i < offset + count; i++)
{
sb.Append(Hex(buffer[i])).Append(' ');
if (buffer[i] < 32)
{
buffer[i] = 46;
}
}
for (; i < offset + 16; i++)
{
sb.Append(" ");
}
sb.Append(' ');
sb.Append(System.Text.Encoding.UTF8.GetString(buffer, offset, count));
sb.Append("\r\n");
}
///
/// Converts a byte to a char.
///
/// value to convert
/// string representation of the byte
private static string Hex(byte wert)
{
string a;
int lo = (wert & 240) / 16;
if (lo < 10)
{
a = lo.ToString();
}
else
{
a = string.Empty + (char)(55 + lo);
}
lo = wert & 15;
if (lo < 10)
{
a = a + lo.ToString();
}
else
{
a = a + (char)(55 + lo);
}
return a;
}
}
}