public class IDManager {

	private static final int INITIAL_SIZE = 50;

	private static final float GROW_RATE = 1.5f;

	private EData[] _data = new EData[INITIAL_SIZE];

	private List<EData> _invalids = new LinkedList<>();

	private int _minFree = 0;

	// ============================== lifecycle
	// =================================

	public IDManager() {
		// empty constructor
	}

	public void dispose() {
		Arrays.fill(_data, null);
		_data = null;
		_invalids.clear();
		_invalids = null;
	}

	// ============================= methods
	// ====================================

	public boolean containsID(int id) {
		return id < _data.length && _data[id] != null;
	}

	public boolean containsID(EData d) {
		return containsID(d.getID());
	}

	public boolean put(EData d) {
		int id = d.getID();
		if (containsID(id)) {
			_invalids.add(d);
			return false;
		}
		checkSize(id);
		_data[id] = d;
		return true;
	}

	public int getFreeID() {
		while (_minFree < _data.length) {
			if (_data[_minFree] == null) {
				return _minFree;
			}
			_minFree++;
		}
		return _minFree;
	}

	public EData get(int id) {
		return _data[id];
	}

	public EData remove(int id) {

		// if id is out of range, return null.
		if (id >= _data.length) {
			return null;
		}

		// otherwise adjust minfree pointer and remove object.
		EData retour = _data[id];
		_data[id] = null;
		_minFree = Math.min(_minFree, id);

		// return object
		return retour;
	}

	public boolean hasInvalids() {
		return _invalids.size() > 0;
	}

	public void integrateInvalids() {
		for (EData d : _invalids) {
			d.setID(getFreeID());
			put(d);
		}
		_invalids.clear();
	}

	public String toString() {
		String s = "IDManager: "; //$NON-NLS-1$
		for (int i = 0; i < _data.length; ++i) {
			s = s + i + "=" + _data[i] + "; "; //$NON-NLS-1$ //$NON-NLS-2$
		}
		return s;
	}

	// ========================== private methods
	// ===============================

