blob: 96753a121c073615cc6b553093a74f7c8c280a64 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2013 CEA LIST.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Ansgar Radermacher - ansgar.radermacher@cea.fr CEA LIST - initial API and implementation
*
*******************************************************************************/
package org.eclipse.papyrus.designer.languages.java.jdt.texteditor.sync;
import java.util.List;
import org.eclipse.emf.common.util.EList;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IImportDeclaration;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.ILocalVariable;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IParent;
import org.eclipse.jdt.core.ISourceRange;
import org.eclipse.jdt.core.ISourceReference;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jface.text.rules.IToken;
import org.eclipse.papyrus.designer.languages.common.base.StringConstants;
import org.eclipse.papyrus.designer.languages.common.extensionpoints.ILangCodegen;
import org.eclipse.papyrus.designer.languages.common.extensionpoints.LanguageCodegen;
import org.eclipse.papyrus.designer.languages.common.extensionpoints.SyncInformation;
import org.eclipse.papyrus.designer.languages.java.jdt.texteditor.Activator;
import org.eclipse.papyrus.designer.languages.java.jdt.texteditor.TextEditorConstants;
import org.eclipse.papyrus.designer.languages.java.jdt.texteditor.Utils;
import org.eclipse.papyrus.designer.languages.java.jdt.texteditor.listener.ModelListener;
import org.eclipse.papyrus.designer.languages.java.profile.PapyrusJava.Array;
import org.eclipse.papyrus.designer.languages.java.profile.PapyrusJava.Final;
import org.eclipse.papyrus.designer.languages.java.profile.PapyrusJava.Import;
import org.eclipse.papyrus.designer.transformation.base.utils.CommandSupport;
import org.eclipse.papyrus.uml.tools.utils.StereotypeUtil;
import org.eclipse.ui.IEditorInput;
import org.eclipse.uml2.uml.Behavior;
import org.eclipse.uml2.uml.BehavioralFeature;
import org.eclipse.uml2.uml.Classifier;
import org.eclipse.uml2.uml.Comment;
import org.eclipse.uml2.uml.NamedElement;
import org.eclipse.uml2.uml.Operation;
import org.eclipse.uml2.uml.Parameter;
import org.eclipse.uml2.uml.ParameterDirectionKind;
import org.eclipse.uml2.uml.Type;
public class SyncJDTtoModel implements Runnable {
// standard name used for return types
public static final String RET = "ret"; //$NON-NLS-1$
// special treatment of void in case of return parameters
public static final String VOID_TYPE = "void"; //$NON-NLS-1$
public static final String REGISTER = "register"; //$NON-NLS-1$
public static final String CONST = "const"; //$NON-NLS-1$
public static final String VOLATILE = "volatile"; //$NON-NLS-1$
public static final String sAtParam = "@param"; //$NON-NLS-1$
public static final String sAtReturn = "@return"; //$NON-NLS-1$
public static final String ansiCLib = "AnsiCLibrary"; //$NON-NLS-1$
public SyncJDTtoModel(IEditorInput input, Classifier classifier, String projectName, String generatorID) {
m_input = input;
m_classifier = classifier;
m_projectName = projectName;
m_codegen = LanguageCodegen.getGenerator(TextEditorConstants.JAVA, generatorID);
}
public final String c_cpp_langID = "C/C++"; //$NON-NLS-1$
public void syncCDTtoModel() {
CommandSupport.exec(m_classifier, "update model from CDT", this); //$NON-NLS-1$
}
@Override
public void run() {
IJavaElement ice = null; // CDTUITools.getEditorInputCElement(m_input);
ModelListener.syncFromEditor = true;
if (ice instanceof ICompilationUnit) {
ICompilationUnit icu = (ICompilationUnit) ice;
try {
examineChildren(icu, icu);
} catch (JavaModelException e) {
Activator.log.error(e);
}
}
ModelListener.syncFromEditor = false;
}
/**
* Examine the children of a translation unit in order to extract the
* methods that are defined within hte unit
*
* @param icu
* @param parent
* @throws JavaModelException
*/
public void examineChildren(ICompilationUnit icu, IParent parent)
throws JavaModelException {
int position = 0;
// if (parent instanceof Namespace) {
for (IJavaElement child : parent.getChildren()) {
if (child instanceof IParent) {
examineChildren(icu, (IParent) child);
}
ISourceRange range = null;
if (child instanceof ISourceReference) {
range = ((ISourceReference) child).getSourceRange();
}
if (child instanceof IMethod) {
// function declaration is a superclass for method declaration
// (but need to trace functions differently?)
String name = ((IMethod) child).getElementName();
System.err.println("Name: " + name);
// IASTNode node = selector.findEnclosingNode(range.getStartPos(), range.getLength());
// if (node instanceof IASTFunctionDefinition) {
// IASTFunctionDefinition definition = (IASTFunctionDefinition) node;
// IASTFunctionDeclarator declarator = definition.getDeclarator();
// String unfilteredBody = getBody(icu, definition);
// // get additional information about method synchronization from generator
// SyncInformation syncInfo = m_codegen.getSyncInformation(name, unfilteredBody);
// String body = Utils.removeGenerated(unfilteredBody);
// if (syncInfo == null || !syncInfo.isGenerated) {
// // only update method, if it is not generated
// NamedElement ne = updateMethod(position, parent, name, body, declarator, syncInfo);
// updateComment(icu, definition, ne);
// }
// // System.err.println("body source <" + body + ">");
// }
position++;
}
}
}
/**
* update the contents of the CppInclude directive
*
* @param icu
* the translation unit
* @throws JavaModelException
*/
public void updateJavaImport(ICompilationUnit icu) throws JavaModelException {
String body = StringConstants.EMPTY;
for (IImportDeclaration import_ : icu.getImports()) {
body += import_.getSource();
}
if (body.length() > 0) {
Import importSt = StereotypeUtil.applyApp(m_classifier, Import.class);
if (importSt != null) {
importSt.setManualImports(body);
}
}
}
/**
* Update a method in the model based on the qualified name.
*
* @param position
* The position of the method within the file. Used to identify
* renaming operations
* @param parent
* the CDT parent which is used to get a list of children
* @param qualifiedName
* the qualified name of a method
* @param body
* the method body
* @param declarator
* the declarator for the method
* @return the operation or the behavior within the model that got updated. The latter is returned in
* case of behaviors that do not have a specification (e.g. the effect of a transition).
*/
public NamedElement updateMethod(int position, IMethod method, String qualifiedName, String body,
SyncInformation syncInfo) {
String names[] = qualifiedName.split(Utils.nsSep);
String name = names[names.length - 1];
Operation operation = null;
Behavior behavior = null;
return null;
}
/**
* Get a modifier that is next to a parameter variable
*
* @param token
* the token associated with a parameter
* @param modifiers
* a ParameterModifiers object (that gets modified)
*/
protected void updateModifierFromParameter(IToken token, ParameterModifiers modifiers) {
// TODO
}
/**
* Update the multiplicity from an existing parameter
*
* @param parameter
* a new parameter (may be null)
* @param existingParameter
* an existing parameter (may be null)
*/
protected void updateMultiplicity(Parameter parameter, Parameter existingParameter) {
if (parameter != null && existingParameter != null) {
parameter.setLower(existingParameter.getLower());
parameter.setUpper(existingParameter.getUpper());
}
}
/**
* Get a modifier that is next to a parameter type
*
* @param token
* the token associated with a parameter type
* @param modifiers
* a ParameterModifiers object (that gets modified)
* @return an eventually modified parameter type name
*/
protected String updateModifierFromParameterType(IToken token, String parameterTypeName, ParameterModifiers modifiers) {
// TODO
return null;
}
/**
* Obtain the type of a parameter, checking (and loading) ANSI-C as well.
*
* @param parameterName
* @param typeName
* the name of a type (might be qualified, might be a simple type name)
* @param existingParameters
* @return
*/
protected Type getParameterType(String typeName, Parameter existingParameter) {
return null;
}
/**
* @param paramList
* a list of parameters
* @param parameterName
* a parameter name
* @return the first parameter from the list with matching name. If the parameter
* name is null, return the first return parameter. returns null, if no
* matching name is found
*/
protected Parameter getParameterViaName(List<Parameter> paramList, String parameterName) {
for (Parameter existingParameter : paramList) {
if (parameterName == null) {
if (existingParameter.getDirection() == ParameterDirectionKind.RETURN_LITERAL) {
return existingParameter;
}
} else if (existingParameter.getName().equals(parameterName)) {
return existingParameter;
}
}
return null;
}
/**
* Apply the modifiers for a parameter, notably the stereotypes of the C++ profile
*
* @param parameterType
* the CDT AST parameter specification
* @param umlParameter
* the UML parameter (to which a stereotype should be applied)
* @param modifiers
* the modifiers that should be applied (stored in an instance of class ParameterModifiers)
*/
public void applyParameterModifiers(ILocalVariable parameter, Parameter umlParameter, ParameterModifiers modifiers) {
if (parameter.isReadOnly()) {
StereotypeUtil.apply(umlParameter, Final.class);
}
// only create array stereotype, if multiplicity == 1 (otherwise likely already handled by multiplicity)
if (umlParameter.getUpper() == 1 && false /* parameter.getArray.length() > 0 */) {
Array arraySt = StereotypeUtil.applyApp(umlParameter, Array.class);
if (arraySt != null && !modifiers.array.equals("[]") && (!modifiers.array.equals("[ ]"))) { //$NON-NLS-1$//$NON-NLS-2$
arraySt.setDefinition(modifiers.array);
}
}
}
/**
* Obtain an operation from the model by using the name of a CDT method.
* If an operation of the given name does not exist, it might indicate that
* the method has been renamed.
*
* @param name
* the operation name within CDT
* @param parent
* the parent of the CDT method within CDT editor model
* @param position
* the position within the other methods. This information is used to locate methods
* within the model that might have been renamed in the CDT editor.
* @return a UML operation
*/
public Operation getModelOperationFromName(String name, IParent parent, int position) {
Operation operation = m_classifier.getOperation(name, null, null);
if (operation == null) {
// operation is not found via name in the model. try to locate the operation in the model at the same
// "position" as the method in the file and
// verify that this method does not have the same name as any method
// in the CDT file.
if (position < m_classifier.getOperations().size()) {
operation = m_classifier.getOperations().get(position);
String modelName = operation.getName();
try {
for (IJavaElement child : parent.getChildren()) {
if (child instanceof IMethod) {
String cdtName = ((IMethod) child).getElementName();
if (cdtName.equals(modelName)) {
// an existing operation in the CDT file already
// has this name
operation = null;
break;
}
}
}
} catch (JavaModelException e) {
}
}
}
return operation;
}
public static String getBody(ICompilationUnit icu, IMethod definition) throws JavaModelException {
String contents = definition.getSource();
// if (body instanceof IASTCompoundStatement) {
// IASTCompoundStatement bodyComp = (IASTCompoundStatement) body;
// IASTFileLocation bodyLoc = bodyComp.getFileLocation();
// int start = bodyLoc.getNodeOffset();
// int end = start + bodyLoc.getNodeLength();
// char contents[] = icu.getContents();
// body contains enclosing { } which we need to remove (+2, -2). We
// cannot use the
// first and last statement, since leading and trailing comments are
// not part of the AST tree.
// return Utils.decreaseIndent(contents, start + 2, end - 2);
// }
// return ""; //$NON-NLS-1$
return contents;
}
/**
* update a comment of a named element. Besides the comment of the element itself, comments on contained
* parameters are handled.
*
* @param icu
* a translation unit
* @param definition
* @param ne
* a named element that is either an operation or a behavior (in order to update parameters)
* @throws JavaModelException
*/
public void updateComment(ICompilationUnit icu, IMethod method, NamedElement ne) throws JavaModelException {
String comment = method.getSource();
comment = comment.replace("\n * ", StringConstants.EOL).//$NON-NLS-1$
replace(StringConstants.COMMENT_END, StringConstants.EMPTY).trim();
if (comment.length() > 0) {
// filter @param
int atParam = comment.indexOf(sAtParam);
int atReturn = comment.indexOf(sAtReturn);
// does atReturn appear before @atParam?
int atParamOrReturn = (atReturn != -1 && (atReturn < atParam || atParam == -1)) ? atReturn : atParam;
String commentMethodOnly = (atParamOrReturn != -1) ? comment.substring(0, atParamOrReturn).trim() : comment;
while (atParam != -1) {
int currentAtParam = atParam;
atParam = comment.indexOf(sAtParam, atParam + 1);
String commentParam = (atParam != -1) ? comment.substring(currentAtParam, atParam)
: comment.substring(currentAtParam);
Comment commentParamUML;
int atParamName = sAtParam.length();
while ((atParamName < commentParam.length())
&& Character.isWhitespace(commentParam.charAt(atParamName))) {
atParamName++;
}
int atParamNameEnd = atParamName;
while ((atParamNameEnd < commentParam.length())
&& !Character.isWhitespace(commentParam.charAt(atParamNameEnd))) {
atParamNameEnd++;
}
if (atParamNameEnd < commentParam.length() - 1) {
String parameterName = commentParam.substring(atParamName, atParamNameEnd);
String commentParamText = commentParam.substring(atParamNameEnd).trim();
Parameter parameter = null;
if (ne instanceof BehavioralFeature) {
parameter = ((BehavioralFeature) ne).getOwnedParameter(parameterName, null, false, false);
} else if (ne instanceof Behavior) {
parameter = ((Behavior) ne).getOwnedParameter(parameterName, null, false, false);
}
if (parameter != null) {
EList<Comment> commentsParamUML = parameter.getOwnedComments();
if (commentsParamUML.size() == 0) {
commentParamUML = parameter.createOwnedComment();
commentParamUML.getAnnotatedElements().add(commentParamUML);
} else {
commentParamUML = commentsParamUML.get(0);
}
commentParamUML.setBody(commentParamText);
} else {
// parameter is not found in model, e.g. either renamed
// or not yet existing
// store comment in operation comment
commentMethodOnly += "\n " + sAtParam + parameterName + //$NON-NLS-1$
" not found(!) " + commentParamText; //$NON-NLS-1$
}
}
}
if (commentMethodOnly.equals(StringConstants.STAR)) {
commentMethodOnly = StringConstants.EMPTY;
}
// update/create comment, if a non-empty comment context has been detected.
EList<Comment> commentsUML = ne.getOwnedComments();
if (commentMethodOnly.length() > 0) {
Comment commentUML;
if (commentsUML.size() == 0) {
commentUML = ne.createOwnedComment();
commentUML.getAnnotatedElements().add(commentUML);
} else {
commentUML = commentsUML.get(0);
}
commentUML.setBody(commentMethodOnly);
} else if (commentsUML.size() > 0) {
// destroy first comment
commentsUML.get(0).destroy();
}
}
}
/**
* Accessor
*
* @return value of codegen attribute
*/
public ILangCodegen getCodeGen() {
return m_codegen;
}
/**
* input of the CDT editor. Used to obtain code within editor.
*/
protected IEditorInput m_input;
/**
* The classifier (class) that is currently edited
*/
protected Classifier m_classifier;
/**
* name of CDT project in which the generated code is stored.
*/
protected String m_projectName;
/**
* reference to code generator
*/
protected ILangCodegen m_codegen;
}