blob: bb17c2b1806886955ba60aa5422a35d1481692a2 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2018 Agence spatiale canadienne / Canadian Space Agency
* 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:
* Pierre Allard,
* Regent L'Archeveque - initial API and implementation
*
* SPDX-License-Identifier: EPL-1.0
*******************************************************************************/
package org.eclipse.apogy.tools;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.io.FileUtils;
import org.eclipse.core.filebuffers.FileBuffers;
import org.eclipse.core.filebuffers.ITextFileBuffer;
import org.eclipse.core.filebuffers.ITextFileBufferManager;
import org.eclipse.core.filebuffers.LocationKind;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.equinox.app.IApplication;
import org.eclipse.equinox.app.IApplicationContext;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.Javadoc;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.PackageDeclaration;
import org.eclipse.jface.text.Document;
import org.eclipse.text.edits.TextEdit;
/**
* This class controls all aspects of the application's execution
*/
public class Application implements IApplication {
public static final String SRC_GEN = "src-generated";
public static final String SRC_CUSTOM = "src-custom";
public static final String CUSTOM_PREFIX = "Custom";
public static final String CUSTOM_FACTORY_TEMPLATE = " @Override\n" + " public %s create%s()\n" + " {\n"
+ " return new %s();\n" + " }";
public static final String CUSTOM_ITEM_ADAPTER_FACTORY_TEMPLATE = " @Override\n"
+ " public Adapter create%sAdapter()\n" + " {\n" + " if(%s==null){\n"
+ " %s = new %s(this);\n" + " }\n\n" + " return %s;\n" + " }";
public static final String COPYRIGHTS_TEXT = "/********************************************************************************\n"
+ " * Copyright (c) 2018 Agence spatiale canadienne / Canadian Space Agency\n" + " * Contributors:\n"
+ " * Pierre Allard (Pierre.Allard@canada.ca),\n"
+ " * Regent L'Archeveque (Regent.Larcheveque@canada.ca),\n"
+ " * Sebastien Gemme (Sebastien.Gemme@canada.ca),\n"
+ " * Canadian Space Agency (CSA) - Initial API and implementation\n" + " *\n"
+ " * This program and the accompanying materials are made available under the\n"
+ " * terms of the Eclipse Public License v. 1.0 which is available at\n"
+ " * http://www.eclipse.org/legal/epl-v10.html.\n" + " *\n" + " * SPDX-License-Identifier: EPL-1.0\n"
+ " ********************************************************************************/";
public static final String ITEM_PROVIDER_CONSTRUCTOR_TEMPLATE = "public %s(AdapterFactory adapterFactory) {\n"
+ "super(adapterFactory);\n" + "}\n\n";
@Override
public Object start(IApplicationContext context) throws Exception {
refactorGenerated();
return IApplication.EXIT_OK;
}
@Override
public void stop() {
}
public void refactorGenerated() throws Exception {
System.out.println("Application.refactorGenerated() initialized...");
IWorkspace workspace = ResourcesPlugin.getWorkspace();
IWorkspaceRoot root = workspace.getRoot();
IProject[] projects = root.getProjects();
for (IProject project : projects) {
// Check if it is a XCore Project.
IFile xcoreModelFile = getXCoreModelFile(project);
if (xcoreModelFile != null) {
System.out.println("Application.refactorGenerated(): Process Project <" + project.getName() + ">");
String emfPrefix = getValue(xcoreModelFile, "prefix");
String modelDirectory = getValue(xcoreModelFile, "modelDirectory");
String editDirectory = getValue(xcoreModelFile, "editDirectory");
String xcorePackage = getPackage(xcoreModelFile);
processEmfCore(project, emfPrefix, xcorePackage);
if (editDirectory != null) {
processEmfEdit(project, emfPrefix, xcorePackage, modelDirectory, editDirectory);
}
}
}
System.out.println("Application.refactorGenerated(): completed...");
}
private void processEmfEdit(IProject project, String emfPrefix, String xcorePackage, String modelDirectory,
String editDirectory) throws Exception {
String editProjectName = editDirectory.substring(1);
editProjectName = editProjectName.substring(0, editProjectName.indexOf("/"));
IWorkspace workspace = ResourcesPlugin.getWorkspace();
IWorkspaceRoot root = workspace.getRoot();
IProject editProject = root.getProject(editProjectName);
IJavaProject javaProject = JavaCore.create(editProject);
String originalFactoryClassname = emfPrefix + "ItemProviderAdapterFactory";
String customFactoryClassname = CUSTOM_PREFIX + originalFactoryClassname;
IFolder customSourceFolder = editProject.getFolder(SRC_CUSTOM + File.separator
+ xcorePackage.replaceAll("\\.", File.separator) + File.separator + "provider");
String factoryPackage = xcorePackage + ".provider";
ArrayList<String> customClasseNames = new ArrayList<String>();
for (IPackageFragment packageFragment : javaProject.getPackageFragments()) {
if (packageFragment.getElementName().equals(xcorePackage + "." + "provider")) {
for (ICompilationUnit compilationUnit : packageFragment.getCompilationUnits()) {
if (!compilationUnit.getElementName().endsWith("EditPlugin.java")) {
String className = compilationUnit.getElementName().replaceAll(".java", "");
if (!className.equals("MathEditUtils")) {
// Parse the original code.
ASTParser parser = ASTParser.newParser(AST.JLS10);
parser.setKind(ASTParser.K_COMPILATION_UNIT);
parser.setSource(compilationUnit);
parser.setResolveBindings(true);
CompilationUnit astRoot = (CompilationUnit) parser.createAST(null);
astRoot.recordModifications();
CustomASTVisitor visitor = new CustomASTVisitor();
astRoot.accept(visitor);
// Remove the generated fields.
for (FieldDeclaration field : visitor.getGeneratedFields()) {
field.delete();
}
// Remove the generated methods.
for (MethodDeclaration method : visitor.getGeneratedMethods()) {
method.delete();
}
// Remove GEN methods.
List<String> genMethodNames = new ArrayList<String>();
for (MethodDeclaration method : visitor.getGenMethods()) {
genMethodNames.add(method.getName().getIdentifier());
method.delete();
}
// Clean generated javadoc comments.
for (Javadoc javadoc : visitor.getJavadocDeclarations()) {
if (CustomASTVisitor.contains(javadoc, "@generated")) {
javadoc.delete();
}
}
// Clean package declaration if any.
for (PackageDeclaration packageDeclaration : visitor.getPackageDeclarations()) {
Javadoc javadoc = packageDeclaration.getJavadoc();
if (javadoc != null) {
javadoc.delete();
}
}
// Rewrite the new AST.
Document document = new Document(compilationUnit.getSource());
TextEdit edits = astRoot.rewrite(document, null);
edits.apply(document);
/*
*
* Apply changes outside the AST (Easier).
*
*/
String buffer = document.get();
// Replace class name.
buffer = buffer.replaceAll("public class\\s+\\w+",
"public class " + CUSTOM_PREFIX + className);
buffer = buffer.replaceAll("public abstract class\\s+\\w+",
"public abstract class " + CUSTOM_PREFIX + className);
// Replace inherited class and add implicit constructor.
buffer = buffer.replaceAll("extends[\\s\\w\\.]+\\{", "extends " + className + "{\n"
+ String.format(ITEM_PROVIDER_CONSTRUCTOR_TEMPLATE, CUSTOM_PREFIX + className));
// Remove GEN methods and replace call by super.method()
for (String methodName : genMethodNames) {
buffer = buffer.replaceAll(methodName,
"super." + methodName.substring(0, methodName.length() - 3));
}
// Remove Copyrights.
buffer = buffer.replaceAll(
"\\/\\*+[\\s\\S]+(?=Copyright)[\\s\\S]+(?=Canadian Space Agency)[\\s\\S]+?(?=\\*\\/)\\*\\/",
"");
document.set(buffer);
// Check if the src-custom still contains some code.
parser = ASTParser.newParser(AST.JLS10);
parser.setKind(ASTParser.K_COMPILATION_UNIT);
parser.setSource(buffer.toCharArray());
parser.setResolveBindings(true);
astRoot = (CompilationUnit) parser.createAST(null);
visitor = new CustomASTVisitor();
astRoot.accept(visitor);
if (className
.equals("AbstractURLNodeGeometryPlacementAtFeatureOfInterestToolItemProvider")) {
System.out.println("Application.processEmfEdit(): TESTING");
}
// There should be the constructor only.
if ((visitor.getMethods().size() > 1 || !visitor.getFields().isEmpty()
|| !visitor.getOtherBodyDeclarations().isEmpty())
&& !className.endsWith("ItemProviderAdapterFactory")) {
System.out.println("Application.processEmfEdit(): Custom code detected: " + className);
// Save changes on disk in src-custom
IFile file = customSourceFolder.getFile(CUSTOM_PREFIX + className + ".java");
ITextFileBufferManager bufferManager = FileBuffers.getTextFileBufferManager();
bufferManager.connect(file.getLocation(), LocationKind.NORMALIZE, null);
ITextFileBuffer textFileBuffer = bufferManager.getTextFileBuffer(file.getLocation(),
LocationKind.NORMALIZE);
textFileBuffer.getDocument().set(document.get());
textFileBuffer.commit(null, true);
bufferManager.disconnect(file.getLocation(), LocationKind.NORMALIZE, null);
// Add the custom class into the customClasses list
customClasseNames.add(className);
}
}
}
}
}
}
// Update the Custom Factory.
customSourceFolder.refreshLocal(IResource.DEPTH_INFINITE, null);
IFile file = customSourceFolder.getFile(customFactoryClassname + ".java");
StringBuffer factoryFileContentBuffer = new StringBuffer();
if (file.exists()) {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(file.getContents()));
String inputLine;
while ((inputLine = bufferedReader.readLine()) != null) {
factoryFileContentBuffer.append(inputLine);
factoryFileContentBuffer.append("\n");
}
}
String factoryFileContent = factoryFileContentBuffer.toString();
for (String customClassName : customClasseNames) {
String attributeName = customClassName;
attributeName = attributeName.replaceFirst(customClassName.substring(0, 1),
customClassName.substring(0, 1).toLowerCase());
String methodName = "";
try {
methodName = customClassName.substring(0, customClassName.indexOf("ItemProvider"));
} catch (Exception e) {
System.out.println("Application.processEmfEdit(): " + customClassName);
e.printStackTrace();
}
String newContent = String.format(CUSTOM_ITEM_ADAPTER_FACTORY_TEMPLATE, methodName, attributeName,
attributeName, CUSTOM_PREFIX + customClassName, attributeName);
// Remove class generated block comment.
Pattern pattern = Pattern.compile("public class\\W+\\w*\\W+extends\\W+\\w+\\W*(\\{)",
Pattern.MULTILINE | Pattern.DOTALL);
Matcher matcher = pattern.matcher(factoryFileContent);
if (matcher.find()) {
factoryFileContent = matcher.replaceAll(matcher.group(0) + "\n" + newContent);
}
}
// Fix import statement.
Pattern pattern = Pattern.compile("import[\\w\\s.]+;");
Matcher matcher = pattern.matcher(factoryFileContent);
if (matcher.find()) {
factoryFileContent = matcher
.replaceAll("import " + factoryPackage + "." + originalFactoryClassname + ";\n");
}
// Save on disk the new factory.
file.setContents(new ByteArrayInputStream(factoryFileContent.getBytes()), true, false, null);
}
public void processEmfCore(IProject project, String emfPrefix, String xcorePackage) throws Exception {
IJavaProject javaProject = JavaCore.create(project);
String customFactoryClassname = CUSTOM_PREFIX + emfPrefix + "FactoryImpl";
IFolder customSourceFolder = project.getFolder(SRC_CUSTOM + File.separator
+ project.getName().replaceAll("\\.", File.separator) + File.separator + "impl");
ArrayList<String> customClassNames = new ArrayList<String>();
ArrayList<String> abstractClasses = new ArrayList<String>();
for (IPackageFragment packageFragment : javaProject.getPackageFragments()) {
if (packageFragment.getElementName().equals(xcorePackage + "." + "impl")) {
for (ICompilationUnit compilationUnit : packageFragment.getCompilationUnits()) {
if (!compilationUnit.getElementName().endsWith(emfPrefix + "PackageImpl")
// && !compilationUnit.getElementName().endsWith(emfPrefix + "FacadeImpl")
) {
String className = compilationUnit.getElementName().replaceAll(".java", "");
// Parse the original code.
ASTParser parser = ASTParser.newParser(AST.JLS10);
parser.setKind(ASTParser.K_COMPILATION_UNIT);
parser.setSource(compilationUnit);
parser.setResolveBindings(true);
CompilationUnit astRoot = (CompilationUnit) parser.createAST(null);
astRoot.recordModifications();
CustomASTVisitor visitor = new CustomASTVisitor();
astRoot.accept(visitor);
// Remove the generated fields.
for (FieldDeclaration field : visitor.getGeneratedFields()) {
field.delete();
}
// Remove the generated methods.
for (MethodDeclaration method : visitor.getGeneratedMethods()) {
method.delete();
}
// Remove GEN methods.
List<String> genMethodNames = new ArrayList<String>();
for (MethodDeclaration method : visitor.getGenMethods()) {
genMethodNames.add(method.getName().getIdentifier());
method.delete();
}
// Clean generated javadoc comments.
for (Javadoc javadoc : visitor.getJavadocDeclarations()) { // Clean package block comment.
if (CustomASTVisitor.contains(javadoc, "@generated")) {
javadoc.delete();
}
}
// Clean package declaration if any.
for (PackageDeclaration packageDeclaration : visitor.getPackageDeclarations()) {
Javadoc javadoc = packageDeclaration.getJavadoc();
if (javadoc != null) {
javadoc.delete();
}
}
// Rewrite the new AST.
Document document = new Document(compilationUnit.getSource());
TextEdit edits = astRoot.rewrite(document, null);
edits.apply(document);
/*
*
* Apply changes outside the AST (Easier).
*
*/
String buffer = document.get();
// Replace class name.
buffer = buffer.replaceAll("public class\\s+\\w+", "public class " + CUSTOM_PREFIX + className);
buffer = buffer.replaceAll("public abstract class\\s+\\w+",
"public abstract class " + CUSTOM_PREFIX + className);
// Replace inherited class.
buffer = buffer.replaceAll("extends[\\s\\w\\.]+\\{", "extends " + className + "{\n");
// Remove GEN methods and replace call by super.method()
for (String methodName : genMethodNames) {
buffer = buffer.replaceAll(methodName,
"super." + methodName.substring(0, methodName.length() - 3));
}
// Remove Copyrights.
buffer = buffer.replaceAll(
"\\/\\*+[\\s\\S]+(?=Copyright)[\\s\\S]+(?=Canadian Space Agency)[\\s\\S]+?(?=\\*\\/)\\*\\/",
"");
// Check if it is an abstract class.
Pattern pattern = Pattern.compile("public\\s+abstract\\s+class",
Pattern.MULTILINE | Pattern.DOTALL);
Matcher matcher = pattern.matcher(buffer);
if (matcher.find()) {
abstractClasses.add(CUSTOM_PREFIX + className);
}
document.set(buffer);
// Check if the src-custom still contains some code.
parser = ASTParser.newParser(AST.JLS10);
parser.setKind(ASTParser.K_COMPILATION_UNIT);
parser.setSource(buffer.toCharArray());
parser.setResolveBindings(true);
astRoot = (CompilationUnit) parser.createAST(null);
visitor = new CustomASTVisitor();
astRoot.accept(visitor);
if (!visitor.getMethods().isEmpty() || !visitor.getFields().isEmpty()
|| !visitor.getOtherBodyDeclarations().isEmpty()) {
System.out.println("Application.processEmfCore(): Custom code detected: " + className);
// Save changes on disk in src-custom
IFile file = customSourceFolder.getFile(CUSTOM_PREFIX + className + ".java");
ITextFileBufferManager bufferManager = FileBuffers.getTextFileBufferManager();
bufferManager.connect(file.getLocation(), LocationKind.NORMALIZE, null);
ITextFileBuffer textFileBuffer = bufferManager.getTextFileBuffer(file.getLocation(),
LocationKind.NORMALIZE);
textFileBuffer.getDocument().set(document.get());
textFileBuffer.commit(null, true);
bufferManager.disconnect(file.getLocation(), LocationKind.NORMALIZE, null);
// Add the custom class into the customClasses list
if (!className.endsWith("FactoryImpl")) {
// Do not abstract class in the factory (cannot be created).
if (buffer.matches("public\\s+abstract\\s+class")) {
customClassNames.add(className);
}
}
} else {
System.out.println("Application.processEmfCore(): No Custom code detected: " + className);
}
}
}
}
}
// Update the Custom Factory.
customSourceFolder.refreshLocal(IResource.DEPTH_INFINITE, null);
IFile file = customSourceFolder.getFile(customFactoryClassname + ".java");
StringBuffer originalFactoryFileContent = new StringBuffer();
if (file.exists()) {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(file.getContents()));
String inputLine;
while ((inputLine = bufferedReader.readLine()) != null) {
originalFactoryFileContent.append(inputLine);
originalFactoryFileContent.append("\n");
}
}
String factoryFileContent = originalFactoryFileContent.toString();
for (String customClassName : customClassNames) {
if (abstractClasses.contains(customClassName)) {
System.out.println("Application.processEmfCore(): Do not include in the factory the abstract class: "
+ customClassName);
} else {
String bufferName = customClassName.replaceAll(CUSTOM_PREFIX, "");
String interfaceName = bufferName.substring(0, bufferName.length() - "Impl".length());
String newContent = "{\n" + String.format(CUSTOM_FACTORY_TEMPLATE, interfaceName, interfaceName,
CUSTOM_PREFIX + customClassName);
// Remove class generated block comment.
Pattern pattern = Pattern.compile("\\{", Pattern.MULTILINE);
Matcher matcher = pattern.matcher(factoryFileContent);
if (matcher.find()) {
factoryFileContent = matcher.replaceFirst(newContent);
}
}
}
// Save on disk the new factory.
file.setContents(new ByteArrayInputStream(factoryFileContent.getBytes()), true, false, null);
}
private IFile getXCoreModelFile(IProject project) {
IFile xcoreModelFile = null;
IFolder folder = project.getFolder("model");
if (folder != null) {
File directory = new File(folder.getLocationURI().getPath());
if (directory != null && directory.isDirectory()) {
List<File> files = (List<File>) FileUtils.listFiles(directory, new String[] { "xcore" }, false);
if (!files.isEmpty()) {
xcoreModelFile = folder.getFile(files.get(files.size() - 1).getName());
}
}
}
return xcoreModelFile;
}
private String getValue(IFile xcoreFile, String key) {
String value = null;
try {
String xcoreModelFileContent = new String(
Files.readAllBytes(Paths.get(xcoreFile.getLocationURI().getPath())));
Pattern r = Pattern.compile(key + "=\"(.*)\"");
Matcher m = r.matcher(xcoreModelFileContent);
if (m.find()) {
value = m.group(1);
} else {
System.err.println("Application.getValue(" + key + "): Not found");
}
} catch (IOException e) {
e.printStackTrace();
}
return value;
}
private String getPackage(IFile xcoreFile) {
String value = null;
try {
String xcoreModelFileContent = new String(
Files.readAllBytes(Paths.get(xcoreFile.getLocationURI().getPath())));
Pattern r = Pattern.compile("package\\s+(.*)");
Matcher m = r.matcher(xcoreModelFileContent);
if (m.find()) {
value = m.group(1);
}
} catch (IOException e) {
e.printStackTrace();
}
return value;
}
}