import java.awt.Color;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;



public class JMXPSingleClass extends NAME IT
{
	 // singletone routines
	 private static MXPColors allcolors;
	public static int Bold = 0x01;
	public static int Italic = 0x02;
	public static int Underline = 0x04;
	public static int Strikeout = 0x08;
		
	//----------------------------------------------------------------------------------------------------
	// enums
	//align type for internal windows and images (type Middle is only valid for images)
	enum alignType {
		nop,
		Left,
		Right,
		Bottom,
		Top,
		Middle
	};	 
	enum mxpMode { openMode, secureMode, lockedMode};
	enum parserState {
		  pText,
		  pAnsiSeq,
		  pTag,
		  pComment,
		  pQuotedParam
		};
	enum chunkType {
		  chunkNone,
		  chunkText,
		  chunkTag,
		  chunkError
		};		
	  enum tagParserState {
	    tagBegin,
	    tagName,
	    tagParam,
	    tagParamValue,
	    tagQuotedParam,
	    tagAfterQuotedParam,
	    tagBetweenParams
	  };
	  
	  enum paramParserState {
		  parNone,
		  parName,
		  parValue,
		  parQuotedValue
		};	
		//----------------------------------------------------------------------------------------------------
		//----------------------------------------------------------------------------------------------------
		private class windowStruct
		{
			  public String name, title;
			  public int left, top, width, height;
			  public boolean scrolling, floating;
		}
		//----------------------------------------------------------------------------------------------------
		private class internalWindowStruct 
		{
			  public String name, title;
			  public alignType align;
			  public boolean scrolling;
		}
		//----------------------------------------------------------------------------------------------------
		private class relocateStruct 
		{
			public String server;
			public int port;
		}
		//----------------------------------------------------------------------------------------------------
		private class imageStruct 
		{
			  public String fname, url, type;
			  public int height, width, hspace, vspace;
			  public alignType align;
		}
		//----------------------------------------------------------------------------------------------------
		private class moveStruct 
		{
			public int x, y;
		}
		//----------------------------------------------------------------------------------------------------
		private class soundStruct 
		{
			public boolean isSOUND;         //1 if SOUND, 0 if MUSIC
			public String fname, url;    //(fName and U params)
			public int vol;              //volume (V param)
			public int repeats;          //-1 for infinite (L param)
			public int priority;         //0-100; SOUND only (P param)
			public boolean continuemusic;   //continue without restarting if rerequested? MUSIC only (C param)
			public String type;           //sound/music type (T param)
		}
		//----------------------------------------------------------------------------------------------------
		private class statStruct 
		{
			  public String variable, maxvariable, caption;
		}
		//----------------------------------------------------------------------------------------------------
		private class gaugeStruct 
		{
			  public String variable, maxvariable, caption;
			  public Color color;
		}
		//----------------------------------------------------------------------------------------------------
	private class SendStruct {
		public String name, command, text, hint;
		public boolean toprompt, ismenu;
	}
	//----------------------------------------------------------------------------------------------------
	private class linkStruct 
	{
		public String name, url, text, hint;
	}
	//----------------------------------------------------------------------------------------------------
	private class formatStruct 
	{
		public static final int USE_BOLD=0x01;
		public static final int USE_ITALICS=0x02;
		public static final int USE_UNDERLINE=0x04;
		public static final int USE_STRIKEOUT=0x08;
		public static final int USE_FG=0x10;
		public static final int USE_BG=0x20;
		public static final int USE_FONT=0x40;
		public static final int USE_SIZE=0x80;
		public static final int USE_ALL=0xFF;
		
		public int usemask;    //8-bit; which params should be applied
		public int attributes;
		public Color fg, bg;
		public String font; //if NULL and it should be applied => default font should be set
		public int size;
	}
	//----------------------------------------------------------------------------------------------------
	private class flagStruct 
	{
		public boolean begin;
		public String name;
	}
	//----------------------------------------------------------------------------------------------------
	private class varStruct 
	{
		  public String name, value;
		  public boolean erase;
	};
	//----------------------------------------------------------------------------------------------------
	private class MXPColors 
	{
		 private final Color nocolor = new Color(0,0,0,0);
		 private String  COLOR_NAMES [] = { 
				  "snow",
				  "ghostwhite",
				  "whitesmoke",
				  "gainsboro",
				  "floralwhite",
				  "oldlace",
				  "linen",
				  "antiquewhite",
				  "papayawhip",
				  "blanchedalmond",
				  "bisque",
				  "peachpuff",
				  "navajowhite",
				  "moccasin",
				  "cornsilk",
				  "ivory",
				  "lemonchiffon",
				  "seashell",
				  "honeydew",
				  "mintcream",
				  "azure",
				  "aliceblue",
				  "lavender",
				  "lavenderblush",
				  "mistyrose",
				  "white",
				  "black",
				  "darkslategray",
				  "darkslategrey",
				  "dimgray",
				  "dimgrey",
				  "slategray",
				  "slategrey",
				  "lightslategray",
				  "lightslategrey",
				  "gray",
				  "grey",
				  "lightgrey",
				  "lightgray",
				  "midnightblue",
				  "navy",
				  "navyblue",
				  "cornflowerblue",
				  "darkslateblue",
				  "slateblue",
				  "mediumslateblue",
				  "lightslateblue",
				  "mediumblue",
				  "royalblue",
				  "blue",
				  "dodgerblue",
				  "deepskyblue",
				  "skyblue",
				  "lightskyblue",
				  "steelblue",
				  "lightsteelblue",
				  "lightblue",
				  "powderblue",
				  "paleturquoise",
				  "darkturquoise",
				  "mediumturquoise",
				  "turquoise",
				  "cyan",
				  "lightcyan",
				  "cadetblue",
				  "mediumaquamarine",
				  "aquamarine",
				  "darkgreen",
				  "darkolivegreen",
				  "darkseagreen",
				  "seagreen",
				  "mediumseagreen",
				  "lightseagreen",
				  "palegreen",
				  "springgreen",
				  "lawngreen",
				  "green",
				  "chartreuse",
				  "mediumspringgreen",
				  "greenyellow",
				  "limegreen",
				  "yellowgreen",
				  "forestgreen",
				  "olivedrab",
				  "darkkhaki",
				  "khaki",
				  "palegoldenrod",
				  "lightgoldenrodyellow",
				  "lightyellow",
				  "yellow",
				  "gold",
				  "lightgoldenrod",
				  "goldenrod",
				  "darkgoldenrod",
				  "rosybrown",
				  "indianred",
				  "saddlebrown",
				  "sienna",
				  "peru",
				  "burlywood",
				  "beige",
				  "wheat",
				  "sandybrown",
				  "tan",
				  "chocolate",
				  "firebrick",
				  "brown",
				  "darksalmon",
				  "salmon",
				  "lightsalmon",
				  "orange",
				  "darkorange",
				  "coral",
				  "lightcoral",
				  "tomato",
				  "orangered",
				  "red",
				  "hotpink",
				  "deeppink",
				  "pink",
				  "lightpink",
				  "palevioletred",
				  "maroon",
				  "mediumvioletred",
				  "violetred",
				  "magenta",
				  "violet",
				  "plum",
				  "orchid",
				  "mediumorchid",
				  "darkorchid",
				  "darkviolet",
				  "blueviolet",
				  "purple",
				  "mediumpurple",
				  "thistle",
				  "snow1",
				  "snow2",
				  "snow3",
				  "snow4",
				  "seashell1",
				  "seashell2",
				  "seashell3",
				  "seashell4",
				  "antiquewhite1",
				  "antiquewhite2",
				  "antiquewhite3",
				  "antiquewhite4",
				  "bisque1",
				  "bisque2",
				  "bisque3",
				  "bisque4",
				  "peachpuff1",
				  "peachpuff2",
				  "peachpuff3",
				  "peachpuff4",
				  "navajowhite1",
				  "navajowhite2",
				  "navajowhite3",
				  "navajowhite4",
				  "lemonchiffon1",
				  "lemonchiffon2",
				  "lemonchiffon3",
				  "lemonchiffon4",
				  "cornsilk1",
				  "cornsilk2",
				  "cornsilk3",
				  "cornsilk4",
				  "ivory1",
				  "ivory2",
				  "ivory3",
				  "ivory4",
				  "honeydew1",
				  "honeydew2",
				  "honeydew3",
				  "honeydew4",
				  "lavenderblush1",
				  "lavenderblush2",
				  "lavenderblush3",
				  "lavenderblush4",
				  "mistyrose1",
				  "mistyrose2",
				  "mistyrose3",
				  "mistyrose4",
				  "azure1",
				  "azure2",
				  "azure3",
				  "azure4",
				  "slateblue1",
				  "slateblue2",
				  "slateblue3",
				  "slateblue4",
				  "royalblue1",
				  "royalblue2",
				  "royalblue3",
				  "royalblue4",
				  "blue1",
				  "blue2",
				  "blue3",
				  "blue4",
				  "dodgerblue1",
				  "dodgerblue2",
				  "dodgerblue3",
				  "dodgerblue4",
				  "steelblue1",
				  "steelblue2",
				  "steelblue3",
				  "steelblue4",
				  "deepskyblue1",
				  "deepskyblue2",
				  "deepskyblue3",
				  "deepskyblue4",
				  "skyblue1",
				  "skyblue2",
				  "skyblue3",
				  "skyblue4",
				  "lightskyblue1",
				  "lightskyblue2",
				  "lightskyblue3",
				  "lightskyblue4",
				  "slategray1",
				  "slategray2",
				  "slategray3",
				  "slategray4",
				  "lightsteelblue1",
				  "lightsteelblue2",
				  "lightsteelblue3",
				  "lightsteelblue4",
				  "lightblue1",
				  "lightblue2",
				  "lightblue3",
				  "lightblue4",
				  "lightcyan1",
				  "lightcyan2",
				  "lightcyan3",
				  "lightcyan4",
				  "paleturquoise1",
				  "paleturquoise2",
				  "paleturquoise3",
				  "paleturquoise4",
				  "cadetblue1",
				  "cadetblue2",
				  "cadetblue3",
				  "cadetblue4",
				  "turquoise1",
				  "turquoise2",
				  "turquoise3",
				  "turquoise4",
				  "cyan1",
				  "cyan2",
				  "cyan3",
				  "cyan4",
				  "darkslategray1",
				  "darkslategray2",
				  "darkslategray3",
				  "darkslategray4",
				  "aquamarine1",
				  "aquamarine2",
				  "aquamarine3",
				  "aquamarine4",
				  "darkseagreen1",
				  "darkseagreen2",
				  "darkseagreen3",
				  "darkseagreen4",
				  "seagreen1",
				  "seagreen2",
				  "seagreen3",
				  "seagreen4",
				  "palegreen1",
				  "palegreen2",
				  "palegreen3",
				  "palegreen4",
				  "springgreen1",
				  "springgreen2",
				  "springgreen3",
				  "springgreen4",
				  "green1",
				  "green2",
				  "green3",
				  "green4",
				  "chartreuse1",
				  "chartreuse2",
				  "chartreuse3",
				  "chartreuse4",
				  "olivedrab1",
				  "olivedrab2",
				  "olivedrab3",
				  "olivedrab4",
				  "darkolivegreen1",
				  "darkolivegreen2",
				  "darkolivegreen3",
				  "darkolivegreen4",
				  "khaki1",
				  "khaki2",
				  "khaki3",
				  "khaki4",
				  "lightgoldenrod1",
				  "lightgoldenrod2",
				  "lightgoldenrod3",
				  "lightgoldenrod4",
				  "lightyellow1",
				  "lightyellow2",
				  "lightyellow3",
				  "lightyellow4",
				  "yellow1",
				  "yellow2",
				  "yellow3",
				  "yellow4",
				  "gold1",
				  "gold2",
				  "gold3",
				  "gold4",
				  "goldenrod1",
				  "goldenrod2",
				  "goldenrod3",
				  "goldenrod4",
				  "darkgoldenrod1",
				  "darkgoldenrod2",
				  "darkgoldenrod3",
				  "darkgoldenrod4",
				  "rosybrown1",
				  "rosybrown2",
				  "rosybrown3",
				  "rosybrown4",
				  "indianred1",
				  "indianred2",
				  "indianred3",
				  "indianred4",
				  "sienna1",
				  "sienna2",
				  "sienna3",
				  "sienna4",
				  "burlywood1",
				  "burlywood2",
				  "burlywood3",
				  "burlywood4",
				  "wheat1",
				  "wheat2",
				  "wheat3",
				  "wheat4",
				  "tan1",
				  "tan2",
				  "tan3",
				  "tan4",
				  "chocolate1",
				  "chocolate2",
				  "chocolate3",
				  "chocolate4",
				  "firebrick1",
				  "firebrick2",
				  "firebrick3",
				  "firebrick4",
				  "brown1",
				  "brown2",
				  "brown3",
				  "brown4",
				  "salmon1",
				  "salmon2",
				  "salmon3",
				  "salmon4",
				  "lightsalmon1",
				  "lightsalmon2",
				  "lightsalmon3",
				  "lightsalmon4",
				  "orange1",
				  "orange2",
				  "orange3",
				  "orange4",
				  "darkorange1",
				  "darkorange2",
				  "darkorange3",
				  "darkorange4",
				  "coral1",
				  "coral2",
				  "coral3",
				  "coral4",
				  "tomato1",
				  "tomato2",
				  "tomato3",
				  "tomato4",
				  "orangered1",
				  "orangered2",
				  "orangered3",
				  "orangered4",
				  "red1",
				  "red2",
				  "red3",
				  "red4",
				  "deeppink1",
				  "deeppink2",
				  "deeppink3",
				  "deeppink4",
				  "hotpink1",
				  "hotpink2",
				  "hotpink3",
				  "hotpink4",
				  "pink1",
				  "pink2",
				  "pink3",
				  "pink4",
				  "lightpink1",
				  "lightpink2",
				  "lightpink3",
				  "lightpink4",
				  "palevioletred1",
				  "palevioletred2",
				  "palevioletred3",
				  "palevioletred4",
				  "maroon1",
				  "maroon2",
				  "maroon3",
				  "maroon4",
				  "violetred1",
				  "violetred2",
				  "violetred3",
				  "violetred4",
				  "magenta1",
				  "magenta2",
				  "magenta3",
				  "magenta4",
				  "orchid1",
				  "orchid2",
				  "orchid3",
				  "orchid4",
				  "plum1",
				  "plum2",
				  "plum3",
				  "plum4",
				  "mediumorchid1",
				  "mediumorchid2",
				  "mediumorchid3",
				  "mediumorchid4",
				  "darkorchid1",
				  "darkorchid2",
				  "darkorchid3",
				  "darkorchid4",
				  "purple1",
				  "purple2",
				  "purple3",
				  "purple4",
				  "mediumpurple1",
				  "mediumpurple2",
				  "mediumpurple3",
				  "mediumpurple4",
				  "thistle1",
				  "thistle2",
				  "thistle3",
				  "thistle4",
				  "gray0",
				  "grey0",
				  "gray1",
				  "grey1",
				  "gray2",
				  "grey2",
				  "gray3",
				  "grey3",
				  "gray4",
				  "grey4",
				  "gray5",
				  "grey5",
				  "gray6",
				  "grey6",
				  "gray7",
				  "grey7",
				  "gray8",
				  "grey8",
				  "gray9",
				  "grey9",
				  "gray10",
				  "grey10",
				  "gray11",
				  "grey11",
				  "gray12",
				  "grey12",
				  "gray13",
				  "grey13",
				  "gray14",
				  "grey14",
				  "gray15",
				  "grey15",
				  "gray16",
				  "grey16",
				  "gray17",
				  "grey17",
				  "gray18",
				  "grey18",
				  "gray19",
				  "grey19",
				  "gray20",
				  "grey20",
				  "gray21",
				  "grey21",
				  "gray22",
				  "grey22",
				  "gray23",
				  "grey23",
				  "gray24",
				  "grey24",
				  "gray25",
				  "grey25",
				  "gray26",
				  "grey26",
				  "gray27",
				  "grey27",
				  "gray28",
				  "grey28",
				  "gray29",
				  "grey29",
				  "gray30",
				  "grey30",
				  "gray31",
				  "grey31",
				  "gray32",
				  "grey32",
				  "gray33",
				  "grey33",
				  "gray34",
				  "grey34",
				  "gray35",
				  "grey35",
				  "gray36",
				  "grey36",
				  "gray37",
				  "grey37",
				  "gray38",
				  "grey38",
				  "gray39",
				  "grey39",
				  "gray40",
				  "grey40",
				  "gray41",
				  "grey41",
				  "gray42",
				  "grey42",
				  "gray43",
				  "grey43",
				  "gray44",
				  "grey44",
				  "gray45",
				  "grey45",
				  "gray46",
				  "grey46",
				  "gray47",
				  "grey47",
				  "gray48",
				  "grey48",
				  "gray49",
				  "grey49",
				  "gray50",
				  "grey50",
				  "gray51",
				  "grey51",
				  "gray52",
				  "grey52",
				  "gray53",
				  "grey53",
				  "gray54",
				  "grey54",
				  "gray55",
				  "grey55",
				  "gray56",
				  "grey56",
				  "gray57",
				  "grey57",
				  "gray58",
				  "grey58",
				  "gray59",
				  "grey59",
				  "gray60",
				  "grey60",
				  "gray61",
				  "grey61",
				  "gray62",
				  "grey62",
				  "gray63",
				  "grey63",
				  "gray64",
				  "grey64",
				  "gray65",
				  "grey65",
				  "gray66",
				  "grey66",
				  "gray67",
				  "grey67",
				  "gray68",
				  "grey68",
				  "gray69",
				  "grey69",
				  "gray70",
				  "grey70",
				  "gray71",
				  "grey71",
				  "gray72",
				  "grey72",
				  "gray73",
				  "grey73",
				  "gray74",
				  "grey74",
				  "gray75",
				  "grey75",
				  "gray76",
				  "grey76",
				  "gray77",
				  "grey77",
				  "gray78",
				  "grey78",
				  "gray79",
				  "grey79",
				  "gray80",
				  "grey80",
				  "gray81",
				  "grey81",
				  "gray82",
				  "grey82",
				  "gray83",
				  "grey83",
				  "gray84",
				  "grey84",
				  "gray85",
				  "grey85",
				  "gray86",
				  "grey86",
				  "gray87",
				  "grey87",
				  "gray88",
				  "grey88",
				  "gray89",
				  "grey89",
				  "gray90",
				  "grey90",
				  "gray91",
				  "grey91",
				  "gray92",
				  "grey92",
				  "gray93",
				  "grey93",
				  "gray94",
				  "grey94",
				  "gray95",
				  "grey95",
				  "gray96",
				  "grey96",
				  "gray97",
				  "grey97",
				  "gray98",
				  "grey98",
				  "gray99",
				  "grey99",
				  "gray100",
				  "grey100",
				  "darkgrey",
				  "darkgray",
				  "darkblue",
				  "darkcyan",
				  "darkmagenta",
				  "darkred",
				  "lightgreen",
				};

