/*******************************************************************************
 * Copyright (c) 2006 Oracle 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:
 *     Oracle Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.bpel.validator.rules;

import java.util.Map;
import java.util.Set;

import org.eclipse.bpel.validator.model.ARule;
import org.eclipse.bpel.validator.model.Filters;
import org.eclipse.bpel.validator.model.IFilter;
import org.eclipse.bpel.validator.model.IModelQueryLookups;
import org.eclipse.bpel.validator.model.INode;
import org.eclipse.bpel.validator.model.IProblem;
import org.eclipse.bpel.validator.model.NodeNameFilter;



/**
 * Validates variable related rules.
 * <p>
 * When thinking 
 * @author Michal Chmielewski (michal.chmielewski@oracle.com)
 * @date Sep 14, 2006
 *
 */


@SuppressWarnings("nls")

public class LinkValidator extends CValidator {
	
	/** Parent nodes */
	static public IFilter<INode> PARENTS = new NodeNameFilter( ND_LINKS );
	
	
	String ncName ;


	protected INode fFlowNode;


	protected Set<String> fSet;	

	protected Map<String,INode> fSourceMap;
	
	protected Map<String,INode> fTargetMap;


	protected INode fSourceNode;

	protected INode fTargetNode;
	
	
	
	/**
	 * Start performing link checks.
	 */	
	
	@Override
	protected void start () {
		super.start();		
		ncName = mNode.getAttribute( AT_NAME );
		
		fFlowNode = mSelector.selectParent(mNode, Filters.FLOW);
		
	}
	
	/**
	 * Rule to check the name of the link. 
	 */
	@ARule(
		sa = 0,
		desc = "Check the NCName of the link",
		author = "michal.chmielewski@oracle.com",
		date = "02/15/2007"
	)
	
	public void rule_CheckName_1 () {
		
		// Must be a valid NCName ...		
		if (checkNCName(mNode, ncName, AT_NAME ) == false) {
			disableRules();
		}
			
		fSet = getValue (fFlowNode, FlowValidator.LINKS_SET , null);
		if (fSet != null) {
			fSet.add ( ncName );
		}			
		
	}
	
	
	 
	/**
	 * Checks the link connections to source and target.
	 * 
	 */
	
	
	@ARule(
		sa = 66,
		desc = "Every link must have exactly one activity as source and one as target",
		author = "michal.chmielewski@oracle.com",
		date = "02/17/2007",
		tag = "pass2",
		order = 10
	)
	
	public void rule_CheckLinkConnections_10 () {
		IProblem problem;
		
		fSourceMap = getValue(fFlowNode,FlowValidator.SOURCE_MAP,null);
		
		if (fSourceMap != null) {
			
			if (fSourceMap.containsKey(ncName) == false) {
				problem = createError();
				problem.fill("BPELC_LINK__NO_SOURCE",
						toString(mNode.nodeName()),
						ncName);
			}
			fSourceNode = fSourceMap.get(ncName);
		}
		
		
		fTargetMap = getValue (fFlowNode,FlowValidator.TARGET_MAP,null);
		if (fTargetMap != null) {
			if (fTargetMap.containsKey(ncName) == false) {
				problem = createError();
				problem.fill("BPELC_LINK__NO_TARGET",
						toString(mNode.nodeName()),
						ncName);
			}
			
			fTargetNode = fTargetMap.get(ncName);
		}
	}
	
	
	 
	/**
	 * Checks the link connections to source and target.
	 * 
	 */
	
	
	@ARule(
		sa = 67,
		desc = "Two different links MUST NOT share the same source *and* target activities; "
			   + " that is, at most one link may be used to connect two activities.",
		author = "michal.chmielewski@oracle.com",
		date = "02/17/2007",
		tag = "pass2",
		order = 12
	)
	
	public void rule_CheckDistinctLinks_12 () {
		IProblem problem;
		
		if (fSourceMap == null || fTargetMap == null || fSourceNode == null || fTargetNode == null ) {
			return ;
		}				
		
		for(String linkName : fSet) {
			
			// skip ourselves.
			if (linkName.equals(ncName)) {
				continue;
			}
			
			INode linkTarget = fTargetMap.get(linkName);
			INode linkSource = fSourceMap.get(linkName);
			
			if (mModelQuery.check(IModelQueryLookups.TEST_EQUAL, linkTarget, fTargetNode) &&
				mModelQuery.check(IModelQueryLookups.TEST_EQUAL, linkSource, fSourceNode)) {
			
				problem = createError();
				problem.fill("BPELC_LINK__NOT_UNIQUE",
						toString(mNode.nodeName()),
						ncName,
						linkName);				
			}			
		}
	}
	
	
	
	/**
	 * Check for target before the source ... 
	 */
	
	@ARule(
		sa = 72,
		desc = "A <link> declared in a <flow> MUST NOT create a control cycle, "
			 + " that is, the source activity must not " 
			 + " have the target activity as a logically preceding activity.",			 
		author = "michal.chmielewski@oracle.com",
		date = "02/17/2007",
		tag = "pass2",
		order = 14
	)
		
	public void rule_ControlCycle_14 () {
		IProblem problem;
		
		if (fSourceNode == null || fTargetNode == null ) {
			return ;
		}
		
		
		// check if we can get from target to source node ...
		
	
		INode linkTarget = null;
		INode linkSource = null;
		
		if (mModelQuery.check(IModelQueryLookups.TEST_EQUAL, linkTarget, fTargetNode) &&
			mModelQuery.check(IModelQueryLookups.TEST_EQUAL, linkSource, fSourceNode)) {
		
			problem = createError();
			problem.fill("BPELC_LINK__CYCLE",
					toString(mNode.nodeName()),
					ncName,
					fSourceNode.nodeName(),
					fTargetNode.nodeName()
					);				
		}			
	}
		
		
		
	
	/** 
	 * End of public rule methods.
	 * 
	 * Other methods are support methods for this class to perform its
	 * validation function.
	 * 
	 */

}
