package org.concord.energy2d.system;

import java.awt.Color;
import java.awt.Font;
import java.awt.geom.Rectangle2D;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;

import javax.imageio.ImageIO;
import javax.xml.bind.DatatypeConverter;

import org.concord.energy2d.model.Anemometer;
import org.concord.energy2d.model.Boundary;
import org.concord.energy2d.model.Cloud;
import org.concord.energy2d.model.Constants;
import org.concord.energy2d.model.DirichletThermalBoundary;
import org.concord.energy2d.model.Fan;
import org.concord.energy2d.model.HeatFluxSensor;
import org.concord.energy2d.model.Heliostat;
import org.concord.energy2d.model.MassBoundary;
import org.concord.energy2d.model.Particle;
import org.concord.energy2d.model.ParticleFeeder;
import org.concord.energy2d.model.SimpleMassBoundary;
import org.concord.energy2d.model.ThermalBoundary;
import org.concord.energy2d.model.Model2D;
import org.concord.energy2d.model.NeumannThermalBoundary;
import org.concord.energy2d.model.Part;
import org.concord.energy2d.model.Thermometer;
import org.concord.energy2d.model.Thermostat;
import org.concord.energy2d.model.Tree;
import org.concord.energy2d.util.ColorFill;
import org.concord.energy2d.util.Scripter;
import org.concord.energy2d.util.Texture;
import org.concord.energy2d.util.XmlCharacterDecoder;
import org.concord.energy2d.view.Picture;
import org.concord.energy2d.view.TextBox;
import org.concord.energy2d.view.View2D;
import org.xml.sax.Attributes;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;

/**
 * @author Charles Xie
 * 
 */
class XmlDecoder extends DefaultHandler {

	private System2D box;
	private String str;

	// model properties
	private float modelWidth = 10;
	private float modelHeight = 10;
	private float timeStep = 0.1f;
	private int measurementInterval = 100;
	private int controlInterval = 100;
	private int viewUpdateInterval = 20;
	private boolean sunny;
	private float sunAngle = (float) (Math.PI * 0.5);
	private float solarPowerDensity = 2000;
	private int solarRayCount = 24;
	private float solarRaySpeed = 0.1f;
	private int photonEmissionInterval = 20;
	private float perimeterStepSize = 0.05f;
	private boolean convective = true;
	private float zHeatDiffusivity;
	private boolean zHeatDiffusivityOnlyForFluid;
	private float gravitationalAcceleration = -1;
	private float thermophoreticCoefficient = 0;
	private float particleDrag = -1;
	private float particleHardness = -1;
	private float backgroundConductivity = Constants.AIR_THERMAL_CONDUCTIVITY;
	private float backgroundDensity = Constants.AIR_DENSITY;
	private float backgroundSpecificHeat = Constants.AIR_SPECIFIC_HEAT;
	private float backgroundViscosity = Constants.AIR_VISCOSITY;
	private float backgroundTemperature;
	private float thermalExpansionCoefficient = 0.00025f;
	private byte buoyancyApproximation = Model2D.BUOYANCY_AVERAGE_COLUMN;
	private byte gravityType = Model2D.GRAVITY_UNIFORM;
	private String nextSim, prevSim;

	// view properties
	private byte graphDataType, graphTimeUnit;
	private boolean fahrenheitUsed;
	private boolean viewFactorLines;
	private boolean borderTickmarks;
	private boolean grid;
	private boolean snapToGrid = true;
	private boolean isotherm;
	private boolean streamline;
	private boolean colorPalette;
	private byte colorPaletteType = View2D.RAINBOW;
	private boolean showLogo = true;
	private boolean controlPanel;
	private byte controlPanelPosition = 0;
	private byte heatMapType = View2D.HEATMAP_TEMPERATURE;
	private float colorPaletteX, colorPaletteY, colorPaletteW, colorPaletteH;
	private int gridSize = 10;
	private boolean velocity;
	private boolean heatFluxArrows;
	private boolean heatFluxLines;
	private boolean graphOn;
	private boolean clock = true;
	private boolean smooth = true;
	private float minimumTemperature;
	private float maximumTemperature = 50;
	private float fanRotationSpeedScaleFactor = 1;
	private String graphXLabel, graphYLabel;
	private float graphYmin = 0, graphYmax = 50;

	// discrete properties
	private String uid;
	private String label;

	// part properties
	private float partThermalConductivity = Float.NaN;
	private float partSpecificHeat = Float.NaN;
	private float partDensity = Float.NaN;
	private float partEmissivity = Float.NaN;
	private float partAbsorption = Float.NaN;
	private float partReflection = Float.NaN;
	private float partElasticity = 1;
	private boolean partScattering;
	private boolean partScatteringVisible = true;
	private float partTransmission = Float.NaN;
	private float temperature = Float.NaN;
	private float fanSpeed;
	private float fanAngle;
	private boolean partConstantTemperature = false;
	private float partPower = Float.NaN;
	private float partTemperatureCoefficient = 0;
	private float partReferenceTemperature = 0;
	private boolean partVisible = true;
	private boolean draggable = true;
	private boolean movable = true;
	private Color color;
	private Color velocityColor;
	private byte textureStyle;
	private int textureWidth = 10;
	private int textureHeight = 10;
	private Color textureForeground = Color.BLACK;
	private Color textureBackground = Color.WHITE;
	private boolean partFilled = true;
	private Part part;

	// particle properties
	private float particleRx = Float.NaN;
	private float particleRy = Float.NaN;
	private float particleVx = Float.NaN;
	private float particleVy = Float.NaN;
	private float particleTheta = Float.NaN;
	private float particleOmega = Float.NaN;
	private float particleRadius = Float.NaN;
	private float particleMass = Float.NaN;
	private Particle particle;

	XmlDecoder(System2D box) {
		this.box = box;
	}

	public void startDocument() {
		box.taskManager.clearCustomTasks();
		// reset for elements added later before XML was saved
		MassBoundary b = box.model.getMassBoundary();
		if (b instanceof SimpleMassBoundary) {
			SimpleMassBoundary smb = (SimpleMassBoundary) b;
			smb.setFlowTypeAtBorder(Boundary.LEFT, MassBoundary.REFLECTIVE);
			smb.setFlowTypeAtBorder(Boundary.RIGHT, MassBoundary.REFLECTIVE);
			smb.setFlowTypeAtBorder(Boundary.LOWER, MassBoundary.REFLECTIVE);
			smb.setFlowTypeAtBorder(Boundary.UPPER, MassBoundary.REFLECTIVE);
		}
	}

