/*
 * $Id: VKCUtils.java,v 1.13 2006/04/11 19:47:58 marbobo Exp $
 *
 * Copyright (c) Siemens AG 2008 
 *           (c) Universität Paderborn 2008 
 * Licensed under EUPL 1.1. 
 *
 * $Log: VKCUtils.java,v $
 * Revision 1.13  2006/04/11 19:47:58  marbobo
 * + getOSIdent() erweitert
 *
 * Revision 1.12  2006/03/23 11:44:30  marbobo
 * + Mögliche NPE abgefangen
 *
 * Revision 1.11  2004/11/10 15:02:46  koppy
 * Projektweite Überarbeitung von Error-Logs, Stacktraces,..
 *
 * Revision 1.10  2004/07/20 08:46:27  jonyp
 * added copyright info + cvs log
 *
 *
 */
package vkc.util;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.Socket;
import java.net.URL;
import java.util.Arrays;
import java.util.StringTokenizer;
import java.util.Vector;

import vkc.misc.VKCLogManager;

public class VKCUtils {
    public static final String OS_WINDOWS = "win32";
    public static final String OS_SOLARIS = "solaris";
    public static final String OS_LINUX = "linux";
    public static final String OS_OS2 = "os2";
    public static final String OS_MACOS = "macos";
    public static final String OS_HPUX = "hp-ux";
    public static final String OS_AIX = "aix";
    public static final String OS_IRIX = "irix";
    public static final String OS_FREEBSD = "freebsd";
    public static final String OS_DIGITALUNIX = "digital_unix";
    public static final String OS_UNIXWARE = "unix_sv";
    public static final String OS_UNSUPPORTED = "unsupported";
    public static final String VM_VERSION     = "1.4";
	private static String[] charSubstitution = {

	//00x		1		2		3		4		5		6		7		8		9
			"", "", "", "", "", "", "", "", "", "",
			//01x
			"", "", "", "", "", "", "", "", "", "",
			//02x
			"", "", "", "", "", "", "", "", "", "",
			//03x
			"", "", " ", "", "", "", "", "", "", "",
			//04x
			"", "", "", "", "", "", "", "", "", "",
			//05x
			"", "", "", "", "", "", "", "", "", "",
			//06x
			"", "", "", "", "", "a", "b", "c", "d", "e",
			//07x
			"f", "g", "h", "i", "j", "k", "l", "m", "n", "o",
			//08x
			"p", "q", "r", "s", "t", "u", "v", "w", "x", "y",
			//09x
			"z", "", "", "", "", "", "", "a", "b", "c",
			//10x
			"d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
			//11x
			"n", "o", "p", "q", "r", "s", "t", "u", "v", "w",
			//12x
			"x", "y", "z", "", "", "", "", "", "c", "ue",
			//13x
			"e", "a", "ae", "a", "a", "c", "e", "e", "e", "i",
			//14x
			"i", "i", "ae", "a", "e", "ae", "ae", "o", "oe", "o",
			//15x
			"u", "u", "y", "oe", "ue", "c", "", "", "", "",
			//16x
			"a", "i", "", "", "", "", "", "", "", "",
			//17x
			"", "", "", "", "", "", "", "", "", "",
			//18x
			"", "", "", "", "", "", "", "", "", "",
			//19x
			"", "", "a", "a", "a", "a", "ae", "a", "ae", "c",
			//20x
			"e", "e", "e", "e", "i", "i", "i", "i", "e", "n",
			//21x
			"o", "o", "o", "o", "oe", "oe", "o", "u", "u", "u",
			//22x
			"ue", "y", "t", "ss", "a", "a", "a", "a", "ae", "a",
			//23x
			"ae", "c", "e", "e", "e", "e", "i", "i", "i", "i",
			//24x
			"e", "n", "o", "o", "o", "o", "oe", "oe", "o", "u",
			//25x
			"u", "u", "ue", "y", "t", "y" };

	public static boolean debug = false;

	public static String os = System.getProperty("os.name");

