Its me again...with the same context. I ran a test file in Dr.Java for my constructor. It is about reading a file in the directory to test. Here is part of content in test file:
public void cw_println_file1 () throws Exception {
String actual, msg, expect;
String filename;
CensoredWriter out;
for(int i=0; i<fileLines.length; i++){
String [][] test = fileLines[i];
String censor = test[0][0];
String [] outLines = test[1];
String [] expectLines = test[2];
filename = String.format("testfile_%d.txt",i);
out = new CensoredWriter(filename,censor);
for(int j=0; j<outLines.length; j++) {
out.println(outLines[j]);
}
out.close();
assertLines(expectLines, filename);
}
}
I got an error message like this:
File: C:\Users\jiangbuyun\Desktop\lab5\distrib-lab05\Lab05Tests.java [line: 137]
Failure: org.junit.ComparisonFailure: File testfile_1.txt Line 1
Expect: A %!^##er-scooper, or %!^## scoop, is a device used to pick up animal
Actual: A %!^##er-scooper, or %!^## scoop, is a device used to pick up animalfeces from public places and yards, particularly those ofdogs. %!^*##er-scooper devices often have a bag or bag attachment.
Expected:<...ed to pick up animal[]>
but was:<...ed to pick up animal[feces from public places and yards, particularly those ofdogs. %!^*##er-scooper devices often have a bag or bag attachment.]
I found it is supposed to stop at the brackets after "animal" and nothing is between the two brackets, but it actually continued to read text and put it into the brackets.
public class CensoredWriter extends PrintWriter {
String censored;
public CensoredWriter(OutputStream o, String c) {
super(o);
this.censored = c;
}
public CensoredWriter(File f, String c) throws Exception {
super(f);
this.censored = c;
}
public CensoredWriter(String s, String c) throws Exception {
super(s);
this.censored = c;
}
public String transform(String s) {
String a = s.replaceAll(censored, "%!^*##");
return a;
}
#Override
public void print(String s) {
super.print(transform(s));
}
#Override
public void println(String s) {
print(s);
flush();
}
}
From what you posted, the issue is that you seem to replace censored with "%!^*##", so that part of the implementation works. However, stopping after animal is the part you haven't implemented yet.
By the way, when asking questions, try to provide the necessary context. For example, I suppose your method cw_println_file1 is a test (#Test) method -- it would help to see that without guessing. Also, if your method requires some stuff from a #Before method (eg. fileLines seems to be declared somewhere else), it might help for the reader to understand your problem to see those required parts as well.
Related
This question is kind of continuation of one previously asked in Preserving parameter/argument names in compiled java classes and the answer is accepted, but the solution seems to be not working.
With ad-hoc built JDK 19 (having exposed Executable.hasRealParameterData()) I take the code
public class Main {
public static void main(String[] args) throws NoSuchMethodException {
Method foo = Main.class.getMethod("foo", String.class, int.class);
System.out.println(foo.hasRealParameterData());
}
public void foo(String parameter1, int parameter2) {}
}
and compile it with
% javac Main.java
Then I run compiled Java class and it prints false into console. This is fine because decompiled Main class looks like
public class Main {
public Main() {}
public static void main(String[] var0) throws NoSuchMethodException {
Method var1 = Main.class.getMethod("foo", String.class, Integer.TYPE);
System.out.println(var1.hasRealParameterData());
}
public void foo(String var1, int var2) {} // parameter names are not 'real'
}
i.e. parameter names are synthetic.
This behaviour is understandable.
Then I take the same Java sources and recompile the class with
javac -g:vars Main.java
I run the same code again and again it prints false to console. This puzzles me, because now the compiled code looks different:
public class Main {
public Main() {}
public static void main(String[] args) throws NoSuchMethodException {
Method foo = Main.class.getMethod("foo", String.class, Integer.TYPE);
System.out.println(foo.hasRealParameterData());
}
public void foo(String parameter1, int parameter2) {} // parameter names are 'real'
}
Same happens if for recompilation I use plain -g flag (generates all auxiliary data).
Now let's stop calling JDK's private API and rely only on the methods available out-of-the-box, e.g. Parameter.isNamePresent() (this one calls Executable.hasRealParameterData() under the hood):
public static void main(String[] args) throws NoSuchMethodException {
Method foo = Main.class.getMethod("foo", String.class, int.class);
Parameter parameter1 = foo.getParameters()[0];
Parameter parameter2 = foo.getParameters()[1];
System.out.println(parameter1.isNamePresent());
System.out.println(parameter2.isNamePresent());
}
public void foo(String parameter1, int parameter2) {}
And again, no matter how I compile the sources, this code prints false false.
The problem here is that Executable.hasRealParameterData() calls native method getParameters0() implemented like:
JVM_ENTRY(jobjectArray, JVM_GetMethodParameters(JNIEnv *env, jobject method))
{
// method is a handle to a java.lang.reflect.Method object
Method* method_ptr = jvm_get_method_common(method);
methodHandle mh (THREAD, method_ptr);
Handle reflected_method (THREAD, JNIHandles::resolve_non_null(method));
const int num_params = mh->method_parameters_length();
if (num_params < 0) {
// A -1 return value from method_parameters_length means there is no
// parameter data. Return null to indicate this to the reflection
// API.
assert(num_params == -1, "num_params should be -1 if it is less than zero");
return (jobjectArray)NULL;
} else {
// Otherwise, we return something up to reflection, even if it is
// a zero-length array. Why? Because in some cases this can
// trigger a MalformedParametersException.
// make sure all the symbols are properly formatted
for (int i = 0; i < num_params; i++) {
MethodParametersElement* params = mh->method_parameters_start();
int index = params[i].name_cp_index;
constantPoolHandle cp(THREAD, mh->constants());
bounds_check(cp, index, CHECK_NULL);
if (0 != index && !mh->constants()->tag_at(index).is_utf8()) {
THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(),
"Wrong type at constant pool index");
}
}
objArrayOop result_oop = oopFactory::new_objArray(vmClasses::reflect_Parameter_klass(), num_params, CHECK_NULL);
objArrayHandle result (THREAD, result_oop);
for (int i = 0; i < num_params; i++) {
MethodParametersElement* params = mh->method_parameters_start();
// For a 0 index, give a NULL symbol
Symbol* sym = 0 != params[i].name_cp_index ?
mh->constants()->symbol_at(params[i].name_cp_index) : NULL;
int flags = params[i].flags;
oop param = Reflection::new_parameter(reflected_method, i, sym,
flags, CHECK_NULL);
result->obj_at_put(i, param);
}
return (jobjectArray)JNIHandles::make_local(THREAD, result());
}
}
JVM_END
From the code I see that null is returned only in case when there's no parameter data. But the data is there, in the compiled class.
So my question is whether this is a bug or am I doing something wrong?
P.S. Parameter.isNamePresent() works unexpectedly even when I run it on conventional, not hacked JDK.
P.P.S. In compiled code I see 'real' parameter names, but if I stop at debug point in IDEA parameter name is suddenly arg0 in Parameter.name field.
As it was pointed out by Daniel Fuchs we need to compile the code with -parameters flag.
https://docs.oracle.com/en/java/javase/18/docs/specs/man/javac.html
Lambda is used here, but when ::new is used, the following parameters are populated into the constructor:
#FunctionalInterface
interface Lambdademo1<T> {
T test(String s);
}
class Test {
public static void test2(Lambdademo1<Apple> lambdademo1, String s) {
Apple i = lambdademo1.test(s);
System.out.println(i.getColor());
}
public static void main(String args[]){
test2(Apple::new,"hehehe");
}
}
Output:
hehehe
UPDATE:
class test {
public static void main(String args[]) {
test1((String s) -> new Integer(1), "hehehe");
test1(Integer::new, "hehehe"); //It's wrong
test2(Apple::new,"hehehe");
test3(Apple1::new,"hehehe"); //Compile error
// I think XXX::new is equivalen to new XXX() but here shoe it's not
}
public static void test1(Lambdademo1<Integer> lambdademo1, String s) {
Integer i = lambdademo1.test(s);
System.out.println(i);
}
public static void test2(Lambdademo1<Apple> lambdademo1, String s) {
Apple i = lambdademo1.test(s);
System.out.println(i.getColor());
}
public static void test3(Lambdademo1<Apple1> lambdademo1, String s) {
Apple1 i = lambdademo1.test(s);
System.out.println(i.getColor());
}
}
The Apple1 class:
class Apple1 {
private String color;
// getter and setter
}
The Apple class:
class Apple {
private String color;
public Apple(String color) {
this.color = color;
}
// getter and setter
}
Original answer
Apple::new can (and does) refer to a constructor Apple(String) because it follows the contract of T test(String s) - (String string) -> new Apple(string); or Apple:new
Apparently, that constructor sets the value for the color field since the getter returns the value you passed to the constructor.
test2(Apple::new,"hehehe");
is equivalent to
System.out.println(new Apple("hehehe").getColor());
Update
Let's discuss each line in detail to make it clear.
1.
test1((String s) -> new Integer(1), "hehehe");
You are taking a String s, not using it, and returning a constant new Integer(1) or simply 1.
We might rewrite it to
test1(s -> 1,"hehehe" );
2.
test1(Integer::new, "hehehe");
It's not wrong. It's absolutely compilable line. There is a constructor Integer(String s) that converts the given String to an int using Integer.parseInt(String).
Since "hehehe" isn't a parsable int, you will get a NumberFormatException, but that's a runtime issue.
3.
It's fine, and I have explained it in the original answer above.
4.
test3(Apple1::new,"hehehe");
You haven't defined any constructors for Apple1, so we have the no-arguments one by default. Since it doesn't take a String, we can't use it to represent Lambdademo1#test.
Writing a lambda will make it compile, though.
test3(s -> new Apple1(),"hehehe");
I think XXX::new is equivalent to new XXX() but here it's not.
It depends on context. XXX::new always refers to a constructor. What constructor? We don't know it until we see the context.
Examine an example where Apple::new points at 3 different constructors.
class Apple {
public Apple() {}
public Apple(Integer i) {}
public Apple(String s) {}
public static void main(String[] args) {
Supplier<Apple> a = Apple::new;
Function<Integer, Apple> b = Apple::new;
Function<String, Apple> c = Apple::new;
}
}
I'm creating a kind of data testing program, and one specific part is giving me a huge amount of trouble. In my main method class there is one section where I need to send over a String of data as a parameter in a method to my methods class (let's call it ValidatorClass) and the idea being that the method will then return any validation errors or if there are none simply an empty String.
This would be fine except that I use "for loops" when going through my data to validate as doing it without is just too clunky. I tried to research about arrays of methods and found plenty of useful things that work with void methods but found nothing on any methods that return variables.
In a nutshell I'm asking: Is it possible to create an array of methods (or implement an array of objects to simulate an array of methods) that return a variable?
Here is some example code, but in the actual program the method's return would actually be used further on:
public class Validation{
public static void main(String args){
ValidatorClass valTest = new ValidatorClass();
String[] dataList = {"Andrew", "Jameson", "Male"}
for(int i = 0; i < dataList.length; i++){
String errors = valTest.testInput(dataList[i], i).validationList[i];
System.out.println(errors);
}
}
}
And in ValidatorClass:
public class ValidatorClass{
public String testInput(String data, int index){
//Tests the data by calling method "index" which corresponds to data type.
//ie. validateName would be index : 1, validateSurname index : 2 etc
String errors = validationMethodList[index](data); //Somehow add data as a parameter to it
return errors;
}
public String validateName(String name){
String errors = "";
if(name.length < 1){
errors += "Name Not Entered";
}
return errors;
}
public String validateSurname(String surname){
String errors = "";
if(surname.length < 1){
errors += "Surame Not Entered";
}
return errors;
}
public String validateGender(String gender){
String errors = "";
if(!gender.equalsIgnoreCase("male") || !gender.equalsIngoreCase("female")){
errors += "Invalid Gender";
}
return errors;
}
}
I imagine that you have something like...
static String validate1(Validatible v) { /* do something */ }
static String validate2(Validatible v) { /* do something else */ }
static String validate3(Validatible v) { /* do something still else */ }
And that you want to execute, in some method...
Validatible v = getValidatible();
System.out.println(validate1(v));
System.out.println(validate2(v));
System.out.println(validate3(v));
Then perhaps you could write an interface:
public interface Validator {
String validate(Validatible v);
}
...and keep them in an array or a list...
private static final List<Validator> validators = Arrays.asList(
new Validator() {
#Override
public String validate() {
/* do something */
}
},
new Validator() {
#Override
public String validate() {
/* do something else */
}
},
new Validator() {
#Override
public String validate() {
/* do something still else */
}
}
);
// Can be written more compactly if in Java 8.
Thereafter, you can call the methods in a for-loop:
Validatible v = getValidatible();
for(Validator validator : validators) {
System.out.println(validator.validate(v));
}
Possible improvements would include using a StringBuilder to build a single String (or using the Stream API and using Collectors.joining) if this fits your purpose better.
My Original code is :
private String hello;
private int i = 0;
public void test() {
if (i == 0) {
hello = "asdas";
} else {
hello = "asasvfasfas";
}
}
After Obfuscating with proguard :
private String a;
private int c = 0;
public void a()
{
if (this.c == 0);
for (this.a = "asdas"; ; this.a = "asasvfasfas")
return;
}
In project properties :
proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
My proguard-project.txt file is empty, so I guess it should be using the default config file : proguard-android.txt.
Why it is behaving like this? How can I prevent this kind of code optimization? Please help.
Because your code is only that fragment you entered, I assume, your code will be easily optimized into this:
private String hello;
public void test() {
hello = "asdas";
}
The Proguard just doesn't remove your original but unreachable source lines, just puts them into unreachable places. It is converting your code into equivalent but not-so human friendly format.
So, the generated code works as yours, it is just obfuscated. If you don't like it, don't use obfuscators.
I'm bit confused. I have the following:
public static String showInputDialog() {
Form frm = new Form();
final Command cmd = new Command("Ok");
final TextField txt = new TextField("Enter the text", null, 1024, 0);
frm.addCommand(cmd);
frm.append(txt);
frm.setCommandListener(new CommandListener() {
public void commandAction(Command c, Displayable d) {
if (c == cmd) {
return txt.getString(); // Error !!
} else {
return null; // Error !!
}
}
});
}
As you can see, I want to return the input dialog string, while the anonymous class method should return void. How can I resolve this problem?
This does not work as you expected.
I see there are already some solutions, but I feel a bit more discussion about what is actually going on might be helpful.
When you call the frm.setCommandListener(new CommandListener() { ... }) the code presents the user with a dialog where she can type in some text and submit, but the code does not stop and wait until the user finishes.
Instead the code continues to execute - without yielding the result. Only after the user finished typing and submits, you get called back to process the result - which might happen much later, or not at all.
I guess you have some code calling this method like:
public void someMethod(int foo, String bar) {
[...]
String result = MyInputForm.showInputDialog();
// do something with the result
System.out.println("hey, got a result "+ result);
[...]
}
Instead you need to reorganize this. First write a helper class handling the result:
public static class MyCallBack {
public MyCallBack(... /* here pass in what you need to process the result*/) {
... remember necessary stuff in instance variables
}
public void processResult(String result) {
// do something with the result
System.out.println("hey, got a result "+ result);
[...]
}
}
then the calling side does just:
public void someMethod(int foo, String bar) {
[...]
MyInputForm.showInputDialog( new MyCallBack(... here pass in stuff ...) );
[...]
}
and the actual code has to be changed to:
public static String showInputDialog(final MyCallBack callback) {
Form frm = new Form();
final Command cmd = new Command("Ok");
final TextField txt = new TextField("Enter the text", null, 1024, 0);
frm.addCommand(cmd);
frm.append(txt);
frm.setCommandListener(new CommandListener() {
public void commandAction(Command c, Displayable d) {
if (c == cmd) {
return callback.processResult(txt.getString());
} else {
return; // or just omit the else part
}
}
});
}
Two issues:
this way of programming feels pretty backwards, but it is really the way it works.
what feels not right is that I need to define a second helper class aside of the CommandListener. That is really not good style. I hope it can be improved, but as I do not see the complete code (which would be too much information anyway), I have to leave it to you to improve the code and get rid of the clutter. While I feel you want to have a modular, reusable input dialog helper, this might not be the best approach; better define the Form,TextField and Command directly where you need the result and get that running. Make it reusable in a second step after you get it running.
You don't need to return it if you instead do something with the String or store it somewhere, for example:
static String result;
public String commandAction(Command c, Displayable d) {
if (c == cmd) {
result = txt.getString();
} else {
result = null;
}
}
Although you'll have threading issues to deal with.
Given that CommandListener is fixed, 2 possible options are
Use a class member variable in the outer class & assign to that variable instead
private static String myText;
...
public static String showInputDialog() {
...
frm.setCommandListener(new CommandListener() {
public void commandAction(Command c, Displayable d) {
if (c == cmd) {
myText = txt.getString();
} else {
myText = null;
}
}
});
}
or Create a concrete implementation of your CommandListener and set the return value as a property of the new implementation
I would have a look at making the method/variable in this snippet non-static...
You cant return the string because you dont know when the listener will be called.
You can do something with it once you have the string though.
public static void showInputDialog() {
StringHandler sh = new StringHandler();
frm.setCommandListener(new CommandListener() {
public void commandAction(Command c, Displayable d) {
if (c == cmd) {
sh.handle(txt.getString());
} else {
sh.handle(null);
}
}
});}
public class StringHandler {
public void handle(String s){
// Do something with that string.
}
}