/*******************************************************************************
 * Copyright (c) 2006, 2015 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.cdt.internal.core.dom.lrparser.symboltable;

import org.eclipse.cdt.internal.core.dom.lrparser.c99.bindings.IC99Binding;

/**
 * A facade for a FunctionalMap to make it behave like
 * a symbol table for C99.
 *
 * In particular we need to be able to lookup identifiers based both
 * on the String representation of the identifier and its "namespace".
 *
 * @author Mike Kucera
 */
public class C99SymbolTable {

	/**
	 * Adapter objects are used as the keys. The trick here is to implement
	 * compareTo() in such a way that identifiers are separated by their namespace.
	 */
	private static class Key implements Comparable<Key> {
		private final String ident;
		private final CNamespace namespace;

		public Key(CNamespace namespace, String ident) {
			if (namespace == null || ident == null)
				throw new NullPointerException();

			this.ident = ident;
			this.namespace = namespace;
		}

		@Override
		public int compareTo(Key x) {
			// this separates namespaces in the symbol table
			int c = namespace.compareTo(x.namespace);
			// only if the namespace is the same do we check the identifier
			return (c == 0) ? ident.compareTo(x.ident) : c;
		}

		@Override
		public String toString() {
			return ident + "::" + namespace;//$NON-NLS-1$
		}
	}

	/**
	 * Start with EMPTY_TABLE and build up a symbol table using insert().
	 */
	public static final C99SymbolTable EMPTY_TABLE = new C99SymbolTable();

	// the map we are providing a facade for
	private final FunctionalMap<Key, IC99Binding> map;

	/**
	 * Constructors are private, start with EMPTY_TABLE
	 * and build it up using insert().
	 */
	private C99SymbolTable() {
		map = FunctionalMap.emptyMap();
	}

	private C99SymbolTable(FunctionalMap<Key, IC99Binding> newRoot) {
		map = newRoot;
	}

	/**
	 * Returns a new symbol table that contains the given mapping.
	 * @throws NullPointerException if the namespace or key is null.
	 */
	public C99SymbolTable insert(CNamespace ns, String key, IC99Binding binding) {
		return new C99SymbolTable(map.insert(new Key(ns, key), binding));
	}

	/**
	 * Looks up the binding given its namespace and identifier.
	 * @return null If there is no binding corresponding to the key.
	 * @throws NullPointerException if the namespace or key is null.
	 */
	public IC99Binding lookup(CNamespace ns, String key) {
		return map.lookup(new Key(ns, key));
	}

	public int size() {
		return map.size();
	}

	public boolean isEmpty() {
		return map.size() == 0;
	}

	@Override
	public String toString() {
		return map.toString();
	}

	//	void printStructure() {
	//		map.printStructure();
	//	}

}
