package com.hero;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.StringTokenizer;

import javax.swing.JOptionPane;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;

import com.hero.objects.Adder;
import com.hero.objects.GenericObject;
import com.hero.objects.List;
import com.hero.objects.characteristics.BaseSize;
import com.hero.objects.characteristics.Body;
import com.hero.objects.characteristics.Characteristic;
import com.hero.objects.characteristics.Comliness;
import com.hero.objects.characteristics.Constitution;
import com.hero.objects.characteristics.Custom1;
import com.hero.objects.characteristics.Custom10;
import com.hero.objects.characteristics.Custom2;
import com.hero.objects.characteristics.Custom3;
import com.hero.objects.characteristics.Custom4;
import com.hero.objects.characteristics.Custom5;
import com.hero.objects.characteristics.Custom6;
import com.hero.objects.characteristics.Custom7;
import com.hero.objects.characteristics.Custom8;
import com.hero.objects.characteristics.Custom9;
import com.hero.objects.characteristics.DCV;
import com.hero.objects.characteristics.DMCV;
import com.hero.objects.characteristics.Def;
import com.hero.objects.characteristics.Dexterity;
import com.hero.objects.characteristics.Ego;
import com.hero.objects.characteristics.Endurance;
import com.hero.objects.characteristics.EnergyDefense;
import com.hero.objects.characteristics.Intelligence;
import com.hero.objects.characteristics.Leaping;
import com.hero.objects.characteristics.OCV;
import com.hero.objects.characteristics.OMCV;
import com.hero.objects.characteristics.PhysicalDefense;
import com.hero.objects.characteristics.Presence;
import com.hero.objects.characteristics.Recovery;
import com.hero.objects.characteristics.Running;
import com.hero.objects.characteristics.Size;
import com.hero.objects.characteristics.Speed;
import com.hero.objects.characteristics.Strength;
import com.hero.objects.characteristics.Stun;
import com.hero.objects.characteristics.Swimming;
import com.hero.objects.disads.Disadvantage;
import com.hero.objects.enhancers.Enhancer;
import com.hero.objects.martialarts.ExtraDamageClasses;
import com.hero.objects.martialarts.Maneuver;
import com.hero.objects.martialarts.RangedDamageClasses;
import com.hero.objects.martialarts.WeaponElement;
import com.hero.objects.modifiers.Modifier;
import com.hero.objects.perks.Access;
import com.hero.objects.perks.Contact;
import com.hero.objects.perks.CustomPerk;
import com.hero.objects.perks.Favor;
import com.hero.objects.perks.Follower;
import com.hero.objects.perks.FringeBenefit;
import com.hero.objects.perks.Money;
import com.hero.objects.perks.Perk;
import com.hero.objects.perks.Reputation;
import com.hero.objects.perks.ResourcePool;
import com.hero.objects.perks.Vehicle;
import com.hero.objects.powers.*;
import com.hero.objects.skills.AnimalHandler;
import com.hero.objects.skills.AutofireSkills;
import com.hero.objects.skills.CombatLevels;
import com.hero.objects.skills.ComputerProgramming;
import com.hero.objects.skills.Cramming;
import com.hero.objects.skills.CustomSkill;
import com.hero.objects.skills.DefenseManeuver;
import com.hero.objects.skills.Electronics;
import com.hero.objects.skills.Forgery;
import com.hero.objects.skills.Gambling;
import com.hero.objects.skills.KnowledgeSkill;
import com.hero.objects.skills.Language;
import com.hero.objects.skills.MentalCombatLevels;
import com.hero.objects.skills.Navigation;
import com.hero.objects.skills.PenaltySkillLevels;
import com.hero.objects.skills.ProfessionalSkill;
import com.hero.objects.skills.RapidAttackHTH;
import com.hero.objects.skills.RapidAttackRanged;
import com.hero.objects.skills.Skill;
import com.hero.objects.skills.SkillLevels;
import com.hero.objects.skills.Survival;
import com.hero.objects.skills.SystemsOperation;
import com.hero.objects.skills.TransportFamiliarity;
import com.hero.objects.skills.TwoWeaponFightingHTH;
import com.hero.objects.skills.TwoWeaponFightingRanged;
import com.hero.objects.skills.WeaponFamiliarity;
import com.hero.objects.skills.Weaponsmith;
import com.hero.objects.talents.CombatLuck;
import com.hero.objects.talents.CombatSense;
import com.hero.objects.talents.CustomTalent;
import com.hero.objects.talents.DangerSense;
import com.hero.objects.talents.EnvironmentalMovement;
import com.hero.objects.talents.LightningReflexesAll;
import com.hero.objects.talents.LightningReflexesSingle;
import com.hero.objects.talents.MageSight;
import com.hero.objects.talents.Resistance;
import com.hero.objects.talents.SimulateDeath;
import com.hero.objects.talents.SpeedReading;
import com.hero.objects.talents.StrikingAppearance;
import com.hero.objects.talents.Talent;
import com.hero.objects.talents.UniversalTranslator;
import com.hero.util.Constants;
import com.hero.util.XMLUtility;

/**
 * Copyright (c) 2000 - 2005, CompNet Design, Inc. All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, is prohibited unless the following conditions are met: 1.
 * Express written consent of CompNet Design, Inc. is obtained by the developer.
 * 2. Redistributions must retain this copyright notice. THIS SOFTWARE IS
 * PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
 * EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * @author CompNet Design, Inc.
 * @version $Revision$
 */
public class Template implements Cloneable {

	public static Template getTemplate(InputStream is, String id) {
		if (!HeroDesigner.headless) {
			HeroDesigner.loadProgress
					.setString("Loading template...building file");
		}
		Template template = null;
		try {
			template = new Template(is, id, HeroDesigner.getActiveTemplate());
		} catch (Exception exp) {
			exp.printStackTrace();
			String error = exp.getMessage();
			if (error.startsWith("Error on line")) {
				try {
					error = "Error loading selected template.\n\nError message:\n"
							+ error.substring(error.indexOf(":") + 1, error
									.length());
				} catch (Exception ex) {
				}
			}
			error += "\n\nTemplate has NOT been loaded!";
			JOptionPane.showMessageDialog(HeroDesigner.getAppFrame(), error,
					"Error Loading Template", JOptionPane.ERROR_MESSAGE);

		}
		return template;
	}

	public static Template getTemplate(String fileName) {
		Template template = null;
		try {
			File test = new File(fileName);
			if (test.exists() && test.isFile() && test.canRead()) {
				template = new Template(test, HeroDesigner.getActiveTemplate());
			} else {
				String name = fileName;
				if (name.indexOf("/") >= 0) {
					name = name.substring(name.lastIndexOf("/") + 1, name
							.length());
				}
				if (name.indexOf("\\") >= 0) {
					name = name.substring(name.lastIndexOf("\\") + 1, name
							.length());
				}
				test = new File(HeroDesigner.getInstance().getPrefs()
						.getTemplateDir()
						+ File.separator + name);
				if (test.exists() && test.isFile() && test.canRead()) {
					template = new Template(test, HeroDesigner
							.getActiveTemplate());
				}
			}
		} catch (Exception exp) {
			String error = exp.getMessage();
			if ((error != null) && error.startsWith("Error on line")) {
				try {
					error = "Error loading selected template.\n\nError message:\n"
							+ error.substring(error.indexOf(":") + 1, error
									.length());
				} catch (Exception ex) {
				}
			}
			error += "\n\nTemplate has NOT been loaded!";
			JOptionPane.showMessageDialog(HeroDesigner.getAppFrame(), error,
					"Error Loading Template", JOptionPane.ERROR_MESSAGE);
			exp.printStackTrace();
		}
		return template;
	}

	private String alternateIDsLabel;

	private String appearanceLabel;

	private boolean backgroundAllowed;

	private String backgroundLabel;

	private String campaignNameLabel;

	private String campaignUseLabel;

	private ArrayList<GenericObject> characteristics;

	private String characterNameLabel;

	private ArrayList<GenericObject> disads;

	private boolean eyeColorAllowed;

	private String eyeColorLabel;

	private int generalLevel;

	private String genreLabel;

	private String gmLabel;

	private boolean hairColorAllowed;

	private String hairColorLabel;

	private boolean heightAllowed;

	private String id;

	private String name;

	private ArrayList<GenericObject> martialArts;

	private ArrayList<GenericObject> modifiers;

	private int ncmCostMultiplier;

	private Adder ncmObject;

	private ArrayList<GenericObject> perks;

	private String personalityLabel;

	private String playerNameLabel;

	private ArrayList<GenericObject> powers;

	private String quoteLabel;

	private ArrayList<GenericObject> skillEnhancers;

	private ArrayList<GenericObject> skills;

	private String tacticsLabel;

	private ArrayList<GenericObject> talents;

	private boolean weightAllowed;

	private ArrayList<Template> parents;

	private ArrayList<Prefab> prefabs;

	private Element rootElement;

	private Date charSaveDate = null;

	public Template(Element root, String templateID, Template currentTemplate,
			Date charSave) {
		this(root, templateID, currentTemplate, charSave, false);
	}

