How to automatically escape unicode characters in Java property files using Gradle? - java

I'm translating a Java application by using a ResourceBundle with various *.properties files. Now I like to have a Gradle task or want to modify a task to automatically escape any unicode character by replacing it with its ASCII representation, something like Java's native2ascii tool does.
This is what I'm done so far with my build file, but the output remains unescaped:
import org.apache.tools.ant.filters.EscapeUnicode
tasks.withType(ProcessResources) {
filesMatching('**/*.properties') {
println "\t-> ${it}"
filter EscapeUnicode
}
}
Any help is appreciated.

You can do it providing the additional copy specification for properties files, this way:
import org.apache.tools.ant.filters.EscapeUnicode
tasks.withType(ProcessResources).each { task ->
task.from(task.getSource()) {
include '**/*.properties'
filter(EscapeUnicode)
}
}

Related

Read the class file and replace content of the class file

I am compiling this Java file and I get one class file.
My task is to change the Static content "Hello" in the Class file and replace with "Hi".
How to read the Class file first, and how to replace the static content?
public class test {
public static void main(String[] args) {
System.out.println("Hello");
}
}
Is there any standard code(A template) for that ?
You need to use some Java bytecode instrumentation libraries like ASM. Good to start to read links are:
A Guide to Java Bytecode Manipulation with ASM
How To Modify Constant Pool Using ASM?
Does your solution need to be in Java? You can use Jawa to accomplish the same in Python with a lot less boilerplate than ASM and its equivalents.
Install it: pip install jawa
Then:
from jawa.constants import String
from jawa.classloader import ClassLoader
# Create a ClassLoader for the current directory
# and load your "test" class.
test = ClassLoader('.')['test']
# Find the first String with the value "Hello"
# in the constant pool.
constant = test.constants.find_one(
type_=String,
f=lambda c: c.string.value == 'Hello'
)
# Change it to your new value
constant.string.value = 'Hi'
# ... and save the new class.
with open('test.class', 'wb') as new_test:
test.save(new_test)
Result:
$ java test
Hi
Full documentation is at http://jawa.tkte.ch. Regardless of what tool you end up using it's absolutely required to read the JVM ClassFile specification or you won't really understand what's going on. You can find it at https://docs.oracle.com/javase/specs/jvms/se10/html/jvms-4.html.

How to access version of my project in build.gradle from a Java class

I'm quite new to Gradle so the answer might be simple, so I apologize if the answer is simple: I have a testing tool that needs to fetch it's version and compare it to the version of the application it is testing. However , the version of my tool is in my build.graddle as
version '1.0'
I tried different way to access it ( such as) :
task generateSources {
File outDir
outDir = file("$buildDir/classes/test/tests/Version.java")
doFirst {
outDir.exists() || outDir.mkdirs()
new File(outDir).write("public class Version { public static final String VERSION = \"$project.version\"; }")
}
}
compileJava.dependsOn generateSources
compileJava.source generateSources.outputs.files, sourceSets.main.java
I found this piece of code to output the version to another java file, but I fail to see how I'd be able to retrieve that info afterwards ( I mean, my tests are defined in src and I would need to point to a file that doesn't exist at compilation -- correct me if I'm wrong here).
Any idea on how I could accomplish this task?
First of all, you are trying to create java source file in your build/classes (it should contain compiled classes, not sources) directory, but you have to do it in your sources, otherwise it won't be compiled. And if you need this new class to be vailable not for tests, then use src/main/java, not src/test/java/
But anyway, I suppose for your case it's much easier to use some properties file for that and replace some token within it during build. That will allow you to make some static logic to get this property value and use it yet before running the build. So all you need is:
1- to have some properties file in your resources src/main/resources (for example app.properties), where should version variable be stored, with it's value like APP_VERSION_TOKEN
version=%APP_VERSION_TOKEN%
2- configure you Gradle processResources to replace tokens, something like this:
processResources {
filesMatching('**/app.properties') {
filter {
it.replace('%APP_VERSION_TOKEN%', version)
}
}
}
3- make some method to read this file and return the value of the property and use it where you need.
And that's all. For unit tests you can have another file with the same name under src/test/resource with the unchanging value you need for testing.

How is a token replaced in a file for a Gradle build product?

