/*******************************************************************************
* Copyright (c) 2008, 2014 Borland Software 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:
* Borland Software Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.m2m.internal.qvt.oml.editor.ui.completion;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import lpg.runtime.IPrsStream;
import lpg.runtime.IToken;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.m2m.internal.qvt.oml.ast.env.QvtOperationalEnv;
import org.eclipse.m2m.internal.qvt.oml.compiler.MetamodelRegistryProvider;
import org.eclipse.m2m.internal.qvt.oml.compiler.UnitProxy;
import org.eclipse.m2m.internal.qvt.oml.compiler.WorkspaceMetamodelRegistryProvider;
import org.eclipse.m2m.internal.qvt.oml.cst.MappingMethodCS;
import org.eclipse.m2m.internal.qvt.oml.cst.MappingModuleCS;
import org.eclipse.m2m.internal.qvt.oml.cst.UnitCS;
import org.eclipse.m2m.internal.qvt.oml.cst.parser.QVTOParsersym;
import org.eclipse.m2m.internal.qvt.oml.editor.ui.Activator;
import org.eclipse.m2m.internal.qvt.oml.emf.util.mmregistry.IMetamodelRegistryProvider;
import org.eclipse.m2m.internal.qvt.oml.emf.util.mmregistry.MetamodelRegistry;
import org.eclipse.ocl.lpg.AbstractLexer;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.texteditor.ITextEditor;
/**
* @author Aleksandr Igdalov
* Created on Jul 5, 2007
*/
public class QvtCompletionData {
public interface ITokenQualificator {
boolean isSuited(IToken token);
}
public static final int[] MAPPING_DECLARATION_TRAILING_TOKEN_KINDS = new int[] {
QVTOParsersym.TK_LBRACE, QVTOParsersym.TK_SEMICOLON,
QVTOParsersym.TK_when, QVTOParsersym.TK_where,
QVTOParsersym.TK_inherits, QVTOParsersym.TK_merges, QVTOParsersym.TK_disjuncts
};
private final ITextEditor myEditor;
private final ITextViewer myViewer;
private final IDocument myDocument;
private final IMetamodelRegistryProvider myMetamodelProvider;
private final int myOffset;
private final Map<String, Object> myUserData = new HashMap<String, Object>();
private IToken myLeftToken;
private IToken myCurrentToken;
private AbstractLexer myLexer;
private IPrsStream myPrsStream;
private Exception myException;
private IFile myIFile;
private UnitProxy myCFile;
private String myCharSet;
private QvtCompletionCompiler myQvtCompiler;
private IToken myParentImperativeOperation;
public QvtCompletionData(ITextEditor editor, ITextViewer viewer, UnitProxy unit, int offset) {
myEditor = editor;
myViewer = viewer;
myDocument = viewer.getDocument();
myOffset = offset;
myMetamodelProvider = new WorkspaceMetamodelRegistryProvider();
try {
// FIXME - accept other editor inputs
myIFile = ((FileEditorInput) myEditor.getEditorInput()).getFile();
myCharSet = myIFile.getCharset();
myCFile = unit;
myQvtCompiler = createQvtCompiler();
myLexer = myQvtCompiler.createLexer(unit);
myPrsStream = myLexer.getILexStream().getIPrsStream();
getLeftTokenAndCurrentToken();
} catch (Exception ex) {
myException = ex;
Activator.log(ex);
}
}
public String getCharacterSet() {
return myCharSet;
}
public MetamodelRegistry getMetamodelRegistry() {
return myMetamodelProvider.getRegistry(MetamodelRegistryProvider.createContext(myCFile.getURI()));
}
public ITextViewer getViewer() {
return myViewer;
}
public IDocument getDocument() {
return myDocument;
}
public int getOffset() {
return myOffset;
}
public IToken getLeftToken() {
return myLeftToken;
}
public IToken getCurrentToken() {
return myCurrentToken;
}
public AbstractLexer getLexer() {
return myLexer;
}
public IPrsStream getPrsStream() {
return myPrsStream;
}
public Map<String, Object> getUserData() {
return myUserData;
}
public UnitProxy getCFile() {
return myCFile;
}
public QvtOperationalEnv getEnvironment() {
return myQvtCompiler.compileAll();
}
public QvtCompletionCompiler getQvtCompiler() {
return myQvtCompiler;
}
public IToken getLeftToken(int leftOffsetIndex) {
int leftTokenIndex = myLeftToken.getTokenIndex();
int targetTokenIndex = leftTokenIndex - leftOffsetIndex;
if (targetTokenIndex >= 1) {
return myPrsStream.getTokenAt(targetTokenIndex);
}
return null;
}
public boolean isValid() {
return (myEditor != null) && (myViewer != null)
&& (myOffset >= 0)
&& (myLexer != null) && (myPrsStream != null)
&& (myException == null);
}
public void showError(String message) {
ErrorDialog.openError(myViewer.getTextWidget().getShell(),
Messages.QvtCompletionData_ErrorPerformingCodeCompletion, null,
new Status(IStatus.ERROR, Activator.PLUGIN_ID, message));
}
private void getLeftTokenAndCurrentToken() throws BadLocationException {
if ((myOffset == 0) || (myPrsStream.getTokens().isEmpty())) {
return;
}
// We might be at the end of some identifier
char previousChar = myViewer.getDocument().getChar(myOffset - 1);
if (Character.isJavaIdentifierPart(previousChar)) {
int tokenIndex = myPrsStream.getTokenIndexAtCharacter(myOffset - 1);
// getTokenIndexAtCharacter() may produce wrong results on first tokens
myLeftToken = (tokenIndex == 1) ? null : myPrsStream.getTokenAt(tokenIndex - 1);
myCurrentToken = myPrsStream.getTokenAt(tokenIndex);
} else {
int tokenIndex = myPrsStream.getTokenIndexAtCharacter(myOffset);
int leftTokenIndex = (tokenIndex <= 0) ? -tokenIndex : tokenIndex - 1;
if (leftTokenIndex != 0) {
myLeftToken = myPrsStream.getTokenAt(leftTokenIndex);
}
if (myPrsStream.getTokens().size() >= leftTokenIndex + 2) {
IToken next = myPrsStream.getTokenAt(leftTokenIndex + 1);
myCurrentToken = (next.getStartOffset() < myOffset) ? next : null;
}
}
}
/* Completion performed */
public MappingModuleCS[] getAllMappingModulesCS() {
myQvtCompiler.compileAll();
List<MappingModuleCS> modules = new ArrayList<MappingModuleCS>();
for (CFileData cFileData : myQvtCompiler.getCFileDataMap().values()) {
UnitCS unitCS = cFileData.getUnitCS();
if(unitCS != null) {
modules.addAll(unitCS.getModules());
}
}
return modules.toArray(new MappingModuleCS[modules.size()]);
}
public MappingModuleCS getCurrentMappingModuleCS() {
myQvtCompiler.compileAll();
CFileData cFileData = myQvtCompiler.getCFileDataMap().get(myCFile.getURI());
if (cFileData != null) {
return cFileData.getMappingModuleCS();
}
return null;
}
public MappingMethodCS[] getAllImperativeOperationsCS() {
MappingModuleCS[] allMappingModulesCS = getAllMappingModulesCS();
List<MappingMethodCS> methods = new ArrayList<MappingMethodCS>();
for (MappingModuleCS mappingModuleCS : allMappingModulesCS) {
methods.addAll(mappingModuleCS.getMethods());
}
return methods.toArray(new MappingMethodCS[methods.size()]);
}
public boolean isWithin(int[] keywordTokenKinds, int[] unexpectedTokenKinds) {
for (int i = myLeftToken.getTokenIndex(); i >= 0; i--) {
IToken token = myPrsStream.getTokenAt(i);
if (QvtCompletionData.isKindOf(token, keywordTokenKinds)) {
return true;
}
for (int unexpectedTokenKind : unexpectedTokenKinds) {
if (token.getKind() == unexpectedTokenKind) {
return false;
}
}
}
return false;
}
public IToken getParentBracingExpression(ITokenQualificator tokenQualif, int leftBraceKind, int rightBraceKind,
int maxDepth, int[] zeroDepthTerminatorKinds, int[] unexpectedTokenKinds, int[] ignoredClauses) {
int depth = 0;
Stack<Integer> maxCurrentDepthStack = new Stack<Integer>();
maxCurrentDepthStack.push(Integer.MIN_VALUE);
if (myLeftToken == null) {
return null;
}
for (int i = myLeftToken.getTokenIndex(); i >= 0; i--) {
IToken token = myPrsStream.getTokenAt(i);
if (tokenQualif.isSuited(token)) {
if ((depth >= 1) && (depth > maxCurrentDepthStack.peek())) {
return token;
}
} else if (token.getKind() == leftBraceKind) {
depth++;
if (depth > maxDepth) {
return null;
}
} else if (token.getKind() == rightBraceKind) {
if (depth >= maxCurrentDepthStack.peek()) {
maxCurrentDepthStack.push(depth);
}
depth--;
} else {
if ((depth == 0) && QvtCompletionData.isKindOf(token, zeroDepthTerminatorKinds)) {
return null;
}
if (QvtCompletionData.isKindOf(token, unexpectedTokenKinds)) {
return null;
}
}
if (QvtCompletionData.isKindOf(token, ignoredClauses)) {
IToken nextToken = LightweightParserUtil.getNextToken(token);
if ((nextToken != null) && QvtCompletionData.isKindOf(nextToken, leftBraceKind)
&& (depth == maxCurrentDepthStack.peek())) {
assert maxCurrentDepthStack.size() > 1;
if (maxCurrentDepthStack.size() > 1) {
maxCurrentDepthStack.pop();
}
}
}
}
return null;
}
private QvtCompletionCompiler createQvtCompiler() {
return new QvtCompletionCompiler(myMetamodelProvider, this);
}
// utility methods
public static final boolean isKindOf(IToken token, int... tokenKinds) {
if (tokenKinds != null) {
for (int tokenKind : tokenKinds) {
if (token.getKind() == tokenKind) {
return true;
}
}
}
return false;
}
public static final boolean isKindOf(IToken token, String... tokenKinds) {
if (tokenKinds != null) {
for (String tokenText : tokenKinds) {
if (tokenText.equals(token.toString())) {
return true;
}
}
}
return false;
}
public static final IToken[] extractTokens(IToken startToken, int... endTokenKinds) {
List<IToken> tokens = new ArrayList<IToken>();
IToken currentToken = startToken;
IPrsStream prsStream = startToken.getIPrsStream();
while (true) {
if (QvtCompletionData.isKindOf(currentToken, endTokenKinds)) {
return tokens.toArray(new IToken[tokens.size()]);
}
tokens.add(currentToken);
int currentTokenIndex = currentToken.getTokenIndex();
int nextTokenIndex = currentTokenIndex + 1;
if (nextTokenIndex < prsStream.getSize()) {
currentToken = prsStream.getTokenAt(nextTokenIndex);
} else {
return null;
}
}
}
public IToken getParentImperativeOperation() {
if (myParentImperativeOperation == null) {
myParentImperativeOperation = getParentBracingExpression(new ITokenQualificator() {
public boolean isSuited(IToken token) {
return QvtCompletionData.isKindOf(token, LightweightParserUtil.IMPERATIVE_OPERATION_TOKENS);
}
},
QVTOParsersym.TK_LBRACE, QVTOParsersym.TK_RBRACE, Integer.MAX_VALUE, null, null, LightweightParserUtil.MAPPING_CLAUSE_TOKENS);
if (myParentImperativeOperation != null) {
if (QvtCompletionData.isKindOf(myParentImperativeOperation, QVTOParsersym.TK_main)) {
IToken previousToken = LightweightParserUtil.getPreviousToken(myParentImperativeOperation);
if ((previousToken != null)
&& QvtCompletionData.isKindOf(previousToken,
QVTOParsersym.TK_mapping, QVTOParsersym.TK_query)) {
// The "mapping main" or "query main" cases - backward compatibility
myParentImperativeOperation = previousToken;
}
}
}
}
return myParentImperativeOperation;
}
public void setParentImperativeOperation(IToken parentImperativeOperation) {
myParentImperativeOperation = parentImperativeOperation;
}
}