			Color COLOR_DEF[] = {
			  new Color(255, 250, 250),
			  new Color(248, 248, 255),
			  new Color(245, 245, 245),
			  new Color(220, 220, 220),
			  new Color(255, 250, 240),
			  new Color(253, 245, 230),
			  new Color(250, 240, 230),
			  new Color(250, 235, 215),
			  new Color(255, 239, 213),
			  new Color(255, 235, 205),
			  new Color(255, 228, 196),
			  new Color(255, 218, 185),
			  new Color(255, 222, 173),
			  new Color(255, 228, 181),
			  new Color(255, 248, 220),
			  new Color(255, 255, 240),
			  new Color(255, 250, 205),
			  new Color(255, 245, 238),
			  new Color(240, 255, 240),
			  new Color(245, 255, 250),
			  new Color(240, 255, 255),
			  new Color(240, 248, 255),
			  new Color(230, 230, 250),
			  new Color(255, 240, 245),
			  new Color(255, 228, 225),
			  new Color(255, 255, 255),
			  new Color(0, 0, 0),
			  new Color(47, 79, 79),
			  new Color(47, 79, 79),
			  new Color(105, 105, 105),
			  new Color(105, 105, 105),
			  new Color(112, 128, 144),
			  new Color(112, 128, 144),
			  new Color(119, 136, 153),
			  new Color(119, 136, 153),
			  new Color(190, 190, 190),
			  new Color(190, 190, 190),
			  new Color(211, 211, 211),
			  new Color(211, 211, 211),
			  new Color(25, 25, 112),
			  new Color(0, 0, 128),
			  new Color(0, 0, 128),
			  new Color(100, 149, 237),
			  new Color(72, 61, 139),
			  new Color(106, 90, 205),
			  new Color(123, 104, 238),
			  new Color(132, 112, 255),
			  new Color(0, 0, 205),
			  new Color(65, 105, 225),
			  new Color(0, 0, 255),
			  new Color(30, 144, 255),
			  new Color(0, 191, 255),
			  new Color(135, 206, 235),
			  new Color(135, 206, 250),
			  new Color(70, 130, 180),
			  new Color(176, 196, 222),
			  new Color(173, 216, 230),
			  new Color(176, 224, 230),
			  new Color(175, 238, 238),
			  new Color(0, 206, 209),
			  new Color(72, 209, 204),
			  new Color(64, 224, 208),
			  new Color(0, 255, 255),
			  new Color(224, 255, 255),
			  new Color(95, 158, 160),
			  new Color(102, 205, 170),
			  new Color(127, 255, 212),
			  new Color(0, 100, 0),
			  new Color(85, 107, 47),
			  new Color(143, 188, 143),
			  new Color(46, 139, 87),
			  new Color(60, 179, 113),
			  new Color(32, 178, 170),
			  new Color(152, 251, 152),
			  new Color(0, 255, 127),
			  new Color(124, 252, 0),
			  new Color(0, 255, 0),
			  new Color(127, 255, 0),
			  new Color(0, 250, 154),
			  new Color(173, 255, 47),
			  new Color(50, 205, 50),
			  new Color(154, 205, 50),
			  new Color(34, 139, 34),
			  new Color(107, 142, 35),
			  new Color(189, 183, 107),
			  new Color(240, 230, 140),
			  new Color(238, 232, 170),
			  new Color(250, 250, 210),
			  new Color(255, 255, 224),
			  new Color(255, 255, 0),
			  new Color(255, 215, 0),
			  new Color(238, 221, 130),
			  new Color(218, 165, 32),
			  new Color(184, 134, 11),
			  new Color(188, 143, 143),
			  new Color(205, 92, 92),
			  new Color(139, 69, 19),
			  new Color(160, 82, 45),
			  new Color(205, 133, 63),
			  new Color(222, 184, 135),
			  new Color(245, 245, 220),
			  new Color(245, 222, 179),
			  new Color(244, 164, 96),
			  new Color(210, 180, 140),
			  new Color(210, 105, 30),
			  new Color(178, 34, 34),
			  new Color(165, 42, 42),
			  new Color(233, 150, 122),
			  new Color(250, 128, 114),
			  new Color(255, 160, 122),
			  new Color(255, 165, 0),
			  new Color(255, 140, 0),
			  new Color(255, 127, 80),
			  new Color(240, 128, 128),
			  new Color(255, 99, 71),
			  new Color(255, 69, 0),
			  new Color(255, 0, 0),
			  new Color(255, 105, 180),
			  new Color(255, 20, 147),
			  new Color(255, 192, 203),
			  new Color(255, 182, 193),
			  new Color(219, 112, 147),
			  new Color(176, 48, 96),
			  new Color(199, 21, 133),
			  new Color(208, 32, 144),
			  new Color(255, 0, 255),
			  new Color(238, 130, 238),
			  new Color(221, 160, 221),
			  new Color(218, 112, 214),
			  new Color(186, 85, 211),
			  new Color(153, 50, 204),
			  new Color(148, 0, 211),
			  new Color(138, 43, 226),
			  new Color(160, 32, 240),
			  new Color(147, 112, 219),
			  new Color(216, 191, 216),
			  new Color(255, 250, 250),
			  new Color(238, 233, 233),
			  new Color(205, 201, 201),
			  new Color(139, 137, 137),
			  new Color(255, 245, 238),
			  new Color(238, 229, 222),
			  new Color(205, 197, 191),
			  new Color(139, 134, 130),
			  new Color(255, 239, 219),
			  new Color(238, 223, 204),
			  new Color(205, 192, 176),
			  new Color(139, 131, 120),
			  new Color(255, 228, 196),
			  new Color(238, 213, 183),
			  new Color(205, 183, 158),
			  new Color(139, 125, 107),
			  new Color(255, 218, 185),
			  new Color(238, 203, 173),
			  new Color(205, 175, 149),
			  new Color(139, 119, 101),
			  new Color(255, 222, 173),
			  new Color(238, 207, 161),
			  new Color(205, 179, 139),
			  new Color(139, 121, 94),
			  new Color(255, 250, 205),
			  new Color(238, 233, 191),
			  new Color(205, 201, 165),
			  new Color(139, 137, 112),
			  new Color(255, 248, 220),
			  new Color(238, 232, 205),
			  new Color(205, 200, 177),
			  new Color(139, 136, 120),
			  new Color(255, 255, 240),
			  new Color(238, 238, 224),
			  new Color(205, 205, 193),
			  new Color(139, 139, 131),
			  new Color(240, 255, 240),
			  new Color(224, 238, 224),
			  new Color(193, 205, 193),
			  new Color(131, 139, 131),
			  new Color(255, 240, 245),
			  new Color(238, 224, 229),
			  new Color(205, 193, 197),
			  new Color(139, 131, 134),
			  new Color(255, 228, 225),
			  new Color(238, 213, 210),
			  new Color(205, 183, 181),
			  new Color(139, 125, 123),
			  new Color(240, 255, 255),
			  new Color(224, 238, 238),
			  new Color(193, 205, 205),
			  new Color(131, 139, 139),
			  new Color(131, 111, 255),
			  new Color(122, 103, 238),
			  new Color(105, 89, 205),
			  new Color(71, 60, 139),
			  new Color(72, 118, 255),
			  new Color(67, 110, 238),
			  new Color(58, 95, 205),
			  new Color(39, 64, 139),
			  new Color(0, 0, 255),
			  new Color(0, 0, 238),
			  new Color(0, 0, 205),
			  new Color(0, 0, 139),
			  new Color(30, 144, 255),
			  new Color(28, 134, 238),
			  new Color(24, 116, 205),
			  new Color(16, 78, 139),
			  new Color(99, 184, 255),
			  new Color(92, 172, 238),
			  new Color(79, 148, 205),
			  new Color(54, 100, 139),
			  new Color(0, 191, 255),
			  new Color(0, 178, 238),
			  new Color(0, 154, 205),
			  new Color(0, 104, 139),
			  new Color(135, 206, 255),
			  new Color(126, 192, 238),
			  new Color(108, 166, 205),
			  new Color(74, 112, 139),
			  new Color(176, 226, 255),
			  new Color(164, 211, 238),
			  new Color(141, 182, 205),
			  new Color(96, 123, 139),
			  new Color(198, 226, 255),
			  new Color(185, 211, 238),
			  new Color(159, 182, 205),
			  new Color(108, 123, 139),
			  new Color(202, 225, 255),
			  new Color(188, 210, 238),
			  new Color(162, 181, 205),
			  new Color(110, 123, 139),
			  new Color(191, 239, 255),
			  new Color(178, 223, 238),
			  new Color(154, 192, 205),
			  new Color(104, 131, 139),
			  new Color(224, 255, 255),
			  new Color(209, 238, 238),
			  new Color(180, 205, 205),
			  new Color(122, 139, 139),
			  new Color(187, 255, 255),
			  new Color(174, 238, 238),
			  new Color(150, 205, 205),
			  new Color(102, 139, 139),
			  new Color(152, 245, 255),
			  new Color(142, 229, 238),
			  new Color(122, 197, 205),
			  new Color(83, 134, 139),
			  new Color(0, 245, 255),
			  new Color(0, 229, 238),
			  new Color(0, 197, 205),
			  new Color(0, 134, 139),
			  new Color(0, 255, 255),
			  new Color(0, 238, 238),
			  new Color(0, 205, 205),
			  new Color(0, 139, 139),
			  new Color(151, 255, 255),
			  new Color(141, 238, 238),
			  new Color(121, 205, 205),
			  new Color(82, 139, 139),
			  new Color(127, 255, 212),
			  new Color(118, 238, 198),
			  new Color(102, 205, 170),
			  new Color(69, 139, 116),
			  new Color(193, 255, 193),
			  new Color(180, 238, 180),
			  new Color(155, 205, 155),
			  new Color(105, 139, 105),
			  new Color(84, 255, 159),
			  new Color(78, 238, 148),
			  new Color(67, 205, 128),
			  new Color(46, 139, 87),
			  new Color(154, 255, 154),
			  new Color(144, 238, 144),
			  new Color(124, 205, 124),
			  new Color(84, 139, 84),
			  new Color(0, 255, 127),
			  new Color(0, 238, 118),
			  new Color(0, 205, 102),
			  new Color(0, 139, 69),
			  new Color(0, 255, 0),
			  new Color(0, 238, 0),
			  new Color(0, 205, 0),
			  new Color(0, 139, 0),
			  new Color(127, 255, 0),
			  new Color(118, 238, 0),
			  new Color(102, 205, 0),
			  new Color(69, 139, 0),
			  new Color(192, 255, 62),
			  new Color(179, 238, 58),
			  new Color(154, 205, 50),
			  new Color(105, 139, 34),
			  new Color(202, 255, 112),
			  new Color(188, 238, 104),
			  new Color(162, 205, 90),
			  new Color(110, 139, 61),
			  new Color(255, 246, 143),
			  new Color(238, 230, 133),
			  new Color(205, 198, 115),
			  new Color(139, 134, 78),
			  new Color(255, 236, 139),
			  new Color(238, 220, 130),
			  new Color(205, 190, 112),
			  new Color(139, 129, 76),
			  new Color(255, 255, 224),
			  new Color(238, 238, 209),
			  new Color(205, 205, 180),
			  new Color(139, 139, 122),
			  new Color(255, 255, 0),
			  new Color(238, 238, 0),
			  new Color(205, 205, 0),
			  new Color(139, 139, 0),
			  new Color(255, 215, 0),
			  new Color(238, 201, 0),
			  new Color(205, 173, 0),
			  new Color(139, 117, 0),
			  new Color(255, 193, 37),
			  new Color(238, 180, 34),
			  new Color(205, 155, 29),
			  new Color(139, 105, 20),
			  new Color(255, 185, 15),
			  new Color(238, 173, 14),
			  new Color(205, 149, 12),
			  new Color(139, 101, 8),
			  new Color(255, 193, 193),
			  new Color(238, 180, 180),
			  new Color(205, 155, 155),
			  new Color(139, 105, 105),
			  new Color(255, 106, 106),
			  new Color(238, 99, 99),
			  new Color(205, 85, 85),
			  new Color(139, 58, 58),
			  new Color(255, 130, 71),
			  new Color(238, 121, 66),
			  new Color(205, 104, 57),
			  new Color(139, 71, 38),
			  new Color(255, 211, 155),
			  new Color(238, 197, 145),
			  new Color(205, 170, 125),
			  new Color(139, 115, 85),
			  new Color(255, 231, 186),
			  new Color(238, 216, 174),
			  new Color(205, 186, 150),
			  new Color(139, 126, 102),
			  new Color(255, 165, 79),
			  new Color(238, 154, 73),
			  new Color(205, 133, 63),
			  new Color(139, 90, 43),
			  new Color(255, 127, 36),
			  new Color(238, 118, 33),
			  new Color(205, 102, 29),
			  new Color(139, 69, 19),
			  new Color(255, 48, 48),
			  new Color(238, 44, 44),
			  new Color(205, 38, 38),
			  new Color(139, 26, 26),
			  new Color(255, 64, 64),
			  new Color(238, 59, 59),
			  new Color(205, 51, 51),
			  new Color(139, 35, 35),
			  new Color(255, 140, 105),
			  new Color(238, 130, 98),
			  new Color(205, 112, 84),
			  new Color(139, 76, 57),
			  new Color(255, 160, 122),
			  new Color(238, 149, 114),
			  new Color(205, 129, 98),
			  new Color(139, 87, 66),
			  new Color(255, 165, 0),
			  new Color(238, 154, 0),
			  new Color(205, 133, 0),
			  new Color(139, 90, 0),
			  new Color(255, 127, 0),
			  new Color(238, 118, 0),
			  new Color(205, 102, 0),
			  new Color(139, 69, 0),
			  new Color(255, 114, 86),
			  new Color(238, 106, 80),
			  new Color(205, 91, 69),
			  new Color(139, 62, 47),
			  new Color(255, 99, 71),
			  new Color(238, 92, 66),
			  new Color(205, 79, 57),
			  new Color(139, 54, 38),
			  new Color(255, 69, 0),
			  new Color(238, 64, 0),
			  new Color(205, 55, 0),
			  new Color(139, 37, 0),
			  new Color(255, 0, 0),
			  new Color(238, 0, 0),
			  new Color(205, 0, 0),
			  new Color(139, 0, 0),
			  new Color(255, 20, 147),
			  new Color(238, 18, 137),
			  new Color(205, 16, 118),
			  new Color(139, 10, 80),
			  new Color(255, 110, 180),
			  new Color(238, 106, 167),
			  new Color(205, 96, 144),
			  new Color(139, 58, 98),
			  new Color(255, 181, 197),
			  new Color(238, 169, 184),
			  new Color(205, 145, 158),
			  new Color(139, 99, 108),
			  new Color(255, 174, 185),
			  new Color(238, 162, 173),
			  new Color(205, 140, 149),
			  new Color(139, 95, 101),
			  new Color(255, 130, 171),
			  new Color(238, 121, 159),
			  new Color(205, 104, 137),
			  new Color(139, 71, 93),
			  new Color(255, 52, 179),
			  new Color(238, 48, 167),
			  new Color(205, 41, 144),
			  new Color(139, 28, 98),
			  new Color(255, 62, 150),
			  new Color(238, 58, 140),
			  new Color(205, 50, 120),
			  new Color(139, 34, 82),
			  new Color(255, 0, 255),
			  new Color(238, 0, 238),
			  new Color(205, 0, 205),
			  new Color(139, 0, 139),
			  new Color(255, 131, 250),
			  new Color(238, 122, 233),
			  new Color(205, 105, 201),
			  new Color(139, 71, 137),
			  new Color(255, 187, 255),
			  new Color(238, 174, 238),
			  new Color(205, 150, 205),
			  new Color(139, 102, 139),
			  new Color(224, 102, 255),
			  new Color(209, 95, 238),
			  new Color(180, 82, 205),
			  new Color(122, 55, 139),
			  new Color(191, 62, 255),
			  new Color(178, 58, 238),
			  new Color(154, 50, 205),
			  new Color(104, 34, 139),
			  new Color(155, 48, 255),
			  new Color(145, 44, 238),
			  new Color(125, 38, 205),
			  new Color(85, 26, 139),
			  new Color(171, 130, 255),
			  new Color(159, 121, 238),
			  new Color(137, 104, 205),
			  new Color(93, 71, 139),
			  new Color(255, 225, 255),
			  new Color(238, 210, 238),
			  new Color(205, 181, 205),
			  new Color(139, 123, 139),
			  new Color(0, 0, 0),
			  new Color(0, 0, 0),
			  new Color(3, 3, 3),
			  new Color(3, 3, 3),
			  new Color(5, 5, 5),
			  new Color(5, 5, 5),
			  new Color(8, 8, 8),
			  new Color(8, 8, 8),
			  new Color(10, 10, 10),
			  new Color(10, 10, 10),
			  new Color(13, 13, 13),
			  new Color(13, 13, 13),
			  new Color(15, 15, 15),
			  new Color(15, 15, 15),
			  new Color(18, 18, 18),
			  new Color(18, 18, 18),
			  new Color(20, 20, 20),
			  new Color(20, 20, 20),
			  new Color(23, 23, 23),
			  new Color(23, 23, 23),
			  new Color(26, 26, 26),
			  new Color(26, 26, 26),
			  new Color(28, 28, 28),
			  new Color(28, 28, 28),
			  new Color(31, 31, 31),
			  new Color(31, 31, 31),
			  new Color(33, 33, 33),
			  new Color(33, 33, 33),
			  new Color(36, 36, 36),
			  new Color(36, 36, 36),
			  new Color(38, 38, 38),
			  new Color(38, 38, 38),
			  new Color(41, 41, 41),
			  new Color(41, 41, 41),
			  new Color(43, 43, 43),
			  new Color(43, 43, 43),
			  new Color(46, 46, 46),
			  new Color(46, 46, 46),
			  new Color(48, 48, 48),
			  new Color(48, 48, 48),
			  new Color(51, 51, 51),
			  new Color(51, 51, 51),
			  new Color(54, 54, 54),
			  new Color(54, 54, 54),
			  new Color(56, 56, 56),
			  new Color(56, 56, 56),
			  new Color(59, 59, 59),
			  new Color(59, 59, 59),
			  new Color(61, 61, 61),
			  new Color(61, 61, 61),
			  new Color(64, 64, 64),
			  new Color(64, 64, 64),
			  new Color(66, 66, 66),
			  new Color(66, 66, 66),
			  new Color(69, 69, 69),
			  new Color(69, 69, 69),
			  new Color(71, 71, 71),
			  new Color(71, 71, 71),
			  new Color(74, 74, 74),
			  new Color(74, 74, 74),
			  new Color(77, 77, 77),
			  new Color(77, 77, 77),
			  new Color(79, 79, 79),
			  new Color(79, 79, 79),
			  new Color(82, 82, 82),
			  new Color(82, 82, 82),
			  new Color(84, 84, 84),
			  new Color(84, 84, 84),
			  new Color(87, 87, 87),
			  new Color(87, 87, 87),
			  new Color(89, 89, 89),
			  new Color(89, 89, 89),
			  new Color(92, 92, 92),
			  new Color(92, 92, 92),
			  new Color(94, 94, 94),
			  new Color(94, 94, 94),
			  new Color(97, 97, 97),
			  new Color(97, 97, 97),
			  new Color(99, 99, 99),
			  new Color(99, 99, 99),
			  new Color(102, 102, 102),
			  new Color(102, 102, 102),
			  new Color(105, 105, 105),
			  new Color(105, 105, 105),
			  new Color(107, 107, 107),
			  new Color(107, 107, 107),
			  new Color(110, 110, 110),
			  new Color(110, 110, 110),
			  new Color(112, 112, 112),
			  new Color(112, 112, 112),
			  new Color(115, 115, 115),
			  new Color(115, 115, 115),
			  new Color(117, 117, 117),
			  new Color(117, 117, 117),
			  new Color(120, 120, 120),
			  new Color(120, 120, 120),
			  new Color(122, 122, 122),
			  new Color(122, 122, 122),
			  new Color(125, 125, 125),
			  new Color(125, 125, 125),
			  new Color(127, 127, 127),
			  new Color(127, 127, 127),
			  new Color(130, 130, 130),
			  new Color(130, 130, 130),
			  new Color(133, 133, 133),
			  new Color(133, 133, 133),
			  new Color(135, 135, 135),
			  new Color(135, 135, 135),
			  new Color(138, 138, 138),
			  new Color(138, 138, 138),
			  new Color(140, 140, 140),
			  new Color(140, 140, 140),
			  new Color(143, 143, 143),
			  new Color(143, 143, 143),
			  new Color(145, 145, 145),
			  new Color(145, 145, 145),
			  new Color(148, 148, 148),
			  new Color(148, 148, 148),
			  new Color(150, 150, 150),
			  new Color(150, 150, 150),
			  new Color(153, 153, 153),
			  new Color(153, 153, 153),
			  new Color(156, 156, 156),
			  new Color(156, 156, 156),
			  new Color(158, 158, 158),
			  new Color(158, 158, 158),
			  new Color(161, 161, 161),
			  new Color(161, 161, 161),
			  new Color(163, 163, 163),
			  new Color(163, 163, 163),
			  new Color(166, 166, 166),
			  new Color(166, 166, 166),
			  new Color(168, 168, 168),
			  new Color(168, 168, 168),
			  new Color(171, 171, 171),
			  new Color(171, 171, 171),
			  new Color(173, 173, 173),
			  new Color(173, 173, 173),
			  new Color(176, 176, 176),
			  new Color(176, 176, 176),
			  new Color(179, 179, 179),
			  new Color(179, 179, 179),
			  new Color(181, 181, 181),
			  new Color(181, 181, 181),
			  new Color(184, 184, 184),
			  new Color(184, 184, 184),
			  new Color(186, 186, 186),
			  new Color(186, 186, 186),
			  new Color(189, 189, 189),
			  new Color(189, 189, 189),
			  new Color(191, 191, 191),
			  new Color(191, 191, 191),
			  new Color(194, 194, 194),
			  new Color(194, 194, 194),
			  new Color(196, 196, 196),
			  new Color(196, 196, 196),
			  new Color(199, 199, 199),
			  new Color(199, 199, 199),
			  new Color(201, 201, 201),
			  new Color(201, 201, 201),
			  new Color(204, 204, 204),
			  new Color(204, 204, 204),
			  new Color(207, 207, 207),
			  new Color(207, 207, 207),
			  new Color(209, 209, 209),
			  new Color(209, 209, 209),
			  new Color(212, 212, 212),
			  new Color(212, 212, 212),
			  new Color(214, 214, 214),
			  new Color(214, 214, 214),
			  new Color(217, 217, 217),
			  new Color(217, 217, 217),
			  new Color(219, 219, 219),
			  new Color(219, 219, 219),
			  new Color(222, 222, 222),
			  new Color(222, 222, 222),
			  new Color(224, 224, 224),
			  new Color(224, 224, 224),
			  new Color(227, 227, 227),
			  new Color(227, 227, 227),
			  new Color(229, 229, 229),
			  new Color(229, 229, 229),
			  new Color(232, 232, 232),
			  new Color(232, 232, 232),
			  new Color(235, 235, 235),
			  new Color(235, 235, 235),
			  new Color(237, 237, 237),
			  new Color(237, 237, 237),
			  new Color(240, 240, 240),
			  new Color(240, 240, 240),
			  new Color(242, 242, 242),
			  new Color(242, 242, 242),
			  new Color(245, 245, 245),
			  new Color(245, 245, 245),
			  new Color(247, 247, 247),
			  new Color(247, 247, 247),
			  new Color(250, 250, 250),
			  new Color(250, 250, 250),
			  new Color(252, 252, 252),
			  new Color(252, 252, 252),
			  new Color(255, 255, 255),
			  new Color(255, 255, 255),
			  new Color(169, 169, 169),
			  new Color(169, 169, 169),
			  new Color(0, 0, 139),
			  new Color(0, 139, 139),
			  new Color(139, 0, 139),
			  new Color(139, 0, 0),
			  new Color(144, 238, 144),
		 };