	public Template(Element root, String templateID, Template currentTemplate,
			Date charSave, boolean checkForNew) {
		if (!HeroDesigner.headless) {
			HeroDesigner.loadProgress
					.setString("Loading template...building file");
		}
		if (charSave != null) {
			charSaveDate = charSave;
		}
		id = templateID;
		if (checkForNew && (charSaveDate != null)) {
			if (id != null) {
				File f = new File(id);
				if (f.exists()) {
					if (f.lastModified() > charSaveDate.getTime()) {
						int input = JOptionPane.YES_OPTION;
						if (!HeroDesigner.headless) {
							input = JOptionPane
									.showConfirmDialog(
											HeroDesigner.getAppFrame(),
											"There appears to be a newer copy of the template (hdt) file used on this character.  Would you like to load the new template file?",
											"New Template Found",
											JOptionPane.YES_NO_OPTION);
						}
						if (input == JOptionPane.YES_OPTION) {
							try {
								SAXBuilder builder = new SAXBuilder(false);
								Document doc = builder.build(f);
								builder = null;
								root = doc.getRootElement();
							} catch (JDOMException e) {
								e.printStackTrace();
							} catch (IOException e) {
								e.printStackTrace();
							}
						}
					}
				} else {
					f = new File(HeroDesigner.getInstance().getPrefs()
							.getTemplateDir()
							+ File.separator + f.getName());
					if (f.exists()) {
						if (f.lastModified() > charSaveDate.getTime()) {
							int input = JOptionPane.YES_OPTION;
							if (!HeroDesigner.headless) {
								input = JOptionPane
										.showConfirmDialog(
												HeroDesigner.getAppFrame(),
												"There appears to be a newer copy of the template (hdt) file used on this character.  Would you like to load the new template file?",
												"New Template Found",
												JOptionPane.YES_NO_OPTION);
							}
							if (input == JOptionPane.YES_OPTION) {
								try {
									SAXBuilder builder = new SAXBuilder(false);
									Document doc = builder.build(f);
									builder = null;
									root = doc.getRootElement();
								} catch (JDOMException e) {
									e.printStackTrace();
								} catch (IOException e) {
									e.printStackTrace();
								}
							}
						}
					}
				}
			}
		}
		initTemplate(root, currentTemplate);
	}

	private Template(File xmlFile, Template currentTemplate) throws Exception {
		if (!HeroDesigner.headless) {
			HeroDesigner.loadProgress
					.setString("Loading template...building file");
		}
		id = xmlFile.getAbsolutePath();
		SAXBuilder builder = new SAXBuilder(false);
		Document doc = builder.build(xmlFile);
		builder = null;
		Element root = doc.getRootElement();
		initTemplate(root, currentTemplate);
	}

	private Template(InputStream is, String templateID, Template currentTemplate)
			throws Exception {
		if (!HeroDesigner.headless) {
			HeroDesigner.loadProgress
					.setString("Loading template...building file");
		}
		id = templateID;
		SAXBuilder builder = new SAXBuilder(false);
		Document doc = builder.build(is);
		builder = null;
		Element root = doc.getRootElement();
		initTemplate(root, currentTemplate);
	}

	public boolean is6E() {
		if (getId().indexOf("6E") > 0) {
			return true;
		}
		for (Template p:getParentTemplates()) {
			if (p.is6E()) {
				return true;
			}
		}
		return false;
	}
	public void addExtensionTemplate(Template pTemplate) {
		if (pTemplate.getId().startsWith("builtIn")) {
			return;
		}
		for (int i = 0; i < parents.size(); i++) {
			Template t = parents.get(i);
			if (t.getId().startsWith("builtIn")) {
				for (Template temp : pTemplate.getExtensionTemplates()) {
					if (temp.getId().startsWith("builtIn")) {
						break;
					}
					parents.add(i, temp);
				}
				if (getId().startsWith("builtIn")) {
					id = "extension." + getId();
				}
				initTemplate(getTemplateXML(), null);
				return;
			}
		}
		for (Template temp : pTemplate.getExtensionTemplates()) {
			if (temp.getId().startsWith("builtIn")) {
				break;
			}
			parents.add(temp);
		}
		if (getId().startsWith("builtIn")) {
			id = "extension." + getId();
		}

		initTemplate(getTemplateXML(), null);
	}

	private void addToVector(GenericObject add, ArrayList<GenericObject> vec) {
		for (int i = 0; i < vec.size(); i++) {
			GenericObject o = vec.get(i);
			if (o.getXMLID().equals(add.getXMLID())) {
				vec.set(i, add);
				return;
			}
		}
		vec.add(add);
	}

	private void addToVectorByDisplay(GenericObject add,
			ArrayList<GenericObject> vec) {
		for (int i = 0; i < vec.size(); i++) {
			GenericObject o = vec.get(i);
			if (o.getDisplay().equals(add.getDisplay())) {
				vec.set(i, add);
				return;
			}
		}
		vec.add(add);
	}

	public Template clone() {
		try {
			return (Template) super.clone();
		} catch (Exception exp) {
			exp.printStackTrace();
		}
		return null;
	}

	private Template findTemplate(String id, ArrayList<Template> vec) {
		for (Template t : vec) {
			if (t.getId().equals(id)) {
				return t;
			} else {
				t = findTemplate(id, t.getParentTemplates());
				if (t != null) {
					return t;
				}
			}
		}
		return null;
	}

	public String getAlternateIDsLabel() {
		return alternateIDsLabel;
	}

	public String getAppearanceLabel() {
		return appearanceLabel;
	}

	public String getBackgroundLabel() {
		return backgroundLabel;
	}

	public String getCampaignNameLabel() {
		return campaignNameLabel;
	}

	public String getCampaignUseLabel() {
		return campaignUseLabel;
	}

	public ArrayList<GenericObject> getCharacteristics() {
		return characteristics;
	}

	public String getCharacterNameLabel() {
		return characterNameLabel;
	}

	public ArrayList<GenericObject> getDisads() {

		return disads;
	}

	public ArrayList<Template> getExtensionTemplates() {
		ArrayList<Template> ret = new ArrayList<Template>();
		if (getId().startsWith("builtIn")) {
			return ret;
		}
		ret.add(this);
		OUTER: for (Template t : parents) {
			if (t.getId().startsWith("builtIn")) {
				return ret;
			}
			for (Template comp : ret) {
				if (comp.getId().equals(t.getId())) {
					continue OUTER;
				}
			}
			ret.add(t);
		}
		return ret;
	}

	public String getEyeColorLabel() {
		return eyeColorLabel;
	}

	public int getGeneralLevel() {
		return generalLevel;
	}

	public String getGenreLabel() {
		return genreLabel;
	}

	public String getGmLabel() {
		return gmLabel;
	}

	public String getHairColorLabel() {
		return hairColorLabel;
	}

	public String getId() {
		if (id == null) {
			id = "";
		}
		return id;
	}

	public ArrayList<GenericObject> getMartialArts() {

		return martialArts;
	}

	public ArrayList<GenericObject> getModifiers() {

		return modifiers;
	}

	public String getName() {
		if (getId().startsWith("builtIn.")) {
			String suffix = " (5th Edition)";
			if (getId().indexOf("6E") > 0) {
				suffix = " (6th Edition)";
			}
			if (getId().startsWith("builtIn.Superheroic")) {
				return "Superheroic" + suffix;
			} else if (getId().startsWith("builtIn.AI")) {
				return "AI" + suffix;
			} else if (getId().startsWith("builtIn.Automaton")) {
				return "Automaton" + suffix;
			} else if (getId().startsWith("builtIn.Base")) {
				return "Base" + suffix;
			} else if (getId().startsWith("builtIn.Computer")) {
				return "Computer" + suffix;
			} else if (getId().startsWith("builtIn.Heroic")) {
				return "Heroic" + suffix;
			} else if (getId().startsWith("builtIn.Normal")) {
				return "Normal" + suffix;
			} else if (getId().startsWith("builtIn.Vehicle")) {
				return "Vehicle" + suffix;
			}
		}

		if ((name == null) || (name.trim().length() == 0)) {
			if (rootElement != null) {
				String orig = XMLUtility.getValue(rootElement, "originalPath");
				if ((orig != null) && (orig.length() > 0)) {
					File o = new File(orig);
					return o.getName();
				}
			}
			File check = new File(getId());
			return check.getName();
		}
		return name;
	}

	public int getNcmCostMultiplier() {
		if (is6E()) return 2;
		return ncmCostMultiplier;
	}

	public Adder getNCMObject() {
		return ncmObject;
	}

	public double getNcmValue() {
		if (ncmObject == null) {
			return 0;
		}
		return ncmObject.getBaseCost();
	}

	private ArrayList<GenericObject> getParentCharacteristics() {
		ArrayList<GenericObject> ret = new ArrayList<GenericObject>();
		if (parents != null) {
			for (int i = parents.size() - 1; i >= 0; i--) {
				Template t = parents.get(i);
				ArrayList<GenericObject> chars = t.getCharacteristics();
				for (GenericObject o : chars) {
					if (o instanceof List) {
						addToVectorByDisplay(o, ret);
					} else {
						addToVector(o, ret);
					}
				}
			}
		}
		return ret;
	}

	private ArrayList<GenericObject> getParentDisads() {
		ArrayList<GenericObject> ret = new ArrayList<GenericObject>();
		if (parents != null) {
			for (int i = parents.size() - 1; i >= 0; i--) {
				Template t = parents.get(i);
				ArrayList<GenericObject> chars = t.getDisads();
				for (GenericObject o : chars) {
					if (o instanceof List) {
						addToVectorByDisplay(o, ret);
					} else {
						addToVector(o, ret);
					}
				}
			}
		}
		return ret;
	}

	private ArrayList<GenericObject> getParentEnhancers() {
		ArrayList<GenericObject> ret = new ArrayList<GenericObject>();
		if (parents != null) {
			for (int i = parents.size() - 1; i >= 0; i--) {
				Template t = parents.get(i);
				ArrayList<GenericObject> chars = t.getSkillEnhancers();
				for (GenericObject o : chars) {
					if (o instanceof List) {
						addToVectorByDisplay(o, ret);
					} else {
						addToVector(o, ret);
					}
				}
			}
		}
		return ret;
	}

