blob: 075d1b60aaa2ed61faef6771ec05be2ace8a1c81 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005 - 2007 committers of openArchitectureWare 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:
* committers of openArchitectureWare - initial API and implementation
*******************************************************************************/
package org.eclipse.xtend.ui.debug;
import static org.eclipse.internal.xtend.expression.debug.ExpressionElementAdapter.TYPE;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Stack;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.debug.core.model.IBreakpoint;
import org.eclipse.emf.mwe.ui.debug.model.MWEBreakpoint;
import org.eclipse.emf.mwe.ui.debug.processing.PluginAdapter;
import org.eclipse.emf.mwe.ui.workflow.util.PluginConfigurationElementUtil;
import org.eclipse.internal.xpand2.ast.Statement;
import org.eclipse.internal.xpand2.ast.TextStatement;
import org.eclipse.internal.xtend.expression.ast.BooleanOperation;
import org.eclipse.internal.xtend.expression.ast.Case;
import org.eclipse.internal.xtend.expression.ast.Cast;
import org.eclipse.internal.xtend.expression.ast.ChainExpression;
import org.eclipse.internal.xtend.expression.ast.CollectionExpression;
import org.eclipse.internal.xtend.expression.ast.Expression;
import org.eclipse.internal.xtend.expression.ast.FeatureCall;
import org.eclipse.internal.xtend.expression.ast.ISyntaxElement;
import org.eclipse.internal.xtend.expression.ast.IfExpression;
import org.eclipse.internal.xtend.expression.ast.LetExpression;
import org.eclipse.internal.xtend.expression.ast.ListLiteral;
import org.eclipse.internal.xtend.expression.ast.OperationCall;
import org.eclipse.internal.xtend.expression.ast.SwitchExpression;
import org.eclipse.internal.xtend.expression.ast.SyntaxElement;
import org.eclipse.internal.xtend.expression.debug.BaseSpecialTreatment;
import org.eclipse.internal.xtend.expression.debug.EvaluatedElementWrapper;
import org.eclipse.internal.xtend.expression.debug.ExpressionModelPresentation;
import org.eclipse.internal.xtend.xtend.ast.Around;
import org.eclipse.internal.xtend.xtend.ast.CreateExtensionStatement;
import org.eclipse.internal.xtend.xtend.ast.ExpressionExtensionStatement;
import org.eclipse.internal.xtend.xtend.ast.ExtensionFile;
import org.eclipse.internal.xtend.xtend.ast.JavaExtensionStatement;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.widgets.Display;
import org.eclipse.xtend.shared.ui.Activator;
import org.eclipse.xtend.shared.ui.core.IXtendXpandProject;
import org.eclipse.xtend.shared.ui.core.IXtendXpandResource;
import org.eclipse.xtend.ui.XtendEditorPlugin;
import org.eclipse.xtend.ui.editor.XtendEditor;
/**
* The IPluginAdapter implementation for Expressions.
*
* @author Clemens Kadura (zAJKa)
* @author Karsten Thoms - maintenance
* @author Aykut Kilic (itemis) - Bug#465802,480646
*/
public class ExpressionPluginAdapter implements PluginAdapter {
protected ExpressionModelPresentation pres;
protected Set<BaseSpecialTreatment> specials = new HashSet<BaseSpecialTreatment>();
// -------------------------------------------------------------------------
public ExpressionPluginAdapter() {
pres = new ExpressionModelPresentation(specials);
}
// -------------------------------------------------------------------------
protected String getRequiredExtension() {
return "ext"; // TODO: CK: high: handle chk as well
}
public boolean canHandleResourceExtension(String ext) {
return getRequiredExtension().equals(ext);
}
public boolean canHandleType(String type) {
return TYPE.equals(type);
}
public String getEditorId() {
// TODO: CK: next: integrate Check editor
return PluginConfigurationElementUtil.getConfigAttribute("org.eclipse.ui.editors/" + "editor[class=" + XtendEditor.class.getName() + "]/id");
}
public boolean isToggleBpEnabled(IResource resource, int start, int end, int line) {
ISyntaxElement element = findElementForPosition(resource, start, line);
if (element == null)
return false;
return pres.getStart(element) <= start && pres.getStartingEndPosition(element) >= end;
}
public MWEBreakpoint createBreakpoint(IResource resource, int start, int end, int line) throws CoreException {
ISyntaxElement element = findElementForPosition(resource, start, line);
if (element == null || (!(element instanceof FeatureCall)
&& !(element instanceof Statement)) || element instanceof TextStatement
|| element.getEnd() == 0 )
// TODO: CK: Message that no BP could be created
return null;
return new MWEBreakpoint(resource, element.getNameString(null), element.getLine(), element.getStart(), element.getEnd());
}
public IBreakpoint checkBreakpoints(IBreakpoint[] bps, IResource resource, int start, int end, int line) throws CoreException {
ISyntaxElement element = findElementForPosition(resource, start, line);
if(element == null) return null;
for (IBreakpoint bp1 : bps) {
MWEBreakpoint bp = (MWEBreakpoint) bp1;
if (bp.getResource().equals(resource.getFullPath().toString())
&& bp.getLine() == element.getLine())
return bp;
}
return null;
}
/**
* @since 2.2
*/
protected ISyntaxElement findElementForPosition(final IResource resource, final int position, final int line) {
ISyntaxElement rootElem = getContainingRootElement(resource, position);
if (rootElem == null)
return null;
ISyntaxElement body = getBodyOfRootElement(rootElem);
if (body instanceof ChainExpression) {
return getContainingElementOfChainExpression(
(ChainExpression) body, position);
} else if (body instanceof LetExpression) {
return getContainingElementOfLetExpression((LetExpression) body,
position);
} else if (body instanceof IfExpression) {
return getContainingElementOfIfExpression((IfExpression) body, position);
} else
return getContainingElement((SyntaxElement)body,position);
}
private boolean containsPosition(final ISyntaxElement elem, final int position) {
return elem.getStart() <= position && elem.getEnd() > position;
}
/**
* @since 2.2
*/
protected int shiftPositionIfInside(final int currentPosition,
final SyntaxElement lowerBound, final SyntaxElement upperBound) {
return currentPosition >= lowerBound.getStart()
&& currentPosition <= upperBound.getStart() ? upperBound
.getStart() : currentPosition;
}
/**
* @since 2.2
*/
protected ISyntaxElement getContainingElement(final SyntaxElement se, final int position) {
if (!containsPosition(se, position)) return null;
if ( se instanceof ChainExpression )
return getContainingElementOfChainExpression((ChainExpression) se, position);
else if ( se instanceof LetExpression ) {
return getContainingElementOfLetExpression((LetExpression) se, position);
} else if ( se instanceof SwitchExpression ) {
return getContainingElementOfSwitchExpression((SwitchExpression) se, position);
} else if ( se instanceof ListLiteral ) {
return getContainingElementOfListLiteral((ListLiteral) se, position);
} else if ( se instanceof IfExpression ) {
return getContainingElementOfIfExpression((IfExpression) se, position );
} else if ( se instanceof CollectionExpression ) {
return getContainingElementOfCollectionExpression((CollectionExpression)se, position);
} else if ( se instanceof BooleanOperation ) {
return getContainingElementOfBooleanOperation( (BooleanOperation)se, position );
} else if ( se instanceof FeatureCall ) {
return getContainingElementOfFeatureCall( (FeatureCall)se, position );
}
return null;
}
/**
* @since 2.2
*/
protected ISyntaxElement getContainingChild(final SyntaxElement parent, final Collection<SyntaxElement> children, int position) {
if(!containsPosition(parent, position)) return null;
ISyntaxElement result = null;
int newPosition = position;
SyntaxElement lastElem = parent;
for( SyntaxElement child : children) {
if( child == null ) continue;
fixBoundaries(child);
if( child.getEnd()==0 ) {
System.out.println(child.getClass().getSimpleName() + " " + child.toString());
}
newPosition = shiftPositionIfInside(newPosition, lastElem, child);
result = getContainingElement(child, newPosition);
if(result != null) return result;
lastElem = child;
}
return parent;
}
private ISyntaxElement getContainingElementOfLetExpression(final LetExpression le, int position) {
List<SyntaxElement> children = new ArrayList<SyntaxElement>();
children.add(le.getVarExpression());
children.add(le.getTargetExpression());
return getContainingChild(le, children, position);
}
private ISyntaxElement getContainingElementOfIfExpression(final IfExpression ie, int position) {
List<SyntaxElement> children = new ArrayList<SyntaxElement>();
children.add(ie.getCondition());
children.add(ie.getThenPart());
children.add(ie.getElsePart());
return getContainingChild(ie, children, position);
}
private ISyntaxElement getContainingElementOfCollectionExpression(final CollectionExpression ce, int position ) {
List<SyntaxElement> children = new ArrayList<SyntaxElement>();
children.add(ce.getClosure());
return getContainingChild(ce, children, position);
}
private SyntaxElement fixBoundaries(SyntaxElement e) {
if( !( e.getStart() == 0 && e.getEnd() == 0) )
return e;
if( e instanceof BooleanOperation ) {
BooleanOperation be = (BooleanOperation)e;
SyntaxElement l = fixBoundaries(be.getLeft());
SyntaxElement r = fixBoundaries(be.getRight());
be.setLine(l.getLine());
be.setStart(l.getStart());
be.setEnd(r.getEnd());
}
return e;
}
private ISyntaxElement getContainingElementOfBooleanOperation( final BooleanOperation be, int position ) {
fixBoundaries(be);
List<SyntaxElement> children = new ArrayList<SyntaxElement>();
children.add(be.getLeft());
children.add(be.getRight());
return getContainingChild(be, children, position);
}
private ISyntaxElement getContainingElementOfFeatureCall( final FeatureCall fe, int position ) {
if(!containsPosition(fe,position))
return null;
Stack<FeatureCall> fcStack = new Stack<FeatureCall>();
Expression e = fe;
do {
FeatureCall fc = (FeatureCall)e;
fcStack.push(fc);
e = fc.getTarget();
} while( e instanceof FeatureCall );
FeatureCall fallback = fcStack.peek();
while( !fcStack.isEmpty() ) {
FeatureCall fc = fcStack.pop();
if(fc instanceof OperationCall) {
ISyntaxElement result = getContainingElementOfOperationCall((OperationCall)fc, position);
if(result != null)
return result;
} else if( fc instanceof CollectionExpression) {
if(containsPosition(fc, position) ) return getContainingElement(((CollectionExpression)fc).getClosure(),position);
}
}
return fallback;
}
private ISyntaxElement getContainingElementOfOperationCall( final OperationCall oe, int position ) {
if(isFunctionCall(oe)) return oe;
List<SyntaxElement> children = new ArrayList<SyntaxElement>(oe.getParamsAsList());
return getContainingChild(oe, children, position);
}
private boolean isFunctionCall(OperationCall op) {
char[] name = op.getName().toString().toCharArray();
if( name.length == 0 ) return false;
for( int i=0; i<name.length; i++ ) {
if(i==0) {
if(!Character.isJavaIdentifierStart(name[i])) return false;
} else{
if(!Character.isJavaIdentifierPart(name[i])) return false;
}
}
return true;
}
private ISyntaxElement getContainingElementOfListLiteral(final ListLiteral ll, int position) {
List<SyntaxElement> children = new ArrayList<SyntaxElement>(ll.getElementsAsList());
return getContainingChild(ll, children, position);
}
private ISyntaxElement getContainingElementOfSwitchExpression(final SwitchExpression se, int position) {
List<SyntaxElement> children = new ArrayList<SyntaxElement>();
children.add(se.getSwitchExpr());
for (Case caze : se.getCases()) {
children.add(caze.getCondition());
children.add(caze.getThenPart());
}
children.add(se.getDefaultExpr());
return getContainingChild(se, children, position);
}
private ISyntaxElement getContainingElementOfChainExpression(final ChainExpression ce, final int position) {
List<SyntaxElement> children = new ArrayList<SyntaxElement>();
children.add(ce.getFirst());
children.add(ce.getNext());
return getContainingChild(ce, children, position);
}
/**
* @since 2.2
*/
protected ISyntaxElement getContainingRootElement(final IResource resource,
final int position) {
IXtendXpandResource file = getXtendXpandResource(resource);
List<ISyntaxElement> elems = collectFirstLevelElements(file);
for (ISyntaxElement elem : elems)
if (containsPosition(elem, position))
return elem;
return null;
}
private IXtendXpandResource getXtendXpandResource(final IResource resource) {
final IXtendXpandProject project = Activator.getExtXptModelManager().findProject(resource.getProject().getFullPath());
if (project != null) {
for (IXtendXpandResource res : project.getAllRegisteredResources()) {
if (!resource.equals(res.getUnderlyingStorage()))
continue;
return res;
}
}
return null;
}
// -------------------------------------------------------------------------
// Expression specific methods
protected List<ISyntaxElement> collectFirstLevelElements(IXtendXpandResource res) {
ExtensionFile file = (ExtensionFile) res.getExtXptResource();
List<ISyntaxElement> result = new ArrayList<ISyntaxElement>();
result.addAll(file.getArounds());
result.addAll(file.getChecks());
for (ISyntaxElement ext : file.getExtensions()) {
// Hint: we don't create BP internal of Java extension statements
// Java Debugger to be used instead
if (ext instanceof JavaExtensionStatement)
continue;
result.add(ext);
}
return result;
}
/**
* @since 2.2
*/
protected ISyntaxElement getBodyOfRootElement(final ISyntaxElement elem) {
if (elem instanceof ExpressionExtensionStatement)
return ((ExpressionExtensionStatement) elem).getExpression();
else if (elem instanceof CreateExtensionStatement)
return ((CreateExtensionStatement) elem).getExpression();
else if (elem instanceof Around)
return ((Around) elem).getExpression();
return null;
}
protected List<ISyntaxElement> getBody(ISyntaxElement exp) {
// Hint: we accept all kinds of body elements here, because their
// children could be OperationCalls
ISyntaxElement[] body = new ISyntaxElement[0];
if (exp instanceof IfExpression) {
IfExpression op = (IfExpression) exp;
body = new ISyntaxElement[] { op.getCondition(), op.getThenPart(), op.getElsePart() };
} else if (exp instanceof OperationCall) {
// Note: Check first for OperationCall because it extends
// FeatureCall
OperationCall op = (OperationCall) exp;
List<ISyntaxElement> kids = new ArrayList<ISyntaxElement>();
kids.add(op.getTarget());
kids.add(new EvaluatedElementWrapper(op));
kids.addAll(op.getParamsAsList());
body = kids.toArray(new ISyntaxElement[0]);
} else if (exp instanceof FeatureCall)
body = new ISyntaxElement[] { ((FeatureCall) exp).getTarget(), new EvaluatedElementWrapper(exp) };
else if (exp instanceof ExpressionExtensionStatement)
body = new ISyntaxElement[] { ((ExpressionExtensionStatement) exp).getExpression() };
else if (exp instanceof CreateExtensionStatement)
body = new ISyntaxElement[] { ((CreateExtensionStatement) exp).getExpression() };
else if (exp instanceof Around)
body = new ISyntaxElement[] { ((Around) exp).getExpression() };
else if (exp instanceof BooleanOperation) {
BooleanOperation op = (BooleanOperation) exp;
body = new ISyntaxElement[] { op.getLeft(), op.getRight() };
} else if (exp instanceof SwitchExpression) {
SwitchExpression op = (SwitchExpression) exp;
body = new ISyntaxElement[] { op.getSwitchExpr(), op.getDefaultExpr() };
} else if (exp instanceof ChainExpression) {
ChainExpression op = (ChainExpression) exp;
body = new ISyntaxElement[] { op.getFirst(), op.getNext() };
} else if (exp instanceof LetExpression) {
LetExpression op = (LetExpression) exp;
body = new ISyntaxElement[] { op.getVarExpression(), op.getTargetExpression() };
} else if (exp instanceof Cast) {
Cast op = (Cast) exp;
body = new ISyntaxElement[] { op.getTarget() };
}
List<ISyntaxElement> result = new ArrayList<ISyntaxElement>();
for (ISyntaxElement element : body)
if (element != null)
result.add(element);
return result;
}
protected boolean isValidReturnElement(final ISyntaxElement se) {
return (se instanceof OperationCall || se instanceof ChainExpression);
}
// -------------------------------------------------------------------------
private Image icon = null;
public Image getIcon() {
if (icon != null)
return icon;
IPath path = new Path("/icons/extensionfile.gif");
InputStream is = null;
try {
is = FileLocator.openStream(XtendEditorPlugin.getDefault().getBundle(), path, false);
} catch (IOException e) {
}
if (is != null) {
final ImageData iData = new ImageData(is);
icon = new Image(Display.getCurrent(), iData);
return icon;
}
return null;
}
}