/* ComponentDefinitionMap.java

	Purpose:
		
	Description:
		
	History:
		Mon Sep  4 20:20:36     2006, Created by tomyeh

Copyright (C) 2006 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.zk.ui.metainfo;

import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
 * A map of component definitions.
 * Used with {@link PageDefinition#getComponentDefinitionMap}
 * and {@link LanguageDefinition}.
 *
 * <p>It is thread-safe (since it is used in {@link LanguageDefinition}).
 *
 * @author tomyeh
 */
public class ComponentDefinitionMap implements Cloneable, java.io.Serializable {
	/** A map of component definition defined in this page. */
	private transient Map<String, ComponentDefinition> _compdefs;
	/** Map(String clsnm, ComponentDefinition compdef). */
	private transient Map<String, ComponentDefinition> _compdefsByClass;
	/** Whether the element name is case-insensitive. */
	private final boolean _ignoreCase;

	/** Constructor.
	 */
	public ComponentDefinitionMap(boolean ignoreCase) {
		_ignoreCase = ignoreCase;
	}

	/** Returns whether the component names are case-insensitive.
	 */
	public boolean isCaseInsensitive() {
		return _ignoreCase;
	}

	/** Returns a readonly collection of the names (String)
	 * of component definitions defined in this map.
	 */
	public Collection<String> getNames() {
		if (_compdefs != null)
			return _compdefs.keySet();
		return Collections.emptyList();
	}

	/** Returns a readonly collection of component definitions
	 * ({@link ComponentDefinition}) defined in this map.
	 * @since 3.6.3
	 */
	public Collection<ComponentDefinition> getDefinitions() {
		if (_compdefs != null)
			return _compdefs.values();
		return Collections.emptyList();
	}

	/** Adds a component definition to this map.
	 *
	 * <p>Thread safe.
	 */
	public void add(ComponentDefinition compdef) {
		if (compdef == null)
			throw new IllegalArgumentException("null");

		String name = compdef.getName();
		if (isCaseInsensitive())
			name = name.toLowerCase(java.util.Locale.ENGLISH);

		Object implcls = compdef.getImplementationClass();
		if (implcls instanceof Class)
			implcls = ((Class) implcls).getName();

		synchronized (this) {
			if (_compdefs == null) {
				_compdefsByClass = Collections.synchronizedMap(new HashMap<String, ComponentDefinition>(4));
				_compdefs = Collections.synchronizedMap(new HashMap<String, ComponentDefinition>(4));
			}

			_compdefs.put(name, compdef);
			_compdefsByClass.put((String) implcls, compdef);
		}
	}

	/** Returns whether the specified component exists.
	 */
	public boolean contains(String name) {
		return _compdefs != null
				&& _compdefs.containsKey(isCaseInsensitive() ? name.toLowerCase(java.util.Locale.ENGLISH) : name);
	}

	/** Returns the component definition of the specified name, or null if not
	 * not found.
	 *
	 * <p>Note: unlike {@link LanguageDefinition#getComponentDefinition},
	 * this method doesn't throw ComponentNotFoundException if not found.
	 * It just returns null.
	 */
	public ComponentDefinition get(String name) {
		return _compdefs != null
				? _compdefs.get(isCaseInsensitive() ? name.toLowerCase(java.util.Locale.ENGLISH) : name) : null;
	}

	/** Returns the component definition of the specified class, or null if not
	 * found.
	 *
	 * <p>Note: unlike {@link LanguageDefinition#getComponentDefinition},
	 * this method doesn't throw ComponentNotFoundException if not found.
	 * It just returns null.
	 */
	public ComponentDefinition get(Class cls) {
		if (_compdefsByClass != null) {
			for (; cls != null; cls = cls.getSuperclass()) {
				final ComponentDefinition compdef = _compdefsByClass.get(cls.getName());
				if (compdef != null)
					return compdef;
			}
		}
		return null;
	}

	//Serializable//
	//NOTE: they must be declared as private
	private synchronized void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException {
		s.defaultWriteObject();

		if (_compdefs != null) {
			synchronized (_compdefs) {
				s.writeInt(_compdefs.size());
				for (ComponentDefinition compdef : _compdefs.values()) {
					if (compdef instanceof Serializable) {
						s.writeObject(compdef);
					} else {
						throw new java.io.NotSerializableException(compdef.getClass().getName());
					}
				}
			}
		} else {
			s.writeInt(0);
		}
	}

	private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException {
		s.defaultReadObject();

		int cnt = s.readInt();
		while (--cnt >= 0)
			add((ComponentDefinition) s.readObject());
	}

	//Cloneable//
	public Object clone() {
		final ComponentDefinitionMap clone;
		try {
			clone = (ComponentDefinitionMap) super.clone();
			clone._compdefs = Collections.synchronizedMap(new HashMap<>(_compdefs));
			clone._compdefsByClass = Collections
					.synchronizedMap(new HashMap<>(_compdefsByClass));
		} catch (CloneNotSupportedException ex) {
			throw new InternalError();
		}
		return clone;
	}
}