	public void endDocument() {

		box.view.setControlPanelVisible(controlPanel);
		box.view.setControlPanelPosition(controlPanelPosition);
		box.setPreviousSimulation(prevSim);
		box.setNextSimulation(nextSim);
		box.model.setLx(modelWidth);
		box.model.setLy(modelHeight);
		box.view.setArea(0, modelWidth, 0, modelHeight);
		box.model.setTimeStep(timeStep);
		box.measure.setInterval(measurementInterval);
		box.control.setInterval(controlInterval);
		box.repaint.setInterval(viewUpdateInterval);
		box.model.setSunny(sunny);
		box.model.setSunAngle(sunAngle);
		box.model.setSolarPowerDensity(solarPowerDensity);
		box.model.setSolarRayCount(solarRayCount);
		box.model.setSolarRaySpeed(solarRaySpeed);
		box.model.setPhotonEmissionInterval(photonEmissionInterval);
		box.model.setPerimeterStepSize(perimeterStepSize);
		box.model.setConvective(convective);
		box.model.setZHeatDiffusivity(zHeatDiffusivity);
		box.model.setZHeatDiffusivityOnlyForFluid(zHeatDiffusivityOnlyForFluid);
		if (gravitationalAcceleration >= 0)
			box.model.setGravitationalAcceleration(gravitationalAcceleration);
		box.model.setThermophoreticCoefficient(thermophoreticCoefficient);
		if (particleDrag >= 0)
			box.model.setParticleDrag(particleDrag);
		if (particleHardness >= 0)
			box.model.setParticleHardness(particleHardness);
		box.model.setBackgroundConductivity(backgroundConductivity);
		box.model.setBackgroundDensity(backgroundDensity);
		box.model.setBackgroundSpecificHeat(backgroundSpecificHeat);
		box.model.setBackgroundTemperature(backgroundTemperature);
		box.model.setBackgroundViscosity(backgroundViscosity);
		box.model.setThermalExpansionCoefficient(thermalExpansionCoefficient);
		box.model.setBuoyancyApproximation(buoyancyApproximation);
		box.model.setGravityType(gravityType);

		box.view.setGraphDataType(graphDataType);
		box.view.setGraphTimeUnit(graphTimeUnit);
		box.view.setFahrenheitUsed(fahrenheitUsed);
		box.view.setViewFactorLinesOn(viewFactorLines);
		box.view.setBorderTickmarksOn(borderTickmarks);
		box.view.setGridOn(grid);
		box.view.setSnapToGrid(snapToGrid);
		box.view.setGridSize(gridSize);
		box.view.setIsothermOn(isotherm);
		box.view.setStreamlineOn(streamline);
		box.view.setVelocityOn(velocity);
		box.view.setHeatFluxArrowsOn(heatFluxArrows);
		box.view.setHeatFluxLinesOn(heatFluxLines);
		box.view.setColorPaletteOn(colorPalette);
		box.view.setColorPaletteType(colorPaletteType);
		box.view.setShowLogo(showLogo);
		box.view.setHeatMapType(heatMapType);
		float xColorPalette = colorPaletteX > 1 ? colorPaletteX / box.view.getWidth() : colorPaletteX;
		float yColorPalette = colorPaletteY > 1 ? colorPaletteY / box.view.getHeight() : colorPaletteY;
		float wColorPalette = colorPaletteW > 1 ? colorPaletteW / box.view.getWidth() : colorPaletteW;
		float hColorPalette = colorPaletteH > 1 ? colorPaletteH / box.view.getHeight() : colorPaletteH;
		box.view.setColorPaletteRectangle(xColorPalette, yColorPalette, wColorPalette, hColorPalette);
		box.view.setMinimumTemperature(minimumTemperature);
		box.view.setMaximumTemperature(maximumTemperature);
		box.view.setFanRotationSpeedScaleFactor(fanRotationSpeedScaleFactor);
		box.view.setClockOn(clock);
		box.view.setSmooth(smooth);
		box.view.setGraphOn(graphOn);
		if (graphXLabel != null)
			box.view.setGraphXLabel(graphXLabel);
		if (graphYLabel != null)
			box.view.setGraphYLabel(graphYLabel);
		if (graphYmin != 0)
			box.view.setGraphYmin(graphYmin);
		if (graphYmax != 50)
			box.view.setGraphYmax(graphYmax);

		box.model.refreshPowerArray();
		box.model.refreshTemperatureBoundaryArray();
		box.model.refreshMaterialPropertyArrays();
		box.model.setInitialTemperature();
		if (box.model.isRadiative())
			box.model.generateViewFactorMesh();

		// since we don't know the width and height of the model until now, we have to fix the locations and the sizes of
		// the sensors, since they are relative to the size of the model.
		List<Thermometer> thermometers = box.model.getThermometers();
		if (!thermometers.isEmpty()) {
			synchronized (thermometers) {
				for (Thermometer t : thermometers) {
					Rectangle2D.Float r = (Rectangle2D.Float) t.getShape();
					r.width = Thermometer.RELATIVE_WIDTH * modelWidth;
					r.height = Thermometer.RELATIVE_HEIGHT * modelHeight;
					r.x = r.x - 0.5f * r.width;
					r.y = r.y - 0.5f * r.height;
				}
			}
		}
		List<HeatFluxSensor> heatFluxSensors = box.model.getHeatFluxSensors();
		if (!heatFluxSensors.isEmpty()) {
			synchronized (heatFluxSensors) {
				for (HeatFluxSensor h : heatFluxSensors) {
					Rectangle2D.Float r = (Rectangle2D.Float) h.getShape();
					r.width = HeatFluxSensor.RELATIVE_WIDTH * modelWidth;
					r.height = HeatFluxSensor.RELATIVE_HEIGHT * modelHeight;
					r.x = r.x - 0.5f * r.width;
					r.y = r.y - 0.5f * r.height;
					box.model.measure(h);
				}
			}
		}
		List<Anemometer> anemometers = box.model.getAnemometers();
		if (!anemometers.isEmpty()) {
			synchronized (anemometers) {
				for (Anemometer a : anemometers) {
					Rectangle2D.Float r = (Rectangle2D.Float) a.getShape();
					r.width = Anemometer.RELATIVE_WIDTH * modelWidth;
					r.height = Anemometer.RELATIVE_HEIGHT * modelHeight;
					r.x = r.x - 0.5f * r.width;
					r.y = r.y - 0.5f * r.height;
				}
			}
		}

		box.view.repaint();

		resetGlobalVariables();

	}