		 private static final int NUM_MXP_COLORS = 657;
		 
		 private HashMap<String, Color> colors;

		 /**
		  * Constructor
		  */
		 public MXPColors()
		 {
			 colors = new HashMap<String, Color>();
			 //fill in the colors mapping...
			 for (int i = 0; i < NUM_MXP_COLORS; i++)
			 {
				 addColor(COLOR_NAMES[i], COLOR_DEF[i]);
			 }
		 }
		 
		 public void addColor( String color_name, Color value )
		 {
			 colors.put(color_name, value);
		 }
		 
		 public Color noColor()
		 {
			 return nocolor;
		 }
		 
		 public Color getColor(String color)
		 {
			 Color result = nocolor;
			 color = color.toLowerCase();
			 if ((color.length() == 7) && (color.charAt(0) == '#'))
			 {
				 color = color.substring(1, 6);
				 int rgb = Integer.parseInt(color, 16);
				 result = new Color(rgb);
			 }
			 if ( colors.containsKey(color ))
			 {
				 result = colors.get(color);
			 }
			 return result;
		 }

		 public void removeColor (String color)
		 {
			 colors.remove(color);
		 }		 		 
	}
	//----------------------------------------------------------------------------------------------------
	private class MXPResult 
	{
		  public int type;          //result type
		  public Object data;        //result data, contents depend on result type
	}
	//----------------------------------------------------------------------------------------------------
	private class EntityManager 
	{
		/** empty string, to speed up some things a little bit */
		private String empty_string;
		/** partial entity */
		private String partent;
		/** are we in an entity? */
		private boolean inEntity;
		private HashMap<String, String> entities = new HashMap<String, String>();
		
		private String ENTITY_NAMES[] = { 
				  "Aacute",
				  "aacute",
				  "Acirc",
				  "acirc",
				  "acute",
				  "AElig",
				  "aelig",
				  "Agrave",
				  "agrave",
				  "amp",
				  "apos",
				  "Aring",
				  "aring",
				  "Atilde",
				  "atilde",
				  "Auml",
				  "auml",
				  "brvbar",
				  "Ccedil",
				  "ccedil",
				  "cedil",
				  "cent",
				  "copy",
				  "curren",
				  "deg",
				  "divide",
				  "Eacute",
				  "eacute",
				  "Ecirc",
				  "ecirc",
				  "Egrave",
				  "egrave",
				  "ETH",
				  "eth",
				  "Euml",
				  "euml",
				  "frac12",
				  "frac14",
				  "frac34",
				  "gt",
				  "Iacute",
				  "iacute",
				  "Icirc",
				  "icirc",
				  "iexcl",
				  "Igrave",
				  "igrave",
				  "iquest",
				  "Iuml",
				  "iuml",
				  "laquo",
				  "lt",
				  "macr",
				  "micro",
				  "middot",
				  "nbsp",
				  "not",
				  "Ntilde",
				  "ntilde",
				  "Oacute",
				  "oacute",
				  "Ocirc",
				  "ocirc",
				  "Ograve",
				  "ograve",
				  "ordf",
				  "ordm",
				  "Oslash",
				  "oslash",
				  "Otilde",
				  "otilde",
				  "Ouml",
				  "ouml",
				  "para",
				  "plusmn",
				  "pound",
				  "quot",
				  "raquo",
				  "reg",
				  "sect",
				  "shy",
				  "sup1",
				  "sup2",
				  "sup3",
				  "szlig",
				  "THORN",
				  "thorn",
				  "times",
				  "Uacute",
				  "uacute",
				  "Ucirc",
				  "ucirc",
				  "Ugrave",
				  "ugrave",
				  "uml",
				  "Uuml",
				  "uuml",
				  "Yacute",
				  "yacute",
				  "yen",
				};

			private	int ENTITY_DEF[] = {
				  193,
				  225,
				  194,
				  226,
				  180,
				  198,
				  230,
				  192,
				  224,
				  38,
				  39,
				  197,
				  229,
				  195,
				  227,
				  196,
				  228,
				  166,
				  199,
				  231,
				  184,
				  162,
				  169,
				  164,
				  176,
				  247,
				  201,
				  233,
				  202,
				  234,
				  200,
				  232,
				  208,
				  240,
				  203,
				  235,
				  189,
				  188,
				  190,
				  62,
				  205,
				  237,
				  206,
				  238,
				  161,
				  204,
				  236,
				  191,
				  207,
				  239,
				  171,
				  60,
				  175,
				  181,
				  183,
				  160,
				  172,
				  209,
				  241,
				  211,
				  243,
				  212,
				  244,
				  210,
				  242,
				  170,
				  186,
				  216,
				  248,
				  213,
				  245,
				  214,
				  246,
				  182,
				  177,
				  163,
				  34,
				  187,
				  174,
				  167,
				  173,
				  185,
				  178,
				  179,
				  223,
				  222,
				  254,
				  215,
				  218,
				  250,
				  219,
				  251,
				  217,
				  249,
				  168,
				  220,
				  252,
				  221,
				  253,
				  165,
				};
		
		private static final int NUM_MXP_ENTITIES = 100;

		
		public EntityManager(boolean noStdEntities)
		{
			reset(noStdEntities);
		}

		//~cEntityManager ();

		/** 
		* add or update entity 
		*/
		public void addEntity (String name, String value)
		{
			if (name.equals("")) return;		
			//add or modify the entity
			entities.put(name, value);		
		}
		
		/** 
		 * delete entity 
		 */
		public void deleteEntity (String name)
		{
			entities.remove(name);	
		}
		
		public String entity(String name)
		{
			String result = entities.get(name);
			if ( result == null )
			{
				result = "";
			}
			return result;
		}
		
		public boolean exists(String name) 
		{ 
			return entities.containsKey(name); 
		};
		
		/** 
		 * expand entities in a string 
		 */
		public String expandEntities(String s, boolean finished)
		{
			  String s1 = "";
			  if (!partent.equals(""))  //some unfinished entity is waiting...
			  {
				  inEntity = true;
			  }

			  for (int i = 0; i < s.length(); i++ )
			  {
				  char ch = s.charAt(i);
			    if (inEntity)
			    {
			      
			      if (ch == ';')  //end of entity
			      {
			        inEntity = false;
			        if (partent.equals("")) //received &;
			        {
			          s1 += "&;";
			        }
			        else
			        if (partent.charAt(0) == '_')  //invalid entity name - IGNORED
			        {
			          partent = "";
			        }
			        else
			        if (partent.charAt(0) == '#')  //&#nnn; entity
			        {
			          //compute number
			          int n = 0;
			          //starting from second character
			          for (int j = 1; j < partent.length(); j++)
			          {
			            int x = partent.charAt(j) - 48;
			            if ((x < 0) || (x > 9)) //WRONG
			            {
			              n = 0;
			              break;
			            }
			            n = n * 10 + x;
			            if (n > 255)  //number too big!
			            {
			              n = 0;
			              break;
			            }
			          }
			          //verify number, IGNORE entity if it's wrong
			          if ((n >= 32) && (n <= 255))
			          {
			            s1 += (char) n;
			          }
			          partent = "";
			        }
			        else
			        {
			          //now we have correct entity name, let's expand it, if possible :)
			          if (entities.containsKey(partent))
			            s1 += entities.get(partent);
			          else
			            //keep the same string if the entity doesn't exist...
			            s1 += "&" + partent + ";";
			          partent = "";
			        }
			      }
			      else if (ch == '&')
			      //unterminated entity, new entity may start here
			      {
			        s1 += "&" + partent;
			        partent = "";
			        //isEntity remains set
			      }
			      else if ((partent.equals("") && correct1(ch)) || 
			    		  ((!partent.equals("")) && correctN(ch)))
			      {
			        partent += ch;
			      }
			      //this wasn't an entity after all
			      else
			      {
			        inEntity = false;
			        s1 += "&" + partent + ch;
			        partent = "";
			      }
			    }
			    else
			    {
			      if (ch == '&')
			        inEntity = true;
			      else
			        //copy without change
			        s1 += ch;
			    }
			  }
			  //string ends in an unterminated entity, but only if the string is finished
			  if (inEntity && finished)
			  {
			    s1 += "&" + partent;
			    partent = "";
			    inEntity = false;
			  }

			  //return the resulting string
			  return s1;
			
		}
		
		public boolean needMoreText()
		{
			return !partent.equals("");		
		}
		  
		void reset(boolean noStdEntities)
		{
			  partent = "";
			  entities.clear();
			  inEntity = false;
			  
			  if (noStdEntities)
			  {
			    return;
			  }
			  
			  
			  //restore standard HTML entities
			  for (int i = 0; i < NUM_MXP_ENTITIES; i++)
			  {
				  String s = "";
				  s += (char)ENTITY_DEF[i];
				  entities.put(ENTITY_NAMES[i], s);
			  }
		}
		
		//can this be the first letter of an entity?
		private boolean correct1 (char l)
		{
		  return (((l >= 'a') && (l <= 'z')) || ((l >= 'A') && (l <= 'Z')) || (l == '#'));
		}

		//can this be a letter of entity?
		private boolean correctN (char l)
		{
		  return (((l >= 'a') && (l <= 'z')) || ((l >= 'A') && (l <= 'Z')) || (l == '_') ||
		      ((l >= '0') && (l <= '9')));
		}
		
	}
	//----------------------------------------------------------------------------------------------------
	private class ElementManager 
	{
		  /** list of all custom elements */
		  private HashMap<String, sElement> elements = new HashMap<String, sElement>();
		  private HashMap<String, sInternalElement> ielements = new HashMap<String, sInternalElement>();

		  /** line tags associated with elements */
		  private HashMap<Integer, String> lineTags = new HashMap<Integer, String>();

		  /** aliases for internal elements */
		  private HashMap<String, String> aliases = new HashMap<String, String>();

		  /** last line tag */
		  private int lastLineTag;

		  /** state class */
		  private MXPState state;
		  /** result handler */
		  private ResultHandler results;
		  /** entity manager */
		  private EntityManager entities;
		  /** expander of parameters in custom tags */
		  private EntityManager paramexpander;
		  /** parser of custom element definitions */
		  private MXPParser parser;
		  
		
		private class sElementPart 
		{
			  public boolean istag;
			  public String text;
		};

		/** one external element (defined by <!element>) */
		private class sElement 
		{
		  /** is it an open element? */
		  public boolean open;
		  /** is it an element with no closing tag? */
		  public boolean empty;
		  /** tag associated with this element */
		  int tag;
		  /** flag associated with this element */
		  public String flag = "";
		  /** list of all element contents */
		  public ArrayList<sElementPart> element = new ArrayList<sElementPart>();
		  /** list of element attributes in the right order */
		  public ArrayList<String> attlist = new ArrayList<String>();
		  /** default values for attributes */
		  public HashMap<String, String> attdefault = new HashMap<String, String>();
		  /** closing sequence */
		  public ArrayList<String> closingseq = new ArrayList<String>();
		};

		/** 
		 * one internal element 
		 */
		private class sInternalElement 
		{
		  /** is it an open element? */
		  public boolean open;
		  /** is it an element with no closing tag? */
		  public boolean empty;
		  /** list of element attributes in the right order */
		  public ArrayList<String> attlist = new ArrayList<String>();
		  /** default values for attributes; if there's an empty public String (but defined), then it's a flag */
		  public HashMap<String, String> attdefault = new HashMap<String, String>();
		};

		/** one parameter in one tag :-) */
		private class sParam {
		  public boolean flag;
		  public String name = "";
		  public String value = "";
		};
		
		/** constructor */
		public ElementManager (MXPState st, ResultHandler res, EntityManager enm)
		{
			  state = st;
			  results = res;
			  entities = enm;

			  paramexpander = new EntityManager (true);
			  parser = new MXPParser(null,null,null);
			  
			  reset();
			  createInternalElements();		
		}

		public void reset()
		{
		  lastLineTag = 0;
		  removeAll();
		}
		
		public void createInternalElements()
		{
		  //the list doesn't contain information on whether an argument is required or not
		  //processor of the tag implements this functionality

		  //create all internal elements
		  sInternalElement e;

		  //!element
		  e = new sInternalElement();
		  e.empty = true;
		  e.open = false;
		  e.attlist.add("name");  //this name is not in the spec!
		  e.attlist.add("definition");  //this name is not in the spec!
		  e.attlist.add("att");
		  e.attlist.add("tag");
		  e.attlist.add("flag");
		  e.attlist.add("open");
		  e.attlist.add("delete");
		  e.attlist.add("empty");
		  e.attdefault.put("open","");  //flags
		  e.attdefault.put("delete","");
		  e.attdefault.put("empty","");
		  ielements.put("!element", e);

		  //!attlist
		  e = new sInternalElement();
		  e.empty = true;
		  e.open = false;
		  e.attlist.add("name");  //this name is not in the spec!
		  e.attlist.add("att");
		  ielements.put("!attlist", e);

		  //!entity
		  e = new sInternalElement();
		  e.empty = true;
		  e.open = false;
		  e.attlist.add("name");  //this name is not in the spec!
		  e.attlist.add("value");  //this name is not in the spec!
		  e.attlist.add("desc");
		  e.attlist.add("private");
		  e.attlist.add("publish");
		  e.attlist.add("add");
		  e.attlist.add("delete");
		  e.attlist.add("remove");
		  e.attdefault.put("private", "");  //flags
		  e.attdefault.put("publish", "");
		  e.attdefault.put("delete", "");
		  e.attdefault.put("add", "");
		  e.attdefault.put("remove", "");
		  ielements.put("!entity", e);

		  //var
		  e = new sInternalElement();
		  e.empty = false;
		  e.open = false;
		  e.attlist.add("name");  //this name is not in the spec!
		  e.attlist.add("desc");
		  e.attlist.add("private");
		  e.attlist.add("publish");
		  e.attlist.add("add");
		  e.attlist.add("delete");
		  e.attlist.add("remove");
		  e.attdefault.put("private", "");  //flags
		  e.attdefault.put("publish", "");
		  e.attdefault.put("delete", "");
		  e.attdefault.put("add", "");
		  e.attdefault.put("remove", "");
		  ielements.put("var", e);

		  //b
		  e = new sInternalElement();
		  e.empty = false;
		  e.open = true;
		  ielements.put("b", e);

		  //i
		  e = new sInternalElement();
		  e.empty = false;
		  e.open = true;
		  ielements.put("i", e);

		  //u
		  e = new sInternalElement();
		  e.empty = false;
		  e.open = true;
		  ielements.put("u", e);

		  //s
		  e = new sInternalElement();
		  e.empty = false;
		  e.open = true;
		  ielements.put("s", e);

		  //c
		  e = new sInternalElement();
		  e.empty = false;
		  e.open = true;
		  e.attlist.add("fore");
		  e.attlist.add("back");
		  ielements.put("c", e);

		  //h
		  e = new sInternalElement();
		  e.empty = false;
		  e.open = true;
		  ielements.put("h", e);

		  //font
		  e = new sInternalElement();
		  e.empty = false;
		  e.open = true;
		  e.attlist.add("face");
		  e.attlist.add("size");
		  e.attlist.add("color");
		  e.attlist.add("back");
		  ielements.put("font", e);

		  //nobr
		  e = new sInternalElement();
		  e.empty = true;
		  e.open = false;
		  ielements.put("nobr", e);

		  //p
		  e = new sInternalElement();
		  e.empty = false;
		  e.open = false;
		  ielements.put("p", e);

		  //br
		  e = new sInternalElement();
		  e.empty = true;
		  e.open = false;
		  ielements.put("br", e);

		  //sbr
		  e = new sInternalElement();
		  e.empty = true;
		  e.open = false;
		  ielements.put("sbr", e);

		  //a
		  e = new sInternalElement();
		  e.empty = false;
		  e.open = false;
		  e.attlist.add("href");
		  e.attlist.add("hint");
		  e.attlist.add("expire");
		  ielements.put("a", e);

		  //send
		  e = new sInternalElement();
		  e.empty = false;
		  e.open = false;
		  e.attlist.add("href");
		  e.attlist.add("hint");
		  e.attlist.add("prompt");
		  e.attlist.add("expire");
		  e.attdefault.put("prompt", "");  //flags
		  ielements.put("send", e);

		  //expire
		  e = new sInternalElement();
		  e.empty = true;
		  e.open = false;
		  e.attlist.add("name");  //this name is not in the spec!
		  ielements.put("expire", e);

		  //version
		  e = new sInternalElement();
		  e.empty = true;
		  e.open = false;
		  ielements.put("version", e);

		  //support
		  e = new sInternalElement();
		  e.empty = true;
		  e.open = false;
		  ielements.put("support", e);

		  //h1
		  e = new sInternalElement();
		  e.empty = false;
		  e.open = false;
		  ielements.put("h1", e);

		  //h2
		  e = new sInternalElement();
		  e.empty = false;
		  e.open = false;
		  ielements.put("h2", e);

		  //h3
		  e = new sInternalElement();
		  e.empty = false;
		  e.open = false;
		  ielements.put("h3", e);

		  //h4
		  e = new sInternalElement();
		  e.empty = false;
		  e.open = false;
		  ielements.put("h4", e);

		  //h5
		  e = new sInternalElement();
		  e.empty = false;
		  e.open = false;
		  ielements.put("h5", e);

		  //h6
		  e = new sInternalElement();
		  e.empty = false;
		  e.open = false;
		  ielements.put("h6", e);

		  //hr
		  e = new sInternalElement();
		  e.empty = true;
		  e.open = false;
		  ielements.put("hr", e);

		  //small
		  e = new sInternalElement();
		  e.empty = false;
		  e.open = false;
		  ielements.put("small", e);

		  //tt
		  e = new sInternalElement();
		  e.empty = false;
		  e.open = false;
		  ielements.put("tt", e);

		  //sound
		  e = new sInternalElement();
		  e.empty = true;
		  e.open = false;
		  e.attlist.add("fname");
		  e.attlist.add("v");
		  e.attlist.add("l");
		  e.attlist.add("p");
		  e.attlist.add("t");
		  e.attlist.add("u");
		  e.attdefault.put("v", "100");
		  e.attdefault.put("l", "1");
		  e.attdefault.put("p", "50");
		  ielements.put("sound", e);

		  //music
		  e = new sInternalElement();
		  e.empty = true;
		  e.open = false;
		  e.attlist.add("fname");
		  e.attlist.add("v");
		  e.attlist.add("l");
		  e.attlist.add("c");
		  e.attlist.add("t");
		  e.attlist.add("u");
		  e.attdefault.put("v", "100");
		  e.attdefault.put("l", "1");
		  e.attdefault.put("c", "1");
		  ielements.put("music", e);

		  //gauge
		  e = new sInternalElement();
		  e.empty = true;
		  e.open = false;
		  e.attlist.add("entity");  //this name is not in the spec!
		  e.attlist.add("max");
		  e.attlist.add("caption");
		  e.attlist.add("color");
		  ielements.put("gauge", e);

		  //stat
		  e = new sInternalElement();
		  e.empty = true;
		  e.open = false;
		  e.attlist.add("entity");  //this name is not in the spec!
		  e.attlist.add("max");
		  e.attlist.add("caption");
		  ielements.put("stat", e);

		  //frame
		  e = new sInternalElement();
		  e.empty = true;
		  e.open = false;
		  e.attlist.add("name");
		  e.attlist.add("action");
		  e.attlist.add("title");
		  e.attlist.add("internal");
		  e.attlist.add("align");
		  e.attlist.add("left");
		  e.attlist.add("top");
		  e.attlist.add("width");
		  e.attlist.add("height");
		  e.attlist.add("scrolling");
		  e.attlist.add("floating");
		  e.attdefault.put("action", "open");
		  e.attdefault.put("align", "top");
		  e.attdefault.put("left", "0");
		  e.attdefault.put("top", "0");
		  e.attdefault.put("internal", "");  //flags
		  e.attdefault.put("scrolling", "");
		  e.attdefault.put("floating", "");
		  ielements.put("frame", e);

		  //dest
		  e = new sInternalElement();
		  e.empty = false;
		  e.open = false;
		  e.attlist.add("name");  //this name is not in the spec!
		  e.attlist.add("x");
		  e.attlist.add("y");
		  e.attlist.add("eol");
		  e.attlist.add("eof");
		  e.attdefault.put("eol", "");  //flags
		  e.attdefault.put("eof", "");
		  ielements.put("dest", e);

		  //relocate
		  e = new sInternalElement();
		  e.empty = true;
		  e.open = false;
		  e.attlist.add("name");  //this name is not in the spec!
		  e.attlist.add("port");  //this name is not in the spec!
		  ielements.put("relocate", e);

		  //user
		  e = new sInternalElement();
		  e.empty = true;
		  e.open = false;
		  ielements.put("user", e);

		  //password
		  e = new sInternalElement();
		  e.empty = true;
		  e.open = false;
		  ielements.put("password", e);

		  //image
		  e = new sInternalElement();
		  e.empty = true;
		  e.open = false;
		  e.attlist.add("fname");
		  e.attlist.add("url");
		  e.attlist.add("t");
		  e.attlist.add("h");
		  e.attlist.add("w");
		  e.attlist.add("hspace");
		  e.attlist.add("vspace");
		  e.attlist.add("align");
		  e.attlist.add("ismap");
		  e.attdefault.put("align", "top");
		  e.attdefault.put("ismap", "");  //flags
		  ielements.put("image", e);

		  //filter
		  e = new sInternalElement();
		  e.empty = true;
		  e.open = false;
		  e.attlist.add("src");
		  e.attlist.add("dest");
		  e.attlist.add("name");
		  ielements.put("filter", e);

		  //finally, define some aliases for internal elements
		  aliases.put("!el", "!element");
		  aliases.put("!at", "!attlist");
		  aliases.put("!en", "!entity");
		  aliases.put("v", "var");
		  aliases.put("bold", "b");
		  aliases.put("strong", "b");
		  aliases.put("italic", "i");
		  aliases.put("em", "i");
		  aliases.put("underline", "u");
		  aliases.put("strikeout", "s");
		  aliases.put("high", "h");
		  aliases.put("color", "c");
		  aliases.put("destination", "dest");
		}
		
