/
com/planet_ink/coffee_mud/Abilities/Common/
com/planet_ink/coffee_mud/Abilities/Diseases/
com/planet_ink/coffee_mud/Abilities/Druid/
com/planet_ink/coffee_mud/Abilities/Fighter/
com/planet_ink/coffee_mud/Abilities/Languages/
com/planet_ink/coffee_mud/Abilities/Misc/
com/planet_ink/coffee_mud/Abilities/Prayers/
com/planet_ink/coffee_mud/Abilities/Properties/
com/planet_ink/coffee_mud/Abilities/Skills/
com/planet_ink/coffee_mud/Abilities/Songs/
com/planet_ink/coffee_mud/Abilities/Spells/
com/planet_ink/coffee_mud/Abilities/Thief/
com/planet_ink/coffee_mud/Abilities/Traps/
com/planet_ink/coffee_mud/Behaviors/
com/planet_ink/coffee_mud/CharClasses/interfaces/
com/planet_ink/coffee_mud/Commands/
com/planet_ink/coffee_mud/Commands/interfaces/
com/planet_ink/coffee_mud/Common/
com/planet_ink/coffee_mud/Common/interfaces/
com/planet_ink/coffee_mud/Exits/interfaces/
com/planet_ink/coffee_mud/Items/Armor/
com/planet_ink/coffee_mud/Items/Basic/
com/planet_ink/coffee_mud/Items/CompTech/
com/planet_ink/coffee_mud/Items/MiscMagic/
com/planet_ink/coffee_mud/Items/Weapons/
com/planet_ink/coffee_mud/Items/interfaces/
com/planet_ink/coffee_mud/Libraries/
com/planet_ink/coffee_mud/Libraries/interfaces/
com/planet_ink/coffee_mud/Locales/
com/planet_ink/coffee_mud/MOBS/
com/planet_ink/coffee_mud/Races/
com/planet_ink/coffee_mud/Races/interfaces/
com/planet_ink/coffee_mud/WebMacros/
com/planet_ink/coffee_mud/WebMacros/interfaces/
com/planet_ink/coffee_mud/core/
com/planet_ink/coffee_mud/core/collections/
com/planet_ink/coffee_mud/core/interfaces/
com/planet_ink/coffee_mud/core/intermud/
com/planet_ink/coffee_mud/core/intermud/i3/
com/planet_ink/coffee_web/server/
com/planet_ink/siplet/applet/
lib/
resources/factions/
resources/fakedb/
resources/progs/autoplayer/
resources/quests/holidays/
web/
web/admin.templates/
web/admin/grinder/
web/admin/images/
web/clan.templates/
web/pub.templates/
web/pub/images/mxp/
web/pub/sounds/
web/pub/textedit/
package com.planet_ink.coffee_mud.Libraries;
import com.planet_ink.coffee_mud.core.interfaces.*;
import com.planet_ink.coffee_mud.core.*;
import com.planet_ink.coffee_mud.core.collections.*;
import com.planet_ink.coffee_mud.Abilities.interfaces.*;
import com.planet_ink.coffee_mud.Areas.interfaces.*;
import com.planet_ink.coffee_mud.Behaviors.interfaces.*;
import com.planet_ink.coffee_mud.CharClasses.interfaces.*;
import com.planet_ink.coffee_mud.Commands.interfaces.*;
import com.planet_ink.coffee_mud.Common.interfaces.*;
import com.planet_ink.coffee_mud.Exits.interfaces.*;
import com.planet_ink.coffee_mud.Items.interfaces.*;
import com.planet_ink.coffee_mud.Locales.interfaces.*;
import com.planet_ink.coffee_mud.MOBS.interfaces.*;
import com.planet_ink.coffee_mud.Races.interfaces.*;

import java.util.*;

import com.planet_ink.coffee_mud.Libraries.interfaces.*;
import com.planet_ink.coffee_mud.Libraries.interfaces.XMLLibrary.XMLTag;
import com.planet_ink.coffee_mud.Libraries.interfaces.XMLLibrary.XMLTag;