	/**
	 * Methode liefert den Stacktrace eines Throwable als String
	 */
	public final static String getStackTrace(Throwable t) {
		if (t == null) {
			return null;
		}

		ByteArrayOutputStream bout = new ByteArrayOutputStream();

		try {
			OutputStreamWriter osw = new OutputStreamWriter(bout);
			PrintWriter pw = new PrintWriter(osw);
			t.printStackTrace(pw);
			pw.flush();
			pw.close();
			osw.close();
		} catch (Exception ex) {
			VKCLogManager.errorLog(VKCUtils.class, "", ex.getMessage(), ex);
		}

		FileUtils.closeOutputStream(bout);

		byte[] bytes = bout.toByteArray();
		bout = null;

		return new String(bytes);
	}

	/**
	 * holt einen Parameter aus einem .id-String
	 * 
	 * <p></p>
	 *
	 * @param param - Name des gesuchten Parameters
	 * @param str - id-String in dem gesucht werden soll
	 *
	 * @return Wert des gesuchten Parameters, falls vorhanden; leeren String
	 *         sonst.
	 */
	public static String getParamFromString(String str, String param) {
		if (str == null) {
			return ("");
		}

		String strBeg = "<" + param + ">";
		String strEnd = "</" + param + ">";

		int index1 = -1;
		int index2 = -1;
		String value = "";

		try {
			index1 = str.indexOf(strBeg);

			if (index1 < 0) {
				return ("");
			}

			index2 = str.indexOf(strEnd);

			if (index2 < 0) {
				return ("");
			}

			value = str.substring(index1 + strBeg.length(), index2);
		} catch (Exception e) {
			VKCLogManager.errorLog(VKCUtils.class, "", e.getMessage(), e);
			value = "";
		}

		return (value);
	}

	/**
	 * Ermittelt den aktuellen Usernamen. Falls vorhanden, werden alle
	 * Leerzeichen im Usernamen durch '_' ersetzt. Für OS'e die keine
	 * Usernamen kennen, wird stattdessen die IP des Rechners als Username
	 * geliefert
	 * 
	 * <p></p>
	 *
	 * @return Username als String bei Erfolg. null sonst.
	 */
	public static String getUserName() {
		try {
			String userName = System.getProperty("user.name");
			userName = userName.replace(' ', '_');

			//Fuer Betriebssysteme die keine Usernamen kennen
			if (userName.equals("unknown")) {
				String hname = InetAddress.getLocalHost().getHostName();

				if (hname != null) {
					userName = hname.replace('.', '-').replace('@', '-');
				} else {
					String haddress = getHostAddress();

					if (haddress == null) {
						return (null);
					}

					userName = haddress.replace('.', '-');
					haddress = null;
				}
			}

			return userName;
		} catch (Exception e) {
			VKCLogManager.errorLog(VKCUtils.class, "", e.getMessage(), e);

			return null;
		}
	}

	/**
	 * Ermittelt die IP-Adresse des lokalen Rechners.
	 * 
	 * <p></p>
	 *
	 * @return IP-Adresse als String des lokalen Rechners bei Erfolg. null bei
	 *         Fehlern
	 */
	public static String getHostAddress() {
		try {
			String hostAddress = InetAddress.getLocalHost().getHostAddress();

			return hostAddress;
		} catch (Exception e) {
			VKCLogManager.errorLog(VKCUtils.class, "", e.getMessage(), e);

			return null;
		}
	}

	/**
	 * Ermittelt den Namen des lokalen Rechners.
	 * 
	 * <p></p>
	 *
	 * @return Name des lokalen Rechners bei Erfolg. null bei Fehlern
	 */
	public static String getHostName() {
		try {
			String hostName = InetAddress.getLocalHost().getHostName();

			// Wegen Linux-Inkompatiblitaet
			// (Linux gibt kompletten IP-Namen zurueck incl. Domain.)
			if (hostName.indexOf(".") >= 0) {
				hostName = hostName.substring(0, hostName.indexOf("."));
			}

			return hostName;
		} catch (Exception e) {
			VKCLogManager.errorLog(VKCUtils.class, "", e.getMessage(), e);

			return null;
		}
	}

