/*
* 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.ui.contentassist;
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.AstEntity;
import net.sf.orcc.cal.cal.AstPort;
import net.sf.orcc.cal.cal.AstProcedure;
import net.sf.orcc.cal.cal.AstTag;
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.Function;
import net.sf.orcc.cal.cal.Inequality;
import net.sf.orcc.cal.cal.InputPattern;
import net.sf.orcc.cal.cal.Priority;
import net.sf.orcc.cal.cal.Variable;
import net.sf.orcc.cal.util.CalActionList;
import net.sf.orcc.cal.util.Util;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.xtext.Assignment;
import org.eclipse.xtext.CrossReference;
import org.eclipse.xtext.EcoreUtil2;
import org.eclipse.xtext.RuleCall;
import org.eclipse.xtext.resource.IEObjectDescription;
import org.eclipse.xtext.scoping.IScope;
import org.eclipse.xtext.ui.editor.contentassist.ContentAssistContext;
import org.eclipse.xtext.ui.editor.contentassist.ICompletionProposalAcceptor;
import com.google.common.base.Predicate;
/**
* see
* http://www.eclipse.org/Xtext/documentation/latest/xtext.html#contentAssist on
* how to customize content assistant
*/
public class CalProposalProvider extends AbstractCalProposalProvider {
/**
* Adds the name of units that contain objects referenceable from the model
* using the given reference.
*
* @param model
* the model
* @param reference
* a reference
* @param units
* a set of units
*/
private void addUnits(EObject model, EReference reference, Set<String> units) {
IScope scope = getScopeProvider().getScope(model, reference);
Iterable<IEObjectDescription> candidates = scope.getAllElements();
for (IEObjectDescription candidate : candidates) {
URI uri = candidate.getEObjectURI();
uri = uri.trimFragment();
EObject obj = model.eResource().getResourceSet()
.getResource(uri, true).getContents().get(0);
if (obj instanceof AstEntity) {
AstEntity entity = (AstEntity) obj;
AstUnit unit = entity.getUnit();
if (unit != null) {
String name = Util.getQualifiedName(entity);
units.add(name);
}
}
}
}
@Override
public void complete_QualifiedNameWithWildCard(EObject model,
RuleCall ruleCall, ContentAssistContext context,
ICompletionProposalAcceptor acceptor) {
if (model != null) {
// the set of units that contain variables
Set<String> units = new HashSet<String>();
// find all variables to which we have access and are in units
EReference reference = CalPackage.eINSTANCE
.getVariableReference_Variable();
addUnits(model, reference, units);
// find all functions to which we have access and are in units
reference = CalPackage.eINSTANCE.getExpressionCall_Function();
addUnits(model, reference, units);
for (String unit : units) {
if (!acceptor.canAcceptMoreProposals())
return;
ICompletionProposal proposal = createCompletionProposal(unit
+ ".*", context);
acceptor.accept(proposal);
}
}
}
@Override
public void completeAstTag_Identifiers(EObject model,
Assignment assignment, ContentAssistContext context,
ICompletionProposalAcceptor acceptor) {
if (model instanceof Inequality || model instanceof AstTransition) {
proposeAllTags(model.eContainer(), context, acceptor);
} else if (model instanceof AstTag) {
AstTag tag = (AstTag) model;
EObject parent = tag.eContainer();
if (parent instanceof Inequality || parent instanceof AstTransition) {
proposeTagAfter(tag, parent.eContainer(), context, acceptor);
}
}
}
@Override
public void completeExpressionCall_Function(EObject model,
Assignment assignment, ContentAssistContext context,
ICompletionProposalAcceptor acceptor) {
AstEntity entity = EcoreUtil2
.getContainerOfType(model, AstEntity.class);
if (entity != null) {
// TODO: propose the imported functions as well.
}
AstUnit unit = EcoreUtil2.getContainerOfType(model, AstUnit.class);
if (unit != null) {
proposeFunctions(unit.getFunctions(), context, acceptor);
}
AstActor actor = EcoreUtil2.getContainerOfType(model, AstActor.class);
if (actor != null) {
proposeFunctions(actor.getFunctions(), context, acceptor);
}
}
@Override
public void completeInequality_Tags(EObject model, Assignment assignment,
ContentAssistContext context, ICompletionProposalAcceptor acceptor) {
if (model instanceof Priority) {
proposeAllTags(model, context, acceptor);
}
}
@Override
public void completeInputPattern_Port(EObject model, Assignment assignment,
ContentAssistContext context, ICompletionProposalAcceptor acceptor) {
AstActor actor = (AstActor) model.eContainer();
proposePorts(actor.getInputs(), assignment, context, acceptor);
}
@Override
public void completeOutputPattern_Port(EObject model,
Assignment assignment, ContentAssistContext context,
ICompletionProposalAcceptor acceptor) {
AstActor actor = (AstActor) model.eContainer();
proposePorts(actor.getOutputs(), assignment, context, acceptor);
}
@Override
public void completeVariableReference_Variable(EObject model,
Assignment assignment, ContentAssistContext context,
ICompletionProposalAcceptor acceptor) {
AstEntity entity = EcoreUtil2
.getContainerOfType(model, AstEntity.class);
if (entity != null) {
// TODO: propose the imported variables as well.
}
AstUnit unit = EcoreUtil2.getContainerOfType(model, AstUnit.class);
if (unit != null) {
proposeVariables(unit.getVariables(), context, acceptor);
}
AstActor actor = EcoreUtil2.getContainerOfType(model, AstActor.class);
if (actor != null) {
proposeVariables(actor.getStateVariables(), context, acceptor);
}
AstAction action = EcoreUtil2
.getContainerOfType(model, AstAction.class);
if (action != null) {
proposeVariables(action.getVariables(), context, acceptor);
for (InputPattern input : action.getInputs()) {
proposeVariables(input.getTokens(), context, acceptor);
}
}
AstProcedure proc = EcoreUtil2.getContainerOfType(model,
AstProcedure.class);
if (proc != null) {
proposeVariables(proc.getVariables(), context, acceptor);
}
}
/**
* Proposes all tags of length 1.
*
* @param model
* @param context
* @param acceptor
*/
private void proposeAllTags(EObject model, ContentAssistContext context,
ICompletionProposalAcceptor acceptor) {
AstActor actor = (AstActor) model.eContainer();
CalActionList actionList = new CalActionList();
actionList.addActions(actor.getActions());
List<AstTag> tags = actionList.getTags(1);
for (AstTag tag : tags) {
String tagName = getLabelProvider().getText(tag);
ICompletionProposal proposal = createCompletionProposal(tagName,
context);
acceptor.accept(proposal);
}
}
/**
* @param functions
* @param context
* @param acceptor
*/
private void proposeFunctions(EList<Function> functions,
ContentAssistContext context, ICompletionProposalAcceptor acceptor) {
for (Function function : functions) {
String proposed = function.getName();
if (proposed.startsWith(context.getPrefix())) {
ICompletionProposal proposal = createCompletionProposal(
proposed, context);
acceptor.accept(proposal);
}
}
}
/**
* Proposes the ports from the given port list.
*
* @param ports
* @param assignment
* @param context
* @param acceptor
*/
private void proposePorts(final EList<AstPort> ports,
Assignment assignment, ContentAssistContext context,
ICompletionProposalAcceptor acceptor) {
lookupCrossReference(((CrossReference) assignment.getTerminal()),
context, acceptor, new Predicate<IEObjectDescription>() {
@Override
public boolean apply(IEObjectDescription objDesc) {
return ports.contains(objDesc.getEObjectOrProxy());
}
});
}
/**
* Proposes the tags that are immediate completions of the given tag.
* <ol>
* <li>build action list</li>
* <li>get actions that match the tag minus its last component</li>
* <li>for each action, propose the next component</li>
* </ol>
*
* @param tag
* @param context
* @param acceptor
*/
private void proposeTagAfter(AstTag tag, EObject parent,
ContentAssistContext context, ICompletionProposalAcceptor acceptor) {
AstActor actor = (AstActor) parent.eContainer();
int n = tag.getIdentifiers().size() - 1;
List<String> identifiers = tag.getIdentifiers().subList(0, n);
CalActionList actionList = new CalActionList();
actionList.addActions(actor.getActions());
List<AstAction> actions = actionList.getTaggedActions(identifiers);
for (AstAction action : actions) {
identifiers = action.getTag().getIdentifiers();
if (n < identifiers.size()) {
List<String> last = identifiers.subList(n, n + 1);
AstTag proposedTag = CalFactory.eINSTANCE.createAstTag();
proposedTag.getIdentifiers().addAll(last);
String tagName = getLabelProvider().getText(proposedTag);
ICompletionProposal proposal = createCompletionProposal(
tagName, context);
acceptor.accept(proposal);
}
}
}
/**
* Proposes the variables from the given variable list whose names match
* with the prefix of the current context.
*
* @param vars
* @param context
* @param acceptor
*/
private void proposeVariables(EList<Variable> vars,
ContentAssistContext context, ICompletionProposalAcceptor acceptor) {
for (Variable var : vars) {
String proposed = var.getName();
if (proposed.startsWith(context.getPrefix())) {
ICompletionProposal proposal = createCompletionProposal(
proposed, context);
acceptor.accept(proposal);
}
}
}
}