//------------------------------------------------------------------------------
// Copyright (c) 2005, 2006 IBM Corporation and others.
// 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:
// IBM Corporation - initial implementation
//------------------------------------------------------------------------------
package org.eclipse.epf.uma.ecore.util;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.eclipse.emf.ecore.EStructuralFeature;

/**
 * Supports the reverse traversal of an association between two model objects.
 * 
 * @author Phong Nguyen Le
 * @author Kelvin Low
 * @since 1.0
 */
public class OppositeFeature {

	private String name;

	private EStructuralFeature targetFeature;

	private boolean isMany;

	private boolean resolveOwner = true;

	private Class ownerClass;

	//private static boolean deliverOnResolve;

	/**
	 * Maps source model objects to OppositeFeature objects.
	 */
	public static final Map classOppositeFeaturesMap = new HashMap();

	/**
	 * Maps EStructuralFeature objects to OppositeFeature objects.
	 */
	public static final Map featureOppositeFeatureMap = new HashMap();

	/**
	 * Creates a new instance.
	 * 
	 * @param ownerClass
	 *            the model class that owns this opposite feature
	 * @param name
	 *            the name of this opposite feature
	 * @param targetFeature
	 *            the feature which this opposite feature is based on
	 * @param isMany
	 *            if <true>, the multiplicity of the association is unbounded
	 * @param resolveOwner
	 *            if <true>, resolve the owner class if it is a proxy
	 */
	public OppositeFeature(Class ownerClass, String name,
			EStructuralFeature targetFeature, boolean isMany,
			boolean resolveOwner) {
		this.ownerClass = ownerClass;
		this.name = name;
		this.targetFeature = targetFeature;
		this.isMany = isMany;
		this.resolveOwner = resolveOwner;
	}

	/**
	 * Creates a new instance.
	 * 
	 * @param ownerClass
	 *            the model class that owns this opposite feature
	 * @param name
	 *            the name of this opposite feature
	 * @param targetFeature
	 *            the feature which this opposite feature is based on
	 * @param isMany
	 *            if <true>, the multiplicity of the association is unbounded
	 */
	public OppositeFeature(Class ownerClass, String name,
			EStructuralFeature targetFeature, boolean isMany) {
		this(ownerClass, name, targetFeature, isMany, true);
	}

	/**
	 * Gets the modal class that owns this opposite feature.
	 * 
	 * @return the modal class that owns this opposite feature
	 */
	public Class getOwnerClass() {
		return ownerClass;
	}

	/**
	 * Checks whether the association is unbounded.
	 * 
	 * @return <code>true</code> if the association is unbounded
	 */
	public boolean isMany() {
		return isMany;
	}

	/**
	 * Gets the name of this opposite feature.
	 * 
	 * @return the opposite feature name
	 */
	public String getName() {
		return name;
	}

	/**
	 * Gets the feature which this opposite feature is based on.
	 * 
	 * @return the original feature generated by EMF
	 */
	public EStructuralFeature getTargetFeature() {
		return targetFeature;
	}

	/**
	 * Gets the ID of this opposite feature.
	 * 
	 * @return the opposite feature ID
	 */
	public int getFeatureID() {
		return -1 - targetFeature.getFeatureID();
	}

	/**
	 * Checks whether the owner class of this opposite feature, if proxied,
	 * should be resolved when the target feature is set.
	 * 
	 * @return <true> if the owner class, if proxied, should be resolved
	 */
	public boolean resolveOwner() {
		return resolveOwner;
	}

	/**
	 * Gets a string representation of this opposite feature.
	 * 
	 * @return A <code>String</code> containing the key attributes of this
	 *         opposite feature.
	 */
	public String toString() {
		return name + "[targetFeature=" + targetFeature //$NON-NLS-1$
				+ ", isMany=" + isMany + "]"; //$NON-NLS-1$ //$NON-NLS-2$
	}

	/**
	 * Registers the given opposite feature.
	 * 
	 * @param oppositeFeature
	 *            the opposite feature to register
	 */
	public static final void registerOppositeFeature(
			OppositeFeature oppositeFeature) {
		Class cls = oppositeFeature.getOwnerClass();
		Set features = (Set) classOppositeFeaturesMap.get(cls);
		if (features == null) {
			features = new HashSet();
			classOppositeFeaturesMap.put(cls, features);
		}
		features.add(oppositeFeature);

		featureOppositeFeatureMap.put(oppositeFeature.getTargetFeature(),
				oppositeFeature);
	}

	/**
	 * Checks whether notification is sent when a proxy owner class is resolved.
	 * 
	 * @return <code>true</code> if notification is being sent when a proxy
	 *         owner class is resolved
	 */
	/*
	public static final boolean isDeliverOnResolve() {
		return deliverOnResolve;
	}
	*/

	/**
	 * Enables notification when a proxy owner class is resolved.
	 * 
	 * @param enableNotification
	 *            if <code>true</code>, send notification when a proxy owner
	 *            class is resolved
	 */
	/*
	public static final void setDeliverOnResolve(boolean enableNotification) {
		deliverOnResolve = enableNotification;
	}
	*/

	/**
	 * Gets the opposite feature for the given feature.
	 * 
	 * @param feature
	 *            an EMF <code>EStructuralFeature</code> feature
	 * @return an opposite feature that is based on the given feature
	 */
	public static final OppositeFeature getOppositeFeature(
			EStructuralFeature feature) {
		return (OppositeFeature) featureOppositeFeatureMap.get(feature);
	}

}
