/*
* Copyright (c) 2010, IETR/INSA of Rennes
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of the IETR/INSA of Rennes nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
* WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
package net.sf.orcc.cal.scoping;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import net.sf.orcc.cal.cal.AstAction;
import net.sf.orcc.cal.cal.AstActor;
import net.sf.orcc.cal.cal.AstProcedure;
import net.sf.orcc.cal.cal.AstState;
import net.sf.orcc.cal.cal.AstTransition;
import net.sf.orcc.cal.cal.AstUnit;
import net.sf.orcc.cal.cal.CalFactory;
import net.sf.orcc.cal.cal.CalPackage;
import net.sf.orcc.cal.cal.ExpressionList;
import net.sf.orcc.cal.cal.ExternalTarget;
import net.sf.orcc.cal.cal.Fsm;
import net.sf.orcc.cal.cal.Function;
import net.sf.orcc.cal.cal.Generator;
import net.sf.orcc.cal.cal.Guard;
import net.sf.orcc.cal.cal.InputPattern;
import net.sf.orcc.cal.cal.ScheduleFsm;
import net.sf.orcc.cal.cal.StatementForeach;
import net.sf.orcc.cal.cal.Variable;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.xtext.EcoreUtil2;
import org.eclipse.xtext.nodemodel.ILeafNode;
import org.eclipse.xtext.nodemodel.INode;
import org.eclipse.xtext.nodemodel.util.NodeModelUtils;
import org.eclipse.xtext.scoping.IScope;
import org.eclipse.xtext.scoping.Scopes;
import org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider;
/**
* This class contains custom scoping description.
*
* see : http://www.eclipse.org/Xtext/documentation/latest/xtext.html#scoping on
* how and when to use it
*
*/
public class CalScopeProvider extends AbstractDeclarativeScopeProvider {
private void addState(Fsm fsm, Set<String> nameSet, List<INode> nodes) {
ILeafNode leaf = (ILeafNode) nodes.get(0);
String name = leaf.getText();
if (!nameSet.contains(name)) {
AstState state = CalFactory.eINSTANCE.createAstState();
state.setName(name);
state.setNode(leaf);
fsm.getStates().add(state);
nameSet.add(name);
}
}
private void buildStates(Fsm fsm) {
Set<String> nameSet = new HashSet<String>();
// source states
for (AstTransition transition : fsm.getTransitions()) {
List<INode> nodes = NodeModelUtils.findNodesForFeature(transition,
CalPackage.eINSTANCE.getAstTransition_Source());
addState(fsm, nameSet, nodes);
}
}
/**
* Returns the scope that contains all variables and parameters of the given
* action/function/procedure.
*
* @param eObject
* @param parameters
* @param variables
* @param reference
* @return
*/
private IScope getScope(EObject eObject, List<Variable> parameters,
List<Variable> variables, EReference reference) {
IScope outer = getScope(eObject.eContainer(), reference);
return Scopes.scopeFor(variables, Scopes.scopeFor(parameters, outer));
}
private IScope getScope(Fsm fsm) {
if (fsm.getStates().isEmpty()) {
buildStates(fsm);
}
return Scopes.scopeFor(fsm.getStates());
}
/**
* Returns a scope vi-1 -> ... -> v1 -> v0 -> parameters where vi is the
* given variable (the scope includes previous variables up to it).
*
* @param variable
* @param parameters
* @param variables
* @param reference
* @return
*/
private IScope getScope(Variable variable, List<Variable> parameters,
List<Variable> variables, EReference reference) {
IScope outer = getScope(variable.eContainer().eContainer(), reference);
IScope inner = Scopes.scopeFor(parameters, outer);
for (Variable aVar : variables) {
List<Variable> elements = new ArrayList<Variable>(1);
if (aVar == variable) {
break;
}
elements.add(aVar);
inner = Scopes.scopeFor(elements, inner);
}
return inner;
}
/**
* Returns the scope of procedures within an actor.
*
* @param actor
* an actor
* @param reference
* a reference
* @return a scope
*/
public IScope scope_AstProcedure(AstActor actor, EReference reference) {
return Scopes.scopeFor(actor.getProcedures(),
delegateGetScope(actor, reference));
}
/**
* Returns the scope of procedures within a unit.
*
* @param unit
* a unit
* @param reference
* a reference
* @return a scope
*/
public IScope scope_AstProcedure(AstUnit unit, EReference reference) {
return Scopes.scopeFor(unit.getProcedures(),
delegateGetScope(unit, reference));
}
/**
* Returns the scope for a source state referenced inside a transition.
*
* @param fsm
* a fsm
* @param reference
* a variable reference
* @return a scope
*/
public IScope scope_AstTransition_source(Fsm fsm, EReference reference) {
return getScope(fsm);
}
/**
* Returns the scope for a source state referenced inside a transition.
*
* @param fsm
* a fsm
* @param reference
* a variable reference
* @return a scope
*/
public IScope scope_AstTransition_target(Fsm fsm, EReference reference) {
return getScope(fsm);
}
public IScope scope_ExternalTarget_from(ExternalTarget target,
EReference reference) {
return getScope(target.getFsm().getContents());
}
public IScope scope_ExternalTarget_fsm(ExternalTarget target,
EReference reference) {
AstActor actor = EcoreUtil2.getContainerOfType(target, AstActor.class);
return Scopes.scopeFor(actor.getLocalFsms());
}
public IScope scope_ExternalTarget_state(ExternalTarget target,
EReference reference) {
return getScope(target.getFsm().getContents());
}
public IScope scope_ExternalTarget_to(ExternalTarget target,
EReference reference) {
return getScope((Fsm) target.eContainer());
}
/**
* Returns the scope of functions within an actor.
*
* @param actor
* an actor
* @param reference
* a reference
* @return a scope
*/
public IScope scope_Function(AstActor actor, EReference reference) {
return Scopes.scopeFor(actor.getFunctions(),
delegateGetScope(actor, reference));
}
/**
* Returns the scope of functions within a unit.
*
* @param unit
* a unit
* @param reference
* a reference
* @return a scope
*/
public IScope scope_Function(AstUnit unit, EReference reference) {
return Scopes.scopeFor(unit.getFunctions(),
delegateGetScope(unit, reference));
}
/**
* Returns the scope for an input pattern.
*
* @param action
* an action
* @param reference
* a variable reference
* @return a scope
*/
public IScope scope_InputPattern_port(AstAction action, EReference reference) {
AstActor actor = (AstActor) action.eContainer();
return Scopes.scopeFor(actor.getInputs());
}
/**
* Returns the scope for an output pattern.
*
* @param action
* an action
* @param reference
* a variable reference
* @return a scope
*/
public IScope scope_OutputPattern_port(AstAction action,
EReference reference) {
AstActor actor = (AstActor) action.eContainer();
return Scopes.scopeFor(actor.getOutputs());
}
/**
* Returns the scope for a source state referenced inside a transition.
*
* @param schedule
* a schedule
* @param reference
* a variable reference
* @return a scope
*/
public IScope scope_ScheduleFsm_initialState(ScheduleFsm schedule,
EReference reference) {
return getScope(schedule.getContents());
}
/**
* Returns the scope for a variable referenced inside an action.
*
* @param action
* an action
* @param reference
* a variable reference
* @return a scope
*/
public IScope scope_VariableReference_variable(AstAction action,
EReference reference) {
List<Variable> tokens = new ArrayList<Variable>();
for (InputPattern pattern : action.getInputs()) {
tokens.addAll(pattern.getTokens());
}
return getScope(action, tokens, action.getVariables(), reference);
}
/**
* Returns the scope for a variable referenced inside an actor.
*
* @param actor
* an actor
* @param ref
* a variable reference
* @return a scope
*/
public IScope scope_VariableReference_variable(AstActor actor,
EReference reference) {
List<Variable> elements = new ArrayList<Variable>();
elements.addAll(actor.getParameters());
elements.addAll(actor.getStateVariables());
return Scopes.scopeFor(elements, delegateGetScope(actor, reference));
}
/**
* Returns a scope for variables referenced in a guard. The scope is the
* same as the action's one, but doesn't contains local variables of this
* action.
*
* @param guard
* @param reference
* @return
*/
public IScope scope_VariableReference_variable(Guard guard,
EReference reference) {
final AstAction action = (AstAction) guard.eContainer();
// Compute the list of input ports variables for the current Action
final List<Variable> tokens = new ArrayList<Variable>();
for (final InputPattern pattern : action.getInputs()) {
tokens.addAll(pattern.getTokens());
}
return getScope(action, tokens, Collections.<Variable> emptyList(),
reference);
}
/**
* Returns the scope for a variable referenced inside a procedure.
*
* @param procedure
* a procedure
* @param reference
* a variable reference
* @return a scope
*/
public IScope scope_VariableReference_variable(AstProcedure procedure,
EReference reference) {
return getScope(procedure, procedure.getParameters(),
procedure.getVariables(), reference);
}
/**
* Returns the scope for a variable referenced inside a generator.
*
* @param list
* a list expression
* @param reference
* a variable reference
* @return a scope
*/
public IScope scope_VariableReference_variable(ExpressionList list,
EReference reference) {
List<Variable> elements = new ArrayList<Variable>();
for (Generator generator : list.getGenerators()) {
elements.add(generator.getVariable());
}
EObject container = list.eContainer();
return Scopes.scopeFor(elements, getScope(container, reference));
}
/**
* Returns the scope for a variable referenced inside a function.
*
* @param function
* a function
* @param reference
* a variable reference
* @return a scope
*/
public IScope scope_VariableReference_variable(Function function,
EReference reference) {
return getScope(function, function.getParameters(),
function.getVariables(), reference);
}
/**
* Returns the scope for a variable referenced inside a foreach statement.
*
* @param foreach
* a foreach statement
* @param reference
* a variable reference
* @return a scope
*/
public IScope scope_VariableReference_variable(StatementForeach foreach,
EReference reference) {
List<Variable> variables = new ArrayList<Variable>();
variables.add(foreach.getVariable());
return Scopes.scopeFor(variables,
getScope(foreach.eContainer(), reference));
}
/**
* Returns the scope for a variable referenced inside a variable
* declaration.
*
* @param variable
* a variable declaration
* @param reference
* a variable reference
* @return a scope
*/
public IScope scope_VariableReference_variable(Variable variable,
EReference reference) {
EObject cter = variable.eContainer();
List<Variable> parameters;
List<Variable> variables;
if (cter instanceof Function) {
parameters = ((Function) cter).getParameters();
variables = ((Function) cter).getVariables();
} else if (cter instanceof AstProcedure) {
parameters = ((AstProcedure) cter).getParameters();
variables = ((AstProcedure) cter).getVariables();
} else if (cter instanceof AstAction) {
AstAction action = (AstAction) cter;
parameters = new ArrayList<Variable>();
for (InputPattern pattern : action.getInputs()) {
parameters.addAll(pattern.getTokens());
}
variables = action.getVariables();
} else {
// in a state variable
return getScope(variable.eContainer(), reference);
}
return getScope(variable, parameters, variables, reference);
}
}