com.crispico.flower.mp.metamodel.codesyncjava.algorithm.JavaSyncUtils.java Source code

Java tutorial

Introduction

Here is the source code for com.crispico.flower.mp.metamodel.codesyncjava.algorithm.JavaSyncUtils.java

Source

/* license-start
 * 
 * Copyright (C) 2008 - 2013 Crispico, <http://www.crispico.com/>.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation version 3.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details, at <http://www.gnu.org/licenses/>.
 * 
 * Contributors:
 *   Crispico - Initial API and implementation
 *
 * license-end
 */
package com.crispico.flower.mp.metamodel.codesyncjava.algorithm;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.eclipse.core.internal.resources.ResourceException;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.emf.common.util.BasicDiagnostic;
import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.common.util.DiagnosticChain;
import org.eclipse.emf.common.util.EMap;
import org.eclipse.emf.ecore.EAnnotation;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.Block;
import org.eclipse.jdt.core.dom.BodyDeclaration;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.IExtendedModifier;
import org.eclipse.jdt.core.dom.Javadoc;
import org.eclipse.jdt.core.dom.MarkerAnnotation;
import org.eclipse.jdt.core.dom.MemberValuePair;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.Modifier;
import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword;
import org.eclipse.jdt.core.dom.NormalAnnotation;
import org.eclipse.jdt.core.dom.PrimitiveType;
import org.eclipse.jdt.core.dom.ReturnStatement;
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.Statement;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
import org.eclipse.jface.text.Document;
import org.eclipse.text.edits.TextEdit;
import org.eclipse.uml2.uml.Comment;
import org.eclipse.uml2.uml.Element;
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.TypedElement;
import org.eclipse.uml2.uml.VisibilityKind;

import com.crispico.flower.mp.FlowerMPUtils;
import com.crispico.flower.mp.GeneralCreateNewElement;
import com.crispico.flower.mp.metamodel.classmetamodel.ClassMetamodel;
import com.crispico.flower.mp.metamodel.classmetamodel.ClassMetamodelUtils;
import com.crispico.flower.mp.metamodel.codesync.CodeSyncMetamodel;
import com.crispico.flower.mp.metamodel.codesync.CodeSyncUtils;
import com.crispico.flower.mp.metamodel.codesync.algorithm.CodeSyncException;
import com.crispico.flower.mp.metamodel.codesync.algorithm.SyncUtils;
import com.crispico.flower.mp.metamodel.codesync.algorithm.reverse.base.ReversePackage_Type;
import com.crispico.flower.mp.metamodel.codesync.jet.JetTemplateFactory;
import com.crispico.flower.mp.metamodel.codesync.model.SrcDirPackage;
import com.crispico.flower.mp.metamodel.codesync.model.SyncElement;
import com.crispico.flower.mp.metamodel.codesyncjava.algorithm.reverse.ReverseJavaDeclaration;
import com.crispico.flower.mp.metamodel.codesyncjava.algorithm.reverse.ReverseJavaType;
import com.crispico.flower.mp.metamodel.codesyncjava.model.CodeSyncJavaFactory;
import com.crispico.flower.mp.metamodel.codesyncjava.model.CodeSyncJavaPackage;
import com.crispico.flower.mp.metamodel.codesyncjava.model.JavaAnnotation;
import com.crispico.flower.mp.metamodel.codesyncjava.model.JavaOperation;
import com.crispico.flower.mp.validation.FlowerDiagnostic;

/**
 * @author sorin
 * @flowerModelElementId _zVs8IJiOEd6aNMdNFvR5WQ
 */
public class JavaSyncUtils {

    /**
     * @author Luiza
     * @flowerModelElementId _zVs8JJiOEd6aNMdNFvR5WQ
     */
    public static final String MODIFIER_ABSTRACT = "abstract";
    /**
     * @flowerModelElementId _zVs8KZiOEd6aNMdNFvR5WQ
     */
    public static final String MODIFIER_STATIC = "static";

    public static final String MODIFIER_FINAL = "final";

    /**
     * Pattern used to identify line end characters in a java comment
     * @flowerModelElementId _dOK_YL8REd6XgrpwHbbsYQ
     */
    protected static final Pattern COMMENT_LINE_PATTERN = Pattern.compile("(\r{0,1}\n[\t]*( [*] {0,1}))");
    /**
     * Pattern used to identify java doc markers
     * @flowerModelElementId _dOK_ZL8REd6XgrpwHbbsYQ
     */
    protected static final Pattern COMMENT_MARKERS_PATTERN = Pattern.compile("^(/[*][*] {0,1})|( {0,1}[*]{0,1}/)$");

    /**
     * Loads the java file and sets the retrieved comments for every method/attribute found inside it
     * @throws JavaModelException 
     * @flowerModelElementId _zVs8LZiOEd6aNMdNFvR5WQ
     */
    @SuppressWarnings("unchecked")
    public static CompilationUnit loadJavaFile(IFile file, ASTParser parser) throws Exception {
        CompilationUnit result = null;
        char[] fileContent;
        try {
            fileContent = getFileContent(file).toCharArray();
        } catch (ResourceException e) {
            file.refreshLocal(IResource.DEPTH_INFINITE, null);
            fileContent = getFileContent(file).toCharArray();
        }

        HashMap defaultOptions = new HashMap(JavaCore.getOptions());
        String compilerSourceLevel = (String) defaultOptions.get(JavaCore.COMPILER_SOURCE);

        // Source level 1.5 is necessary to enable annotations
        if (Double.valueOf(compilerSourceLevel) < 1.5) {
            defaultOptions.put(JavaCore.COMPILER_SOURCE, "1.5");
            parser.setCompilerOptions(defaultOptions);
        }

        parser.setSource(fileContent);
        result = (CompilationUnit) parser.createAST(null);

        for (Object o : result.getCommentList()) {
            if (o instanceof Javadoc) {
                Javadoc doc = (Javadoc) o;
                String docComment = new String(fileContent, doc.getStartPosition(), doc.getLength());

                //use Pattern and Matcher instead of string.replaceAll(...) to compile the pattern only once
                Matcher matcher = COMMENT_LINE_PATTERN.matcher(docComment);
                docComment = matcher.replaceAll("\r\n");
                matcher = COMMENT_MARKERS_PATTERN.matcher(docComment);
                docComment = matcher.replaceAll("");

                doc.setProperty("comment", docComment);
            }
        }
        return result;
    }