	/**
	 * Liefert einen Bezeichnerstring fuer das zugrundeliegende Betriebssystem
	 *
	 * @return OS-Bezeichner
	 */
	public static String getOSIdent() {
		String vm_os = System.getProperty("os.name").toLowerCase();

		if (vm_os.indexOf("windows") >= 0) {
			return OS_WINDOWS;
		}

		if ((vm_os.indexOf("solaris") >= 0) || (vm_os.indexOf("sunos") >= 0)) {
			return OS_SOLARIS;
		}

		if (vm_os.indexOf("linux") >= 0) {
			return OS_LINUX;
		}

		if (vm_os.indexOf("os/2") >= 0) {
			return OS_OS2;
		}

		if (vm_os.indexOf("macos") >= 0 || vm_os.indexOf("mac os") >= 0) {
			return OS_MACOS;
		}

		if (vm_os.indexOf("hp-ux") >= 0) {
			return OS_HPUX;
		}

		if (vm_os.indexOf("aix") >= 0) {
			return OS_AIX;
		}

		if (vm_os.indexOf("irix") >= 0) {
			return OS_IRIX;
		}

		if (vm_os.indexOf("freebsd") >= 0) {
			return OS_FREEBSD;
		}

		if (vm_os.indexOf("digital unix") >= 0) {
			return OS_DIGITALUNIX;
		}

		if (vm_os.indexOf("unix_sv") >= 0) {
			return OS_UNIXWARE;
		}

		return OS_UNSUPPORTED;
	}

	/**
	 * Konvertiert die Umlaute im uebergebenen String XML-konform.
	 *
	 * @param input Eingabe-String
	 *
	 * @return XML-konformer String
	 */
	public static String umlEncode(String input) {
		StringBuffer output = new StringBuffer();

		if (input == null) {
			return "";
		}

		int length = input.length();
		char aktchar;

		for (int i = 0; i < length; i++) {
			aktchar = input.charAt(i);

			if ("äöüßÄÖÜß".indexOf(aktchar) < 0) {
				output.append(aktchar);
			} else {
				if (aktchar == 'ä') {
					output.append("&auml;");
				} else if (aktchar == 'ö') {
					output.append("&ouml;");
				} else if (aktchar == 'ü') {
					output.append("&uuml;");
				} else if (aktchar == 'ß') {
					output.append("&suml;");
				} else if (aktchar == 'Ä') {
					output.append("&Auml;");
				} else if (aktchar == 'Ö') {
					output.append("&Ouml;");
				} else if (aktchar == 'Ü') {
					output.append("&Uuml;");
				} else if (aktchar == 'ß') {
					output.append("&suml;");
				} else {
					output.append(aktchar);
				}
			}
		}

		return output.toString();
	}

	/**
	 * Konvertiert XML-Umlaute in "echte" Umlaute
	 *
	 * @param input XML-String
	 *
	 * @return String
	 */
	public static String umlautDecode(String input) {
		try {
			if (input == null) {
				return "";
			}

			if (input.indexOf("&ouml;") >= 0) {
				input = VKCStringUtils.replaceToken(input, "&ouml;", "ö");
			}

			if (input.indexOf("&Ouml;") >= 0) {
				input = VKCStringUtils.replaceToken(input, "&Ouml;", "Ö");
			}

			if (input.indexOf("&auml;") >= 0) {
				input = VKCStringUtils.replaceToken(input, "&auml;", "ä");
			}

			if (input.indexOf("&Auml;") >= 0) {
				input = VKCStringUtils.replaceToken(input, "&Auml;", "Ä");
			}

			if (input.indexOf("&uuml;") >= 0) {
				input = VKCStringUtils.replaceToken(input, "&uuml;", "ü");
			}

			if (input.indexOf("&Uuml;") >= 0) {
				input = VKCStringUtils.replaceToken(input, "&Uuml;", "Ü");
			}

			if (input.indexOf("&suml;") >= 0) {
				input = VKCStringUtils.replaceToken(input, "&suml;", "ß");
			}

			return (input);
		} catch (Exception e) {
			VKCLogManager.errorLog(VKCUtils.class, "", e.getMessage(), e);

			return ("");
		}
	}

	/**
	 * Wrapper-Methode um eine Socket zu schliessen
	 * 
	 * <p></p>
	 *
	 * @param s - die zu schliessende Socket
	 */
	public static void closeSocket(Socket s) {
		try {
			if (s != null) {
				s.close();
			}

			s = null;
		} catch (Exception e) {
			if (debug) {
				VKCLogManager.errorLog(VKCUtils.class, "", e.getMessage(), e);
			}

			return;
		}

		return;
	}

