/*******************************************************************************
 * Copyright (c) 2001, 2009 IBM Corporation and others.
 * All rights reserved. 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
 *     Jens Lukowski/Innoopract - initial renaming/restructuring
 *     
 *******************************************************************************/
package org.eclipse.wst.sse.core.internal.model;



import java.util.ArrayList;
import java.util.List;

import org.eclipse.wst.sse.core.internal.Logger;
import org.eclipse.wst.sse.core.internal.provisional.INodeAdapterFactory;


/**
 * This class simply maintains the list of factories and returns singleton
 * instances of them. Some "built in" types are automatically created form
 * FactoryConfig, if not found registerd, but normally clients can/should
 * register their own factories.
 * 
 * Not intended for clients to subclass or instantiate.
 * 
 */
public final class FactoryRegistry {

	private List factories;

	/**
	 * intentionally default access
	 */
	FactoryRegistry() {
		super();

	}

	private List _getFactories() {

		if (factories == null) {
			// may need to use java.util.Collections.synchronizedList() if
			// syncronization becomes
			// necessary (and if so, remember to synchronize on factories)
			factories = new ArrayList();
		}
		return factories;

	}

	public void addFactory(INodeAdapterFactory factory) {
		_getFactories().add(factory);
	}

	public void clearFactories() {
		factories.clear();
	}

	/*
	 * @see FactoryRegistry#contains(Object)
	 */
	public boolean contains(Object type) {
		boolean result = false;
		// note: we're not using cloned list, so strictly speaking
		// is not thread safe.
		List internalList = _getFactories();
		for (int i = 0; i < internalList.size(); i++) {
			INodeAdapterFactory factory = (INodeAdapterFactory) internalList.get(i);
			if (factory.isFactoryForType(type)) {
				result = true;
				break;
			}
		}
		return result;
	}

	/**
	 * Returns a shallow copy of the list of factories in the registry. Note:
	 * this can not be used to add/remove factories. Its primarily provided
	 * for those few cases where a list of factories must be copied from one
	 * model and added to another.
	 */
	public List getFactories() {
		// note: for object integrity, we don't let anyone get
		// our main list (so they have to add through addFactory),
		// but we will return a shallow "cloned" list.
		List factoryList = new ArrayList(_getFactories());
		return factoryList;
	}

	/**
	 * This method is a not a pure resistry. Factories retrieved based on
	 * their response to "isFactoryForType(type)". Note that if there is more
	 * than one factory that can answer 'true' that the most recently added
	 * factory is used.
	 */
	public INodeAdapterFactory getFactoryFor(Object type) {

		INodeAdapterFactory result = null;
		if (factories == null)
			return null;
		int listSize = factories.size();
		for (int i = listSize - 1; i >= 0; i--) {
			// It is the adapter factories responsibility to answer
			// isFactoryForType so it gets choosen.
			// Notice we are going through the list backwards to get the
			// factory added last.
			INodeAdapterFactory a = (INodeAdapterFactory) factories.get(i);
			if (a.isFactoryForType(type)) {
				result = a;
				break;
			}
		}
		return result;

	}

	/**
	 * 
	 */
	public void release() {
		// modified to work on copy of list, for V5PTF1
		// send release to local copy of list
		// of factories, since some factories, during
		// their release function, may remove
		// themselves from the registry.
		List localList = getFactories();
		for (int i = 0; i < localList.size(); i++) {
			INodeAdapterFactory a = (INodeAdapterFactory) localList.get(i);
			// To help bullet proof code, we'll catch and log
			// any messages thrown by factories during release,
			// but we'll attempt to keep going.
			// In nearly all cases, though, such errors are
			// severe for product/client, and need to be fixed.
			try {
				a.release();
			}
			catch (Exception e) {
				Logger.logException("Program problem releasing factory" + a, e); //$NON-NLS-1$
			}
		}
	}

	/**
	 * Removes a factory if it can be retrieved by getFactoryFor(type). If
	 * there is more than one, all are removed. If there is none, the call
	 * simply returns (that is, it is not considered an error).
	 */
	public void removeFactoriesFor(java.lang.Object type) {
		if (factories != null) {
			int listSize = factories.size();
			// we'll go backwards through list, since we're removing, so
			// 'size' change won't matter.
			// Note: I'm assuming other items in the collection do not change
			// position
			// simply because another was removed.
			for (int i = listSize - 1; i >= 0; i--) {
				// It is the adapter factories responsibility to answer
				// isFactoryForType so it gets choosen.
				INodeAdapterFactory a = (INodeAdapterFactory) factories.get(i);
				if (a.isFactoryForType(type)) {
					factories.remove(a);
				}
			}
		}
	}

	public void removeFactory(INodeAdapterFactory factory) {
		_getFactories().remove(factory);

	}

}