    /**
     * 
     * @param file the <code>{@link IFile}</code>
     * @return the content of this file
     * 
     * @author Luiza.
     * The reading technique was modified because we were loosing the characters marking end of row
     * @flowerModelElementId _zVs8OJiOEd6aNMdNFvR5WQ
     */
    private static String getFileContent(IFile file) throws Exception {
        BufferedReader br = null;
        try {
            br = new BufferedReader(new InputStreamReader(file.getContents()));
            StringBuffer buffer = new StringBuffer();
            char[] charBuffer = new char[100];
            int charsRed = 0;
            while (true) {
                charsRed = br.read(charBuffer);
                if (charsRed == -1) {
                    break;
                }
                if (charsRed < charBuffer.length) {
                    for (int i = 0; i < charsRed; i++) {
                        buffer.append(charBuffer[i]);
                    }
                    break;
                }
                buffer.append(charBuffer);
            }
            return buffer.toString();
        } finally {
            if (br != null)
                br.close();
        }
    }

    /**
     * @flowerModelElementId _zVs8PpiOEd6aNMdNFvR5WQ
     */
    public static void writeJavafile(CompilationUnit cUnit, IFile file) {
        // TODO ::plus numele packetului?
        String sourceCode = null;
        try {
            Document doc = null;
            if (file.exists())
                doc = new Document(getFileContent(file));
            else
                doc = new Document();

            TextEdit edits = cUnit.rewrite(doc, null);
            edits.apply(doc);
            sourceCode = doc.get();

            if (sourceCode.getBytes().length == 0)
                throw new IllegalArgumentException("java sourcecode.getBytes length = 0 ");
            ByteArrayInputStream bais = new ByteArrayInputStream(sourceCode.getBytes());
            if (!file.exists())
                file.create(bais, true, null);
            else // keep the 3rd flag true! keeps history
                file.setContents(bais, true, true, null);
            bais.close();
        } catch (Exception e) {
            throw new RuntimeException("Could not write Java file: " + file.getFullPath(), e);
        }
    }

    /**
     * For a <code>CompilationUnit</code> retrieves the name of the class with modifier <code>public</code>
     * @flowerModelElementId _zVs8RJiOEd6aNMdNFvR5WQ
     */
    public static String getNameOfMasterClass(CompilationUnit node) {
        TypeDeclaration td = getMasterClass(node);
        if (td != null)
            return td.getName().toString();
        else
            return null;
    }

    /**
     * If no public type is found in a compilation unit it is returned the first one
     * 
     * @param node {@link CompilationUnit}
     * @return <code>TypeDeclaration</code> object which defines the main class in a compilation unit 
     * (this is the public class or interface which gives the name of the file).
     * 
     * @flowerModelElementId _zVs8SpiOEd6aNMdNFvR5WQ
     */
    @SuppressWarnings("unchecked")
    public static TypeDeclaration getMasterClass(CompilationUnit node) {
        Iterator<TypeDeclaration> it = node.types().iterator();
        for (; it.hasNext();) {
            TypeDeclaration td = it.next();
            for (Iterator<Modifier> it2 = td.modifiers().iterator(); it2.hasNext();) {
                IExtendedModifier iExtendedModifier = it2.next();
                if (iExtendedModifier.isAnnotation())
                    continue;
                if (((Modifier) iExtendedModifier).isPublic())
                    return td;
            }
        }
        if (node.types().size() > 0)
            return (TypeDeclaration) node.types().get(0);
        throw new IllegalStateException(
                "A master class for " + node.getPackage() + ".[" + node.types() + "] could not been found");
    }

    /**
     * Returns the list of {@link IExtendedModifier modifiers} of the given
     * <code>astElement</code>. This shall contain annotations, visibility
     * markers (public, protected, private), static, final.
     * 
     * @param astElement
     *            the types accepted for this parameter are :
     *            {@link CompilationUnit}, {@link TypeDeclaration}, or
     *            {@link BodyDeclaration}
     * 
     * @author Luiza
     */
    @SuppressWarnings("unchecked")
    private static List<IExtendedModifier> getModifiers(Object astElement) {
        if (astElement instanceof CompilationUnit) {
            return getMasterClass((CompilationUnit) astElement).modifiers();
        } else if (astElement instanceof TypeDeclaration) {
            return ((TypeDeclaration) astElement).modifiers();
        } else if (astElement instanceof BodyDeclaration) {
            return ((BodyDeclaration) astElement).modifiers();
        } else
            throw new IllegalArgumentException("Can't get modifiers for " + astElement);
    }

    /**
     * @flowerModelElementId _zVs8UZiOEd6aNMdNFvR5WQ
     */
    public static VisibilityKind convertJavaToModelVisibility(Object node) {
        List<IExtendedModifier> modifiers = getModifiers(node);

        for (IExtendedModifier iExtendedModifier : modifiers) {
            if (iExtendedModifier.isAnnotation())
                continue;
            String modif = ((Modifier) iExtendedModifier).getKeyword().toString();
            if ("public".equals(modif)) {
                return VisibilityKind.PUBLIC_LITERAL;
            } else if ("private".equals(modif)) {
                return VisibilityKind.PRIVATE_LITERAL;
            } else if ("protected".equals(modif)) {
                return VisibilityKind.PROTECTED_LITERAL;
            }
        }
        return VisibilityKind.PACKAGE_LITERAL;
    }