	public void startElement(String uri, String localName, String qName, Attributes attrib) {

		str = null;

		String attribName, attribValue;

		if (qName == "task") {
			if (attrib != null) {
				boolean enabled = true;
				int interval = -1, lifetime = Task.PERMANENT, priority = 1;
				String uid = null, description = null, script = null;
				for (int i = 0, n = attrib.getLength(); i < n; i++) {
					attribName = attrib.getQName(i).intern();
					attribValue = attrib.getValue(i);
					if (attribName == "interval") {
						interval = Integer.parseInt(attribValue);
					} else if (attribName == "lifetime") {
						lifetime = Integer.parseInt(attribValue);
					} else if (attribName == "priority") {
						priority = Integer.parseInt(attribValue);
					} else if (attribName == "uid") {
						uid = attribValue;
					} else if (attribName == "description") {
						description = attribValue;
					} else if (attribName == "script") {
						script = attribValue;
					} else if (attribName == "enabled") {
						enabled = Boolean.parseBoolean(attribValue);
					}
				}
				Task t = new Task(uid, interval, lifetime) {
					@Override
					public void execute() {
						if (getScript() != null)
							box.taskManager.runScript(getScript());
					}
				};
				t.setSystemTask(false);
				t.setEnabled(enabled);
				t.setScript(new XmlCharacterDecoder().decode(script));
				t.setDescription(new XmlCharacterDecoder().decode(description));
				t.setPriority(priority);
				box.taskManager.add(t);
				box.taskManager.processPendingRequests();
			}
		} else if (qName == "rectangle") {
			if (attrib != null) {
				float x = Float.NaN, y = Float.NaN, w = Float.NaN, h = Float.NaN;
				for (int i = 0, n = attrib.getLength(); i < n; i++) {
					attribName = attrib.getQName(i).intern();
					attribValue = attrib.getValue(i);
					if (attribName == "x") {
						x = Float.parseFloat(attribValue);
					} else if (attribName == "y") {
						y = Float.parseFloat(attribValue);
					} else if (attribName == "width") {
						w = Float.parseFloat(attribValue);
					} else if (attribName == "height") {
						h = Float.parseFloat(attribValue);
					}
				}
				if (!Float.isNaN(x) && !Float.isNaN(y) && !Float.isNaN(w) && !Float.isNaN(h))
					part = box.model.addRectangularPart(x, y, w, h);
			}
		} else if (qName == "ellipse") {
			if (attrib != null) {
				float x = Float.NaN, y = Float.NaN, a = Float.NaN, b = Float.NaN;
				for (int i = 0, n = attrib.getLength(); i < n; i++) {
					attribName = attrib.getQName(i).intern();
					attribValue = attrib.getValue(i);
					if (attribName == "x") {
						x = Float.parseFloat(attribValue);
					} else if (attribName == "y") {
						y = Float.parseFloat(attribValue);
					} else if (attribName == "a") {
						a = Float.parseFloat(attribValue);
					} else if (attribName == "b") {
						b = Float.parseFloat(attribValue);
					}
				}
				if (!Float.isNaN(x) && !Float.isNaN(y) && !Float.isNaN(a) && !Float.isNaN(b))
					part = box.model.addEllipticalPart(x, y, a, b);
			}
		} else if (qName == "ring") {
			if (attrib != null) {
				float x = Float.NaN, y = Float.NaN, inner = Float.NaN, outer = Float.NaN;
				for (int i = 0, n = attrib.getLength(); i < n; i++) {
					attribName = attrib.getQName(i).intern();
					attribValue = attrib.getValue(i);
					if (attribName == "x") {
						x = Float.parseFloat(attribValue);
					} else if (attribName == "y") {
						y = Float.parseFloat(attribValue);
					} else if (attribName == "inner") {
						inner = Float.parseFloat(attribValue);
					} else if (attribName == "outer") {
						outer = Float.parseFloat(attribValue);
					}
				}
				if (!Float.isNaN(x) && !Float.isNaN(y) && !Float.isNaN(inner) && !Float.isNaN(outer))
					part = box.model.addRingPart(x, y, inner, outer);
			}
		} else if (qName == "annulus") {
			if (attrib != null) {
				float x = Float.NaN, y = Float.NaN, innerA = Float.NaN, innerB = Float.NaN, outerA = Float.NaN, outerB = Float.NaN;
				for (int i = 0, n = attrib.getLength(); i < n; i++) {
					attribName = attrib.getQName(i).intern();
					attribValue = attrib.getValue(i);
					if (attribName == "x") {
						x = Float.parseFloat(attribValue);
					} else if (attribName == "y") {
						y = Float.parseFloat(attribValue);
					} else if (attribName == "innerA") {
						innerA = Float.parseFloat(attribValue);
					} else if (attribName == "innerB") {
						innerB = Float.parseFloat(attribValue);
					} else if (attribName == "outerA") {
						outerA = Float.parseFloat(attribValue);
					} else if (attribName == "outerB") {
						outerB = Float.parseFloat(attribValue);
					}
				}
				if (!Float.isNaN(x) && !Float.isNaN(y) && !Float.isNaN(innerA) && !Float.isNaN(innerB) && !Float.isNaN(outerA) && !Float.isNaN(outerB))
					part = box.model.addAnnulusPart(x, y, innerA, innerB, outerA, outerB);
			}
		} else if (qName == "polygon") {
			if (attrib != null) {
				int count = -1;
				String vertices = null;
				for (int i = 0, n = attrib.getLength(); i < n; i++) {
					attribName = attrib.getQName(i).intern();
					attribValue = attrib.getValue(i);
					if (attribName == "count") {
						count = Integer.parseInt(attribValue);
					} else if (attribName == "vertices") {
						vertices = attribValue;
					}
				}
				if (count > 0 && vertices != null) {
					float[] v = Scripter.parseArray(count * 2, vertices);
					float[] x = new float[count];
					float[] y = new float[count];
					for (int i = 0; i < count; i++) {
						x[i] = v[2 * i];
						y[i] = v[2 * i + 1];
					}
					part = box.model.addPolygonPart(x, y);
				}
			}
		} else if (qName == "blob") {
			if (attrib != null) {
				int count = -1;
				String points = null;
				for (int i = 0, n = attrib.getLength(); i < n; i++) {
					attribName = attrib.getQName(i).intern();
					attribValue = attrib.getValue(i);
					if (attribName == "count") {
						count = Integer.parseInt(attribValue);
					} else if (attribName == "points") {
						points = attribValue;
					}
				}
				if (count > 0 && points != null) {
					float[] v = Scripter.parseArray(count * 2, points);
					float[] x = new float[count];
					float[] y = new float[count];
					for (int i = 0; i < count; i++) {
						x[i] = v[2 * i];
						y[i] = v[2 * i + 1];
					}
					part = box.model.addBlobPart(x, y);
				}
			}
		} else if (qName == "particle") {
			particle = new Particle();
			box.model.addParticle(particle);
		} else if (qName == "temperature_at_border") {
			if (attrib != null) {
				float left = Float.NaN, right = Float.NaN, upper = Float.NaN, lower = Float.NaN;
				for (int i = 0, n = attrib.getLength(); i < n; i++) {
					attribName = attrib.getQName(i).intern();
					attribValue = attrib.getValue(i);
					if (attribName == "left") {
						left = Float.parseFloat(attribValue);
					} else if (attribName == "right") {
						right = Float.parseFloat(attribValue);
					} else if (attribName == "upper") {
						upper = Float.parseFloat(attribValue);
					} else if (attribName == "lower") {
						lower = Float.parseFloat(attribValue);
					}
				}
				if (!Float.isNaN(left) && !Float.isNaN(right) && !Float.isNaN(upper) && !Float.isNaN(lower)) {
					DirichletThermalBoundary b = null;
					ThermalBoundary boundary = box.model.getThermalBoundary();
					if (boundary instanceof DirichletThermalBoundary) {
						b = (DirichletThermalBoundary) boundary;
					} else {
						b = new DirichletThermalBoundary();
						box.model.setThermalBoundary(b);
					}
					b.setTemperatureAtBorder(Boundary.UPPER, upper);
					b.setTemperatureAtBorder(Boundary.RIGHT, right);
					b.setTemperatureAtBorder(Boundary.LOWER, lower);
					b.setTemperatureAtBorder(Boundary.LEFT, left);
				}
			}
		} else if (qName == "flux_at_border") { // heat flux
			if (attrib != null) {
				float left = Float.NaN, right = Float.NaN, upper = Float.NaN, lower = Float.NaN;
				for (int i = 0, n = attrib.getLength(); i < n; i++) {
					attribName = attrib.getQName(i).intern();
					attribValue = attrib.getValue(i);
					if (attribName == "left") {
						left = Float.parseFloat(attribValue);
					} else if (attribName == "right") {
						right = Float.parseFloat(attribValue);
					} else if (attribName == "upper") {
						upper = Float.parseFloat(attribValue);
					} else if (attribName == "lower") {
						lower = Float.parseFloat(attribValue);
					}
				}
				if (!Float.isNaN(left) && !Float.isNaN(right) && !Float.isNaN(upper) && !Float.isNaN(lower)) {
					NeumannThermalBoundary b = null;
					ThermalBoundary boundary = box.model.getThermalBoundary();
					if (boundary instanceof NeumannThermalBoundary) {
						b = (NeumannThermalBoundary) boundary;
					} else {
						b = new NeumannThermalBoundary();
						box.model.setThermalBoundary(b);
					}
					b.setFluxAtBorder(Boundary.UPPER, upper);
					b.setFluxAtBorder(Boundary.RIGHT, right);
					b.setFluxAtBorder(Boundary.LOWER, lower);
					b.setFluxAtBorder(Boundary.LEFT, left);
				}
			}
		} else if (qName == "mass_flow_at_border") {
			if (attrib != null) {
				byte left = MassBoundary.REFLECTIVE;
				byte right = MassBoundary.REFLECTIVE;
				byte upper = MassBoundary.REFLECTIVE;
				byte lower = MassBoundary.REFLECTIVE;
				for (int i = 0, n = attrib.getLength(); i < n; i++) {
					attribName = attrib.getQName(i).intern();
					attribValue = attrib.getValue(i);
					if (attribName == "left") {
						left = Byte.parseByte(attribValue);
					} else if (attribName == "right") {
						right = Byte.parseByte(attribValue);
					} else if (attribName == "upper") {
						upper = Byte.parseByte(attribValue);
					} else if (attribName == "lower") {
						lower = Byte.parseByte(attribValue);
					}
				}
				SimpleMassBoundary b = (SimpleMassBoundary) box.model.getMassBoundary();
				b.setFlowTypeAtBorder(Boundary.UPPER, upper);
				b.setFlowTypeAtBorder(Boundary.RIGHT, right);
				b.setFlowTypeAtBorder(Boundary.LOWER, lower);
				b.setFlowTypeAtBorder(Boundary.LEFT, left);
			}
		} else if (qName == "thermometer") {
			if (attrib != null) {
				float x = Float.NaN, y = Float.NaN;
				String label = null, uid = null, attachID = null;
				byte stencil = Thermometer.ONE_POINT;
				for (int i = 0, n = attrib.getLength(); i < n; i++) {
					attribName = attrib.getQName(i).intern();
					attribValue = attrib.getValue(i);
					if (attribName == "x") {
						x = Float.parseFloat(attribValue);
					} else if (attribName == "y") {
						y = Float.parseFloat(attribValue);
					} else if (attribName == "stencil") {
						stencil = Byte.parseByte(attribValue);
					} else if (attribName == "uid") {
						uid = attribValue;
					} else if (attribName == "attach") {
						attachID = attribValue;
					} else if (attribName == "label") {
						label = attribValue;
					}
				}
				if (!Float.isNaN(x) && !Float.isNaN(y)) {
					Thermometer t = box.model.addThermometer(x, y, uid, label, stencil);
					if (attachID != null)
						t.setAttachID(attachID);
				}
			}
		} else if (qName == "heat_flux_sensor") {
			if (attrib != null) {
				float x = Float.NaN, y = Float.NaN;
				String label = null, uid = null, attachID = null;
				float angle = 0;
				for (int i = 0, n = attrib.getLength(); i < n; i++) {
					attribName = attrib.getQName(i).intern();
					attribValue = attrib.getValue(i);
					if (attribName == "x") {
						x = Float.parseFloat(attribValue);
					} else if (attribName == "y") {
						y = Float.parseFloat(attribValue);
					} else if (attribName == "angle") {
						angle = Float.parseFloat(attribValue);
					} else if (attribName == "uid") {
						uid = attribValue;
					} else if (attribName == "attach") {
						attachID = attribValue;
					} else if (attribName == "label") {
						label = attribValue;
					}
				}
				if (!Float.isNaN(x) && !Float.isNaN(y)) {
					HeatFluxSensor h = box.model.addHeatFluxSensor(x, y, uid, label, angle);
					if (attachID != null)
						h.setAttachID(attachID);
				}
			}
		} else if (qName == "anemometer") {
			if (attrib != null) {
				float x = Float.NaN, y = Float.NaN;
				String label = null, uid = null, attachID = null;
				byte stencil = Anemometer.ONE_POINT;
				for (int i = 0, n = attrib.getLength(); i < n; i++) {
					attribName = attrib.getQName(i).intern();
					attribValue = attrib.getValue(i);
					if (attribName == "x") {
						x = Float.parseFloat(attribValue);
					} else if (attribName == "y") {
						y = Float.parseFloat(attribValue);
					} else if (attribName == "stencil") {
						stencil = Byte.parseByte(attribValue);
					} else if (attribName == "uid") {
						uid = attribValue;
					} else if (attribName == "attach") {
						attachID = attribValue;
					} else if (attribName == "label") {
						label = attribValue;
					}
				}
				if (!Float.isNaN(x) && !Float.isNaN(y)) {
					Anemometer a = box.model.addAnemometer(x, y, uid, label, stencil);
					if (attachID != null)
						a.setAttachID(attachID);
				}
			}
		} else if (qName == "thermostat") {
			if (attrib != null) {
				float setpoint = Float.NaN, deadband = Float.NaN;
				String thermometerUID = null, powerSourceUID = null;
				for (int i = 0, n = attrib.getLength(); i < n; i++) {
					attribName = attrib.getQName(i).intern();
					attribValue = attrib.getValue(i);
					if (attribName == "set_point") {
						setpoint = Float.parseFloat(attribValue);
					} else if (attribName == "deadband") {
						deadband = Float.parseFloat(attribValue);
					} else if (attribName == "thermometer") {
						thermometerUID = attribValue;
					} else if (attribName == "power_source") {
						powerSourceUID = attribValue;
					}
				}
				if (!Float.isNaN(setpoint) && !Float.isNaN(deadband) && powerSourceUID != null) {
					Part p = box.model.getPart(powerSourceUID);
					if (p != null) {
						Thermometer t = null;
						if (thermometerUID != null)
							t = box.model.getThermometer(thermometerUID);
						Thermostat ts = box.model.addThermostat(t, p);
						ts.setDeadband(deadband);
						ts.setSetPoint(setpoint);
					}
				}
			}
		} else if (qName == "cloud") {
			if (attrib != null) {
				float x = Float.NaN, y = Float.NaN, w = Float.NaN, h = Float.NaN;
				float speed = 0;
				String label = null, uid = null;
				Color color = Color.WHITE;
				for (int i = 0, n = attrib.getLength(); i < n; i++) {
					attribName = attrib.getQName(i).intern();
					attribValue = attrib.getValue(i);
					if (attribName == "x") {
						x = Float.parseFloat(attribValue);
					} else if (attribName == "y") {
						y = Float.parseFloat(attribValue);
					} else if (attribName == "width") {
						w = Float.parseFloat(attribValue);
					} else if (attribName == "height") {
						h = Float.parseFloat(attribValue);
					} else if (attribName == "speed") {
						speed = Float.parseFloat(attribValue);
					} else if (attribName == "uid") {
						uid = attribValue;
					} else if (attribName == "label") {
						label = attribValue;
					} else if (attribName == "color") {
						color = new Color(Integer.parseInt(attribValue, 16));
					}
				}
				if (!Float.isNaN(x) && !Float.isNaN(y) && !Float.isNaN(w) && !Float.isNaN(h)) {
					Cloud c = new Cloud(new Rectangle2D.Float(0, 0, w, h));
					c.setX(x);
					c.setY(y);
					c.setSpeed(speed);
					c.setUid(uid);
					c.setLabel(label);
					c.setColor(color);
					box.model.addCloud(c);
				}
			}
		} else if (qName == "tree") {
			if (attrib != null) {
				float x = Float.NaN, y = Float.NaN, w = Float.NaN, h = Float.NaN;
				byte type = Tree.REGULAR;
				String label = null, uid = null;
				Color color = Color.GREEN.darker();
				for (int i = 0, n = attrib.getLength(); i < n; i++) {
					attribName = attrib.getQName(i).intern();
					attribValue = attrib.getValue(i);
					if (attribName == "x") {
						x = Float.parseFloat(attribValue);
					} else if (attribName == "y") {
						y = Float.parseFloat(attribValue);
					} else if (attribName == "width") {
						w = Float.parseFloat(attribValue);
					} else if (attribName == "height") {
						h = Float.parseFloat(attribValue);
					} else if (attribName == "type") {
						type = Byte.parseByte(attribValue);
					} else if (attribName == "uid") {
						uid = attribValue;
					} else if (attribName == "label") {
						label = attribValue;
					} else if (attribName == "color") {
						color = new Color(Integer.parseInt(attribValue, 16));
					}
				}
				if (!Float.isNaN(x) && !Float.isNaN(y) && !Float.isNaN(w) && !Float.isNaN(h)) {
					Tree t = new Tree(new Rectangle2D.Float(0, 0, w, h), type);
					t.setX(x);
					t.setY(y);
					t.setUid(uid);
					t.setLabel(label);
					t.setColor(color);
					box.model.addTree(t);
				}
			}
		} else if (qName == "text") {
			if (attrib != null) {
				float x = Float.NaN, y = Float.NaN;
				int size = 14, style = Font.PLAIN;
				String str = null, face = null, uid = null;
				Color color = null;
				boolean border = false;
				for (int i = 0, n = attrib.getLength(); i < n; i++) {
					attribName = attrib.getQName(i).intern();
					attribValue = attrib.getValue(i);
					if (attribName == "x") {
						x = Float.parseFloat(attribValue);
					} else if (attribName == "y") {
						y = Float.parseFloat(attribValue);
					} else if (attribName == "uid") {
						uid = attribValue;
					} else if (attribName == "string") {
						str = attribValue;
					} else if (attribName == "size") {
						size = Integer.parseInt(attribValue);
					} else if (attribName == "style") {
						style = Integer.parseInt(attribValue);
					} else if (attribName == "name") { // TODO: backward compatibility, to remove by 2013
						face = attribValue;
					} else if (attribName == "face") {
						face = attribValue;
					} else if (attribName == "color") {
						color = new Color(Integer.parseInt(attribValue, 16));
					} else if (attribName == "border") {
						border = Boolean.parseBoolean(attribValue);
					}
				}
				if (!Float.isNaN(x) && !Float.isNaN(y)) {
					TextBox t = box.view.addText(new XmlCharacterDecoder().decode(str), x, y);
					t.setUid(uid);
					t.setSize(size);
					t.setStyle(style);
					t.setFace(face);
					t.setColor(color);
					t.setBorder(border);
					box.view.repaint();
				}
			}
		} else if (qName == "image") {
			if (attrib != null) {
				float x = Float.NaN, y = Float.NaN, w = Float.NaN, h = Float.NaN;
				String filename = null, format = null, uid = null, data = null;
				boolean border = false, draggable = true;
				for (int i = 0, n = attrib.getLength(); i < n; i++) {
					attribName = attrib.getQName(i).intern();
					attribValue = attrib.getValue(i);
					if (attribName == "x") {
						x = Float.parseFloat(attribValue);
					} else if (attribName == "y") {
						y = Float.parseFloat(attribValue);
					} else if (attribName == "width") {
						w = Float.parseFloat(attribValue);
					} else if (attribName == "height") {
						h = Float.parseFloat(attribValue);
					} else if (attribName == "uid") {
						uid = attribValue;
					} else if (attribName == "data") {
						data = attribValue;
					} else if (attribName == "format") {
						format = attribValue;
					} else if (attribName == "filename") {
						filename = attribValue;
					} else if (attribName == "border") {
						border = Boolean.parseBoolean(attribValue);
					} else if (attribName == "draggable") {
						draggable = Boolean.parseBoolean(attribValue);
					}
				}
				if (!Float.isNaN(x) && !Float.isNaN(y) && data != null) {
					InputStream in = new ByteArrayInputStream(DatatypeConverter.parseBase64Binary(data));
					Picture p = null;
					try {
						p = box.view.addPicture(ImageIO.read(in), format, filename, x, y);
					} catch (IOException e) {
						e.printStackTrace();
					} finally {
						try {
							in.close();
						} catch (IOException e) {
							e.printStackTrace();
						}
					}
					if (p != null) {
						p.setUid(uid);
						p.setBorder(border);
						p.setDraggable(draggable);
						p.setX(x);
						p.setY(y);
						if (!Float.isNaN(w))
							p.setWidth(w);
						if (!Float.isNaN(h))
							p.setHeight(h);
						box.view.repaint();
					}
				}
			}
		} else if (qName == "fan") {
			if (attrib != null) {
				float x = Float.NaN, y = Float.NaN, w = Float.NaN, h = Float.NaN;
				String label = null, uid = null;
				float speed = 0;
				float angle = 0;
				for (int i = 0, n = attrib.getLength(); i < n; i++) {
					attribName = attrib.getQName(i).intern();
					attribValue = attrib.getValue(i);
					if (attribName == "x") {
						x = Float.parseFloat(attribValue);
					} else if (attribName == "y") {
						y = Float.parseFloat(attribValue);
					} else if (attribName == "width") {
						w = Float.parseFloat(attribValue);
					} else if (attribName == "height") {
						h = Float.parseFloat(attribValue);
					} else if (attribName == "speed") {
						speed = Float.parseFloat(attribValue);
					} else if (attribName == "angle") {
						angle = Float.parseFloat(attribValue);
					} else if (attribName == "uid") {
						uid = attribValue;
					} else if (attribName == "label") {
						label = attribValue;
					}
				}
				if (!Float.isNaN(x) && !Float.isNaN(y) && !Float.isNaN(w) && !Float.isNaN(h)) {
					Fan f = new Fan(new Rectangle2D.Float(x, y, w, h));
					f.setUid(uid);
					f.setLabel(label);
					f.setSpeed(speed);
					f.setAngle(angle);
					box.model.addFan(f);
				}
			}
		} else if (qName == "heliostat") {
			if (attrib != null) {
				float x = Float.NaN, y = Float.NaN, w = Float.NaN, h = Float.NaN;
				String label = null, uid = null, targetID = null;
				byte type = Heliostat.MIRROR;
				for (int i = 0, n = attrib.getLength(); i < n; i++) {
					attribName = attrib.getQName(i).intern();
					attribValue = attrib.getValue(i);
					if (attribName == "x") {
						x = Float.parseFloat(attribValue);
					} else if (attribName == "y") {
						y = Float.parseFloat(attribValue);
					} else if (attribName == "type") {
						type = Byte.parseByte(attribValue);
					} else if (attribName == "width") {
						w = Float.parseFloat(attribValue);
					} else if (attribName == "height") {
						h = Float.parseFloat(attribValue);
					} else if (attribName == "uid") {
						uid = attribValue;
					} else if (attribName == "target") {
						targetID = attribValue;
					} else if (attribName == "label") {
						label = attribValue;
					}
				}
				if (!Float.isNaN(x) && !Float.isNaN(y) && !Float.isNaN(w) && !Float.isNaN(h)) {
					Heliostat hs = new Heliostat(new Rectangle2D.Float(x, y, w, h), box.model);
					hs.setUid(uid);
					hs.setType(type);
					hs.setLabel(label);
					if (targetID != null)
						hs.setTarget(box.model.getPart(targetID));
					box.model.addHeliostat(hs);
				}
			}
		} else if (qName == "particle_feeder") {
			if (attrib != null) {
				float x = Float.NaN, y = Float.NaN, mass = Float.NaN, radius = Float.NaN;
				String label = null, uid = null;
				float period = Float.NaN;
				int maximum = 0;
				Color color = Color.WHITE;
				Color velocityColor = Color.BLACK;
				for (int i = 0, n = attrib.getLength(); i < n; i++) {
					attribName = attrib.getQName(i).intern();
					attribValue = attrib.getValue(i);
					if (attribName == "x") {
						x = Float.parseFloat(attribValue);
					} else if (attribName == "y") {
						y = Float.parseFloat(attribValue);
					} else if (attribName == "mass") {
						mass = Float.parseFloat(attribValue);
					} else if (attribName == "radius") {
						radius = Float.parseFloat(attribValue);
					} else if (attribName == "period") {
						period = Float.parseFloat(attribValue);
					} else if (attribName == "maximum") {
						maximum = Integer.parseInt(attribValue);
					} else if (attribName == "uid") {
						uid = attribValue;
					} else if (attribName == "label") {
						label = attribValue;
					} else if (attribName == "color") {
						color = new Color(Integer.parseInt(attribValue, 16));
					} else if (attribName == "velocity_color") {
						velocityColor = new Color(Integer.parseInt(attribValue, 16));
					}
				}
				if (!Float.isNaN(x) && !Float.isNaN(y)) {
					ParticleFeeder pf = new ParticleFeeder(x, y);
					pf.setUid(uid);
					pf.setLabel(label);
					pf.setColor(color);
					pf.setVelocityColor(velocityColor);
					if (!Float.isNaN(mass))
						pf.setMass(mass);
					if (!Float.isNaN(radius))
						pf.setRadius(radius);
					if (!Float.isNaN(period))
						pf.setPeriod(period);
					if (maximum > 0)
						pf.setMaximum(maximum);
					box.model.addParticleFeeder(pf);
				}
			}
		}

	}

