/*******************************************************************************
 * Copyright (c) 1998, 2012 Oracle and/or its affiliates. All rights reserved.
 * This program and the accompanying materials are made available under the 
 * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 
 * which accompanies this distribution. 
 * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
 * and the Eclipse Distribution License is available at 
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * Contributors:
 *     Oracle - initial API and implementation from Oracle TopLink
******************************************************************************/
package org.eclipse.persistence.tools.workbench.mappingsmodel.handles;

import org.eclipse.persistence.tools.workbench.mappingsmodel.MWModel;
import org.eclipse.persistence.tools.workbench.mappingsmodel.meta.MWClass;
import org.eclipse.persistence.tools.workbench.mappingsmodel.meta.MWClassAttribute;
import org.eclipse.persistence.tools.workbench.utility.node.Node;

import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.mappings.OneToOneMapping;
import org.eclipse.persistence.oxm.XMLDescriptor;

/**
 * MWAttributeHandle is used to isolate the painful bits of code
 * necessary to correctly handle references to MWClassAttributes.
 * 
 * Since a MWClassAttribute is nested within the XML file
 * for a MWClass, we need to store a reference to a particular
 * attribute as a pair of instance variables:
 *   - the name of the declaring MWClass
 *   - the name of the attribute
 * 
 * This causes no end of pain when dealing with TopLink, property
 * change listeners, backward-compatibility, etc.
 */
public final class MWAttributeHandle extends MWHandle {

	/**
	 * This is the actual attribute.
	 * It is built from the declaring type and attribute names, below.
	 */
	private volatile MWClassAttribute attribute;

	/**
	 * The declaring type and attribute names are transient. They
	 * are used only to hold their values until postProjectBuild()
	 * is called and we can resolve the actual attribute.
	 * We do not keep these in synch with the attribute itself because
	 * we cannot know when the attribute has been renamed etc.
	 */
	private volatile String attributeDeclaringTypeName;
	private volatile String attributeName;


	// ********** constructors **********

	/**
	 * default constructor - for TopLink use only
	 */
	private MWAttributeHandle() {
		super();
	}
	
	public MWAttributeHandle(MWModel parent, NodeReferenceScrubber scrubber) {
		super(parent, scrubber);
	}

	public MWAttributeHandle(MWModel parent, MWClassAttribute attribute, NodeReferenceScrubber scrubber) {
		super(parent, scrubber);
		this.attribute = attribute;
	}


	// ********** instance methods **********

	public MWClassAttribute getAttribute() {
		return this.attribute;
	}

	public void setAttribute(MWClassAttribute attribute) {
		this.attribute = attribute;
	}

	protected Node node() {
		return getAttribute();
	}

	public MWAttributeHandle setScrubber(NodeReferenceScrubber scrubber) {
		this.setScrubberInternal(scrubber);
		return this;
	}

	public void postProjectBuild() {
		super.postProjectBuild();
		if (this.attributeDeclaringTypeName != null && this.attributeName != null) {
			// the type will never be null - the repository will auto-generate one if necessary
			this.attribute = this.typeNamed(this.attributeDeclaringTypeName).attributeNamedFromCombinedAll(this.attributeName);
		}
		// Ensure attributeDeclaringTypeName and attributeName are not
		// used by setting them to null....
		// If the XML is corrupt and only one of these attributes is populated,
		// this will cause the populated attribute to be cleared out if the
		// objects are rewritten.
		this.attributeDeclaringTypeName = null;
		this.attributeName = null;
	}

	/**
	 * Override to delegate comparison to the attribute itself.
	 * If the handles being compared are in a collection that is being sorted,
	 * NEITHER attribute should be null.
	 */
	public int compareTo(Object o) {
		return this.attribute.compareTo(((MWAttributeHandle) o).attribute);
	}

	public void toString(StringBuffer sb) {
		if (this.attribute == null) {
			sb.append("null");
		} else {
			this.attribute.toString(sb);
		}
	}


	// ********** TopLink methods **********

	public static XMLDescriptor buildDescriptor(){
		XMLDescriptor descriptor = new XMLDescriptor();
	
		descriptor.setJavaClass(MWAttributeHandle.class);
	
		descriptor.addDirectMapping("attributeDeclaringTypeName", "getAttributeDeclaringTypeNameForTopLink", "setAttributeDeclaringTypeNameForTopLink", "attribute-declaring-type-name/text()");	
		descriptor.addDirectMapping("attributeName", "getAttributeNameForTopLink", "setAttributeNameForTopLink", "attribute-name/text()");
	
		return descriptor;
	}
	
	private String getAttributeDeclaringTypeNameForTopLink(){
		return (this.attribute == null) ? null : this.attribute.getDeclaringType().getName();
	}

	private void setAttributeDeclaringTypeNameForTopLink(String attributeDeclaringTypeName){
		this.attributeDeclaringTypeName = attributeDeclaringTypeName;
	}

	private String getAttributeNameForTopLink() {
		return (this.attribute == null) ? null : attribute.getName();
	}

	private void setAttributeNameForTopLink(String attributeName) {
		this.attributeName = attributeName;
	}
}