    /**
     * Gets the feature value from Java Element for Model Element.
     * 
     * @param node astElement for class or method declaration
     * @param modifier flag indicating the modifier for which to get the feature value
     * This should be one of the predefined flags:
     * <ul>
     *   <li>{@link #MODIFIER_ABSTRACT}</li>
     *   <li>{@link #MODIFIER_STATIC}</li>
     * </ul>
     * @return value for Model feature 
     * 
     * @author Luiza
     * @flowerModelElementId _zVs8VpiOEd6aNMdNFvR5WQ
     */
    public static boolean getFeatureValueFromJavaModifier(Object node, String modifier) {
        List<IExtendedModifier> modifiers = getModifiers(node);

        for (IExtendedModifier iExtendedModifier : modifiers) {
            if (iExtendedModifier.isAnnotation())
                continue;
            String modif = ((Modifier) iExtendedModifier).getKeyword().toString();
            if (modifier.equals(modif))
                return true;
        }
        return false;
    }

    /**
     * Parse the given {@link String value} into a java expression.
     * 
     * @author Luiza
     */
    private static Expression makeExpression(AST ast, String value) {
        ASTParser expressionParser = ASTParser.newParser(AST.JLS3);
        expressionParser.setKind(ASTParser.K_EXPRESSION);
        expressionParser.setSource(value.toCharArray());
        Expression expression = (Expression) expressionParser.createAST(null);
        return (Expression) ASTNode.copySubtree(ast, expression);
    }

    /**
     * Retrieves the annotations from the <code>astElement</code>'s modifiers
     * list.
     * 
     * @param astElement
     * @return a list of {@link JavaAnnotationHolder} wrapping the
     *         {@link Annotation annotations}
     *         
     * @author Luiza
     */
    public static List<JavaAnnotationHolder> getAnnotations(Object astElement) {
        List<JavaAnnotationHolder> annotations = new ArrayList<JavaAnnotationHolder>();
        List<IExtendedModifier> modifiers = getModifiers(astElement);

        for (Iterator<IExtendedModifier> it = modifiers.iterator(); it.hasNext();) {
            IExtendedModifier iExtendedModifier = it.next();
            if (iExtendedModifier.isAnnotation()) {
                annotations.add(new JavaAnnotationHolder((Annotation) iExtendedModifier));
            }
        }
        return annotations;
    }

    /**
     * Adds <code>newAnnotation</code> to the list of modifiers of
     * <code>toAstElement</code>. 
     * The new Annotation will be placed on the first position in the modifiers list.
     * 
     * @param toAstElement
     *            the {@link ASTNode parent ast element} to receive the
     *            {@link Annotation}
     * @param newAnnotation
     *            the new created {@link Annotation}. Note that it must be
     *            unparented. Otherwise an exception will be generated.
     *            
     * @author Luiza
     */
    public static void addAnnotation(Object toAstElement, Annotation newAnnotation) {
        List<IExtendedModifier> modifiers = getModifiers(toAstElement);
        int lastAnnotationIndex = -1, index = 0;

        // find the last annotation position
        for (IExtendedModifier modifier : modifiers) {
            if (modifier.isAnnotation())
                lastAnnotationIndex = index;
            index++;
        }
        index = lastAnnotationIndex + 1;
        if (index >= modifiers.size())
            modifiers.add(newAnnotation);
        else
            modifiers.add(index, newAnnotation);
    }

    /**
     * Creates a new appropriate {@link Annotation java Annotation} from the
     * given {@link EAnnotation modelEAnnotation}. The returned
     * {@link Annotation} can be a {@link MarkerAnnotation}, a
     * {@link SingleMemberAnnotation} or a {@link NormalAnnotation} according to
     * the <code>modelEAnnotation</code> features.
     * 
     * @param ast
     *            the parent {@link AST} used to create the new
     *            {@link Annotation}
     * @param modelEAnnotation
     *            the model element {@link EAnnotation}
     * @return a new unparented {@link Annotation}
     * 
     * @author Luiza
     */
    @SuppressWarnings("unchecked")
    public static Annotation createNewJavaAnnotationFromModelEAnnotation(AST ast, EAnnotation modelEAnnotation) {
        EMap<String, String> detailsMap = modelEAnnotation.getDetails();
        Annotation newAnnotation = null;

        if (detailsMap.size() == 0) {
            newAnnotation = ast.newMarkerAnnotation();
        } else if (detailsMap.size() == 1 && detailsMap.get("") != null) {
            String value = detailsMap.get("");
            newAnnotation = ast.newSingleMemberAnnotation();
            ((SingleMemberAnnotation) newAnnotation).setValue(makeExpression(ast, value));
        } else {
            newAnnotation = ast.newNormalAnnotation();
            for (Entry<String, String> mapEntry : detailsMap) {
                MemberValuePair mvp = ast.newMemberValuePair();
                mvp.setName(ast.newSimpleName(mapEntry.getKey()));
                mvp.setValue(makeExpression(ast, mapEntry.getValue()));
                ((NormalAnnotation) newAnnotation).values().add(mvp);
            }
        }
        newAnnotation.setTypeName(ast.newName(modelEAnnotation.getSource()));
        return newAnnotation;
    }

    /**
     * Removes the given {@link Annotation annotation} from
     * <code>astElement</code>
     * 
     * @author Luiza
     */
    public static void removeAnnotation(Object astElement, Annotation annotation) {
        List<IExtendedModifier> modifiers = getModifiers(astElement);
        modifiers.remove(annotation);
    }

