public class Manager {

	/*
	 * ========================= applet lifecycle states =======================
	 */

	public enum LifeCycle {

		/**
		 * Default lifecycle state on startup
		 */
		DEFAULT,

		/**
		 * Livecycle state indicating a successful initialization
		 */
		INITIALIZED,

		/**
		 * Lifecycle state indicating that the application is running and the
		 * gui expects input.
		 */
		RUNNING,

		/**
		 * Lifecycle state indicating that the application is running but the
		 * gui is blocked.
		 */
		BLOCKED,

		/**
		 * Lifecycle state indicating that the application is stopped.
		 */
		STOPPED,

		/**
		 * Lifecycle state indicating that the application is destroyed.
		 */
		DESTROYED,
	}

	/*
	 * =============================== fields ==================================
	 */

	// flag indicating, whether we have privileged local file access
	private boolean _ioAccess;

	// reference to the applet. Stays null, if started as application.
	private Applet _applet = null;

	/*
	 * ========================== singleton stuff ==============================
	 */

	private Manager() {
		// empty constructor
	}

	// singleton self reference
	private static Manager instance = null;

	public static Manager getInstance() {
		if (instance == null) {
			instance = new Manager();
		}
		return instance;
	}

	/*
	 * =================== lifecycle state transitions =========================
	 */

	/**
	 * creates and initializes all instances
	 */
	public void init() {
		Status.initValues();
		DictionaryIO.loadDefaultLanguage();
		SkinIO.loadDefaultSkin();
		Status.LIFECYCLE.setValue(LifeCycle.INITIALIZED);
	}