	/**
	 * Fuellt einen String auf die uebergebene Laenge mit Leerzeichen auf.
	 */
	public static String fill(String kopf, int laenge) {
		int i;
		String back = kopf;
		int fill;

		fill = laenge - kopf.length();

		if (fill > 0) {
			for (i = fill; i > 0; i--) {
				back += " ";
			}
		}

		return back;
	}

	/**
	 * Testet ob ein String einen VKC-Parameter enthält
	 *
	 * @param line - der zu testende String,
	 * @param parameter - der gesuchte VKC-Parameter
	 *
	 * @return Parameter als String (leeren String falls der VKC-Parameter
	 *         nicht enthalten ist)
	 */
	public static String getVKCPageParam(String line, String parameter) {
		try {
			int index;
			int colIndex;
			String value = "";

			parameter += "=";
			index = line.indexOf(parameter);

			if (index > 0) {
				index += parameter.length();
				colIndex = line.substring(index, line.length()).indexOf(",");

				if (colIndex < 0) {
					colIndex = line.substring(index, line.length()).indexOf("-->");
				}

				colIndex += index;

				value = line.substring(index, colIndex);

				return value.trim();
			}

			return ("");
		} catch (Exception e) {
			if (debug) {
				VKCLogManager.errorLog(VKCUtils.class, "", e.getMessage(), e);
			}

			return ("");
		}
	}

	/**
	 * Tested eine URL ob sie gültig ist
	 */
	public static boolean testUrl(String url) {
		try {
			new java.net.URL(url);

			return true;
		} catch (java.net.MalformedURLException mue) {
			return false;
		}
	}

	/**
	 * Methode zum extrahieren von URLs aus ASCII-Texten
	 *
	 * @param text - ASCII Text
	 *
	 * @return String mit jeweils einer URL pro Zeile
	 */
	public static String getUrlsFromString(String text) {
		String urls = "";

		StringTokenizer st = new StringTokenizer(text, " \n");
		String word;
		String url;

		while (st.hasMoreTokens()) {
			word = st.nextToken();

			//enthaelt das Wort "://" und die Punkte befinden sich hinter dem "://"
			int index = word.indexOf("://");

			if ((index >= 0) && ((word.indexOf(".") - index) > 2)) {
				if (testUrl(word)) {
					urls += (word + "\n");
				}
			} else {
				//enthaelt das Wort mindestens einen Punkt und einen Slash hinter dem Punkt
				//und keinen Doppelpunkt und kein At
				index = word.indexOf(".");

				if ((index >= 0) && ((word.indexOf("/") - index) > 2) && (word.indexOf(":") < 0) && (word.indexOf("@") < 0)) {
					url = "http://" + word;

					if (testUrl(url)) {
						urls += (url + "\n");
					}
				} else {
					//enthaelt das Wort mindestens 2 Punkte mit 3 Zeichen Abstand
					//und keinen Doppelpunkt und kein At
					index = word.indexOf(".");

					if ((index >= 0) && (word.indexOf(":") < 0) && (word.indexOf("@") < 0)) {
						index = word.substring(index + 1).indexOf(".");

						if (index > 2) {
							url = "http://" + word;

							if (testUrl(url)) {
								urls += (url + "\n");
							}
						}
					}
				}
			}
		}

		return urls.trim();
	}

	/**
	 * Bestimmt den Wert eines VKC-Parameters aus einem String
	 *
	 * @param param - der gesuchte Parameter.
	 * @param serverFeatures - String mit VKC-Parametern
	 *
	 * @return Wert eines Parameters, falls er vorhanden ist. leeren String,
	 *         sonst.
	 */
	public static String getParamValue(String param, String serverFeatures) {
		boolean localdebug = false;

		if (localdebug) {
			System.out.println("in getFeatureValue(): " + param);
		}

		try {
			int findex = serverFeatures.indexOf("#" + param + "=");

			if (findex < 0) {
				return ("");
			}

			String fval = serverFeatures.substring(findex, serverFeatures
					.length());

			// alles bis zum Anfang des Wertes abschneiden
			int flength = ("#" + param + "=").length();
			fval = fval.substring(flength, fval.length());

			// Wert = Anfang bis zum ersten #
			findex = fval.indexOf("#");
			fval = fval.substring(0, findex);

			serverFeatures = null;

			return (fval);
		} catch (Exception e) {
			if (debug) {
				VKCLogManager.errorLog(VKCUtils.class, "", e.getMessage(), e);
			}

			return ("");
		}
	}