	private void checkSize(int id) {
		int size = _data.length;
		while (size <= id) {
			size = (int) (size * GROW_RATE);
		}
		if (size > _data.length) {
			EData[] newData = new EData[size];
			System.arraycopy(_data, 0, newData, 0, _data.length);
			_data = newData;
		}
	}
} public class Configuration extends ParentData {

	private IDManager _idManager = new IDManager();

	// ============================ lifecycle
	// ===================================

	public Configuration() {
		// empty
	}

	public void dispose() {
		super.dispose();
		_idManager.dispose();
		_idManager = null;
	}

	// ============================== methods
	// ===================================

	public IDManager getIDManager() {
		return _idManager;
	}

	public Configuration getConfiguration() {
		return this;
	}

	public CyclingLights getCyclingLights() {
		return (CyclingLights) getGarten().getKind(EType.CYCLING_LIGHTS, 0);
	}
} public class ParentData extends EData {

	// object garten to store children
	private KinderGarten _garten = null;

	// list of children to collect them during initialization.
	// When initialization finished, they will moved to the garten.
	protected List<EData> _childList = new LinkedList<>();

	// ============================= lifecycle
	// ==================================

	public ParentData() {
		// empty
	}

	public void init() {
		super.init();
		getGarten();
	}

	public void dispose() {
		super.dispose();

		// recursively call dispose on all children
		EData[] children = getGarten().getAllKinder();
		for (int i = 0; i < children.length; ++i) {
			children[i].dispose();
		}

		// remove all children
		removeAllChildren();
	}

	// ============================= methods
	// ====================================

	public boolean hasPower() {
		// default status for trunks, trays, configuration
		return true;
	}

	/**
	 * Adds a child dataObject to this dataObject
	 */
	public void addChild(EData child) {
		// add child to collector and set parent reference
		_childList.add(child);
		child.setParent(this);
	}

	public KinderGarten getGarten() {
		if (_garten == null) {

			// convert list to garten
			_garten = new KinderGarten(_childList);
			_childList = null;

			// recurse on children and init
			for (EData child : _garten.getAllKinder()) {
				assertInit(child);
			}
		}
		return _garten;
	}

	public void removeAllChildren() {
		_childList = null;
		_garten = null;
	}

	public EData getChild(int gridx, int gridy) {

		// recurse on all children and check their location in grid
		EData[] children = _garten.getAllKinder();
		for (int i = 0; i < children.length; ++i) {
			int[] numbers = children[i].getGridNumbers();
			if (numbers[0] <= gridx && numbers[2] > gridx && numbers[1] <= gridy && numbers[3] > gridy) {

				// return child
				return children[i];
			}
		}
		return null;
	}

	public Unit getUnit(int gridx) {
		return (Unit) getChild(gridx, 2);
	}

	public void addObserverToTree(Observer o) {
		addObserver(o);
		EData[] children = getGarten().getAllKinder();
		for (int i = 0; i < children.length; ++i) {
			children[i].addObserverToTree(o);
		}
	}

	// ============================== xml methods
	// ===============================

	/**
	 * Returns a list containing all Tag and child-Tag of this dataObject.
	 * 
	 * @return a <code>List</code> containing all tags.
	 */
	public void appendTags(List<String> l, int indent) {

		// if low indentation level, write comment line and increment progressor
		if (indent <= 2) {
			XMLUtil.appendCommentLine(l, indent, getName());
			Progressor.getInstance().incrementValue();
		}
		// append open tag, child tags and the close tag
		l.add(getOpenTag(indent));
		appendChildTags(l, indent);
		l.add(getCloseTag(indent));
	}

	protected void appendChildTags(List<String> l, int indent) {

		// increase indentation level
		indent++;

		// recurse over all children and append tag lists
		EData[] children = getGarten().getAllKinder();
		for (int i = 0; i < children.length; ++i) {
			children[i].appendTags(l, indent);
		}
	}
} public abstract class Unit extends ParentData implements Observer, PulseInteractor {

	// ============================== lifecycle
	// =================================

	public Unit() {
		// empty
	}

	public void init() {

		// super inits children
		super.init();

		// add this as dataListener to heaters in order to track power changing
		getHeaters().addObserver(this);
	}

	// ================================ methods
	// =================================

	public abstract Switch getHeaters();

	public boolean hasPower() {
		return getHeaters().isValue();
	}

	public EData getBlinkens() {
		return ((Configuration) getParent()).getChild(_gridNumbers[0], 0);
	}

	// ======================== pulseinteractor methods
	// =========================

	/**
	 * @param time
	 * @param source
	 * @return @see eniac.data.PulseInteractor#canReceiveProgram(long,
	 *         eniac.data.PulseInteractor)
	 */
	public boolean canReceiveProgram(long time, PulseInteractor source) {
		return hasPower();
	}

	public void receiveProgram(long time, PulseInteractor source) {
		// empty
	}

	public void sendProgram(long time, PulseInteractor source) {
		// empty
	}

	/**
	 * @param time
	 * @param source
	 * @return @see eniac.data.PulseInteractor#canReceiveDigit(long,
	 *         eniac.data.PulseInteractor)
	 */
	public boolean canReceiveDigit(long time, PulseInteractor source) {
		return hasPower();
	}

	public void receiveDigit(long time, long value, PulseInteractor source) {
		// empty
	}

	public void sendDigit(long time, long value, PulseInteractor source) {
		// empty
	}
} public enum EType {

	/**
	 * A configuration of 3 units
	 */
	CONFIGURATION_3,

	/**
	 * A configuration of 4 units
	 */
	CONFIGURATION_4,

	/**
	 * A configuration of 8 units
	 */
	CONFIGURATION_8,

	/**
	 * A configuration of 12 units
	 */
	CONFIGURATION_12,

	/**
	 * A configuration of 16 units
	 */
	CONFIGURATION_16,

	/**
	 * A configuration of 20 units
	 */
	CONFIGURATION_20,

	/**
	 * the accumulator
	 */
	ACCUMULATOR_UNIT,

	/**
	 * the cycling unit
	 */
	CYCLING_UNIT,

	/**
	 * the initiating unit
	 */
	INITIATING_UNIT,

	/**
	 * the 1st constant transmitter unit
	 */
	CONSTANT_TRANSMITTER_1_UNIT,

	/**
	 * the 2nd constant transmitter unit
	 */
	CONSTANT_TRANSMITTER_2_UNIT,

	/**
	 * an upper trunk which is 2 units width
	 */
	UPPER_TRUNK_2,

	/**
	 * an upper trunk which is 4 units width
	 */
	UPPER_TRUNK_4,

	/**
	 * a lower trunk which is 3 units width
	 */
	LOWER_TRUNK_3,

	/**
	 * a lower trunk which is 4 units width
	 */
	LOWER_TRUNK_4,

	/**
	 * a tray (part of a trunk) which is 2 units width
	 */
	TRAY_2,

	/**
	 * a tray (part of a trunk) which is 3 units width
	 */
	TRAY_3,

	/**
	 * a tray (part of a trunk) which is 4 units width
	 */
	TRAY_4,

	/**
	 * a blinken lights of an accumulator
	 */
	BLINKEN_LIGHTS,

	/**
	 * the heaters switch of an accumulator (may display the accumulator icon)
	 */
	ACCU_HEATERS,

	/**
	 * a standard heaters switch
	 */
	HEATERS,

	/**
	 * a heaters switch that displays 0 - 1
	 */
	HEATERS_01,

	/**
	 * the step button of the cycling unit
	 */
	STEP_BUTTON,

	/**
	 * the go button of the initiating unit
	 */
	GO_BUTTON,

	/**
	 * the clear button of the initiating unit
	 */
	CLEAR_BUTTON,

	/**
	 * the clear button for the cycle counter
	 */
	CYCLE_COUNTER_CLEAR,

	/**
	 * the iteration switch of the cycling unit
	 */
	ITERATION_SWITCH,

	/**
	 * the significant figures switch of an accumulator
	 */
	SIGNIFICIANT_FIGURES_SWITCH,

	/**
	 * the operations switch of an accumulator
	 */
	OPERATION_SWITCH,

	/**
	 * the repeat switch of an accumulator
	 */
	REPEAT_SWITCH,

	/**
	 * a cipher of the cycle counter at the cycling unit
	 */
	CIPHER,

	/**
	 * a digit of the blinken lights
	 */
	BLINKEN_NUMBER_SWITCH,

	/**
	 * the sign of the blinken lights
	 */
	BLINKEN_SIGN_SWITCH,

	/**
	 * the cycling lights -- display the pulse
	 */
	CYCLING_LIGHTS,

	/**
	 * the go lights of the initiating unit
	 */
	GO_LIGHTS,

	/**
	 * a digit connector (10 digits + sign + carryover)
	 */
	DIGIT_CONNECTOR,

	/**
	 * a program connector (1 pulse)
	 */
	PROGRAM_CONNECTOR,

	/**
	 * an inter-connector (can connect 2 accumulator units)
	 */
	INTER_CONNECTOR,

	/**
	 * a digit connector in horizontal layout (e.g. at the edges of a tray)
	 */
	DIGIT_CONNECTOR_CROSS,

	/**
	 * a pair of program connectors (in + out)
	 */
	PROGRAM_CONNECTOR_PAIR,

	/**
	 * a cover, without functionality (accumulator, between heaters and
	 * significant figures switch)
	 */
	BLEND_8,

	/**
	 * a cover, without functionality (accumulator, above the repeat switches)
	 */
	BLEND_16,

	/**
	 * a cover, without functionality (constant transmitter panel)
	 */
	BLEND_A_10,

	/**
	 * the symbol for the cycling unit
	 */
	CYCLING_SYMBOL,

	/**
	 * the symbol for the initiating unit
	 */
	INITIATING_SYMBOL,

	/**
	 * the symbol for the constant transmitter unit 1
	 */
	CONSTANT_TRANSMITTER_1_SYMBOL,

	/**
	 * the symbol for the constant transmitter unit 2
	 */
	CONSTANT_TRANSMITTER_2_SYMBOL,

	/**
	 * the constant selector switch for A+B
	 */
	CONSTANT_SELECTOR_SWITCH_AB,

	/**
	 * the constant selector switch for C+D
	 */
	CONSTANT_SELECTOR_SWITCH_CD,

	/**
	 * the constant selector switch for E+F
	 */
	CONSTANT_SELECTOR_SWITCH_EF,

	/**
	 * the constant selector switch for G+H
	 */
	CONSTANT_SELECTOR_SWITCH_GH,

	/**
	 * the constant selector switch for J+K
	 */
	CONSTANT_SELECTOR_SWITCH_JK,

	/**
	 * a number switch at the constant transmitter unit 2
	 */
	CONSTANT_SWITCH,

	/**
	 * the image displayed at the initiating unit
	 */
	INITIATING_IMAGE,

	/**
	 * the frequency slider (cycling unit)
	 */
	FREQUENCY_SLIDER,

	/**
	 * the benchmark, displaying the number of addition cycles per second
	 */
	BENCHMARK,

	/**
	 * the addition cycle counter
	 */
	CYCLE_COUNTER,

	/**
	 * image to be used in the overview panel, as marker for the currently
	 * visible area
	 */
	XOR_IMAGE,

	/**
	 * toggle switch for the constant transmitter 2, row J, left
	 */
	CONSTANT_SIGN_TOGGLE_JL,

	/**
	 * toggle switch for the constant transmitter 2, row J, right
	 */
	CONSTANT_SIGN_TOGGLE_JR,

	/**
	 * toggle switch for the constant transmitter 2, row K, left
	 */
	CONSTANT_SIGN_TOGGLE_KL,

	/**
	 * toggle switch for the constant transmitter 2, row K, right
	 */
	CONSTANT_SIGN_TOGGLE_KR,

	/**
	 * the numbers display at the constant transmitter 2
	 */
	CONSTANT_2_LIGHTS,

	/**
	 * a decimal cipher of the constant transmitter 2 lights
	 */
	CONSTANT_BLINKEN_CIPHER,

	/**
	 * the +/- sign for a number at the constant transmitter 2 lights
	 */
	CONSTANT_BLINKEN_SIGN,

	/**
	 * a cipher displayed at constant transmitter 1, to be shown during a
	 * transmission
	 */
	CONSTANT_TRANSMITTION_CIPHER,

	/**
	 * a =/- sign displayed at constant transmitter 1, to be shown during a
	 * transmission
	 */
	CONSTANT_TRANSMITTION_SIGN,

	/**
	 * the lights at constant transmitter 1, will display number and sign during
	 * a transmission
	 */
	CONSTANT_TRANSMITTION_LIGHTS;

	public enum Tag {
		NAME, TYPE, MODEL, VIEW, CODE, CODES, VALUE;
	}

	private String _edataClass;

	private String _epanelClass;

	private EType.Tag _codeName;

	private String[] _codes;

	private Descriptor[] _descriptors;

	private Grid[] _gridCache;

	private EType() {
		_gridCache = new Grid[StringConverter.toInt(EProperties.getInstance().getProperty("GRID_CACHE_SIZE"))];
	}

	// ========================== getters and setters
	// ===========================

	public void setCodes(String[] codes) {
		_codes = codes;
	}

	public String[] getCodes() {
		return _codes;
	}

	public void setCodeName(EType.Tag codeName) {
		_codeName = codeName;
	}

	public EType.Tag getCodeName() {
		return _codeName;
	}

	public void setEDataClass(String edataClass) {
		_edataClass = edataClass;
	}

	public void setEPanelClass(String epanelClass) {
		_epanelClass = epanelClass;
	}

	public void setDescriptors(Descriptor[] descriptors) {
		// empty grid cache
		Arrays.fill(_gridCache, null);
		_descriptors = descriptors;
	}

	// ================================ methods
	// =================================

	public EData makeEData() throws InstantiationException, ClassNotFoundException, IllegalAccessException {

		EData edata = (EData) Class.forName(_edataClass).newInstance();
		edata.setType(this);
		return edata;
	}

	public EPanel makeEPanel() {
		try {
			return (EPanel) Class.forName(_epanelClass).newInstance();
		} catch (Exception e) {
			// System.out.println(_epanelClass);
			e.printStackTrace();
		}
		return null;
	}

	public Descriptor getDescriptor(int lod) {
		return _descriptors[lod];
	}

	public Grid getGrid(int width, int height, int lod) {
		int index = computeIndex(width, height);
		if (_gridCache[index] == null || _gridCache[index].width != width || _gridCache[index].height != height) {

			_gridCache[index] = getDescriptor(lod).makeGrid(width, height);
		}
		return _gridCache[index];
	}

	private int computeIndex(int width, int height) {
		return (width + height) % _gridCache.length;
	}
} public class EPanel extends JPanel implements Observer, MouseInputListener {

	// reference to dataObject
	protected EData _data;

	// =========================== lifecycle
	// ====================================

	public EPanel() {
		super(null);
	}

	public void init() {
		// System.out.println("init " + _data.getType());
		// init listeners
		addMouseListener(this);
		addMouseMotionListener(this);
		_data.addObserver(this);

		// find a non-empty name
		String name;
		EPanel panel = this;
		do {
			name = panel.getData().getName();
			panel = (EPanel) getParent();
		} while (name.equals("")); //$NON-NLS-1$

		// set name as tooltip
		setToolTipText(name);
	}

	/**
	 * @see eniac.data.IDataPanel#dispose() TODO: how to dispose? dataObject
	 *      calls its tree, dataPanel calls its tree?
	 */
	public void dispose() {
		removeMouseListener(this);
		removeMouseMotionListener(this);
		_data.deleteObserver(this);
	}

	// =============================== methods //===============================

	public void setData(EData data) {
		_data = data;
	}

	public EData getData() {
		return _data;
	}

	public JComponent createPropertiesPanel() {
		return new PropertyPanel(_data.getProperties());
	}

	public List<Action> getActions() {
		List<Action> l = new LinkedList<>();
		l.add(new ShowProperties());
		return l;
	}

	public void paintAsIcon(Graphics g, int x, int y, int w, int h, int lod) {
		paintComponent(g, x, y, w, h, lod);
	}

	/**
	 * Paints this dataPanel.
	 */
	public void paintComponent(Graphics g) {
		// maybe call super-paint: super.paintComponent(g);
		paintComponent(g, 0, 0, getWidth(), getHeight(), getLod());
	}

	protected void paintComponent(Graphics g, int x, int y, int width, int height, int lod) {

		// get descriptor. If no descriptor, just return.
		Descriptor d = getDescriptor(lod);
		if (d == null) {
			return;
		}
		// draw background
		drawBackground(g, x, y, width, height, lod, d);

		// paint bgimage, if defined
		Image bgimage = (Image) d.get(Descriptor.Key.BACK_IMAGE);
		if (bgimage != null) {
			g.drawImage(bgimage, x, y, width, height, this);
		}
	}

	protected void drawBackground(Graphics g, int x, int y, int width, int height, int lod, Descriptor d) {

		// get bgcolor. If background color is not defined, take ancestor one's.
		Color color = (Color) d.get(Descriptor.Key.COLOR);
		EPanel p = this;
		while (color == null) {

			// get parent's background color
			Component c = p.getParent();
			if (c == null) {
				// sometimes we have no parent yet
				color = StringConverter.toColor(EProperties.getInstance().getProperty("BACKGROUND_COLOR"));
			}
			else {
				p = (EPanel) c;
				d = p.getDescriptor(lod);
				// note: the parent's descriptor is not null, because ours is
				// not.
				color = (Color) d.get(Descriptor.Key.COLOR);
			}
		}
		// draw background
		g.setColor(color);
		g.fillRect(x, y, width, height);
	}

	public Descriptor getDescriptor(int lod) {
		return _data.getType().getDescriptor(lod);
	}

	public int getLod() {
		return ((EPanel) getParent()).getLod();
	}

	public void update(Observable o, Object arg) {
		assert arg != null;
		// if (Simulator.getInstance().isSimulationThread()) {
		// paintComponent(getGraphics());
		// } else {
		// repaint();
		// }
		if (arg == EData.REPAINT && getDescriptor(getLod()) != null) {
			repaint();
		}
		else if (arg == EData.PAINT_IMMEDIATELY) {
			paintComponent(getGraphics());
		}
	}

	public Point getLocationOnConfigPanel() {
		Component c = this;
		Point p = new Point();
		while (!(c instanceof ConfigPanel)) {
			p.x += c.getX();
			p.y += c.getY();
			c = c.getParent();
		}
		return p;
	}

	public boolean isEnabled() {
		Descriptor d = _data.getType().getDescriptor(getLod());
		return d != null;
	}

	public Rectangle computeBound(ParentGrid pg, int lod) {

		EType type = _data.getType();
		int[] gridNums = _data.getGridNumbers();

		// helper variables
		int x1 = gridNums[0];
		int y1 = gridNums[1];
		int x2 = gridNums[2];
		int y2 = gridNums[3];

		// compute size of the grid-square
		int newWidth = pg.xValues[x2] - pg.xValues[x1];
		int newHeight = pg.yValues[y2] - pg.yValues[y1];
		int newX = pg.xValues[x1];
		int newY = pg.yValues[y1];

		// determine size of the panel that should be centered
		// check, if the whole grid-rectangle should be filled.
		Descriptor descriptor = type.getDescriptor(lod);
		// if (descriptor == null) {
		// System.out.println(type);
		// }
		Descriptor.Fill fill = descriptor.getFill();
		if (fill == Descriptor.Fill.HORIZONTAL || fill == Descriptor.Fill.NONE) {
			int h = (int) (descriptor.getHeight() * pg.zoomY);
			newY = newY + ((newHeight - h) >> 1);
			newHeight = h;
		}
		if (fill == Descriptor.Fill.VERTICAL || fill == Descriptor.Fill.NONE) {
			int w = (int) (descriptor.getWidth() * pg.zoomX);
			newX = newX + ((newWidth - w) >> 1);
			newWidth = w;
		}
		return new Rectangle(newX, newY, newWidth, newHeight);
	}

	// ======================= MouseListener methods
	// ============================

	public void mouseReleased(MouseEvent e) {
		if (isEnabled() && e.getButton() != MouseEvent.BUTTON3) {
			getController().mreleased(e);
		}
	}

	public void mousePressed(MouseEvent e) {
		if (e.getButton() == MouseEvent.BUTTON3) {

			// if right button, create pop-up menu
			JPopupMenu menu = new JPopupMenu();
			for (Action a : getActions()) {
				menu.add(a);
			}
			menu.show(this, e.getX(), e.getY());
		}
		else {
			if (isEnabled()) {
				getController().mpressed(e);
			}
		}
	}

	public void mouseDragged(MouseEvent e) {
		if (isEnabled()) {
			getController().mdragged(e);
		}
	}

	public void mouseMoved(MouseEvent e) {
		// empty
	}

	public void mouseClicked(MouseEvent e) {
		// empty
	}

	public void mouseEntered(MouseEvent e) {
		// empty
	}

	public void mouseExited(MouseEvent e) {
		// empty
	}

	protected Controler getController() {
		// get actionator by descriptor
		Descriptor descriptor = getDescriptor(getLod());
		if (descriptor != null) {
			Object o = descriptor.get(Descriptor.Key.ACTIONATOR);
			if (o != null) {
				return (BasicControler) o;
			}
		}
		return Controler.NONE;
	}

	public class ShowProperties extends AbstractAction {

		public ShowProperties() {
			putValue(Action.NAME, Dictionary.PROPERTIES + "..."); //$NON-NLS-1$
		}

		public void actionPerformed(ActionEvent e) {

			// create and init new dataPropertyPanel
			List<Property> properties = EPanel.this.getData().getProperties();
			PropertyPanel panel = new PropertyPanel(properties);
			panel.init();

			// show dialog
			Manager.getInstance().makeDialog(panel, Dictionary.PROPERTIES.getText());

			// if input result is null, then the dialog was canceled.
			// otherwise set new configuration
			if (panel.isCommitChanges()) {
				EPanel.this.getData().setProperties(properties);
			}
		}
	}

} public class XMLUtil {

	// private default constructor.
	// Just to avoid that this class is getting instantiated
	private XMLUtil() {
		// empty
	}

	// ============================ constants
	// ===================================

	// tabulators indentation levels by writing to xml
	public static final String[] TABS = {"", "\t", "\t\t", "\t\t\t", "\t\t\t\t", "\t\t\t\t\t"}; //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$

	// the header of the eniac xml file
	public static final String ENIAC_HEADER = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?>" //$NON-NLS-1$
			+ "\n" //$NON-NLS-1$
			+ "<!DOCTYPE eniac>" //$NON-NLS-1$
			+ "\n"; //$NON-NLS-1$

	// comment lines to increase readability
	public static final String COMMENT_1 = "<!--###################### "; //$NON-NLS-1$

	public static final String COMMENT_2 = " ######################-->"; //$NON-NLS-1$

	// ========================= tag initialization
	// =============================

	// initializes a number of Strings, each string with its lowecase value.
	// Strings are given in an array of Fields representing public static
	// Strings
	public static void initByLowerCase(Field[] fields) {
		// recurse on all fields
		for (int i = 0; i < fields.length; ++i) {
			try {
				// init field by its lowercased name
				String value = fields[i].getName().toLowerCase();
				fields[i].set(null, value);
			} catch (Exception e) {
				// exception should not occure, because only strings over here.
				e.printStackTrace();
			}
		}
	}

	// ============================ writing xml
	// =================================

	public static void appendCommentLine(List<String> l, int indent, String name) {
		l.add(""); //$NON-NLS-1$
		l.add(TABS[indent] + COMMENT_1 + name + COMMENT_2);
		l.add(""); //$NON-NLS-1$
	}

	public static String wrapAttribute(Enum<?> tag, String value) {
		return " " + tag.name().toLowerCase() + "=\"" + value + "\""; //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
	}

	public static String wrapOpenCloseTag(String tag) {
		return "<" + tag + "/>"; //$NON-NLS-1$ //$NON-NLS-2$
	}

	public static String wrapOpenTag(String tag) {
		return "<" + tag + ">"; //$NON-NLS-1$ //$NON-NLS-2$
	}

	public static String wrapCloseTag(String tag) {
		return "</" + tag + ">"; //$NON-NLS-1$//$NON-NLS-2$
	}

	// ========================== reading xml
	// ===================================

	public static int parseInt(String s, String[] codes) {
		for (int i = 0; i < codes.length; ++i) {
			if (s.equals(codes[i].toString())) {
				return i;
			}
		}
		return -1;
	}

	public static String parseString(Attributes attrs, Enum<?> tag) {
		String s = attrs.getValue(tag.name().toLowerCase());
		if (s == null) {
			throw new DataParsingException(tag, DataParsingException.MISSING_ATTRIBUTE);
		}
		return s;
	}

	public static int parseInt(Attributes attrs, Enum<?> tag) {
		String s = parseString(attrs, tag);
		return StringConverter.toInt(s);
	}

	/**
	 * @param attributes
	 * @param string
	 * @return
	 */
	public static Color parseColor(Attributes attrs, Enum<?> tag) {
		String s = parseString(attrs, tag);
		return StringConverter.toColor(s);
	}

	/**
	 * Parse the value for a tag
	 * 
	 * @param attrs
	 *            : the attributes containing the key-value pair
	 * @param tag
	 *            : the key given as an enum element
	 * @param values
	 *            : an enumSet defining the eligible values
	 * @return the parsed value as an enum element
	 */
	public static <S extends Enum<S>, T extends Enum<T>> T parseEnum(Attributes attrs, Enum<S> tag,
			Class<T> valueEnumClass) {
		String s = parseString(attrs, tag);
		return Enum.valueOf(valueEnumClass, s.toUpperCase());
	}

	public static <S extends Enum<S>> int parseInt(Attributes attrs, Enum<S> codeName, String[] codes) {
		String s = parseString(attrs, codeName);
		int i = parseInt(s, codes);
		if (i == -1) {
			throw new DataParsingException(s, codeName);
		}
		return i;
	}

	public static long parseLong(Attributes attrs, Enum<?> tag) {
		String s = parseString(attrs, tag);
		return StringConverter.toLong(s);
	}

	public static Dimension parseDimension(Attributes attrs, Enum<?> tag) {
		String s = parseString(attrs, tag);
		return StringConverter.toDimension(s);
	}

	public static boolean parseBoolean(Attributes attrs, Enum<?> tag) {
		String s = parseString(attrs, tag);
		return StringConverter.toBoolean(s);
	}

	public static float parseFloat(Attributes attrs, Enum<?> tag) {
		String s = parseString(attrs, tag);
		return StringConverter.toFloat(s);
	}

	public static int[] parseIntArray(Attributes attrs, Enum<?> tag) {
		String s = parseString(attrs, tag);
		return StringConverter.toIntArray(s);
	}
} public abstract class ConditionedProperty extends Property {

	private String _value;

	private JTextField _field;

	public ConditionedProperty(String name, String value) {
		_name = name;
		_value = value;
	}

	public String getValue() {
		setValue(_field.getText());
		return _value;
	}

	public void setValue(String value) {
		if (checkValue(value)) {
			_value = value;
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see eniac.data.property.AbstractProperty#getValueComponent()
	 */
	public JComponent getValueComponent() {
		_field = new JTextField(_value);
		_field.addFocusListener(new FocusAdapter() {
			public void focusLost(FocusEvent e) {
				lostFocus();
			}
		});
		return _field;
	}

	protected abstract boolean checkValue(String value);

	void lostFocus() {
		setValue(_field.getText());
		_field.setText(_value);
	}
} public class ConstantProperty extends Property {

	private String _value;

	public ConstantProperty(String name, String value) {
		_name = name;
		_value = value;
	}

	public String getValue() {
		return _value;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see eniac.data.property.AbstractProperty#getValueComponent()
	 */
	public JComponent getValueComponent() {
		JTextField field = new JTextField(_value);
		field.setEditable(false);
		field.setFocusable(false);
		return field;
	}
} public abstract class Property {

	protected String _name;

	public String getName() {
		return _name;
	}

	public JLabel getNameLabel() {
		return new JLabel(_name.toString());
	}

	public abstract JComponent getValueComponent();
} public class StringConverter {

	private StringConverter() {
		// empty
	}

	/*
	 * ======================= converting string to others =====================
	 */

	/**
	 * Converts a <code>string</code> into an array of <code>int</code>. The
	 * string should contain numbers divided by a komma (,).
	 * 
	 * @param s
	 *            the <code>string</code> to be converted
	 * @return an array of <code>int</code>, or <code>null</code>, if the string
	 *         was <code>null</code> itself or contained bad data.
	 */
	public static int[] toIntArray(String s) {
		s = s.trim();
		StringTokenizer tt = new StringTokenizer(s, ",\n\t ");
		int[] array = new int[tt.countTokens()];
		int i = 0;
		while (tt.hasMoreElements()) {
			array[i++] = Integer.parseInt((String) tt.nextElement());
		}
		return array;
	}

	/**
	 * Converts a <code>string</code> into a <code>dimension</code>.
	 * 
	 * @param s
	 *            the <code>string</code> to be converted
	 * @return a <code>dimension</code> or <code>null</code> if no conversion
	 *         was possible.
	 */
	public static Dimension toDimension(String s) {
		int[] array = toIntArray(s);
		if (array.length < 2) {
			throw new RuntimeException("cannot convert to dimension: [" + s + "]");
		}
		return new Dimension(array[0], array[1]);
	}

	/**
	 * Converts a <code>string</code> into a <code>point</code>.
	 * 
	 * @param s
	 *            the <code>string</code> to be converted
	 * @return a <code>point</code> or <code>null</code> if no conversion was
	 *         possible.
	 */
	public static Point toPoint(String s) {
		int[] array = toIntArray(s);
		if (array.length < 2) {
			throw new RuntimeException("cannot convert to point: [" + s + "]");
		}
		return new Point(array[0], array[1]);
	}

	/**
	 * Converts a <code>string</code> into a <code>rectangle</code>.
	 * 
	 * @param s
	 *            the <code>string</code> to be converted
	 * @return a <code>rectangle</code> or <code>null</code> if no conversion
	 *         was possible.
	 */
	public static Rectangle toRectangle(String s) {
		int[] array = toIntArray(s);
		if (array.length < 4) {
			throw new RuntimeException("cannot convert to rectangle: [" + s + "]");
		}
		return new Rectangle(array[0], array[1], array[2], array[3]);
	}

	/**
	 * Converts a <code>string</code> into a <code>boolean</code>. If s is
	 * <code>null</code>, then <code>false</code> will be returned.
	 * 
	 * @param s
	 *            the <code>string</code> to be converted
	 * @return a <code>boolean</code> as result of conversion.
	 */
	public static boolean toBoolean(String s) {
		return Boolean.valueOf(s).booleanValue();
	}

	public static int toInt(String s) {
		return Integer.parseInt(s);
	}

	public static Color toColor(String s) {
		return new Color(Integer.parseInt(s, 16));
	}

	public static float toFloat(String s) {
		return Float.parseFloat(s);
	}

	/**
	 * @param string
	 * @return
	 */
	public static long toLong(String s) {
		return Long.parseLong(s);
	}

	/*
	 * ======================= converting others to string =====================
	 */

	public static String toString(int[] array) {
		String s = "";
		if (array.length > 0) {
			s += array[0];
			for (int i = 1; i < array.length; ++i) {
				s += ",";
				s += array[i];
			}
		}
		return s;
	}
}
import eniac.property.ConditionedProperty;
import eniac.property.ConstantProperty;
import eniac.property.Property;
import eniac.util.StringConverter;
import java.util.LinkedList;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
import org.xml.sax.Attributes;
import eniac.data.IDManager;
import eniac.data.model.parent.Configuration;
import eniac.data.model.parent.ParentData;
import eniac.data.model.unit.Unit;
import eniac.data.type.EType;
import eniac.data.view.EPanel;
import eniac.io.XMLUtil;
/*******************************************************************************
 * Copyright (c) 2003-2005, 2013 Till Zoppke.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Public License v3.0
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/gpl.html
 * 
 * Contributors:
 *     Till Zoppke - initial API and implementation
 ******************************************************************************/
/*
 * Created on 25.10.2003
 *
 * To change the template for this generated file go to
 * Window - Preferences - Java - Code Generation - Code and Comments
 */
package eniac.data.model;




/**
 * @author zoppke
 * 
 *         To change the template for this generated type comment go to Window -
 *         Preferences - Java - Code Generation - Code and Comments
 */
public class EData extends Observable implements Comparable<EData> {

	// static key indicating that that a repaint is recommended.
	public static final String REPAINT = "repaint"; //$NON-NLS-1$

	public static final String PAINT_IMMEDIATELY = "paint_immediately"; //$NON-NLS-1$

	public enum Tag {
		// tags
		ENIAC, PATH, NAME,

		// attributes
		ID, VALUE, NUMBER, POWER, PARTNER, LOCATION, IO, GRID, SIZE, INDEX, FLAG,

		// attribute values
		IN, OUT, BOTH, FALSE, TRUE,
	}

	// ============================= fields
	// =====================================

	// type of this dataObject as encoded by class DataTypes.
	// parsed from xml-tag
	protected EType _type;

	// unique id of this dataObject. All dataObjects have different ids
	// parsed from xml-attributes
	protected int _id;

	// name of this dataObject.
	// parsed from xml-attributes
	protected String _name;

	// numbers in parent's grid for layouting
	// parsed from xml-attributes
	protected int[] _gridNumbers;

	// index at parent.
	// this is not necessarily the same index as in the kindergarten.
	// two children may share the same index
	// (eg. two programconnectors in a tray)
	protected int _index;

	// parent dataObject. Configuration is root dataObject.
	protected ParentData _parent = null;

	// ============================= lifecycle
	// ==================================

	// default Constructor
	public EData() {
		// empty constructor
	}

	// constructor used by the parser's default handler
	public void setAttributes(Attributes attrs) {
		_id = XMLUtil.parseInt(attrs, Tag.ID);