	public void start() {

		// loading the configuration may open an input dialog for the user.
		// as the java-plugin may expect this method to return quickly,
		// we start a new thread.
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {

				// load types
				TypeHandler.loadTypes();

				// load default configuration
				ConfigIO.loadDefaultConfiguration();

				// check, if we haven't been interrupted
				if (Status.LIFECYCLE.getValue() == LifeCycle.INITIALIZED) {

					// open eframe and adjust runlevel
					EFrame.getInstance().toScreen();
					LogWindow.getInstance();
					Status.LIFECYCLE.setValue(LifeCycle.RUNNING);
				}
			}
		});
	}

	public void stop() {

		// TODO: check, if we need to block first

		// dispose configuration
		Configuration config = (Configuration) Status.CONFIGURATION.getValue();
		if (config != null) {
			config.dispose();
		}
		Status.CONFIGURATION.setValue(null);

		// announce that applet is shutting down
		Status.LIFECYCLE.setValue(LifeCycle.STOPPED);
	}

	public void destroy() {

		// check that we haven't been destroyed before
		if (Status.LIFECYCLE.getValue() != LifeCycle.STOPPED) {
			return;
		}

		// run finalization.
		// Though it probably has no effect, the purpose provides good fortune.
		System.runFinalization();

		// announce that applet is destroyed
		Status.LIFECYCLE.setValue(LifeCycle.DESTROYED);
	}

	/*
	 * ============================== methods ==================================
	 */

	public void setApplet(Applet applet) {
		_applet = applet;
	}

	public void makeDialog(DialogPanel content, String title) {
		// create dialog. Add listener and set content pane
		final JDialog dialog = new JDialog(Progressor.getInstance(), title, true);
		dialog.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				((DialogPanel) dialog.getContentPane()).performCancelAction();
			}
		});
		dialog.setContentPane(content);

		// bring it to the screen.
		dialog.pack();
		dialog.setLocationRelativeTo(Progressor.getInstance());
		content.setWindow(dialog);
		dialog.setVisible(true);
	}

	/*
	 * public boolean isPrivileged() { // if we are started as an application,
	 * we have all privileges if (!isApplet()) { return true; } // check, if we
	 * already tried to set our SecurityManager if (_privileged != null) {
	 * return _privileged.booleanValue(); } // This is an applet. Ask for
	 * priviliges. try { // anonymous security manager granting any permission
	 * new SecurityManager() { public void checkPermission(Permission
	 * permission) { // grant any permission }
	 * 
	 * public void checkPermission(Permission permission, Object obj) { // grant
	 * any permission } }; // user allowed the signed applet. Set flag.
	 * _privileged = new Boolean(true); return true; } catch
	 * (AccessControlException ace) { // User didn't allow the signed applet. //
	 * Reset flag and display message. // ace.printStackTrace();
	 * Log.log(LogWords.NO_PRIVILEGES_GRANTED, JOptionPane.INFORMATION_MESSAGE,
	 * ace.getMessage(), true); _privileged = new Boolean(false); return false;
	 * } }
	 */
	// indicates whether this program is started as applet or as application.
	// private boolean isApplet() {
	// return getAppletContext() == null;
	// }
	public void block() {
		if (Status.LIFECYCLE.getValue() == LifeCycle.RUNNING) {
			Status.LIFECYCLE.setValue(LifeCycle.BLOCKED);
		}
	}

	public void unblock() {
		if (Status.LIFECYCLE.getValue() == LifeCycle.BLOCKED) {
			Status.LIFECYCLE.setValue(LifeCycle.RUNNING);
		}
	}

	// ============================ io methods //===============================

	/**
	 * Opens an <code>inputStream</code> to the specified resource.
	 * 
	 * @param name
	 *            a <code>string</code> specifying the resource to load
	 * @return an <code>inputStream</code> if the resource could be found, <br>
	 *         or <code>null</code> otherwise
	 */
	public InputStream getResourceAsStream(String name) {
		return Manager.class.getClassLoader().getResourceAsStream(name);
	}

	public void setIOAccess(boolean ioAccess) {
		_ioAccess = ioAccess;
	}

	public boolean hasIOAccess() {
		return _ioAccess;
	}
} public class ConfigIO {

	// private constructor to prevent from initializing this class
	private ConfigIO() {
		// empty constructor
	}

	// =========================== public methods
	// ===============================

	/**
	 * Writes an xml representation of this configuration to a file. If the file
	 * already exists, the user will be asked if he wants to overwrite.
	 */
	public static void write(File file, Configuration config, Proxy proxy) throws IOException {

		// check, if file already exists
		if (file.exists()) {
			// ask user to confirm overwrite
			int option = JOptionPane.showConfirmDialog(EFrame.getInstance(), file.getName()
					+ Dictionary.CONFIRM_OVERWRITE_TEXT.getText(), Dictionary.CONFIRM_OVERWRITE_TITLE.getText(),
					JOptionPane.OK_CANCEL_OPTION);
			if (option == JOptionPane.CANCEL_OPTION) {
				// if writing canceled, return
				return;
			}
		}

		// announce that we are writing the configuration
		Progressor.getInstance().setText(Dictionary.CONFIGURATION_WRITING.getText());
		int max = config.getGarten().getAllKinder().length;
		Progressor.getInstance().setProgress(0, max);

		// collect all tags in a list
		List<String> list = new LinkedList<>();
		list.add(XMLUtil.ENIAC_HEADER);
		list.add(XMLUtil.wrapOpenTag(EData.Tag.ENIAC.name().toLowerCase()));
		proxy.appendTags(list, 1);
		config.appendTags(list, 1);
		list.add(XMLUtil.wrapCloseTag(EData.Tag.ENIAC.name().toLowerCase()));

		// convert list to stringbuffer
		StringBuilder stringBuilder = new StringBuilder();
		for (String s : list) {
			stringBuilder.append(s);
			stringBuilder.append('\n');
		}

		// write stringbuffer to file.
		Writer writer = IOUtil.openWriter(file);
		writer.write(stringBuilder.toString());
		writer.flush();
		writer.close();
	}

	/**
	 * Loads a configuration from the local file system
	 * 
	 * @param path
	 *            a <code>string</code> containing the configuration's path
	 */
	public static void loadConfiguration(String path) {
		InputStream in = IOUtil.openInputStream(path);
		loadConfiguration(in);
	}

	/**
	 * Loads a basic configuration from the classLoader as described by a
	 * <code>configurationProxy</code>
	 * 
	 * @param proxy
	 *            the <code>configurationProxy</code> identifying the
	 *            configuration to load.
	 */
	public static void loadConfiguration(Proxy proxy) {
		String path = proxy.getPath();
		InputStream in = Manager.getInstance().getResourceAsStream(path);
		loadConfiguration(in);
	}

	public static List<Proxy> loadProxies() {
		String path = getPathWithoutIndex();
		int max = StringConverter.toInt(EProperties.getInstance().getProperty("MAX_NUMBER_OF_CONFIGS"));
		String text = Dictionary.CONFIGURATION_SCANNING.getText();
		return IOUtil.loadProxies(path, max, text);
	}

	public static void loadDefaultConfiguration() {
		if (EProperties.getInstance().getProperty("INDEX_OF_DEFAULT_CONFIG").startsWith("-")) {

			// if no default configuration is given, let user choose one
			(new OpenConfiguration()).run();
		}
		else {

			// otherwise load configuration as given by default index
			int index = StringConverter.toInt(EProperties.getInstance().getProperty("INDEX_OF_DEFAULT_CONFIG"));
			String path = getPathWithoutIndex();
			String fullPath = IOUtil.addIndex(path, index);
			Proxy proxy = IOUtil.loadProxy(fullPath);
			loadConfiguration(proxy);
		}
	}

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

	private static String getPathWithoutIndex() {
		String folder = EProperties.getInstance().getProperty("CONFIG_FOLDER");
		String file = EProperties.getInstance().getProperty("CONFIG_FILE_WITHOUT_INDEX");
		return folder + "/" + file; //$NON-NLS-1$
	}

	// this is called, when loading the configuration failed
	private static void loadingFailed(String reason) {
		Log.log(LogWords.COULD_NOT_LOAD_CONFIGURATION, JOptionPane.ERROR_MESSAGE, reason, true);
	}

	private static void loadingSucceded(Configuration newConfig) {

		// dispose old configuration object, if there is any
		// EFrame.getInstance().disposeConfigPanel();
		Object oldConfig = Status.CONFIGURATION.getValue();
		Status.CONFIGURATION.setValue(null);
		if (oldConfig != null) {
			((Configuration) oldConfig).dispose();
		}

		// init new configuration object
		newConfig.init();

		// there were invalid ids. Display message to user.
		if (newConfig.getIDManager().hasInvalids()) {
			newConfig.getIDManager().integrateInvalids();
			Log.log(LogWords.INVALID_IDS_CONTAINED, JOptionPane.INFORMATION_MESSAGE, true);
		}

		// set new configuration Object as current Configuration
		Status.CONFIGURATION.setValue(newConfig);
	}

	// loads a configuration from the given inputStream.
	private static void loadConfiguration(InputStream in) {

		// check for null
		if (in == null) {
			loadingFailed("Inputstream is null"); //$NON-NLS-1$
			return;
		}

		// create handler
		ConfigHandler handler = new ConfigHandler();

		// parse data tree from xml
		try {
			IOUtil.parse(in, handler);
			in.close();
		} catch (IOException e) {
			e.printStackTrace();
		}

		// check, if parsing was successful
		Configuration config = handler.getConfiguration();
		if (config != null) {
			loadingSucceded(config);
		}
		else {
			loadingFailed("new configuration is null"); //$NON-NLS-1$
		}
	}
} 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 IOUtil {

	// private constructor to avoid someone creates an instance of this class
	private IOUtil() {
		// empty
	}

	public static Proxy loadProxy(String path, ProxyHandler handler) {
		InputStream in = Manager.getInstance().getResourceAsStream(path);
		if (in == null) {
			return null;
		}
		try {
			IOUtil.parse(in, handler);
			Proxy proxy = handler.getProxy();
			proxy.setPath(path);
			return proxy;
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		}
	}

	public static Proxy loadProxy(String path) {
		ProxyHandler handler = new ProxyHandler();
		return loadProxy(path, handler);
	}

	public static List<Proxy> loadProxies(String path, int maxIndex, String text) {
		ProxyScanner scanner = new ProxyScanner(path, maxIndex, text);
		return scanner.getProxies();
	}

	public static String addIndex(String fileName, int index) {
		return addIndices(fileName, index, index)[0];
	}

	public static String[] addIndices(String fileName, int min, int max) {

		// divide fileName
		int dot = fileName.lastIndexOf('.');
		String path1 = fileName.substring(0, dot);
		String path2 = fileName.substring(dot);

		// add name and indices to return array
		int length = max - min + 1;
		String[] retour = new String[length];
		for (int i = 0; i < retour.length; ++i) {
			retour[i] = path1 + (i + min) + path2;
		}

		// return array;
		return retour;
	}

	public static void parse(InputStream in, DefaultHandler handler) throws IOException {
		try {
			// parse from the stream with the given handler
			SAXParserFactory factory = SAXParserFactory.newInstance();
			SAXParser saxParser = factory.newSAXParser();
			saxParser.parse(in, handler);
		} catch (SAXException e) {
			e.printStackTrace();
			throw new IOException(e.getMessage());
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
			throw new IOException(e.getMessage());
		}
	}

	// ======================== file access methods
	// =============================

	/**
	 * Opens an <code>inputStream</code> to the specified file.
	 * 
	 * @param path
	 *            a <code>string</code> as the name of the file to load
	 * @return an <code>inputStream</code> if the file could be found, <br>
	 *         or <code>null</code> otherwise
	 */
	public static InputStream openInputStream(String path) {
		try {
			return new FileInputStream(path);
		} catch (IOException e) {
			return null;
		}
	}

	public static Writer openWriter(File file) throws IOException {
		return new FileWriter(file, false);
	}

	public static File getSettingsFile() {
		return new File(System.getProperty("user.home") //$NON-NLS-1$
				+ File.separator + EProperties.getInstance().getProperty("SETTINGS_FILE"));
	}

	public static FileFilter getFileFilter() {
		return new FileFilter() {
			public String getDescription() {
				return Dictionary.FILE_FILTER_DESCRIPTION.getText();
			}

			public boolean accept(File file) {
				return file.isDirectory() || file.getName().endsWith(".xml") //$NON-NLS-1$
						|| file.getName().endsWith(".eniac"); //$NON-NLS-1$
			}
		};
	}

} public class Proxy extends EnumMap<Proxy.Tag, String> {

	/**
	 * Enumeration of all tags that are understood by the proxy handler
	 * 
	 * @author till
	 * 
	 *         TODO
	 */
	public enum Tag {

		/**
		 * the proxy tag. data outside this section will be ignored. Any tag
		 * inside this section shall be registered as enum constant in Proxy.Tag
		 */
		PROXY,

		/**
		 * the author of a skin
		 */
		AUTHOR,

		/**
		 * the email address of the author of a skin
		 */
		EMAIL,

		/**
		 * number of LODs in a skin (should be 2) TODO: refactor, we don't need
		 * it
		 */
		NUMBER_OF_LODS,

		/**
		 * the number of descriptors in a skin TODO: do we need this any more?
		 */
		NUMBER_OF_DESCRIPTORS,

		/**
		 * zoom steps for the user to zoom in & out
		 */
		ZOOM_STEPS,

		/**
		 * path to a preview image of the skin
		 */
		PREVIEW,

		/**
		 * name of a configuration or language
		 */
		NAME,

		/**
		 * description of the configuration or language
		 */
		DESCRIPTION,

		/**
		 * string-key of the language, as the 2-letter locale
		 */
		KEY,
	}

	private String _path;

	public Proxy() {
		super(Proxy.Tag.class);
	}

	@Override
	public String toString() {
		return get(Tag.NAME); // need this to be displayed in a jlist
	}

	public void setPath(String path) {
		_path = path;
	}

	public String getPath() {
		return _path;
	}

	public void appendTags(List<String> l, int indent) {

		// append comment line and open tag
		XMLUtil.appendCommentLine(l, indent, Tag.PROXY.toString());
		l.add(XMLUtil.TABS[indent] + XMLUtil.wrapOpenTag(Tag.PROXY.toString()));

		// append child tags
		String tabs = XMLUtil.TABS[indent + 1];
		for (Map.Entry<Proxy.Tag, String> entry : entrySet()) {
			String open = XMLUtil.wrapOpenTag(entry.getKey().toString().toLowerCase());
			String close = XMLUtil.wrapCloseTag(entry.getKey().toString().toLowerCase());
			l.add(tabs + open + entry.getValue() + close);
		}

		// append close tags
		l.add(XMLUtil.TABS[indent] + XMLUtil.wrapCloseTag(Tag.PROXY.toString()));
	}
} public enum Dictionary {

	/**
	 * Name of the "Open-Skin" command.
	 */
	OPEN_SKIN_NAME,

	/**
	 * Short description of the "Open-Skin" command.
	 */
	OPEN_SKIN_SHORT,

	/**
	 * Name of the "Open Configuration" command.
	 */
	OPEN_CONFIGURATION_NAME,

	/**
	 * Short description of the "Open configuration" command.
	 */
	OPEN_CONFIGURATION_SHORT,

	/**
	 * Name of the "Save Configuration" command.
	 */
	SAVE_CONFIGURATION_NAME,

	/**
	 * Short description of the "Save configuration" command.
	 */
	SAVE_CONFIGURATION_SHORT,

	/**
	 * Name of the "Zoom in" command.
	 */
	ZOOM_IN_NAME,

	/**
	 * Short description of the "Zoom in" command.
	 */
	ZOOM_IN_SHORT,

	/**
	 * Name of the "Zoom out" command.
	 */
	ZOOM_OUT_NAME,

	/**
	 * Short description of the "Zoom out" command.
	 */
	ZOOM_OUT_SHORT,

	/**
	 * Title of the "Confirm overwrite" dialog.
	 */
	CONFIRM_OVERWRITE_TITLE,

	/**
	 * Text at the "Confirm overwrite" dialog.
	 */
	CONFIRM_OVERWRITE_TEXT,

	/**
	 * Name of the "Change Language" command.
	 */
	CHANGE_LANGUAGE_NAME,

	/**
	 * Short description of the "Change Language" command.
	 */
	CHANGE_LANGUAGE_SHORT,

	/**
	 * Name of the "Show log-window" dialog.
	 */
	SHOW_LOG_NAME,

	/**
	 * Short description of the "Show log-window" command.
	 */
	SHOW_LOG_SHORT,

	/**
	 * Name of the "Show Overview-window" command.
	 */
	SHOW_OVERVIEW_NAME,

	/**
	 * Short description of the "Show Overview-window" command.
	 */
	SHOW_OVERVIEW_SHORT,

	/**
	 * Name of the "Fit zoom to height" command.
	 */
	ZOOM_FIT_HEIGHT_NAME,

	/**
	 * Short description of the "Fit zoom to height" command.
	 */
	ZOOM_FIT_HEIGHT_SHORT,

	/**
	 * Name of the "Fit zoom to width" command.
	 */
	ZOOM_FIT_WIDTH_NAME,

	/**
	 * Short description of the "Fit zoom to width" command.
	 */
	ZOOM_FIT_WIDTH_SHORT,

	/**
	 * Text at the "Open configuration" dialog.
	 */
	CHOOSE_WEB_LOCATION,

	/**
	 * Text at the "Open configuration" dialog.
	 */
	CHOOSE_FILE,

	/**
	 * Text at the "Open configuration" dialog.
	 */
	LOAD_FROM_FILE,

	/**
	 * The word for "value".
	 */
	VALUE,

	/**
	 * Title of the Main-frame.
	 */
	MAIN_FRAME_TITLE,

	/**
	 * Title of the Overview-window.
	 */
	OVERVIEW_WINDOW_TITLE,

	/**
	 * Title of the "Log window".
	 */
	LOG_WINDOW_TITLE,

	/**
	 * The word for "properties".
	 */
	PROPERTIES,

	/**
	 * The word for "ok".
	 */
	OK,

	/**
	 * The word for "cancel".
	 */
	CANCEL,

	/**
	 * Message in the blocking-dialog that we are scanning for configurations.
	 */
	CONFIGURATION_SCANNING,

	/**
	 * Description of the file filter at File-Chooser-Dialogs.
	 */
	FILE_FILTER_DESCRIPTION,

	/**
	 * Message in the blocking-dialog that we are loading a dictionary.
	 */
	DICTIONARY_LOADING,

	/**
	 * Message in the blocking-dialog that we are scanning for Dictionaries.
	 */
	DICTIONARY_SCANNING,

	/**
	 * Message in the blocking-dialog that we are loading the menu.
	 */
	MENU_LOADING,

	/**
	 * Message in the blocking-dialog that we are scanning for skin files.
	 */
	SKIN_SCANNING,

	/**
	 * Message in the blocking-dialog that we are loading a skin file.
	 */
	SKIN_LOADING,

	/**
	 * Polite busy message.
	 */
	PLEASE_WAIT,

	/**
	 * Title of the "Save Configuration" dialog.
	 */
	SAVE_CONFIGURATION_TITLE,

	/**
	 * The word for "write".
	 */
	WRITE,

	/**
	 * The word for "next".
	 */
	NEXT,

	/**
	 * Enter details before saving a configuration.
	 */
	ENTER_DETAILS,

	/**
	 * The word for "name".
	 */
	NAME,

	/**
	 * The word for "description".
	 */
	DESCRIPTION,

	/**
	 * The title of the (floating) toolbar.
	 */
	TOOLBAR_TITLE,

	/**
	 * The word for "file".
	 */
	FILE,

	/**
	 * The word for "window" (not: windows).
	 */
	WINDOW,

	/**
	 * The word for "view".
	 */
	VIEW,

	/**
	 * The word for "zoom".
	 */
	ZOOM,

	/**
	 * The word for "help".
	 */
	HELP,

	/**
	 * The name of the "Hightlight pule" command.
	 */
	HIGHLIGHT_PULSE_NAME,

	/**
	 * Short description of the "Highlight pulse" command.
	 */
	HIGHLIGHT_PULSE_SHORT,

	/**
	 * Name of the "About this program" command.
	 */
	ABOUT_NAME,

	/**
	 * Short description of the "About this program" command.
	 */
	ABOUT_SHORT,

	/**
	 * Text of the "About this program" dialog.
	 */
	ABOUT_TEXT,

	/**
	 * Name of the "faq" command.
	 */
	FAQ_NAME,

	/**
	 * Short description of the "faq" command.
	 */
	FAQ_SHORT,

	/**
	 * Text of the "faq" dialog.
	 */
	FAQ_TEXT,

	/**
	 * Name of the "settings" command.
	 */
	SETTINGS_NAME,

	/**
	 * Short description of the "settings" command.
	 */
	SETTINGS_SHORT,

	/**
	 * Text at the "save settings" dialog.
	 */
	SAVE_SETTINGS_TEXT,

	/**
	 * Title of the "Save settings" dialog.
	 */
	SAVE_SETTINGS_TITLE,

	/**
	 * Message in the blocking-dialog that we are initializing.
	 */
	INITIALIZING,

	/**
	 * Message in the blocking-dialog that we are loading a configuration.
	 */
	CONFIGURATION_LOADING,

	/**
	 * Message in the blocking-dialog that we are writing a configuration.
	 */
	CONFIGURATION_WRITING;

	/**
	 * Looks for the words associated with the given key. If the key is invalid,
	 * the key itself is returned.
	 * 
	 * @param sid
	 *            a <code>string</code> to identify the words we are looking
	 *            for.
	 * @return the value of the static field with the uppercased name of 'key'
	 *         or the value of key itself, if there is no field.
	 */

	private EnumMap<Dictionary, String> _map = null;

	public void checkInit() {

		// if already initialized, just return.
		if (_map != null) {
			return;
		}

		// create new dictionary map
		_map = new EnumMap<>(Dictionary.class);

		// Init special fields with predefined values. These are those words
		// displayed before the language data is loaded.
		_map.put(INITIALIZING, "Eniac simulation is starting up. Please wait."); //$NON-NLS-1$
		_map.put(DICTIONARY_LOADING, "loading language file"); //$NON-NLS-1$
		_map.put(DICTIONARY_SCANNING, "scanning for language files"); //$NON-NLS-1$
		_map.put(PLEASE_WAIT, "Please wait"); //$NON-NLS-1$
	}

	public String getText() {
		checkInit();
		return _map.get(this);
	}

	public void setText(String text) {
		checkInit();
		_map.put(this, text);
	}

	public enum Tag {
		ENTRY, KEY
	}

//
// try {
// Field field = Dictionary.class.getField(sid.toUpperCase());
// return (String) field.get(null);
// } catch (Exception e) {
// // unknown key. return key as value.
// // TODO: log this
// return sid;
// }
// }

} public class Log implements StatusListener {

	// containing all text that was passed to this logger
	private StringBuilder _stringBuilder = new StringBuilder();

	// vector containing all registered logListeners
	private List<LogListener> _logListeners = new LinkedList<>();

	// reference to LogPanel
	private LogPanel _logPanel = null;

	// ========================== singleton stuff
	// ===============================

	// self reference
	private static Log instance = null;

	private Log() {
		// empty
	}

	public static synchronized Log getInstance() {
		if (instance == null) {
			instance = new Log();
			instance.init();
		}
		return instance;
	}

	private void init() {
		Status.LIFECYCLE.addListener(this);
		addLogListener(new ConsolePrinter());
		addLogListener(new DialogPrinter());
	}

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

	/**
	 * @return
	 */
	public String getText() {
		return _stringBuilder.toString();
	}

	/**
	 * adds a line to this stringbuffer
	 * 
	 * @param line
	 *            A string to be added to this logger
	 */
	private void addMessage(LogMessage message) {
		_stringBuilder.append(message);
		_stringBuilder.append('\n');

		// inform listeners
		for (LogListener listener : _logListeners) {
			listener.incomingMessage(message);
		}
	}

	/**
	 * empties the Logger.
	 */
	public void clear() {
		_stringBuilder = new StringBuilder();

		// inform listeners
		for (LogListener listener : _logListeners) {
			listener.cleared();
		}
	}

	/**
	 * Registers a logListener at this logger
	 * 
	 * @param listener
	 *            the listener to be added
	 */
	public void addLogListener(LogListener listener) {
		_logListeners.add(listener);
	}

	/**
	 * Removes a logListener from this logger
	 * 
	 * @param listener
	 *            The listener to be removed
	 */
	public void removeLogListener(LogListener listener) {
		_logListeners.remove(listener);
	}

	/**
	 * @return
	 */
	public LogPanel getLogPanel() {
		if (_logPanel == null) {
			_logPanel = new LogPanel();
			_logPanel.init();
			addLogListener(_logPanel);
		}
		return _logPanel;
	}

	// ======================== convenience methods
	// =============================

	public static void log(String message) {
		getInstance().addMessage(new LogMessage(message));
	}

	public static void log(String message, int type) {
		getInstance().addMessage(new LogMessage(message, type));
	}

	public static void log(String message, int type, Object[] objects) {
		getInstance().addMessage(new LogMessage(message, type, objects, false));
	}

	public static void log(String message, int type, Object object) {
		getInstance().addMessage(new LogMessage(message, type, new Object[]{object}, false));
	}

	public static void log(String message, int type, Object object, boolean forUser) {

		getInstance().addMessage(new LogMessage(message, type, new Object[]{object}, forUser));
	}

	public static void log(String message, int type, boolean forUser) {
		getInstance().addMessage(new LogMessage(message, type, forUser));
	}

	@Override
	public void statusChanged(Status status, Object newValue) {
		if (newValue == Manager.LifeCycle.DESTROYED) {
			instance = null;
		}
	}
} public class LogWords {

	public static final String

	// messages
			IMAGE_NOT_FOUND = "Cannot find image", //$NON-NLS-1$
			MISSING_IMAGES = "Some images are missing." //$NON-NLS-1$
					+ " Maybe this skin cannot be displayed correctly.", //$NON-NLS-1$
			LOADING_OF_SKIN_FAILED = "Loading of Skin Failed.", //$NON-NLS-1$
			CANNOT_SAVE_FILE = "Cannot save file. No privileges to access local file system. " //$NON-NLS-1$
					+ "If you want to write file, " //$NON-NLS-1$
					+ "please restart and accept certificate.", //$NON-NLS-1$
			NO_PRIVILEGES_GRANTED = "No privileges granted. No file access.", //$NON-NLS-1$
			COULD_NOT_LOAD_CONFIGURATION = "Could not load new configuration.", //$NON-NLS-1$
			INVALID_IDS_CONTAINED = "Configuration contained invalid IDs. " //$NON-NLS-1$
					+ "Maybe it is not working properly."; //$NON-NLS-1$

	private LogWords() {
		// empty
	}
} public class SaveConfigurationPanel extends DialogPanel {

	// actions
	private Action _cancelAction;

	private Action _nextAction;

	// jpanel
	private JPanel _panel;

	// labels and textfields for name and description
	private JLabel _nameLabel;

	private JLabel _descriptionLabel;

	private JTextField _nameField;

	private JTextArea _descriptionArea;

	private JScrollPane _scrollPane;

	// exit status
	private boolean _nextPressed = false;

	public SaveConfigurationPanel() {
		super(new GridBagLayout());
	}

	/**
	 * Initializes this openConfigurationPanel
	 */
	public void init() {

		// ========================== actions
		// ===================================

		// create and add okAction
		_nextAction = new AbstractAction() {
			public void actionPerformed(ActionEvent e) {
				performOkAction();
			}
		};
		_nextAction.putValue(Action.NAME, Dictionary.NEXT.getText());
		getActionMap().put(_nextAction.getValue(Action.NAME), _nextAction);

		// create and add cancelAction
		_cancelAction = new AbstractAction() {
			public void actionPerformed(ActionEvent e) {
				performCancelAction();
			}
		};
		_cancelAction.putValue(Action.NAME, Dictionary.CANCEL.getText());
		getActionMap().put(_cancelAction.getValue(Action.NAME), _cancelAction);

		// ============================= jpanel
		// =================================

		// create components
		_panel = new JPanel(new GridBagLayout());
		_panel.setBorder(BorderFactory.createTitledBorder(Dictionary.ENTER_DETAILS.getText()));
		_nameLabel = new JLabel(Dictionary.NAME.getText());
		_descriptionLabel = new JLabel(Dictionary.DESCRIPTION.getText());
		_nameField = new JTextField(20);
		_descriptionArea = new JTextArea(3, 20);
		_scrollPane = new JScrollPane(_descriptionArea);

		// add components to panel
		_panel.add(_nameLabel, new GridBagConstraints(0, 0, 1, 1, 1.0, 0.0, GridBagConstraints.EAST,
				GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
		_panel.add(_nameField, new GridBagConstraints(1, 0, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER,
				GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
		_panel.add(_descriptionLabel, new GridBagConstraints(0, 1, 1, 1, 1.0, 0.0, GridBagConstraints.EAST,
				GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
		_panel.add(_scrollPane, new GridBagConstraints(1, 1, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER,
				GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));

		// add panel to saveConfigPanel
		add(_panel, new GridBagConstraints(0, 0, 2, 1, 1.0, 0.0, GridBagConstraints.CENTER,
				GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));

		// =========================== buttons
		// ==================================

		// create and init buttons
		JButton okButton = new JButton(_nextAction);
		JButton cancelButton = new JButton(_cancelAction);

		// layout components
		add(okButton, new GridBagConstraints(0, 1, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER,
				GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
		add(cancelButton, new GridBagConstraints(1, 1, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER,
				GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));

		// ============================= add keystrokes
		// =========================

		// fill actionMap
		getActionMap().put(_nextAction.getValue(Action.NAME), _nextAction);
		getActionMap().put(_cancelAction.getValue(Action.NAME), _cancelAction);

		// fill inputMap
		getInputMap(WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0),
				_nextAction.getValue(Action.NAME));
		getInputMap(WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
				_cancelAction.getValue(Action.NAME));

		// adjust inputMaps of buttons
		cancelButton.getActionMap().setParent(getActionMap());
		cancelButton.getInputMap(WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0),
				_cancelAction.getValue(Action.NAME));
	}

	// ================================= getter
	// =================================

	public boolean isNextPressed() {
		return _nextPressed;
	}

	public String getName() {
		return _nameField.getText();
	}

	public String getDescription() {
		return _descriptionArea.getText();
	}

	// ========================== action methods
	// ================================

	public void performCancelAction() {
		_nextPressed = false;
		SwingUtilities.windowForComponent(SaveConfigurationPanel.this).dispose();
	}

	void performOkAction() {
		_nextPressed = true;
		SwingUtilities.windowForComponent(SaveConfigurationPanel.this).dispose();
	}

} public class EProperties extends Properties {

	/**
	 * Properties file to be loaded from the class path
	 */
	public static final String fileName = "eniac.properties";

	/*
	 * ========================= singleton stuff ===============================
	 */

	// singleton self reference
	private static EProperties instance;

	// singleton private constructor
	private EProperties() {

		// load properties
		try {
			load(Manager.class.getClassLoader().getResourceAsStream(fileName));
		} catch (Exception e) {
			System.out.println("Error: Cannot load properties file.");
		}
	}

	public String getProperty(Status status) {
		return super.getProperty(status.toString());
	}

	// note: this method has to be synchronized, because during loading a skin
	// or scanning for proxies there are separate threads started.
	// So we have to make sure, that the new StatusMap object is created AND
	// it is initialized, befor another thread can enter this method and
	// can find a non-null reference.
	public synchronized static EProperties getInstance() {
		if (instance == null) {
			instance = new EProperties();
		}
		return instance;
	}
} public enum Status {

	/**
	 * the current lifecycle state
	 */
	LIFECYCLE,

	/**
	 * the active configuration or null
	 */
	CONFIGURATION,

	/**
	 * flag indicating, whether the overview window is shown or not
	 */
	SHOW_OVERVIEW,

	/**
	 * the default height of the configuration panel
	 */
	BASIC_CONFIGURATION_HEIGHT,

	/**
	 * the height of the configuration panel
	 */
	ZOOMED_HEIGHT,

	/**
	 * flag indicating, whether the overview window is shown or not
	 */
	SHOW_LOG,

	/**
	 * the currently loaded skin (currently only buttercream) or null
	 */
	SKIN,

	/**
	 * the currently loaded language
	 */
	LANGUAGE,

	/**
	 * flag indicating, whether the pulses shall be highlighted or not
	 */
	HIGHLIGHT_PULSE,

	/**
	 * the current simulation time
	 */
	SIMULATION_TIME;

	/*
	 * ============================= fields ==================================
	 */

	// the value for this status key
	private Object _value;

	private LinkedList<StatusListener> _listeners = new LinkedList<>();

	/*
	 * ========================= singleton stuff =======================
	 */

	public static void initValues() {
		CONFIGURATION._value = null;
		SHOW_OVERVIEW._value = StringConverter.toBoolean(EProperties.getInstance().getProperty(SHOW_OVERVIEW));
		ZOOMED_HEIGHT._value = StringConverter.toInt(EProperties.getInstance().getProperty(BASIC_CONFIGURATION_HEIGHT));
		SHOW_LOG._value = StringConverter.toBoolean(EProperties.getInstance().getProperty(SHOW_LOG));
		SKIN._value = null;
		LANGUAGE._value = null;
		HIGHLIGHT_PULSE._value = StringConverter.toBoolean(EProperties.getInstance().getProperty(HIGHLIGHT_PULSE));
		SIMULATION_TIME._value = -1L;
	}

	// =========================== getter and setter
	// ============================

	public void setValue(Object newValue) {
		Object oldValue = _value;
		if (oldValue == null) {
			if (newValue != null) {
				_value = newValue;
				informListeners();
			}
		}
		else if (!oldValue.equals(newValue)) {
			_value = newValue;
			informListeners();
		}
	}

	public boolean toggle() {
		return (boolean) (_value = !((boolean) _value));
	}

	public Object getValue() {
		return _value;
	}

	/*
	 * ========================= listener stuff =======================
	 */

	public void addListener(StatusListener listener) {
		_listeners.add(listener);
	}

	public void removeListener(StatusListener listener) {
		_listeners.remove(listener);
	}

	private void informListeners() {
		for (StatusListener listener : _listeners) {
			listener.statusChanged(this, _value);
		}
	}
} public interface StatusListener {

	public void statusChanged(Status status, Object newValue);

} public class EFrame extends JFrame implements StatusListener {

	/*
	 * ========================== private fields ===============================
	 */

	// scrollPane as south component of the contentPane (North component is
	// the actionBar).
	private JScrollPane _scrollPane;

	// configuration panel for display the current configuration of the eniac.
	// This component is child to the scrollPane.
	// If no current configuration, this is null.
	private ConfigPanel _configPanel = null;

	private MediaTracker _mediaTracker;

	/*
	 * ============================ singleton stuff ===========================
	 */

	private static EFrame instance;

	private EFrame() {

		// set bounds.
		Dimension mySize = StringConverter.toDimension(EProperties.getInstance().getProperty("EFRAME_SIZE"));
		Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
		mySize.width = Math.min(mySize.width, screenSize.width);
		mySize.height = Math.min(mySize.height, screenSize.height);
		setSize(mySize);
		setLocation((screenSize.width - mySize.width) / 2, (screenSize.height - mySize.height) / 3);

		// create media tracker
		_mediaTracker = new MediaTracker(this);

		// add as singleton to starter
		Status.LIFECYCLE.addListener(this);
	}

	public static EFrame getInstance() {
		if (instance == null) {
			instance = new EFrame();
		}
		return instance;
	}

	/**
	 * Initializes this dvFrame. Listener registration is done, the frame is
	 * layouted and finally brought to the screen.
	 */
	public void toScreen() {

		// set window title
		setTitle(Dictionary.MAIN_FRAME_TITLE.getText());

		// window listener
		addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				Manager.getInstance().stop();
				Manager.getInstance().destroy();
				dispose();
			}
		});

		// init scrollPane
		_scrollPane = new JScrollPane();
		Color c = StringConverter.toColor(EProperties.getInstance().getProperty("BACKGROUND_COLOR"));
		_scrollPane.getViewport().setBackground(c);

		// add components
		// create and init menu handler
		Hashtable<String, EAction> actionDefaults = new Hashtable<>();
		MenuHandler handler = new MenuHandler(actionDefaults);
		handler.init();

		setJMenuBar(handler.getMenuBar());
		getContentPane().setLayout(new BorderLayout());
		getContentPane().add(handler.getToolBar(), BorderLayout.NORTH);
		getContentPane().add(_scrollPane, BorderLayout.CENTER);

		// update configurationPanel
		// TODO: find new way of initializing configuration
		updateConfigPanel();

		// add this as propertyChangeListener
		Status.CONFIGURATION.addListener(this);
		Status.SKIN.addListener(this);
		Status.LANGUAGE.addListener(this);

		// init LogWindow
		// LogWindow.getInstance();
		setVisible(true);
	}

	/*
	 * =============================== methods =================================
	 */

	// update config panel. This method is called, when the current
	// configuration changed.
	private void updateConfigPanel() {

		// dispose the old panel if any
		if (_configPanel != null) {
			_scrollPane.setViewportView(null);
			_configPanel.dispose();
		}

		// get current configuration and the viewDimension
		Configuration config = (Configuration) Status.CONFIGURATION.getValue();

		// determine which configPanel to set
		if (config == null) {

			// configuration is null. Set null.
			_configPanel = null;
		}
		else {
			// create new configurationPanel
			_configPanel = (ConfigPanel) config.makePanel();
			// add configPanel to scrollPane and init
			_configPanel.init();
			_scrollPane.setViewportView(_configPanel);
		}

		// tell OVWindow to adjust its panel to the new configPanel
		OVWindow.getInstance().configPanelChanged();
	}

	public int showFileChooser(JFileChooser chooser, String approveButtonText) {
		return chooser.showDialog(this, approveButtonText);
	}

	public ConfigPanel getConfigPanel() {
		return _configPanel;
	}

	/*
	 * ===================== PropertyChangeListener methods ==================
	 */

	/**
	 * This method is called when a status property is changed
	 * 
	 * @param evt
	 *            The propertyEvent indicating that the architecture changed
	 */
	public void statusChanged(Status status, Object newValue) {

		switch (status) {

			case CONFIGURATION :
				// configuration changed. Update the configPanel.
				updateConfigPanel();
				break;
			case SKIN :
				// skin changed. repaint
				repaint();
				break;

			case LANGUAGE :
				// language changed. repaint and adjust title.
				repaint();

				// set default locale to JComponent.
				// So optionPane buttons get the right language.
				JComponent.setDefaultLocale(new Locale((String) newValue));
				setTitle(Dictionary.MAIN_FRAME_TITLE.getText());
				break;

			case LIFECYCLE :
				if (newValue == Manager.LifeCycle.STOPPED) {
					setVisible(false);
				}
				else if (newValue == Manager.LifeCycle.DESTROYED) {
					dispose();
					instance = null;
				}
				break;

			default :
				break;
		}
	}

	/*
	 * ============================ changelistener ===========================
	 */

	public void addChangeListener(ChangeListener listener) {
		_scrollPane.getViewport().addChangeListener(listener);
	}

	public void removeChangeListener(ChangeListener listener) {
		_scrollPane.getViewport().removeChangeListener(listener);
	}

	/**
	 * @param path
	 * @return
	 */
	public Image getResourceAsImage(String name) {

		// get url. If path cannot be resolved, return null.
		URL url = Manager.class.getClassLoader().getResource(name);
		if (url == null) {
			return null;
		}

		Image img = Toolkit.getDefaultToolkit().createImage(url);
		_mediaTracker.addImage(img, 1);
		try {
			_mediaTracker.waitForAll();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		return img;
	}

}
import eniac.log.LogWords;
import eniac.menu.action.gui.SaveConfigurationPanel;
import eniac.util.EProperties;
import eniac.util.Status;
import eniac.util.StatusListener;
import eniac.window.EFrame;
import java.awt.event.ActionEvent;
import java.io.File;
import java.io.IOException;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import eniac.Manager;
import eniac.data.io.ConfigIO;
import eniac.data.model.parent.Configuration;
import eniac.io.IOUtil;
import eniac.io.Proxy;
import eniac.lang.Dictionary;
import eniac.log.Log;
/*******************************************************************************
 * 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 11.11.2003
 *
 * To change the template for this generated file go to
 * Window - Preferences - Java - Code Generation - Code and Comments
 */
package eniac.menu.action;




/**
 * @author zoppke
 * 
 *         To change the template for this generated type comment go to Window -
 *         Preferences - Java - Code Generation - Code and Comments
 */
public class SaveConfiguration extends EAction implements Runnable {

	@Override
	public void init() {
		super.init();
		Status.CONFIGURATION.addListener(new StatusListener() {

			@Override
			public void statusChanged(Status status, Object newValue) {
				setEnabled(newValue != null);
			}
		});
	}

	public void actionPerformed(ActionEvent e) {
		// check for privileges
		if (Manager.getInstance().hasIOAccess()) {
			Thread t = new Thread(this);
			t.start();
			// We have privileges to write file to users file system
		}
		else {
			// we have no privileges to write.
			// display message to tell user
			Log.log(LogWords.CANNOT_SAVE_FILE, JOptionPane.INFORMATION_MESSAGE, true);
		}
	}

	// starts a wizard with dialogs for enter details of configuration
	// and a filechooser for configuration target.
	public void run() {

		Manager.getInstance().block();

		// create dialog that user can enter name and description
		SaveConfigurationPanel panel = new SaveConfigurationPanel();
		panel.init();
		Manager.getInstance().makeDialog(panel, Dictionary.SAVE_CONFIGURATION_TITLE.getText());

		// if canceled, do nothing.
		if (!panel.isNextPressed()) {
			Manager.getInstance().unblock();
			return;
		}

		// otherwise create proxy
		Proxy proxy = new Proxy();
		proxy.put(Proxy.Tag.NAME, panel.getName());
		proxy.put(Proxy.Tag.DESCRIPTION, panel.getDescription());

		// create filechooser
		JFileChooser chooser = new JFileChooser();

		// get default filename without index
		String s = chooser.getFileSystemView().getDefaultDirectory().getAbsolutePath();
		s += File.separator;
		s += EProperties.getInstance().getProperty("DEFAULT_FILE_WITHOUT_NUMBER");

		// determine lowest index that the file doesn't exist
		File file;
		int i = -1;
		do {
			file = new File(IOUtil.addIndex(s, ++i));
		} while (file.exists());

		// create dialog that user can choose path to save
		chooser.setFileFilter(IOUtil.getFileFilter());
		chooser.setSelectedFile(file);
		int returnVal = EFrame.getInstance().showFileChooser(chooser, Dictionary.WRITE.getText());

		// check if user wants to write
		if (returnVal == JFileChooser.APPROVE_OPTION) {

			// try to write to the file specified by filechooser
			try {
				Configuration config = (Configuration) Status.CONFIGURATION.getValue();