Junit5 TestReporter - java

I was trying understand TestReporter in Junit5
#BeforeEach
void beforeEach(TestInfo testInfo) {
}
#ParameterizedTest
#ValueSource(strings = "foo")
void testWithRegularParameterResolver(String argument, TestReporter testReporter) {
testReporter.publishEntry("argument", argument);
}
#AfterEach
void afterEach(TestInfo testInfo) {
// ...
}
what is the use of publishEntry in TestReporter,
Can someone explain me.. Thanks in Advance..

"TestReporter" in conjunction with "TestInfo" gives an instance of the current test, this way you can get info about your actual test. and then publish it, in this example used as kind of logger.
StringBuffer is used for his mutable, fast, and synchonized characteristics, required for a test.
public class TestReporterTest {
StringBuffer sbtags = new StringBuffer();
StringBuffer displayName = new StringBuffer();
StringBuffer className = new StringBuffer();
StringBuffer methodName = new StringBuffer();
#BeforeEach
void init(TestInfo testInfo) {
className.delete( 0, className.length());
className.append( testInfo.getTestClass().get().getName());
displayName.delete( 0, displayName.length());
displayName.append( testInfo.getDisplayName());
methodName.delete( 0, methodName.length());
methodName.append( testInfo.getTestMethod().get().getName());
}
#Test
#DisplayName("testing on reportSingleValue")
void reportSingleValue(TestReporter testReporter) {
testReporter.publishEntry( "className : " + className);
testReporter.publishEntry( "displayName: " + displayName);
testReporter.publishEntry("methodName : " + methodName);
testReporter.publishEntry("algun mensaje de estatus");
}
#Test
void reportKeyValuePair(TestReporter testReporter) {
testReporter.publishEntry( "className : " + className);
testReporter.publishEntry( "displayName: " + displayName);
testReporter.publishEntry("methodName : " + methodName);
testReporter.publishEntry("una Key", "un Value");
}
#Test
void reportMultiKeyValuePairs(TestReporter testReporter) {
Map<String, String> map = new HashMap<>();
map.put("Fast and Furious 8","2018");
map.put("Matrix","1999");
testReporter.publishEntry( "className : " + className);
testReporter.publishEntry( "displayName: " + displayName);
testReporter.publishEntry("methodName : " + methodName);
testReporter.publishEntry(map);
}
}
Running the Test
timestamp = 2019-11-22T12:02:45.898, value = className : TestReporterTest
timestamp = 2019-11-22T12:02:45.904, value = displayName: testing on reportSingleValue
timestamp = 2019-11-22T12:02:45.904, value = methodName : reportSingleValue
timestamp = 2019-11-22T12:02:45.904, value = algun mensaje de estatus
timestamp = 2019-11-22T12:02:45.919, value = className : TestReporterTest
timestamp = 2019-11-22T12:02:45.920, value = displayName: reportMultiKeyValuePairs(TestReporter)
timestamp = 2019-11-22T12:02:45.920, value = methodName : reportMultiKeyValuePairs
timestamp = 2019-11-22T12:02:45.921, Fast and Furious 8 = 2018, Matrix = 1999
timestamp = 2019-11-22T12:02:45.924, value = className : TestReporterTest
timestamp = 2019-11-22T12:02:45.925, value = displayName: reportKeyValuePair(TestReporter)
timestamp = 2019-11-22T12:02:45.925, value = methodName : reportKeyValuePair
timestamp = 2019-11-22T12:02:45.925, una Key = un Value

Apart from the previous answers, When we are writing junit test scripts if we want to get some information out of the process we normally do System.out.println which is not recommended in corporate/enterprise world. Specially in code reviews, peer reviews we are advised to remove all the System.out.println from the code base. So in the junit world if we want to push or publish out of the scripts we are advised to use TestReporter publishEntry() method. With the combination of TestInfo we could read several information out of the original junit scripts.
Hope this facts also support your question.

