/**
* Optimus, framework for Model Transformation
*
* Copyright (C) 2013 Worldline or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.atos.optimus.m2t.merger.java.core;
import static org.eclipse.jdt.core.dom.CompilationUnit.TYPES_PROPERTY;
import static org.eclipse.jdt.core.dom.EnumDeclaration.BODY_DECLARATIONS_PROPERTY;
import static org.eclipse.jdt.core.dom.EnumDeclaration.JAVADOC_PROPERTY;
import static org.eclipse.jdt.core.dom.EnumDeclaration.MODIFIERS2_PROPERTY;
import static org.eclipse.jdt.core.dom.EnumDeclaration.SUPER_INTERFACE_TYPES_PROPERTY;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import net.atos.optimus.common.tools.jdt.JavaCodeHelper;
import net.atos.optimus.m2t.merger.java.core.internal.MergerLogger;
import net.atos.optimus.m2t.merger.java.core.internal.MergerLoggerMessages;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
import org.eclipse.jdt.core.dom.BodyDeclaration;
import org.eclipse.jdt.core.dom.ChildListPropertyDescriptor;
import org.eclipse.jdt.core.dom.ChildPropertyDescriptor;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.EnumConstantDeclaration;
import org.eclipse.jdt.core.dom.EnumDeclaration;
import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
import org.eclipse.jdt.core.dom.rewrite.ListRewrite;
import org.eclipse.jface.text.Document;
/**
* @author Maxence Vanbésien (mvaawl@gmail.com)
* @since 1.0
*/
public class EnumMerger extends BodyDeclarationMerger {
protected JavaCodeMerger jcm = null;
/**
* EnumMerger constructor
*
* @param jcm
* @param astr
* An AST Rewrite instance A JavaCodeMerger instance
* @param log
* A Merger Logger instance
*/
public EnumMerger(JavaCodeMerger jcm, ASTRewrite astr) {
super(astr);
this.jcm = jcm;
}
@Override
protected ChildPropertyDescriptor getJavadocPropertyDescriptor() {
return JAVADOC_PROPERTY;
}
@Override
protected ChildListPropertyDescriptor getModifiersPropertyDescriptor() {
return MODIFIERS2_PROPERTY;
}
protected ChildListPropertyDescriptor getBodyDeclarationsPropertyDescriptor() {
return BODY_DECLARATIONS_PROPERTY;
}
protected ChildListPropertyDescriptor getSuperInterfacesPropertyDescriptor() {
return SUPER_INTERFACE_TYPES_PROPERTY;
}
@SuppressWarnings("unchecked")
protected List<Type> getSuperInterfacesTypes(AbstractTypeDeclaration atd) {
return ((EnumDeclaration) atd).superInterfaceTypes();
}
@Override
public void insert(ASTNode parent, BodyDeclaration fragmentToInsert) {
super.insert(parent, fragmentToInsert);
if (parent instanceof CompilationUnit) {
ListRewrite listRewrite = astr.getListRewrite(parent, TYPES_PROPERTY);
if (listRewrite != null) {
listRewrite.insertLast(fragmentToInsert, null);
}
} else {
AbstractTypeDeclaration lastType = null;
/*
* Read all types of the parentType type declaration (used to insert
* this type at the right place)
*/
List<AbstractTypeDeclaration> existingTypes = JavaCodeHelper.getTypedChildren(
(AbstractTypeDeclaration) parent, AbstractTypeDeclaration.class);
// Save the last type if a type already exist
if (existingTypes.size() > 0) {
lastType = existingTypes.get(existingTypes.size() - 1);
}
ListRewrite lw = astr.getListRewrite(parent, getBodyDeclarationsPropertyDescriptor());
if (lastType == null) {
// ... at the end of the type if this is the first method
lw.insertLast(fragmentToInsert, null);
} else {
// ... after the previous type if a type already exist
lw.insertAfter(fragmentToInsert, lastType, null);
}
}
}
@Override
public void merge(BodyDeclaration existing, BodyDeclaration generated, Set<String> preDefinedAnnotations) {
AbstractTypeDeclaration existingType = (AbstractTypeDeclaration) existing;
AbstractTypeDeclaration generatedType = (AbstractTypeDeclaration) generated;
mergeDeclaration(existingType, generatedType, preDefinedAnnotations);
}
@Override
public void mergeSubFragments(BodyDeclaration existing, BodyDeclaration generated, Document generatedDoc) {
EnumDeclaration existingType = (EnumDeclaration) existing;
EnumDeclaration generatedType = (EnumDeclaration) generated;
mergeBodyDeclarationsChildren(existingType, generatedType, generatedDoc, FieldDeclaration.class);
mergeBodyDeclarationsChildren(existingType, generatedType, generatedDoc, MethodDeclaration.class);
mergeBodyDeclarationsChildren(existingType, generatedType, generatedDoc, AbstractTypeDeclaration.class);
mergeBodyDeclarationsChildren(existingType, generatedType, generatedDoc, EnumConstantDeclaration.class);
}
/**
* Merge type declaration
*/
protected void mergeDeclaration(AbstractTypeDeclaration existing, AbstractTypeDeclaration generated,
Set<String> preDefinedAnnotations) {
super.merge(existing, generated, preDefinedAnnotations);
// Interfaces are merged
mergeInterfaces(existing, generated);
}
/**
* Merge interfaces from existingType and generatedType to an AST Rewrite
* object instance.
*
* @param et
* The existing type
* @param generatedTYpe
* The generated type
* @param astr
* The ASTRewrite object instance containing the merge result
*/
protected void mergeInterfaces(AbstractTypeDeclaration et, AbstractTypeDeclaration gt) {
List<String> existingInterfaces = new ArrayList<String>(3);
// Create an interfaces list in String format.
for (Type t : getSuperInterfacesTypes(et)) {
existingInterfaces.add(JavaCodeHelper.getSimpleName(JavaCodeHelper.getName(t)));
}
/*
* Get a ListRewrite object used to modify interfaces in the existing
* code.
*/
ListRewrite lw = astr.getListRewrite(et, getSuperInterfacesPropertyDescriptor());
// For each interfaces of the generated source
for (Type t : getSuperInterfacesTypes(gt)) {
if (!existingInterfaces.contains(JavaCodeHelper.getSimpleName(JavaCodeHelper.getName(t)))) {
/*
* This interface is containing in the generated code but not in
* the existing code => Add this interface in the merge result.
*/
if (MergerLogger.enabled())
MergerLogger.log(MergerLoggerMessages.ENUM_ADDINTERFACE.value(JavaCodeHelper.getName(t), JavaCodeHelper.getName(et),
JavaCodeHelper.getDescription(et)));
lw.insertLast(t, null);
}
}
}
/**
* Merge all body declaration of a Java source typed with
* {@code bodyDeclarationType} class.
*/
protected <T extends BodyDeclaration> void mergeBodyDeclarationsChildren(AbstractTypeDeclaration existingType,
AbstractTypeDeclaration generatedType, Document generatedDoc, Class<T> bodyDeclarationType) {
if (generatedType != null) {
// For each typed body declaration of the generated type...
List<T> generatedBDs = JavaCodeHelper.getTypedChildren(generatedType, bodyDeclarationType);
for (BodyDeclaration generatedBD : generatedBDs) {
BodyDeclaration existingBD = JavaCodeHelper.getBodyDeclaration(existingType, generatedBD);
// if this is a method and it is not found by signature,
// we try to make correspondance by XMI-ID (if available)
if (existingBD == null) {
existingBD = JavaCodeHelper.getBodyDeclarationFromUniqueId(existingType, generatedBD);
}
// ... call the merge process
jcm.mergeTwoFragments(existingType, existingBD, generatedBD, generatedDoc, astr);
}
}
if (existingType != null) {
// For each typed body declaration of the existing type...
List<T> existingBDs = JavaCodeHelper.getTypedChildren(existingType, bodyDeclarationType);
for (BodyDeclaration existingBD : existingBDs) {
/*
* ... call the merge process if the body declaration in the
* generated type doesn't exist
*/
if ((JavaCodeHelper.getBodyDeclaration(generatedType, existingBD) == null)
&& (JavaCodeHelper.getBodyDeclarationFromUniqueId(generatedType, existingBD) == null)) {
jcm.mergeTwoFragments(existingType, existingBD, null, generatedDoc, astr);
}
}
}
}
}