/*
 * The contents of this file are subject to the terms 
 * of the Common Development and Distribution License 
 * (the License).  You may not use this file except in
 * compliance with the License.
 * 
 * You can obtain a copy of the license at 
 * https://glassfish.dev.java.net/public/CDDLv1.0.html or
 * glassfish/bootstrap/legal/CDDLv1.0.txt.
 * See the License for the specific language governing 
 * permissions and limitations under the License.
 * 
 * When distributing Covered Code, include this CDDL 
 * Header Notice in each file and include the License file 
 * at glassfish/bootstrap/legal/CDDLv1.0.txt.  
 * If applicable, add the following below the CDDL Header, 
 * with the fields enclosed by brackets [] replaced by
 * you own identifying information: 
 * "Portions Copyrighted [year] [name of copyright owner]"
 * 
 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
 */

/*
 * NameMapper.java
 *
 * Created on December 11, 2001, 9:51 AM
 */

package com.sun.jdo.spi.persistence.support.ejb.model.util;

import java.util.*;

import com.sun.jdo.api.persistence.mapping.ejb.AbstractNameMapper;

import com.sun.enterprise.deployment.*;

/** This is a class which helps translate between the various names of the 
 * CMP (ejb name, abstract schema, abstract bean, concrete bean, local
 * interface, remote interface) and the persistence-capable class name.  It 
 * also has methods for translation of field names.  The basic entry point 
 * is ejb name or persistence-capable class name.  This is a subclass of 
 * the AbstractNameMapper and implements the methods based on DOL.  It 
 * also adds methods which are used during deployment time but not needed
 * during development time and therefore, not in the abstract superclass.
 *
 * @author Rochelle Raccah
 */
public abstract class NameMapper extends AbstractNameMapper
{
	private EjbBundleDescriptor _bundleDescriptor;
	private Map _generatedRelToInverseRelMap;
	private Map _relToInverseGeneratedRelMap;

	/** Creates a new instance of NameMapper
	 * @param bundleDescriptor the EjbBundleDescriptor which defines the 
	 * universe of names for this application.
	 */
	protected NameMapper (EjbBundleDescriptor bundleDescriptor)
	{
		_bundleDescriptor = bundleDescriptor;
		initGeneratedRelationshipMaps();
	}

	private void initGeneratedRelationshipMaps ()
	{
		EjbBundleDescriptor bundleDescriptor = getBundleDescriptor();
		Set relationships = bundleDescriptor.getRelationships();

		_generatedRelToInverseRelMap = new HashMap();
		_relToInverseGeneratedRelMap = new HashMap();

		// during development time this code may attempt to get the 
		// iterator even with no relationships, so protect it by a 
		// null check
		if (relationships != null)
		{
			Iterator iterator = relationships.iterator();
			List generatedRels = new ArrayList();
			int counter = 0;

			// gather list of generated cmr fields by examining source and sink
			while (iterator.hasNext())
			{
				RelationshipDescriptor relationship = 
					(RelationshipDescriptor)iterator.next();

				if (relationship.getSource().getCMRField() == null)
					generatedRels.add(relationship);

				if (relationship.getSink().getCMRField() == null)
					generatedRels.add(relationship);
			}

			// now update the maps to contain this info
			iterator = generatedRels.iterator();
			while (iterator.hasNext())
			{
				RelationshipDescriptor relationship = 
					(RelationshipDescriptor)iterator.next();
				RelationRoleDescriptor source = relationship.getSource();
				String sourceEjbName = source.getOwner().getName();
				String sourceCMRField = source.getCMRField();
				boolean sourceIsNull = (sourceCMRField == null);
				RelationRoleDescriptor sink = relationship.getSink();
				String sinkEjbName = sink.getOwner().getName();
				String ejbName = (sourceIsNull ? sourceEjbName : sinkEjbName);
				String otherEjbName = 
					(sourceIsNull ? sinkEjbName : sourceEjbName);
				List ejbField = Arrays.asList(new String[]{otherEjbName, 
					(sourceIsNull ? sink.getCMRField() : sourceCMRField)});
				PersistenceDescriptor pDescriptor = ((EjbCMPEntityDescriptor)
					bundleDescriptor.getEjbByName(ejbName)).
					getPersistenceDescriptor();
				List generatedField = null;
				String uniqueName = null;

				// make sure the user doesn't already have a field
				// with this name
				do
				{
					counter++;
					uniqueName = GENERATED_CMR_FIELD_PREFIX + counter;
				} while (hasField(pDescriptor, uniqueName));

				generatedField = 
					Arrays.asList(new String[]{ejbName, uniqueName});
				_generatedRelToInverseRelMap.put(generatedField, ejbField);
				_relToInverseGeneratedRelMap.put(ejbField, generatedField);
			}
		}
	}

	protected Map getGeneratedFieldsMap ()
	{
		return _generatedRelToInverseRelMap;
	}
	protected Map getInverseFieldsMap () { return _relToInverseGeneratedRelMap; }

	// isCMPField does not return true for relationships, so we use getTypeFor
	private boolean hasField (PersistenceDescriptor persistenceDescriptor, 
		String fieldName)
	{
		Class fieldType = null;

		try
		{
			fieldType = persistenceDescriptor.getTypeFor(fieldName);
		}
		catch (RuntimeException e)
		{
			// fieldType will be null - there is no such field
		}

		return (fieldType != null);
	}

	/** Gets the EjbBundleDescriptor which defines the universe of
	 * names for this application.
	 * @return the EjbBundleDescriptor which defines the universe of
	 * names for this application.
	 */
	public EjbBundleDescriptor getBundleDescriptor ()
	{
		return _bundleDescriptor;
	}

	/** Gets the EjbCMPEntityDescriptor which represents the ejb  
	 * with the specified name.
	 * @param name the name of the ejb
	 * @return the EjbCMPEntityDescriptor which represents the ejb.
	 */
	abstract public EjbCMPEntityDescriptor getDescriptorForEjbName (
		String name);

	/** Get the type of key class of this ejb.
	 * @return the key class type, one of {@link #USER_DEFINED_KEY_CLASS}, 
	 * {@link #PRIMARY_KEY_FIELD}, or {@link #UNKNOWN_KEY_CLASS}
	 */
	public int getKeyClassTypeForEjbName (String name)
	{
		String keyClass = getKeyClassForEjbName(name);

		if (!"java.lang.Object".equals(keyClass))		// NOI18N
		{
			EjbCMPEntityDescriptor descriptor = getDescriptorForEjbName(name);

			return ((descriptor.getPrimaryKeyFieldDesc() != null) ?
				PRIMARY_KEY_FIELD : USER_DEFINED_KEY_CLASS);
		}

		return UNKNOWN_KEY_CLASS;
	}

	/** Gets the name of the ejb which corresponds to the specified abstract 
	 * schema name.
	 * @param schemaName the name of the abstract schema
	 * @return the name of the ejb for the specified abstract schema
	 */
	abstract public String getEjbNameForAbstractSchema (String schemaName);

	/** Gets the name of the concrete bean class which corresponds to the 
	 * specified ejb.
	 * @param name the name of the ejb
	 * @return the name of the concrete bean for the specified ejb
	 */
	abstract public String getConcreteBeanClassForEjbName (String name);
}
