/* Library.java

	Purpose:
		
	Description:
		
	History:
		Fri Aug  1 09:06:36     2008, Created by tomyeh

Copyright (C) 2008 Potix Corporation. All Rights Reserved.

{{IS_RIGHT
	This program is distributed under LGPL Version 2.1 in the hope that
	it will be useful, but WITHOUT ANY WARRANTY.
}}IS_RIGHT
*/
package org.zkoss.lang;

import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Represent the scope of ZK libraries.
 * Unlike {@link System}, the scope of {@link Library} depends how
 * ZK libraries are installed.
 * If they are installed in WEB-INF/lib of a ZK application,
 * the scope is the application. In other words, the library properties
 * ({@link #setProperty}) are shared by the application only.
 *
 * <p>On the other hand, if ZK libraries are installed in a folder
 * shared by all applications, the library properties are shared
 * by all applications.
 *
 * @author tomyeh
 * @since 3.0.7
 */
public class Library {
	private static final Logger log = LoggerFactory.getLogger(Library.class);
	//ZK-3823: Using the ConcurrentHashMap to substitute synchronized HashMap can avoid the thread lock issue.
	//Enhance the performance of the code. 
	private static final Map<String, List<String>> _props = new ConcurrentHashMap<String, List<String>>();

	private Library() {}

	/** Returns the library property indicated by the specified key.
	 * If no such library property, this method will call
	 * {@link System#getProperty} to look for a system property.
	 *
	 * <p>The library property is shared by Java codes that access
	 * the same set of ZK libraries, since it is actually
	 * a static member of this class. Thus, if ZK libraries (including
	 * this class) are installed in WEB-INF/lib of an application,
	 * the library properties are accessible only in the application.
	 *
	 * <p>Note: unlike {@link System#getProperty}, this method won't
	 * throw SecurityException.
	 *
	 * @return the string value of the library property, or the system
	 * property, or null if no such property.
	 * @see #setProperty
	 */
	public static String getProperty(String key) {
		final String v;
		List<String> valList = _props.get(key);
		if (valList != null && valList.size() > 0)
			v = valList.get(0);
		else
			v = null;
		try {
			//Unlike System.getProperty, we make the inocation as safe as possible
			return v != null || key == null || key.length() == 0 ?
				v: System.getProperty(key);
		} catch (SecurityException ex) {
			return null;
		}
	}
	/** Returns the library property indicated by the specified key.
	 * If no such library property, this method will call
	 * {@link System#getProperty} to look for a system property.
	 *
	 * <p>Note: unlike {@link System#getProperty}, this method won't
	 * throw SecurityException.
	 *
	 * @param key the name of the library property
	 * @param def a default value.
	 * @exception NullPointerException if key is null
	 * @exception IllegalArgumentException if key is empty
	 */
	public static String getProperty(String key, String def) {
		final String v;
		List<String> valList = _props.get(key);
		if (valList != null && valList.size() > 0)
			v = valList.get(0);
		else
			v = null;
		try {
			return v != null ? v: System.getProperty(key, def);
		} catch (SecurityException ex) {
			return def;
		}
	}
	/** Sets the library property indicated by the specified key.
	 * @see #getProperty
	 * @exception NullPointerException if key is null
	 * @exception IllegalArgumentException if key is empty
	 * @return the previous value of the library property, or null if
	 * it did not have one.
	 */
	public static String setProperty(String key, String value) {
		if (key == null) throw new NullPointerException();
		if (key.length() == 0) throw new IllegalArgumentException();

		List<String> vals = new LinkedList<String>();
		vals.add(value);
		List<String> prev = _props.put(key, vals);
		return prev != null && prev.size() > 0 ? prev.get(0) : null;
	}

	/** Parses the property value to an integer.
	 * If the specified value is not an integer, the default value is returned
	 * (and a warning is logged).
	 * @param defVal the default value
	 * @since 3.6.1
	 */
	public static int getIntProperty(String key, int defVal) {
		final String val = getProperty(key);
		if (val != null) {
			try {
				return Integer.parseInt(val);
			} catch (Throwable t) {
				log.warn("Failed to parse " + key + ": not an integer, " + val);
			}
		}
		return defVal;
	}

	/** Add a library property indicated by the specified key.
	 * @see #getProperties
	 * @exception NullPointerException if key is null
	 * @exception IllegalArgumentException if key is empty
	 * @return the previous value of the library properties, or null if
	 * it did not have any.
	 */
	public static List<String> addProperty(String key, String value) {
		if (key == null) throw new NullPointerException();
		if (key.length() == 0) throw new IllegalArgumentException();

		synchronized (_props) {
			List<String> old = _props.get(key);
			if (old == null)
				old = new LinkedList<String>();
			old.add(value);
			return _props.put(key, old);
		}
	}

	/** Add library properties indicated by the specified key.
	 * @see #getProperties
	 * @exception NullPointerException if key is null
	 * @exception IllegalArgumentException if key is empty
	 * @return the previous value of the library properties, or null if
	 * it did not have any.
	 */
	public static List<String> addProperties(String key, List<String> values) {
		if (key == null) throw new NullPointerException();
		if (key.length() == 0) throw new IllegalArgumentException();

		synchronized (_props) {
			List<String> old = _props.get(key);
			if (old == null)
				old = new LinkedList<String>();
			old.addAll(values);
			return _props.put(key, old);
		}
	}

	/** Sets the library properties indicated by the specified key.
	 * @see #getProperties
	 * @exception NullPointerException if key is null
	 * @exception IllegalArgumentException if key is empty
	 * @return the previous value of the library properties, or null if
	 * it did not have any.
	 */
	public static List<String> setProperties(String key, List<String> values) {
		if (key == null) throw new NullPointerException();
		if (key.length() == 0) throw new IllegalArgumentException();

		return _props.put(key, new LinkedList<String>(values));
	}

	/** Returns the library properties indicated by the specified key.
	 *
	 * <p>The library properties is shared by Java codes that access
	 * the same set of ZK libraries, since it is actually
	 * a static member of this class. Thus, if ZK libraries (including
	 * this class) are installed in WEB-INF/lib of an application,
	 * the library properties are accessible only in the application.
	 *
	 * @return the List of values of the library properties,
	 * or null if no such property.
	 * @see #setProperties
	 */
	public static List<String> getProperties(String key) {
		return  _props.get(key);
	}
}