    /**
     * Moves <code>toMove</code> after <code>afterAnnotation</code> in the
     * modifiers list of the given <code>astElement</code>.
     * <p> 
     * Supposes that both {@link Annotation annotations} are contained in the
     * <code>astElement</code>'s list of modifiers.
     * 
     * @author Luiza
     */
    public static void moveAfterAnnotation(Object astElement, Annotation toMove, Annotation afterAnnotation) {
        List<IExtendedModifier> modifiers = getModifiers(astElement);
        modifiers.remove(toMove);
        int toIndex = 0;

        if (afterAnnotation == null) { // if toMove should be the first
            for (IExtendedModifier modifier : modifiers)
                if (modifier.isAnnotation())
                    break; // find the first element of its kind
                else
                    toIndex++;
        } else {
            toIndex = modifiers.indexOf(afterAnnotation) + 1;
        }
        modifiers.add(toIndex, (IExtendedModifier) toMove);
    }

    /**
     * Replace <code>oldAnnotation</code> with <code>newAnnotation</code> in
     * the <code>parentAstElement</code>'s modifiers list.
     * <p>
     * Supposes the modifiers list contains <code>oldAnnotation</code>.
     * 
     * @param parentAstElement
     *            the {@link CompilationUnit}, {@link TypeDeclaration},
     *            {@link BodyDeclaration} containing the annotations
     * @param oldAnnotation
     *            the {@link Annotation} to replace.
     * @param newAnnotation
     *            the new {@link Annotation} to add on the
     *            <code>oldAnnotation</code>'s position.
     * 
     * @author Luiza
     */
    public static void replaceAnnotation(Object parentAstElement, Annotation oldAnnotation,
            Annotation newAnnotation) {
        List<IExtendedModifier> modifiers = getModifiers(parentAstElement);

        int index = modifiers.indexOf(oldAnnotation);

        // index still in range
        // oldAnnotation was not the last modifier
        if (index >= 0 && index < modifiers.size()) {
            modifiers.add(index, newAnnotation);
        } else
            modifiers.add(newAnnotation);

        modifiers.remove(index + 1);
    }

    /**
     * @flowerModelElementId _zVs8XZiOEd6aNMdNFvR5WQ
     */
    @SuppressWarnings("unchecked")
    public static void updateVisibilityFromModelToJavaClass(BodyDeclaration bd, VisibilityKind visibilityKind) {// NamedElement
        // modelElement) {
        ModifierKeyword javaVisibFlag = getJavaVisibilityFlagFromJavaClass(bd);
        ModifierKeyword modelVisibFlag = convertModelToJavaVisibilityFlag(visibilityKind);

        if (modelVisibFlag != javaVisibFlag) {
            if (javaVisibFlag != null) { // /meaning the package visibility
                // Which to remove?
                Modifier toRemove = null;
                for (Iterator<Modifier> it = bd.modifiers().iterator(); it.hasNext();) {
                    Modifier modif = it.next();
                    if (modif.isPrivate() || modif.isProtected() || modif.isPublic()) {
                        toRemove = modif;
                        break;
                    }
                }
                bd.modifiers().remove(toRemove);
            }
            // Adding
            if (modelVisibFlag != null) {
                Modifier newModif = bd.getAST().newModifier(modelVisibFlag);
                bd.modifiers().add(newModif);
            }
        }
    }

    /**
     * @flowerModelElementId _zVs8ZJiOEd6aNMdNFvR5WQ
     */
    public static ModifierKeyword getJavaVisibilityFlagFromJavaClass(BodyDeclaration bd) {
        if (Modifier.isPrivate(bd.getModifiers()))
            return ModifierKeyword.PRIVATE_KEYWORD;
        else if (Modifier.isProtected(bd.getModifiers()))
            return ModifierKeyword.PROTECTED_KEYWORD;
        else if (Modifier.isPublic(bd.getModifiers()))
            return ModifierKeyword.PUBLIC_KEYWORD;
        return null;
    }

    /**
     * @flowerModelElementId _zVs8a5iOEd6aNMdNFvR5WQ
     */
    public static ModifierKeyword convertModelToJavaVisibilityFlag(VisibilityKind src) {
        if (VisibilityKind.PRIVATE_LITERAL.equals(src))
            return ModifierKeyword.PRIVATE_KEYWORD;
        else if (VisibilityKind.PROTECTED_LITERAL.equals(src))
            return ModifierKeyword.PROTECTED_KEYWORD;
        else if (VisibilityKind.PUBLIC_LITERAL.equals(src))
            return ModifierKeyword.PUBLIC_KEYWORD;
        else
            return null;
    }