		private void removeAll()
		{
		  //external elements only
		  Object names [] = elements.keySet().toArray();
		  for (Object object : names) 
		  {
			removeElement((String)object);
		  }
		}

		private void removeElement (String name)
		{
		  //external elements only
		  if (elements.containsKey(name))
		  {
		    sElement e = elements.get(name);
		    e.element.clear();
		    e.attlist.clear();
		    e.attdefault.clear();
		    e.closingseq.clear();
		    if (e.tag != 0 )
		      lineTags.remove(e.tag);
		    elements.remove(name);
		  }
		}

		  /** destructor */
		  protected void finalize() throws Throwable 
		  {
			  paramexpander = null;
			  parser = null;		  
			  removeAll ();
			  //internal elements
			  Collection<sInternalElement> values = ielements.values();
			  for (sInternalElement internalElement : values) 
			  {
				  internalElement.attlist.clear();
				  internalElement.attdefault.clear();
			  }
			  ielements.clear();
			  aliases.clear();
			  super.finalize();
		  }

		  /** set pointer to cMXPState - needed due to circular dependencies */
		  public void assignMXPState (MXPState st)
		  {
			  state = st;
		  }

		  /** 
		   * is this element defined? 
		   */
		  public boolean elementDefined (String name)
		  {
			  return ((elements.containsKey(name)) || (ielements.containsKey(name)) ||
				      (aliases.containsKey(name)));		  
		  }

		  /** 
		   * is it an internal tag? 
		   */
		  public boolean internalElement (String name)
		  {
		    return ((ielements.containsKey(name)) || (aliases.containsKey(name)));
		  }

		  /** 
		   * is it a custom element? (i.e. defined via <!element>) 
		   */
		  public boolean customElement (String name)
		  {
		    return (elements.containsKey(name));
		  }

		  /** 
		   * open element? 
		   */	  
		  public boolean openElement (String name)
		  {
		    if (!elementDefined (name))
		      return false;
		    if (internalElement (name))
		    {
		      String n = name;
		      if (aliases.containsKey(name))
		        n = aliases.get(name);
		      return ielements.get(n).open;
		    }
		    else
		      return elements.get(name).open;
		  }

		  /** 
		   * empty element? i.e. does it need a closing tag? 
		   */
		  public boolean emptyElement (String name)
		  {
		    if (!elementDefined (name))
		      return false;
		    if (internalElement (name))
		    {
		      String n = name;
		      if (aliases.containsKey(name))
		        n = aliases.get(name);
		      return ielements.get(n).empty;
		    }
		    else
		      return elements.get(name).empty;
		  }

		  public void gotTag (String tag) throws Exception
		  {
		    String tagname = "";
		    ArrayList<sParam> params = new ArrayList<sParam>();
		    sParam param = new sParam();
		    param.flag = false;
		    char quote = 0;
		    tagParserState pstate = tagParserState.tagBegin;
		    for (int i = 0; i<tag.length(); i++)
		    {
		      char ch = tag.charAt(i);
		      //process character
		      switch (pstate) {
		        case tagBegin: {
		          if (ch != ' ')
		          {
		            pstate = tagParserState.tagName;
		            tagname += ch;
		          }
		          break;
		        }
		        case tagName: {
		          if (ch == ' ')
		            pstate = tagParserState.tagBetweenParams;
		          else
		            tagname += ch;
		          break;
		        }
		        case tagParam: {
		          if (ch == '=')
		            pstate = tagParserState.tagParamValue;
		          else if (ch == ' ')
		          {
		            //one parameter, value only (it could also be a flag, we'll check that later)
		            param.value = param.name;
		            param.name = "";
		            //add a new parameter :-)
		            params.add(param);
		            param = new sParam();
		            param.value = "";
		            pstate = tagParserState.tagBetweenParams;
		          }
		          else
		            param.name += ch;
		          break;
		        }
		        case tagParamValue: {
		          if (ch == ' ')
		          {
		            //add a new parameter :-)
		            params.add(param);
		            param = new sParam();
		            param.name = "";
		            param.value = "";
		            pstate = tagParserState.tagBetweenParams;
		          }
		          else if (param.value.isEmpty() && ((ch == '\'') || (ch == '"')))
		          {
		            pstate = tagParserState.tagQuotedParam;
		            quote = ch;
		          }
		          else
		            param.value += ch;
		          break;
		        }
		        case tagQuotedParam: {
		          if (ch == quote)
		          {
		            //add a new parameter :-)
		            params.add(param);
		            param = new sParam();
		            param.name = "";
		            param.value = "";
		            pstate = tagParserState.tagAfterQuotedParam;
		          }
		          else
		            param.value += ch;
		          break;
		        }
		        case tagAfterQuotedParam: {
		          if (ch == ' ')    //ignore everything up to some space...
		            pstate = tagParserState.tagBetweenParams;
		          break;
		        }
		        case tagBetweenParams: {
		          if (ch != ' ')
		          {
		            if ((ch == '\'') || (ch == '"'))
		            {
		              pstate = tagParserState.tagQuotedParam;
		              param.name = "";
		              quote = ch;
		            }
		            else
		            {
		              pstate = tagParserState.tagParam;
		              param.name += ch;
		            }
		          }
		          break;
		        }
		      };
		    }

		    //last parameter...
		    switch (pstate) 
		    {
		      case tagBegin:
		        results.addToList (results.createError ("Received a tag with no body!"));
		        break;
		      case tagParam: {
		        param.value = param.name;
		        param.name = "";
		        params.add(param);
		        param = new sParam();
		        }
		        break;
		      case tagParamValue:
		        params.add(param);
		        param = new sParam();
		        break;
		      case tagQuotedParam:
		        results.addToList (results.createError ("Received tag " + tagname +
		            " with unfinished quoted parameter!"));
		        break;
		    };

		    //nothing more to do if the tag has no contents...
		    if (pstate == tagParserState.tagBegin) return;
		    
		    //convert tag name to lowercase
		    tagname = tagname.toLowerCase();
		    
		    //handle closing tag...
		    if (tagname.charAt(0) == '/')
		    {
		      if (!params.isEmpty())
		      {
		        results.addToList (results.createError ("Received closing tag " + tagname +
		            " with parametrs!"));
		      }
		      //remove that '/'
		      tagname = tagname.substring(1);
		      //and call closing tag processing
		      handleClosingTag(tagname);
		      return;
		    }
		    
		    //convert all parameter names to lower-case
		    for (sParam param2 : params) 
		    {
		    	param2.name = param2.name.toLowerCase();
			}
		    
		    //now we check the type of the tag and act accordingly
		    if (!elementDefined (tagname))
		    {
		      params.clear();
		      results.addToList (results.createError ("Received undefined tag " + tagname + "!"));
		      return;
		    }

		    mxpMode m = state.getMXPMode ();
		    //mode can be open or secure; locked mode is not possible here (or we're in a bug)
		    if (m == mxpMode.openMode)
		      //open mode - only open tags allowed
		      if (!openElement (tagname))
		      {
		      params.clear();
		        results.addToList (results.createError ("Received secure tag " + tagname +
		            " in open mode!"));
		        return;
		      }

		    if (internalElement (tagname))
		    {
		      //if the name is an alias for another tag, change the name
		      if (aliases.containsKey(tagname))
		        tagname = aliases.get(tagname);
		      //the <support> tag has to be handled separately :(
		      if (tagname.equals("support"))
		      {
		        processSupport(params);
		        return;
		      }
		      //identify all flags in the tag
		      identifyFlags(ielements.get(tagname).attdefault, params);
		      //correctly identify all parameters (assign names where necessary)
		      handleParams (tagname, params, ielements.get(tagname).attlist,
		    		  ielements.get(tagname).attdefault);
		      //separate out all the flags (flags are only valid for internal tags)
		      ArrayList<String> flags = new ArrayList<String>();
		      for ( int i =0; i < params.size();)
		      {
		    	  sParam item = params.get(i);
		    	  if (item.flag)
		    	  {
		    		  flags.add(item.name);
		    		  params.remove(i);
		    	  }
		    	  else
		    		  i++;
		      }
		      //okay, parsing done - send the tag for further processing
		      processInternalTag(tagname, params, flags);
		    }
		    else
		    {
		      handleParams (tagname, params, elements.get(tagname).attlist,
		    		  elements.get(tagname).attdefault);
		      processCustomTag(tagname, params);
		    }
		    
		    params.clear ();
		  }

		  private void handleClosingTag (String name)
		  {
		    String n = name.toLowerCase();
		    if (!elementDefined (n))
		    {
		      results.addToList (results.createError ("Received unknown closing tag </" + n + ">!"));
		      return;
		    }
		    if (emptyElement (n))
		    {
		      results.addToList (results.createError ("Received closing tag for tag " + n +
		          ", which doesn't need a closing tag!"));
		      return;
		    }
		    if (internalElement (n))
		    {
		      //if the name is an alias for another tag, change the name
		      if (aliases.containsKey(n))
		      {
		    	  n = aliases.get(n);
		      }
		      state.gotClosingTag(n);
		    }
		    else
		    {
		      //send closing flag, if needed
		      if (!elements.get(n).flag.isEmpty())
		        state.gotFlag (false, elements.get(n).flag);
		      
		      //expand the closing tag...
		      for (String item : elements.get(n).closingseq) 
		      {
		    	  handleClosingTag(item);
		      }
		    }
		  }

		  private void processSupport (List<sParam> params) throws Exception
		  {
		    List<String> pars = new ArrayList<String>();
		    for (sParam param : params) 
		    {
				pars.add(param.value);
			}
		    state.gotSUPPORT(pars);
		  }

		  private void identifyFlags (HashMap<String, String> attdefault, List<sParam> args)
		  {
			  for (sParam param : args) 
			  {
				  if ( param.name.isEmpty())
				  {
					  String s = param.value.toLowerCase();
					  if ( attdefault.containsKey(s) && (attdefault.get(s).isEmpty()))
					  {
						param.name = s;
						param.value = "";
						param.flag = true; 
					  }
				  }
			  }
		  }
		  
		  private void handleParams (String tagname, List<sParam> args,
				    List<String> attlist, HashMap<String, String> attdefault)
				{
				  //list<string>::const_iterator cur = attlist.begin();
				  //list<sParam>::iterator it;
			  	  int cur = 0;
				  for (sParam item : args) 
				  {				  
					//flag?
				    if (item.flag)
				    {
				      //only advance parameter iterator
				      cur++;
				    }
				    //not a flag
				    else
				    {
				      //empty name?
				      if (item.name.isEmpty())
				      {
				        //set the parameter name:
				        
				        //find the correct attribute name, skipping all flags
				        while (cur < attlist.size())
				        {
				          if ((attdefault.containsKey(attlist.get(cur))) 
				        		  && (attdefault.get(attlist.get(cur)).isEmpty()))  //flag
				            cur++;
				          else
				            break;  //okay, found the correct parameter
				        }
				        if (cur == attlist.size())    //ARGH! Parameter not found :(
				        {
				          results.addToList (results.createError ("Received too many parameters for tag " +
				              tagname + "!"));
				          continue;  //continue with the next parameter...
				        }
				      }
				      //non-empty name?
				      else
				      {
				        //set "cur" to the parameter following the given one
				        
				        //to speed things up a bit, first look if the iterator is pointing at the right parameter
				        // (we won't need to traverse the whole list, if it does)
				        if ((cur == attlist.size()) || (item.name != attlist.get(cur)))
				        {
				          int cur2 = cur;  //remember old "cur" value
				          for (cur = 0; cur < attlist.size(); cur++)
				            if (item.name.equals(attlist.get(cur)))
				              break;
				          if (cur == attlist.size())    //parameter name not found
				          {
				            //restore old iterator value
				            cur = cur2;
				            results.addToList (results.createError ("Received unknown parameter " +
				                item.name + " in tag " + tagname + "!"));
				            //clear name/value to avoid confusion in later stages
				            item.name = "";
				            item.value = "";
				            //proceed with next parameter
				            continue;
				          }
				          //if cur isn't attlist.end(), it's now set to the correct value...
				        }
				      }
				      
				      //things common to all non-flag parameters...
				      
				      //set parameter name
				      item.name = attlist.get(cur);
				      //if parameter value is empty, set it to default value (if any)
				      if (item.value.isEmpty() && (attdefault.containsKey(attlist.get(cur))))
				        item.value = attdefault.get(attlist.get(cur));
				      //advance parameter iterator
				      cur++;
				    }
				  }
				  
				  //finally, we add default parameter values to the beginning of the list... these shall get
				  //overridden by given values, if any (those shall be later in the list)
				  for (Object item : attdefault.keySet().toArray()) 
				  {
					if ( !attdefault.get(item).isEmpty())
					{
						sParam s = new sParam();
					    s.flag = false;
					    s.name = (String)item;
					    s.value = attdefault.get(item);
					    args.add(0, s);					
					}
				  }
				}