/*
   Copyright 2001-2016 Bo Zimmerman

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

	   http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
public class XMLManager extends StdLibrary implements XMLLibrary
{
	@Override public String ID(){return "XMLManager";}

	protected final static String HEX_DIGITS="0123456789ABCDEF";
	
	protected final String[][] IGNORE_TAG_BOUNDS={{"!--","-->"},{"?","?>"},{"![CDATA[","]]>"}};
	protected enum State {	START, BEFORETAG, INTAG, BEFOREATTRIB, BEGINTAGSELFEND, BEFORECLOSETAG, INCLOSETAG,
							AFTERCLOSETAG, INATTRIB, INPOSTATTRIB, BEFOREATTRIBVALUE, INATTRIBVALUE,
							INQUOTEDATTRIBVALUE }
	protected int 			bufDex;
	protected XMLpiece		piece;
	protected State			state;
	protected int[]			beginDex;
	protected int[]			endDex;
	protected StringBuffer	buf;
	protected List<XMLTag>contents;
	protected Set<String>	illegalTags;

	public XMLManager()
	{
		super();
	}
	
	private XMLManager(final StringBuffer buf, final int startDex)
	{
		bufDex=startDex;
		piece=null;
		state=State.START;
		this.buf=buf;
		beginDex = new int[State.values().length];
		endDex = new int[State.values().length];
		contents=new XVector<XMLTag>();
		for(int i=0;i<State.values().length;i++)
		{
			beginDex[i]=-1;
			endDex[i]=-1;
		}

		try
		{
			illegalTags=CMLib.coffeeFilter().getTagTable().keySet();
		}
		catch(final Exception e)
		{
			illegalTags=new HashSet<String>();
		}
	}

	/**
	 *
	 * @author Bo Zimmerman
	 *
	 */
	private static class XMLpiece implements Cloneable, XMLTag
	{
		protected String tag="";
		protected String value="";
		protected List<XMLTag> contents=new XVector<XMLTag>();
		protected Map<String,String> parms=new XHashtable<String,String>();
		protected XMLpiece parent=null;
		protected int outerStart=-1;
		protected int innerStart=-1;
		protected int innerEnd=-1;
		protected int outerEnd=-1;

		public XMLpiece()
		{
			
		}
		
		public XMLpiece(String tag, String value)
		{
			this.tag=tag.toUpperCase().trim();
			this.value=value;
		}
		
		/* (non-Javadoc)
		 * @see com.planet_ink.coffee_mud.Libraries.interfaces.XMLTag#copyOf()
		 */
		/* (non-Javadoc)
		 * @see com.planet_ink.coffee_mud.Libraries.interfaces.XMLTag#copyOf()
		 */
		@Override
		public XMLTag copyOf()
		{
			try
			{
				final XMLpiece piece2=(XMLpiece)this.clone();
				piece2.contents =new XVector<XMLTag>(contents());
				piece2.parms = new XHashtable<String,String>(parms());
				return piece2;
			}
			catch(final Exception e)
			{
				return this;
			}
		}

		/* (non-Javadoc)
		 * @see com.planet_ink.coffee_mud.Libraries.interfaces.XMLTag#addContent(com.planet_ink.coffee_mud.Libraries.interfaces.XMLLibrary.XMLTag)
		 */
		/* (non-Javadoc)
		 * @see com.planet_ink.coffee_mud.Libraries.interfaces.XMLTag#addContent(com.planet_ink.coffee_mud.Libraries.interfaces.XMLLibrary.XMLTag)
		 */
		@Override
		public void addContent(XMLTag x)
		{
			if (x == null) return;
			if (contents() == null) 
				contents = new XVector<XMLTag>();
			if(x instanceof XMLpiece)
				((XMLpiece)x).parent = this;
			contents().add(x);
		}

		/* (non-Javadoc)
		 * @see com.planet_ink.coffee_mud.Libraries.interfaces.XMLTag#toString()
		 */
		@Override
		public String toString()
		{
			final StringBuilder str=new StringBuilder("");
			str.append("<").append(tag());
			for(final String parm : parms().keySet())
				str.append(" ").append(parm).append("=\"").append(parms().get(parm)).append("\"");
			str.append(">").append(value()).append("</").append(tag()).append(">");
			return str.toString();
		}

		/* (non-Javadoc)
		 * @see com.planet_ink.coffee_mud.Libraries.interfaces.XMLTag#tag()
		 */
		@Override
		public String tag()
		{
			return tag;
		}
		/* (non-Javadoc)
		 * @see com.planet_ink.coffee_mud.Libraries.interfaces.XMLTag#value()
		 */
		@Override
		public String value()
		{
			return value;
		}

		/* (non-Javadoc)
		 * @see com.planet_ink.coffee_mud.Libraries.interfaces.XMLTag#contents()
		 */
		@Override
		public List<XMLTag> contents()
		{
			return contents;
		}

		/* (non-Javadoc)
		 * @see com.planet_ink.coffee_mud.Libraries.interfaces.XMLTag#parms()
		 */
		@Override
		public Map<String,String> parms()
		{
			return parms;
		}

		/* (non-Javadoc)
		 * @see com.planet_ink.coffee_mud.Libraries.interfaces.XMLTag#parent()
		 */
		@Override
		public XMLpiece parent()
		{
			return parent;
		}

		/* (non-Javadoc)
		 * @see com.planet_ink.coffee_mud.Libraries.interfaces.XMLTag#outerStartIndex()
		 */
		@Override
		public int outerStartIndex()
		{
			return outerStart;
		}

		/* (non-Javadoc)
		 * @see com.planet_ink.coffee_mud.Libraries.interfaces.XMLTag#innerStartIndex()
		 */
		@Override
		public int innerStartIndex()
		{
			return innerStart;
		}

		/* (non-Javadoc)
		 * @see com.planet_ink.coffee_mud.Libraries.interfaces.XMLTag#innerEndIndex()
		 */
		@Override
		public int innerEndIndex()
		{
			return innerEnd;
		}

		/* (non-Javadoc)
		 * @see com.planet_ink.coffee_mud.Libraries.interfaces.XMLTag#outerEndIndex()
		 */
		@Override
		public int outerEndIndex()
		{
			return outerEnd;
		}
		
		@Override
		public String getParmValue(String Tag)
		{
			if((parms != null)&&(Tag != null))
				return parms.get(Tag.toUpperCase().trim());
			return null;
		}
		
		@Override
		public XMLTag getPieceFromPieces(String tag)
		{
			if(contents==null)
				return null;
			for(int v=0;v<contents.size();v++)
			{
				if(contents.get(v).tag().equalsIgnoreCase(tag))
					return contents.get(v);
			}
			return null;
		}
		
		@Override
		public double getDoubleFromPieces(String tag, double defVal)
		{
			final XMLTag x=getPieceFromPieces(tag);
			if((x!=null)&&(x.value()!=null))
				return s_double(x.value());
			return defVal;
		}

		@Override
		public boolean getBoolFromPieces(String tag, boolean defVal)
		{
			final String val=getValFromPieces(tag);
			if((val==null)||(val.length()==0))
				return false;
			if(val.toUpperCase().trim().startsWith("T"))
				return true;
			return false;
		}


		@Override
		public int getIntFromPieces(String tag, int defVal)
		{
			final XMLTag x=getPieceFromPieces(tag);
			if((x!=null)&&(x.value()!=null))
				return s_int(x.value());
			return defVal;
		}

		@Override
		public short getShortFromPieces(String tag, short defVal)
		{
			final XMLTag x=getPieceFromPieces(tag);
			if((x!=null)&&(x.value()!=null))
				return s_short(x.value());
			return defVal;
		}

		@Override
		public long getLongFromPieces(String tag, long defVal)
		{
			final XMLTag x=getPieceFromPieces(tag);
			if((x!=null)&&(x.value()!=null))
				return s_long(x.value());
			return defVal;
		}
		
		@Override
		public String getValFromPieces(String tag)
		{
			return getValFromPieces(tag, "");
		}

		@Override
		public String getValFromPieces(String tag, String defVal)
		{
			final XMLTag x=getPieceFromPieces(tag);
			if((x!=null)&&(x.value()!=null))
				return x.value();
			return defVal;
		}
		
		@Override
		public List<XMLTag> getContentsFromPieces(String tag)
		{
			final XMLTag x=getPieceFromPieces(tag);
			if(x!=null)
				return x.contents();
			return null;
		}

		@Override
		public boolean isTagInPieces(String tag)
		{
			if(contents!=null)
			{
				for(int v=0;v<contents.size();v++)
				{
					if(contents.get(v).tag().equalsIgnoreCase(tag))
						return true;
				}
			}
			return false;
		}

		@Override
		public List<XMLTag> getPiecesFromPieces(String tag)
		{
			if(contents==null)
				return null;
			final List<XMLTag> pieces = new ArrayList<XMLTag>();
			for(int v=0;v<contents.size();v++)
			{
				if(contents.get(v).tag().equalsIgnoreCase(tag))
					pieces.add(contents.get(v));
			}
			return pieces;
		}

		@Override
		public boolean getBoolFromPieces(String tag)
		{
			final String val=getValFromPieces(tag);
			if((val==null)||(val.length()==0))
				return false;
			if(val.toUpperCase().trim().startsWith("T"))
				return true;
			return false;
		}


		@Override
		public int getIntFromPieces(String tag)
		{
			return s_int(getValFromPieces(tag));
		}

		@Override
		public short getShortFromPieces(String tag)
		{
			return s_short(getValFromPieces(tag));
		}

		@Override
		public long getLongFromPieces(String tag)
		{
			return s_long(getValFromPieces(tag));
		}

		@Override
		public double getDoubleFromPieces(String tag)
		{
			return s_double(getValFromPieces(tag));
		}
	}

	@Override
	public XMLTag createNewTag(String key, String value)
	{
		return new XMLpiece(key, value);
	}
	
	@Override
	public String parseOutAngleBrackets(String s)
	{
		int x=s.indexOf('<');
		while(x>=0)
		{
			s=s.substring(0,x)+"&lt;"+s.substring(x+1);
			x=s.indexOf('<');
		}
		x=s.indexOf('>');
		while(x>=0)
		{
			s=s.substring(0,x)+"&gt;"+s.substring(x+1);
			x=s.indexOf('>');
		}
		return s;
	}

	@Override
	public String parseOutAngleBracketsAndQuotes(String s)
	{
		int x=s.indexOf('<');
		while(x>=0)
		{
			s=s.substring(0,x)+"&lt;"+s.substring(x+1);
			x=s.indexOf('<');
		}
		x=s.indexOf('>');
		while(x>=0)
		{
			s=s.substring(0,x)+"&gt;"+s.substring(x+1);
			x=s.indexOf('>');
		}
		x=s.indexOf('\"');
		while(x>=0)
		{
			s=s.substring(0,x)+"&quot;"+s.substring(x+1);
			x=s.indexOf('\"');
		}
		return s;
	}

	@Override
	public String restoreAngleBrackets(String s)
	{
		if(s==null)
			return null;
		final StringBuffer buf=new StringBuffer(s);
		int loop=0;
		while(loop<buf.length())
		{
			switch(buf.charAt(loop))
			{
			case '&':
				if(loop<buf.length()-3)
				{
					switch(buf.charAt(loop+1))
					{
					case 'l':
						if(buf.substring(loop+1,loop+4).equalsIgnoreCase("lt;"))
							buf.replace(loop,loop+4,"<");
						break;
					case 'g':
						if(buf.substring(loop+1,loop+4).equalsIgnoreCase("gt;"))
							buf.replace(loop,loop+4,">");
						break;
					case 'q':
						if(buf.substring(loop+1,loop+6).equalsIgnoreCase("quot;"))
							buf.replace(loop,loop+6,"\"");
						break;
					case 'a':
						if(buf.substring(loop+1,loop+6).equalsIgnoreCase("amp;"))
							buf.replace(loop,loop+5,"&");
						else
						if(buf.substring(loop+1,loop+6).equalsIgnoreCase("apos;"))
							buf.replace(loop,loop+6,"'");
						break;
					}
				}
				break;
			case '%':
				if(loop<buf.length()-2)
				{
					final int dig1=HEX_DIGITS.indexOf(buf.charAt(loop+1));
					final int dig2=HEX_DIGITS.indexOf(buf.charAt(loop+2));
					if((dig1>=0)&&(dig2>=0))
					{
						buf.setCharAt(loop,(char)((dig1*16)+dig2));
						buf.deleteCharAt(loop+1);
						buf.deleteCharAt(loop+1);
					}
				}
				break;
			}
			++loop;
		}
		return buf.toString();
	}

	/**
	 * Returns the double value of a string without crashing
 	 *
	 * Usage: dSize = WebIQBase.s_double(WebIQBase.getRes(AttStatsRes,"BlobSize"));
	 * @param DOUBLE String to convert
	 * @return double Double value of the string
	 */
	protected static double s_double(String DOUBLE)
	{
		double sdouble=0;
		try{ sdouble=Double.parseDouble(DOUBLE); }
		catch(final Exception e){ return 0;}
		return sdouble;
	}

	/**
	 * Returns the integer value of a string without crashing
 	 *
	 * Usage: int num=s_int(CMD.substring(14));
	 * @param INT Integer value of string
	 * @return int Integer value of the string
	 */
	protected static int s_int(String INT)
	{
		int sint=0;
		try{ sint=Integer.parseInt(INT); }
		catch(final java.lang.NumberFormatException e){ return 0;}
		return sint;
	}

	/**
	 * Returns the short value of a string without crashing
 	 *
	 * Usage: int num=s_short(CMD.substring(14));
	 * @param SHORT Short value of string
	 * @return short Short value of the string
	 */
	protected static short s_short(String SHORT)
	{
		short sint=0;
		try{ sint=Short.parseShort(SHORT); }
		catch(final java.lang.NumberFormatException e){ return 0;}
		return sint;
	}

	/**
	 * Returns the long value of a string without crashing
 	 *
	 * Usage: int num=s_long(CMD.substring(14));
	 * @param LONG Long value of string
	 * @return long Long value of the string
	 */
	protected static  long s_long(String LONG)
	{
		long slong=0;
		try{ slong=Long.parseLong(LONG); }
		catch(final java.lang.NumberFormatException e){ return 0;}
		return slong;
	}

	/**
	 * Return the outer wrapper and contents of an XML tag &lt;TNAME&gt;Data&lt;/TNAME&gt;
	 *
  	 * Usage: Data+=XMLoTag("MODELOBJECTONE",VA.ModelObjectOne);
	 * @param TName Tag name to use
	 * @param Data the data to embed
	 * @return String Information corresponding to the tname
	 */
	@Override
	public String convertXMLtoTag(String TName, String Data)
	{
		if((Data==null)||(Data.length()==0))
			return "<"+TName+" />";
		return "<"+TName+">"+Data+"</"+TName+">";
	}

	/**
	 * Return the outer wrapper and contents of an XML tag &lt;TNAME&gt;Data&lt;/TNAME&gt;
	 *
  	 * Usage: Data+=XMLoTag("MODELOBJECTONE",VA.ModelObjectOne);
	 * @param TName Tag name to use
	 * @param Data the data to embed
	 * @return String Information corresponding to the tname
	 */
	@Override
	public String convertXMLtoTag(String TName, int Data)
	{
		return "<"+TName+">"+Data+"</"+TName+">";
	}

	/**
	 * Return the outer wrapper and contents of an XML tag &lt;TNAME&gt;Data&lt;/TNAME&gt;
	 *
  	 * Usage: Data+=XMLoTag("MODELOBJECTONE",VA.ModelObjectOne);
	 * @param TName Tag name to use
	 * @param Data the data to embed
	 * @return String Information corresponding to the tname
	 */
	@Override
	public String convertXMLtoTag(String TName, short Data)
	{
		return "<"+TName+">"+Data+"</"+TName+">";
	}

	/**
	 * Return the outer wrapper and contents of an XML tag &lt;TNAME&gt;Data&lt;/TNAME&gt;
	 *
  	 * Usage: Data+=XMLoTag("MODELOBJECTONE",VA.ModelObjectOne);
	 * @param TName Tag name to use
	 * @param Data the data to embed
	 * @return String Information corresponding to the tname
	 */
	@Override
	public String convertXMLtoTag(String TName, boolean Data)
	{
		return "<"+TName+">"+Data+"</"+TName+">";
	}

	/**
	 * Return the outer wrapper and contents of an XML tag &lt;TNAME&gt;Data&lt;/TNAME&gt;
	 *
  	 * Usage: Data+=XMLoTag("MODELOBJECTONE",VA.ModelObjectOne);
	 * @param TName Tag name to use
	 * @param Data the data to embed
	 * @return String Information corresponding to the tname
	 */
	@Override
	public String convertXMLtoTag(String TName, long Data)
	{
		return "<"+TName+">"+Data+"</"+TName+">";
	}

	/**
	 * Return the contents of an XML tag, given the tag to search for
	 *
  	 * Usage: String XML=returnXMLBlock(Response,"PDIModelErrors");
	 * @param Blob String to searh
	 * @param Tag Tag name to search for
	 * @return String Information corresponding to the tname
	 */
	@Override
	public String returnXMLBlock(String Blob, String Tag)
	{
		int foundb=Blob.indexOf("<"+Tag+">");
		if(foundb<0)
			foundb=Blob.indexOf("<"+Tag+" ");
		if(foundb<0)
			foundb=Blob.indexOf("<"+Tag+"/");
		if(foundb<0)
			return "";

		int founde=Blob.indexOf("/"+Tag+">",foundb)-1;
		if(founde<0)
			founde=Blob.indexOf("/"+Tag+" ",foundb)-1;
		if(founde<0)
		{
			founde=Blob.indexOf('>',foundb);
			if((founde>0)&&(Blob.charAt(founde-1)!='/'))
				founde=-1;
		}
		if(founde<0)
			return "";

		Blob=Blob.substring(foundb,founde).trim();
		return Blob;
	}

	@Override
	public String getValFromPieces(List<XMLTag> V, String tag)
	{
		return getValFromPieces(V, tag, "");
	}

	@Override
	public String getValFromPieces(List<XMLTag> V, String tag, String defVal)
	{
		final XMLTag x=getPieceFromPieces(V,tag);
		if((x!=null)&&(x.value()!=null))
			return x.value();
		return defVal;
	}

	@Override
	public List<XMLTag> getContentsFromPieces(List<XMLTag> V, String tag)
	{
		final XMLTag x=getPieceFromPieces(V,tag);
		if(x!=null)
			return x.contents();
		return null;
	}

	@Override
	public XMLTag getPieceFromPieces(List<XMLTag> V, String tag)
	{
		if(V==null)
			return null;
		for(int v=0;v<V.size();v++)
		{
			if(V.get(v).tag().equalsIgnoreCase(tag))
				return V.get(v);
		}
		return null;
	}

	@Override
	public boolean isTagInPieces(List<XMLTag> V, String tag)
	{
		if(V!=null)
		for(int v=0;v<V.size();v++)
		{
			if(V.get(v).tag().equalsIgnoreCase(tag))
				return true;
		}
		return false;
	}

	@Override
	public List<XMLTag> getPiecesFromPieces(List<XMLTag> V, String tag)
	{
		if(V==null)
			return null;
		final List<XMLTag> pieces = new ArrayList<XMLTag>();
		for(int v=0;v<V.size();v++)
			if(V.get(v).tag().equalsIgnoreCase(tag))
				pieces.add(V.get(v));
		return pieces;
	}

	@Override
	public boolean getBoolFromPieces(List<XMLTag> V, String tag)
	{
		final String val=getValFromPieces(V,tag);
		if((val==null)||(val.length()==0))
			return false;
		if(val.toUpperCase().trim().startsWith("T"))
			return true;
		return false;
	}


	@Override
	public int getIntFromPieces(List<XMLTag> V, String tag)
	{
		return s_int(getValFromPieces(V,tag));
	}

	@Override
	public short getShortFromPieces(List<XMLTag> V, String tag)
	{
		return s_short(getValFromPieces(V,tag));
	}

	@Override
	public long getLongFromPieces(List<XMLTag> V, String tag)
	{
		return s_long(getValFromPieces(V,tag));
	}

	@Override
	public double getDoubleFromPieces(List<XMLTag> V, String tag)
	{
		return s_double(getValFromPieces(V,tag));
	}

	@Override
	public boolean getBoolFromPieces(List<XMLTag> V, String tag, boolean defVal)
	{
		final String val=getValFromPieces(V,tag);
		if((val==null)||(val.length()==0))
			return false;
		if(val.toUpperCase().trim().startsWith("T"))
			return true;
		return false;
	}


	@Override
	public int getIntFromPieces(List<XMLTag> V, String tag, int defVal)
	{
		final XMLTag x=getPieceFromPieces(V,tag);
		if((x!=null)&&(x.value()!=null))
			return s_int(x.value());
		return defVal;
	}

	@Override
	public short getShortFromPieces(List<XMLTag> V, String tag, short defVal)
	{
		final XMLTag x=getPieceFromPieces(V,tag);
		if((x!=null)&&(x.value()!=null))
			return s_short(x.value());
		return defVal;
	}

	@Override
	public long getLongFromPieces(List<XMLTag> V, String tag, long defVal)
	{
		final XMLTag x=getPieceFromPieces(V,tag);
		if((x!=null)&&(x.value()!=null))
			return s_long(x.value());
		return defVal;
	}

	@Override
	public double getDoubleFromPieces(List<XMLTag> V, String tag, double defVal)
	{
		final XMLTag x=getPieceFromPieces(V,tag);
		if((x!=null)&&(x.value()!=null))
			return s_double(x.value());
		return defVal;
	}
	
	protected void changeTagState(State newState)
	{
		this.endDex[state.ordinal()]=bufDex;
		state=newState;
		bufDex++;
		this.beginDex[state.ordinal()]=bufDex;
	}

	protected void changedTagState(State newState)
	{
		this.endDex[state.ordinal()]=bufDex-1;
		state=newState;
		this.beginDex[state.ordinal()]=bufDex;
		bufDex++;
	}

	protected void abandonTagState(State newState)
	{
		if((piece!=null)&&(piece.outerEndIndex()<0))
		{
			if(piece.parent()!=null)
				piece.parent().contents().remove(piece);
			else
				contents.remove(piece);
			final XMLTag childPiece=piece;
			piece=piece.parent();
			Log.warnOut("XMLManager","Abandoned tag "+childPiece.tag()+((piece!=null)?" of parent "+piece.tag():""));
		}
		changeTagState(newState);
	}

	protected void handleTagBounds()
	{
		int x=0;
		for (final String[] bounds : IGNORE_TAG_BOUNDS)
		{
			final String boundStart=bounds[0];
			if(bufDex <= (buf.length()-boundStart.length()))
			{
				for(x=0;x<boundStart.length();x++)
					if(buf.charAt(bufDex+x)!=boundStart.charAt(x))
						break;
				if(x>=boundStart.length())
				{
					int comDex=bufDex+boundStart.length();
					final String boundEnd=bounds[1];
					while(comDex <= (buf.length()-boundEnd.length()))
					{
						if(buf.charAt(comDex)==boundEnd.charAt(0))
						{
							for(x=1;x<boundEnd.length();x++)
								if(buf.charAt(comDex+x)!=boundEnd.charAt(x))
									break;
							if(x>=boundEnd.length())
							{
								buf.delete(bufDex-1, comDex+boundEnd.length());
								bufDex-=2;
								changeTagState(State.START);
								return;
							}
						}
						comDex++;
					}
					buf.delete(bufDex-1,buf.length());
					bufDex-=2;
					changeTagState(State.START);
				}
			}
		}
	}


	protected void startState(final char c)
	{
		switch(c)
		{
		case ' ': case '\t': case '\r': case '\n': bufDex++; return;
		case '<':
		{
			changeTagState(State.BEFORETAG);
			handleTagBounds();
			return;
		}
		default: bufDex++; return;
		}
	}

	protected void beforeTag(final char c)
	{
		switch(c)
		{
		case ' ': case '\t': case '\r': case '\n': bufDex++; return;
		case '<': changeTagState(State.BEFORETAG); return;
		case '/': changedTagState(State.BEFORECLOSETAG); return;
		default:
			if(Character.isLetter(c))
				changedTagState(State.INTAG);
			else
				changeTagState(State.START);
			return;
		}
	}

	protected boolean canStartPiece(int endOfTagName)
	{
		final String tagName=buf.substring(beginDex[State.INTAG.ordinal()],endOfTagName).toUpperCase().trim();
		if(illegalTags.contains(tagName))
			return false;
		return true;
	}

	protected void startPiece(int endOfTagName)
	{
		final XMLpiece newPiece=new XMLpiece();
		newPiece.outerStart = beginDex[State.BEFORETAG.ordinal()];
		newPiece. tag = buf.substring(beginDex[State.INTAG.ordinal()],endOfTagName).toUpperCase().trim();
		if(piece!=null)
			piece.contents().add(newPiece);
		else
			contents.add(newPiece);
		newPiece.parent = piece;
		piece=newPiece;
	}

	protected void doneWithPiece(int outerEnd)
	{
		if(piece!=null)
		{
			piece.outerEnd = outerEnd;
			piece=piece.parent();
		}
	}

	protected void closePiece(int outerEnd)
	{
		final String closeTag=buf.substring(beginDex[State.INCLOSETAG.ordinal()],endDex[State.INCLOSETAG.ordinal()]).toUpperCase().trim();
		XMLpiece closePiece=piece;
		while((closePiece!=null)&&(!closePiece.tag().equalsIgnoreCase(closeTag)))
			closePiece=closePiece.parent();
		if(closePiece!=null)
		{
			if(closePiece.innerStartIndex()>=0)
				closePiece.value = buf.substring(closePiece.innerStartIndex(),beginDex[State.BEFORETAG.ordinal()]-1);
			piece=closePiece;
			doneWithPiece(outerEnd);
		}
		else
			Log.errOut("XMLManager","Unable to close tag "+closeTag);
	}

	protected void inTag(final char c)
	{
		switch(c)
		{
		case ' ': case '\t': case '\r': case '\n':
			if(canStartPiece(bufDex))
			{
				startPiece(bufDex);
				changedTagState(State.BEFOREATTRIB);
			}
			else
				changeTagState(State.START);
			return;
		case '<': changeTagState(State.BEFORETAG); return;
		case '>':
			if(canStartPiece(bufDex))
			{
				startPiece(bufDex);
				piece.innerStart = bufDex+1;
			}
			changeTagState(State.START);
			return;
		case '/':
			startPiece(bufDex);
			changedTagState(State.BEGINTAGSELFEND);
			return;
		default: bufDex++; return;
		}
	}

	protected void beginTagSelfEnd(final char c)
	{
		switch(c)
		{
		case ' ': case '\t': case '\r': case '\n': bufDex++; return;
		case '/': bufDex++; break;
		case '<': abandonTagState(State.BEFORETAG); return;
		case '>': doneWithPiece(bufDex); changeTagState(State.START); return;
		default: changeTagState(State.BEFOREATTRIB); return;
		}
	}

	protected void beforeCloseTag(final char c)
	{
		switch(c)
		{
		case ' ': case '\t': case '\r': case '\n': bufDex++; return;
		case '<': changeTagState(State.BEFORETAG); return;
		case '/': changeTagState(State.BEFORECLOSETAG); return;
		default:
			if(Character.isLetter(c))
				changedTagState(State.INCLOSETAG);
			else
				changeTagState(State.START);
			return;
		}
	}

	protected void inCloseTag(final char c)
	{
		switch(c)
		{
		case ' ': case '\t': case '\r': case '\n':
			changedTagState(State.AFTERCLOSETAG);
			return;
		case '<': changeTagState(State.BEFORETAG); return;
		case '>':
			changeTagState(State.START);
			closePiece(bufDex-1);
			return;
		case '/': changedTagState(State.BEFORECLOSETAG); return;
		default: bufDex++; return;
		}
	}

	protected void afterCloseTag(final char c)
	{
		switch(c)
		{
		case ' ': case '\t': case '\r': case '\n': bufDex++; return;
		case '<':
			closePiece(bufDex);
			changeTagState(State.BEFORETAG);
			return;
		case '>':
			closePiece(bufDex);
			changeTagState(State.START);
			return;
		default: bufDex++; return;
		}
	}

	protected void beforeAttrib(final char c)
	{
		switch(c)
		{
		case ' ': case '\t': case '\r': case '\n': bufDex++; return;
		case '<':
			piece.innerStart = bufDex;
			abandonTagState(State.BEFORETAG);
			return;
		case '>':
			changeTagState(State.START);
			piece.innerStart = bufDex;
			return;
		case '/':
			changedTagState(State.BEGINTAGSELFEND);
			return;
		default:
			changedTagState(State.INATTRIB);
			return;
		}
	}

	protected void beAttrib(final char c)
	{
		switch(c)
		{
		case ' ': case '\t': case '\r': case '\n': bufDex++; return;
		case '<':
			piece.innerStart = bufDex;
			abandonTagState(State.BEFORETAG);
			return;
		case '>':
			changeTagState(State.START);
			piece.innerStart = bufDex;
			return;
		case '/':
			endEmptyAttrib(bufDex);
			changedTagState(State.BEGINTAGSELFEND);
			return;
		default:
			changedTagState(State.INATTRIB);
			return;
		}
	}

	protected void endEmptyAttrib(int endOfAttrib)
	{
		if(piece!=null)
		{
			final String value="";
			final String parmName=buf.substring(beginDex[State.INATTRIB.ordinal()],endOfAttrib);
			if((parmName.length()>25)||(parmName.length()==0))
				Log.warnOut("XMLManager","Suspicious attribute '"+parmName+"' for tag "+piece.tag());
			piece.parms().put(parmName, value);
		}
	}

	protected void inAttrib(final char c)
	{
		switch(c)
		{
		case ' ': case '\t': case '\r': case '\n':  changedTagState(State.INPOSTATTRIB); return;
		case '=':  changeTagState(State.BEFOREATTRIBVALUE); return;
		case '<':
			endEmptyAttrib(bufDex);
			piece.innerStart = bufDex;
			abandonTagState(State.BEFORETAG);
			return;
		case '>':
			endEmptyAttrib(bufDex);
			changeTagState(State.START);
			piece.innerStart = bufDex;
			return;
		case '/':
			endEmptyAttrib(bufDex);
			changedTagState(State.BEGINTAGSELFEND);
			return;
		default: bufDex++; return;
		}
	}

	protected void inPostAttrib(final char c)
	{
		switch(c)
		{
		case ' ': case '\t': case '\r': case '\n':  bufDex++; return;
		case '=':  changeTagState(State.BEFOREATTRIBVALUE); return;
		case '<':
			endEmptyAttrib(endDex[State.INATTRIB.ordinal()]);
			piece.innerStart = bufDex;
			abandonTagState(State.BEFORETAG);
			return;
		case '>':
			endEmptyAttrib(endDex[State.INATTRIB.ordinal()]);
			changeTagState(State.START);
			piece.innerStart =bufDex;
			return;
		case '/':
			endEmptyAttrib(endDex[State.INATTRIB.ordinal()]);
			changedTagState(State.BEGINTAGSELFEND);
			return;
		default: changedTagState(State.INATTRIB); return;
		}
	}

	protected void assignAttrib(int endOfValue)
	{
		if(piece!=null)
		{
			final String parmName=buf.substring(beginDex[State.INATTRIB.ordinal()], endDex[State.INATTRIB.ordinal()]).toUpperCase().trim();
			final String value=buf.substring(beginDex[state.ordinal()],endOfValue).trim();
			if((parmName.length()>25)||(parmName.length()==0))
				Log.warnOut("XMLManager","Suspicious attribute '"+parmName+"' for tag "+piece.tag());
			piece.parms().put(parmName, value);
		}

	}

	protected void beforeAttribValue(final char c)
	{
		switch(c)
		{
		case ' ': case '\t': case '\r': case '\n':  bufDex++; return;
		case '=':  bufDex++; return;
		case '<':
			assignAttrib(bufDex);
			piece.innerStart = bufDex;
			abandonTagState(State.BEFORETAG);
			return;
		case '>':
			assignAttrib(bufDex);
			changeTagState(State.START);
			piece.innerStart = bufDex;
			return;
		case '/':
			assignAttrib(bufDex);
			changedTagState(State.BEGINTAGSELFEND);
			return;
		case '"': changeTagState(State.INQUOTEDATTRIBVALUE); return;
		default: changedTagState(State.INATTRIBVALUE); return;
		}
	}

	protected void inAttribValue(final char c)
	{
		switch(c)
		{
		case ' ': case '\t': case '\r': case '\n':
			assignAttrib(bufDex);
			changedTagState(State.BEFOREATTRIB);
			return;
		case '<':
			assignAttrib(bufDex);
			piece.innerStart = bufDex;
			abandonTagState(State.BEFORETAG);
			return;
		case '>':
			assignAttrib(bufDex);
			changeTagState(State.START);
			piece.innerStart = bufDex;
			return;
		case '/':
			assignAttrib(bufDex);
			changedTagState(State.BEGINTAGSELFEND);
			return;
		default: bufDex++; return;
		}
	}

	protected void inQuotedAttribValue(final char c)
	{
		switch(c)
		{
		case '"':
			assignAttrib(bufDex);
			changeTagState(State.BEFOREATTRIB);
			return;
		default: bufDex++; return;
		}
	}

	protected XMLTag parseXML()
	{
		while(bufDex<buf.length())
		{
			switch(state)
			{
			case START:				startState(buf.charAt(bufDex)); break;
			case BEFORETAG:			beforeTag(buf.charAt(bufDex)); break;
			case INTAG:				inTag(buf.charAt(bufDex)); break;
			case BEGINTAGSELFEND:	beginTagSelfEnd(buf.charAt(bufDex)); break;
			case BEFORECLOSETAG:	beforeCloseTag(buf.charAt(bufDex)); break;
			case INCLOSETAG:		inCloseTag(buf.charAt(bufDex)); break;
			case AFTERCLOSETAG:		afterCloseTag(buf.charAt(bufDex)); break;
			case BEFOREATTRIB:		beforeAttrib(buf.charAt(bufDex)); break;
			case INATTRIB:			inAttrib(buf.charAt(bufDex)); break;
			case INPOSTATTRIB:		inPostAttrib(buf.charAt(bufDex)); break;
			case BEFOREATTRIBVALUE:	beforeAttribValue(buf.charAt(bufDex)); break;
			case INATTRIBVALUE:		inAttribValue(buf.charAt(bufDex)); break;
			case INQUOTEDATTRIBVALUE: inQuotedAttribValue(buf.charAt(bufDex)); break;
			}
		}
		while((piece!=null)&&(piece.parent()!=null))
			piece=piece.parent();
		return piece;
	}

	@Override
	public List<XMLTag> parseAllXML(String buf)
	{
		return parseAllXML(new StringBuffer(buf));
	}

	@Override
	public List<XMLTag> parseAllXML(StringBuffer buf)
	{
		final XMLManager manager=new XMLManager(buf, 0);
		manager.parseXML();
		return manager.contents;
	}

	@Override
	public String returnXMLValue(String Blob)
	{
		int start=0;

		try
		{
			while((start<Blob.length())&&(Blob.charAt(start)!='>'))
				start++;
			if((start>=Blob.length())||(Blob.charAt(start-1)!='>')||(Blob.charAt(start-1)=='/'))
				return "";
		}
		catch (final Throwable t){return "";}
		return Blob.substring(start+1).trim();
	}


	@Override
	public String returnXMLValue(String Blob, String Tag)
	{
		int start=0;
		Blob=returnXMLBlock(Blob,Tag);
		try
		{
			while((start<Blob.length())&&(Blob.charAt(start)!='>'))
				start++;
			if((start>=Blob.length())||(Blob.charAt(start)!='>')||(Blob.charAt(start-1)=='/'))
				return "";
		}
		catch (final Throwable t){return "";}
		return Blob.substring(start+1).trim();
	}

	@Override
	public boolean returnXMLBoolean(String Blob, String Tag)
	{
		final String val=returnXMLValue(Blob,Tag);
		if((val==null)||(val.length()==0))
			return false;
		if(val.toUpperCase().trim().startsWith("T"))
			return true;
		return false;
	}

	@Override
	public String getXMLList(List<String> V)
	{
		final StringBuffer str=new StringBuffer("");
		if(V!=null)
		for(final String s : V)
			if(s!=null)
			{
				if(s.trim().length()==0)
					str.append("<X />");
				else
					str.append("<X>"+parseOutAngleBrackets(s)+"</X>");
			}
		return str.toString();
	}

	@Override
	public List<String> parseXMLList(String numberedList)
	{
		final List<XMLLibrary.XMLTag> xml=parseAllXML(numberedList);
		final Vector<String> V=new Vector<String>();
		for(int v=0;v<xml.size();v++)
			V.addElement(this.restoreAngleBrackets(xml.get(v).value()));
		return V;
	}
}