For a given java file, I'd like to check if it's syntactically correct. ie. If it has semi-colons in the right places, matching parenthesis etc. Importantly, I need to check the file in isolation from all of its dependencies.
This other answer is promising, however it's really doing a semantic check rather than a syntactic check. It does what a compiler would do - check all the imports statements as well as verify external classes that are referenced in the code.
Is there a way to do a true syntax check? (A check that only inspects the raw text against Java's formal grammar)
Create or use a Java source code parser. For some parser generators there are public Java grammars available - you could use it to generate the parser.
E.g. Java 8 grammar for ANTLR (no idea about quality of that grammar though, you'd have to do your evaluation - but the grammar is written by the author of ANTLR, so should be OK I guess).
As Jiri suggested, use the ANTLR library to put together a syntax checker.
Download and extract the ANTLR grammars from here
Download the ANTLR4 jar from here
Run the following command to generate classes from the Java 8 grammar:
java -jar antlr-4.5.3-complete.jar
~/Downloads/grammars-v4-master/java8/Java8.g4
Copy the .java files that were created into your project
Then you can write your syntax checker:
public static void main(String[] args) throws SAXException, IOException {
ANTLRInputStream input = new ANTLRFileStream(args[0]);
Java8Lexer lexer = new Java8Lexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
Java8Parser parser = new Java8Parser(tokens);
final StringBuilder errorMessages = new StringBuilder();
parser.addErrorListener(new BaseErrorListener() {
#Override
public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) {
String err = String.format("Failed to parse at line %d:%d due to %s", line, charPositionInLine + 1, msg);
errorMessages.append(err);
errorMessages.append(System.lineSeparator());
}
});
parser.compilationUnit();
int syntaxErrors = parser.getNumberOfSyntaxErrors();
if (syntaxErrors == 0) {
System.out.println(args[0] + ": PASS");
} else {
System.out.println(args[0] + ": FAILED (" + syntaxErrors + " syntax errors");
}
}
I was researching how to do a quick java syntax-checker for usage in vim. I inspired by #Fidel's answer (thanks for the example checker!) but it didn't suffice me as I needed it to work in a standalone way.
Here is a step by step process of what had to be done in order to have runnable command:
Run:
# download and generate .java classes
wget https://repo1.maven.org/maven2/org/antlr/antlr4/4.5.3/antlr4-4.5.3.jar -O antlr4-4.5.3.jar
wget https://raw.githubusercontent.com/antlr/grammars-v4/master/java8/Java8.g4 -O Java8.g4
java -jar antlr4-4.5.3.jar ./Java8.g4
Then write a syntax checker, perhaps similar to #Fidel's one and place it under package checker; place it directly under ./checker directory
Also place all of the generated .java classes under that package (ie. put package checker; to the first line of all the files)
Run:
# prepare directory structure and compile
mkdir -p checker && mv *.java ./checker/
javac -cp antlr4-4.5.3.jar ./checker/*.java
Prepare a Manifest file that will look similarly to:
Class-Path: antlr4-4.5.3.jar
Main-Class: checker.<name-of-your-checker-class>
Run:
# package into a jar file and run to test everything works
jar cfm checker.jar Manifest.txt checker/*.class
java -jar ./checker.jar <filename-you-want-to-run-syntax-check-against>
For my usage in vim I then created a simple shell script to wrap execution of the process of running the java jar that looks like this:
#!/bin/bash
DIR=$(readlink -f $0)
DIR=${DIR:0:(-3)} # put the length of the script name here instead of '3'
java -jar ${DIR}checker.jar $#
Then make it executable and symlink it under $PATH:
chmod a+x <file-name>
sudo ln -s /path/to/the/script /usr/bin/java-syntax
And finally added a keybinding like this into a .vimrc file (ie. to map running the syntax-check when F10 key is pressed):
" java syntax validation
map <F10> :!java-syntax % <CR>
I also stored the process into a github repository, where all the commands are prepared in a makefile and it suffices to run make in order to build and package the checker. Feel free to use it as an inspiration.
Related
I have a Jenkins job running. I just want to get all files. In every file name there is a Chinese letter. So the problem is now that Jenkins has problems reading in those files. Jenkins makes just "?" out of the Asian letter. The second problem is. Actually it is more than 100 files. But Jenkins only gives me 20 files. Maybe now a lot of files will look the same because of the question mark "?" .
Does anyone know how I can fix this. The problem only occurs on Jenkins ( running on Linux ) . On my local machine in Eclipse it works though.
File resourcePath = new File("resources/china_data/");
File[] files = resourcePath.listFiles();
for (final File file : files)
{
System.out.console(file.getName);
}
An alternative solution is to use the new java.nio.Path api in place of the java.io.File api
Also try setting the below in your code initially.
System.setProperty("sun.jnu.encoding","utf-8");
System.setProperty("file.encoding","UTF-8");
Assuming you are using System.out.println, this happens when the program runs with an ASCII locale:
$ cat Main.java
import java.util.*;
import java.io.*;
class Main {
public static void main(String[] args) throws Exception {
File resourcePath = new File("resources/china_data/");
File[] files = resourcePath.listFiles();
for (final File file : files)
{
System.out.println(file.getName());
}
}
}
$ javac Main.java
$ LC_CTYPE=C java Main
???????
When the program runs with a UTF-8 capable locale, either from the environment or configured through Java, you get the expected result:
$ LC_CTYPE=en_US.UTF-8 java Main
中华人民共和国
$ LC_CTYPE=C java -Dfile.encoding=UTF-8 Main
中华人民共和国
If you're not sure how to configure your server, you can also do this from within Java:
System.setOut(new PrintStream(System.out, true, "UTF-8"));
I have a remote Linux machine which i would like to pull files from using rsync based on a specific pattern in the file on a local machine.
When i am in the terminal window I am able to perform this task using the next command:
rsync -avzrm --include '**/someDir/*MG*' --include '*/' --exclude '*' -e "ssh -i /home/localUser/.ssh/id_rsa -l remoteUser" remotrUser#ip.address.net:/home/remoteUser/baseDir/ /home/localUser/testDir/
As a result of this command I am able to copy all the files from within the baseDir directory and its sub directories that contain 'MG' in their name.
I am trying to achieve the same result using a java code:
public class DataDumper {
public static void main (String[] args){
try {
String[] cmd = new String[]{"rsync", "-avzrm", "--include", "'**/someDir/*MG*'", "--include", "'*/'", "--exclude", "'*'",
"-e", "ssh -i /home/localUser/.ssh/id_rsa -l ec2-user",
"remoteUser#ip.address.net:/home/remoteUser/baseDir/", "/home/localUser/testing"};
Process p = new ProcessBuilder().command(cmd).inheritIO().start();
}
catch (IOException ex) {ex.printStackTrace();}
catch (Throwable th) {th.printStackTrace();}
}
}
But, i am ending up pulling all the files inside the baseDir directory.
What am i doing wrong? why would java not recognize the include and exclude options?
Any help would be appreciated. Thank you.
My problem is not executing the command.
My problem is that some of the arguments of the command are ignored from some reason.
... "--include", "'**/someDir/*MG*'", "--include", "'*/'", "--exclude", "'*'"
Remove the single quotes from the patterns. They're being passed to rsync literally, and rsync is probably interpreting them as part of the filenames that it should look for.
In short, try this:
... "--include", "**/someDir/*MG*", "--include", "*/", "--exclude", "*"
The reason you use single quotes for for these fields on the command line is to prevent the * characters from being interpreted by the shell that is interpreting your commands. Single-quoting the arguments containing * will cause the shell to remove the single quotes and pass the * characters literally to the rsync program.
When you're running rsync through java's ProcessBuilder there is no shell, so there's no need to protect the * characters. But there's also nothing to remove the single quote characters.
I have been using Geany to create Java programs, where until now I was able to compile them successfully. The simple program created below in Java was made using Geany, however the illegal character error (\u0000) occurred.
public class SumOfCubedDigits
{
public static void main(String[] args)
{
for (int i=1; i<=9; i++)
{
for (int j=0; j<=9; j++)
{
for (int k=0; k<=9; k++)
{
double iCubed=Math.pow(i,3);
double jCubed=Math.pow(j,3);
double kCubed=Math.pow(k,3);
double cubedDigits = iCubed + jCubed + kCubed;
int concatenatedDigits = (i*100 + j*10 + k);
if (cubedDigits==concatenatedDigits)
{
System.out.println(concatenatedDigits);
}
}
}
}
}
}
I recreated the program in nano and it was able to compile successfully. I then copied it across to Geany under a different name of SumTest.java, compiled it and got the same illegal character error. Clearly the error is with the Geany IDE for Raspberry Pi. I'd like to know how I could fix the editor to create and compile programs successfully as it not just this program, it is any program created in Java using Geany.
This might be a problem with encoding that Geany uses when saving the source file.
If you compile the file with javac without specifying the -encoding parameter the platform's default encoding is used. On a modern Linux this is likely to be UTF-8; on Windows it is one of the ANSI character sets or UTF-16, I think.
To find out what the default encoding is, you can compile and run a small java program:
public class DefaultCharsetPrinter {
public static void main(String[] argv) {
System.out.println(Charset.defaultCharset());
}
}
This should print the name of the default encoding used by java programs.
In Geany you can set the file encoding in menu Document > Set Encoding. You need to set this to the same value used by javac. The Geany manual describes additional options for setting the encoding.
As you are seeing a lot errors complaining about the null character it is most likely that Geany stores the file in an encoding with multiple bytes per character (for instance UTF-16) while javac uses an encoding with a single byte per character. If I save your source file as UTF-16 and then try to compile it with javac using UTF-8 encoding, I get the same error messages that you see. After saving the file as UTF-8 in Geany, the file compiles without problems.
I had the same problem with a file i generated using the command echo echo "" > Main.java in Windows Powershell.
I searched the problem and it seemed to have something to do with encoding. I checked the encoding of the file using file -i Main.java and the result was text/plain; charset=utf-16le.
Later i deleted the file and recreated it using git bash using touch Main.java and with this the file compiled successfully. I checked the file encoding using file -i command and this time the result was Main.java: text/x-c; charset=us-ascii.
Next i searched the internet and found that to create an empty file using Powershell we can use the Cmdlet New-Item. I create the file using New-Item Main.java and checked it's encoding and this time the result was Main.java: text/x-c; charset=us-ascii and this time it compiled successully.
I am new to perl but have done some programming in java facing a problem in running the perl script present in the jar file .
I am using windows and I have written a perl script to convert one type of file to another type .
I have checked the perl script with the java program using the Runtime and I am able to run the same as required and i am getting the output converted files as well (using the cmd line)
I have created a GUI in java to get the files to convert to the target files . I am able to run the file from netbeans IDE as will .
But when I am trying to run the jar file .
I am using URL to get the URL to the perl script .
URL url = this.getClass().getResource("/com/MyProject/FileConverter/fileconverter.pl");
and Runtime for Executing the script :
String[] cmd = {"perl",path,input_file,output_file};
process = Runtime.getRuntime().exec(cmd);
Please help in resolving the issue . Basically i do need to know how we can run the perl script present in the same jar file that we are executing.
You will have to read that perl file as resource and write it somewhere on file system as File (like this) and then pass that path to your command
See Also
Extract and load DLL from JAR
I'm assuming you have your perl script file in you jar and you don't want to extract it, just execute it "from inside".
One solution is to get the "stream" of your "resource" (your perl script), and then execute "perl" writing your script in the process' standard input.
This is better explained with a piece of code:
IMPORTANT CAVEAT: the path to your script in getResourceAsStream shouldn't start with /
// Start the process "perl"
Process process = Runtime.getRuntime().exec("perl");
// get the script as an InputStream to "inject" it to perl's standard input
try (
InputStream script = ClassLoader.getSystemClassLoader()
.getResourceAsStream("com/MyProject/FileConverter/fileconverter.pl");
OutputStream output = process.getOutputStream()
) {
// This is to "inject" your input and output file,
// as there is no other easy way ot specify command line arguments
// for your script
String firstArgs = "$ARGV[0] = \"" + input_file + "\";\n" +
"$ARGV[1] = \"" + output_file + "\";\n";
output.write(firstArgs.getBytes());
// send the rest of your cript to perl
byte[] buffer = new byte[2048];
int size;
while((size = script.read(buffer)) != -1) {
output.write(buffer, 0, size);
}
output.flush();
}
// just in case... wait for perl to finish
process.waitFor();
I need to execute a command from a program. The command line is ok, I tried it in the terminal, but it doesn't work in the program.
I add a copy from my code:
File dir = new File("videos");
String[] children = dir.list();
if (children == null) {
// Either dir does not exist or is not a directory
System.out.print("No existe el directorio\n");
} else {
for (int i=0; i<children.length; i++) {
// Get filename of file or directory
String filename = children[i];
//Recojo el momento exacto
System.out.print("\n" +filename);
Process p = Runtime.getRuntime().exec("exiftool -a -u -g1 -j videos/"+filename+">metadata/"+filename+".json");
}
The program must get the name of all of the files in a folder (filename) and extract the metadata of theese videos, writting them on a .json files in the folder 'metadata'.
Where is the problem?
The problem is, the redirection character (>) is a shell-based construct, not an executable. So unless you're running this command through something like bash (which you're not), it's going to be interpreted as a literal character argument to your exiftool invocation.
If you want to get this to work, you have two options:
Get bash to do it - pass the whole command line as an argument to bash -c. This might need some heroic escaping, although in your case it looks OK.
Do the redirection yourself within Java. Invoke the command without the redirected output (i.e. everything up to the > sign), then read from the process' outputstream and write all the contents to the appropriate file.
The latter approach sounds like more work initially, but when you consider that you need to always read a Process' output anyway (see the javadocs, second paragraph), it's actually very little extra on top of that. You're simply sending this output to a file instead of throwing it away.
If you have Java 7, it's easier:
Process p = new ProcessBuilder()
.command("exiftool", "-a", "-u", "-g1", "-j",
new File("videos", filename).toString())
.redirectOutput(new File("metadata", filename + ".json"))
.start();
This falls under "solution 2", but the runtime library takes care of the boilerplate.