/*******************************************************************************
* Copyright (c) 2009 R.Dvorak 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:
* Radek Dvorak - initial API and implementation
*******************************************************************************/
package org.eclipse.m2m.qvt.oml.debug.core.vm;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.eclipse.emf.ecore.EOperation;
import org.eclipse.m2m.internal.qvt.oml.ast.parser.QvtOperationalAstWalker;
import org.eclipse.m2m.internal.qvt.oml.common.util.LineNumberProvider;
import org.eclipse.m2m.internal.qvt.oml.compiler.CompiledUnit;
import org.eclipse.m2m.internal.qvt.oml.expressions.ContextualProperty;
import org.eclipse.m2m.internal.qvt.oml.expressions.ExpressionsPackage;
import org.eclipse.m2m.internal.qvt.oml.expressions.ImperativeOperation;
import org.eclipse.m2m.internal.qvt.oml.expressions.MappingBody;
import org.eclipse.m2m.internal.qvt.oml.expressions.MappingCallExp;
import org.eclipse.m2m.internal.qvt.oml.expressions.Module;
import org.eclipse.m2m.internal.qvt.oml.expressions.ObjectExp;
import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.AssignExp;
import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.BlockExp;
import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.BreakExp;
import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.ContinueExp;
import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.ImperativeIterateExp;
import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.ImperativeLoopExp;
import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.VariableInitExp;
import org.eclipse.ocl.ecore.IteratorExp;
import org.eclipse.ocl.ecore.OperationCallExp;
import org.eclipse.ocl.expressions.OCLExpression;
import org.eclipse.ocl.utilities.ASTNode;
import org.eclipse.ocl.utilities.Visitable;
public class ValidBreakpointLocator {
private static final class BreakpointableNodeLocator implements QvtOperationalAstWalker.NodeProcessor {
private final List<ASTNode> elements;
private final int lineNumber;
private final LineNumberProvider lineNumbers;
BreakpointableNodeLocator(int lineNumber, LineNumberProvider lineNumbers) {
this.elements = new ArrayList<ASTNode>();
this.lineNumber = lineNumber;
this.lineNumbers = lineNumbers;
}
public void process(Visitable visitable, Visitable parent) {
if (visitable instanceof ASTNode == false) {
return;
}
ASTNode astNode = (ASTNode) visitable;
boolean found = false;
if(isBreakpointableElementStart(astNode)) {
int line = lineNumbers.getLineNumber(astNode.getStartPosition());
if(line == lineNumber) {
elements.add(astNode);
found = true;
}
}
if(!found && isBreakpointableElementEnd(astNode)) {
int line = lineNumbers.getLineNumber(astNode.getEndPosition());
if(line == lineNumber) {
elements.add(astNode);
}
}
}
}
private ValidBreakpointLocator() {
super();
}
public static List<ASTNode> getBreakpointableElementsForLine(CompiledUnit compiledModule, final LineNumberProvider lineNumbers, final int lineNumber) {
BreakpointableNodeLocator locator = new BreakpointableNodeLocator(lineNumber, lineNumbers);
QvtOperationalAstWalker walker = new QvtOperationalAstWalker(locator);
for(Module nextModule : compiledModule.getModules()) {
nextModule.accept(walker);
List<ASTNode> elements = locator.elements;
if(!elements.isEmpty()) {
// already found, can't be spread across multiple modules
return elements;
}
}
return Collections.emptyList();
}
static boolean isBreakpointableElementStart(ASTNode element) {
boolean breakpointable =
(element instanceof OCLExpression<?> ||
element instanceof ObjectExp ||
element instanceof AssignExp ||
element instanceof BreakExp ||
element instanceof ContinueExp ||
element instanceof VariableInitExp
) && element instanceof BlockExp == false
&& element instanceof ImperativeIterateExp == false
&& element instanceof IteratorExp == false
&& element instanceof ContextualProperty == false
&& element instanceof ImperativeLoopExp == false;
if (breakpointable && (element instanceof ObjectExp)) {
if(element.eContainer() instanceof MappingBody &&
element.eContainingFeature() == ExpressionsPackage.eINSTANCE.getOperationBody_Content()) {
// Remark:
// Here we try to check if the object expression is implicit
// IOW, has no concrete syntaxt element => mapped to mapping
// body by its positions
MappingBody mappingBody = (MappingBody) element.eContainer();
ObjectExp objectExp = (ObjectExp) element;
return mappingBody.getStartPosition() != objectExp.getStartPosition();
}
}
return breakpointable;
}
static boolean isBreakpointableElementEnd(ASTNode element) {
EOperation referredOperation = null;
if (element instanceof OperationCallExp) {
referredOperation = ((OperationCallExp) element).getReferredOperation();
}
boolean breakpointable = element instanceof ImperativeOperation
|| element instanceof MappingCallExp
|| referredOperation instanceof ImperativeOperation;
return breakpointable;
}
}