The method name suggests you are publishing a new entry to the report, which is supported by the Java Doc for 5.3.0
https://junit.org/junit5/docs/current/api/org/junit/jupiter/api/TestReporter.html
This would allow you to add additional, useful information to the test report; perhaps you would like to add what the tests initial conditions are to the report or some environmental information.

Related

Criteria inside JsonPath

I'm trying to filter jsonPath by type. To extract Integers
I would expect this will return nothing as 'xx' is not integer:
JsonPath.read("{'status': 'xx'}", "$.status", Criteria.where(".status").is(Integer.class));
Similarly this
JsonPath.read("{'status': 'xx'}", "$.status", Criteria.where(".status").eq(200));
both cases returns String = "xx"
I would expect it to return either null or empty string as it doesn't match number 200.
Correct #i.bondarenko, I would simply add - for the first check of searching whether status value is an Integer - that he/she should use a Pattern to pass to the filter, like for example
Pattern numberPattern = Pattern.compile("\\d+");
Filter filter = filter(where("status").regex(numberPattern));
Object test = JsonPath.read("{\"status\": \"xx\"}", "$[?].status", filter);
System.out.println("Test : " + test);
That will print Test : []
UPDATED
It is a JSONArray indeed, therefore, you already have the Integers of your whole JSON in that array (if they exist). For example,
Pattern numberPattern = Pattern.compile("\\d+");
Filter filter = filter(where("status").regex(numberPattern));
net.minidev.json.JSONArray test = JsonPath.read("{\"status\": 300}", "$[?].status", filter);
if (!test.isEmpty()) {
for (Object object : test) {
System.out.println("Test : " + object.toString());
}
}
So, there is no need to add try-catch, it is enough to just check the size of your JSONArray result
You should use $[?].status as json path for criteria.
Also where("field").is("value") accept value but not a class.
You could have a look at implementation of Criteria.eq(...)
public Criteria eq(Object o) {
return is(o);
}
Here is the code:
public static void main(String[] args) {
Criteria criteria = Criteria.where("status").gt(10);
Object read = JsonPath.read("{'status': 18}", "$[?].status", criteria);
System.out.println("First: " + read);
read = JsonPath.read("{'status': 2}", "$[?].status", criteria);
System.out.println("Second: " + read);
criteria = Criteria.where("status").is("value");
read = JsonPath.read("{'status': 'value'}", "$[?].status", criteria);
System.out.println("Third: " + read);
criteria = Criteria.where("status").is("value");
read = JsonPath.read("{'status': 'NON'}", "$[?].status", criteria);
System.out.println("Third: " + read);
}
Output:
First: [18]
Second: []
Third: ["value"]
Third: []

Aspect breaking bytecode on specific class