I have a normal buildscript for Gradle set up, and one thing I want to do is have the version of my build specified. This is the code I've set up to replace the version token in my main Java source file:
import org.apache.tools.ant.filters.ReplaceTokens
processResources {
from (sourceSets.main.java) {
include 'T145/myproj/Main.java'
filter(ReplaceTokens, tokens: ['#VERSION#' : project.version])
}
}
However it doesn't work. I tried using the replace function, but that didn't prove to be a success either. My Main.java has a public variable VERSION that equals #VERSION#, and that is what I want to be replaced.
Based on what I'm seeing in the Gradle manual example of ReplaceTokens, you want to get rid of the #'s in your filter line, so that it reads:
filter(ReplaceTokens, tokens: [VERSION : project.version])
Gradle assumes that the token it's looking for has the #'s delimiting it already, so it is trying to replace ##VERSION## instead of #VERSION#, like you want.

How to add a line in all java files in eclipse

I am not sure if that is possible but I have a old java application projects which have 1000+ java files. I am trying to add log4j support to the application which require me to add
public static Logger logger = Logger.getLogger(MyClass.class.getName());
in every file.
Is there any way I can perform the operation using eclipse. I have tried source->format but that is not allowing me to add the line. Do I have to open every file and add that line?
You could make use of templates in eclipse, but in this case, you need to edit each file and add it.
Update :
Save following content in some file-named with extension ".xml"
<?xml version="1.0" encoding="UTF-8" standalone="no"?><templates><template autoinsert="true" context="java-members" deleted="false" description="adds the logger statement" enabled="true" name="logger">public static Logger logger = Logger.getLogger(${enclosing_type}.class.getName());</template></templates>
Press CTRL+3, Type - "templates" and choose for Templates- Java Editor as shown below
Import the file from menu from right as shown below
now go to any of your file and type "logger"in your class file and do CTRL+space , quick assist will show you the "logger" template
as shown below
and your logging statement will appear, with your class in which you are editing as shown below
You can do it programatically. Start with a filter for all your .java:
public class FileExtensionFilter implements FilenameFilter {
private Set<String> filteredExtensions;
public FileExtensionFilter() {
filteredExtensions = new HashSet<String>();
}
#Override
public boolean accept(File dir, String name) {
boolean accept = true;
for (String filteredExtension:filteredExtensions) {
accept = accept && !name.endsWith(filteredExtension);
}
return accept;
}
public void addFilteredExtension(String extension) {
filteredExtensions.add(extension);
}
}
Then you can look for the file using a recursive method:
public Set<String> searchFileBasedOnExtension(File file) {
Set<String> extensions = new HashSet<String>();
if (file.isDirectory()) {
for (File f : file.listFiles(fileExtensionFilter)) {
extensions.addAll(checkForExtensions(f));
}
} else {
String extension = file.getName().substring(Math.max(file.getName().lastIndexOf('.'),0));
extensions.add(extension);
fileExtensionFilter.addFilteredExtension(extension);
}
return extensions;
}
Then based on the set you receive, you can iterate it, read the file to find the position to add the "import" and also find the class name, and save it into a variable to replace it for each file, since each file represents a different class.
Sample:
for (String s : setWithFileNames) {
// Use BufferedReader to read the file, save the content in a String, then look inside the String the classname and the first import position.
// Use bufferedWriter to re-write the file with the changes you made.
}
Hope it gives you a hand with your requirement. Best regards.
Opening each file and adding this line would be tedious.
I am not sure if Eclipse has such thing.
But I would suggest to go for a shell script or a Java function to do this.
Read each file.
Search for the first '{' character.
Insert the logger statement in the line next to that.
You can get the class name from the file name.
I know this might not be the best solution for you.
Hope this helps.
If you have notepad ++ you can do it. Use the find replace feature for a direcory using regular expressions.
so a line starting with public class need to be replaced by the line and another line with the log statement.
Another suggestion is to use AspectJ. AspectJ is an extension to Java that allows you to systematically weave in extra functionality to existing classes. You would first need to install AJDT (AspectJ development tools). Then you need to create an Aspect like this:
aspect LoggingAspect {
before (Object thiz) : execution(public * *(..)) && this(thiz) {
Logger logger = Logger.getLogger(thiz.getClass().getName());
logger.log("Something");
}
}
The aspect above will log every execution of a public method of one of the classes that you compile. You can certainly tweak this in many ways. Logging is one of the simplest things that AspectJ can do. The nice thing about going down this path is that you can easily enable/disable logging from your project by commenting out 4 lines.
The main AspectJ website. And the AspectJ programming guide is a good place to start with AspectJ.
You can run the below command to add any line on the specific line number, Just provide the folder name it will search all the subfolders with the file extension as .java and add the line on the specific position:
find <Folder Path> -name "*.java" -exec sed -i '1i<Text: You want to add>' {} \;