	private ArrayList<GenericObject> getParentMartialArts() {
		ArrayList<GenericObject> ret = new ArrayList<GenericObject>();
		if (parents != null) {
			for (int i = parents.size() - 1; i >= 0; i--) {
				Template t = parents.get(i);
				ArrayList<GenericObject> chars = t.getMartialArts();
				for (GenericObject o : chars) {
					if ((o instanceof List) || (o instanceof Maneuver)) {
						addToVectorByDisplay(o, ret);
					} else {
						addToVector(o, ret);
					}
				}
			}
		}
		return ret;
	}

	private ArrayList<GenericObject> getParentModifiers() {
		ArrayList<GenericObject> ret = new ArrayList<GenericObject>();
		if (parents != null) {
			for (int i = parents.size() - 1; i >= 0; i--) {
				Template t = parents.get(i);
				ArrayList<GenericObject> chars = t.getModifiers();
				for (GenericObject o : chars) {
					if (o instanceof List) {
						addToVectorByDisplay(o, ret);
					} else {
						addToVector(o, ret);
					}
				}
			}
		}
		return ret;
	}

	private ArrayList<GenericObject> getParentPerks() {
		ArrayList<GenericObject> ret = new ArrayList<GenericObject>();
		if (parents != null) {
			for (int i = parents.size() - 1; i >= 0; i--) {
				Template t = parents.get(i);
				ArrayList<GenericObject> chars = t.getPerks();
				for (GenericObject o : chars) {
					if (o instanceof List) {
						addToVectorByDisplay(o, ret);
					} else {
						addToVector(o, ret);
					}
				}
			}
		}
		return ret;
	}

	private ArrayList<GenericObject> getParentPowers() {
		ArrayList<GenericObject> ret = new ArrayList<GenericObject>();
		if (parents != null) {
			for (int i = parents.size() - 1; i >= 0; i--) {
				Template t = parents.get(i);
				ArrayList<GenericObject> chars = t.getPowers();
				for (GenericObject o : chars) {
					if (o instanceof List) {
						addToVectorByDisplay(o, ret);
					} else {
						addToVector(o, ret);
					}
				}
			}
		}
		return ret;
	}

	protected ArrayList<Template> getParents(Element root,
			Template currentTemplate) {
		ArrayList<Template> ret = new ArrayList<Template>();
		String p = root.getAttributeValue("extends");
		ArrayList<String> strings = new ArrayList<String>();
		if ((p != null) && (p.indexOf(",") < 0)) {
			strings.add(p);
		} else if (p != null) {
			StringTokenizer tok = new StringTokenizer(p, ",");
			while (tok.hasMoreTokens()) {
				String check = tok.nextToken();
				if (check.trim().length() > 0) {
					strings.add(check);
				}
			}
		}
		OUTER: for (String str : strings) {
			for (Template t : ret) {
				if (t.getId().equals(str)) {
					continue OUTER;
				}
			}
			/* removed due to issues with 6E Sense Adders that are not present in 5E
			if ((currentTemplate != null)
					&& str.equals(currentTemplate.getId())) {
				ret.add(currentTemplate);
				continue OUTER;
			} else if (currentTemplate != null) {
				Template t = findTemplate(str, currentTemplate
						.getParentTemplates());
				if (t != null) {
					ret.add(t);
					continue OUTER;
				}
			}
			*/
			String name = str;
			if (str.startsWith("builtIn.")) {
				name = name.substring(name.indexOf(".") + 1, name.length());
				Template t = Template.getTemplate(ClassLoader
						.getSystemResourceAsStream(name), str);
				if (t != null) {
					ret.add(t);
				}
			} else {
				Template t = Template.getTemplate(str);
				if (t != null) {
					ret.add(t);
				}
			}
		}
		int index = ret.size() - 1;
		for (int i = 0; i < ret.size(); i++) {
			Template t = ret.get(i);
			if (t.getId().startsWith("builtIn")) {
				index = i;
			}
		}
		Element child = root.getChild("TEMPLATE");
		if (child != null) {
			Template t = new Template(child, child.getAttributeValue("id"),
					currentTemplate, charSaveDate, true);
			OUTER: for (Template e : t.getExtensionTemplates()) {
				for (Template comp : ret) {
					if ((comp.getId().length() > 0) && (e.getId().length() > 0)
							&& comp.getId().equals(e.getId())) {
						continue OUTER;
					}
				}
				if (index >= 0) {
					ret.add(index, e);
				} else {
					ret.add(e);
				}
			}
		}
		return ret;
	}

	private ArrayList<GenericObject> getParentSkills() {
		ArrayList<GenericObject> ret = new ArrayList<GenericObject>();
		if (parents != null) {
			for (int i = parents.size() - 1; i >= 0; i--) {
				Template t = parents.get(i);
				ArrayList<GenericObject> chars = t.getSkills();
				for (GenericObject o : chars) {
					if (o instanceof List) {
						addToVectorByDisplay(o, ret);
					} else {
						addToVector(o, ret);
					}
				}
			}
		}
		return ret;
	}

	private ArrayList<GenericObject> getParentTalents() {
		ArrayList<GenericObject> ret = new ArrayList<GenericObject>();
		if (parents != null) {
			for (int i = parents.size() - 1; i >= 0; i--) {
				Template t = parents.get(i);
				ArrayList<GenericObject> chars = t.getTalents();
				for (GenericObject o : chars) {
					if (o instanceof List) {
						addToVectorByDisplay(o, ret);
					} else {
						addToVector(o, ret);
					}
				}
			}
		}
		return ret;
	}

	public ArrayList<Template> getParentTemplates() {
		return parents;
	}

	public ArrayList<GenericObject> getPerks() {

		return perks;
	}

	public String getPersonalityLabel() {
		return personalityLabel;
	}

	public String getPlayerNameLabel() {
		return playerNameLabel;
	}

	public ArrayList<GenericObject> getPowers() {

		return powers;
	}

	public ArrayList<Prefab> getPrefabs() {
		return prefabs;
	}

	public String getQuoteLabel() {
		return quoteLabel;
	}

	public Element getRootElement() {
		return rootElement;
	}

	public ArrayList<GenericObject> getSkillEnhancers() {
		return skillEnhancers;
	}

	public ArrayList<GenericObject> getSkills() {

		return skills;
	}

	public String getTacticsLabel() {
		return tacticsLabel;
	}

	public ArrayList<GenericObject> getTalents() {

		return talents;
	}

	public Element getTemplateExtensionXML() {
		Element root = null;
		if (!getId().startsWith("builtIn")) {
			root = getTemplateXML();
		}
		if (root == null) {
			root = new Element("TEMPLATE");
			root.setAttribute("version", "3.0");
			root.setAttribute("extends", getId());
			String n = name;
			if ((n == null) || (n.trim().length() == 0)) {
				n = getId();
				File f = new File(getId());
				if (f.exists()) {
					n = f.getName();
				}
			}
			root.setAttribute("name", n);
			root.addContent(new Element("MAINAPP"));
			root.addContent(new Element("CHARACTERISTICS"));
			root.addContent(new Element("SKILLS"));
			root.addContent(new Element("SKILL_ENHANCERS"));
			root.addContent(new Element("MARTIAL_ARTS"));
			root.addContent(new Element("PERKS"));
			root.addContent(new Element("TALENTS"));
			root.addContent(new Element("POWERS"));
			root.addContent(new Element("MODIFIERS"));
			root.addContent(new Element("DISADVANTAGES"));
		}
		return root;
	}

	public Element getTemplateXML() {
		if (getId().startsWith("builtIn")) {
			return getTemplateExtensionXML();
		}
		Element root = null;
		Element ret = null;
		for (Template t : getExtensionTemplates()) {
			if (t.getId().startsWith("builtIn")) {
				continue;
			}
			if (root == null) {
				root = t.getRootElement();
				ret = root;
				root.removeChildren("TEMPLATE");
			} else {
				root.addContent(t.getRootElement().detach());
				root = t.getRootElement();
			}
			if ((root.getAttributeValue("extends") != null)
					&& !root.getAttributeValue("extends").startsWith("builtIn")) {
				root.removeAttribute("extends");
			}
			root.setAttribute("id", t.getId());
		}
		return ret;
	}

