/* MessageConst.java


	Purpose: Defines message types
	Description:
	History:
	 2001/4/2 2001/4/2, Tom M. Yeh: Created.


Copyright (C) 2001 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.mesg;

import java.util.Map;
import java.util.HashMap;

/**
 * Defines the constants of message codes.
 *
 * @author tomyeh
 * @see Messages
 */
public interface MessageConst {
	/** Denotes a non-existent code. */
	public static final int NULL_CODE = 0;

	/** The info of each message bundle.
	 * <p>Each bundle is associated with a class and a set of files.
	 */
	/*package*/ static class BundleInfo {
		public final Class klass;
		public final String filename;

		private BundleInfo(Class klass, String filename) {
			this.klass = klass;
			this.filename = filename;
		}
		public String toString() {
			return "[" + this.klass.getName() + ", " + this.filename + ']';
		}
	}

	/** Used to handle the mapping of a message code to a filename.
	 *
	 * <p>FUTURE: we might consider to use hashCode or other to represents
	 * the identifier such that the client will have the same code as the server.
	 */
	public static class Aide {
		private static Map<Integer, BundleInfo> _bis = new HashMap<Integer, BundleInfo>(4);

		/** Registers a message filename, and returns an identifier to
		 * represent it.
		 *
		 * <p>The path is assumed to be /metainfo/mesg.
		 *
		 * @param filename the filename without path and extension, e.g, "msgacc"
		 * @return an identifier to represent this message file.
		 */
		public static final int register(Class klass, String filename) {
			if (filename.indexOf('/') >= 0 || filename.indexOf('.') >= 0)
				throw new IllegalArgumentException("Neither path nor extension is allowed: "+filename);

			//The algorithm is to make id as deterministic as possible
			//though not really necessary
			final BundleInfo bi = new BundleInfo(klass, "/metainfo/mesg/" + filename);
			String sID = klass.getName();
			int id = getId(sID);
			synchronized (Aide.class) {
				Integer iID;
				final Map<Integer, BundleInfo> bis = new HashMap<Integer, BundleInfo>(_bis);
				while (bis.containsKey(iID = new Integer(id)))
					++id; //not deterministic
				bis.put(iID, bi);
				_bis = bis; //_bis itself is readonly (so no sync required)
			}
			return id << 16; //as the high word
		}
		private static final int getId(String sID) {
			final int id = sID.hashCode();
			return (id >= 0 ? id: -id) & 0x3fff;
		}
		/** Returns the filename with path, but without extension, of the
		 * specified message code.
		 */
		/*package*/ static final BundleInfo getBundleInfo(int code) {
			final int id = code >>> 16;
			final BundleInfo bi = _bis.get(new Integer(id));
			if (bi == null)
				throw new IllegalArgumentException("Wrong message ID: "+id
					+" ("+code+")\nRegistered: "+_bis);
			return bi;
		}
	}
}