		  private void processInternalTag (String name2, List<sParam> params,
				    List<String> flags)
				{
				  //list<sParam>::const_iterator it;
				  //list<string>::const_iterator it2;
				  if (name2.equals("!element"))
				  {
				    String lname = "", definition = "", att = "", flag = "";
				    int tag = 0;
				    boolean fopen = false, fdelete = false, fempty = false;
				    for (sParam item : params) 
				    {
				      String s = item.name;
				      if (s.equals("name")) lname = item.value.toLowerCase();
				      if (s.equals("definition")) definition = item.value;
				      if (s.equals("att")) att = item.value;
				      if (s.equals("flag")) flag = item.value;
				      if (s.equals("tag")) tag = Integer.parseInt(item.value);
				    }
				    for (String string : flags) 
				    {
				      if (string.equals("open")) fopen = true;
				      if (string.equals("delete")) fdelete = true;
				      if (string.equals("empty")) fempty = true;
				    }
				    
				    if (lname.isEmpty())
				    {
				      results.addToList (results.createError (
				          "Received an <!element> tag with no element name!"));
				      return;
				    }
				    //definition can be empty, that's no problem...

				    //if we want to delete the tag...
				    if (fdelete)
				    {
				      //sanity check
				      if (!elements.containsKey(lname))
				      {
				        results.addToList (results.createWarning (
				            "Received request to remove an undefined tag " + lname + "!"));
				        return;
				      }
				      removeElement (lname);
				      return;
				    }
				    
				    //parse tag definition
				    parser.simpleParse(definition);
				    ArrayList<sElementPart> tagcontents = new ArrayList<sElementPart>();
				    while (parser.hasNext())
				    {
				      chunk ch = parser.getNext();
				      if (ch.chk == chunkType.chunkError)
				        results.addToList (results.createError (ch.text));
				      else
				      {
				        //create a new element part
				        sElementPart part = new sElementPart();
				        part.text = ch.text;
				        part.istag = (ch.chk == chunkType.chunkTag) ? true : false;
				        tagcontents.add(part);
				      }
				    }
				    
				    //parse attribute list
				    ArrayList<String> attlist = new ArrayList<String>();
				    HashMap<String, String> attdefault = new HashMap<String, String>();
				    processParamList (att, attlist, attdefault);
				    
				    //and do the real work
				    addElement(lname, tagcontents, attlist, attdefault, fopen, fempty, tag, flag);
				  }
				  else if (name2.equals("!attlist"))
				  {
				    String name = "", att = "";
				    for ( Iterator it = params.iterator(); it.hasNext();)
				    {
				    	sParam item = (sParam)it.next();
				    	String s = item.name;
				    	if (s.equals("name")) name = item.value;
				    	if (s.equals("att")) att = item.value;
				    }
				    
				    if (name.isEmpty())
				    {
				      results.addToList (results.createError (
				          "Received an <!attlist> tag with no element name!"));
				      return;
				    }

				    //parse attribute list
				    ArrayList<String> attlist =  new ArrayList<String>();
				    HashMap<String, String> attdefault = new HashMap<String, String>();
				    processParamList (att, attlist, attdefault);
				    
				    //and do the real work
				    setAttList (name, attlist, attdefault);
				  }
				  else if (name2.equals("!entity"))
				  {
				    String name = "", value = "", desc;
				    boolean fpriv = false, fpub = false, fadd = false, fdel = false, frem = false;
				    for (sParam item : params) 
				    {
				      String s = item.name;
				      if (s.equals("name")) name = item.value;
				      if (s.equals("value")) value = item.value;
				      if (s.equals("desc")) desc = item.value;
				    }
				    for (String item : flags) 
				    {
				      if (item.equals( "private")) fpriv = true;
				      if (item.equals( "publish")) fpub = true;
				      if (item.equals( "delete")) fdel = true;
				      if (item.equals( "add")) fadd = true;
				      if (item.equals( "remove")) frem = true;
				    }
				    
				    if (name.isEmpty())
				    {
				      results.addToList (results.createError (
				          "Received an <!entity> tag with no variable name!"));
				      return;
				    }
				    
				    //fpub is IGNORED...
				    //fadd and frem is IGNORED...
				    if (!(fadd) && !(frem))
				    {
				      if (fdel)
				      {
				        entities.deleteEntity (name);
				        if (!fpriv) //do not announce PRIVATE entities
				          state.gotVariable (name, "", true);
				      }
				      else
				      {
				        //we now have a new variable...
				        entities.addEntity (name, value);
				        if (!fpriv) //do not announce PRIVATE entities
				          state.gotVariable (name, value, false);
				      }
				    }
				    else
				      results.addToList (results.createWarning (
				          "Ignored <!ENTITY> tag with ADD or REMOVE flag."));
				  }
				  else if (name2.equals("var"))
				  {
				    //this is very similar to the !entity handler above...
				    
				    String name = "", desc = "";
				    boolean fpriv = false, fpub = false, fadd = false, fdel = false, frem = false;
				    for ( sParam item : params) 
				    {
				      String s = item.name;
				      if (s.equals("name")) name = item.value;
				      if (s.equals("desc")) desc = item.value;
				    }
				    for ( String item : flags)
				    {  
				      if (item.equals("private")) fpriv = true;
				      if (item.equals("publish")) fpub = true;
				      if (item.equals("add")) fadd = true;
				      if (item.equals("delete")) fdel = true;
				      if (item.equals("remove")) frem = true;
				    }
				    
				    if (name.isEmpty())
				    {
				      results.addToList (results.createError (
				          "Received an <var> tag with no variable name!"));
				      return;
				    }
				    
				    //fpriv and fpub is IGNORED...
				    //fadd and fdel is IGNORED...
				    if (!(fadd) && !(fdel))
				      state.gotVAR (name);
				    else
				      results.addToList (results.createWarning ("Ignored <VAR> tag with ADD or REMOVE flag."));
				  }
				  else if (name2.equals("b"))
				    state.gotBOLD();
				  else if (name2.equals("i"))
				    state.gotITALIC();
				  else if (name2.equals("u"))
				    state.gotUNDERLINE();
				  else if (name2.equals("s"))
				    state.gotSTRIKEOUT();
				  else if (name2.equals("c"))
				  {
				    String fore = "", back = "";
				    for (sParam item : params) 
				    {
				      String s = item.name;
				      if (s.equals("fore")) fore = item.value;
				      if (s.equals("back")) back = item.value;
				    }
				    Color fg = state.fgColor();
				    Color bg = state.bgColor();
				    if (!fore.isEmpty())
				      fg = allcolors.getColor(fore);
				    if (!back.isEmpty())
				      bg = allcolors.getColor(back);
				    state.gotCOLOR (fg, bg);
				  }
				  else if (name2.equals("h"))
				    state.gotHIGH();
				  else if (name2.equals("font"))
				  {
				    String face = "", fore="", back="";
				    int size = 0;
				    for ( sParam item : params) 
				    {
				      String s = item.name;
				      if (s.equals("face")) face = item.value;
				      if (s.equals("size")) size = Integer.parseInt(item.value);
				      if (s.equals("color")) fore = item.value;
				      if (s.equals("back")) back = item.value;
				    }
				    if (face.isEmpty())
				      face = state.fontFace();
				    if (size == 0)
				      size = state.fontSize();
				    Color fg = state.fgColor();
				    Color bg = state.bgColor();
				    if (!fore.isEmpty())
				      fg = allcolors.getColor(fore);
				    if (!back.isEmpty())
				      bg = allcolors.getColor(back);
				    state.gotFONT (face, size, fg, bg);
				  }
				  else if (name2.equals("p"))
				    state.gotP();
				  else if (name2.equals("br"))
				    state.gotBR();
				  else if (name2.equals("nobr"))
				    state.gotNOBR();
				  else if (name2.equals("sbr"))
				    state.gotSBR();
				  else if (name2.equals("a"))
				  {
				    String href = "", hint = "", expire = "";
				    for ( sParam item : params) 
				    {
				      String s = item.name;
				      if (s.equals("href")) href = item.value;
				      if (s.equals("hint")) hint = item.value;
				      if (s.equals("expire")) expire = item.value;
				    }
				    state.gotA (href, hint, expire);
				  }
				  else if (name2.equals("send"))
				  {
				    String href = "", hint = "", expire = "";
				    boolean prompt = false;
				    for ( sParam item : params) 
				    {
				      String s = item.name;
				      if (s.equals("href")) href = item.value;
				      if (s.equals("hint")) hint = item.value;
				      if (s.equals("expire")) expire = item.value;
				    }
				    for ( String item : flags)
				    {  
				      if (item.equals("prompt")) prompt = true;
				    }
				    state.gotSEND (href, hint, prompt, expire);
				  }
				  else if (name2.equals("expire"))
				  {
				    String name = "";
				    for ( sParam item : params) 
				    {
				      String s = item.name;
				      if (s.equals("name")) name = item.value;
				    }
				    //name can be empty - all named links shall then expire
				    state.gotEXPIRE(name);
				  }
				  else if (name2.equals("version"))
				    state.gotVERSION();
				  else if (name2.equals("h1"))
				    state.gotHtag (1);
				  else if (name2.equals("h2"))
				    state.gotHtag (2);
				  else if (name2.equals("h3"))
				    state.gotHtag (3);
				  else if (name2.equals("h4"))
				    state.gotHtag (4);
				  else if (name2.equals("h5"))
				    state.gotHtag (5);
				  else if (name2.equals("h6"))
				    state.gotHtag (6);
				  else if (name2.equals("hr"))
				    state.gotHR();
				  else if (name2.equals("small"))
				    state.gotSMALL();
				  else if (name2.equals("tt"))
				    state.gotTT();
				  else if (name2.equals("sound"))
				  {
				    String fname = "", t = "", u = "";
				    int v = 0, l = 0, p = 0;  //shall be overridden by defaults...
				    for ( sParam item : params) 
				    {
				      String s = item.name;
				      if (s.equals("fname")) fname = item.value;
				      if (s.equals("t")) t = item.value;
				      if (s.equals("u")) u = item.value;
				      if (s.equals("v")) v = Integer.parseInt(item.value);
				      if (s.equals("l")) l = Integer.parseInt(item.value);
				      if (s.equals("p")) p = Integer.parseInt(item.value);
				    }
				    if (fname.isEmpty())
				    {
				      results.addToList (results.createError ("Received SOUND tag with no file name!"));
				      return;
				    }
				    if ((v < 0) ||  (v > 100))
				    {
				      results.addToList (results.createWarning ("Ignoring incorrect V param for SOUND tag."));
				      v = 100;  //set default value
				    }
				    if ((l < -1) || (l > 100) || (l == 0))
				    {
				      results.addToList (results.createWarning ("Ignoring incorrect L param for SOUND tag."));
				      l = 1;  //set default value
				    }
				    if ((p < 0) ||  (p > 100))
				    {
				      results.addToList (results.createWarning ("Ignoring incorrect P param for SOUND tag."));
				      p = 50;  //set default value
				    }
				    state.gotSOUND (fname, v, l, p, t, u);
				  }
				  else if (name2.equals("music"))
				  {
				    String fname = "", t = "", u = "";
				    int v = 0, l = 0, c = 0;  //shall be overridden by defaults...
				    for ( sParam item : params) 
				    {
				      String s = item.name;
				      if (s.equals("fname")) fname = item.value;
				      if (s.equals("t")) t = item.value;
				      if (s.equals("u")) u = item.value;
				      if (s.equals("v")) v = Integer.parseInt(item.value);
				      if (s.equals("l")) l = Integer.parseInt(item.value);
				      if (s.equals("c")) c = Integer.parseInt(item.value);
				    }
				    if (fname.isEmpty())
				    {
				      results.addToList (results.createError ("Received MUSIC tag with no file name!"));
				      return;
				    }
				    if ((v < 0) ||  (v > 100))
				    {
				      results.addToList (results.createWarning ("Ignoring incorrect V param for MUSIC tag."));
				      v = 100;  //set default value
				    }
				    if ((l < -1) || (l > 100) || (l == 0))
				    {
				      results.addToList (results.createWarning ("Ignoring incorrect L param for MUSIC tag."));
				      l = 1;  //set default value
				    }
				    if ((c != 0) && (c != 1))
				    {
				      results.addToList (results.createWarning ("Ignoring incorrect C param for MUSIC tag."));
				      c = 1;  //set default value
				    }
				    state.gotMUSIC (fname, v, l, (c!=0), t, u);
				  }
				  else if (name2.equals("gauge"))
				  {
				    String entity = "", max = "", caption = "", color = "";
				    for ( sParam item : params) 
				    {
				      String s = item.name;
				      if (s.equals("entity")) entity = item.value;
				      if (s.equals("max")) max = item.value;
				      if (s.equals("caption")) caption = item.value;
				      if (s.equals("color")) color = item.value;
				    }
				    if (entity.isEmpty())
				    {
				      results.addToList (results.createError ("Received GAUGE with no entity name!"));
				      return;
				    }
				    Color c;
				    if (color.isEmpty()) color = "white";
				    c = allcolors.getColor(color);
				    state.gotGAUGE (entity, max, caption, c);
				  }
				  else if (name2.equals("stat"))
				  {
				    String entity = "", max = "", caption = "";
				    for ( sParam item : params) 
				    {
				      String s = item.name;
				      if (s.equals("entity")) entity = item.value;
				      if (s.equals("max")) max = item.value;
				      if (s.equals("caption")) caption = item.value;
				    }
				    if (entity.isEmpty())
				    {
				      results.addToList (results.createError ("Received STAT with no entity name!"));
				      return;
				    }
				    state.gotSTAT (entity, max, caption);
				  }
				  else if (name2.equals("frame"))
				  {
				    String name = "", action = "", title = "", align = "";
				    int left = 0, top = 0, width = 0, height = 0;
				    boolean finternal = false, fscroll = false, ffloat = false;
				    for ( sParam item : params) 
				    {
				      String s = item.name;
				      if (s.equals("name")) name = item.value;
				      if (s.equals("action")) action = item.value;
				      if (s.equals("title")) title = item.value;
				      if (s.equals("align")) align = item.value;
				      if (s.equals("left")) left = state.computeCoord (item.value, true,false);
				      if (s.equals("top")) top = state.computeCoord (item.value, false,false);
				      if (s.equals("width")) width = state.computeCoord (item.value, true,false);
				      if (s.equals("height")) height = state.computeCoord (item.value, false,false);
				    }
				    for ( String item : flags)
				    {  
				      if (item.equals("internal")) finternal = true;
				      if (item.equals("scrolling")) fscroll = true;
				      if (item.equals("floating")) ffloat = true;
				    }
				    if (name.isEmpty())
				    {
				      results.addToList (results.createError ("Received FRAME tag with no frame name!"));
				      return;
				    }
				    state.gotFRAME (name, action, title, finternal, align, left, top, width, height,
				        fscroll, ffloat);
				  }
				  else if (name2.equals("dest"))
				  {
				    String name = "";
				    int x = 0, y = 0;
				    boolean feol = false, feof = false;
				    for ( sParam item : params) 
				    {
				      String s = item.name;
				      if (s.equals("name")) name = item.value;
				      if (s.equals("x")) x = Integer.parseInt(item.value);
				      if (s.equals("y")) y = Integer.parseInt(item.value);
				    }
				    for ( String item : flags)
				    {  
				      if (item.equals("eol")) feol = true;
				      if (item.equals("eof")) feof = true;
				    }
				    if (name.isEmpty())
				    {
				      results.addToList (results.createError ("Received DEST tag with no frame name!"));
				      return;
				    }
				    state.gotDEST (name, x, y, feol, feof);
				  }
				  else if (name2.equals("relocate"))
				  {
				    String name = "";
				    int port = 0;
				    for ( sParam item : params) 
				    {
				      String s = item.name;
				      if (s.equals("name")) name = item.value;
				      if (s.equals("port")) port = Integer.parseInt(item.value);
				    }
				    if (name.isEmpty())
				    {
				      results.addToList (results.createError ("Received RELOCATE tag with no server name!"));
				      return;
				    }
				    if (port == 0)
				    {
				      results.addToList (results.createError ("Received RELOCATE tag with no server port!"));
				      return;
				    }
				    state.gotRELOCATE (name, port);
				  }
				  else if (name2.equals("user"))
				    state.gotUSER();
				  else if (name2.equals("password"))
				    state.gotPASSWORD();
				  else if (name2.equals("image"))
				  {
				    String name="", url="", t="", align="";
				    int h = 0, w = 0, hspace = 0, vspace = 0;
				    boolean fismap = false;
				    for ( sParam item : params) 
				    {
				      String s = item.name;
				      if (s.equals("fname")) name = item.value;
				      if (s.equals("url")) url = item.value;
				      if (s.equals("t")) t = item.value;
				      if (s.equals("align")) align = item.value;
				      if (s.equals("h")) h = state.computeCoord (item.value, true, true);
				      if (s.equals("w")) w = state.computeCoord (item.value, false, true);
				      if (s.equals("hspace")) hspace = Integer.parseInt(item.value);;
				      if (s.equals("vspace")) vspace = Integer.parseInt(item.value);;
				    }
				    for ( String item : flags)
				    {  
				      if (item.equals("ismap")) fismap = true;
				    }
				    if (name.isEmpty())
				    {
				      results.addToList (results.createError ("Received IMAGE tag with no image name!"));
				      return;
				    }
				    state.gotIMAGE (name, url, t, h, w, hspace, vspace, align, fismap);
				  }
				  else if (name2.equals("filter"))
				  {
				/*
				    String src, dest, name;
				    for (it = params.begin(); it != params.end(); ++it)
				    {
				      String s = (*it).name;
				      if (s.equals("src") src = (*it).value;
				      if (s.equals("dest") dest = (*it).value;
				      if (s.equals("name") name = (*it).value;
				    }
				    state.gotFILTER (src, dest, name);
				*/
				    results.addToList (results.createWarning ("Ignoring unsupported FILTER tag."));
				  }
				}

		  private void setAttList(String name, ArrayList<String> attlist,
				HashMap<String, String> attdefault) 
		  {
			  //sanity check
			  if (!elements.containsKey(name))
			  {
			    results.addToList (results.createWarning ("Received attribute list for undefined tag " +
			        name + "!"));
			    return;
			  }
			  sElement e = elements.get(name);
			  e.attlist.clear();
			  e.attdefault.clear();
			  e.attlist = attlist;
			  e.attdefault = attdefault;		 
		  }

		private void addElement(String name, List<sElementPart> contents,
				ArrayList<String> attlist, HashMap<String, String> attdefault,
				boolean open, boolean empty, int tag, String flag) 
		  {
			  //sanity checks
			  if (elementDefined (name))
			  {
			    results.addToList (results.createError ("Multiple definition of element " + name + "!"));
			    return;
			  }

			  sElement e = new sElement();
			  e.open = open;
			  e.empty = empty;
			  if ((tag >= 20) && (tag <= 99))
			  {
			    e.tag = tag;
			    if (lineTags.containsKey(tag))
			      results.addToList (results.createError ("Element " + name +
			          " uses an already assigned line tag!"));
			    lineTags.put(tag, name);
			  }
			  else
			    e.tag = 0;
			  e.flag = flag;

			  //assign element contents, generating the list of closing tags
			  e.element.clear();
			  for (sElementPart ep : contents) 
			  {
			    if (ep.istag)
			    {
			      String tag1 = ep.text.split(" ")[0].toLowerCase();
			      if (elementDefined (tag1))
			      {
			        if (open && !(openElement (tag1)))
			        {
			          ep = null;
			          results.addToList (results.createError ("Definition of open " + name +
			              " tag contains secure tag " + tag1 + "!"));
			        }
			        else if (empty && !(emptyElement (tag1)))
			        {
			        	ep = null;
			          results.addToList (results.createError ("Definition of empty " + name +
			              " tag contains non-empty tag " + tag1 + "!"));
			        }
			        else
			        {
			          e.element.add (ep);
			          if (!emptyElement(tag1)) e.closingseq.add(0,tag1);
			        }
			      }
			      else
			      {
			        //element doesn't exist yet - we must believe that it's correct
			        e.element.add(ep);
			        if (!empty) e.closingseq.add(0,tag1);
			        results.addToList (results.createWarning ("Definition of element " + name +
			            " contains undefined element " + tag1 + "!"));
			      }
			    }
			    else
			      e.element.add(ep);
			  }

			  //assign the element definition
			  elements.put(name,e);

			  //set attribute list
			  setAttList (name, attlist, attdefault);		  
		  }

		private void processParamList(String params, ArrayList<String> attlist,
				HashMap<String, String> attdefault) 
		  {
			  //this is similar to the parser in gotTag(), but it's a bit simpler...
			  if (params==null) return;
			  String name = "", value = "";
			  char quote = 0;
			  paramParserState state = paramParserState.parNone;
			  for ( int i =0; i < params.length(); i++)
			  {
			    char ch = params.charAt(i);

			    //process character
			    switch (state) {
			      case parNone: {
			        if (ch != ' ')
			        {
			          state = paramParserState.parName;
			          name += ch;
			        }
			        break;
			      }
			      case parName: {
			        if (ch == '=')
			          state = paramParserState.parValue;
			        else if (ch == ' ')
			        {
			          //new parameter, no default value
			          attlist.add(name.toLowerCase());
			          name = "";
			          state = paramParserState.parNone;
			        }
			        else
			          name += ch;
			        break;
			      }
			      case parValue: {
			        if (ch == ' ')
			        {
			          //new parameter, with default value
			          attlist.add(name.toLowerCase());
			          attdefault.put(name, value);
			          name = "";
			          value = "";
			          state = paramParserState.parNone;
			        }
			        else if (value.isEmpty() && ((ch == '\'') || (ch == '"')))
			        {
			          state = paramParserState.parQuotedValue;
			          quote = ch;
			        }
			        else
			          value += ch;
			        break;
			      }
			      case parQuotedValue: {
			        if (ch == quote)
			        {
			          //new parameter, with default value
			          attlist.add(name.toLowerCase());
			          attdefault.put(name, value);
			          name = "";
			          value = "";
			          state = paramParserState.parNone;
			        }
			        else
			          value += ch;
			        break;
			      }
			    };
			  }

			  //last parameter...
			  switch (state) {
			    case parName: {
			      //new parameter, no default value
			      attlist.add(name.toLowerCase());
			    }
			    break;
			    case parValue: {
			      //new parameter, with default value
			      attlist.add(name.toLowerCase());
			      attdefault.put(name, value);
			      break;
			    }
			    case parQuotedValue:
			      results.addToList (results.createWarning (
			          "Received tag definition with unfinished quoted default parameter value!"));
			      //new parameter, with default value (unfinished, but hey...)
			      attlist.add(name.toLowerCase());
			      attdefault.put(name, value);
			    break;
			  };
			  
			  //everything done...
			  
		  }

		private void processCustomTag(String name, List<sParam> params) throws Exception
		  {
		    //generate a mapping with all parameter values
		    paramexpander.reset(false);
		    for (sParam param : params) 
		    {
			      //assign parameter value... default values and stuff were already expanded
			      paramexpander.addEntity (param.name, "'" + param.value + "'");			
			}	    
		    //process tag contents one by one
		    for (sElementPart sep : elements.get(name).element) 
		    {
		      String part = sep.text;
		      
		      //expand tag parameters first
		      part = paramexpander.expandEntities (part, true);
		      
		      //parameters are expanded, process this part
		      if (sep.istag)
		        //this part is another tag - expand it recursively
		        gotTag(part);
		      else
		        //this part is regular text
		        state.gotText(part,true);
		    }

		    if (!elements.get(name).flag.isEmpty())
		      state.gotFlag (true, elements.get(name).flag);
		  }

		void gotLineTag (int number) throws Exception
		{
		  if ((number < 20) || (number > 99))
		  {
		    lastLineTag = 0;
		    return;
		  }
		  if (!lineTags.containsKey(number))
		  {
		    lastLineTag = 0;
		    return;
		  }
		  String tag = lineTags.get(number);
		  lastLineTag = number;
		  //behave as if we've just gotten the appropriate tag
		  gotTag(tag);
		}

		public void gotNewLine ()
		{
		  if ((lastLineTag < 20) || (lastLineTag > 99))
		  {
		    lastLineTag = 0;
		    return;
		  }
		  if (!lineTags.containsKey(lastLineTag))
		  {
		    lastLineTag = 0;
		    return;
		  }
		  String tag = lineTags.get(lastLineTag);
		  lastLineTag = 0;
		  if (emptyElement (tag))
		    //no closing tag needed
		    return;
		  //okay, send out the appropriate closing tag
		  handleClosingTag (tag);
		}
		
	}
	//----------------------------------------------------------------------------------------------------
	private class chunk 
	{
		  public chunkType chk;
		  public String text;
	}		
	//----------------------------------------------------------------------------------------------------
	private class MXPParser 
	{


		private parserState pstate;
		private String str = "";
		private ArrayList<chunk> chunks = new ArrayList<chunk>();
		private char quoteChar;
		private boolean wasBackslashR;
		private MXPState state;
		private ElementManager elements;
		private ResultHandler results;
		
		
		/** constructor */
		public MXPParser (MXPState st, ElementManager elm, ResultHandler res )
		{
			  state = st;
			  elements = elm;
			  results = res;
			  
			  pstate = parserState.pText;
			  wasBackslashR = false;
			
		}