	public void endElement(String uri, String localName, String qName) {

		if (qName == "next_sim") {
			nextSim = str;
		} else if (qName == "prev_sim") {
			prevSim = str;
		} else if (qName == "model_width") {
			modelWidth = Float.parseFloat(str);
		} else if (qName == "model_height") {
			modelHeight = Float.parseFloat(str);
		} else if (qName == "timestep") {
			timeStep = Float.parseFloat(str);
		} else if (qName == "measurement_interval") {
			measurementInterval = Integer.parseInt(str);
		} else if (qName == "control_interval") {
			controlInterval = Integer.parseInt(str);
		} else if (qName == "viewupdate_interval") {
			viewUpdateInterval = Integer.parseInt(str);
		} else if (qName == "sunny") {
			sunny = Boolean.parseBoolean(str);
		} else if (qName == "sun_angle") {
			sunAngle = Float.parseFloat(str);
		} else if (qName == "solar_power_density") {
			solarPowerDensity = Float.parseFloat(str);
		} else if (qName == "solar_ray_count") {
			solarRayCount = Integer.parseInt(str);
		} else if (qName == "solar_ray_speed") {
			solarRaySpeed = Float.parseFloat(str);
		} else if (qName == "photon_emission_interval") {
			photonEmissionInterval = Integer.parseInt(str);
		} else if (qName == "perimeter_step_size") {
			perimeterStepSize = Float.parseFloat(str);
		} else if (qName == "z_heat_diffusivity") {
			zHeatDiffusivity = Float.parseFloat(str);
		} else if (qName == "z_heat_diffusivity_only_for_fluid") {
			zHeatDiffusivityOnlyForFluid = Boolean.parseBoolean(str);
		} else if (qName == "gravitational_acceleration") {
			gravitationalAcceleration = Float.parseFloat(str);
		} else if (qName == "thermophoretic_coefficient") {
			thermophoreticCoefficient = Float.parseFloat(str);
		} else if (qName == "particle_drag") {
			particleDrag = Float.parseFloat(str);
		} else if (qName == "particle_hardness") {
			particleHardness = Float.parseFloat(str);
		} else if (qName == "convective") {
			convective = Boolean.parseBoolean(str);
		} else if (qName == "background_conductivity") {
			backgroundConductivity = Float.parseFloat(str);
		} else if (qName == "background_density") {
			backgroundDensity = Float.parseFloat(str);
		} else if (qName == "background_specific_heat") {
			backgroundSpecificHeat = Float.parseFloat(str);
		} else if (qName == "background_temperature") {
			backgroundTemperature = Float.parseFloat(str);
		} else if (qName == "background_viscosity") {
			backgroundViscosity = Float.parseFloat(str);
		} else if (qName == "thermal_buoyancy" || qName == "thermal_expansion_coefficient") {
			thermalExpansionCoefficient = Float.parseFloat(str);
		} else if (qName == "buoyancy_approximation") {
			buoyancyApproximation = Byte.parseByte(str);
		} else if (qName == "gravity_type") {
			gravityType = Byte.parseByte(str);
		} else if (qName == "minimum_temperature") {
			minimumTemperature = Float.parseFloat(str);
		} else if (qName == "maximum_temperature") {
			maximumTemperature = Float.parseFloat(str);
		} else if (qName == "fan_rotation_speed_scale_factor") {
			fanRotationSpeedScaleFactor = Float.parseFloat(str);
		} else if (qName == "graph_data_type") {
			graphDataType = Byte.parseByte(str);
		} else if (qName == "graph_time_unit") {
			graphTimeUnit = Byte.parseByte(str);
		} else if (qName == "view_factor_lines") {
			viewFactorLines = Boolean.parseBoolean(str);
		} else if (qName == "ruler" || qName == "border_tickmarks") {
			borderTickmarks = Boolean.parseBoolean(str);
		} else if (qName == "fahrenheit_used") {
			fahrenheitUsed = Boolean.parseBoolean(str);
		} else if (qName == "isotherm") {
			isotherm = Boolean.parseBoolean(str);
		} else if (qName == "streamline") {
			streamline = Boolean.parseBoolean(str);
		} else if (qName == "velocity") {
			velocity = Boolean.parseBoolean(str);
		} else if (qName == "heat_flux_arrow") {
			heatFluxArrows = Boolean.parseBoolean(str);
		} else if (qName == "heat_flux_line") {
			heatFluxLines = Boolean.parseBoolean(str);
		} else if (qName == "grid") {
			grid = Boolean.parseBoolean(str);
		} else if (qName == "snap_to_grid") {
			snapToGrid = Boolean.parseBoolean(str);
		} else if (qName == "grid_size") {
			gridSize = Integer.parseInt(str);
		} else if (qName == "color_palette") {
			colorPalette = Boolean.parseBoolean(str);
		} else if (qName == "color_palette_type") {
			colorPaletteType = Byte.parseByte(str);
		} else if (qName == "brand") {
			showLogo = Boolean.parseBoolean(str);
		} else if (qName == "control_panel") {
			controlPanel = Boolean.parseBoolean(str);
		} else if (qName == "control_panel_position") {
			controlPanelPosition = Byte.parseByte(str);
		} else if (qName == "heat_map") {
			heatMapType = Byte.parseByte(str);
		} else if (qName == "color_palette_x") {
			colorPaletteX = Float.parseFloat(str);
		} else if (qName == "color_palette_y") {
			colorPaletteY = Float.parseFloat(str);
		} else if (qName == "color_palette_w") {
			colorPaletteW = Float.parseFloat(str);
		} else if (qName == "color_palette_h") {
			colorPaletteH = Float.parseFloat(str);
		} else if (qName == "clock") {
			clock = Boolean.parseBoolean(str);
		} else if (qName == "smooth") {
			smooth = Boolean.parseBoolean(str);
		} else if (qName == "graph") {
			graphOn = Boolean.parseBoolean(str);
		} else if (qName == "graph_xlabel") {
			graphXLabel = str;
		} else if (qName == "graph_ylabel") {
			graphYLabel = str;
		} else if (qName == "graph_ymin") {
			graphYmin = Float.parseFloat(str);
		} else if (qName == "graph_ymax") {
			graphYmax = Float.parseFloat(str);
		} else if (qName == "elasticity") {
			partElasticity = Float.parseFloat(str);
		} else if (qName == "thermal_conductivity") {
			partThermalConductivity = Float.parseFloat(str);
		} else if (qName == "specific_heat") {
			partSpecificHeat = Float.parseFloat(str);
		} else if (qName == "density") {
			partDensity = Float.parseFloat(str);
		} else if (qName == "emissivity") {
			partEmissivity = Float.parseFloat(str);
		} else if (qName == "absorption") {
			partAbsorption = Float.parseFloat(str);
		} else if (qName == "reflection") {
			partReflection = Float.parseFloat(str);
		} else if (qName == "scattering") {
			partScattering = Boolean.parseBoolean(str);
		} else if (qName == "scattering_visible") {
			partScatteringVisible = Boolean.parseBoolean(str);
		} else if (qName == "transmission") {
			partTransmission = Float.parseFloat(str);
		} else if (qName == "constant_temperature") {
			partConstantTemperature = Boolean.parseBoolean(str);
		} else if (qName == "power") {
			partPower = Float.parseFloat(str);
		} else if (qName == "temperature_coefficient") {
			partTemperatureCoefficient = Float.parseFloat(str);
		} else if (qName == "reference_temperature") {
			partReferenceTemperature = Float.parseFloat(str);
		} else if (qName == "wind_speed") {
			fanSpeed = Float.parseFloat(str);
		} else if (qName == "wind_angle") {
			fanAngle = Float.parseFloat(str);
		} else if (qName == "texture_style") {
			textureStyle = Byte.parseByte(str);
		} else if (qName == "texture_width") {
			textureWidth = Integer.parseInt(str);
		} else if (qName == "texture_height") {
			textureHeight = Integer.parseInt(str);
		} else if (qName == "texture_fg") {
			textureForeground = new Color(Integer.parseInt(str, 16));
		} else if (qName == "texture_bg") {
			textureBackground = new Color(Integer.parseInt(str, 16));
		} else if (qName == "filled") {
			partFilled = Boolean.parseBoolean(str);
		} else if (qName == "visible") {
			partVisible = Boolean.parseBoolean(str);
		} else if (qName == "draggable") {
			draggable = Boolean.parseBoolean(str);
		} else if (qName == "movable") {
			movable = Boolean.parseBoolean(str);
		} else if (qName == "color") {
			color = new Color(Integer.parseInt(str, 16));
		} else if (qName == "velocity_color") {
			velocityColor = new Color(Integer.parseInt(str, 16));
		} else if (qName == "rx") {
			particleRx = Float.parseFloat(str);
		} else if (qName == "ry") {
			particleRy = Float.parseFloat(str);
		} else if (qName == "vx") {
			particleVx = Float.parseFloat(str);
		} else if (qName == "vy") {
			particleVy = Float.parseFloat(str);
		} else if (qName == "theta") {
			particleTheta = Float.parseFloat(str);
		} else if (qName == "omega") {
			particleOmega = Float.parseFloat(str);
		} else if (qName == "radius") {
			particleRadius = Float.parseFloat(str);
		} else if (qName == "mass") {
			particleMass = Float.parseFloat(str);
		} else if (qName == "uid") {
			uid = str;
		} else if (qName == "label") {
			label = str;
		} else if (qName == "temperature") {
			temperature = Float.parseFloat(str);
		} else if (qName == "boundary") {
			// nothing to do at this point
		} else if (qName == "part") {
			if (part != null) {
				if (!Float.isNaN(partThermalConductivity))
					part.setThermalConductivity(partThermalConductivity);
				if (!Float.isNaN(partSpecificHeat))
					part.setSpecificHeat(partSpecificHeat);
				if (!Float.isNaN(partDensity))
					part.setDensity(partDensity);
				if (!Float.isNaN(temperature))
					part.setTemperature(temperature);
				if (!Float.isNaN(partPower))
					part.setPower(partPower);
				if (!Float.isNaN(partEmissivity))
					part.setEmissivity(partEmissivity);
				if (!Float.isNaN(partAbsorption))
					part.setAbsorptivity(partAbsorption);
				if (!Float.isNaN(partReflection))
					part.setReflectivity(partReflection);
				if (!Float.isNaN(partTransmission))
					part.setTransmissivity(partTransmission);
				part.setElasticity(partElasticity);
				part.setScattering(partScattering);
				part.setScatteringVisible(partScatteringVisible);
				part.setThermistorTemperatureCoefficient(partTemperatureCoefficient);
				part.setThermistorReferenceTemperature(partReferenceTemperature);
				part.setWindAngle(fanAngle);
				part.setWindSpeed(fanSpeed);
				part.setConstantTemperature(partConstantTemperature);
				part.setDraggable(draggable);
				part.setVisible(partVisible);
				if (color != null)
					part.setFillPattern(new ColorFill(color));
				if (textureStyle != 0)
					part.setFillPattern(new Texture(textureForeground.getRGB(), textureBackground.getRGB(), textureStyle, textureWidth, textureHeight));
				part.setFilled(partFilled);
				part.setUid(uid);
				part.setLabel(label);
				resetPartVariables();
			}
		} else if (qName == "particle") {
			if (particle != null) {
				if (!Float.isNaN(particleRx))
					particle.setRx(particleRx);
				if (!Float.isNaN(particleRy))
					particle.setRy(particleRy);
				if (!Float.isNaN(particleVx))
					particle.setVx(particleVx);
				if (!Float.isNaN(particleVy))
					particle.setVy(particleVy);
				if (!Float.isNaN(particleTheta))
					particle.setTheta(particleTheta);
				if (!Float.isNaN(particleOmega))
					particle.setOmega(particleOmega);
				if (!Float.isNaN(particleRadius))
					particle.setRadius(particleRadius);
				if (!Float.isNaN(particleMass))
					particle.setMass(particleMass);
				if (!Float.isNaN(temperature))
					particle.setTemperature(temperature);
				if (color != null)
					particle.setFillPattern(new ColorFill(color));
				if (textureStyle != 0)
					particle.setFillPattern(new Texture(textureForeground.getRGB(), textureBackground.getRGB(), textureStyle, textureWidth, textureHeight));
				if (velocityColor != null)
					particle.setVelocityColor(velocityColor);
				particle.setUid(uid);
				particle.setLabel(label);
				particle.setMovable(movable);
				particle.setDraggable(draggable);
				particle.storeState();
				resetParticleVariables();
			}
		}

	}

