| /******************************************************************************* |
| * 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; |
| } |