    /**
     * Updates java code according to the model value for abstract or static modifiers
     * 
     * @author Luiza
     * @param bd <code>{@link BodyDeclaration}</code> for a class, method or field.
     * @param isTrue value for the modifier
     * @param modifier flag indicating the modifier to update.
     * This should be one of the predefined flags:  
     * <ul>
     *    <li>{@link #MODIFIER_ABSTRACT}</li>
     *    <li>{@link #MODIFIER_STATIC}</li>
     * </ul>
     * @flowerModelElementId _zVs8cJiOEd6aNMdNFvR5WQ
     */
    @SuppressWarnings("unchecked")
    public static void updateModifierFromModelToJavaClass(BodyDeclaration bd, boolean isTrue, String modifier) {

        ModifierKeyword javaModifierFlag = null;
        ModifierKeyword modelModifierFlag = null;
        boolean isForAbstractModif = modifier.equals(MODIFIER_ABSTRACT);
        boolean isForStaticModif = modifier.equals(MODIFIER_STATIC);
        boolean isForFinalModif = modifier.equals(MODIFIER_FINAL);

        if (isForAbstractModif) {
            javaModifierFlag = Modifier.isAbstract(bd.getModifiers()) ? ModifierKeyword.ABSTRACT_KEYWORD : null;
            modelModifierFlag = isTrue ? ModifierKeyword.ABSTRACT_KEYWORD : null;
        } else if (isForStaticModif) {
            javaModifierFlag = Modifier.isStatic(bd.getModifiers()) ? ModifierKeyword.STATIC_KEYWORD : null;
            modelModifierFlag = isTrue ? ModifierKeyword.STATIC_KEYWORD : null;
        } else if (isForFinalModif) {
            javaModifierFlag = Modifier.isFinal(bd.getModifiers()) ? ModifierKeyword.FINAL_KEYWORD : null;
            modelModifierFlag = isTrue ? ModifierKeyword.FINAL_KEYWORD : null;
        } else
            throw new IllegalArgumentException(
                    "updateModifierFromModelToJavaClass - can't update java code for modifier " + modifier);

        //if there are differences between the model and the java file
        if (modelModifierFlag != javaModifierFlag) {
            //if this modifier has been set before find it and remove it
            if (javaModifierFlag != null) {
                //Modifier toRemove = null;
                /* 
                 * we use bd.modifiers() instead of bd.getModifiers(), which computes a bit-wise integer consisting in all the modifier values 
                 * because it also iterates over the modifiers and method bd.setModifiers(modifiers) is deprecated
                 */
                for (Iterator<Modifier> it = bd.modifiers().iterator(); it.hasNext();) {
                    Modifier modif = it.next();
                    if (isForAbstractModif && modif.isAbstract()) {
                        it.remove();
                        break;
                    } else if (isForStaticModif && modif.isStatic()) {
                        it.remove();
                        break;
                    } else if (isForFinalModif && modif.isFinal()) {
                        it.remove();
                        break;
                    }
                }
                //bd.modifiers().remove(toRemove);
            }

            // add the new modifier value if changes have been made in the model
            if (modelModifierFlag != null) {
                Modifier newModif = bd.getAST().newModifier(modelModifierFlag);
                bd.modifiers().add(newModif);
            }
        }

    }

    /**
     * @flowerModelElementId _zVs8eJiOEd6aNMdNFvR5WQ
     */
    public static Object getJavaLookupKeyForField(FieldDeclaration fd) {
        return getSimpleNameString(fd);
    }

    /**
     * @author Mariana - added support for array dimensions (e.g. int[][]) and variable arguments (e.g. int...)
     * 
     * @throws CodeSyncException 
     * @flowerModelElementId _zVs8fpiOEd6aNMdNFvR5WQ
     */
    @SuppressWarnings("unchecked")
    public static Object getJavaLookupKeyForMethod(MethodDeclaration md, ReversePackage_Type reversePackage_Type)
            throws CodeSyncException {
        String result = getSimpleNameString(md) + "(";
        List<SingleVariableDeclaration> params = md.parameters();
        for (Iterator<SingleVariableDeclaration> it = params.iterator(); it.hasNext();) {
            SingleVariableDeclaration p = it.next();
            result += SyncUtils.getFullyQualifiedNameForAstType(p.getType().toString(), reversePackage_Type);
            // support for extra dimensions
            // e.g. int x[][] will return int[][]
            for (int i = 0; i < p.getExtraDimensions(); i++) {
                result += "[]";
            }
            // support for variable arguments
            if (p.isVarargs()) {
                result += "...";
            }
            result += ",";
        }
        result += ")";
        return result;
    }

    public static Object getModelLookupKeyForOperation(Operation o) throws CodeSyncException {
        String result = o.getName() + "(";
        for (Parameter p : o.getOwnedParameters())
            if (p.getDirection().equals(ParameterDirectionKind.IN_LITERAL)) {
                org.eclipse.uml2.uml.Type t = SyncUtils.getActualTypeValue(p, p.getType());
                o.getNamespace().allNamespaces();

                String paramType = t == null ? null : SyncUtils.getFullyQualifiedTypeNameFromType(t);
                result += paramType + ",";
            }
        result += ")";
        return result;
    }

    public static Object getLookupKeyForJavaAnnotation(Annotation annotation) {
        StringBuilder key = new StringBuilder(annotation.getTypeName().toString() + '(');

        if (annotation.isSingleMemberAnnotation()) {
            key.append(((SingleMemberAnnotation) annotation).getValue().toString() + ",");
        } else if (annotation.isNormalAnnotation()) {
            for (Object o : ((NormalAnnotation) annotation).values()) {
                MemberValuePair mvp = (MemberValuePair) o;
                key.append(mvp.getName().toString() + "=" + mvp.getValue().toString() + ",");
            }
        }
        key.append(')');
        return key.toString();
    }

    /**
     * Creates a new appropriate {@link Type} from the given <code>value</code>.
     * <p>
     * Note that if <code>value</code> is <code>null</code> returns
     * {@link PrimitiveType#VOID}.
     * 
     * @param ast
     *            {@link AST} of the {@link ASTNode ASTElement} needing a new
     *            Type
     * @param value
     *            the name of the {@link Type} to be created.
     * @param canBePrimitiveType
     *            if <code>true</code> try to create a primitive from the
     *            given <code>value</code> if possible, otherwise create a new
     *            Type without checking primitives
     * @author Luiza
     * 
     * @flowerModelElementId _zVs8hZiOEd6aNMdNFvR5WQ
     */
    public static Type getJavaTypeFromString(AST ast, String value, boolean canBePrimitiveType) {
        if (canBePrimitiveType) {
            PrimitiveType.Code primitiveTypeCode = null;
            if (value == null)
                primitiveTypeCode = PrimitiveType.VOID;
            else {
                primitiveTypeCode = PrimitiveType.toCode(value);
            }
            if (primitiveTypeCode != null)
                return ast.newPrimitiveType(primitiveTypeCode);
        }

        // not a primitive
        ASTParser statementParser = ASTParser.newParser(AST.JLS3);
        statementParser.setKind(ASTParser.K_STATEMENTS);
        statementParser.setSource((value + " a;").toCharArray()); // try to parse a variable declaration

        Block block = (Block) statementParser.createAST(null);
        VariableDeclarationStatement declaration = (VariableDeclarationStatement) block.statements().get(0);
        return (Type) ASTNode.copySubtree(ast, declaration.getType()); // detach the type from the parent node
    }