		public void simpleParse(String text) 
		{
			  if (text.isEmpty())
				    return;
			  chunk ch = new chunk();
			  pstate = parserState.pText;
			  str = "";
			  for ( int i =0; i < text.length(); i++ ) 
			  {			
				    char c = text.charAt(i);
				    switch (pstate) 
				    {
				      case pText: {
				        if (c == '<')
				        {
				          //end of text - got start of tag
				          if (!str.isEmpty())
				          {
				            ch.chk = chunkType.chunkText;
				            ch.text = str;
				            chunks.add(ch);
				            ch = new chunk();
				            str = "";
				          }
				          pstate = parserState.pTag;
				        }
				        else
				          str += c;  //add new character to the text... 
				        break;
				      }
				      case pTag: {
				        if (c == '>')
				        {
				          ch.chk = chunkType.chunkTag;
				          ch.text = str;
				          chunks.add(ch);
				          ch = new chunk();
				          str = "";
				          pstate = parserState.pText;
				        }
				        else
				        if ((c == '"') || (c == '\''))
				        {
				          pstate = parserState.pQuotedParam;
				          quoteChar = c;
				          str += c;
				        }
				        else
				          str += c;
				        break;
				      }
				      case pQuotedParam: {
				        if (c == quoteChar)
				        {
				          //quoted parameter ends... this simple approach will work correctly for correct
				          //tags, it may treat incorrect quotes as correct, but element manager will take care
				          //of that
				          pstate = parserState.pTag;
				          str += c;
				        }
				        else
				          str += c;
				        break;
				      }
				    };
				  }
				  //unfinished things...
				  if (pstate == parserState.pText)
				  {
				    ch.chk = chunkType.chunkText;
				    ch.text = str;
				    chunks.add(ch);
				    ch = new chunk();
				  }
				  if ((pstate == parserState.pTag) || (pstate == parserState.pQuotedParam))
				  {
				    ch.chk = chunkType.chunkError;
				    ch.text = "Tag definition contains unfinished tag <" + str;
				    chunks.add(ch);
				    ch = new chunk();
				  }
				  str = "";
			
		}

		public boolean hasNext() 
		{
			  return chunks.isEmpty() ? false : true;
		}

		public chunk getNext() 
		{
			  if (!hasNext())
			  {
			    chunk nochunk = new chunk();
			    nochunk.chk = chunkType.chunkNone;
			    return nochunk;
			  }
			  chunk ch = chunks.get(0);
			  chunks.remove(0);
			  return ch;
		  }

