JavaParser - save modified source code file with new name - java

I'm using JavaParser to modify java source code. My goal is to read a single java source code file (ArithmeticClassToBeMutated) and store it in a compilation unit. Then, i'd like to replace/mutate its arithmetic operators (PLUS,MINUS,MULTIPLY,DIVISION,REMAINDER). All instances of an operator (e.g. plus) shall always be replaced with another one (e.g. minus). In the end, i want to have 4 output files:
One java source code file where every "Plus" became a "Minus",
one file where every "Plus" became a "Multiply",
one file where every "Plus" became a "Division", and
one file where every "Plus" became a "Remainder/Modulo). I can't type the symbols or else i get a formatting error.
In my code (see below), the replacement/modification itself works. Now, my question is: how can I change the name of the output source code files? I did it with the methods add and saveAll:
sourceRoot.add("", OperatorToBeMutated.name() + "_TO_" + arithmeticOperators[i].name() + "_MUTATED_"
+ cu.getStorage().get().getFileName(), cu);
sourceRoot.saveAll(
CodeGenerationUtils.mavenModuleRoot(ReplacementAO.class).resolve(Paths.get("output")));
However, this creates two output files for each operator replacement. One file has the same name as the input file, and one file has my naming convention. The content is the same. What can I do to only save a single file (with my own naming) for each loop? Specifying no name would result in the output file overwriting itself with each iteration, as the name stays the same.
Thank you!
public static String filename = "ArithmeticClassToBeMutated.java";
public static void main(String[] args) {
for (i = 0; i < arithmeticOperators.length; i++) {
if (arithmeticOperators[i] == OperatorToBeMutated) {
continue;
}
sourceRoot = new SourceRoot(
CodeGenerationUtils.mavenModuleRoot(ReplacementAO.class).resolve("src/main/resources"));
CompilationUnit cu = sourceRoot.parse("", filename);
cu.accept(new ModifierVisitor<Void>() {
#Override
public Visitable visit(BinaryExpr n, Void arg) {
if (n.getOperator() == OperatorToBeMutated && n.getLeft().isNameExpr()
&& n.getRight().isNameExpr()) {
n.setOperator(arithmeticOperators[i]);
comment.setContent("Here, the operator " + OperatorToBeMutated.name()
+ " was mutated with the operator " + arithmeticOperators[i].name() + ".");
n.setComment(comment);
}
return super.visit(n, arg);
}
}, null);
sourceRoot.add("", OperatorToBeMutated.name() + "_TO_" + arithmeticOperators[i].name() + "_MUTATED_"
+ cu.getStorage().get().getFileName(), cu);
sourceRoot.saveAll(
CodeGenerationUtils.mavenModuleRoot(ReplacementAO.class).resolve(Paths.get("output")));
}
}

You wouldn't want to rename your file name to be different from the class name, as a public java class needs to have the same name as its file name. As far as I am aware this will throw a compiler error for public classes:
Can I compile a java file with a different name than the class?
I would suggest putting the mutated classes into different folders. Just adding another directory at the end of your path will automatically create a new folder. So for your example:
sourceRoot.saveAll(CodeGenerationUtils.mavenModuleRoot(LogicalOperators.class).resolve(Paths.get("output" + "/mutation1")));

Related

Programmatically fetch multiple code from string in Java