    /**
     * @flowerModelElementId _zVs8jZiOEd6aNMdNFvR5WQ
     */
    public static String getReturnTypeAsString(BodyDeclaration bd) {
        if (bd instanceof FieldDeclaration)
            return ((FieldDeclaration) bd).getType().toString();
        else if (bd instanceof MethodDeclaration) {
            //constructors in java have null returning type
            Type returnType = ((MethodDeclaration) bd).getReturnType2();
            return returnType == null ? null : returnType.toString();
        } else {
            throw new IllegalArgumentException("Expecting method or field declaration: " + bd);
        }
    }

    /**
     * @flowerModelElementId _zVs8kpiOEd6aNMdNFvR5WQ
     */
    public static String getSimpleNameString(BodyDeclaration bd) {
        if (bd instanceof FieldDeclaration) {
            FieldDeclaration fd = (FieldDeclaration) bd;
            VariableDeclarationFragment vdf = (VariableDeclarationFragment) fd.fragments().get(0);
            return vdf.getName().toString();
        } else if (bd instanceof MethodDeclaration) {
            MethodDeclaration md = (MethodDeclaration) bd;
            return md.getName().toString();
        } else {
            throw new IllegalArgumentException("Expecting method or field declaration: " + bd);
        }
    }

    /**
     * This is a common method for {@link ReverseJavaType} and
     * {@link ReverseJavaDeclaration} used for retrieving the comment feature.
     * 
     * @param astElement
     *            must be of type {@link CompilationUnit} or
     *            {@link BodyDeclaration}
     * @return the comment list for this <code>astElement</code>
     * 
     * @author Luiza
     */
    public static List<Comment> getComment(ASTNode astElement) {
        List<Comment> listComments = new ArrayList<Comment>();
        String documentation = "";
        Javadoc javaDoc = null;

        if (astElement instanceof CompilationUnit) { // class or interface
            javaDoc = JavaSyncUtils.getMasterClass((CompilationUnit) astElement).getJavadoc();
        } else if (astElement instanceof BodyDeclaration) { // attribute or method
            javaDoc = ((BodyDeclaration) astElement).getJavadoc();
        } else {
            return null;
        }

        if (javaDoc != null) {
            documentation = (String) javaDoc.getProperty("comment"); // the comment as it is in the file
        }
        if (documentation != null && documentation.length() > 0) {
            listComments.add(SyncUtils.createComment(documentation));
        }
        return listComments;
    }

    /**
     * The method verifies if a Java type name is identifier.
     * The following conditions must be satisfied:
     * <ul> 
     *        <li> if the name contains '<' and '>', the number of
     *          open brackets should be equal with the number of
     *          closed ones; otherwise the <code>isIdentifier()</code>
     *          method is called;</li>
     *       <li> if the name contains '<' the first token (substring from 
     *          index 0 to the index of '<') should be an identifier</li>
     * </ul>
     * @param object = object whose name has changed
     * @param diagnosticChain = <code>DiagnosticChain</code> that will be updated
     * @param string = the new name
     * @param errorName = error's title
     * @param notIdentifierError = error's message
     * 
     * @author Georgi
     * @flowerModelElementId _mX0RIARBEd-87NNJOl334A
     */
    public static FlowerDiagnostic isJavaIdentifier(EObject object, DiagnosticChain diagnosticChain,
            String string) {
        String errorMessage = null; /* will be set by caller method*/

        FlowerDiagnostic diag;

        if (string == null || string.trim().length() == 0) {
            String qualifiedName = FlowerMPUtils.getElementQualifiedName((Element) object);
            diag = ClassMetamodelUtils.createDignostic(qualifiedName, errorMessage,
                    ClassMetamodel.VAL_CODE_NAME_NULL, Diagnostic.ERROR, object);
            ((BasicDiagnostic) (diagnosticChain)).add(diag);
            return diag;
        } else {
            if (FlowerDiagnostic.collectOKDiagnostic(object)) {
                String qualifiedName = FlowerMPUtils.getElementQualifiedName((Element) object);
                diag = ClassMetamodelUtils.createDignostic(qualifiedName, errorMessage,
                        ClassMetamodel.VAL_CODE_NAME_NULL, Diagnostic.OK, object);
                ((BasicDiagnostic) (diagnosticChain)).add(diag);
                // this diagnostic will not be returned because it will not allow the rest of the validation to work, it will remain undecorated with the external message
            }
        }

        if (!string.contains("<") && !string.contains(">")) {
            diag = CodeSyncUtils.isIdentifier(object, diagnosticChain, string);
            return diag; // error message will be set by caller method
        }

        int imbricationLevel = 0;
        int pos = string.indexOf('<');
        if (pos > 0) {
            boolean isIdentifier = object instanceof SrcDirPackage
                    ? CodeSyncUtils.isSrcDirIdentifier(string.substring(0, pos))
                    : CodeSyncUtils.isIdentifier(string.substring(0, pos));
            if (!isIdentifier) {
                String qualifiedName = FlowerMPUtils.getElementQualifiedName((Element) object);
                diag = ClassMetamodelUtils.createDignostic(qualifiedName, errorMessage,
                        CodeSyncMetamodel.VAL_CODE_NAME_NOT_IDENTIFIER, Diagnostic.ERROR, object);
                ((BasicDiagnostic) (diagnosticChain)).add(diag);
                return diag;
            }
        }
        for (int i = 0; i < string.length(); i++)
            if (string.charAt(i) == '<')
                imbricationLevel++;
            else if (string.charAt(i) == '>')
                imbricationLevel--;
            else if (string.charAt(i) == ',') {
                if (string.charAt(i - 1) == '>' || string.charAt(i - 1) == '<' || string.charAt(i + 1) == '>'
                        || string.charAt(i + 1) == '<') {
                    String qualifiedName = FlowerMPUtils.getElementQualifiedName((Element) object);
                    diag = ClassMetamodelUtils.createDignostic(qualifiedName, errorMessage,
                            CodeSyncMetamodel.VAL_CODE_NAME_NOT_IDENTIFIER, Diagnostic.ERROR, object);
                    ((BasicDiagnostic) (diagnosticChain)).add(diag);
                    return diag;
                }
            }
        if (imbricationLevel != 0) {
            String qualifiedName = FlowerMPUtils.getElementQualifiedName((Element) object);
            diag = ClassMetamodelUtils.createDignostic(qualifiedName, errorMessage,
                    CodeSyncMetamodel.VAL_CODE_NAME_NOT_IDENTIFIER, Diagnostic.ERROR, object);
            ((BasicDiagnostic) (diagnosticChain)).add(diag);
            return diag;
        }
        if (FlowerDiagnostic.collectOKDiagnostic(object)) {
            String qualifiedName = FlowerMPUtils.getElementQualifiedName((Element) object);
            diag = ClassMetamodelUtils.createDignostic(qualifiedName, errorMessage,
                    CodeSyncMetamodel.VAL_CODE_NAME_NOT_IDENTIFIER, Diagnostic.OK, object);
            ((BasicDiagnostic) (diagnosticChain)).add(diag);
            return diag;
        }

        return null;
    }