	/**
	 * Methode vergrössert ein char-Array um eine feste Länge
	 * 
	 * <p></p>
	 */
	public static char[] enlargeBuffer(char[] buf) {
		try {
			int newLen = 1024;

			if (buf == null) {
				return (new char[newLen]);
			}

			newLen += buf.length;

			char[] newBuf = new char[newLen];
			System.arraycopy(newBuf, 0, buf, 0, buf.length);

			return (newBuf);
		} catch (Throwable e) {
			VKCLogManager.errorLog(VKCUtils.class, "", e.getMessage(), e);

			return (null);
		}
	}

	/* Entfernt Umlaute usw. aus dem uebergebenen String
	 * <p>
	 * @param word - zu konvertierendes Wort
	 * @return Konvertiertes Wort
	 * @author lawern
	 */
	public static String convertToBasicSpelling(String word) {
		if (word == null) {
			return null;
		}

		int orgWordLength;
		byte[] orgWord = null;

		try {
			orgWord = word.getBytes("ASCII");
			orgWordLength = orgWord.length;

			StringBuffer convWord = new StringBuffer(orgWordLength << 2);

			for (int i = 0; i < orgWordLength; i++) {
				convWord.append(charSubstitution[orgWord[i]]);
			}

			return convWord.toString().trim();
		} catch (Exception e) {
			return null;
		}

	}

	/**
	 * Konvertiert Windows-Dateinamen ins Cygwin-Format
	 * 
	 * <p></p>
	 *
	 * @param path - Windows Pfad
	 *
	 * @return Cygwin Pfad
	 */
	public static final String convertWinToCygwinPath(String path) {
		String cygwinpath;

		cygwinpath = path.replace('\\', '/');

		int doppelpunkt = cygwinpath.indexOf(":");

		if (doppelpunkt >= 0) {
			String tmp;
			tmp = cygwinpath.substring(0, doppelpunkt);
			cygwinpath = "/cygdrive/"
					+ tmp
					+ cygwinpath.substring(doppelpunkt + 1, cygwinpath.length());
		}

		return cygwinpath;
	}

	/**
	 * Konvertiert Cygwin Pfadnamen ins Windows-Format
	 * 
	 * <p></p>
	 *
	 * @param path - Cygwin Pfad
	 *
	 * @return Windows Pfad
	 */
	public static final String convertCygwintoWinPath(String path) {
		String winpath = path.replace('/', '\\');

		if (path.indexOf("/cygdrive/") == 0) {
			//absoluter Pfad
			winpath = winpath.charAt(10) + ":" + winpath.substring(11);
		}

		return winpath;
	}

	/**
	 * Konvertiert die übergebe File-URL in einen absoluten lokalen Pfad in
	 * Abhängigkeit von der jeweiligen Plattform. Wenn es nicht um einen
	 * lokalen Pfad handelt wird die Url zurückgegeben.
	 *
	 * @param url File-URL in der Form file:/
	 *
	 * @return lokaler Pfad
	 */
	public static final String getLocalPath(URL url) {
		String path = null;

		if (url.getProtocol().equals("file")) {
			try {
				path = url.getPath();
				path = (new File(path)).getAbsolutePath();
			} catch (Exception e) {
			}
		} else {
			path = url.toString();
		}

		return path;
	}

	/**
	 * liefert eine gekürzte Version eines Pfades
	 * 
	 * <p></p>
	 *
	 * @param path - URL oder Dateisystempfad
	 *
	 * @return "gekürtzer Pfad" (maximal 50 Zeichen lang)
	 */
	public static String getShortPath(String path) {
		if (path == null) {
			return null;
		}

		path = path.trim();

		if (path.length() < 50) {
			return path;
		}

		if (path.startsWith("http")) {
			try {
				// Behandlung von URLs
				URL u = new URL(path);
				String protocol = u.getProtocol();
				String host = u.getHost();
				int port = u.getPort();
				String portStr = ((port <= 0) ? "" : (":" + port));
				String prefix = protocol + "://" + host + portStr;

				int remaining = 50 - prefix.length() - 3;

				path = prefix + "..." + path.substring(path.length() - remaining, path.length());
			} catch (MalformedURLException me) {
				return path;
			}
		} else {
			path = "..." + path.substring(path.length() - 50, path.length());
		}

		return path;
	}