I'm new to AOP, I've created an aspect to trace all methods or classes marked with #Trace annotation. I'm using compile time weaving. (Java 8, Aspectj 1.8, Spring 4)
TraceAspect.java
#Aspect
public class TraceAspect {
private static Map<String, Integer> threadMap = new HashMap<>();
#Pointcut("#within(Trace) || #annotation(Trace)")
void annotated(){}
#Around("annotated() && execution(* *(..))")
public Object trace(final ProceedingJoinPoint joinPoint) throws Throwable {
String threadName = Thread.currentThread().getName();
String indent = indent(inThread(threadName));
System.out.println(threadName + " : " + indent + "-> " + joinPoint.getSignature().toString());
long start = System.nanoTime();
Object ret = joinPoint.proceed();
long end = System.nanoTime();
System.out.println(threadName + " : " + indent + "<- " + joinPoint.getSignature().toString() + " ended (took " + (end - start) + " nanoseconds)");
outThread(threadName);
return ret;
}
private String indent(int depth) {
String result = "";
for (int index = 0; index < depth; index++) {
result += " ";
}
return result;
}
private int inThread(String threadName) {
if (threadMap.get(threadName) == null) {
threadMap.put(threadName, 0);
}
int stackDepth = threadMap.get(threadName) + 1;
threadMap.put(threadName, stackDepth);
return stackDepth;
}
private void outThread(String threadName) {
int stackDepth = threadMap.get(threadName) - 1;
threadMap.put(threadName, stackDepth);
}
}
The CryptsyExchange.java (which is a Spring Bean) when marked with #Trace, classloader throws ClassFormat error on build(..) method while initializing that bean in application context...
CryptsyExchange.java
#Trace
public class CryptsyExchange {
private static final Logger LOGGER = LoggerFactory.getLogger(CryptsyExchange.class);
private DataService dataService;
private Configuration config;
private Converter converter;
private Exchange exchange;
private List<CryptsyAccount> accounts = Collections.synchronizedList(new LinkedList<>());
private CryptsyAccount defaultAccount;
public static CryptsyExchange build(String name, DataService dataService, ConfigPathBuilder pathBuilder) {
condition(notNullOrEmpty(name) && notNull(dataService, pathBuilder));
CryptsyExchange cryptsyExchange = new CryptsyExchange();
cryptsyExchange.dataService = dataService;
// Loading configuration
final Configuration configuration = Configuration.load(pathBuilder.getExchangeConfigPath(name));
cryptsyExchange.config = configuration;
// Retrieve corresponding exchange from datastore
cryptsyExchange.exchange = dataService.registerExchange(cryptsyExchange.config.getString("exchange"));
// Get accounts from configuration
Map<String, Map<String, String>> accounts = configuration.getMap("accounts");
// Initialize accounts
accounts.entrySet().stream().forEach((entry) -> {
String key = entry.getKey();
Map<String, String> accountMap = entry.getValue();
// Retrieve corresponding datastore account
Account account = dataService.registerAccount(cryptsyExchange.exchange, key);
// Initialize cryptsy specific account
CryptsyAccount cryptsyAccount = new CryptsyAccount(account, accountMap.get("key"), accountMap.get("secret"));
cryptsyExchange.accounts.add(cryptsyAccount);
if (notNull(accountMap.get("isDefault")) && Boolean.valueOf(accountMap.get("isDefault"))) {
cryptsyExchange.defaultAccount = cryptsyAccount;
}
});
// Initializing Converter
cryptsyExchange.converter = cryptsyExchange.new Converter();
// Recover associations from configuration
Map<String, String> exchangeCurrencyToCurrency = configuration.getMap("exchangeCurrencyToCurrency");
Set<String> markedForRemoval = new HashSet<>();
exchangeCurrencyToCurrency.entrySet().stream().forEach((entry) -> {
String cryptsyCurrencyCode = entry.getKey();
String currencySymbol = entry.getValue();
com.jarvis.data.entity.Currency currency = dataService.getCurrency(currencySymbol);
if (notNull(currency)) {
cryptsyExchange.converter.associateCurrency(currency, cryptsyCurrencyCode);
} else {
LOGGER.debug("associated currency [" + currencySymbol + "] does not exist in database, removing from configuration");
markedForRemoval.add(cryptsyCurrencyCode);
}
});
// Removing currency associations missing from database
if (!markedForRemoval.isEmpty()) {
markedForRemoval.forEach((currency) -> configuration.remove("exchangeCurrencyToCurrency", currency));
}
Map<String, String> exchangeMarketToMarket = configuration.getMap("exchangeMarketToMarket");
markedForRemoval.clear();
exchangeMarketToMarket.entrySet().stream().forEach((entry) -> {
String cryptsyMarketId = entry.getKey();
String marketName = entry.getValue();
Market market = dataService.getMarket(marketName);
if (notNull(market)) {
cryptsyExchange.converter.associateMarket(market, Integer.valueOf(cryptsyMarketId));
} else {
LOGGER.debug("associated market [+" + marketName + "] does not exist, removing from configuration");
markedForRemoval.add(cryptsyMarketId);
}
});
// Removing market associations missing from database
if (!markedForRemoval.isEmpty()) {
markedForRemoval.forEach((market) -> configuration.remove("exchangeMarketToMarket", market));
}
// Update configuration
configuration.save();
return cryptsyExchange;
}
// Lot of other code there
}
And of course the stackTrace:
Exception in thread "main" java.lang.ClassFormatError: Illegal local variable table length 288 in method com.jarvis.exchange.cryptsy.CryptsyExchange.build_aroundBody0(Ljava/lang/String;Lcom/jarvis/data/service/DataService;Lcom/jarvis/util/ConfigPathBuilder;Lorg/aspectj/lang/JoinPoint;)Lcom/jarvis/exchange/cryptsy/CryptsyExchange;
at java.lang.Class.getDeclaredMethods0(Native Method)
at java.lang.Class.privateGetDeclaredMethods(Class.java:2688)
at java.lang.Class.getDeclaredMethods(Class.java:1962)
at org.springframework.util.ReflectionUtils.doWithMethods(ReflectionUtils.java:467)
at org.springframework.util.ReflectionUtils.doWithMethods(ReflectionUtils.java:451)
at org.springframework.util.ReflectionUtils.getUniqueDeclaredMethods(ReflectionUtils.java:512)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getTypeForFactoryMethod(AbstractAutowireCapableBeanFactory.java:663)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.predictBeanType(AbstractAutowireCapableBeanFactory.java:593)
at org.springframework.beans.factory.support.AbstractBeanFactory.isFactoryBean(AbstractBeanFactory.java:1396)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doGetBeanNamesForType(DefaultListableBeanFactory.java:382)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(DefaultListableBeanFactory.java:353)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:82)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:609)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:464)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:83)
at com.jarvis.Jarvis.<clinit>(Jarvis.java:10)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:259)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:116)
I've tried this on any other class on my project (annotation can be applied on Type or method) and it worked, but exactly with this bean build method I'm facing issues and can't find any workaround. Maybe current support for Java 8 by Aspectj is buggy and it actually corrupts bytecode. Or perhaps there is something wrong I've done there?
Some questions:
Do you use full-blown AspectJ or just Spring AOP with #AspectJ syntax?
Do you compile with source/target level 1.8 or maybe still 1.7?
Does the exact same code work with Java 7 and AspectJ 1.8.0?
If not, does it work with Java 7 and AspectJ 1.7.4?
Can you please provide a stand-alone minimal code sample reproducing the problem? Maybe even on GitHub with a Maven build?) Many of your referenced classes are invisible to me, so I cannot reproducte it.
AJ 1.8.0 is brandnew and some of its problems are actually caused by ECJ (Eclipse Java compiler). Some have already been fixed, so you could try a current developer build (select "Last Known Good developer build".
Update:
I was able to reproduce the problem with a small code sample, independent of any other classes. The problem is not annotations or simple forEach lambdas, but obviously nested forEach lambdas.
I filed an AspectJ bug on http://bugs.eclipse.org/bugs/show_bug.cgi?id=435446.
Update 2:
The bug ticket also describes how to work around the problem by excluding lambda calls from the pointcut.
Another workaround I just found is to run the JVM with parameter -noverify.
Update 3:
The bugfix is done and available as a development build. It will be part of the upcoming AspectJ 1.8.1.
I've found that the problem comes from lambda expression usage :(
replacing all lambdas with regular for fixes the problem
exchangeCurrencyToCurrency.entrySet().stream().forEach((entry) -> {
String cryptsyCurrencyCode = entry.getKey();
String currencySymbol = entry.getValue();
com.jarvis.data.entity.Currency currency = dataService.getCurrency(currencySymbol);
if (notNull(currency)) {
associateCurrency(currency, cryptsyCurrencyCode);
} else {
LOGGER.debug("associated currency [" + currencySymbol + "] does not exist in database, removing from configuration");
markedForRemoval.add(cryptsyCurrencyCode);
}
});
result
for(Map.Entry<String, String> entry : exchangeCurrencyToCurrency.entrySet()){
String cryptsyCurrencyCode = entry.getKey();
String currencySymbol = entry.getValue();
com.jarvis.data.entity.Currency currency = dataService.getCurrency(currencySymbol);
if (notNull(currency)) {
associateCurrency(currency, cryptsyCurrencyCode);
} else {
LOGGER.debug("associated currency [" + currencySymbol + "] does not exist in database, removing from configuration");
markedForRemoval.add(cryptsyCurrencyCode);
}
}

Create Custom InputFormat of ColumnFamilyInputFormat for cassandra

I am working on a project, using cassandra 1.2, hadoop 1.2
I have created my normal cassandra mapper and reducer, but I want to create my own Input format class, which will read the records from cassandra, and I'll get the desired column's value, by splitting that value using splitting and indexing ,
so, I planned to create custom Format class. but I'm confused and not able to know, how would I make it? What classes are to be extend and implement, and how I will able to fetch the row key, column name, columns value etc.
I have my Mapperclass as follow:
public class MyMapper extends
Mapper<ByteBuffer, SortedMap<ByteBuffer, IColumn>, Text, Text> {
private Text word = new Text();
MyJDBC db = new MyJDBC();
public void map(ByteBuffer key, SortedMap<ByteBuffer, IColumn> columns,
Context context) throws IOException, InterruptedException {
long std_id = Long.parseLong(ByteBufferUtil.string(key));
long newSavePoint = 0;
if (columns.values().isEmpty()) {
System.out.println("EMPTY ITERATOR");
sb.append("column_N/A" + ":" + "N/A" + " , ");
} else {
for (IColumn cell : columns.values()) {
name = ByteBufferUtil.string(cell.name());
String value = null;
if (name.contains("int")) {
value = String.valueOf(ByteBufferUtil.toInt(cell.value()));
} else {
value = ByteBufferUtil.string(cell.value());
}
String[] data = value.toString().split(",");
// if (data[0].equalsIgnoreCase("login")) {
Long[] dif = getDateDiffe(d1, d2);
// logics i want to perform inside my custominput class , rather here, i just want a simple mapper class
if (condition1 && condition2) {
myhits++;
sb.append(":\t " + data[0] + " " + data[2] + " "+ data[1] /* + " " + data[3] */+ "\n");
newSavePoint = d2;
}
}
sb.append("~" + like + "~" + newSavePoint + "~");
word.set(sb.toString().replace("\t", ""));
}
db.setInterval(Long.parseLong(ByteBufferUtil.string(key)), newSavePoint);
db.setHits(Long.parseLong(ByteBufferUtil.string(key)), like + "");
context.write(new Text(ByteBufferUtil.string(key)), word);
}
I want to decrease my Mapper Class logics, and want to perform same calculations on my custom input class.
Please help, i wish for the positive r4esponse from stackies...
You can do the intended task by moving the Mapper logic to your custom input class (as you have indicated already)
I found this nice post which explains a similar problem statement as you have. I think it might solve your problem.

Query TestSet Json returns wrong list of test cases in Rally Rest API

When I request "TestSet" query to fetch TestCases in a specific test set.
But, the fetched TestCases contains the full list of test cases in the project not in the specific test set.
QueryRequest queryTestSet = new QueryRequest("TestSet");
queryTestSet.setFetch(new Fetch("Name", "Project", "TestCases"));
queryTestSet.setQueryFilter(new QueryFilter("name", "=", "TestSet_Name"));
QueryResponse responseTestSet = RallyRestAPI.getAPI().query(queryTestSet);
JsonArray testcasesJson = responseTestSet.getResults().get(0).getAsJsonObject().getAsJsonArray("TestCases");
For example, there are total 500 test cases in the project
I added test cases and results of "Automated" test cases in a TestSet (300 test cases)
Then, I request the query above, the size of "testcasesJson" returns 500 and it includes full list of test cases.
How can I read the test cases that only added in a TestSet?
Rally Rest JAR Version: rally-rest-api-1.0.7.jar
This code example that uses 2.0.4 jar returned only test cases associated with a test set:
public class GetTCofTS {
public static void main(String[] args) throws Exception {
String host = "https://rally1.rallydev.com";
String username = "user#co.com";
String password = "secret";
String applicationName = "RESTExampleFindTestCasesOfTestSet";
String workspaceRef = "/workspace/1111";
String projectRef = "/project/2222";
String wsapiVersion = "1.43";
RallyRestApi restApi = null;
try {
restApi = new RallyRestApi(
new URI(host),
username,
password);
restApi.setApplicationName(applicationName);
QueryRequest testSetRequest = new QueryRequest("TestSet");
testSetRequest.setWorkspace(workspaceRef);
restApi.setWsapiVersion(wsapiVersion);
testSetRequest.setFetch(new Fetch(new String[] {"Name", "TestCases", "FormattedID"}));
testSetRequest.setQueryFilter(new QueryFilter("Name", "=", "someTS"));
QueryResponse testSetQueryResponse = restApi.query(testSetRequest);
System.out.println("Successful: " + testSetQueryResponse.wasSuccessful());
System.out.println("Size: " + testSetQueryResponse.getTotalResultCount());
for (int i=0; i<testSetQueryResponse.getResults().size();i++){
JsonObject testSetJsonObject = testSetQueryResponse.getResults().get(i).getAsJsonObject();
System.out.println("Name: " + testSetJsonObject.get("Name") + " ref: " + testSetJsonObject.get("_ref").getAsString() + " Test Cases: " + testSetJsonObject.get("TestCases"));
int numberOfTestCases = testSetJsonObject.get("TestCases").getAsJsonArray().size();
System.out.println(numberOfTestCases);
if(numberOfTestCases>0){
for (int j=0;j<numberOfTestCases;j++){
System.out.println(testSetJsonObject.get("TestCases").getAsJsonArray().get(j).getAsJsonObject().get("FormattedID"));
}
}
}
} finally {
if (restApi != null) {
restApi.close();
}
}
}
}

Refactor parameter names programmatically

Using eclipse's jdt refactoring framework, I am trying to convert two different code bases to the same names. They are almost identical codebases except that names are different.
Function/Field/Class renaming works fine, but when it comes to parameters it yells at me that the workbench is not created yet. However i'm trying to do this in a headless manor.
private void refactor(String task, IJavaElement element, String new_name) throws CoreException
{
RefactoringStatus status = new RefactoringStatus();
RefactoringContribution contrib = RefactoringCore.getRefactoringContribution(task);
RenameJavaElementDescriptor rnDesc = (RenameJavaElementDescriptor)contrib.createDescriptor();
rnDesc.setFlags(JavaRefactoringDescriptor.JAR_MIGRATION | JavaRefactoringDescriptor.JAR_REFACTORING);
rnDesc.setProject(element.getJavaProject().getProject().getName());
rnDesc.setUpdateReferences(true);
rnDesc.setJavaElement(element);
rnDesc.setNewName(new_name);
Refactoring ref = rnDesc.createRefactoring(status);
ref.checkInitialConditions(NULL_MON);
ref.checkFinalConditions(NULL_MON);
Change change = ref.createChange(NULL_MON);
change.perform(NULL_MON);
}
This works fine:
for (IMethod method : type.getMethods())
{
refactor(IJavaRefactorings.RENAME_METHOD, method, {new name});
}
This does not:
for (IMethod method : type.getMethods())
{
for (ILocalVariable param : method.getParameters())
{
refactor(IJavaRefactorings.RENAME_LOCAL_VARIABLE, param, {new name});
}
}
And the error, not really helpful as I said I need to do this in a headless manor {so can't make workbench}
java.lang.IllegalStateException: Workbench has not been created yet.
at org.eclipse.ui.PlatformUI.getWorkbench(PlatformUI.java:92)
at org.eclipse.jdt.internal.ui.javaeditor.ASTProvider.install(ASTProvider.java:245)
at org.eclipse.jdt.internal.ui.javaeditor.ASTProvider.<init>(ASTProvider.java:236)
at org.eclipse.jdt.internal.ui.JavaPlugin.getASTProvider(JavaPlugin.java:710)
at org.eclipse.jdt.ui.SharedASTProvider.getAST(SharedASTProvider.java:128)
at org.eclipse.jdt.internal.corext.refactoring.util.RefactoringASTParser.parseWithASTProvider(RefactoringASTParser.java:119)
at org.eclipse.jdt.internal.corext.refactoring.rename.RenameLocalVariableProcessor.initAST(RenameLocalVariableProcessor.java:231)
at org.eclipse.jdt.internal.corext.refactoring.rename.RenameLocalVariableProcessor.checkInitialConditions(RenameLocalVariableProcessor.java:218)
at org.eclipse.ltk.core.refactoring.participants.ProcessorBasedRefactoring.checkInitialConditions(ProcessorBasedRefactoring.java:203)
UPDATE: Made some progress, now I can refactor functions that are not overrides. But any function that overrides another or an interface screws up:
F_ARGUMENTS = JavaRefactoringDescriptor.class.getDeclaredField("fArguments");
F_ARGUMENTS.setAccessible(true);
private void refactor(IMethod method, String[] names) throws CoreException
{
/* My attempt to fix the interface issues, causes duplicate functions instead of renaming the parameters
IMethod parent = null;
if (method.getDeclaringType().isInterface())
{
parent = MethodChecks.overridesAnotherMethod(method, method.getDeclaringType().newSupertypeHierarchy(NULL_MON));
}
else if (MethodChecks.isVirtual(method))
{
ITypeHierarchy hierarchy = method.getDeclaringType().newTypeHierarchy(NULL_MON);
parent = MethodChecks.isDeclaredInInterface(method, hierarchy, NULL_MON);
if (parent == null)
{
parent = MethodChecks.overridesAnotherMethod(method, hierarchy);
}
}
parent = (parent == null ? method : parent);
if (!method.equals(parent))
{
refactor(parent, names);
return;
}*/
String task = IJavaRefactorings.CHANGE_METHOD_SIGNATURE;
RefactoringStatus status = new RefactoringStatus();
ChangeMethodSignatureRefactoringContribution contrib = (ChangeMethodSignatureRefactoringContribution)RefactoringCore.getRefactoringContribution(task);
ChangeMethodSignatureDescriptor desc = (ChangeMethodSignatureDescriptor)contrib.createDescriptor();
desc.setFlags(JavaRefactoringDescriptor.JAR_MIGRATION |
JavaRefactoringDescriptor.JAR_REFACTORING |
RefactoringDescriptor.MULTI_CHANGE |
RefactoringDescriptor.STRUCTURAL_CHANGE);
Map<String, String> args = null;
try
{
args = (Map<String, String>)F_ARGUMENTS.get(desc);
}
catch (Exception e)
{
e.printStackTrace();
}
String project = method.getJavaProject().getProject().getName();
desc.setProject(method.getJavaProject().getProject().getName());
args.put("input", JavaRefactoringDescriptorUtil.elementToHandle(project, method));
args.put("name", method.getElementName());
args.put("deprecate", "false");
args.put("delegate", "true");
boolean changed = false;
int x = 0;
for (ILocalVariable param : method.getParameters())
{
if (!param.getElementName().equals(names[x]))
{
changed = true;
}
String type = "String"; //Doesn't seem to actually matter as long as they are both the same
String info = type + " " + param.getElementName() + " " + x + " " +
type + " " + names[x] + " false";
args.put("parameter" + (x + 1), info);
x++;
}
if (changed)
{
refactor(desc.createRefactoring(status));
}
}
This is what I came up with:
ChangeSignatureProcessor changeSignatureProcessor = new ChangeSignatureProcessor((IMethod) node.resolveBinding().getJavaElement());
ParameterInfo info=new ParameterInfo("FormContext", "formContext", ParameterInfo.INDEX_FOR_ADDED);
info.setDefaultValue("formContext");
changeSignatureProcessor.getParameterInfos().add(0,info);
RefactoringStatus status = new RefactoringStatus();
CheckConditionsContext context= new CheckConditionsContext();
context.add(new ValidateEditChecker(null));
context.add(new ResourceChangeChecker());
changeSignatureProcessor.checkInitialConditions(monitor);
changeSignatureProcessor.checkFinalConditions(monitor,context);
changeSignatureProcessor.createChange(monitor).perform(monitor);

Categories

Resources