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