    private static final HashSet<String> keywords = new HashSet<String>(Arrays.asList("abstract", "assert",
            "boolean", "break", "byte", "case", "catch", "char", "class", "const", "continue", "default", "do",
            "double", "else", "enum", "extends", "false", "final", "finally", "float", "for", "goto", "if",
            "implements", "import", "instanceof", "int", "interface", "long", "native", "new", "null", "package",
            "private", "protected", "public", "return", "short", "static", "strictfp", "super", "switch",
            "synchronized", "this", "throw", "throws", "transient", "true", "try", "void", "volatile", "while"));

    /**
     * The method checks if a Java type name is a Java keyword.
     * 
     * @param object the object whose name was changed
     * @param diagnosticChain <code>DiagnosticChain</code> to be updated
     * @param string the new name
     * @param errorName the error's title
     * @param keywordError the error's message
     * 
     * @author Mariana
     * @author Sorin
     */
    public static FlowerDiagnostic isJavaKeyword(EObject object, DiagnosticChain diagnosticChain, String string) {
        String errorMessage = null; /* will be set by caller method*/
        FlowerDiagnostic diag;

        // The old implementation with Scanner demanded to use lot of memory for initialization.
        if (keywords.contains(string)) {
            String qualifiedName = (object instanceof NamedElement)
                    ? FlowerMPUtils.getElementQualifiedName((Element) object)
                    : string;
            diag = ClassMetamodelUtils.createDignostic(qualifiedName, errorMessage, ClassMetamodel.VAL_CODE_KEYWORD,
                    Diagnostic.ERROR, object);
            ((BasicDiagnostic) (diagnosticChain)).add(diag);
            return diag;
        } else {
            if (FlowerDiagnostic.collectOKDiagnostic(object)) {
                String qualifiedName = (object instanceof NamedElement)
                        ? FlowerMPUtils.getElementQualifiedName((Element) object)
                        : string;
                diag = ClassMetamodelUtils.createDignostic(qualifiedName, errorMessage,
                        ClassMetamodel.VAL_CODE_KEYWORD, Diagnostic.OK, object);
                ((BasicDiagnostic) (diagnosticChain)).add(diag);
                return diag;
            }
        }
        return null;
    }

    /**
     * The method is called to check if the given element has the 
     * type not set. The verification is made for <code>JavaProperty</code>,
     * <code>JavaParameter</code> and <code>JavaOperation</code>.
     * 
     * If the <code>object</code> is a Java operation and it has the same name as the class, 
     * it is a constructor and the check is not done.
     * 
     * @param object
     * @param diagnosticChain
     * @param errorMessage
     * @author Georgi
     * @flowerModelElementId _mX0RKARBEd-87NNJOl334A
     */
    public static FlowerDiagnostic isTypeNull(EObject object, DiagnosticChain diagnosticChain) {
        String errorMessage = null; /* will be set by caller method*/
        org.eclipse.uml2.uml.Type type = null;
        if (object instanceof TypedElement)
            type = ((TypedElement) (object)).getType();
        else if (object instanceof Operation)
            type = ((Operation) object).getType();
        else
            return null;
        FlowerDiagnostic diag = null;
        if (CodeSyncUtils.validationAllowedOnElement((SyncElement) object) && type == null
                && !(object instanceof Operation && ((Operation) object).getName() != null && ((Operation) object)
                        .getName().equals(((org.eclipse.uml2.uml.Type) object.eContainer()).getName()))) {
            String qualifiedName = FlowerMPUtils.getElementQualifiedName((Element) object);
            diag = ClassMetamodelUtils.createDignostic(qualifiedName, errorMessage,
                    CodeSyncMetamodel.VAL_CODE_TYPE_NULL, Diagnostic.ERROR, object);
        } else {
            if (FlowerDiagnostic.collectOKDiagnostic(object)) {
                // remove error if the element is deleted, locked, or under locked parent
                String qualifiedName = FlowerMPUtils.getElementQualifiedName((Element) object);
                diag = ClassMetamodelUtils.createDignostic(qualifiedName, errorMessage,
                        CodeSyncMetamodel.VAL_CODE_TYPE_NULL, Diagnostic.OK, object);
            }
        }
        if (diag != null)
            ((BasicDiagnostic) (diagnosticChain)).add(diag);
        return diag;
    }

