/*******************************************************************************
 * Copyright (c) 2005, 2007 BEA Systems, Inc.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    mkaufman@bea.com - initial API and implementation
 *******************************************************************************/


package org.eclipse.jdt.apt.core.internal.util;

import java.io.IOException;
import java.util.Map;

/**
 * An entry on the processor factory path.  Typically a jar, plug-in,
 * etc. that contains annotation processors.  It may contain Java 5
 * processors, Java 6 processors, both or neither. 
 */
public abstract class FactoryContainer
{
	public enum FactoryType {
		PLUGIN,  // Eclipse plugin 
		EXTJAR,  // external jar file (not in workspace)
		WKSPJAR, // jar file within workspace
		VARJAR;  // external jar file referenced by classpath variable
	}
	
	/**	
	 * Returns an ID that is guaranteed to be sufficiently unique for this container --
	 * that is, all necessary state can be reconstructed from just the id and FactoryType.
	 * For plugins, it's the plugin id, for jar files, the path to the jar, etc.
	 */
	public abstract String getId();
	
	/**
	 * This method is used to display the container in the UI.
	 * If this default implementation is not adequate for a particular
	 * container, that container should provide an override.
	 */
	@Override
	public String toString() {
		return getId();
	}
	
	public abstract FactoryType getType();
	
	/**
	 * Test whether the resource that backs this container exists,
	 * can be located, and is (at least in principle) accessible for 
	 * factories to be loaded from.  For instance, a plugin exists if 
	 * the plugin is loaded in Eclipse; a jar exists if the jar file 
	 * can be found on disk.  The test is not required to be perfect:
	 * for instance, a jar file might exist but be corrupted and
	 * therefore not really readable, but this method would still return
	 * true.
	 * @return true if the resource backing the container exists.
	 */
	public abstract boolean exists();
	
	/**
	 * Subclasses must return a map of implementation name to service
	 * name, for all the processor services this container provides.
	 * @throws IOException
	 */
	protected abstract Map<String, String> loadFactoryNames() throws IOException;
	
	/**
	 * Map of implementation name to service name.  For instance,
	 * "org.xyz.FooProcessor" -> "javax.annotation.processing.Processor".
	 */
	protected Map<String, String> _factoryNames;
	
	public Map<String, String> getFactoryNames() throws IOException
	{ 
		if ( _factoryNames == null )
			_factoryNames = loadFactoryNames();
		return _factoryNames;
	}
	
	@Override
	public int hashCode() {
		return getType().hashCode() ^ getId().hashCode();
	}

	@Override
	public boolean equals(Object o) {
		if (!(o instanceof FactoryContainer)) {
			return false;
		}

		FactoryContainer other = (FactoryContainer) o;
		return other.getType() == getType() && other.getId().equals(getId());
	}
}