	/**
	 * Gibt die Elemente einer Liste als Vector zurück
	 */
	public static final Vector getListAsVector(javax.swing.DefaultListModel list) {
		Vector listVector = new Vector();

		for (int i = 0; i < list.getSize(); i++) {
			listVector.addElement(list.elementAt(i));
		}

		return listVector;
	}

	/**
	 * Sortiert die Elemente eines DefaultListModels
	 */
	public static final void sortList(javax.swing.DefaultListModel list) {
		Object[] tmpObj = list.toArray();
		Arrays.sort(tmpObj, null);

		list.clear();

		for (int i = 0; i < tmpObj.length; i++) {
			list.addElement(tmpObj[i]);
		}

		tmpObj = null;
	}

	/**
	 * Liesst eine Zeile aus einem (HTTP-) Inputstream ein und liefert sie als
	 * String zurück. CRLF markiert dabei das Zeilenende. ( laut HTTP-RFCs )
	 * 
	 * <p></p>
	 *
	 * @param in - Inputstream aus dem gelesen werden soll
	 *
	 * @return CRLF terminierten String falls das Lesen geklappt hat. null,
	 *         sonst.
	 */
	final public static String read_CRLF_line(InputStream in) {
		int count = 0;
		int c = -1;
		StringBuffer sb = new StringBuffer();
		boolean CR = false;

		try {
			while (true) {
				if ((c = in.read()) == -1) {
					break;
				}

				count++;
				sb.append((char) c);

				if ((c != '\r') && (c != '\n')) {
					CR = false;

					continue;
				}

				if (c == '\r') {
					CR = true;

					continue;
				}

				if ((c == '\n') && CR) {
					break;
				}
			}

			if (count == 0) {
				return (null);
			}

			return (sb.toString());
		} catch (Throwable e) {
			VKCLogManager.errorLog(VKCUtils.class, "", e.getMessage(), e);

			return null;
		}
	}

	/**
	 * Kodiert einen String so, dass er innerhalb eines TDF-Tags den Parser
	 * nicht stört.
	 *
	 * @param s zu kodierender String
	 *
	 * @return Kodierter String
	 */
	public final static String tdfEncode(String s) {
		char c;

		if (s == null) {
			return null;
		}

		StringBuffer sb = new StringBuffer(s.length());

		for (int i = 0; i < s.length(); i++) {
			c = s.charAt(i);

			switch (c) {
				case '\\':
					//Backslash durch \b ersetzen
					sb.append("\\b");
					break;
				case '<':
					//<-Zeichen durch \l ersetzen
					sb.append("\\l");
					break;
				case '>':
					//>-Zeichen durch \g ersetzen
					sb.append("\\g");
					break;
				default:
					sb.append(c);
			}
		}

		return sb.toString();
	}

	/**
	 * Dekodiert einen TDF-String wieder in ein darstellbares Format.
	 *
	 * @param s zu dekodierender TDF-String
	 *
	 * @return Dekodierter TDF-String
	 */
	public final static String tdfDecode(String s) {
		char c;

		if (s == null) {
			return null;
		}

		int stringLength = s.length();
		StringBuffer sb = new StringBuffer(stringLength);

		int i = 0;

		while (i < stringLength) {
			c = s.charAt(i++);

			if ((c == '\\') && ((i + 1) < stringLength)) {
				c = s.charAt(i++);

				switch (c) {
					case 'b':
						//\b duch Backslash ersetzen
						sb.append('\\');
						break;
					case 'l':
						//\l durch <-Zeichen ersetzen
						sb.append('<');
						break;
					case 'g':
						//\g durch >-Zeichen ersetzen
						sb.append('>');
						break;
				}
			} else {
				sb.append(c);
			}
		}

		return sb.toString();
	}
}