	private void resetPartVariables() {
		partThermalConductivity = Float.NaN;
		partSpecificHeat = Float.NaN;
		partDensity = Float.NaN;
		partElasticity = 1;
		partConstantTemperature = false;
		partPower = Float.NaN;
		partTemperatureCoefficient = 0;
		partReferenceTemperature = 0;
		partEmissivity = Float.NaN;
		partAbsorption = Float.NaN;
		partReflection = Float.NaN;
		partTransmission = Float.NaN;
		partScattering = false;
		partScatteringVisible = true;
		fanSpeed = 0;
		fanAngle = 0;
		partVisible = true;
		partFilled = true;
		textureStyle = 0;
		textureWidth = 10;
		textureHeight = 10;
		textureForeground = Color.BLACK;
		textureBackground = Color.WHITE;
		uid = null;
		label = null;
		color = null;
		temperature = Float.NaN;
		draggable = true;
	}

	private void resetParticleVariables() {
		particleRx = Float.NaN;
		particleRy = Float.NaN;
		particleVx = Float.NaN;
		particleVy = Float.NaN;
		particleTheta = Float.NaN;
		particleOmega = Float.NaN;
		particleRadius = Float.NaN;
		particleMass = Float.NaN;
		movable = true;
		textureStyle = 0;
		textureWidth = 10;
		textureHeight = 10;
		textureForeground = Color.BLACK;
		textureBackground = Color.WHITE;
		uid = null;
		label = null;
		color = null;
		velocityColor = null;
		temperature = Float.NaN;
		draggable = true;
	}