I have string looks like below, the string is joined by line-breaker. In this string, the the first 2 lines and last two lines are fixed, "public class MyClass {/n public void code() {/n"
String doc =
"public class MyClass {
public void code() {
try (...) {
...
}
}
}"
I only want to take out the multiple lines code in the method code, which means no first 2 lines and last 2 lines. This is what I did in my project:
String[] lines = docj.split("\\r?\\n");
String[] codes = Arrays.copyOfRange(lines, 2, lines.length - 2);
String result = String.join("\n", codes);
Do you have better way to fetch the string in the middle?
The only real answer: use an existing parser framework, such as javaparser.
Seriously, that simple.
Anything else means: you are spending time and energy to solve a solved problem. The result will be deficient, compared to any mature product, and it will be a constant liability in the future. You can get your tool to work with code you have in front of you right now, but the second your tool gets used to "parse" slightly different code, it will most likely break.
In case you are asking for educational purposes, then learn how compiler works, and what it takes to tokenize Java source code, and how to turn it into an abstract syntax tree (AST) representation.
Assuming the task is meant for basic educational purposes or a quick hack (otherwise #GhostCat's answer draws first):
Already method detection, taken seriously is not so easy. Basically you have to start implementing your own syntax parser for a fraction the Java language: chop everything to single words, skip the class declaration, wait for "static", "public", "protected", "private", "synchronized", hope I didn't forget one, skip over them and the return type definition ("void", "string"...), then you are at the name, then come optional type parameters ("<T>"), then "(", then optionally method parameters etc.).
Perhaps there are restrictions to the task, that make it less complicated. You should ask for clarification.
The problem in any case will be to find the closing braces and skip them. If you can afford to neglect such stuff as braces in strings (string s = "ab{{c";) or comments ("/* {{{ */")it is enough to count up for each { occuring after e.g. "public void code() {" and count down for "}". when the brace count is 0 and you see another "}", that one can be skipped and everything until the next method declaration.
If that's not precise enough, or your requirements are of a more serious nature, you'd have to get into parsing, e.g. using antlr or Javaparser. Here's a project that seems to do a similar task.
Learning Java Parser takes some amount of time. It isn't difficult, and there is a Java Doc Documentation Page available on the Internet. (See Here) ... But unfortunately, there isn't a lot of text to read in the documentation pages themselves. This class prints out the Method Bodies from a source-code file that is saved as a String.
Every method in the class is printed...
import com.github.javaparser.ast.*;
import com.github.javaparser.ast.stmt.BlockStmt;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.visitor.VoidVisitor;
import com.github.javaparser.ast.visitor.VoidVisitorAdapter;
import com.github.javaparser.*;
import java.io.IOException;
import java.util.Optional;
public class MethodBody
{
static final String src =
"public class MyClass {" + '\n' +
" public void code() {" + '\n' +
" try {" + '\n' +
" /* do stuff */ " + '\n' +
" }" + '\n' +
" catch (Exception e) { }" + '\n' +
" }" + '\n' +
"}";
public static void main(String[] argv) throws IOException
{
CompilationUnit cu = StaticJavaParser.parse(src);
VoidVisitor<?> visitor = new VoidVisitorAdapter<Void>()
{
public void visit(MethodDeclaration md, Void arg)
{
System.out.println("Method Name: " + md.getName());
Optional<BlockStmt> optBody = md.getBody();
if (! optBody.isPresent()) System.out.println("No Method Body Definition\n");
System.out.println("Method Body:\n" + optBody.get().toString() + "\n\n");
}
};
visitor.visit(cu, null);
}
}
The above code will print this to terminal:
Method Name: code
Method Body:
{
try {
/* do stuff */
} catch (Exception e) {
}
}

IntelliJ - is it possible to copy an output of String.format or StringBuilder into clipboard?

According to the following question and response:
How find out static string result concatenation in Intellij Idea without running code?
it is possible to copy result of the String concatenation from IntelliJ into the clipboard.
Let me cite:
just put cursor on the string concatenation
Press alt + enter
Select "Copy String concatenation text into clipboard"
Paste result somewhere.
However, I find out that this solution does not work in case of the String.format.
Is there any workaround or plugin for the IntelliJ that allows to perform this operation?
public class Main {
public static void main(String[] args) {
final String one = "want";
final String two = "copy";
final String three = "concatenated string";
// I want to copy whole concatenated string
String canBeCopiedIntoClipBoard = "I " + one + " to " + two + " whole " + three;
String cannotCopyResultIntoClipBoard = String.format("I %s to %s whole %s", one, two, three);
assert canBeCopiedIntoClipBoard.equals(cannotCopyResultIntoClipBoard);
System.out.println(canBeCopiedIntoClipBoard);
}
}
It cannot be done directly from the IDE because of the simple fact that the variables used in the construction of the result may come from anywhere and may not be known at compile-time, as well as intricacies of the implementation of whichever external class you invoke (think of environment variables that may play a role).
You can do it if you can run to the point of the construction of the result.
Put a breakpoint at the line of 'cannotCopyResultIntoClipBoard' and start debug,
Select the part that generates the string,
Open to the 'Evaluate' panel (Sometimes Alt+F8, always Run→Evaluate Expression…),
Press enter,
In the result panel, right click, Copy Value.

Adding a line in a method block of java code using python

I have a lot of java files wherein I have to search for a method, if present I have to add a line inside this method "If this line does not already exist". This line has to be added before the closing brace of the method.
So far I have the following code:
import os
import ntpath
extensions = set(['.java','.kt'])
for subdir, dirs, files in os.walk("/src/main"):
for file in files:
filepath = subdir + os.sep + file
extension = os.path.splitext(filepath)[1]
if extension in extensions:
if 'onCreate(' in open(filepath).read():
print (ntpath.basename(filepath))
if 'onPause' in open (filepath).read():
print ("is Activity and contains onPause\n")
#Check if Config.pauseCollectingLifecycleData(); is in this code bloack, if exists do nothing, if does not exist add to the end of code block before }
if 'onResume' in open (filepath).read():
print ("is Activity and contains onResume\n")
#Check if Config.resumeCollectingLifecycleData(); is in this code bloack, if exists do nothing, if does not exist add to the end of code block before }
But I am not sure where to go from here, Python not being my first language. Could I request to be guided in the right direction.
Example:
I am looking for a method with the following signature:
public void onPause(){
super.onPause();
// Add my line here
}
public void onPause(){
super.onPause();
Config.pauseCollectingLifecycleData(); // Line exists do nothing
}
This is actually quite difficult. First of all, your if "onPause" in sourcecode approach currently doesn't distinguish between defining onPause() and calling it. And second of all, finding the correct closing } isn't trivial. Naively, you might just count opening and closing curlies ({ increments the blocklevel, } decrements it), and assume that the } that makes the blocklevel zero is the closing curly of the method. However, this might be wrong! Because the method might contain some string literal containing (possibly unbalanced) curlies. Or comments with curlies. This would mess up the blocklevel count.
To do this properly, you would have to build an actual Java parser. That's a lot of work, even when using libraries such as tatsu.
If you're fine with a rather volatile kludge, you can try and use the blocklevel count mentioned above together with the indentation as a clue (assuming your source code is decently indented). Here's something I've hacked up as a starting point:
def augment_function(sourcecode, function, line_to_insert):
in_function = False
blocklevel = 0
insert_before = None
source = sourcecode.split("\n")
for line_no, line in enumerate(source):
if in_function:
if "{" in line:
blocklevel += 1
if "}" in line:
blocklevel -= 1
if blocklevel == 0:
insert_before = line_no
indent = len(line) - len(line.lstrip(" ")) + 4 #4=your indent level
break
elif function in line and "public " in line:
in_function = True
if "{" in line:
blocklevel += 1
if insert_before:
source.insert(insert_before, " "*indent + line_to_insert)
return "\n".join(source)
# test code:
java_code = """class Foo {
private int foo;
public void main(String[] args) {
foo = 1;
}
public void setFoo(int f)
{
foo = f;
}
public int getFoo(int f) {
return foo;
}
}
"""
print(augment_function(java_code, "setFoo", "log.debug(\"setFoo\")"))
Note that this is vulnerable to all sorts of edge cases (such as { in a string or in a comment, or tab indent instead of space, or possibly a thousand other things). This is just a starting point for you.

How to convert a filepath to valid file path in Java 1.7

Using Java 1.6 Filepath can be entered by user and then I apply various regular expressions to remove characters that are invalid for the platform (such as '?' is invalid on Windows), and check path length to ensure we end up with a valid filepath for the OS before trying to create the filepath.
But there are two problems:
Its a pain working out what is valid or not for each platform.
I'm making assumptions based on default filesystem for the platform, but of course an OSX system could be writing to a non-mac filesystem such a FAT32, in which case these checks will not be valid.
So I was hoping there would be a better way to do it with NIO2 in Java 7, but haven't found a solution yet, is there one ?
Depending on your expected result (corrected String? Invalid character position? Exception?), this should give you an idea of what can be done:
import java.io.File;
import java.nio.file.InvalidPathException;
public class Test {
public static final void main(final String[] args) {
final String current = new File(".").toPath().toAbsolutePath().normalize().toFile().toString();
Test.correctPath(current);
Test.correctPath(current + "aValidExpression");
Test.correctPath(current + "aValidExpression?;:-&é");
Test.correctPath(current + "aValidExpr//ession?;:-&é");
Test.correctPath(current + "aValidExpre\\ssion?;:-&é");
}
public static final String correctPath(final String path) {
try {
final String returnValue = new File(path).toPath().toAbsolutePath().normalize().toFile().toString();
System.out.println(returnValue);
return returnValue;
} catch (final InvalidPathException e) {
System.err.println(e.getMessage());
final int errorIndex = e.getIndex();
final String newPath = path.substring(0, errorIndex - 1) + path.substring(errorIndex + 1);
return Test.correctPath(newPath);
}
}
}
I hope it helps.
The key to your question is the phrase "remove characters that are invalid for the platform". The various String to Path conversion functions, such as get() and resolve(), will tell you whether the string was valid as a path, but the won't tell why it's invalid. One way of being invalid is to contain invalid characters. Another would be to have, say, too many slash characters together. Regardless, the library does not give any more information than this; it provides no facility to assist in validating user input in any way that would help a user fix an input error. This ought to be a standard practice, admittedly, but it's hardly a practice at all.
Upshot: You'll have to write such a validation library yourself if you want to have one. Upside: You certainly aren't the only person with such a problem.
I guess you should look at Path.getPath
public static Path get(String first,
String... more)
getPath("/foo","bar","gus")-->/foo/bar/gus
Converts a path string, or a sequence of strings that when joined form a path string, to a Path. If more does not specify any elements then the value of the first parameter is the path string to convert. If more specifies one or more elements then each non-empty string, including first, is considered to be a sequence of name elements (see Path) and is joined to form a path string. The details as to how the Strings are joined is provider specific but typically they will be joined using the name-separator as the separator. For example, if the name separator is "/" and getPath("/foo","bar","gus") is invoked, then the path string "/foo/bar/gus" is converted to a Path. A Path representing an empty path is returned if first is the empty string and more does not contain any non-empty strings.

Java - How to find out whether a File name is valid? [duplicate]

This question already has answers here:
Is there a way in Java to determine if a path is valid without attempting to create a file?
(7 answers)
Closed 7 years ago.
In my Java application I am renaming files to a file name provided in a String parameter. There is a method
boolean OKtoRename(String oldName, String newName)
which basically checks whether the newName isn't already taken by some other file, as I wouldn't want to bury existing ones.
It now occurred to me that perhaps the newName String will not denote a valid file name. So I thought to add this check to the method:
if (new File(newName).isFile()) {
return false;
}
Which obviously isn't the right way to do it, since in most cases the newFile does not yet exist and therefore although it is OKtoRename, the function returns false.
I was wondering, is there a method (I know there isn't for the java.io.File objects) like canExist()? Or would I have to resort to regex to make sure the newFile String does not contain invalid characters (e.g. ?, *, ", :)? I wonder if there is perhaps a function hidden somewhere in the JDK that would tell me if a string could possibly denote a valid file name.
I assembled a list of illegal filename characters (considering UNIX, Mac OS X and Windows systems) based on some online research a couple of months ago. If the new filename contains any of these, there's a risk that it might not be valid on all platforms.
private static final char[] ILLEGAL_CHARACTERS = { '/', '\n', '\r', '\t', '\0', '\f', '`', '?', '*', '\\', '<', '>', '|', '\"', ':' };
EDIT:
I would like to stress, that this is not a complete solution: as a commenter pointed out, even though it passes this test your file name could still be a Windows specific keyword like COM, PRN, etc. However, if your file name contains any of these characters, it will certainly cause trouble in a cross-platform environment.
Use createNewFile(), which will atomically create the file only if it doesn't yet exist.
If the file is created, the name is valid and it is not clobbering an existing file. You can then open the files and efficiently copy data from one to the other with FileChannel.transferXXX operations.
An important thing to keep in mind that, in general, the check and the creation should be atomic. If you first check whether an operation is safe, then perform the operation as a separate step, conditions may have changed in the meantime, making the operation unsafe.
Additional food for thought is available at this related post: "Move/Copy operations in Java."
Update:
Since this answer, the NIO.2 APIs have been introduced, which add more interaction with the file system.
Suppose you have an interactive program, and want to validate after each keystroke whether the file is potentially valid. For example, you might want to enable a "Save" button only when the entry is valid rather than popping up an error dialog after pressing "Save". Creating and ensuring the deletion of a lot of unnecessary files that my suggestion above would require seems like a mess.
With NIO.2, you can't create a Path instance containing characters that are illegal for the file system. An InvalidPathException is raised as soon as you try to create the Path.
However, there isn't an API to validate illegal names comprised of valid characters, like "PRN" on Windows. As a workaround, experimentation showed that using an illegal file name would raise a distinct exception when trying to access attributes (using Files.getLastModifiedTime(), for example).
If you specify a legal name for a file that does exist, you get no exception.
If you specify a legal name for a file that does not exist, it raises NoSuchFileException.
If you specify an illegal name, FileSystemException is raised.
However, this seems very kludgey and might not be reliable on other operating systems.
Here system specific way is suggested.
public static boolean isFilenameValid(String file) {
File f = new File(file);
try {
f.getCanonicalPath();
return true;
} catch (IOException e) {
return false;
}
}
If developing for Eclipse, check out org.eclipse.core.internal.resources.OS
public abstract class OS {
private static final String INSTALLED_PLATFORM;
public static final char[] INVALID_RESOURCE_CHARACTERS;
private static final String[] INVALID_RESOURCE_BASENAMES;
private static final String[] INVALID_RESOURCE_FULLNAMES;
static {
//find out the OS being used
//setup the invalid names
INSTALLED_PLATFORM = Platform.getOS();
if (INSTALLED_PLATFORM.equals(Platform.OS_WIN32)) {
//valid names and characters taken from http://msdn.microsoft.com/library/default.asp?url=/library/en-us/fileio/fs/naming_a_file.asp
INVALID_RESOURCE_CHARACTERS = new char[] {'\\', '/', ':', '*', '?', '"', '<', '>', '|'};
INVALID_RESOURCE_BASENAMES = new String[] {"aux", "com1", "com2", "com3", "com4", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
"com5", "com6", "com7", "com8", "com9", "con", "lpt1", "lpt2", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$
"lpt3", "lpt4", "lpt5", "lpt6", "lpt7", "lpt8", "lpt9", "nul", "prn"}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$
Arrays.sort(INVALID_RESOURCE_BASENAMES);
//CLOCK$ may be used if an extension is provided
INVALID_RESOURCE_FULLNAMES = new String[] {"clock$"}; //$NON-NLS-1$
} else {
//only front slash and null char are invalid on UNIXes
//taken from http://www.faqs.org/faqs/unix-faq/faq/part2/section-2.html
INVALID_RESOURCE_CHARACTERS = new char[] {'/', '\0',};
INVALID_RESOURCE_BASENAMES = null;
INVALID_RESOURCE_FULLNAMES = null;
}
}
/**
* Returns true if the given name is a valid resource name on this operating system,
* and false otherwise.
*/
public static boolean isNameValid(String name) {
//. and .. have special meaning on all platforms
if (name.equals(".") || name.equals("..")) //$NON-NLS-1$ //$NON-NLS-2$
return false;
if (INSTALLED_PLATFORM.equals(Platform.OS_WIN32)) {
//empty names are not valid
final int length = name.length();
if (length == 0)
return false;
final char lastChar = name.charAt(length-1);
// filenames ending in dot are not valid
if (lastChar == '.')
return false;
// file names ending with whitespace are truncated (bug 118997)
if (Character.isWhitespace(lastChar))
return false;
int dot = name.indexOf('.');
//on windows, filename suffixes are not relevant to name validity
String basename = dot == -1 ? name : name.substring(0, dot);
if (Arrays.binarySearch(INVALID_RESOURCE_BASENAMES, basename.toLowerCase()) >= 0)
return false;
return Arrays.binarySearch(INVALID_RESOURCE_FULLNAMES, name.toLowerCase()) < 0;
}
return true;
}
}
Just something i found, in java 7 and later, there is a class called Paths that has a method called get that takes one or more Strings and throws
InvalidPathException - if the path string cannot be converted to a Path
This is how I implemented this:
public boolean isValidFileName(final String aFileName) {
final File aFile = new File(aFileName);
boolean isValid = true;
try {
if (aFile.createNewFile()) {
aFile.delete();
}
} catch (IOException e) {
isValid = false;
}
return isValid;
}
To me it appears to be an OS dependent problem. You may simply want to check for some invalid character in the file name. Windows does this when you try to rename the file, it pops a message saying that a file cannot contain any of the following characters: \ / : * ? < > |
I am not sure if your question is "is there a library doing the job for me?" in that case I don't know any.
Using
String validName = URLEncoder.encode( fileName , "UTF-8");
File newFile = new File( validName );
Does the work.
I have just found today. I'm not sure if it works 100% of the time, but so far, I have been able to create valid file names.

Categories