		public void parse(String text) throws Exception
		{
			  //WARNING: examine this function only at your own risk!
			  //it is advised to have a look at the simpleParse() function first - it's similar
			  //to this one, but much simpler...
			  if (text.isEmpty())
			    return;
			  for (int i = 0; i < text.length(); i++)
			  {
			    char c = text.charAt(i);
			    
			// Looks like number of brain-dead servers that send out \n\r is bigger than the
			// number of servers that send out \r alone - the latter maybe don't exist at
			// all. Hence, with this commented out, we can't handle the \r-only ones,
			// but \n\r works.
			/*
			    //handle \r not followed by \n - treated as a newline
			    if (wasBackslashR && (c != '\n'))
			    {
			      //"str" now certainly is empty, so we needn't care about that
			      //report new-line
			      elements->gotNewLine();
			      state->gotNewLine();
			    }
			*/
			    wasBackslashR = false;
			    
			    //we need current mode - parsing in LOCKED mode is limited
			    //mode is retrieved in every iteration to ensure that it's always up-to-date
			    mxpMode mode = state.getMXPMode();
			    switch (pstate) {
			      case pText: {
			        //tags not recognized in LOCKED mode...
			        if ((c == '\u001B') || ((mode != mxpMode.lockedMode) && 
			        		(c == '<')) || (c == '\n') || (c == '\r'))
			        {
			          //end of text - got newline / ANSI seq / start of tag
			          if (!str.isEmpty())
			          {
			            state.gotText(str,true);
			            str = "";
			          }
			          if (c == '\u001B')
			            pstate = parserState.pAnsiSeq;
			          if ((c == '<') && (mode != mxpMode.lockedMode))
			            pstate = parserState.pTag;
			          if (c == '\n')
			          {
			            //report new-line
			            elements.gotNewLine();
			            state.gotNewLine();
			          }
			          if (c == '\r')
			            wasBackslashR = true;
			        }
			        else
			          str += c;  //add new character to the text... 
			        break;
			      }
			      case pAnsiSeq: {
			        if ((c == '\u001B') || (c == '\n') || (c == '\r'))
			        {
			          //the same as in pTag section...
			          results.addToList (results.createError ("Received unfinished ANSI sequence!"));
			          str = "";
			          if (c == '\u001B')
			            pstate = parserState.pAnsiSeq;
			          if (c == '\n')
			          {
			            //report new-line
			            elements.gotNewLine();
			            state.gotNewLine();
			            pstate = parserState.pText;
			          }
			          if (c == '\r')
			          {
			            pstate = parserState.pText;
			            wasBackslashR = true;
			          }
			        }
			        else
			        if (((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')))
			        {
			          //ANSI sequence ends...
			          if (c == 'z')  //line tag
			          {
			            if (str.isEmpty())
			            {
			              //invalid sequence
			              str = "\u001Bz";
			            }
			            else
			            {
			              //process this sequence
			              int len = str.length();
			              int num = 0;
			              for (int j = 1; j < len; j++)  //str[0] is '[', which is SKIPPED
			              {
			                char cc = str.charAt(j);
			                if (cc == ';')  //this shouldn't happen, but some MUD might want to use it...
			                {
			                  if ((num >= 0) && (num <= 99))  //ensure that number lies in correct range
			                  {
			                    state.gotLineTag (num);
			                    elements.gotLineTag (num);
			                  }
			                  else
			                    results.addToList (results.createError ("Received invalid line tag!"));
			                  num = 0;
			                }
			                else
			                  num = num * 10 + (cc - 48);  //48 is the code of '0'
			              }
			              //report last line tag (and usually the only one)
			              if ((num >= 0) && (num <= 99))  //ensure that number lies in correct range
			              {
			                state.gotLineTag (num);
			                elements.gotLineTag (num);
			              }
			              else
			                results.addToList (results.createError ("Received invalid line tag!"));
			              str = "";
			            }
			          }
			          else  //something else
			          {
			            //'\u001B' and c are not in the string - add them there
			            str = '\u001B' + str + c;
			          } 
			          pstate = parserState.pText;
			        }
			        else
			        if (c == '[')  //this one is valid, but only at the beginning
			        {
			          if (str.isEmpty())
			            str += c;
			          else
			          {
			            //'[' in the middle of ANSI seq => not an ANSI seq...
			            pstate = parserState.pText;
			            str = '\u001B' + str + c;
			          }
			        }
			        else
			        if ((c == ';') || ((c >= '0') && (c <= '9')))  //correct char, unless str is empty
			          if (!str.isEmpty())
			            str += c;  //here we go...
			          else
			          {
			            //ANSI seq must start with [ - therefore this is not an ANSI sequence after all
			            pstate = parserState.pText;
			            str += '\u001B';
			            str += c;
			          }
			        else
			        //incorrect character...
			        {
			          str = '\u001B' + str + c;
			          pstate = parserState.pText;
			        }
			        break;
			      }
			      case pTag: {
			        if (c == '>')
			        {
			          elements.gotTag (str);
			          str = "";
			          pstate = parserState.pText;
			        }
			        else
			        if ((c == '"') || (c == '\''))
			        {
			          pstate = parserState.pQuotedParam;
			          quoteChar = c;
			          str += c;
			        }
			        else if ((c == '\u001B') || (c == '\n') || (c == '\r'))
			        {
			          //handle incorrectly terminated tag and continue parsing...
			          results.addToList (results.createError ("Received unfinished tag <" + str));
			          str = "";
			          if (c == '\u001B')
			            pstate = parserState.pAnsiSeq;
			          if (c == '\n')
			          {
			            //report new-line
			            elements.gotNewLine();
			            state.gotNewLine();
			            pstate = parserState.pText;
			          }
			          if (c == '\r')
			          {
			            pstate = parserState.pText;
			            wasBackslashR = true;
			          }
			        }
			        else if (str.equals("!--"))  //comment
			        {
			          str += c;
			          pstate = parserState.pComment;
			        }
			        else
			          str += c;
			        break;
			      }
			      case pComment: {
			        if (c == '>')
			        {
			          int l = str.length();
			          if ((str.charAt(l-2) == '-') && (str.charAt(l-1) == '-')) //okay, comment ends
			          {
			            str = "";
			            pstate = parserState.pText;
			          }
			          else
			            str += c;
			        }
			        else if ((c == '\u001B') || (c == '\n') || (c == '\r'))
			        {
			          //handle incorrectly terminated comment and continue parsing...
			          results.addToList (results.createError ("Received an unfinished comment!"));
			          str = "";
			          if (c == '\u001B')
			            pstate = parserState.pAnsiSeq;
			          if (c == '\n')
			          {
			            //report new-line
			            elements.gotNewLine();
			            state.gotNewLine();
			            pstate = parserState.pText;
			          }
			          if (c == '\r')
			          {
			            pstate = parserState.pText;
			            wasBackslashR = true;
			          }
			        }
			        else
			          str += c;
			        break;
			      }
			      case pQuotedParam: {
			        if (c == quoteChar)
			        {
			          //quoted parameter ends... this simple approach will work correctly for correct
			          //tags, it may treat incorrect quotes as correct, but element manager will take care
			          //of that
			          pstate = parserState.pTag;
			          str += c;
			        }
			        else
			        if ((c == '\u001B') || (c == '\n') || (c == '\r'))
			        {
			          //the same as in pTag section...
			          results.addToList (results.createError ("Received unfinished tag <" + str));
			          str = "";
			          if (c == '\u001B')
			            pstate = parserState.pAnsiSeq;
			          if (c == '\n')
			          {
			            //report new-line
			            elements.gotNewLine();
			            state.gotNewLine();
			            pstate = parserState.pText;
			          }
			          if (c == '\r')
			          {
			            pstate = parserState.pText;
			            wasBackslashR = true;
			          }
			        }
			        else
			          str += c;
			        break;
			      }
			    };
			  }
			  //report remaining text, if any (needed to improve speed of text displaying and to handle
			  //prompts correctly)
			  if ((pstate == parserState.pText) && (!str.isEmpty()))
			  {
			    state.gotText(str,true);
			    str = "";
			  }
		}		
	}
	//----------------------------------------------------------------------------------------------------
	private class MXPProcessor 
	{
		private ResultHandler results;
		private MXPState state;
		private MXPParser parser;
		private EntityManager entities;
		private ElementManager elements;
		
		public MXPProcessor(String package_name, String version)
		{
			  //create all the objects...
			  results = new ResultHandler();
			  entities = new EntityManager(false);
			  elements = new ElementManager(null, results, entities);
			  state = new MXPState(results, elements, entities, package_name, version);
			  elements.assignMXPState(state);
			  parser = new MXPParser(state, elements, results);		
		}

		public void processText(String text) throws Exception 
		{
			if (text.isEmpty()) return;
			parser.parse(text);
		}

		public void setDefaultText(String font, int size, boolean _bold,
				boolean _italic, boolean _underline, boolean _strikeout, Color fg,
				Color bg) 
		{
			state.setDefaultText (font, size, _bold, _italic, _underline, _strikeout, fg, bg);		
		}

		public void setHeaderParams(int which, String font, int size,
				boolean _bold, boolean _italic, boolean _underline,
				boolean _strikeout, Color fg, Color bg)
		{
			state.setHeaderParams(which, font, size, _bold, _italic, _underline, _strikeout, fg, bg);		
		}

		public void setDefaultGaugeColor(Color color) 
		{
			state.setDefaultGaugeColor(color);		
		}

		public void setNonProportFont(String font) 
		{
			state.setNonProportFont(font);		
		}

		public void setClient(String name, String version) 
		{
			state.setClient(name,version);
		}

		public boolean hasResult()
		{
			return results.haveResults();		
		}

		public MXPResult nextResult() 
		{
			return results.nextResult();
		}
		
		public void switchToOpen ()
		{
		  state.switchToOpen ();
		}

		public void setScreenProps (int sx, int sy, int wx, int wy, int fx, int fy)
		{
		  state.setScreenProps (sx, sy, wx, wy, fx, fy);
		}

		public void supportsLink (boolean supports)
		{
		  state.supportsLink (supports);
		}

		public void supportsGauge (boolean supports)
		{
		  state.supportsGauge (supports);
		}

		public void supportsSound (boolean supports)
		{
		  state.supportsSound (supports);
		}

		public void supportsStatus (boolean supports)
		{
		  state.supportsStatus (supports);
		}

		public void supportsFrame (boolean supports)
		{
		  state.supportsFrame (supports);
		}

		public void supportsImage (boolean supports)
		{
		  state.supportsImage (supports);
		}

		public void supportsRelocate (boolean supports)
		{
		  state.supportsRelocate (supports);
		}		
	}
	//----------------------------------------------------------------------------------------------------
	private class ResultHandler 
	{	
		/** 
		 * result that was most recently sent to the app 
		 */	
		private MXPResult returnedResult;
		private ArrayList<MXPResult> results = new ArrayList<MXPResult>();
		
		/** 
		 * constructor 
		 */	
		public ResultHandler()
		{
			returnedResult = null;
		}

		public MXPResult createError(String error) 
		{
			  MXPResult res = new MXPResult();
			  res.type = -1;
			  res.data = error;
			  return res;
		}
		
		public void addToList(MXPResult res)
		{
		  if (res!=null)
		  {
			  results.add(res);
		  }
		}

		public MXPResult createText(String t) 
		{
			  MXPResult res = new MXPResult();
			  res.type = 1;
			  res.data = t;
			  return res;
		}

		public MXPResult createWarning(String string) 
		{
			  MXPResult res = new MXPResult();
			  res.type = -2;
			  res.data = string;
			  return res;
		}

		public MXPResult createVariable(String varName, String varValue, boolean erase) 
		{
			MXPResult res = new MXPResult();
			res.type = 4;
			varStruct vs = new varStruct();
			vs.name = varName;
			vs.value = varValue;
			vs.erase = erase;
			res.data = vs;
			return res;
		}

		public void deleteResult(MXPResult res) 
		{
			if (res == null)
				return;
			res.data = null;
			res = null;
		}

		public MXPResult createSendThis(String text) 
		{
			  MXPResult res = new MXPResult();
			  res.type = 9;
			  res.data = text;
			  return res;
		}

		public MXPResult createFlag(boolean begin, String flag) 
		{
			MXPResult res = new MXPResult();
			res.type = 3;
			flagStruct fs = new flagStruct();
			fs.begin = begin;
			fs.name = flag;
			res.data = fs;
			return res;
		}

		public MXPResult createFormatting(int usemask, int curattrib,
				Color fgcolor, Color bgcolor, String font, int cursize) 
		{
			MXPResult res = new MXPResult();
			res.type = 5;
			formatStruct fs = new formatStruct();
			fs.usemask = usemask;
			fs.attributes = curattrib;
			fs.fg = fgcolor;
			fs.bg = bgcolor;
			fs.size = cursize;
			fs.font = font;
			res.data = fs;
			return res;		
		}

		public MXPResult createSetWindow(String curWindow) 
		{
			MXPResult res = new MXPResult();
			res.type = 15;
			res.data = curWindow;
			return res;
		}

		public MXPResult createLink(String name, String url, String text,
				String hint) 
		{
			  MXPResult res = new MXPResult();
			  res.type = 6;
			  linkStruct ls = new linkStruct();
			  ls.name = name;
			  ls.url = url;
			  ls.text = text;
			  ls.hint = hint;
			  res.data = ls;
			  return res;
		}

		public MXPResult createSendLink(String name, String cmd, String text,
				String hint, boolean prompt, boolean menu) 
		{
			  MXPResult res = new MXPResult();
			  res.type = 7;
			  SendStruct ss = new SendStruct();
			  ss.name = name;
			  ss.command = cmd;
			  ss.text = text;
			  ss.hint = hint;
			  ss.toprompt = prompt;
			  ss.ismenu = menu;
			  res.data = ss;
			  return res;
		}

		public MXPResult createExpire(String name) 
		{
			  MXPResult res = new MXPResult();
			  res.type = 8;
			  res.data = name;
			  return res;
			
		}

		public MXPResult createHorizLine() 
		{
			  MXPResult res = new MXPResult();
			  res.type = 10;
			  res.data = null;
			  return res;
		}

		public MXPResult createSound(boolean isSOUND, String fname, int vol, int count,
				int priority, boolean contifrereq, String type, String url) 
		{
			  MXPResult res = new MXPResult();
			  res.type = 11;
			  soundStruct ss = new soundStruct();
			  ss.fname = fname;
			  ss.type = type;
			  ss.url = url;
			  ss.isSOUND = isSOUND;
			  ss.vol = vol;
			  ss.repeats = count;
			  ss.priority = priority;
			  ss.continuemusic = contifrereq;
			  res.data = ss;
			  return res;
			
		}

		public MXPResult createGauge(String variable, String maxvariable,
				String caption, Color color) 
		{
			  MXPResult res = new MXPResult();
			  res.type = 22;
			  gaugeStruct gs = new gaugeStruct();
			  gs.variable = variable;
			  gs.maxvariable = maxvariable; 
			  gs.caption = caption;
			  gs.color = color;
			  res.data = gs;
			  return res;
		}

		public MXPResult createMoveCursor(int x, int y) 
		{
			  MXPResult res = new MXPResult();
			  res.type = 16;
			  moveStruct ms = new moveStruct();
			  ms.x = x;
			  ms.y = y;
			  res.data = ms;
			  return res;
		}

		public MXPResult createEraseText(boolean feof) 
		{
			  MXPResult res = new MXPResult();
			  res.type = 17;
			  res.data = feof;
			  return res;
			
		}

		public MXPResult createStat(String variable, String maxvariable, String caption) 
		{
			  MXPResult res = new MXPResult();
			  res.type = 23;
			  statStruct ss = new statStruct();
			  ss.variable = variable;
			  ss.maxvariable = maxvariable;
			  ss.caption = caption;
			  res.data = ss;
			  return res;
			
		}

		public MXPResult createInternalWindow(String nm, String tt, alignType at,
				boolean scrolling) 
		{
			  MXPResult res = new MXPResult();
			  res.type = 13;
			  internalWindowStruct ws = new internalWindowStruct();
			  ws.name = nm;
			  ws.align = at;
			  ws.scrolling = scrolling;
			  res.data = ws;
			  return res;		
		}

		public MXPResult createWindow(String nm, String tt, int left, int top,
				int width, int height, boolean scrolling, boolean floating)
		{
			  MXPResult res = new MXPResult();
			  res.type = 12;
			  windowStruct ws = new windowStruct();
			  ws.name = nm; 
			  ws.title = tt;
			  ws.left = left;
			  ws.top = top;
			  ws.width = width;
			  ws.height = height;
			  ws.scrolling = scrolling;
			  ws.floating = floating;
			  res.data = ws;
			  return res;
			
		}

		public MXPResult createCloseWindow(String nm) 
		{
			  MXPResult res = new MXPResult();
			  res.type = 14;
			  res.data = nm;
			  return res;		
		}

		public MXPResult createRelocate(String hostname, int port) 
		{
			  MXPResult res = new MXPResult();
			  res.type = 18;
			  relocateStruct rs = new relocateStruct();
			  rs.server = hostname;
			  rs.port = port;
			  res.data = rs;
			  return res;

		}

		public MXPResult createSendLogin(boolean username) 
		{
			  MXPResult res = new MXPResult();
			  res.type = 19;
			  res.data = (Boolean)username;
			  return res;
		}

		public MXPResult createImageMap(String lastcmd) 
		{
			  MXPResult res = new MXPResult();
			  res.type = 21;
			  res.data = lastcmd;
			  return res;		
		}

		public MXPResult createImage(String fname, String url, String type,
				int height, int width, int hspace, int vspace, alignType at)
		{
			  MXPResult res = new MXPResult();
			  res.type = 20;
			  imageStruct is = new imageStruct();
			  is.fname = fname;
			  is.url = url;
			  is.type = type;
			  is.height = height;
			  is.width = width;
			  is.hspace = hspace;
			  is.vspace = vspace;
			  is.align = at;
			  res.data = is;
			  return res;
			
		}

		public MXPResult createLineTag(int number) 
		{
			  MXPResult res = new MXPResult();
			  res.type = 2;
			  res.data = number;
			  return res;
			
		}

		public boolean haveResults() 
		{
			return results.size() > 0;		
		}

		public MXPResult nextResult() 
		{
			  returnedResult = results.get(0);
			  results.remove(0);
			  return returnedResult;
		}
		
	}
	//----------------------------------------------------------------------------------------------------
	private class MXPState 
	{		
		//initial LOCKED state?
		boolean initiallyLocked;
		//	currently implemented MXP version
		String mxpVersion;
		String clientName, clientVersion;	
		int Hattribs[] = new int[6];
		Color Hfg[] = new Color[6], Hbg[] = new Color[6];
		String Hfont [] = new String[6];
		int Hsize[] = new int[6];
		int defaultsize;
		String ttFont;
		/** list of existing frames */
		HashMap<String, Boolean> frames = new HashMap<String, Boolean>();
		//screen and font parameters
		int sX, sY;      // width/height of the screen
		int wX, wY;      // width/height of the output window
		int fX, fY;      // width/height of character X
		//user-defined values
		Color defaultfg, defaultbg;
		String defaultfont;
		int defaultattribs;
		Color gaugeColor;

		/** 
		 * current mode 
		 */
		private mxpMode mode;
		private ResultHandler results;
		private ElementManager elements;
		private EntityManager entities;
		/** temporary secure mode? */
		private boolean tempMode;
		/** did we just leave secure mode?*/
		private boolean wasSecureMode;
		//variables
		private boolean inVar;
		/** current default mode */
		private mxpMode defaultmode;
		private String varName, varValue;

		//links
		private boolean inLink, isALink;
		private String linkText;

		/** list of closing tags */
		private ArrayList<closingTag> closingTags = new ArrayList<closingTag>();

		//text attributes
		private boolean bold, italic, underline, strikeout;
		private Color fgcolor, bgcolor;
		private String curfont;
		private int cursize;
		
		//paragraphs
		private boolean inParagraph;  //in P tag; no method returns this
		private boolean ignoreNextNewLine;  //after NOBR; no method returns this
		private String lastcmd;
		private boolean gotmap;
		//current window
		private String curWindow, prevWindow;
		
		//SUPPORTS stuff...
		private boolean suplink, supgauge, supstatus, supsound, supframe, supimage, suprelocate;	

		private class closingTag 
		{
			  //tag name (lowercase)
			  String name;
			  //closing result, if there's exactly one
			  MXPResult closingresult;
			  //usually only zero or one element, but sometimes more :-)
			  List<MXPResult> closingresults;
		};
		
		public MXPState(ResultHandler resh, ElementManager elm,
				EntityManager enm, String package_name, String version) 
		{
			  results = resh;
			  elements = elm;
			  entities = enm;

			  //currently implemented MXP version
			  mxpVersion = "1.0";

			  //starting MXP mode is LOCKED, to prevent problems with non-MXP MUDs).
			  //This goes against the MXP protocol, therefore there's a setting that will keep the OPEN
			  //mode, if desired - see public API.
			  mode = mxpMode.lockedMode;
			  defaultmode = mxpMode.lockedMode;
			  initiallyLocked = true;
			  tempMode = false;
			  wasSecureMode = false;
			  
			  //some default values...
			  MXPColors colors = allcolors;
			  defaultfg = colors.getColor("gray");
			  defaultbg = colors.getColor("black");
			  defaultfont = "Courier";
			  defaultsize = 12;
			  defaultattribs = 0;
			  //by default, all headers are written in the same font (Courier), they are bold and they
			  //differ in sizes...
			  for (int i = 0; i < 6; i++)
			  {
			    Hfont[i] = "Courier";
			    Hfg[i] = defaultfg;
			    Hbg[i] = defaultbg;
			    Hattribs[i] = Bold;
			  }
			  Hsize[0] = 32;
			  Hsize[1] = 24;
			  Hsize[2] = 20;
			  Hsize[3] = 16;
			  Hsize[4] = 14;
			  Hsize[5] = 12;
			  ttFont = "Courier";
			  setDefaultGaugeColor (colors.getColor("white"));
			  //PACKAGE and VERSION are defined in config.h
			  clientName = package_name;
			  clientVersion = version;
			  //some default screen and font attributes...
			  fX = 16;
			  fY = 8;
			  sX = 800;
			  sY = 600;
			  
			  suplink = supgauge = supstatus = supframe = supimage = suprelocate = false;
			  
			  //params
			  reset();
		}

		private void reset() 
		{
			  bold = (defaultattribs & Bold) != 0;
			  italic = (defaultattribs & Italic) != 0;
			  underline = (defaultattribs & Underline) != 0;
			  strikeout = (defaultattribs & Strikeout) != 0;
			  fgcolor = defaultfg;
			  bgcolor = defaultbg;
			  curfont = defaultfont;
			  cursize = defaultsize;
			  inVar = false;
			  varValue = "";
			  inParagraph = false;
			  ignoreNextNewLine = false;
			  inLink = false;
			  isALink = false;
			  linkText = "";
			  gotmap = false;
			  curWindow = "";
			  prevWindow = "";
		}

		public void setDefaultGaugeColor(Color color) 
		{
			gaugeColor = color;
		}

		/** 
		 * return current mode 
		 */
		public mxpMode getMXPMode() 
		{
			  return mode;
		}

		public void gotSUPPORT(List<String> params) throws Exception 
		{
		  commonTagHandler();

		  if (!params.isEmpty())  //some parameters - this is not supported at the moment
		    results.addToList(results.createWarning (
		        "Received <support> with parameters, but this isn't supported yet..."));
		    
		  String res;
		  res = "\u001b[1z<SUPPORTS +!element +!attlist +!entity +var +b +i +u +s +c +h +font";
		  res += " +nobr +p +br +sbr +version +support +h1 +h2 +h3 +h4 +h5 +h6 +hr +small +tt";
		  if (suplink)
		    res += " +a +send +expire";
		  if (supgauge)
		    res += " +gauge";
		  if (supstatus)
		    res += " +status";
		  if (supsound)
		    res += " +sound +music";
		  if (supframe)
		    res += " +frame +dest";
		  if (supimage)
		    res += " +image";
		  if (suprelocate)
		    res += " +relocate +user +password";
		  res += ">\r\n";
		  results.addToList (results.createSendThis (res));
		  
		  commonAfterTagHandler();
		}

		/** stuff common for all tags */
		private void commonTagHandler() 
		{
			  //got a new tag - close outstanding entities, if any (unless we're in LOCKED mode)
			  if (mode != mxpMode.lockedMode)
			  {
			    String t = entities.expandEntities ("", true);
			    if (!(t.isEmpty()))
			      gotText(t, false);
			  }

			  //outstanding tags are closed, if we're going out of secure mode, unless a change back to secure
			  //mode occurs
			  if (wasSecureMode)
			  {
			    closeAllTags ();
			    wasSecureMode = false;
			  }

			  //error is reported, if we're inside VAR...
			  if (inVar)
			    results.addToList (results.createError ("Got a tag inside a variable!"));		
		}

		public void gotText (String text, boolean expandentities)
		{
		  if (text.isEmpty())
			  return;
		  //temp-secure mode . ERROR!
		  if (tempMode)
		  {
		    tempMode = false;
		    mode = defaultmode;
		    results.addToList (results.createError ("Temp-secure line tag not followed by a tag!"));
		  }

		  //outstanding tags are closed, if we're going out of secure mode, unless a change back to secure
		  //mode occurs
		  if (wasSecureMode)
		  {
		    closeAllTags ();
		    wasSecureMode = false;
		  }

		  //expand entities, if needed
		  String t;
		  if (expandentities && (mode != mxpMode.lockedMode))
		    t = entities.expandEntities (text, false);
		  else
		    t = text;

		  //special handling if we're in a variable or a link
		  if (inVar)
		    varValue += t;
		  if (inLink)
		    linkText += t;

		  //text can be sent is it's not a part of a link or of a variable
		  if (!(inVar || inLink))
		    //add text to the list of things to send
		    results.addToList (results.createText(t));
		}

		private void closeAllTags()
		{
		  if (closingTags.isEmpty())
		    return;

		  //process open tags one by one...
		  while (!closingTags.isEmpty())
		  {
		    //closingTags is a FIFO queue, tho technically it's a list
			  int last = closingTags.size()-1;
			  closingTag tag = closingTags.get(last);
			  closingTags.remove(last);
			  results.addToList (results.createWarning ("Had to auto-close tag " + tag.name + "."));	    
			  closeTag(tag);
		  }
		}
		
		private void closeTag(closingTag tag)
		{
		  //some tags need special handling...
		  if (tag.name.equals("p"))
		  {
		    inParagraph = false;
		    ignoreNextNewLine = false;
		    //also send a newline after end of paragraph... MXP docs say nothing about this :(
		    results.addToList (results.createText ("\r\n"));
		  }
		  if (tag.name.equals("var"))
		  {
		    tag.closingresult = null;
		    tag.closingresults = null;
		    results.addToList (results.createVariable (varName, varValue, false));
		    results.addToList (results.createText (varName + ": " + varValue));
		    entities.addEntity (varName, varValue);
		    inVar = false;
		    varName = "";
		    varValue = "";
		  }
		  if (tag.name.equals("a"))
		  {
		    if (inLink && isALink)
		    {
		      linkStruct ls = (linkStruct)tag.closingresult.data;
		      //assign text, using URL if no text given
		      String lt = linkText.isEmpty() ? (ls.url != null ? ls.url : "") : linkText;
		      lt = stripANSI (lt);
		      ls.text = lt;
		    }
		    else
		      //this should never happen
		      results.addToList (results.createError ("Received </A> tag, but I'm not in a link!"));
		    linkText = "";
		    inLink = false;
		    isALink = false;
		  }
		  if (tag.name.equals("send"))
		  {
		    if (gotmap)
		    {
		      //don't send this closing result
		      results.deleteResult (tag.closingresult);
		      tag.closingresult = null;
		      
		      if (!linkText.isEmpty())
		        results.addToList (results.createError
		            ("Received image map and a command in one SEND tag!"));
		    }
		    else if (inLink && (!isALink))
		    {
		      SendStruct ss = (SendStruct) tag.closingresult.data;
		      //assign text, also assign to command if none given
		      
		      //assign linkText to ss.text
		      linkText = stripANSI (linkText);
		      ss.text = linkText;
		      if (ss.hint != null )
		      {
		        //expand &text; in hint
		        String hint = ss.hint;

		        boolean found = true, havematch = false;
		        while (found)
		        {
		        	hint = hint.replaceFirst("&text;", linkText);
		        	if ( hint.indexOf("&text;") < 0 ) found = false; //no more matches
		        }
		        if (havematch)  //apply changes if needed
		        {
		          //assign hint to ss.hint
		          ss.hint = hint;
		        }
		      }
		      if (!ss.command.isEmpty()) 
		      {
		        String cmd = ss.command;
		        //also expand &text; in href
		        
		        boolean found = true, havematch = false;
		        while (found)
		        {
		        	if ( cmd.indexOf("&text;") > 0 )
		        	{
		        		cmd = cmd.replace("&text;", linkText);
		        		havematch = true;
		        	}
		        	else
		        		found = false;  //no more matches
		        }
		        if (havematch)  //apply changes if needed
		        {
		        	//assign cmd to ss.command
		        	ss.command = cmd;
		        }
		      }
		      else if (!linkText.isEmpty())
		      {
		    	  //assign linkText to ss.command
		    	  ss.command = linkText;
		      }
		    }
		    else
		      //this should never happen
		      results.addToList (results.createError ("Received </SEND> tag, but I'm not in a link!"));

		    linkText = "";
		    inLink = false;
		    isALink = false;
		    gotmap = false;
		  }

		  //handle applying/sending of closing results, is any
		  if (tag.closingresult!=null)
		  {
		    //apply result, reverting changes made by opening tag
		    applyResult(tag.closingresult);
		    //and send the changes to the client app
		    results.addToList (tag.closingresult);
		  }
		  if (tag.closingresults!=null)
		  {
		    //the same for remaining closing tags...
			for (MXPResult item : tag.closingresults) 
			{
				applyResult(item);
				results.addToList(item);
			}
		  }
		  //finally, the closing tag gets deleted
		  //note that this won't delete the results themselves - they will be deleted after
		  //they are processed by the client app
		  tag.closingresults = null;
		  tag = null;
		}

		private String stripANSI (String s)
		{
		  // first of all, find out whether there are any ANSI sequences
		  boolean ansi = false;
		  for (int i = 0; i < s.length(); ++i)
		    if (s.charAt(i) == 27) ansi = true;
		  if (!ansi) return s;

		  // there are ANSI sequences - have to get rid of them
		  String res = "";
		  ansi = false;
		  for (int i = 0; i < s.length(); ++i) {
		    if (!ansi) {
		      if (s.charAt(i) == 27)
		        ansi = true;
		      else
		        res += s.charAt(i);
		    } else {
		      // ANSI seq is ended by a-z,A-Z
		      if ( Character.isLetter(s.charAt(i)) )
		        ansi = false;
		    }
		  }
		  return res;
		}

		private void applyResult (MXPResult what)
		{
		  switch (what.type) {
		    case 5: {
		      formatStruct fs = (formatStruct) what.data;
		      int usemask = fs.usemask;
		      if ((usemask & formatStruct.USE_BOLD) != 0 )
		        bold = (fs.attributes & Bold) != 0;
		      if ((usemask & formatStruct.USE_ITALICS) != 0 )
		        italic = (fs.attributes & Italic) != 0;
		      if ((usemask & formatStruct.USE_UNDERLINE) != 0 )
		        underline = (fs.attributes & Underline) != 0;
		      if ((usemask & formatStruct.USE_STRIKEOUT) != 0 )
		        strikeout = (fs.attributes & Strikeout) != 0;
		      if ((usemask & formatStruct.USE_FG) != 0 )
		        fgcolor = fs.fg;
		      if ((usemask & formatStruct.USE_BG) != 0 )
		        bgcolor = fs.bg;
		      if ((usemask & formatStruct.USE_FONT) != 0 )
		        curfont = fs.font;
		      if ((usemask & formatStruct.USE_SIZE) != 0 )
		        cursize = fs.size;
		      break;
		    }
		    case 15: 
		    {
		      prevWindow = curWindow;
		      if (what.data != null)
		        curWindow = (String)what.data;
		      else
		        curWindow = "";
		      break;
		    }
		  };
		}

		private void commonAfterTagHandler()
		{
		  //secure mode for one tag?
		  if (tempMode)
		  {
		    tempMode = false;
		    //set mode back to default mode
		    mode = defaultmode;
		  }
		}

		public void gotClosingTag(String name) 
		{
			  String nm = name.toLowerCase();
			  //hack, to prevent an error from being reported when </var> or end-of-flag comes
			  //we cannot simply test for </var> and friends and disable it then, because
			  //we could have the var tag inside some element
			  boolean oldInVar = inVar;
			  inVar = false;
			  
			  commonTagHandler();
			  
			  //restore the inVar variable...
			  inVar = oldInVar;

			  boolean okay = false;
			  while (!okay)
			  {
			    if (closingTags.isEmpty())
			      break;  //last one closed...
			    //closingTags is a FIFO queue, tho technically it's a list
			    int last = closingTags.size()-1;
			    closingTag tag = closingTags.get(last);
			    closingTags.remove(last);

			    if (tag.name.equals(nm))
			      okay = true;  //good
			    else
			      results.addToList (results.createWarning ("Had to auto-close tag " + tag.name +
			          ", because closing tag </" + name + "> was received."));
			    
			    closeTag (tag);
			  }

			  if (!okay)
			    results.addToList (results.createError ("Received unpaired closing tag </" + name + ">."));

			  commonAfterTagHandler();
		}

		//we treat flag as another tag - this is needed to allow correct flag closing even if the //appropriate closing tag wasn't sent by the MUD (auto-closing of flag)
		public void gotFlag(boolean begin, String flag) 
		{
			  boolean setFlag = false;  //is this a set-variable flag?
			  String f = flag.toLowerCase();
			  if ( f.indexOf("set ") == 0 )
				  setFlag = true;

			  //disable inVar and remember old value, if this is a set-flag
			  //this is needed to prevent error report in commonTagHandler()
			  boolean oldInVar = inVar;
			  if (setFlag) inVar = false;

			  commonTagHandler();
			  
			  //restore inVar value
			  inVar = oldInVar;
			  
			  //no -> inform about the flag
			  if (begin)
			  {
			    MXPResult res = results.createFlag (true, flag);
			    MXPResult res2 = createClosingResult (res);
			    results.addToList (res);
			    addClosingTag ("flag", res2, null);
			    
			    //"set xxx" type of flag?
			    if (setFlag)
			    {
			      if (inVar)  //in variable already
			      {
			        results.addToList (results.createError
			            ("Got a set-flag, but I'm already in a variable definition!"));
			        return;
			      }
			      //we are now in a variable
			      inVar = true;
			      varName = f.substring(f.lastIndexOf(' ') + 1);  //last word
			      varValue = "";
			    }
			  }
			  else
			  {
			    //closing set-flag...
			    if (inVar && setFlag)
			    {
			      results.addToList (results.createVariable (varName, varValue, false));
			      //send variable value, but no varname as in </var>
			      results.addToList (results.createText (varValue));
			      entities.addEntity (varName, varValue);
			      inVar = false;
			      varName = "";
			      varValue = "";
			    }
			    gotClosingTag ("flag");
			  }
			  
			  //no commonAfterTagHandler() here - this ain't no real tag :D
		}

		//mxpResult handling
		MXPResult createClosingResult (MXPResult what)
		{
			MXPResult res = null;
			switch (what.type) 
			{
		    	case 3: 
		    	{
		    		flagStruct fs = (flagStruct) what.data;
		    		res = results.createFlag (false, fs.name);
		    		break;
		    	}
		    	case 5: 
		    	{
		    		formatStruct fs = (formatStruct)what.data;
		    		//usemask is the most relevant thing here - things not enabled there won't be applied,
		    		//so we can place anything there
		    		int usemask = fs.usemask;
		    		int curattrib = (bold?1:0) * Bold + (italic?1:0) * Italic +
		    			(underline?1:0) * Underline + (strikeout?1:0) * Strikeout;
		    		String font = "";
		    		if ( (usemask & formatStruct.USE_FONT) != 0 )
		    			font = curfont;
		    		res = results.createFormatting (usemask, curattrib, fgcolor, bgcolor, font, cursize);
		    		break;
		    	}
		    case 15: {
		      res = results.createSetWindow (curWindow);
		      break;
		    }
		  };
		  return res;
		}

		private void addClosingTag(String name, MXPResult res, List<MXPResult> res2)
		{
			closingTag ctag = new closingTag();
			ctag.name = name;
			ctag.closingresult = res;
			ctag.closingresults = res2;
			closingTags.add(ctag);
		}

		public void gotVariable(String name, String string, boolean b) 
		{
			  commonTagHandler();
			  //send the variable value
			  results.addToList(results.createVariable (name, string, b));
			  commonAfterTagHandler();		
		}

		/** 
		 * called upon the VAR tag 
		 */
		public void gotVAR(String name) 
		{
			  commonTagHandler();
			  if (inVar)
			  {
			    results.addToList (results.createError ("Nested VAR tags are not allowed!"));
			    commonAfterTagHandler();
			    return;
			  }

			  //we are now in a variable
			  inVar = true;
			  varName = name;
			  varValue = "";

			  //create a closing result; the variable name shall be updated when the tag will be closed
			  addClosingTag("var", null, null);

			  commonAfterTagHandler();		
		}

		public void gotBOLD() 
		{
			  commonTagHandler();
			  MXPResult res = results.createFormatting(formatStruct.USE_BOLD, Bold, 
					  allcolors.noColor(), allcolors.noColor(), "", 0);
			  MXPResult res2 = createClosingResult (res);
			  applyResult (res);
			  results.addToList (res);
			  addClosingTag ("b", res2, null);

			  commonAfterTagHandler();
		}

		public void gotITALIC() 
		{
			  commonTagHandler();

			  MXPResult res = results.createFormatting (formatStruct.USE_ITALICS, Italic, 
					  allcolors.noColor(),  allcolors.noColor(), "", 0);
			  MXPResult res2 = createClosingResult (res);
			  applyResult (res);
			  results.addToList (res);
			  addClosingTag ("i", res2, null);

			  commonAfterTagHandler();
		}

		public void gotUNDERLINE() 
		{
			  commonTagHandler();

			  MXPResult res = results.createFormatting (formatStruct.USE_UNDERLINE, Underline, 
					  allcolors.noColor(), allcolors.noColor(), "", 0);
			  MXPResult res2 = createClosingResult (res);
			  applyResult (res);
			  results.addToList (res);
			  addClosingTag ("u", res2, null);

			  commonAfterTagHandler();
		}

		public void gotSTRIKEOUT() 
		{
			  commonTagHandler();

			  MXPResult res = results.createFormatting (formatStruct.USE_STRIKEOUT, Strikeout, 
					  allcolors.noColor(),
					  allcolors.noColor(), "", 0);
			  MXPResult res2 = createClosingResult (res);
			  applyResult (res);
			  results.addToList (res);
			  addClosingTag ("s", res2, null);

			  commonAfterTagHandler();
		}

		public Color bgColor() 
		{
			  return bgcolor;
		}

		public Color fgColor() 
		{
			return fgcolor;	
		}

		public void gotCOLOR(Color fg, Color bg) 
		{
			  commonTagHandler();

			  MXPResult res = results.createFormatting (formatStruct.USE_FG | formatStruct.USE_BG,
					  0, fg, bg, "", 0);
			  MXPResult res2 = createClosingResult (res);
			  applyResult (res);
			  results.addToList (res);
			  addClosingTag ("c", res2, null);

			  commonAfterTagHandler();
		}

		public void gotHIGH() 
		{
			  commonTagHandler();

			  Color color = fgcolor;
			  //High color is computed by adding 128 to each attribute...
			  //This is a very primitive way of doing it, and it's probably insufficient. We'll see.
			  int r,g,b;
			  r = (color.getRed() < 128) ? (color.getRed() + 128) : 255;
			  g = (color.getGreen() < 128) ? (color.getGreen() + 128) : 255;
			  b = (color.getBlue() < 128) ? (color.getBlue() + 128) : 255;
			  color = new Color(r,g,b); 

			  MXPResult res = results.createFormatting (formatStruct.USE_FG, 0, 
					  color, allcolors.noColor(), "", 0);
			  MXPResult res2 = createClosingResult (res);
			  applyResult (res);
			  results.addToList (res);
			  addClosingTag ("h", res2, null);

			  commonAfterTagHandler();
		}

		public String fontFace() 
		{
			return curfont;
		}

		public int fontSize() 
		{
			return cursize;
		}

		public void gotFONT(String face, int size, Color fg, Color bg) 
		{
			  commonTagHandler();

			  MXPResult res = results.createFormatting (formatStruct.USE_FG | formatStruct.USE_BG |
				  formatStruct.USE_FONT | formatStruct.USE_SIZE, 0, fg, bg,
			      face, size);
			  MXPResult res2 = createClosingResult (res);
			  applyResult (res);
			  results.addToList (res);
			  addClosingTag ("font", res2, null);

			  commonAfterTagHandler();
		}

		public void gotP()
		{
			  commonTagHandler();
			  //we're now in a paragraph
			  inParagraph = true;
			  addClosingTag ("p", null, null);		  
			  //no reporting to the client
			  commonAfterTagHandler();		
		}

		public void gotBR() {
			  commonTagHandler();
			  //inform the client that we got a newline (but no mode changes shall occur)
			  results.addToList (results.createText ("\r\n"));
			  commonAfterTagHandler();
		}

		public void gotNOBR() {
			  commonTagHandler();
			  //next new-line is to be ignored
			  ignoreNextNewLine = true;
			  //no reporting to client
			  commonAfterTagHandler();
		}

		public void gotSBR() {
			  commonTagHandler();
			  //soft-break is represented as 0x1F
			  results.addToList (results.createText ("\u001f"));
			  commonAfterTagHandler();
		}

		public void gotA(String href, String hint, String expire) {
			  commonTagHandler();
			  inLink = true;
			  isALink = true;
			  linkText = "";
			  MXPResult res = results.createLink (expire, href, "", hint);
			  addClosingTag ("a", res, null);
			  commonAfterTagHandler();
		}

		public void gotSEND(String command, String hint, boolean prompt, String expire) 
		{
			  commonTagHandler();

			  inLink = true;
			  isALink = false;
			  linkText = "";
			  gotmap = false;
			  String cmd = stripANSI (command);
			  lastcmd = cmd;
			  MXPResult res = results.createSendLink (expire, cmd, "", hint, prompt,
			      (command.indexOf("|") < 0) ? false : true);

			  addClosingTag ("send", res, null);

			  commonAfterTagHandler();
		}

		public void gotEXPIRE(String name) 
		{
			  commonTagHandler();
			  results.addToList (results.createExpire(name));
			  commonAfterTagHandler();
		}

		public void gotVERSION()
		{
			  commonTagHandler();

			  //this is to be sent...
			  results.addToList (results.createSendThis ("\u001b[1z<VERSION MXP=" + mxpVersion + " CLIENT=" +
			      clientName + " VERSION=" + clientVersion + ">\r\n"));

			  commonAfterTagHandler();
		}

		public void gotHtag(int which) 
		{
			  if ((which < 1) || (which > 6)) //BUG!!!
			  {
			    commonAfterTagHandler();
			    return;
			  }

			  commonTagHandler();

			  int idx = which - 1;
			  MXPResult res = results.createFormatting (formatStruct.USE_ALL, Hattribs[idx], Hfg[idx], Hbg[idx],
			      Hfont[idx], Hsize[idx]);
			  MXPResult res2 = createClosingResult (res);
			  applyResult (res);
			  results.addToList (res);
			  String ct = "h" + (idx+1);
			  addClosingTag (ct, res2, null);

			  commonAfterTagHandler();
		}

		public void gotHR() 
		{
			  commonTagHandler();

			  results.addToList (results.createHorizLine ());

			  commonAfterTagHandler();
		}

		public void gotSMALL() 
		{
			  commonTagHandler();

			  //SMALL means 3/4 of standard size :)
			  MXPResult res = results.createFormatting (formatStruct.USE_SIZE, 0, allcolors.noColor(),
					  allcolors.noColor(), "", defaultsize * 3/4);
			  MXPResult res2 = createClosingResult (res);
			  applyResult (res);
			  results.addToList (res);
			  addClosingTag ("small", res2, null);

			  commonAfterTagHandler();
		}

		public void gotTT() 
		{
			  commonTagHandler();

			  MXPResult res = results.createFormatting (formatStruct.USE_FONT, 0, 
					  allcolors.noColor(), allcolors.noColor(), ttFont, 0);
			  MXPResult res2 = createClosingResult (res);
			  applyResult (res);
			  results.addToList (res);
			  addClosingTag ("tt", res2, null);

			  commonAfterTagHandler();
		}

		public void gotSOUND(String fname, int vol, int count, int priority, String type, String url) 
		{
			  commonTagHandler();
			  results.addToList (results.createSound (true, fname, vol, count, priority, false, type, url));
			  commonAfterTagHandler();		
		}

		public void gotMUSIC(String fname, int vol, int count, boolean contifrereq, String type,
				String url) 
		{
			  commonTagHandler();
			  results.addToList (results.createSound (false, fname, vol, count, 0, contifrereq, type, url));
			  commonAfterTagHandler();		
		}

		public void gotGAUGE(String entity, String maxentity, String caption, Color color) 
		{
			  commonTagHandler();
			  results.addToList (results.createGauge (entity, maxentity, caption, color));
			  commonAfterTagHandler();		
		}

		public void gotDEST(String name, int x, int y, boolean feol, boolean feof) 
		{
			  commonTagHandler();
			  String nm = name.toLowerCase();
			  boolean nmExists = (frames.containsKey(nm));
			  
			  if (!nmExists)
			  {
			    results.addToList(results.createError(
			    		"Received a request to redirect to non-existing window " + nm));
			    return;
			  }
			  
			  MXPResult res = results.createSetWindow(name);
			  MXPResult res2 = createClosingResult (res);
			  applyResult (res);
			  results.addToList (res);

			  int _x = x;
			  int _y = y;
			  if ((y >= 0) && (x < 0)) _x = 0;
			  if ((_x >= 0) && (_y >= 0))
			    results.addToList (results.createMoveCursor (_x, _y));

			  List<MXPResult> ls = null;
			  //erase AFTER displaying text
			  if (feol || feof)
			  {
			    ls = new ArrayList<MXPResult>();
			    ls.add(res2);
			    res2 = results.createEraseText(feof);
			  }

			  //closing tag...
			  addClosingTag ("dest", res2, ls);

			  commonAfterTagHandler();
		  }

		public void gotSTAT(String entity, String max, String caption) 
		{
			  commonTagHandler();
			  results.addToList (results.createStat(entity, max, caption));
			  commonAfterTagHandler();
		  }

		public int computeCoord(String coord, boolean isX, boolean inWindow ) 
		{
			  int retval = Integer.parseInt(coord);
			  int len = coord.length();
			  char ch = coord.charAt(len - 1);
			  if (ch == 'c') retval *= (isX ? fX : fY);
			  if (ch == '%') retval = retval * (inWindow ? (isX ? wX : wY) : (isX ? sX : sY)) / 100;
			  return retval;
		}

		public void gotFRAME(String name, String action, String title,
				boolean internal, String align, int left, int top, int width,
				int height, boolean scrolling, boolean floating) {
			  commonTagHandler();

			  if (name.isEmpty())
			  {
			    results.addToList (results.createError ("Got FRAME tag without frame name!"));
			    commonAfterTagHandler();
			    return;
			  }

			  String nm = name.toLowerCase();
			  String act = action.toLowerCase();
			  String alg = align.toLowerCase();

			  String tt = title;
			  //name is the default title
			  if (tt.isEmpty())
			    tt = name;
			   
			  //align
			  alignType at = alignType.Top;
			  if (!align.isEmpty())
			  {
			    boolean alignok = false;
			    if (align.equals("left")) { at = alignType.Left; alignok = true; }
			    if (align.equals("right")) { at = alignType.Right; alignok = true; }
			    if (align.equals("top")) { at = alignType.Top; alignok = true; }
			    if (align.equals("bottom")) { at = alignType.Bottom; alignok = true; }
			    if (!alignok)
			      results.addToList (results.createError ("Received FRAME tag with unknown ALIGN option!"));
			  }

			  //does the list of frames contain frame with name nm?
			  boolean nmExists = frames.containsKey(nm);
			  
			  if (act.equals("open"))
			  {
			    if (nmExists)
			    {
			      results.addToList (results.createError ("Received request to create an existing frame!"));
			      commonAfterTagHandler();
			      return;
			    }
			    //cannot create _top or _previous
			    if ((nm.equals("_top")) || (nm.equals("_previous")))
			    {
			      results.addToList (results.createError ("Received request to create a frame with name " +
			          nm + ", which is invalid!"));
			      commonAfterTagHandler();
			      return;
			    }
			    if (internal)
			    {
			      //false for internal windows... value not used as of now, but it may be used later...
			      frames.put(nm, false);
			      results.addToList (results.createInternalWindow (nm, tt, at, scrolling));
			    }
			    else
			    {
			      //true for normal windows... value not used as of now, but it may be used later...
			      frames.put(nm, true);
			      results.addToList (results.createWindow (nm, tt, left, top, width, height,
			          scrolling, floating));
			    }
			  }
			  if (act.equals("close"))
			  {
			    if (nmExists)
			    {
			      frames.remove(nm);
			      results.addToList (results.createCloseWindow (nm));
			    }
			    else
			      results.addToList (results.createError
			          ("Received request to close a non-existing frame!"));
			  }
			  if (act.equals("redirect"))
			  {
			    //if the frame exists, or if the name is either _top or _previous, we redirect to that window
			    if ((nm.equals("_top")) || (nm.equals("_previous")) || nmExists)
			      redirectTo (nm);

			    else
			    {
			      //create that window
			      if (internal)
			      {
			        //false for internal windows... value not used as of now, but it may be used later...
			        frames.put(nm,false);
			        results.addToList (results.createInternalWindow (nm, tt, at, scrolling));
			      }
			      else
			      {
			        //true for normal windows... value not used as of now, but it may be used later...
			    	  frames.put(nm,true);
			        results.addToList (results.createWindow (nm, tt, left, top, width, height,
			            scrolling, floating));
			      }
			      //then redirect to it
			      redirectTo (nm);
			    }
			  }

			  commonAfterTagHandler();
		}

		private void redirectTo(String nm)
		{
			  nm = nm.toLowerCase();		  
			  String emptystring = "";
			  MXPResult res = null;
			  if (nm.equals("_top"))
			    res = results.createSetWindow(emptystring);
			  else
			  if (nm.equals("_previous"))
			    res = results.createSetWindow (prevWindow);
			  else
			    if (frames.containsKey(nm))
			      res = results.createSetWindow (nm);
			    else
			      res = results.createError ("Received request to redirect to non-existing window " + nm);
			  //apply result - will update info about previous window and so...
			  applyResult (res);
			  results.addToList (res);
		}

		public void gotRELOCATE(String hostname, int port) 
		{
			  commonTagHandler();
			  results.addToList (results.createRelocate (hostname, port));
			  commonAfterTagHandler();		
		}

		public void gotUSER() 
		{
			  commonTagHandler();
			  results.addToList (results.createSendLogin (true));
			  commonAfterTagHandler();		
		}

		public void gotPASSWORD() 
		{
			  commonTagHandler();
			  results.addToList (results.createSendLogin (false));
			  commonAfterTagHandler();		
		}

		public void gotIMAGE(String fname, String url, String type, int height, int width,
				int hspace, int vspace, String align, boolean ismap) 
		{
			  commonTagHandler();

			  //align
			  String alg = align.toLowerCase();
			  alignType at = alignType.Top;
			  if (!align.isEmpty())
			  {
			    boolean alignok = false;
			    if (align.equals("left")) { at = alignType.Left; alignok = true; }
			    if (align.equals("right")) { at = alignType.Right; alignok = true; }
			    if (align.equals("top")) { at = alignType.Top; alignok = true; }
			    if (align.equals("bottom")) { at = alignType.Bottom; alignok = true; }
			    if (align.equals("middle")) { at = alignType.Middle; alignok = true; }
			    if (!alignok)
			      results.addToList (results.createError ("Received IMAGE tag with unknown ALIGN option!"));
			  }

			  if (gotmap)
			    results.addToList (results.createError ("Received multiple image maps in one SEND tag!"));

			  if (ismap)
			  {
			    if (inLink && (!isALink))
			    {
			      results.addToList (results.createImageMap(lastcmd));
			      lastcmd = "";
			      gotmap = true;
			    }
			    else
			      results.addToList (results.createError ("Received an image map with no SEND tag!"));
			  }
			  results.addToList (results.createImage (fname, url, type, height, width, hspace, vspace, at));

			  commonAfterTagHandler();
		}

		public void gotNewLine() 
		{
			  //got a newline char - close outstanding entities, if any (unless we're in LOCKED mode)
			  if (mode != mxpMode.lockedMode)
			  {
			    String t = entities.expandEntities ("", true);
			    if (!t.isEmpty())
			      gotText (t, false);
			  }

			  //was temp-secure mode?
			  if (tempMode)
			  {
			    tempMode = false;
			    mode = defaultmode;
			    results.addToList (results.createError ("Temp-secure line tag followed by a newline!"));
			  }

			  //leaving secure mode?
			  wasSecureMode = false;
			  if ((mode == mxpMode.secureMode) && (defaultmode != mxpMode.secureMode))
			    wasSecureMode = true;

			  //ending line in OPEN mode - close all tags!
			  if (mode == mxpMode.openMode)
			    closeAllTags ();

			  //is we're in SECURE mode, some tags may need to be closed...

			  //line ended inside a link
			  if (inLink)
			  {
			    inLink = false;
			    isALink = false;
			    linkText = "";
			    results.addToList (results.createError ("Received an unterminated link!"));
			  }

			  if (inVar)
			  {
			    inVar = false;
			    results.addToList (results.createError ("Received an unterminated VAR tag!"));
			    varValue = "";
			  }

			  //should next newline be ignored?
			  if (ignoreNextNewLine)
			  {
			    ignoreNextNewLine = false;
			    return;
			  }

			  //if we're in a paragraph, don't report the new-line either
			  if (inParagraph)
			    return;

			  //set mode back to default mode
			  mode = defaultmode;
			  
			  //neither NOBR nor P - report newline
			  results.addToList (results.createText ("\r\n"));
		}

		public void gotLineTag(int number) 
		{
			  //got a line tag - close outstanding entities, if any (unless we're in LOCKED mode)
			  if (mode != mxpMode.lockedMode)
			  {
			    String t = entities.expandEntities ("", true);
			    if (!t.isEmpty())
			      gotText (t, false);
			  }

			  //leaving secure mode
			  if (wasSecureMode && (number != 1))
			    closeAllTags ();
			  wasSecureMode = false;

			  if (number < 0) return;
			  if (number > 99) return;
			  if (number >= 10)
			    results.addToList (results.createLineTag(number));
			  else
			  {
			    switch (number) {
			      case 0:
			        setMXPMode(mxpMode.openMode);
			        break;
			      case 1:
			        setMXPMode (mxpMode.secureMode);
			        break;
			      case 2:
			        setMXPMode (mxpMode.lockedMode);
			        break;
			      case 3:
			        closeAllTags ();
			        //default mode remains the same...
			        setMXPMode (mxpMode.openMode);
			        reset ();
			        break;
			      case 4:
			        setMXPMode (mxpMode.secureMode);
			        tempMode = true;
			        break;
			      case 5:
			        setMXPMode (mxpMode.openMode);
			        defaultmode = mxpMode.openMode;
			        break;
			      case 6:
			        setMXPMode (mxpMode.secureMode);
			        defaultmode = mxpMode.secureMode;
			        break;
			      case 7:
			        setMXPMode (mxpMode.lockedMode);
			        defaultmode = mxpMode.lockedMode;
			        break;
			      default:
			        results.addToList (results.createWarning ("Received unrecognized line tag."));
			        break;
			    };
			  }
			
		}

		private void setMXPMode(mxpMode m)
		{
			  mode = m;
			  tempMode = false;
			  wasSecureMode = false;
			  
			  //if we start in LOCKED mode and mode change occurs, we set default mode
			  //to OPEN, so that we are compatible with the spec...
			  if (initiallyLocked)
			  {
			    initiallyLocked = false;
			    defaultmode = mxpMode.openMode;
			  }
			
		}

		public void setDefaultText(String font, int size, boolean _bold,
				boolean _italic, boolean _underline, boolean _strikeout, Color fg,
				Color bg) 
		{
			  if (curfont == defaultfont) curfont = font;
			  defaultfont = font;

			  if (cursize == defaultsize) cursize = size;
			  defaultsize = size;

			  int curattrib = (bold?1:0) * Bold + (italic?1:0) * Italic +
			      (underline?1:0) * Underline + (strikeout?1:0) * Strikeout;
			  int newattribs = (_bold?1:0) * Bold + (_italic?1:0) * Italic +
			      (_underline?1:0) * Underline + (_strikeout?1:0) * Strikeout;
			  if (curattrib == defaultattribs)
			  {
			    bold = _bold;
			    italic = _italic;
			    underline = _underline;
			    strikeout = _strikeout;
			  }
			  defaultattribs = newattribs;

			  if (fgcolor == defaultfg) fgcolor = fg;
			  defaultfg = fg;
			  if (bgcolor == defaultbg) bgcolor = bg;
			  defaultbg = bg;
		}

		public void setHeaderParams(int which, String font, int size,
				boolean _bold, boolean _italic, boolean _underline,
				boolean _strikeout, Color fg, Color bg) 
		{
			  //invalid H-num?
			  if ((which < 1) || (which > 6))
			    return;

			  Hfont[which - 1] = font;

			  Hsize[which - 1] = size;

			  int newattribs = (_bold?1:0) * Bold + (_italic?1:0) * Italic +
			      (_underline?1:0) * Underline + (_strikeout?1:0) * Strikeout;
			  Hattribs[which - 1] = newattribs;

			  Hfg[which - 1] = fg;
			  Hbg[which - 1] = bg;
			
		}

		public void setNonProportFont(String font)
		{
			  ttFont = font;		
		}

		public void setClient(String name, String version)
		{
			clientName = name;
			clientVersion = version;
		}
		public void supportsLink (boolean supports)
		{
		  suplink = supports;
		}

		public void supportsGauge (boolean supports)
		{
		  supgauge = supports;
		}

		public void supportsStatus (boolean supports)
		{
		  supstatus = supports;
		}

		public void supportsSound (boolean supports)
		{
		  supsound = supports;
		}

		public void supportsFrame (boolean supports)
		{
		  supframe = supports;
		}

		public void supportsImage (boolean supports)
		{
		  supimage = supports;
		}

		public void supportsRelocate (boolean supports)
		{
		  suprelocate = supports;
		}

		public void switchToOpen ()
		{
		  mode = mxpMode.openMode;
		  defaultmode = mxpMode.openMode;
		  initiallyLocked = false;
		  //not we conform to MXP spec... use with care - only affects non-MXP MUDs, where it allows
		  //open tags - MUDs supporting MXP are NOT affected
		}
		
		public void setScreenProps (int sx, int sy, int wx, int wy, int fx, int fy)
		{
		  sX = sx;
		  sY = sy;
		  wX = wx;
		  wY = wy;
		  fX = fx;
		  fY = fy;
		}
	}	
	//----------------------------------------------------------------------------------------------------
	//----------------------------------------------------------------------------------------------------
	//----------------------------------------------------------------------------------------------------
	//----------------------------------------------------------------------------------------------------
	//----------------------------------------------------------------------------------------------------
	

	//----------------------------------------------------------------------------------------------------
	// Plugin class
	private MXPProcessor proc;
	
	public JMXPSingleClass()
	{
		allcolors = new MXPColors();
		proc = new MXPProcessor("MyPlugin", "1.0");
	}

	public ArrayList<Byte> parseByteArray(ArrayList<Byte> bytes) 
	{
		try{			
			byte[] array = new byte[bytes.size()];
			for(int i=0; i < bytes.size(); i++){
				array[i] = bytes.get(i).byteValue();
			}
			String input = new String(array,"ISO-8859-1"); // may be UTF-8?
			proc.processText(input);
			// no output
			return null;						
		}
		catch(Exception e)
		{
			e.printStackTrace();
			return bytes;
		}
	}
	}