	private void initTemplate(Element root, Template currentTemplate) {
		if (!HeroDesigner.headless) {
			HeroDesigner.loadProgress
					.setString("Loading template...reading main app section");
		}
		rootElement = root;

		// check if we're dealing with a character file or a hdt template file:
		if (!root.getName().equals("TEMPLATE")) {
			Element hdtcheck = root.getChild("TEMPLATE");
			if (hdtcheck != null) {
				rootElement = hdtcheck;
				root = hdtcheck;
			}
		}

		String nameCheck = root.getAttributeValue("name");
		if ((nameCheck != null) && (nameCheck.trim().length() > 0)) {
			name = nameCheck;
		}
		SenseAdder.getAllSenseAdders().clear();
		SenseGroup.clear();
		parents = getParents(root, currentTemplate);
		characteristics = getParentCharacteristics();
		modifiers = getParentModifiers();
		skills = getParentSkills();
		powers = getParentPowers();
		perks = getParentPerks();
		talents = getParentTalents();
		disads = getParentDisads();
		martialArts = getParentMartialArts();
		skillEnhancers = getParentEnhancers();

		Template parent = null;
		if (parents.size() > 0) {
			parent = parents.get(0);
		}

		backgroundAllowed = parent != null ? parent.isBackgroundAllowed()
				: true;
		heightAllowed = parent != null ? parent.isHeightAllowed() : true;
		weightAllowed = parent != null ? parent.isWeightAllowed() : true;
		eyeColorAllowed = parent != null ? parent.isEyeColorAllowed() : true;
		hairColorAllowed = parent != null ? parent.isHairColorAllowed() : true;
		generalLevel = parent != null ? parent.getGeneralLevel() : 10;

		characterNameLabel = parent != null ? parent.getCharacterNameLabel()
				: "Character Name";
		alternateIDsLabel = parent != null ? parent.getAlternateIDsLabel()
				: "Alternate IDs";
		campaignNameLabel = parent != null ? parent.getCampaignNameLabel()
				: "Campaign Name";
		genreLabel = parent != null ? parent.getGenreLabel() : "Genre";
		playerNameLabel = parent != null ? parent.getPlayerNameLabel()
				: "Player Name";
		gmLabel = parent != null ? parent.getGmLabel() : "GM";
		eyeColorLabel = parent != null ? parent.getEyeColorLabel()
				: "Eye Color";
		hairColorLabel = parent != null ? parent.getHairColorLabel()
				: "Hair Color";
		backgroundLabel = parent != null ? parent.getBackgroundLabel()
				: "Background/History";
		personalityLabel = parent != null ? parent.getPersonalityLabel()
				: "Personality/Motivation";
		quoteLabel = parent != null ? parent.getQuoteLabel() : "Quote";
		tacticsLabel = parent != null ? parent.getTacticsLabel()
				: "Powers/Tactics";
		campaignUseLabel = parent != null ? parent.getCampaignUseLabel()
				: "Campaign Use";
		appearanceLabel = parent != null ? parent.getAppearanceLabel()
				: "Appearance";
		ncmCostMultiplier = parent != null ? parent.getNcmCostMultiplier() : 0;

		// init main app prefs
		Element mainApp = root.getChild("MAINAPP");
		if (mainApp != null) {
			String check = XMLUtility.getValue(mainApp, "BACKGROUND_TAB");
			if ((check != null) && (check.trim().length() > 0)) {
				backgroundAllowed = check.toUpperCase().startsWith("Y");
			}
			check = XMLUtility.getValue(mainApp, "HEIGHT");
			if ((check != null) && (check.trim().length() > 0)) {
				heightAllowed = check.toUpperCase().startsWith("Y");
			}
			check = XMLUtility.getValue(mainApp, "WEIGHT");
			if ((check != null) && (check.trim().length() > 0)) {
				weightAllowed = check.toUpperCase().startsWith("Y");
			}
			check = XMLUtility.getValue(mainApp, "EYECOLOR");
			if ((check != null) && (check.trim().length() > 0)) {
				eyeColorAllowed = check.toUpperCase().startsWith("Y");
				if (!eyeColorAllowed) {
					eyeColorLabel = null;
				}
			}
			check = XMLUtility.getValue(mainApp, "HAIRCOLOR");
			if ((check != null) && (check.trim().length() > 0)) {
				hairColorAllowed = check.toUpperCase().startsWith("Y");
				if (!hairColorAllowed) {
					hairColorLabel = null;
				}
			}
			check = XMLUtility.getValue(mainApp, "CHARACTERNAMELABEL");
			if ((check != null) && (check.trim().length() > 0)) {
				characterNameLabel = check;
			}
			check = XMLUtility.getValue(mainApp, "ALTERNATEIDSLABEL");
			if ((check != null) && (check.trim().length() > 0)) {
				alternateIDsLabel = check;
			}

			check = XMLUtility.getValue(mainApp, "NAME1");
			if ((check != null) && (mainApp.getChild("NAME1") != null)) {
				if (check.trim().length() == 0) {
					characterNameLabel = null;
				} else {
					characterNameLabel = check;
				}
			}
			check = XMLUtility.getValue(mainApp, "NAME2");
			if ((check != null) && (mainApp.getChild("NAME2") != null)) {
				if (check.trim().length() == 0) {
					alternateIDsLabel = null;
				} else {
					alternateIDsLabel = check;
				}
			}
			check = XMLUtility.getValue(mainApp, "CAMPAIGN");
			if ((check != null) && (mainApp.getChild("CAMPAIGN") != null)) {
				if (check.trim().length() == 0) {
					campaignNameLabel = null;
				} else {
					campaignNameLabel = check;
				}
			}
			check = XMLUtility.getValue(mainApp, "GENRE");
			if ((check != null) && (mainApp.getChild("GENRE") != null)) {
				if (check.trim().length() == 0) {
					genreLabel = null;
				} else {
					genreLabel = check;
				}
			}
			check = XMLUtility.getValue(mainApp, "PLAYER");
			if ((check != null) && (mainApp.getChild("PLAYER") != null)) {
				if (check.trim().length() == 0) {
					playerNameLabel = null;
				} else {
					playerNameLabel = check;
				}
			}
			check = XMLUtility.getValue(mainApp, "GM");
			if ((check != null) && (mainApp.getChild("GM") != null)) {
				if (check.trim().length() == 0) {
					gmLabel = null;
				} else {
					gmLabel = check;
				}
			}
			check = XMLUtility.getValue(mainApp, "EYE_COLOR");
			if ((check != null) && (mainApp.getChild("EYE_COLOR") != null)) {
				if (check.trim().length() == 0) {
					eyeColorLabel = null;
				} else {
					eyeColorLabel = check;
				}
			}
			check = XMLUtility.getValue(mainApp, "HAIR_COLOR");
			if ((check != null) && (mainApp.getChild("HAIR_COLOR") != null)) {
				if (check.trim().length() == 0) {
					hairColorLabel = null;
				} else {
					hairColorLabel = check;
				}
			}
			check = XMLUtility.getValue(mainApp, "BACKGROUND");
			if ((check != null) && (mainApp.getChild("BACKGROUND") != null)) {
				if (check.trim().length() == 0) {
					backgroundLabel = null;
				} else {
					backgroundLabel = check;
				}
			}
			check = XMLUtility.getValue(mainApp, "PERSONALITY");
			if ((check != null) && (mainApp.getChild("PERSONALITY") != null)) {
				if (check.trim().length() == 0) {
					personalityLabel = null;
				} else {
					personalityLabel = check;
				}
			}
			check = XMLUtility.getValue(mainApp, "QUOTE");
			if ((check != null) && (mainApp.getChild("QUOTE") != null)) {
				if (check.trim().length() == 0) {
					quoteLabel = null;
				} else {
					quoteLabel = check;
				}
			}
			check = XMLUtility.getValue(mainApp, "TACTICS");
			if ((check != null) && (mainApp.getChild("TACTICS") != null)) {
				if (check.trim().length() == 0) {
					tacticsLabel = null;
				} else {
					tacticsLabel = check;
				}
			}
			check = XMLUtility.getValue(mainApp, "USE");
			if ((check != null) && (mainApp.getChild("USE") != null)) {
				if (check.trim().length() == 0) {
					campaignUseLabel = null;
				} else {
					campaignUseLabel = check;
				}
			}
			check = XMLUtility.getValue(mainApp, "APPEARANCE");
			if ((check != null) && (mainApp.getChild("APPEARANCE") != null)) {
				if (check.trim().length() == 0) {
					appearanceLabel = null;
				} else {
					appearanceLabel = check;
				}
			}

			Element ncm = mainApp.getChild("NCM");
			if (ncm != null) {
				ncmObject = new Adder(ncm);
				if (!ncmObject.includedInTemplate()) {
					ncmObject = null;
				}

			} else if ((mainApp.getChild("REMOVE") != null)
					&& mainApp.getChildTextTrim("REMOVE").equalsIgnoreCase(
							"NCM")) {
				ncmObject = null;
			} else {
				ncmObject = null;
				LOOP: for (int i = 0; i < parents.size(); i++) {
					Template t = parents.get(i);
					if (t.getNCMObject() != null) {
						ncmObject = t.getNCMObject();
						ncmCostMultiplier = t.getNcmCostMultiplier();
						break LOOP;
					}
				}
			}
			check = XMLUtility.getValue(mainApp, "NCM_COST_MULTIPLIER");
			if ((check != null) && (check.trim().length() > 0)) {
				try {
					ncmCostMultiplier = Integer.parseInt(check);
				} catch (NumberFormatException ex) {
				}
			}
			check = XMLUtility.getValue(mainApp, "GENERAL_LEVEL");
			if ((check != null) && (check.trim().length() > 0)) {
				try {
					generalLevel = Integer.parseInt(check);
				} catch (NumberFormatException ex) {
				}
			}
		}

		// set the activeTemplate so that the Characteristics can access it for
		// NCM stuff:
		HeroDesigner.getInstance().setActiveTemplate(this);
		if (!HeroDesigner.headless) {
			HeroDesigner.loadProgress
					.setString("Loading template...reading characteristics section");
		}
		// init characteristics
		Element charXML = root.getChild("CHARACTERISTICS");
		if (charXML == null) {
			charXML = new Element("CHARACTERISTICS");
		}
		Iterator iter = charXML.getChildren("REMOVE").iterator();
		while (iter.hasNext()) {
			Element remove = (Element) iter.next();
			String id = remove.getTextTrim();
			if (id.trim().length() > 0) {
				removeFromVector(id, characteristics);
			}
		}
		Characteristic check = new Body(charXML);
		if (check.includedInTemplate()) {
			addToVector(check, characteristics);
		}
		check = new OCV(charXML);
		if (check.includedInTemplate()) {
			addToVector(check, characteristics);
		}
		check = new DCV(charXML);
		if (check.includedInTemplate()) {
			addToVector(check, characteristics);
		}
		check = new OMCV(charXML);
		if (check.includedInTemplate()) {
			addToVector(check, characteristics);
		}
		check = new DMCV(charXML);
		if (check.includedInTemplate()) {
			addToVector(check, characteristics);
		}
		check = new Comliness(charXML);
		if (check.includedInTemplate()) {
			addToVector(check, characteristics);
		}
		check = new Constitution(charXML);
		if (check.includedInTemplate()) {
			addToVector(check, characteristics);
		}
		check = new Dexterity(charXML);
		if (check.includedInTemplate()) {
			addToVector(check, characteristics);
		}
		check = new Ego(charXML);
		if (check.includedInTemplate()) {
			addToVector(check, characteristics);
		}
		check = new Endurance(charXML);
		if (check.includedInTemplate()) {
			addToVector(check, characteristics);
		}
		check = new EnergyDefense(charXML);
		if (check.includedInTemplate()) {
			addToVector(check, characteristics);
		}
		check = new Intelligence(charXML);
		if (check.includedInTemplate()) {
			addToVector(check, characteristics);
		}
		check = new PhysicalDefense(charXML);
		if (check.includedInTemplate()) {
			addToVector(check, characteristics);
		}
		check = new Presence(charXML);
		if (check.includedInTemplate()) {
			addToVector(check, characteristics);
		}
		check = new Recovery(charXML);
		if (check.includedInTemplate()) {
			addToVector(check, characteristics);
		}
		check = new Speed(charXML);
		if (check.includedInTemplate()) {
			addToVector(check, characteristics);
		}
		check = new Strength(charXML);
		if (check.includedInTemplate()) {
			addToVector(check, characteristics);
		}
		check = new Stun(charXML);
		if (check.includedInTemplate()) {
			addToVector(check, characteristics);
		}
		check = new Size(charXML);
		if (check.includedInTemplate()) {
			addToVector(check, characteristics);
		}
		check = new BaseSize(charXML);
		if (check.includedInTemplate()) {
			addToVector(check, characteristics);
		}
		check = new Def(charXML);
		if (check.includedInTemplate()) {
			addToVector(check, characteristics);
		}
		check = new Running(charXML);
		if (check.includedInTemplate()) {
			addToVector(check, characteristics);
		}
		check = new Swimming(charXML);
		if (check.includedInTemplate()) {
			addToVector(check, characteristics);
		}
		check = new Leaping(charXML);
		if (check.includedInTemplate()) {
			addToVector(check, characteristics);
		}
		check = new Custom1(charXML);
		if (check.includedInTemplate()) {
			addToVector(check, characteristics);
		}
		check = new Custom2(charXML);
		if (check.includedInTemplate()) {
			addToVector(check, characteristics);
		}
		check = new Custom3(charXML);
		if (check.includedInTemplate()) {
			addToVector(check, characteristics);
		}
		check = new Custom4(charXML);
		if (check.includedInTemplate()) {
			addToVector(check, characteristics);
		}
		check = new Custom5(charXML);
		if (check.includedInTemplate()) {
			addToVector(check, characteristics);
		}
		check = new Custom6(charXML);
		if (check.includedInTemplate()) {
			addToVector(check, characteristics);
		}
		check = new Custom7(charXML);
		if (check.includedInTemplate()) {
			addToVector(check, characteristics);
		}
		check = new Custom8(charXML);
		if (check.includedInTemplate()) {
			addToVector(check, characteristics);
		}
		check = new Custom9(charXML);
		if (check.includedInTemplate()) {
			addToVector(check, characteristics);
		}
		check = new Custom10(charXML);
		if (check.includedInTemplate()) {
			addToVector(check, characteristics);
		}

		Collections.sort(characteristics);
		if (!HeroDesigner.headless) {
			HeroDesigner.loadProgress
					.setString("Loading template...reading skill enhancers section");
		}
		// init skill enhancers
		charXML = root.getChild("SKILL_ENHANCERS");
		if (charXML == null) {
			charXML = new Element("SKILL_ENHANCERS");
		}
		iter = charXML.getChildren("REMOVE").iterator();
		while (iter.hasNext()) {
			Element remove = (Element) iter.next();
			String id = remove.getTextTrim();
			if (id.trim().length() > 0) {
				removeFromVector(id, skillEnhancers);
			}
		}
		java.util.List list = charXML.getChildren("ENHANCER");
		Iterator iterator = list.iterator();
		while (iterator.hasNext()) {
			Element xml = (Element) iterator.next();
			Enhancer mod = new Enhancer(xml);
			if ((mod != null) && mod.includedInTemplate()) {
				addToVector(mod, skillEnhancers);
			}
		}
		
		if (!HeroDesigner.headless) {
			HeroDesigner.loadProgress
					.setString("Loading template...reading modifiers section");
		}
		// init modifiers
		charXML = root.getChild("MODIFIERS");
		if (charXML == null) {
			charXML = new Element("MODIFIERS");
		}
		iter = charXML.getChildren("REMOVE").iterator();
		while (iter.hasNext()) {
			Element remove = (Element) iter.next();
			String id = remove.getTextTrim();
			if (id.trim().length() > 0) {
				removeFromVector(id, modifiers);
			}
		}
		list = charXML.getChildren("MODIFIER");
		iterator = list.iterator();
		while (iterator.hasNext()) {
			Element modXML = (Element) iterator.next();
			Modifier mod = Modifier.getInstance(modXML);
			if ((mod != null) && mod.includedInTemplate()) {
				addToVector(mod, modifiers);
			}
		}
		Collections.sort(modifiers, new Comparator() {
			public int compare(Object o1, Object o2) {
				return o1.toString().compareTo(o2.toString());
			}

			public boolean equals(Object o) {
				return false;
			}
		});

		// init skills
		if (!HeroDesigner.headless) {
			HeroDesigner.loadProgress
					.setString("Loading template...reading skills section");
		}
		Element xml = root.getChild("SKILLS");
		if (xml == null) {
			xml = new Element("SKILLS");
		}
		iter = xml.getChildren("REMOVE").iterator();
		while (iter.hasNext()) {
			Element remove = (Element) iter.next();
			String id = remove.getTextTrim();
			if (id.trim().length() > 0) {
				removeFromVector(id, skills);
			}
		}
		iter = xml.getChildren("SKILL").iterator();
		while (iter.hasNext()) {
			Element newSkill = (Element) iter.next();
			GenericObject skill = new Skill(newSkill);
			if (skill.includedInTemplate()) {
				addToVector(skill, skills);
			}
		}
		GenericObject skill = null;
		skill = new AnimalHandler(xml);
		if (skill.includedInTemplate()) {
			addToVector(skill, skills);
		}
		skill = new AutofireSkills(xml);
		if (skill.includedInTemplate()) {
			addToVector(skill, skills);
		}		
		skill = new CombatLevels(xml);
		if (skill.includedInTemplate()) {
			addToVector(skill, skills);
		}		
		skill = new MentalCombatLevels(xml);
		if (skill.includedInTemplate()) {
			addToVector(skill, skills);
		}
		skill = new ComputerProgramming(xml);
		if (skill.includedInTemplate()) {
			addToVector(skill, skills);
		}
		skill = new Cramming(xml);
		if (skill.includedInTemplate()) {
			addToVector(skill, skills);
		}
		skill = new CustomSkill(xml);
		if (skill.includedInTemplate()) {
			addToVector(skill, skills);
		}
		skill = new DefenseManeuver(xml);
		if (skill.includedInTemplate()) {
			addToVector(skill, skills);
		}
		skill = new Electronics(xml);
		if (skill.includedInTemplate()) {
			addToVector(skill, skills);
		}
		skill = new Forgery(xml);
		if (skill.includedInTemplate()) {
			addToVector(skill, skills);
		}
		skill = new Gambling(xml);
		if (skill.includedInTemplate()) {
			addToVector(skill, skills);
		}
		skill = new KnowledgeSkill(xml);
		if (skill.includedInTemplate()) {
			addToVector(skill, skills);
		}
		skill = new Language(xml);
		if (skill.includedInTemplate()) {
			addToVector(skill, skills);
		}
		skill = new Navigation(xml);
		if (skill.includedInTemplate()) {
			addToVector(skill, skills);
		}
		skill = new PenaltySkillLevels(xml);
		if (skill.includedInTemplate()) {
			addToVector(skill, skills);
		}
		skill = new ProfessionalSkill(xml);
		if (skill.includedInTemplate()) {
			addToVector(skill, skills);
		}
		skill = new RapidAttackHTH(xml);
		if (skill.includedInTemplate()) {
			addToVector(skill, skills);
		}
		skill = new RapidAttackRanged(xml);
		if (skill.includedInTemplate()) {
			addToVector(skill, skills);
		}
		skill = new SkillLevels(xml);
		if (skill.includedInTemplate()) {
			addToVector(skill, skills);
		}
		skill = new Survival(xml);
		if (skill.includedInTemplate()) {
			addToVector(skill, skills);
		}
		skill = new SystemsOperation(xml);
		if (skill.includedInTemplate()) {
			addToVector(skill, skills);
		}
		skill = new TransportFamiliarity(xml);
		if (skill.includedInTemplate()) {
			addToVector(skill, skills);
		}
		skill = new TwoWeaponFightingHTH(xml);
		if (skill.includedInTemplate()) {
			addToVector(skill, skills);
		}
		skill = new TwoWeaponFightingRanged(xml);
		if (skill.includedInTemplate()) {
			addToVector(skill, skills);
		}
		skill = new WeaponFamiliarity(xml);
		if (skill.includedInTemplate()) {
			addToVector(skill, skills);
		}
		skill = new Weaponsmith(xml);
		if (skill.includedInTemplate()) {
			addToVector(skill, skills);
		}
		Collections.sort(skills, new Comparator() {
			public int compare(Object o1, Object o2) {
				return o1.toString().compareTo(o2.toString());
			}

			public boolean equals(Object o) {
				return false;
			}
		});
		if (!HeroDesigner.headless) {
			HeroDesigner.loadProgress
					.setString("Loading template...reading martial arts section");
		}
		// init martial arts
		xml = root.getChild("MARTIAL_ARTS");
		iter = xml.getChildren("REMOVE").iterator();
		while (iter.hasNext()) {
			Element remove = (Element) iter.next();
			String id = remove.getTextTrim();
			if (id.trim().length() > 0) {
				removeFromVector(id, martialArts);
			}
		}
		java.util.List maneuvers = xml.getChildren("MANEUVER");
		iterator = maneuvers.iterator();
		Hashtable hash = new Hashtable();
		Maneuver custom = null;
		while (iterator.hasNext()) {
			Element man = (Element) iterator.next();
			Maneuver maneuver = new Maneuver(man);
			if (maneuver.includedInTemplate()) {
				if ((maneuver.getCategory() == null)
						|| (maneuver.getCategory().trim().length() == 0)) {
					addToVectorByDisplay(maneuver, martialArts);
				} else if (hash.get(maneuver.getCategory()) == null) {
					com.hero.objects.List cat = new com.hero.objects.List(
							maneuver.getCategory());
					cat.addObject(maneuver);
					hash.put(maneuver.getCategory(), cat);
				} else {
					com.hero.objects.List cat = (com.hero.objects.List) hash
							.get(maneuver.getCategory());
					addToVectorByDisplay(maneuver, cat.getObjects());
				}
				if (custom == null) {
					custom = maneuver;
				}
			}
		}
		ExtraDamageClasses exdc = new ExtraDamageClasses(xml);
		if (exdc.includedInTemplate()) {
			addToVector(exdc, martialArts);
		}
		RangedDamageClasses rdc = new RangedDamageClasses(xml);
		if (rdc.includedInTemplate()) {
			addToVector(rdc, martialArts);
		}
		WeaponElement ele = new WeaponElement(xml);
		if (ele.includedInTemplate()) {
			addToVector(ele, martialArts);
		}
		if (custom != null) {
			custom = (Maneuver) custom.clone();
			custom.setDisplay("Custom Maneuver");
			custom.setManeuverActiveCost(0);
			custom.setEffect("Strike");
			custom.setOCV("+0");
			custom.setDCV("+0");
			custom.setPhase("1/2");
			custom.setRanged(-1);
			custom.setCustom(true);
			custom.setExclusive(false);
			addToVectorByDisplay(custom, martialArts);
		}
		Enumeration enumer = hash.keys();
		while (enumer.hasMoreElements()) {
			List l = (List) hash.get(enumer.nextElement());
			if (l.getObjects().size() > 0) {
				addToVectorByDisplay(l, martialArts);
			}
		}
		for (int i = martialArts.size() - 1; i >= 0; i--) {
			if (martialArts.get(i) instanceof List) {
				List l = (List) martialArts.get(i);
				if (l.getObjects().size() == 0) {
					martialArts.remove(i);
				}
			}
		}
		Collections.sort(martialArts, new Comparator() {
			public int compare(Object o1, Object o2) {
				return o1.toString().compareTo(o2.toString());
			}

			public boolean equals(Object o) {
				return false;
			}
		});
		if (!HeroDesigner.headless) {
			HeroDesigner.loadProgress
					.setString("Loading template...reading perks section");
		}
		// init perks
		xml = root.getChild("PERKS");
		if (xml == null) {
			xml = new Element("PERKS");
		}
		iter = xml.getChildren("REMOVE").iterator();
		while (iter.hasNext()) {
			Element remove = (Element) iter.next();
			String id = remove.getTextTrim();
			if (id.trim().length() > 0) {
				removeFromVector(id, perks);
			}
		}
		iter = xml.getChildren("PERK").iterator();
		while (iter.hasNext()) {
			Element el = (Element) iter.next();
			Perk perk = new Perk(el);
			if (perk.includedInTemplate()) {
				addToVector(perk, perks);
			}
		}
		Perk perk = new Access(xml);
		if (perk.includedInTemplate()) {
			addToVector(perk, perks);
		}
		perk = new ResourcePool(xml);
		if (perk.includedInTemplate()) {
			addToVector(perk, perks);
		}
		perk = new Contact(xml);
		if (perk.includedInTemplate()) {
			addToVector(perk, perks);
		}
		perk = new CustomPerk(xml);
		if (perk.includedInTemplate()) {
			addToVector(perk, perks);
		}
		perk = new Favor(xml);
		if (perk.includedInTemplate()) {
			addToVector(perk, perks);
		}
		perk = new Follower(xml);
		if (perk.includedInTemplate()) {
			addToVector(perk, perks);
		}
		perk = new FringeBenefit(xml);
		if (perk.includedInTemplate()) {
			addToVector(perk, perks);
		}
		perk = new Money(xml);
		if (perk.includedInTemplate()) {
			addToVector(perk, perks);
		}
		perk = new Reputation(xml);
		if (perk.includedInTemplate()) {
			addToVector(perk, perks);
		}
		perk = new Vehicle(xml);
		if (perk.includedInTemplate()) {
			addToVector(perk, perks);
		}
		Collections.sort(perks, new Comparator() {
			public int compare(Object o1, Object o2) {
				return o1.toString().compareTo(o2.toString());
			}

			public boolean equals(Object o) {
				return false;
			}
		});
		if (!HeroDesigner.headless) {
			HeroDesigner.loadProgress
					.setString("Loading template...reading talents section");
		}
		// init talents
		xml = root.getChild("TALENTS");
		if (xml == null) {
			xml = new Element("TALENTS");
		}
		iter = xml.getChildren("REMOVE").iterator();
		while (iter.hasNext()) {
			Element remove = (Element) iter.next();
			String id = remove.getTextTrim();
			if (id.trim().length() > 0) {
				removeFromVector(id, talents);
			}
		}
		iter = xml.getChildren("TALENT").iterator();
		while (iter.hasNext()) {
			Element el = (Element) iter.next();
			Talent talent = new Talent(el);
			if (talent.includedInTemplate()) {
				addToVector(talent, talents);
			}
		}
		Sense ms = new MageSight(xml);
		if (ms.includedInTemplate()) {
			addToVector(ms, talents);
		}
		Talent talent = null;
		
		talent = new CombatLuck(xml);
		if (talent.includedInTemplate()) {
			addToVector(talent, talents);
		}
		talent = new CombatSense(xml);
		if (talent.includedInTemplate()) {
			addToVector(talent, talents);
		}
		talent = new CustomTalent(xml);
		if (talent.includedInTemplate()) {
			addToVector(talent, talents);
		}
		talent = new DangerSense(xml);
		if (talent.includedInTemplate()) {
			addToVector(talent, talents);
		}
		talent = new EnvironmentalMovement(xml);
		if (talent.includedInTemplate()) {
			addToVector(talent, talents);
		}
		talent = new LightningReflexesSingle(xml);
		if (talent.includedInTemplate()) {
			addToVector(talent, talents);
		}
		talent = new LightningReflexesAll(xml);
		if (talent.includedInTemplate()) {
			addToVector(talent, talents);
		}
		talent = new Resistance(xml);
		if (talent.includedInTemplate()) {
			addToVector(talent, talents);
		}
		talent = new SimulateDeath(xml);
		if (talent.includedInTemplate()) {
			addToVector(talent, talents);
		}
		talent = new SpeedReading(xml);
		if (talent.includedInTemplate()) {
			addToVector(talent, talents);
		}
		talent = new StrikingAppearance(xml);
		if (talent.includedInTemplate()) {
			addToVector(talent, talents);
		}
		talent = new UniversalTranslator(xml);
		if (talent.includedInTemplate()) {
			addToVector(talent, talents);
		}
		Collections.sort(talents, new Comparator() {
			public int compare(Object o1, Object o2) {
				return o1.toString().compareTo(o2.toString());
			}

			public boolean equals(Object o) {
				return false;
			}
		});
		if (!HeroDesigner.headless) {
			HeroDesigner.loadProgress
					.setString("Loading template...reading powers section");
		}
		// init powers
		xml = root.getChild("POWERS");
		if (xml == null) {
			xml = new Element("POWERS");
		}
		iter = xml.getChildren("REMOVE").iterator();
		while (iter.hasNext()) {
			Element remove = (Element) iter.next();
			String id = remove.getTextTrim();
			if (id.trim().length() > 0) {
				removeFromVector(id, powers);
			}
		}
		iter = xml.getChildren("POWER").iterator();
		while (iter.hasNext()) {
			Element el = (Element) iter.next();
			Power power = new Power(el);
			if (power.includedInTemplate()) {
				addToVector(power, powers);
			}
		}
		Power power = new Absorption(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new NegativeCombatSkillLevels(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new NegativePenaltySkillLevels(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new NegativeSkillLevels(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new Aid(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new Armor(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new Automaton(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new ChangeEnvironment(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new Clairsentience(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new Clinging(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new CompoundPower(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new CustomPower(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new DamageNegation(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new DamageReduction(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new DamageResistance(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new Darkness(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new DensityIncrease(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new Desolidification(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new Dispel(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new DoesNotBleed(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new Drain(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new Duplication(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new EgoAttack(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new EnduranceReserve(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new EnergyBlast(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		com.hero.objects.List senses = new com.hero.objects.List(
				"Enhanced Senses");
		com.hero.objects.List senseAdders = new com.hero.objects.List(
				"Sense Modifiers");
		Iterator groups = xml.getChildren("SENSEGROUP").iterator();
		if (groups.hasNext()) {
			SenseGroup.clear();
		}
		while (groups.hasNext()) {
			new SenseGroup((Element) groups.next());
		}
		Iterator baseSenses = xml.getChildren("SENSE").iterator();
		if (baseSenses.hasNext()) {
			Sense.clear();
		}
		while (baseSenses.hasNext()) {
			new Sense((Element) baseSenses.next());
		}

		for (int i = 0; i < powers.size(); i++) {
			GenericObject o = powers.get(i);
			if (o instanceof com.hero.objects.List) {
				com.hero.objects.List l = (com.hero.objects.List) o;
				if (l.getDisplay().equals("Enhanced Senses")) {
					senses = l;
					SENSELOOP: for (int j = 0; j < senses.getObjects().size(); j++) {
						GenericObject test = senses.getObjects().get(j);
						if (test instanceof com.hero.objects.List) {
							senseAdders = (com.hero.objects.List) test;
							senses.getObjects().remove(j);
							break SENSELOOP;
						}
					}
					powers.remove(i);
					break;
				}
			}
		}
		iter = xml.getChildren("ENHANCEDSENSE").iterator();
		while (iter.hasNext()) {
			Element el = (Element) iter.next();
			power = new Sense(el);
			if (power.findObjectByID(senses.getObjects(), power.getXMLID()) != null) {
				senses.getObjects().remove(
						power.findObjectByID(senses.getObjects(), power
								.getXMLID()));
			}
			senses.addObject(power);
		}
		iter = xml.getChildren("SENSEADDER").iterator();
		while (iter.hasNext()) {
			Element el = (Element) iter.next();
			power = new SenseAdder(el);
			if (power
					.findObjectByID(senseAdders.getObjects(), power.getXMLID()) != null) {
				senseAdders.getObjects().remove(
						power.findObjectByID(senseAdders.getObjects(), power
								.getXMLID()));
			}
			senseAdders.addObject(power);
		}
		power = new ActiveSonar(xml);
		if (power.includedInTemplate()) {
			if (power.findObjectByID(senses.getObjects(), power.getXMLID()) != null) {
				senses.getObjects().remove(
						power.findObjectByID(senses.getObjects(), power
								.getXMLID()));
			}
			senses.addObject(power);
		}
		power = new AnalyzeSense(xml);
		if (power.includedInTemplate()) {
			if (power
					.findObjectByID(senseAdders.getObjects(), power.getXMLID()) != null) {
				senseAdders.getObjects().remove(
						power.findObjectByID(senseAdders.getObjects(), power
								.getXMLID()));
			}
			senseAdders.addObject(power);
		}
		power = new Detect(xml);
		if (power.includedInTemplate()) {
			if (power.findObjectByID(senses.getObjects(), power.getXMLID()) != null) {
				senses.getObjects().remove(
						power.findObjectByID(senses.getObjects(), power
								.getXMLID()));
			}
			senses.addObject(power);
		}
		power = new AdjacentFixed(xml);
		if (power.includedInTemplate()) {
			if (power
					.findObjectByID(senseAdders.getObjects(), power.getXMLID()) != null) {
				senseAdders.getObjects().remove(
						power.findObjectByID(senseAdders.getObjects(), power
								.getXMLID()));
			}
			senseAdders.addObject(power);
		}
		power = new Adjacent(xml);
		if (power.includedInTemplate()) {
			if (power
					.findObjectByID(senseAdders.getObjects(), power.getXMLID()) != null) {
				senseAdders.getObjects().remove(
						power.findObjectByID(senseAdders.getObjects(), power
								.getXMLID()));
			}
			senseAdders.addObject(power);
		}
		power = new Concealed(xml);
		if (power.includedInTemplate()) {
			if (power
					.findObjectByID(senseAdders.getObjects(), power.getXMLID()) != null) {
				senseAdders.getObjects().remove(
						power.findObjectByID(senseAdders.getObjects(), power
								.getXMLID()));
			}
			senseAdders.addObject(power);
		}
		power = new DimensionalSingle(xml);
		if (power.includedInTemplate()) {
			if (power
					.findObjectByID(senseAdders.getObjects(), power.getXMLID()) != null) {
				senseAdders.getObjects().remove(
						power.findObjectByID(senseAdders.getObjects(), power
								.getXMLID()));
			}
			senseAdders.addObject(power);
		}
		power = new DimensionalGroup(xml);
		if (power.includedInTemplate()) {
			if (power
					.findObjectByID(senseAdders.getObjects(), power.getXMLID()) != null) {
				senseAdders.getObjects().remove(
						power.findObjectByID(senseAdders.getObjects(), power
								.getXMLID()));
			}
			senseAdders.addObject(power);
		}
		power = new DimensionalAll(xml);
		if (power.includedInTemplate()) {
			if (power
					.findObjectByID(senseAdders.getObjects(), power.getXMLID()) != null) {
				senseAdders.getObjects().remove(
						power.findObjectByID(senseAdders.getObjects(), power
								.getXMLID()));
			}
			senseAdders.addObject(power);
		}
		power = new DiscriminatorySense(xml);
		if (power.includedInTemplate()) {
			if (power
					.findObjectByID(senseAdders.getObjects(), power.getXMLID()) != null) {
				senseAdders.getObjects().remove(
						power.findObjectByID(senseAdders.getObjects(), power
								.getXMLID()));
			}
			senseAdders.addObject(power);
		}
		power = new EnhancedPerception(xml);
		if (power.includedInTemplate()) {
			if (power.findObjectByID(senses.getObjects(), power.getXMLID()) != null) {
				senses.getObjects().remove(
						power.findObjectByID(senses.getObjects(), power
								.getXMLID()));
			}
			senses.addObject(power);
		}
		power = new HighRangeRadioPerception(xml);
		if (power.includedInTemplate()) {
			if (power.findObjectByID(senses.getObjects(), power.getXMLID()) != null) {
				senses.getObjects().remove(
						power.findObjectByID(senses.getObjects(), power
								.getXMLID()));
			}
			senses.addObject(power);
		}
		power = new IncreasedArc240(xml);
		if (power.includedInTemplate()) {
			if (power
					.findObjectByID(senseAdders.getObjects(), power.getXMLID()) != null) {
				senseAdders.getObjects().remove(
						power.findObjectByID(senseAdders.getObjects(), power
								.getXMLID()));
			}
			senseAdders.addObject(power);
		}
		power = new IncreasedArc360(xml);
		if (power.includedInTemplate()) {
			if (power.findObjectByID(senses.getObjects(), power.getXMLID()) != null) {
				senseAdders.getObjects().remove(
						power.findObjectByID(senseAdders.getObjects(), power
								.getXMLID()));
			}
			senseAdders.addObject(power);
		}
		power = new InfraredPerception(xml);
		if (power.includedInTemplate()) {
			if (power.findObjectByID(senses.getObjects(), power.getXMLID()) != null) {
				senses.getObjects().remove(
						power.findObjectByID(senses.getObjects(), power
								.getXMLID()));
			}
			senses.addObject(power);
		}
		power = new MakeASense(xml);
		if (power.includedInTemplate()) {
			if (power
					.findObjectByID(senseAdders.getObjects(), power.getXMLID()) != null) {
				senseAdders.getObjects().remove(
						power.findObjectByID(senseAdders.getObjects(), power
								.getXMLID()));
			}
			senseAdders.addObject(power);
		}
		power = new MentalAwareness(xml);
		if (power.includedInTemplate()) {
			if (power.findObjectByID(senses.getObjects(), power.getXMLID()) != null) {
				senses.getObjects().remove(
						power.findObjectByID(senses.getObjects(), power
								.getXMLID()));
			}
			senses.addObject(power);
		}
		power = new Microscopic(xml);
		if (power.includedInTemplate()) {
			if (power
					.findObjectByID(senseAdders.getObjects(), power.getXMLID()) != null) {
				senseAdders.getObjects().remove(
						power.findObjectByID(senseAdders.getObjects(), power
								.getXMLID()));
			}
			senseAdders.addObject(power);
		}
		power = new PartiallyPenetrative(xml);
		if (power.includedInTemplate()) {
			if (power
					.findObjectByID(senseAdders.getObjects(), power.getXMLID()) != null) {
				senseAdders.getObjects().remove(
						power.findObjectByID(senseAdders.getObjects(), power
								.getXMLID()));
			}
			senseAdders.addObject(power);
		}
		power = new Penetrative(xml);
		if (power.includedInTemplate()) {
			if (power
					.findObjectByID(senseAdders.getObjects(), power.getXMLID()) != null) {
				senseAdders.getObjects().remove(
						power.findObjectByID(senseAdders.getObjects(), power
								.getXMLID()));
			}
			senseAdders.addObject(power);
		}
		power = new Nightvision(xml);
		if (power.includedInTemplate()) {
			if (power.findObjectByID(senses.getObjects(), power.getXMLID()) != null) {
				senses.getObjects().remove(
						power.findObjectByID(senses.getObjects(), power
								.getXMLID()));
			}
			senses.addObject(power);
		}

		power = new NRayPerception(xml);
		if (power.includedInTemplate()) {
			if (power.findObjectByID(senses.getObjects(), power.getXMLID()) != null) {
				senses.getObjects().remove(
						power.findObjectByID(senses.getObjects(), power
								.getXMLID()));
			}
			senses.addObject(power);
		}
		power = new Radar(xml);
		if (power.includedInTemplate()) {
			if (power.findObjectByID(senses.getObjects(), power.getXMLID()) != null) {
				senses.getObjects().remove(
						power.findObjectByID(senses.getObjects(), power
								.getXMLID()));
			}
			senses.addObject(power);
		}
		power = new RadioPerceiveTransmit(xml);
		if (power.includedInTemplate()) {
			if (power.findObjectByID(senses.getObjects(), power.getXMLID()) != null) {
				senses.getObjects().remove(
						power.findObjectByID(senses.getObjects(), power
								.getXMLID()));
			}
			senses.addObject(power);
		}
		power = new RadioPerception(xml);
		if (power.includedInTemplate()) {
			if (power.findObjectByID(senses.getObjects(), power.getXMLID()) != null) {
				senses.getObjects().remove(
						power.findObjectByID(senses.getObjects(), power
								.getXMLID()));
			}
			senses.addObject(power);
		}
		power = new Range(xml);
		if (power.includedInTemplate()) {
			if (power
					.findObjectByID(senseAdders.getObjects(), power.getXMLID()) != null) {
				senseAdders.getObjects().remove(
						power.findObjectByID(senseAdders.getObjects(), power
								.getXMLID()));
			}
			senseAdders.addObject(power);
		}
		power = new Rapid(xml);
		if (power.includedInTemplate()) {
			if (power
					.findObjectByID(senseAdders.getObjects(), power.getXMLID()) != null) {
				senseAdders.getObjects().remove(
						power.findObjectByID(senseAdders.getObjects(), power
								.getXMLID()));
			}
			senseAdders.addObject(power);
		}
		power = new SpatialAwareness(xml);
		if (power.includedInTemplate()) {
			if (power.findObjectByID(senses.getObjects(), power.getXMLID()) != null) {
				senses.getObjects().remove(
						power.findObjectByID(senses.getObjects(), power
								.getXMLID()));
			}
			senses.addObject(power);
		}
		power = new TargetingSense(xml);
		if (power.includedInTemplate()) {
			if (power
					.findObjectByID(senseAdders.getObjects(), power.getXMLID()) != null) {
				senseAdders.getObjects().remove(
						power.findObjectByID(senseAdders.getObjects(), power
								.getXMLID()));
			}
			senseAdders.addObject(power);
		}
		power = new Telescopic(xml);
		if (power.includedInTemplate()) {
			if (power
					.findObjectByID(senseAdders.getObjects(), power.getXMLID()) != null) {
				senseAdders.getObjects().remove(
						power.findObjectByID(senseAdders.getObjects(), power
								.getXMLID()));
			}
			senseAdders.addObject(power);
		}
		power = new TrackingSense(xml);
		if (power.includedInTemplate()) {
			if (power
					.findObjectByID(senseAdders.getObjects(), power.getXMLID()) != null) {
				senseAdders.getObjects().remove(
						power.findObjectByID(senseAdders.getObjects(), power
								.getXMLID()));
			}
			senseAdders.addObject(power);
		}
		power = new Transmit(xml);
		if (power.includedInTemplate()) {
			if (power
					.findObjectByID(senseAdders.getObjects(), power.getXMLID()) != null) {
				senseAdders.getObjects().remove(
						power.findObjectByID(senseAdders.getObjects(), power
								.getXMLID()));
			}
			senseAdders.addObject(power);
		}
		power = new UltrasonicPerception(xml);
		if (power.includedInTemplate()) {
			if (power.findObjectByID(senses.getObjects(), power.getXMLID()) != null) {
				senses.getObjects().remove(
						power.findObjectByID(senses.getObjects(), power
								.getXMLID()));
			}
			senses.addObject(power);
		}
		power = new UltravioletPerception(xml);
		if (power.includedInTemplate()) {
			if (power.findObjectByID(senses.getObjects(), power.getXMLID()) != null) {
				senses.getObjects().remove(
						power.findObjectByID(senses.getObjects(), power
								.getXMLID()));
			}
			senses.addObject(power);
		}
		if (senses.getObjects().size() > 0) {
			Collections.sort(senses.getObjects());
			Collections.sort(senseAdders.getObjects());
			if (senseAdders.getObjects().size() > 0) {
				senses.addObject(senseAdders);
			}
			powers.add(senses);
		}
		power = new NoHitLocations(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new Entangle(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new ExtraDimensionalMovement(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new ExtraLimbs(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new FTLTravel(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new FindWeakness(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new Flash(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new FlashDefense(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new Flight(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new ForceField(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new ForceWall(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new Gliding(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new Growth(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new HandToHandAttack(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new Healing(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new Images(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new Invisibility(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new KBResistance(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new KillingAttackHTH(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new KillingAttackRanged(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new LackOfWeakness(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new LifeSupport(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new Luck(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new MentalDefense(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new MentalIllusions(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new MindControl(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new MindLink(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new MindScan(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new MissileDeflection(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new Multiform(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new NakedModifier(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new DifferingModifier(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new Possession(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new PowerDefense(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new Reflection(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new Regeneration(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new Shapeshift(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new Shrinking(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new Stretching(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new Succor(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new Summon(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new Suppress(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new Swinging(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new Telekinesis(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new Telepathy(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new Teleportation(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new FloatingLocation(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new FixedLocation(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new Transfer(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new Transform(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}
		power = new Tunneling(xml);
		if (power.includedInTemplate()) {
			addToVector(power, powers);
		}

		for (int i = powers.size() - 1; i >= 0; i--) {
			GenericObject o = powers.get(i);
			if (o instanceof List) {
				String display = o.getDisplay();
				if (display.equals("Skills") || display.equals("Perks")
						|| display.equals("Talents")
						|| display.equals("Characteristics")) {
					powers.remove(o);
				}
			}
		}

		// make sure we work the removals before this point....
		com.hero.objects.List sks = new com.hero.objects.List("Skills");
		sks.setObjects((ArrayList<GenericObject>) skills.clone());
		powers.add(sks);
		com.hero.objects.List pks = new com.hero.objects.List("Perks");
		pks.setObjects((ArrayList<GenericObject>) perks.clone());
		powers.add(pks);
		com.hero.objects.List tls = new com.hero.objects.List("Talents");
		tls.setObjects((ArrayList<GenericObject>) talents.clone());
		powers.add(tls);
		// clean up....so we don't have repeats....
		for (int i = powers.size() - 1; i >= 0; i--) {
			GenericObject o = powers.get(i);
			if (o instanceof com.hero.objects.List) {
				com.hero.objects.List l = (com.hero.objects.List) o;
				if (l.getDisplay().equals("Enhanced Senses")) {
					continue; // we take care of these separately...
				}
				powers.remove(i);
			} else if (o instanceof Characteristic) {
				powers.remove(i);
			}
		}
		com.hero.objects.List chs = new com.hero.objects.List("Characteristics");
		for (int i = 0; i < characteristics.size(); i++) {
			Characteristic ch = (Characteristic) characteristics.get(i);
			ch = (Characteristic) ch.clone();
			ch.setLevels(0);
			ch.setPower(true);

			if ((ch.getType() == Constants.RUNNING)
					|| (ch.getType() == Constants.LEAPING)
					|| (ch.getType() == Constants.SWIMMING)) {
				powers.add(ch);
			} else {
				chs.addObject(ch);
			}
		}
		if (chs.getObjects().size() > 0) {
			powers.add(chs);
		}
		chs = new com.hero.objects.List("Skills");
		for (GenericObject ch : skills) {
			ch = ch.clone();
			ch.setLevels(0);
			ch.setPower(true);
			chs.addObject(ch);
		}
		powers.add(chs);
		chs = new com.hero.objects.List("Perks");
		for (GenericObject ch : perks) {
			ch = ch.clone();
			ch.setLevels(0);
			ch.setPower(true);
			chs.addObject(ch);
		}
		powers.add(chs);
		chs = new com.hero.objects.List("Talents");
		for (GenericObject ch : talents) {
			ch = ch.clone();
			ch.setLevels(0);
			ch.setPower(true);
			chs.addObject(ch);
		}
		powers.add(chs);
		Collections.sort(powers, new Comparator() {
			public int compare(Object o1, Object o2) {
				return o1.toString().compareTo(o2.toString());
			}

			public boolean equals(Object o) {
				return false;
			}
		});

		if (!HeroDesigner.headless) {
			HeroDesigner.loadProgress
					.setString("Loading template...reading disads section");
		}
		// init disads
		xml = root.getChild("DISADVANTAGES");
		if (xml != null) {
			iter = xml.getChildren("REMOVE").iterator();
			while (iter.hasNext()) {
				Element remove = (Element) iter.next();
				String id = remove.getTextTrim();
				if (id.trim().length() > 0) {
					removeFromVector(id, disads);
				}
			}
			list = xml.getChildren("DISAD");
			iterator = list.iterator();
			while (iterator.hasNext()) {
				Element disad = (Element) iterator.next();
				Disadvantage d = Disadvantage.getInstance(disad);
				if (d.includedInTemplate()) {
					addToVector(d, disads);
				}
			}
		}
		Collections.sort(disads, new Comparator() {
			public int compare(Object o1, Object o2) {
				return o1.toString().compareTo(o2.toString());
			}

			public boolean equals(Object o) {
				return false;
			}
		});

		Element rules = root.getChild("RULES");
		if (rules != null) {
			Rules r = new Rules();
			r.restoreRules(rules);
		}

		java.util.List l = root.getChildren("PREFAB");
		prefabs = new ArrayList<Prefab>();
		if ((l != null) && (l.size() > 0)) {
			for (int i = 0; i < l.size(); i++) {
				Prefab p = new Prefab((Element) l.get(i), this);
				prefabs.add(p);
			}
		}
	}

	public boolean isBackgroundAllowed() {
		return backgroundAllowed;
	}

	public boolean isEyeColorAllowed() {
		if ((eyeColorLabel == null) || (eyeColorLabel.trim().length() == 0)) {
			return false;
		}
		return eyeColorAllowed;
	}

	public boolean isHairColorAllowed() {
		if ((hairColorLabel == null) || (hairColorLabel.trim().length() == 0)) {
			return false;
		}
		return hairColorAllowed;
	}

	public boolean isHeightAllowed() {
		return heightAllowed;
	}

	public boolean isVersion1Style() {
		return (parents == null) || (parents.size() == 0);
	}

	public boolean isWeightAllowed() {
		return weightAllowed;
	}

	private void removeFromVector(String xmlID, ArrayList<GenericObject> vec) {
		for (int i = vec.size() - 1; i >= 0; i--) {
			GenericObject o = vec.get(i);
			if (o.getXMLID().equalsIgnoreCase(xmlID.trim())) {
				vec.remove(i);
			} else if (o instanceof List) {
				List l = (List) o;
				removeFromVector(xmlID, l.getObjects());
			}
		}
	}

}