From d4b788a83ce04e34516ddc47bfbfa40aad1f0586 Mon Sep 17 00:00:00 2001 From: Iaron Araujo Date: Wed, 7 Feb 2018 16:00:11 -0300 Subject: [PATCH 1/5] First draft of the CompleteApiScraper and ApiComparator --- .../terasology/documentation/ApiScraper.java | 1 + .../apiScraper/ApiComparator.java | 156 ++++++++++++++++++ .../documentation/apiScraper/ApiSaver.java | 37 +++++ .../apiScraper/CompleteApiScraper.java | 135 +++++++++++++++ .../apiScraper/util/ApiMethod.java | 84 ++++++++++ 5 files changed, 413 insertions(+) create mode 100644 engine-tests/src/test/java/org/terasology/documentation/apiScraper/ApiComparator.java create mode 100644 engine-tests/src/test/java/org/terasology/documentation/apiScraper/ApiSaver.java create mode 100644 engine-tests/src/test/java/org/terasology/documentation/apiScraper/CompleteApiScraper.java create mode 100644 engine-tests/src/test/java/org/terasology/documentation/apiScraper/util/ApiMethod.java diff --git a/engine-tests/src/test/java/org/terasology/documentation/ApiScraper.java b/engine-tests/src/test/java/org/terasology/documentation/ApiScraper.java index 584c08e55..7061cafee 100644 --- a/engine-tests/src/test/java/org/terasology/documentation/ApiScraper.java +++ b/engine-tests/src/test/java/org/terasology/documentation/ApiScraper.java @@ -84,6 +84,7 @@ public final class ApiScraper { } else { //System.out.println("Jar-based Class: " + apiClass + ", came from " + location); sortedApi.put(category, apiClass.getName() + (apiClass.isInterface() ? " (INTERFACE)" : " (CLASS)")); + } break; diff --git a/engine-tests/src/test/java/org/terasology/documentation/apiScraper/ApiComparator.java b/engine-tests/src/test/java/org/terasology/documentation/apiScraper/ApiComparator.java new file mode 100644 index 000000000..9c8bb4acd --- /dev/null +++ b/engine-tests/src/test/java/org/terasology/documentation/apiScraper/ApiComparator.java @@ -0,0 +1,156 @@ +/* + * Copyright 2017 MovingBlocks + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.terasology.documentation.apiScraper; + +import com.google.common.collect.Multimap; +import com.google.common.collect.Multimaps; +import org.terasology.documentation.apiScraper.util.ApiMethod; + +import java.io.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; + +/** + * Created by iaron on 04/02/2018. + */ +public class ApiComparator { + public static void main(String[] args) throws Exception { + + try(BufferedReader br = new BufferedReader(new FileReader("API_file.txt"))){ + + Multimap originalApi = getApi(br); + br.close(); + + BufferedWriter writer = new BufferedWriter(new FileWriter(new File("New_API_file.txt"))); + writer.write(CompleteApiScraper.getApi().toString()); + writer.flush(); + writer.close(); + BufferedReader br2 = new BufferedReader(new FileReader("New_API_file.txt")); + Multimap newApi = getApi(br2); + br2.close(); + + checkIncrease(originalApi, newApi); + System.out.println("REPORT FINISHED"); + } + + } + + private static Multimap getApi(BufferedReader br) throws Exception{ + String line = br.readLine(); + Multimap api = Multimaps.newMultimap(new HashMap>(), ArrayList::new); + while(line != null){ + if(line.startsWith("*")){ + if (line.endsWith("(PACKAGE)")){ + line = br.readLine(); + continue; + } + String className = line; + String aux; + ApiMethod aux_method = new ApiMethod(); + api.put(className, aux_method); + while((aux = br.readLine()) != null && aux.endsWith("(METHOD)")){ + String methodName = aux; + String returnType = br.readLine(); + String parameters = br.readLine(); + String exceptionType = br.readLine(); + ApiMethod method = new ApiMethod(className, methodName, returnType, exceptionType, parameters); + + api.put(className, method); + } + line = aux; + + } else { + line = br.readLine(); + } + } + return api; + } + + private static void checkIncrease(Multimap originalApi, Multimap newApi){ + Collection originalMethods; + Collection newMethods; + for(String className : originalApi.keySet()){ + + originalMethods = originalApi.get(className); + newMethods = newApi.get(className); + + + for(ApiMethod method2 : newMethods){ + + boolean found = false; + for(ApiMethod method1 : originalMethods){ + if(className.equals("* org.terasology.particles.components.generators.VelocityRangeGeneratorComponent (CLASS)")){ + found = false; + } + if(method1.getName().equals(method2.getName())){ + + ApiMethod auxMethod = getMethodWithSameNameAndParameters(method2, originalMethods); + if(!method1.getName().equals(ApiMethod.DEFAULT_VALUE) && auxMethod.getName().equals(ApiMethod.DEFAULT_VALUE)){ + ApiMethod auxMethod2 = getMethodWithSameNameAndParameters(method1, newMethods); + if(!method2.getName().equals(ApiMethod.DEFAULT_VALUE) && auxMethod2.getName().equals(ApiMethod.DEFAULT_VALUE)){ + checkIncrease(method1, method2); + } else{ + System.out.println("MINOR INCREASE, NEW OVERLOADED METHOD " + method2.getName() + + " ON " + method2.getClassName() + "\nNEW PARAMETERS: " + method2.getParametersType()); + System.out.println("================================================================="); + } + + } else { + checkIncrease(auxMethod, method2); + + } + found = true; + } + } + if(! found && !method2.getName().equals(ApiMethod.DEFAULT_VALUE)){ + System.out.println("MINOR INCREASE, NEW METHOD " + method2.getName() + " ON " + method2.getClassName()); + System.out.println("================================================================="); + } + } + + } + + } + + private static void checkIncrease(ApiMethod method1, ApiMethod method2){ + check(method1.getReturnType(), method2.getReturnType(), method1.getName(), method1.getClassName()); + check(method1.getParametersType(), method2.getParametersType(), method1.getName(), method1.getClassName()); + check(method1.getExceptionType(), method2.getExceptionType(), method1.getName(), method1.getClassName()); + } + + private static void check(String s1, String s2, String methodName, String className){ + if(!s1.equals(s2)){ + System.out.println("MAJOR INCREASE ON : " + methodName + " " + className); + System.out.println("ORIGINAL: " + s1); + System.out.println("NEW: " + s2); + System.out.println("================================================================="); + } + + } + + private static ApiMethod getMethodWithSameNameAndParameters(ApiMethod method, Collection originalMethods){ + for(ApiMethod m : originalMethods){ + if(m.getName().equals(method.getName()) && m.getParametersType().equals(method.getParametersType())){ + return m; + } + } + return new ApiMethod(); + + } + + +} diff --git a/engine-tests/src/test/java/org/terasology/documentation/apiScraper/ApiSaver.java b/engine-tests/src/test/java/org/terasology/documentation/apiScraper/ApiSaver.java new file mode 100644 index 000000000..59902a7e3 --- /dev/null +++ b/engine-tests/src/test/java/org/terasology/documentation/apiScraper/ApiSaver.java @@ -0,0 +1,37 @@ +/* + * Copyright 2017 MovingBlocks + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.terasology.documentation.apiScraper; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; + + +public final class ApiSaver { + + /** + * @param args (ignored) + * @throws Exception if the module environment cannot be loaded + */ + public static void main(String[] args) throws Exception { + StringBuffer api = CompleteApiScraper.getApi(); + BufferedWriter writer = new BufferedWriter(new FileWriter(new File("API_file.txt"))); + writer.write(api.toString()); + writer.flush(); + writer.close(); + System.out.println("API file is ready!"); + } +} diff --git a/engine-tests/src/test/java/org/terasology/documentation/apiScraper/CompleteApiScraper.java b/engine-tests/src/test/java/org/terasology/documentation/apiScraper/CompleteApiScraper.java new file mode 100644 index 000000000..9fbc00f50 --- /dev/null +++ b/engine-tests/src/test/java/org/terasology/documentation/apiScraper/CompleteApiScraper.java @@ -0,0 +1,135 @@ +/* + * Copyright 2015 MovingBlocks + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.terasology.documentation.apiScraper; + +import com.google.common.collect.Multimap; +import com.google.common.collect.Multimaps; +import com.google.common.collect.ArrayListMultimap; +import org.terasology.engine.module.ExternalApiWhitelist; +import org.terasology.engine.module.ModuleManager; +import org.terasology.module.ModuleEnvironment; +import org.terasology.module.sandbox.API; +import org.terasology.testUtil.ModuleManagerFactory; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.net.URL; +import java.util.*; +import java.util.stream.Collectors; + +/** + * Enumerates all classes and packages that are annotated with {@link API}. + */ +public final class CompleteApiScraper { + private CompleteApiScraper() { + // Private constructor, utility class + } + + /** + * + * @return Project's Packages, Interfaces, Classes and Methods + * @throws Exception if the module environment cannot be loaded + */ + public static StringBuffer getApi() throws Exception { + ModuleManager moduleManager = ModuleManagerFactory.create(); + ModuleEnvironment environment = moduleManager.getEnvironment(); + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + + Multimap api = Multimaps.newMultimap(new HashMap>(), ArrayList::new); + + for (Class apiClass : environment.getTypesAnnotatedWith(API.class)) { + //System.out.println("Processing: " + apiClass); + boolean isPackage = apiClass.isSynthetic(); + URL location; + String category; + String apiPackage = ""; + if (isPackage) { + apiPackage = apiClass.getPackage().getName(); + location = classLoader.getResource(apiPackage.replace('.', '/')); + } else { + + location = apiClass.getResource('/' + apiClass.getName().replace('.', '/') + ".class"); + } + + if (location == null) { + System.out.println("Failed to get a class/package location, skipping " + apiClass); + continue; + } + + switch (location.getProtocol()) { + case "jar" : + + // Find out what jar it came from and consider that the category + String categoryFragment = location.getPath(); + //System.out.println("category fragment as path: " + categoryFragment); + int bang = categoryFragment.lastIndexOf("!"); + int hyphen = categoryFragment.lastIndexOf("-", bang); + int slash = categoryFragment.lastIndexOf("/", hyphen); + category = categoryFragment.substring(slash + 1, hyphen); + //System.out.println("category fragment pared down: " + category); + addToApi(isPackage, category, apiPackage, apiClass, api); + break; + + case "file" : + // If file based we know it is local so organize it like that + category = "terasology engine"; + addToApi(isPackage, category, apiPackage, apiClass, api); + break; + + default : + System.out.println("Unknown protocol for: " + apiClass + ", came from " + location); + } + } + api.putAll("external", ExternalApiWhitelist.CLASSES.stream() + .map(clazz->clazz.getName() + " (CLASS)").collect(Collectors.toSet())); + api.putAll("external", ExternalApiWhitelist.PACKAGES.stream() + .map(packagee->packagee + " (PACKAGE)").collect(Collectors.toSet())); + + + StringBuffer stringApi = new StringBuffer(); + + stringApi.append("# Modding API:\n"); + for (String key : api.keySet()) { + stringApi.append("## " + key + "\n"); + for (String value : api.get(key)) { + stringApi.append("* " + value + "\n"); + } + stringApi.append("\n"); + } + + return stringApi; + } + + private static void addToApi(boolean isPackage, String category, String apiPackage, Class apiClass, Multimap api){ + if (isPackage) { + //System.out.println("Local Package: " + apiPackage + ", came from " + location); + api.put(category, apiPackage + " (PACKAGE)"); + } else { + //System.out.println("Local Class: " + apiClass + ", came from " + location); + api.put(category, apiClass.getName() + (apiClass.isInterface() ? " (INTERFACE)" : " (CLASS)")); + Method[] methods = apiClass.getDeclaredMethods(); + for(int i = 0; i < methods.length; i++){ + if(!methods[i].isDefault() && !methods[i].isBridge() && !methods[i].isSynthetic()){ + api.put(category, " - " + methods[i].getName() + " (METHOD)"); + api.put(category, " -- " + methods[i].getReturnType() + " (RETURN)"); + api.put(category, " -- " + Arrays.toString(methods[i].getParameterTypes()) + " (PARAMETERS)"); + api.put(category, " -- " + Arrays.toString(methods[i].getExceptionTypes()) + " (EXCEPTIONS)"); + } + } + } + + } +} diff --git a/engine-tests/src/test/java/org/terasology/documentation/apiScraper/util/ApiMethod.java b/engine-tests/src/test/java/org/terasology/documentation/apiScraper/util/ApiMethod.java new file mode 100644 index 000000000..1205dfca9 --- /dev/null +++ b/engine-tests/src/test/java/org/terasology/documentation/apiScraper/util/ApiMethod.java @@ -0,0 +1,84 @@ +/* + * Copyright 2017 MovingBlocks + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.terasology.documentation.apiScraper.util; + + +public class ApiMethod { + + public static final String DEFAULT_VALUE = "DEFAULT"; + + private String className; + private String name; + private String returnType; + private String exceptionType; + private String parametersType; + + public ApiMethod(String className, String name, String returnType, String exceptionType, String parametersType){ + this.className = className; + this.name = name; + this.returnType = returnType; + this.exceptionType = exceptionType; + this.parametersType = parametersType; + } + + public ApiMethod(){ + this(DEFAULT_VALUE,DEFAULT_VALUE,DEFAULT_VALUE,DEFAULT_VALUE,DEFAULT_VALUE); + } + + public String getClassName(){ + return className; + } + + public String getName() { + return name; + } + + public String getReturnType() { + return returnType; + } + + public String getExceptionType() { + return exceptionType; + } + + public String getParametersType() { + return parametersType; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ApiMethod apiMethod = (ApiMethod) o; + + if (!getClassName().equals(apiMethod.getClassName())) return false; + if (!getName().equals(apiMethod.getName())) return false; + if (!getReturnType().equals(apiMethod.getReturnType())) return false; + if (!getExceptionType().equals(apiMethod.getExceptionType())) return false; + return getParametersType().equals(apiMethod.getParametersType()); + } + + @Override + public int hashCode() { + int result = getClassName().hashCode(); + result = 31 * result + getName().hashCode(); + result = 31 * result + getReturnType().hashCode(); + result = 31 * result + getExceptionType().hashCode(); + result = 31 * result + getParametersType().hashCode(); + return result; + } +} From 6264efa98d2d9b73bcc286e49429055a207627ec Mon Sep 17 00:00:00 2001 From: iaronaraujo Date: Thu, 12 Apr 2018 11:53:22 -0300 Subject: [PATCH 2/5] Added increase cases and minor performance refactor --- .../terasology/documentation/ApiScraper.java | 1 - .../apiScraper/ApiComparator.java | 167 +++++++++++++----- .../apiScraper/CompleteApiScraper.java | 36 +++- .../apiScraper/util/ApiMethod.java | 6 +- 4 files changed, 156 insertions(+), 54 deletions(-) diff --git a/engine-tests/src/test/java/org/terasology/documentation/ApiScraper.java b/engine-tests/src/test/java/org/terasology/documentation/ApiScraper.java index 7061cafee..584c08e55 100644 --- a/engine-tests/src/test/java/org/terasology/documentation/ApiScraper.java +++ b/engine-tests/src/test/java/org/terasology/documentation/ApiScraper.java @@ -84,7 +84,6 @@ public final class ApiScraper { } else { //System.out.println("Jar-based Class: " + apiClass + ", came from " + location); sortedApi.put(category, apiClass.getName() + (apiClass.isInterface() ? " (INTERFACE)" : " (CLASS)")); - } break; diff --git a/engine-tests/src/test/java/org/terasology/documentation/apiScraper/ApiComparator.java b/engine-tests/src/test/java/org/terasology/documentation/apiScraper/ApiComparator.java index 9c8bb4acd..fe60b6b6b 100644 --- a/engine-tests/src/test/java/org/terasology/documentation/apiScraper/ApiComparator.java +++ b/engine-tests/src/test/java/org/terasology/documentation/apiScraper/ApiComparator.java @@ -23,16 +23,15 @@ import java.io.*; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; +import java.util.List; + -/** - * Created by iaron on 04/02/2018. - */ public class ApiComparator { public static void main(String[] args) throws Exception { try(BufferedReader br = new BufferedReader(new FileReader("API_file.txt"))){ - Multimap originalApi = getApi(br); + HashMap> originalApi = getApi(br); br.close(); BufferedWriter writer = new BufferedWriter(new FileWriter(new File("New_API_file.txt"))); @@ -40,18 +39,20 @@ public class ApiComparator { writer.flush(); writer.close(); BufferedReader br2 = new BufferedReader(new FileReader("New_API_file.txt")); - Multimap newApi = getApi(br2); + HashMap> newApi = getApi(br2); br2.close(); - checkIncrease(originalApi, newApi); + System.out.println("================================================================="); + checkClassAdditionAndDeletion(originalApi, newApi); + checkMethodChanges(originalApi, newApi); System.out.println("REPORT FINISHED"); } } - private static Multimap getApi(BufferedReader br) throws Exception{ + private static HashMap> getApi(BufferedReader br) throws Exception{ String line = br.readLine(); - Multimap api = Multimaps.newMultimap(new HashMap>(), ArrayList::new); + HashMap> api = new HashMap<>(); while(line != null){ if(line.startsWith("*")){ if (line.endsWith("(PACKAGE)")){ @@ -60,16 +61,25 @@ public class ApiComparator { } String className = line; String aux; - ApiMethod aux_method = new ApiMethod(); - api.put(className, aux_method); - while((aux = br.readLine()) != null && aux.endsWith("(METHOD)")){ - String methodName = aux; - String returnType = br.readLine(); - String parameters = br.readLine(); - String exceptionType = br.readLine(); - ApiMethod method = new ApiMethod(className, methodName, returnType, exceptionType, parameters); + api.put(className, new ArrayList<>()); + ApiMethod method; + while((aux = br.readLine()) != null && (aux.endsWith("(METHOD)") + || aux.endsWith("(CONSTRUCTOR)") + || aux.endsWith("(ABSTRACT METHOD)"))){ + if(aux.endsWith("(METHOD)") || aux.endsWith("(ABSTRACT METHOD)")){ + String returnType = br.readLine(); + String parameters = br.readLine(); + String exceptionType = br.readLine(); + method = new ApiMethod(className, aux, returnType, exceptionType, parameters); + } else { + String returnType = ""; + String parameters = br.readLine(); + String exceptionType = ""; + method = new ApiMethod(className, aux, returnType, exceptionType, parameters); + } - api.put(className, method); + + api.get(className).add(method); } line = aux; @@ -80,53 +90,116 @@ public class ApiComparator { return api; } - private static void checkIncrease(Multimap originalApi, Multimap newApi){ + private static void checkClassAdditionAndDeletion(HashMap> originalApi, HashMap> newApi){ + System.out.println("Checking Class Addition and Deletion"); + for(String className : originalApi.keySet()){ + if(!newApi.containsKey(className)){ + System.out.println("MAJOR INCREASE, DELETION OF " + className); + } + } + + for(String className : newApi.keySet()){ + if(!originalApi.containsKey(className)){ + System.out.println("MINOR INCREASE, ADDITION OF " + className); + } + } + + } + + private static void checkMethodChanges(HashMap> originalApi, + HashMap> newApi){ + System.out.println("Checking Method Changes"); Collection originalMethods; Collection newMethods; for(String className : originalApi.keySet()){ originalMethods = originalApi.get(className); newMethods = newApi.get(className); + if(newMethods == null) continue; + checkMethodDeletion(originalMethods, newMethods); + for(ApiMethod method2 : newMethods){ - for(ApiMethod method2 : newMethods){ - - boolean found = false; - for(ApiMethod method1 : originalMethods){ - if(className.equals("* org.terasology.particles.components.generators.VelocityRangeGeneratorComponent (CLASS)")){ - found = false; - } - if(method1.getName().equals(method2.getName())){ - - ApiMethod auxMethod = getMethodWithSameNameAndParameters(method2, originalMethods); - if(!method1.getName().equals(ApiMethod.DEFAULT_VALUE) && auxMethod.getName().equals(ApiMethod.DEFAULT_VALUE)){ - ApiMethod auxMethod2 = getMethodWithSameNameAndParameters(method1, newMethods); - if(!method2.getName().equals(ApiMethod.DEFAULT_VALUE) && auxMethod2.getName().equals(ApiMethod.DEFAULT_VALUE)){ - checkIncrease(method1, method2); - } else{ - System.out.println("MINOR INCREASE, NEW OVERLOADED METHOD " + method2.getName() + - " ON " + method2.getClassName() + "\nNEW PARAMETERS: " + method2.getParametersType()); - System.out.println("================================================================="); - } - - } else { - checkIncrease(auxMethod, method2); + boolean found = false; + for(ApiMethod method1 : originalMethods){ + if(method1.getName().equals(method2.getName())){ + ApiMethod auxMethod = getMethodWithSameNameAndParameters(method2, originalMethods); + if(auxMethod.getName().equals("")){ + ApiMethod auxMethod2 = getMethodWithSameNameAndParameters(method1, newMethods); + if(auxMethod2.getName().equals("")){ + checkMethodIncrease(method1, method2); + } else if (isInterfaceOrAbstract(method2.getClassName())){ + System.out.println("MINOR INCREASE, NEW OVERLOADED METHOD " + method2.getName() + + " ON " + method2.getClassName() + "\nNEW PARAMETERS: " + method2.getParametersType()); + System.out.println("================================================================="); } - found = true; + + } else { + checkMethodIncrease(auxMethod, method2); + } - } - if(! found && !method2.getName().equals(ApiMethod.DEFAULT_VALUE)){ - System.out.println("MINOR INCREASE, NEW METHOD " + method2.getName() + " ON " + method2.getClassName()); - System.out.println("================================================================="); + found = true; } } + if(! found && isInterfaceOrAbstract(method2.getClassName())){ + if(method2.getName().endsWith("(ABSTRACT METHOD)")){ + System.out.println("MAJOR INCREASE, NEW ABSTRACT METHOD " + method2.getName() + " ON " + method2.getClassName()); + }else{ + System.out.println("MINOR INCREASE, NEW METHOD " + method2.getName() + " ON " + method2.getClassName()); + } + System.out.println("================================================================="); + } + } } } - private static void checkIncrease(ApiMethod method1, ApiMethod method2){ + private static void checkMethodDeletion(CollectionoriginalMethods, Collection newMethods){ + List checkedMethods = new ArrayList<>(); + for(ApiMethod method1 : originalMethods){ + + boolean found = false; + List newMethodsWithSameName = new ArrayList<>(); + List originalMethodsWithSameName = new ArrayList<>(); + for(ApiMethod method2 : newMethods){ + if(method1.getName().equals(method2.getName())){ + found = true; + newMethodsWithSameName.add(method2); + } + } + + if(found && !checkedMethods.contains(method1.getName())){ + for(ApiMethod oMethod : originalMethods){ + if(oMethod.getName().equals(method1.getName())){ + originalMethodsWithSameName.add(oMethod); + } + } + if((originalMethodsWithSameName.size() - newMethodsWithSameName.size()) > 0){ + for(ApiMethod method : originalMethodsWithSameName){ + ApiMethod result = getMethodWithSameNameAndParameters(method, newMethodsWithSameName); + if (result.getName().equals("")){ + checkedMethods.add(method.getName()); + System.out.println("MAJOR INCREASE, OVERLOADED METHOD DELETION: " + method.getName() + + " ON " + method.getClassName() + "\nPARAMETERS: " + method.getParametersType()); + + } + } + } + } + if(!found){ + System.out.println("MAJOR INCREASE, METHOD DELETION: " + method1.getName() + " ON " + method1.getClassName()); + } + } + + } + + private static boolean isInterfaceOrAbstract(String className){ + return (className.endsWith("(ABSTRACT CLASS)") || className.endsWith("(INTERFACE)")); + } + + private static void checkMethodIncrease(ApiMethod method1, ApiMethod method2){ check(method1.getReturnType(), method2.getReturnType(), method1.getName(), method1.getClassName()); check(method1.getParametersType(), method2.getParametersType(), method1.getName(), method1.getClassName()); check(method1.getExceptionType(), method2.getExceptionType(), method1.getName(), method1.getClassName()); @@ -148,7 +221,7 @@ public class ApiComparator { return m; } } - return new ApiMethod(); + return new ApiMethod("","","","",""); } diff --git a/engine-tests/src/test/java/org/terasology/documentation/apiScraper/CompleteApiScraper.java b/engine-tests/src/test/java/org/terasology/documentation/apiScraper/CompleteApiScraper.java index 9fbc00f50..6afb94157 100644 --- a/engine-tests/src/test/java/org/terasology/documentation/apiScraper/CompleteApiScraper.java +++ b/engine-tests/src/test/java/org/terasology/documentation/apiScraper/CompleteApiScraper.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 MovingBlocks + * Copyright 2018 MovingBlocks * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,6 +24,7 @@ import org.terasology.module.ModuleEnvironment; import org.terasology.module.sandbox.API; import org.terasology.testUtil.ModuleManagerFactory; +import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.net.URL; @@ -119,11 +120,40 @@ public final class CompleteApiScraper { api.put(category, apiPackage + " (PACKAGE)"); } else { //System.out.println("Local Class: " + apiClass + ", came from " + location); - api.put(category, apiClass.getName() + (apiClass.isInterface() ? " (INTERFACE)" : " (CLASS)")); + String className = apiClass.getName(); + String type; + if(apiClass.isInterface()){ + type = " (INTERFACE)"; + } else { + int modifier = apiClass.getModifiers(); + if( Modifier.isAbstract(modifier)){ + type = " (ABSTRACT CLASS)"; + }else{ + type = " (CLASS)"; + } + } + api.put(category, className + type); + + Constructor[] constructors = apiClass.getDeclaredConstructors(); + for(int i = 0; i < constructors.length; i++){ + api.put(category, " - " + constructors[i].getName() + " (CONSTRUCTOR)"); + api.put(category, " -- " + Arrays.toString(constructors[i].getParameterTypes()) + " (PARAMETERS)"); + } + + Method[] methods = apiClass.getDeclaredMethods(); for(int i = 0; i < methods.length; i++){ if(!methods[i].isDefault() && !methods[i].isBridge() && !methods[i].isSynthetic()){ - api.put(category, " - " + methods[i].getName() + " (METHOD)"); + + // + int modifier = methods[i].getModifiers(); + if( Modifier.isAbstract(modifier)){ + type = " (ABSTRACT METHOD)"; + }else{ + type = " (METHOD)"; + } + // + api.put(category, " - " + methods[i].getName() + type); api.put(category, " -- " + methods[i].getReturnType() + " (RETURN)"); api.put(category, " -- " + Arrays.toString(methods[i].getParameterTypes()) + " (PARAMETERS)"); api.put(category, " -- " + Arrays.toString(methods[i].getExceptionTypes()) + " (EXCEPTIONS)"); diff --git a/engine-tests/src/test/java/org/terasology/documentation/apiScraper/util/ApiMethod.java b/engine-tests/src/test/java/org/terasology/documentation/apiScraper/util/ApiMethod.java index 1205dfca9..5660ec255 100644 --- a/engine-tests/src/test/java/org/terasology/documentation/apiScraper/util/ApiMethod.java +++ b/engine-tests/src/test/java/org/terasology/documentation/apiScraper/util/ApiMethod.java @@ -18,7 +18,7 @@ package org.terasology.documentation.apiScraper.util; public class ApiMethod { - public static final String DEFAULT_VALUE = "DEFAULT"; + //public static final String DEFAULT_VALUE = "DEFAULT"; private String className; private String name; @@ -34,9 +34,9 @@ public class ApiMethod { this.parametersType = parametersType; } - public ApiMethod(){ + /*public ApiMethod(){ this(DEFAULT_VALUE,DEFAULT_VALUE,DEFAULT_VALUE,DEFAULT_VALUE,DEFAULT_VALUE); - } + }*/ public String getClassName(){ return className; From c61ea404e3da9f45011ef6f3ef2101903d5f6084 Mon Sep 17 00:00:00 2001 From: Iaron Araujo Date: Thu, 12 Apr 2018 15:21:09 -0300 Subject: [PATCH 3/5] Refactored code following CheckStyle's warnings and some suggestions on the PR discussion. Added Javadoc and Comments --- .../apiScraper/ApiComparator.java | 168 ++++++++++++------ .../documentation/apiScraper/ApiSaver.java | 9 +- .../apiScraper/CompleteApiScraper.java | 139 +++++++++------ .../apiScraper/util/ApiMethod.java | 49 ++--- 4 files changed, 233 insertions(+), 132 deletions(-) diff --git a/engine-tests/src/test/java/org/terasology/documentation/apiScraper/ApiComparator.java b/engine-tests/src/test/java/org/terasology/documentation/apiScraper/ApiComparator.java index fe60b6b6b..878975fd4 100644 --- a/engine-tests/src/test/java/org/terasology/documentation/apiScraper/ApiComparator.java +++ b/engine-tests/src/test/java/org/terasology/documentation/apiScraper/ApiComparator.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 MovingBlocks + * Copyright 2018 MovingBlocks * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,33 +15,54 @@ */ package org.terasology.documentation.apiScraper; -import com.google.common.collect.Multimap; -import com.google.common.collect.Multimaps; + import org.terasology.documentation.apiScraper.util.ApiMethod; -import java.io.*; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.File; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; +import java.util.Map; import java.util.List; -public class ApiComparator { +/** + * Generates a "New_API_file.txt" and compares it with the "API_file.txt" to detect major and minor version increases. + * Major increases: Deletion of class, new abstract method, method deletion, existing method's change of + * parameters types, exception types or return type. + * Minor increases: Creation of a new class and new method in an interface or abstract class, + */ +public final class ApiComparator { + + private ApiComparator() { + + } + + public static void main(String[] args) throws Exception { - try(BufferedReader br = new BufferedReader(new FileReader("API_file.txt"))){ + try (BufferedReader br = new BufferedReader(new FileReader("API_file.txt"))) { - HashMap> originalApi = getApi(br); + //Creating a map with the original api's data + Map> originalApi = getApi(br); br.close(); + //Generating "New_API_file.txt" BufferedWriter writer = new BufferedWriter(new FileWriter(new File("New_API_file.txt"))); writer.write(CompleteApiScraper.getApi().toString()); writer.flush(); writer.close(); BufferedReader br2 = new BufferedReader(new FileReader("New_API_file.txt")); - HashMap> newApi = getApi(br2); + + //Creating a map with the new api's data + Map> newApi = getApi(br2); br2.close(); + //Begins comparison and increases report System.out.println("================================================================="); checkClassAdditionAndDeletion(originalApi, newApi); checkMethodChanges(originalApi, newApi); @@ -50,12 +71,19 @@ public class ApiComparator { } - private static HashMap> getApi(BufferedReader br) throws Exception{ + /** + * Reads an api file and puts it's information in a map to be used in the api comparison + * @param br BufferedReader containing an api file content + * @return A map with the api classes and interfaces as keys; + * Their methods and as a list of ApiMethods in the values + * @throws Exception if the readLine fails. + */ + private static Map> getApi(BufferedReader br) throws Exception { String line = br.readLine(); - HashMap> api = new HashMap<>(); - while(line != null){ - if(line.startsWith("*")){ - if (line.endsWith("(PACKAGE)")){ + Map> api = new HashMap<>(); + while (line != null) { + if (line.startsWith("*")) { + if (line.endsWith("(PACKAGE)")) { line = br.readLine(); continue; } @@ -63,10 +91,13 @@ public class ApiComparator { String aux; api.put(className, new ArrayList<>()); ApiMethod method; - while((aux = br.readLine()) != null && (aux.endsWith("(METHOD)") + aux = br.readLine(); + while ((aux != null && (aux.endsWith("(METHOD)") || aux.endsWith("(CONSTRUCTOR)") - || aux.endsWith("(ABSTRACT METHOD)"))){ - if(aux.endsWith("(METHOD)") || aux.endsWith("(ABSTRACT METHOD)")){ + || aux.endsWith("(ABSTRACT METHOD)")))) { + + //Checks if its a method or constructor + if (aux.endsWith("(METHOD)") || aux.endsWith("(ABSTRACT METHOD)")) { String returnType = br.readLine(); String parameters = br.readLine(); String exceptionType = br.readLine(); @@ -80,6 +111,7 @@ public class ApiComparator { api.get(className).add(method); + aux = br.readLine(); } line = aux; @@ -90,46 +122,53 @@ public class ApiComparator { return api; } - private static void checkClassAdditionAndDeletion(HashMap> originalApi, HashMap> newApi){ + private static void checkClassAdditionAndDeletion(Map> originalApi, Map> newApi) { System.out.println("Checking Class Addition and Deletion"); - for(String className : originalApi.keySet()){ - if(!newApi.containsKey(className)){ + for (String className : originalApi.keySet()) { + if (!newApi.containsKey(className)) { System.out.println("MAJOR INCREASE, DELETION OF " + className); } } - for(String className : newApi.keySet()){ - if(!originalApi.containsKey(className)){ + for (String className : newApi.keySet()) { + if (!originalApi.containsKey(className)) { System.out.println("MINOR INCREASE, ADDITION OF " + className); } } } - private static void checkMethodChanges(HashMap> originalApi, - HashMap> newApi){ + /** + * Checks creation and deletion of methods, as well as existing method changes + * @param originalApi the original api generated from "API_file.txt" + * @param newApi the new apí generated from "New_API_file.txt" + */ + private static void checkMethodChanges(Map> originalApi, + Map> newApi) { System.out.println("Checking Method Changes"); Collection originalMethods; Collection newMethods; - for(String className : originalApi.keySet()){ + for (String className : originalApi.keySet()) { originalMethods = originalApi.get(className); newMethods = newApi.get(className); - if(newMethods == null) continue; + if (newMethods == null) { + continue; + } checkMethodDeletion(originalMethods, newMethods); - for(ApiMethod method2 : newMethods){ + for (ApiMethod method2 : newMethods) { - boolean found = false; - for(ApiMethod method1 : originalMethods){ - if(method1.getName().equals(method2.getName())){ + boolean found = false; // if found, the method is an existing one or a new overloaded method + for (ApiMethod method1 : originalMethods) { + if (method1.getName().equals(method2.getName())) { ApiMethod auxMethod = getMethodWithSameNameAndParameters(method2, originalMethods); - if(auxMethod.getName().equals("")){ + if (auxMethod.getName().equals("")) { ApiMethod auxMethod2 = getMethodWithSameNameAndParameters(method1, newMethods); - if(auxMethod2.getName().equals("")){ + if (auxMethod2.getName().equals("")) { checkMethodIncrease(method1, method2); - } else if (isInterfaceOrAbstract(method2.getClassName())){ + } else if (isInterfaceOrAbstract(method2.getClassName())) { System.out.println("MINOR INCREASE, NEW OVERLOADED METHOD " + method2.getName() + " ON " + method2.getClassName() + "\nNEW PARAMETERS: " + method2.getParametersType()); System.out.println("================================================================="); @@ -142,10 +181,10 @@ public class ApiComparator { found = true; } } - if(! found && isInterfaceOrAbstract(method2.getClassName())){ - if(method2.getName().endsWith("(ABSTRACT METHOD)")){ + if (!found && isInterfaceOrAbstract(method2.getClassName())) { + if (method2.getName().endsWith("(ABSTRACT METHOD)")) { System.out.println("MAJOR INCREASE, NEW ABSTRACT METHOD " + method2.getName() + " ON " + method2.getClassName()); - }else{ + } else { System.out.println("MINOR INCREASE, NEW METHOD " + method2.getName() + " ON " + method2.getClassName()); } System.out.println("================================================================="); @@ -156,30 +195,31 @@ public class ApiComparator { } - private static void checkMethodDeletion(CollectionoriginalMethods, Collection newMethods){ + private static void checkMethodDeletion(Collection originalMethods, Collection newMethods) { List checkedMethods = new ArrayList<>(); - for(ApiMethod method1 : originalMethods){ + for (ApiMethod method1 : originalMethods) { boolean found = false; List newMethodsWithSameName = new ArrayList<>(); List originalMethodsWithSameName = new ArrayList<>(); - for(ApiMethod method2 : newMethods){ - if(method1.getName().equals(method2.getName())){ + for (ApiMethod method2 : newMethods) { + if (method1.getName().equals(method2.getName())) { found = true; newMethodsWithSameName.add(method2); } } - if(found && !checkedMethods.contains(method1.getName())){ - for(ApiMethod oMethod : originalMethods){ - if(oMethod.getName().equals(method1.getName())){ + //this checks the deletion of an overloaded method + if (found && !checkedMethods.contains(method1.getName())) { + for (ApiMethod oMethod : originalMethods) { + if (oMethod.getName().equals(method1.getName())) { originalMethodsWithSameName.add(oMethod); } } - if((originalMethodsWithSameName.size() - newMethodsWithSameName.size()) > 0){ - for(ApiMethod method : originalMethodsWithSameName){ + if ((originalMethodsWithSameName.size() - newMethodsWithSameName.size()) > 0) { + for (ApiMethod method : originalMethodsWithSameName) { ApiMethod result = getMethodWithSameNameAndParameters(method, newMethodsWithSameName); - if (result.getName().equals("")){ + if (result.getName().equals("")) { checkedMethods.add(method.getName()); System.out.println("MAJOR INCREASE, OVERLOADED METHOD DELETION: " + method.getName() + " ON " + method.getClassName() + "\nPARAMETERS: " + method.getParametersType()); @@ -188,25 +228,38 @@ public class ApiComparator { } } } - if(!found){ + if (!found) { System.out.println("MAJOR INCREASE, METHOD DELETION: " + method1.getName() + " ON " + method1.getClassName()); } } } - private static boolean isInterfaceOrAbstract(String className){ + private static boolean isInterfaceOrAbstract(String className) { return (className.endsWith("(ABSTRACT CLASS)") || className.endsWith("(INTERFACE)")); } - private static void checkMethodIncrease(ApiMethod method1, ApiMethod method2){ + /** + * Compares a not overloaded method in the newApi and originalApi to notify parameter type, return type or + * exception type changes + * @param method1 a not overloaded method from the originalApi, with the same name as method2 + * @param method2 a not overloaded method from the newApi, with the same name as method1 + */ + private static void checkMethodIncrease(ApiMethod method1, ApiMethod method2) { check(method1.getReturnType(), method2.getReturnType(), method1.getName(), method1.getClassName()); check(method1.getParametersType(), method2.getParametersType(), method1.getName(), method1.getClassName()); check(method1.getExceptionType(), method2.getExceptionType(), method1.getName(), method1.getClassName()); } - private static void check(String s1, String s2, String methodName, String className){ - if(!s1.equals(s2)){ + /** + * Compares a method's field in the newApi and originalApi. This field can be, return, parameter or exception type + * @param s1 field to be compared from a method in the originalApi + * @param s2 field to be compared from a method in the newApi + * @param methodName name of the method to have it's field being compared + * @param className the name of the class the have the method + */ + private static void check(String s1, String s2, String methodName, String className) { + if (!s1.equals(s2)) { System.out.println("MAJOR INCREASE ON : " + methodName + " " + className); System.out.println("ORIGINAL: " + s1); System.out.println("NEW: " + s2); @@ -215,13 +268,20 @@ public class ApiComparator { } - private static ApiMethod getMethodWithSameNameAndParameters(ApiMethod method, Collection originalMethods){ - for(ApiMethod m : originalMethods){ - if(m.getName().equals(method.getName()) && m.getParametersType().equals(method.getParametersType())){ + /** + * Tries to find a method with the same name and parameter type as 'method' in a collection of methods + * @param method the method used in the search + * @param methods the collection of methods + * @return the method with the same name and parameter type as 'method' if it exists. If not, returns a new + * ApiMethod with all the attributes being empty String. + */ + private static ApiMethod getMethodWithSameNameAndParameters(ApiMethod method, Collection methods) { + for (ApiMethod m : methods) { + if (m.getName().equals(method.getName()) && m.getParametersType().equals(method.getParametersType())) { return m; } } - return new ApiMethod("","","","",""); + return new ApiMethod("", "", "", "", ""); } diff --git a/engine-tests/src/test/java/org/terasology/documentation/apiScraper/ApiSaver.java b/engine-tests/src/test/java/org/terasology/documentation/apiScraper/ApiSaver.java index 59902a7e3..bc7295808 100644 --- a/engine-tests/src/test/java/org/terasology/documentation/apiScraper/ApiSaver.java +++ b/engine-tests/src/test/java/org/terasology/documentation/apiScraper/ApiSaver.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 MovingBlocks + * Copyright 2018 MovingBlocks * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,9 +19,14 @@ import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; - +/** + * Saves the API generated by CompleteApiScraper in a txt file + */ public final class ApiSaver { + private ApiSaver() { + + } /** * @param args (ignored) * @throws Exception if the module environment cannot be loaded diff --git a/engine-tests/src/test/java/org/terasology/documentation/apiScraper/CompleteApiScraper.java b/engine-tests/src/test/java/org/terasology/documentation/apiScraper/CompleteApiScraper.java index 6afb94157..c77720c63 100644 --- a/engine-tests/src/test/java/org/terasology/documentation/apiScraper/CompleteApiScraper.java +++ b/engine-tests/src/test/java/org/terasology/documentation/apiScraper/CompleteApiScraper.java @@ -17,7 +17,8 @@ package org.terasology.documentation.apiScraper; import com.google.common.collect.Multimap; import com.google.common.collect.Multimaps; -import com.google.common.collect.ArrayListMultimap; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.terasology.engine.module.ExternalApiWhitelist; import org.terasology.engine.module.ModuleManager; import org.terasology.module.ModuleEnvironment; @@ -28,13 +29,23 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.net.URL; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; import java.util.stream.Collectors; /** - * Enumerates all classes and packages that are annotated with {@link API}. + * Enumerates all classes, interfaces and packages that are annotated with {@link API} and their public methods and + * constructors. */ -public final class CompleteApiScraper { +final class CompleteApiScraper { + + private static final String TERASOLOGY_API_CLASS_CATEGORY = "terasology engine"; + private static final String EXTERNAL = "external"; + + private static final Logger logger = LoggerFactory.getLogger(CompleteApiScraper.class); + private CompleteApiScraper() { // Private constructor, utility class } @@ -44,7 +55,7 @@ public final class CompleteApiScraper { * @return Project's Packages, Interfaces, Classes and Methods * @throws Exception if the module environment cannot be loaded */ - public static StringBuffer getApi() throws Exception { + static StringBuffer getApi() throws Exception { ModuleManager moduleManager = ModuleManagerFactory.create(); ModuleEnvironment environment = moduleManager.getEnvironment(); ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); @@ -52,7 +63,6 @@ public final class CompleteApiScraper { Multimap api = Multimaps.newMultimap(new HashMap>(), ArrayList::new); for (Class apiClass : environment.getTypesAnnotatedWith(API.class)) { - //System.out.println("Processing: " + apiClass); boolean isPackage = apiClass.isSynthetic(); URL location; String category; @@ -66,7 +76,7 @@ public final class CompleteApiScraper { } if (location == null) { - System.out.println("Failed to get a class/package location, skipping " + apiClass); + logger.error("Failed to get a class/package location, skipping " + apiClass); continue; } @@ -75,38 +85,50 @@ public final class CompleteApiScraper { // Find out what jar it came from and consider that the category String categoryFragment = location.getPath(); - //System.out.println("category fragment as path: " + categoryFragment); + int bang = categoryFragment.lastIndexOf("!"); int hyphen = categoryFragment.lastIndexOf("-", bang); int slash = categoryFragment.lastIndexOf("/", hyphen); category = categoryFragment.substring(slash + 1, hyphen); - //System.out.println("category fragment pared down: " + category); - addToApi(isPackage, category, apiPackage, apiClass, api); + + + if (isPackage) { + api.put(category, apiPackage + " (PACKAGE)"); + } else { + addToApi(category, apiClass, api); + } break; case "file" : // If file based we know it is local so organize it like that - category = "terasology engine"; - addToApi(isPackage, category, apiPackage, apiClass, api); + category = TERASOLOGY_API_CLASS_CATEGORY; + if (isPackage) { + api.put(category, apiPackage + " (PACKAGE)"); + } else { + addToApi(category, apiClass, api); + } break; default : - System.out.println("Unknown protocol for: " + apiClass + ", came from " + location); + logger.error("Unknown protocol for: " + apiClass + ", came from " + location); } } - api.putAll("external", ExternalApiWhitelist.CLASSES.stream() + api.putAll(EXTERNAL, ExternalApiWhitelist.CLASSES.stream() .map(clazz->clazz.getName() + " (CLASS)").collect(Collectors.toSet())); - api.putAll("external", ExternalApiWhitelist.PACKAGES.stream() + api.putAll(EXTERNAL, ExternalApiWhitelist.PACKAGES.stream() .map(packagee->packagee + " (PACKAGE)").collect(Collectors.toSet())); - + //Puts the information in the StringBuffer StringBuffer stringApi = new StringBuffer(); - stringApi.append("# Modding API:\n"); for (String key : api.keySet()) { - stringApi.append("## " + key + "\n"); + stringApi.append("## "); + stringApi.append(key); + stringApi.append("\n"); for (String value : api.get(key)) { - stringApi.append("* " + value + "\n"); + stringApi.append("* "); + stringApi.append(value); + stringApi.append("\n"); } stringApi.append("\n"); } @@ -114,52 +136,57 @@ public final class CompleteApiScraper { return stringApi; } - private static void addToApi(boolean isPackage, String category, String apiPackage, Class apiClass, Multimap api){ - if (isPackage) { - //System.out.println("Local Package: " + apiPackage + ", came from " + location); - api.put(category, apiPackage + " (PACKAGE)"); + /** + * Adds interface or class and their methods and constructors to api + * are also added. + * @param category where the apiClass belongs + * @param apiClass the class or interface to be added + * @param api that maps category to classes/interface/methods + */ + private static void addToApi(String category, Class apiClass, Multimap api) { + + String className = apiClass.getName(); + String type; + if (apiClass.isInterface()) { + type = " (INTERFACE)"; } else { - //System.out.println("Local Class: " + apiClass + ", came from " + location); - String className = apiClass.getName(); - String type; - if(apiClass.isInterface()){ - type = " (INTERFACE)"; + int modifier = apiClass.getModifiers(); + if (Modifier.isAbstract(modifier)) { + type = " (ABSTRACT CLASS)"; } else { - int modifier = apiClass.getModifiers(); - if( Modifier.isAbstract(modifier)){ - type = " (ABSTRACT CLASS)"; - }else{ - type = " (CLASS)"; - } + type = " (CLASS)"; } - api.put(category, className + type); + } + api.put(category, className + type); - Constructor[] constructors = apiClass.getDeclaredConstructors(); - for(int i = 0; i < constructors.length; i++){ - api.put(category, " - " + constructors[i].getName() + " (CONSTRUCTOR)"); - api.put(category, " -- " + Arrays.toString(constructors[i].getParameterTypes()) + " (PARAMETERS)"); - } + //Add current apiClass's constructors + Constructor[] constructors = apiClass.getDeclaredConstructors(); + for (Constructor constructor : constructors) { + api.put(category, " - " + constructor.getName() + " (CONSTRUCTOR)"); + api.put(category, " -- " + Arrays.toString(constructor.getParameterTypes()) + " (PARAMETERS)"); + } + //Add current apiClass's methods + Method[] methods = apiClass.getDeclaredMethods(); + for (Method method: methods) { + if (!method.isDefault() && !method.isBridge() && !method.isSynthetic()) { - Method[] methods = apiClass.getDeclaredMethods(); - for(int i = 0; i < methods.length; i++){ - if(!methods[i].isDefault() && !methods[i].isBridge() && !methods[i].isSynthetic()){ - - // - int modifier = methods[i].getModifiers(); - if( Modifier.isAbstract(modifier)){ - type = " (ABSTRACT METHOD)"; - }else{ - type = " (METHOD)"; - } - // - api.put(category, " - " + methods[i].getName() + type); - api.put(category, " -- " + methods[i].getReturnType() + " (RETURN)"); - api.put(category, " -- " + Arrays.toString(methods[i].getParameterTypes()) + " (PARAMETERS)"); - api.put(category, " -- " + Arrays.toString(methods[i].getExceptionTypes()) + " (EXCEPTIONS)"); + //Check if it's an abstract method + int modifier = method.getModifiers(); + if (Modifier.isAbstract(modifier)) { + type = " (ABSTRACT METHOD)"; + } else { + type = " (METHOD)"; } + + //Adds method's information + api.put(category, " - " + method.getName() + type); + api.put(category, " -- " + method.getReturnType() + " (RETURN)"); + api.put(category, " -- " + Arrays.toString(method.getParameterTypes()) + " (PARAMETERS)"); + api.put(category, " -- " + Arrays.toString(method.getExceptionTypes()) + " (EXCEPTIONS)"); } } + } } diff --git a/engine-tests/src/test/java/org/terasology/documentation/apiScraper/util/ApiMethod.java b/engine-tests/src/test/java/org/terasology/documentation/apiScraper/util/ApiMethod.java index 5660ec255..654cf4437 100644 --- a/engine-tests/src/test/java/org/terasology/documentation/apiScraper/util/ApiMethod.java +++ b/engine-tests/src/test/java/org/terasology/documentation/apiScraper/util/ApiMethod.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 MovingBlocks + * Copyright 2018 MovingBlocks * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,9 +16,13 @@ package org.terasology.documentation.apiScraper.util; +import java.util.Objects; + +/** + * Saves informations about methods and constructors to be used at the ApiComparator class + */ public class ApiMethod { - //public static final String DEFAULT_VALUE = "DEFAULT"; private String className; private String name; @@ -26,7 +30,15 @@ public class ApiMethod { private String exceptionType; private String parametersType; - public ApiMethod(String className, String name, String returnType, String exceptionType, String parametersType){ + /** + * + * @param className Name of the class in which the method can be found + * @param name Name of the method + * @param returnType Return type of the method + * @param exceptionType List of exception types of the method + * @param parametersType List of the method's parameters' type + */ + public ApiMethod(String className, String name, String returnType, String exceptionType, String parametersType) { this.className = className; this.name = name; this.returnType = returnType; @@ -34,11 +46,8 @@ public class ApiMethod { this.parametersType = parametersType; } - /*public ApiMethod(){ - this(DEFAULT_VALUE,DEFAULT_VALUE,DEFAULT_VALUE,DEFAULT_VALUE,DEFAULT_VALUE); - }*/ - public String getClassName(){ + public String getClassName() { return className; } @@ -60,25 +69,25 @@ public class ApiMethod { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } ApiMethod apiMethod = (ApiMethod) o; - if (!getClassName().equals(apiMethod.getClassName())) return false; - if (!getName().equals(apiMethod.getName())) return false; - if (!getReturnType().equals(apiMethod.getReturnType())) return false; - if (!getExceptionType().equals(apiMethod.getExceptionType())) return false; - return getParametersType().equals(apiMethod.getParametersType()); + + return getClassName().equals(apiMethod.getClassName()) + && getName().equals(apiMethod.getName()) + && getReturnType().equals(apiMethod.getReturnType()) + && getExceptionType().equals(apiMethod.getExceptionType()) + && getParametersType().equals(apiMethod.getParametersType()); } @Override public int hashCode() { - int result = getClassName().hashCode(); - result = 31 * result + getName().hashCode(); - result = 31 * result + getReturnType().hashCode(); - result = 31 * result + getExceptionType().hashCode(); - result = 31 * result + getParametersType().hashCode(); - return result; + return Objects.hash(); } } From 8fca964f1ffbaa2986fbe4875d5ea5e3adc18118 Mon Sep 17 00:00:00 2001 From: Iaron Araujo Date: Fri, 13 Apr 2018 13:24:19 -0300 Subject: [PATCH 4/5] Refactored code's Javadoc and created constant for file names --- .gitignore | 3 ++ .../apiScraper/ApiComparator.java | 48 +++++++++---------- .../apiScraper/CompleteApiScraper.java | 3 -- .../apiScraper/util/ApiMethod.java | 1 - 4 files changed, 27 insertions(+), 28 deletions(-) diff --git a/.gitignore b/.gitignore index 597d6be80..7a3a0c34c 100644 --- a/.gitignore +++ b/.gitignore @@ -93,5 +93,8 @@ nuiEditorAutosave.json /natives/ config/metrics/ +# Ignore API files +API_file.txt +New_API_file.txt # Ignore weird stuff that might be obsolete diff --git a/engine-tests/src/test/java/org/terasology/documentation/apiScraper/ApiComparator.java b/engine-tests/src/test/java/org/terasology/documentation/apiScraper/ApiComparator.java index 878975fd4..110fce6a8 100644 --- a/engine-tests/src/test/java/org/terasology/documentation/apiScraper/ApiComparator.java +++ b/engine-tests/src/test/java/org/terasology/documentation/apiScraper/ApiComparator.java @@ -29,34 +29,38 @@ import java.util.HashMap; import java.util.Map; import java.util.List; - /** - * Generates a "New_API_file.txt" and compares it with the "API_file.txt" to detect major and minor version increases. - * Major increases: Deletion of class, new abstract method, method deletion, existing method's change of - * parameters types, exception types or return type. - * Minor increases: Creation of a new class and new method in an interface or abstract class, + * Detects API changes between two versions. */ public final class ApiComparator { + private static final String ORIGINAL_API_FILE = "API_file.txt"; + private static final String NEW_API_FILE = "New_API_file.txt"; + private ApiComparator() { } - + /* + * Generates a NEW_API_FILE and compares it with the ORIGINAL_API_FILE to detect major and minor version increases. + * Major increases: Deletion of class, new public abstract method, new interface public method, + * public method deletion, existing public method's change of parameters types, exception types or return type. + * Minor increases: Creation of a new class and new public method in an abstract class. + */ public static void main(String[] args) throws Exception { - try (BufferedReader br = new BufferedReader(new FileReader("API_file.txt"))) { + try (BufferedReader br = new BufferedReader(new FileReader(ORIGINAL_API_FILE))) { //Creating a map with the original api's data Map> originalApi = getApi(br); br.close(); //Generating "New_API_file.txt" - BufferedWriter writer = new BufferedWriter(new FileWriter(new File("New_API_file.txt"))); + BufferedWriter writer = new BufferedWriter(new FileWriter(new File(NEW_API_FILE))); writer.write(CompleteApiScraper.getApi().toString()); writer.flush(); writer.close(); - BufferedReader br2 = new BufferedReader(new FileReader("New_API_file.txt")); + BufferedReader br2 = new BufferedReader(new FileReader(NEW_API_FILE)); //Creating a map with the new api's data Map> newApi = getApi(br2); @@ -68,14 +72,12 @@ public final class ApiComparator { checkMethodChanges(originalApi, newApi); System.out.println("REPORT FINISHED"); } - } /** - * Reads an api file and puts it's information in a map to be used in the api comparison + * Reads an api file and puts its information in a map to be used in the api comparison * @param br BufferedReader containing an api file content - * @return A map with the api classes and interfaces as keys; - * Their methods and as a list of ApiMethods in the values + * @return A map with the api classes and interfaces as keys.Their methods and as a list of ApiMethods in the values * @throws Exception if the readLine fails. */ private static Map> getApi(BufferedReader br) throws Exception { @@ -135,13 +137,12 @@ public final class ApiComparator { System.out.println("MINOR INCREASE, ADDITION OF " + className); } } - } /** * Checks creation and deletion of methods, as well as existing method changes - * @param originalApi the original api generated from "API_file.txt" - * @param newApi the new apí generated from "New_API_file.txt" + * @param originalApi the original api generated from ORIGINAL_API_FILE + * @param newApi the new apí generated from NEW_API_FILE */ private static void checkMethodChanges(Map> originalApi, Map> newApi) { @@ -185,14 +186,18 @@ public final class ApiComparator { if (method2.getName().endsWith("(ABSTRACT METHOD)")) { System.out.println("MAJOR INCREASE, NEW ABSTRACT METHOD " + method2.getName() + " ON " + method2.getClassName()); } else { - System.out.println("MINOR INCREASE, NEW METHOD " + method2.getName() + " ON " + method2.getClassName()); + String minorOrMajor; + if (method2.getClassName().endsWith("(INTERFACE)")) { + minorOrMajor = "MAJOR"; + } else { + minorOrMajor = "MINOR"; + } + System.out.println(minorOrMajor + " INCREASE, NEW METHOD " + method2.getName() + " ON " + method2.getClassName()); } System.out.println("================================================================="); } } - } - } private static void checkMethodDeletion(Collection originalMethods, Collection newMethods) { @@ -232,7 +237,6 @@ public final class ApiComparator { System.out.println("MAJOR INCREASE, METHOD DELETION: " + method1.getName() + " ON " + method1.getClassName()); } } - } private static boolean isInterfaceOrAbstract(String className) { @@ -265,7 +269,6 @@ public final class ApiComparator { System.out.println("NEW: " + s2); System.out.println("================================================================="); } - } /** @@ -282,8 +285,5 @@ public final class ApiComparator { } } return new ApiMethod("", "", "", "", ""); - } - - } diff --git a/engine-tests/src/test/java/org/terasology/documentation/apiScraper/CompleteApiScraper.java b/engine-tests/src/test/java/org/terasology/documentation/apiScraper/CompleteApiScraper.java index c77720c63..5e0494e5f 100644 --- a/engine-tests/src/test/java/org/terasology/documentation/apiScraper/CompleteApiScraper.java +++ b/engine-tests/src/test/java/org/terasology/documentation/apiScraper/CompleteApiScraper.java @@ -132,7 +132,6 @@ final class CompleteApiScraper { } stringApi.append("\n"); } - return stringApi; } @@ -186,7 +185,5 @@ final class CompleteApiScraper { api.put(category, " -- " + Arrays.toString(method.getExceptionTypes()) + " (EXCEPTIONS)"); } } - - } } diff --git a/engine-tests/src/test/java/org/terasology/documentation/apiScraper/util/ApiMethod.java b/engine-tests/src/test/java/org/terasology/documentation/apiScraper/util/ApiMethod.java index 654cf4437..7d4d23a41 100644 --- a/engine-tests/src/test/java/org/terasology/documentation/apiScraper/util/ApiMethod.java +++ b/engine-tests/src/test/java/org/terasology/documentation/apiScraper/util/ApiMethod.java @@ -31,7 +31,6 @@ public class ApiMethod { private String parametersType; /** - * * @param className Name of the class in which the method can be found * @param name Name of the method * @param returnType Return type of the method From dabd9e3b768add77b5d6cc532a7d5ed511e500b3 Mon Sep 17 00:00:00 2001 From: Iaron Araujo Date: Sat, 14 Apr 2018 14:26:40 -0300 Subject: [PATCH 5/5] Interface's new default method and a non-abstract class new public method is now being reported --- .../apiScraper/ApiComparator.java | 31 ++++++++++++------- .../apiScraper/CompleteApiScraper.java | 6 ++++ 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/engine-tests/src/test/java/org/terasology/documentation/apiScraper/ApiComparator.java b/engine-tests/src/test/java/org/terasology/documentation/apiScraper/ApiComparator.java index 110fce6a8..e9878099b 100644 --- a/engine-tests/src/test/java/org/terasology/documentation/apiScraper/ApiComparator.java +++ b/engine-tests/src/test/java/org/terasology/documentation/apiScraper/ApiComparator.java @@ -96,10 +96,11 @@ public final class ApiComparator { aux = br.readLine(); while ((aux != null && (aux.endsWith("(METHOD)") || aux.endsWith("(CONSTRUCTOR)") - || aux.endsWith("(ABSTRACT METHOD)")))) { + || aux.endsWith("(ABSTRACT METHOD)") + || aux.endsWith("(DEFAULT METHOD)")))) { //Checks if its a method or constructor - if (aux.endsWith("(METHOD)") || aux.endsWith("(ABSTRACT METHOD)")) { + if (aux.endsWith("(METHOD)") || aux.endsWith("(ABSTRACT METHOD)") || aux.endsWith("(DEFAULT METHOD)")) { String returnType = br.readLine(); String parameters = br.readLine(); String exceptionType = br.readLine(); @@ -182,17 +183,25 @@ public final class ApiComparator { found = true; } } - if (!found && isInterfaceOrAbstract(method2.getClassName())) { - if (method2.getName().endsWith("(ABSTRACT METHOD)")) { - System.out.println("MAJOR INCREASE, NEW ABSTRACT METHOD " + method2.getName() + " ON " + method2.getClassName()); - } else { - String minorOrMajor; - if (method2.getClassName().endsWith("(INTERFACE)")) { - minorOrMajor = "MAJOR"; + if (!found) { + if (isInterfaceOrAbstract(method2.getClassName())) { + if (method2.getName().endsWith("(ABSTRACT METHOD)")) { + System.out.println("MAJOR INCREASE, NEW ABSTRACT METHOD " + method2.getName() + " ON " + method2.getClassName()); } else { - minorOrMajor = "MINOR"; + String minorOrMajor; + if (method2.getClassName().endsWith("(INTERFACE)")) { + if (method2.getName().endsWith("(DEFAULT METHOD)")) { + minorOrMajor = "MINOR"; + } else { + minorOrMajor = "MAJOR"; + } + } else { + minorOrMajor = "MINOR"; + } + System.out.println(minorOrMajor + " INCREASE, NEW METHOD " + method2.getName() + " ON " + method2.getClassName()); } - System.out.println(minorOrMajor + " INCREASE, NEW METHOD " + method2.getName() + " ON " + method2.getClassName()); + } else { + System.out.println("MINOR INCREASE, NEW METHOD " + method2.getName() + " ON " + method2.getClassName()); } System.out.println("================================================================="); } diff --git a/engine-tests/src/test/java/org/terasology/documentation/apiScraper/CompleteApiScraper.java b/engine-tests/src/test/java/org/terasology/documentation/apiScraper/CompleteApiScraper.java index 5e0494e5f..d006bc8b5 100644 --- a/engine-tests/src/test/java/org/terasology/documentation/apiScraper/CompleteApiScraper.java +++ b/engine-tests/src/test/java/org/terasology/documentation/apiScraper/CompleteApiScraper.java @@ -183,7 +183,13 @@ final class CompleteApiScraper { api.put(category, " -- " + method.getReturnType() + " (RETURN)"); api.put(category, " -- " + Arrays.toString(method.getParameterTypes()) + " (PARAMETERS)"); api.put(category, " -- " + Arrays.toString(method.getExceptionTypes()) + " (EXCEPTIONS)"); + } else if (method.isDefault() && apiClass.isInterface()) { + api.put(category, " - " + method.getName() + " (DEFAULT METHOD)"); + api.put(category, " -- " + method.getReturnType() + " (RETURN)"); + api.put(category, " -- " + Arrays.toString(method.getParameterTypes()) + " (PARAMETERS)"); + api.put(category, " -- " + Arrays.toString(method.getExceptionTypes()) + " (EXCEPTIONS)"); } + } } }