    /**
     * Creates a new block and adds it as body to the given {@link MethodDeclaration}.
     * 
     * @param md - the {@link MethodDeclaration} that requires a new body block.
     * @param blockString - the {@link String} that will form the block content.
     * 
     * @author Luiza
     */
    public static void generateBodyForMethod(MethodDeclaration md, String blockString) {
        AST ast = md.getAST();
        ASTParser statementParser = ASTParser.newParser(AST.JLS3);
        statementParser.setKind(ASTParser.K_STATEMENTS);
        // parse the block
        statementParser.setSource(blockString.toCharArray());
        Block block = (Block) statementParser.createAST(null);
        block = (Block) ASTNode.copySubtree(ast, block);

        md.setBody(block);
    }

    /**
     * Generates a body for method and inserts a comment at first line in the body content.
     * <p>
     * Comments aren't created as child nodes if we parse a block string that contains comment lines. <br>
     * Hack made : <br>
     * <ul>
     *    <li>a new {@link Statement} is created and added to block node. Because we can't create comments as {@link ASTNode}
     * the statement is created as a {@link ReturnStatement}.
     *    <li>the statement will contain the comment code
     *    <li>because the return statement node created must return our code (and not return;), 
     * it must be stored in NodeInfoStore class found as private field in InternalASTRewrite found as private field in the root AST class.    * 
     *    <li>when {@link JavaSyncUtils#writeJavafile(CompilationUnit, IFile)} is called at the end of forward mechanism 
     * it invokes {@link CompilationUnit#rewrite(org.eclipse.jface.text.IDocument, java.util.Map)} that will replace the 
     * return statement code with our code based on what it finds in NodeInfoStore list.
     * </ul>
     * @param md - the {@link MethodDeclaration} that requires a new body block. 
     * @param blockString - the {@link String} that will form the block content.
     * @param commentString - the {@link String} that will form the line comment.
     * 
     * @author Cristina
     */
    @SuppressWarnings("unchecked")
    public static void generateBodyWithCommentForMethod(MethodDeclaration md, String blockString,
            String commentString) {
        // generate body content without comments
        generateBodyForMethod(md, blockString);

        // insert comment on first line
        try {
            // get rewriter field from AST
            Object rewriter = FlowerMPUtils.getPrivateField(AST.class, "rewriter", md.getRoot().getAST());
            Class<?> thisClass = Class.forName("org.eclipse.jdt.core.dom.InternalASTRewrite");
            // get nodeStore field from InternalASTRewrite
            Object nodestore = FlowerMPUtils.getPrivateField(thisClass, "nodeStore", rewriter);
            Class<?> nodeInfoStore = Class.forName("org.eclipse.jdt.internal.core.dom.rewrite.NodeInfoStore");

            // nodeStore.newPlaceholderNode(ASTNode.RETURN_STATEMENT)         
            Method newPlaceholderNode = nodeInfoStore.getDeclaredMethod("newPlaceholderNode", int.class);
            ASTNode placeholder = (ASTNode) newPlaceholderNode.invoke(nodestore, ASTNode.RETURN_STATEMENT);

            // nodestore.markAsStringPlaceholder(placeholder, commentString)
            Method markAsStringPlaceholder = nodeInfoStore.getDeclaredMethod("markAsStringPlaceholder",
                    ASTNode.class, String.class);
            markAsStringPlaceholder.invoke(nodestore, placeholder, commentString);
            // add comment to body content
            md.getBody().statements().add(0, placeholder);
        } catch (Exception e) {
            // swallowed
        }
    }

    /**
     * Creates a template annotation based on jet templates and returns it.
     * <br>
     * This annotation will be add to a method content in the following cases :
     * <ul>   
     *    <li> when creating new java method
     *    <li> when creating an overridable method
     *    <li> when creating a getter/setter method
     * </ul>
     * It has the following structure : <br>
     * TemplateMethod(id=_id_, comment=_comment_, version=_version_)
     * <br>
     * At forward mechanism, based on this annotations details, the body content for given method will be created.
     * @param bodyTemplateClass - jet template for operation's body 
     * @param operation - the operation where the annotation will be added
     * @param commentTemplateClass - jet template for line comment (optional)
     * @return - the created annotation
     * 
     * @author Cristina
     */
    public static JavaAnnotation createTemplateMethodAnnotation(java.lang.Class<?> bodyTemplateClass,
            JavaOperation operation, java.lang.Class<?> commentTemplateClass) {
        JavaAnnotation annotation = (JavaAnnotation) GeneralCreateNewElement.createElementWithName(
                CodeSyncJavaFactory.eINSTANCE, CodeSyncJavaPackage.eINSTANCE.getJavaAnnotation(), operation);
        annotation.setSource("TemplateMethod");
        annotation.getDetails().put("id", JetTemplateFactory.INSTANCE.getJetTemplateId(bodyTemplateClass));
        if (commentTemplateClass != null) {
            annotation.getDetails().put("comment",
                    JetTemplateFactory.INSTANCE.getJetTemplateId(commentTemplateClass));
        }
        annotation.getDetails().put("version", "1.0");
        return annotation;
    }

}