	private void resetGlobalVariables() {

		// model properties
		nextSim = null;
		prevSim = null;
		modelWidth = 10;
		modelHeight = 10;
		timeStep = 1;
		measurementInterval = 100;
		controlInterval = 100;
		viewUpdateInterval = 20;
		sunny = false;
		sunAngle = (float) Math.PI * 0.5f;
		solarPowerDensity = 2000;
		solarRayCount = 24;
		solarRaySpeed = 0.1f;
		photonEmissionInterval = 20;
		perimeterStepSize = 0.05f;
		zHeatDiffusivity = 0;
		zHeatDiffusivityOnlyForFluid = false;
		gravitationalAcceleration = -1;
		thermophoreticCoefficient = 0;
		particleDrag = -1;
		particleHardness = -1;
		convective = true;
		backgroundConductivity = Constants.AIR_THERMAL_CONDUCTIVITY;
		backgroundDensity = Constants.AIR_DENSITY;
		backgroundSpecificHeat = Constants.AIR_SPECIFIC_HEAT;
		backgroundViscosity = Constants.AIR_VISCOSITY;
		backgroundTemperature = 0;
		thermalExpansionCoefficient = 0;
		buoyancyApproximation = Model2D.BUOYANCY_AVERAGE_COLUMN;
		gravityType = Model2D.GRAVITY_UNIFORM;

		// view properties
		graphDataType = 0;
		graphTimeUnit = 0;
		fahrenheitUsed = false;
		viewFactorLines = false;
		borderTickmarks = false;
		grid = false;
		snapToGrid = true;
		gridSize = 10;
		isotherm = false;
		streamline = false;
		colorPalette = false;
		colorPaletteType = View2D.RAINBOW;
		velocity = false;
		heatFluxArrows = false;
		heatFluxLines = false;
		graphOn = false;
		clock = true;
		showLogo = true;
		controlPanel = false;
		controlPanelPosition = 0;
		smooth = true;
		minimumTemperature = 0;
		maximumTemperature = 50;
		fanRotationSpeedScaleFactor = 1;
		graphXLabel = null;
		graphYLabel = null;
		graphYmin = 0;
		graphYmax = 50;
		heatMapType = View2D.HEATMAP_TEMPERATURE;

	}

	public void characters(char[] ch, int start, int length) { // SAX parse breaks from entitiy characters
		if (str == null) {
			str = new String(ch, start, length);
		} else {
			str += new String(ch, start, length);
		}
	}

	public void warning(SAXParseException e) {
		e.printStackTrace();
	}

	public void error(SAXParseException e) {
		e.printStackTrace();
	}

	public void fatalError(SAXParseException e) {
		e.printStackTrace();
	}

}