Unzip Archive with Groovy

is there a built-in support in Groovy to handle Zip files (the groovy way)?
Or do i have to use Java's java.util.zip.ZipFile to process Zip files in Groovy ?
Maybe Groovy doesn't have 'native' support for zip files, but it is still pretty trivial to work with them.
I'm working with zip files and the following is some of the logic I'm using:
def zipFile = new java.util.zip.ZipFile(new File('some.zip'))
zipFile.entries().each {
println zipFile.getInputStream(it).text
}
You can add additional logic using a findAll method:
def zipFile = new java.util.zip.ZipFile(new File('some.zip'))
zipFile.entries().findAll { !it.directory }.each {
println zipFile.getInputStream(it).text
}
In my experience, the best way to do this is to use the Antbuilder:
def ant = new AntBuilder() // create an antbuilder
ant.unzip( src:"your-src.zip",
dest:"your-dest-directory",
overwrite:"false" )
This way you aren't responsible for doing all the complicated stuff - ant takes care of it for you. Obviously if you need something more granular then this isn't going to work, but for most 'just unzip this file' scenarios this is really effective.
To use antbuilder, just include ant.jar and ant-launcher.jar in your classpath.
AFAIK, there isn't a native way. But check out this article on how you'd add a .zip(...) method to File, which would be very close to what you're looking for. You'd just need to make an .unzip(...) method.
The Groovy common extension project provides this functionality for Groovy 2.0 and above: https://github.com/timyates/groovy-common-extensions
The below groovy methods will unzip into specific folder (C:\folder). Hope this helps.
import org.apache.commons.io.FileUtils
import java.nio.file.Files
import java.nio.file.Paths
import java.util.zip.ZipFile
def unzipFile(File file) {
cleanupFolder()
def zipFile = new ZipFile(file)
zipFile.entries().each { it ->
def path = Paths.get('c:\\folder\\' + it.name)
if(it.directory){
Files.createDirectories(path)
}
else {
def parentDir = path.getParent()
if (!Files.exists(parentDir)) {
Files.createDirectories(parentDir)
}
Files.copy(zipFile.getInputStream(it), path)
}
}
}
private cleanupFolder() {
FileUtils.deleteDirectory(new File('c:\\folder\\'))
}
This article expands on the AntBuilder example.
http://preferisco.blogspot.com/2010/06/using-goovy-antbuilder-to-zip-unzip.html
However, as a matter of principal - is there a way to find out all of the properties, closures, maps etc that can be used when researching a new facet in groovy/java?
There seem to be loads of really useful things, but how to unlock their hidden treasures? The NetBeans/Eclipse code-complete features now seem hopelessly limited in the new language richness that we have here.
Unzip using AntBuilder is good way.
Second option is use an third party library - I recommend Zip4j
Although taking the question a bit into another direction, I started off using Groovy for a DSL that I was building, but ended up using Gradle as a starting point to better handle a lot of the file-based tasks that I wanted to do (eg., unzip and untar files, execute other programs, etc). Gradle builds on what groovy can do, and can be extended further via plugins.
// build.gradle
task doUnTar << {
copy {
// tarTree uses file ext to guess compression, or may be specific
from tarTree(resources.gzip('foo.tar.gz'))
into getBuildDir()
}
}
task doUnZip << {
copy {
from zipTree('bar.zip')
into getBuildDir()
}
}
Then, for example (this extracts the bar.zip and foo.tgz into the directory build):
$ gradle doUnZip
$ gradle doUnTar
def zip(String s){
def targetStream = new ByteArrayOutputStream()
def zipStream = new GZIPOutputStream(targetStream)
zipStream.write(s.getBytes())
zipStream.close()
def zipped = targetStream.toByteArray()
targetStream.close()
return zipped.encodeBase64()
}

Categories

Resources