Quickest way to use common OpenOption combinations - java

Is there a concise, idiomatic way (maybe using Apache Commons) to specify common combinations of OpenOption like StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING

These are the easy possibilities you have.
Static Imports, to increase readability:
import static java.nio.file.StandardOpenOption.CREATE_NEW;
import static java.nio.file.StandardOpenOption.WRITE;
OpenOption[] options = new OpenOption[] { WRITE, CREATE_NEW };
Use defaults:
//no Options anyway
Files.newBufferedReader(path, cs)
//default: CREATE, TRUNCATE_EXISTING, and WRITE not allowed: READ
Files.newBufferedWriter(path, cs, options)
//default: READ not allowed: WRITE
Files.newInputStream(path, options)
//default: CREATE, TRUNCATE_EXISTING, and WRITE not allowed: READ
Files.newOutputStream(path, options)
//default: READ do whatever you want
Files.newByteChannel(path, options)
Finally it's possible to specify optionsets like this:
Files.newByteChannel(path, EnumSet.of(CREATE_NEW, WRITE));

The best suggestion I can offer would be to cheat on the equivalence of T... and T[], which one of the other stackoverflow discussions says should work
Can I pass an array as arguments to a method with variable arguments in Java?
So...
OpenOption myOptions[] = {StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING};
OutputStream foo=OutputStream.newOutputStream(myPath,myOptions);
Caveat: Untested.

java.nio.file.Files has 5 flavours of methods with OpenOption varargs parameters:
Files
.newBufferedWriter(...)
.write(...)
.newOutputStream(...)
.newInputStream(...)
.newByteChannel(...)
They directly don't restrict any OpenOption combination, but all of them under the hood call to some of these 3 methods at java.nio.file.spi.FileSystemProvider:
FileSystemProvider
.newInputStream(Path, OpenOption...)
.newOutputStream(Path, OpenOption...)
.newByteChannel(Path, Set<? extends OpenOption>, FileAttribute<?>...)
FileSystemProvider.newInputStream(...) is called by: Files.newInputStream(...)
FileSystemProvider.newOutputStream(...) is called by:
Files
.newBufferedWriter(...)
.newOutputStream(...)
.write(...)
abstract FileSystemProvider.newByteChannel(...) is called by:
Files.newByteChannel(...)
FileSystemProvider.newInputStream(...)
FileSystemProvider.newOutputStream(...)
OptenOption combination restrictions:
FileSystemProvider.newInputStream(...)
UnsupportedOperationException: WRITE || APPEND
FileSystemProvider.newOutputStream(...)
Implicitly: WRITE
IllegalArgumentException: READ
default (if non options): CREATE && TRUNCATE_EXISTING
The abstract FileSystemProvider.newByteChannel(...) method has a platform dependent implementation, which may extend the OpenOption combination restrictions (as in sun.nio.fs.WindowsFileSystemProvider).
All Files method which uses OpenOption vargars under the hood ends in the abstract FileSystemProvider.newByteChannel(...), which implementation is platform dependent. So, the OpenOption combinations restriction in Files methods are platform dependent.

Related

Looking for files of certain extension using lambda

As an exercise, I decide to rewrite some code of mine to use lambda expression. The code should check if a given string is a path to a file with .pdf extension and then it should display all the files that meet this requirement. This so what I've already come up with:
Files.newDirectoryStream(Paths.get(args[0]), path -> path.toFile()
.toString()
.endsWith(".pdf"))
.forEach(System.out::println);
This code fails at one point: it also displays directories. Could you tell me why the following code fails to compile?
Files.newDirectoryStream(Paths.get(args[0]), path -> path.toFile()
.isFile()
.toString()
.endsWith(".pdf"))
.forEach(System.out::println);
Your second code fails to compile because isFile returns a boolean. Once you have a boolean, file name is gone; even if you could convert it to String, matching its suffix to ".pdf" would fail anyway.
You are testing two separate conditions, so you should test them in two separate checks:
Files.newDirectoryStream(Paths.get(args[0]), path ->
Files.isRegularFile(path) && path.toString().endsWith(".pdf")
).forEach(System.out::println);
Note that path.toString().endsWith(...) can be checked without converting Path to File.
Because the compiler expects to have a boolean as second argument of
newDirectoryStream(Path dir, DirectoryStream.Filter<? super Path> filter)
Filter being a functional interface defined as :
boolean accept(T entry) throws IOException;
But here :
.isFile()
.toString()
a String is returned.
Make things in two times by combining two boolean expressions :
Files.newDirectoryStream(Paths.get(args[0]), path ->
Files.isRegularFile(path)
&& path.toString().endsWith(".pdf"))
.forEach(System.out::println);
In addition to Andreas comment about :
path.toFile().toString().endsWith(".pdf"))
that may be abbreviated by : path.toString().endsWith(".pdf"), you could also replace
path.toFile().isFile() by Files.isRegularFile(path).
It allows to rely only on the java.nio.file API rather that mixing it with the java.io.file API.

Get Java reflection representation of Scala type

This seems like a simple question, but it's very challenging to search for, so I'm asking a new question. My apologies if it's already been asked.
Due to the compiler bug described here Scala 2.11.5 compiler crash with type aliases and manifests (also here https://issues.scala-lang.org/browse/SI-9155), I need to use scala TypeTags and friends for discovery of type parameters to methods. However, I then need to use that type information in a Java library that uses java.lang.Class and java.lang.reflect.Type.
How can I convert a scala.reflect.runtime.universe Type into a java.lang.reflect.Type or java.lang.Class?
Put concretely, how would I fill out the body of this method:
def typeFor[T](implicit tag: TypeTag[T]): java.lang.reflect.Type = ...
or, if that's not possible:
def typeFor[T](implicit tag: TypeTag[T]): java.lang.Class[T] = ...
And note, due to the bug posted above, I cannot use scala.reflect.Manifest.
The short answer is no, but you can try to do something similar to this SO question. However there is an open ticket....
This may have some limitations I'm not aware of, but you could drop down to Java reflection and try something like:
import scala.util.control.Exception._
def typeMe[T](implicit t: TypeTag[T]) = {
catching(classOf[Exception]) opt Class.forName(t.tpe.typeSymbol.asClass.fullName)
}
println(typeMe[String])
println(typeMe[ClassTag[_]])
Results in:
Some(class java.lang.String)
Some(interface scala.reflect.ClassTag)
The way I solved it with manifests, was:
private def typeFromManifest(m: Manifest[_]): Type = {
if (m.typeArguments.isEmpty) { m.runtimeClass }
else new ParameterizedType {
def getRawType = m.runtimeClass
def getActualTypeArguments = m.typeArguments.map(typeFromManifest).toArray
def getOwnerType = null
}
}
Right now I'm trying to solve this using something other than Manifest which should be removed from scala runtime.

Construct the stackmap of method while using bcel

I am trying bcel to modify a method by inserting invoke before specific instructions.
It seems that my instrumentation would result in a different stackmap table, which can not be auto-generated by the bcel package itself.
So, my instrumented class file contains the old stackmap table, which would cause error with jvm.
I haved tried with removeCodeAttributes, the method of MethodGen, that can remove all the code attributes. It can work in simple cases, a wrapped function, for example. And it can not work in my case now.
public class Insert{
public static void main(String[] args) throws ClassFormatException, IOException{
Insert isrt = new Insert();
String className = "StringBuilder.class";
JavaClass jclzz = new ClassParser(className).parse();
ClassGen cgen = new ClassGen(jclzz);
ConstantPoolGen cpgen = cgen.getConstantPool();
MethodGen mgen = new MethodGen(jclzz.getMethods()[1], className, cpgen);
InstructionFactory ifac = new InstructionFactory(cgen);
InstructionList ilist = mgen.getInstructionList();
for (InstructionHandle ihandle : ilist.getInstructionHandles()){
System.out.println(ihandle.toString());
}
InstructionFinder f = new InstructionFinder(ilist);
InstructionHandle[] insert_pos = (InstructionHandle[])(f.search("invokevirtual").next());
Instruction inserted_inst = ifac.createInvoke("java.lang.System", "currentTimeMillis", Type.LONG, Type.NO_ARGS, Constants.INVOKESTATIC);
System.out.println(inserted_inst.toString());
ilist.insert(insert_pos[0], inserted_inst);
mgen.setMaxStack();
mgen.setMaxLocals();
mgen.removeCodeAttributes();
cgen.replaceMethod(jclzz.getMethods()[1], mgen.getMethod());
ilist.dispose();
//output the file
FileOutputStream fos = new FileOutputStream(className);
cgen.getJavaClass().dump(fos);
fos.close();
}
}
Removing a StackMapTable is not a proper solution for fixing a wrong StackMapTable. The important cite is:
4.7.4. The StackMapTable Attribute
In a class file whose version number is 50.0 or above, if a method's Code attribute does not have a StackMapTable attribute, it has an implicit stack map attribute (§4.10.1). This implicit stack map attribute is equivalent to a StackMapTable attribute with number_of_entries equal to zero.
Since a StackMapTable must have explicit entries for every branch target, such an implicit StackMapTable will work with branch-free methods only. But in these cases, the method usually doesn’t have an explicit StackMapTable anyway, so you wouldn’t have that problem then (unless the method had branches which your instrumentation removed).
Another conclusion is that you can get away with removing the StackMapTable, if you patch the class file version number to a value below 50. Of course, this is only a solution if you don’t need any class file feature introduced in version 50 or newer…
There was a grace period in which JVMs supported a fall-back mode for class files with broken StackMapTables just for scenarios like yours, where the tool support is not up-to-date. (See -XX:+FailoverToOldVerifier or -XX:-UseSplitVerifier) But the grace period is over now and that support has been declined, i.e. Java 8 JVMs do not support the fall-back mode anymore.
If you want to keep up with the Java development and instrument newer class files which might use features of these new versions you have only two choices:
Calculate the correct StackMapTable manually
Use a tool which supports calculating the correct StackMapTable attributes, e.g. ASM, (see java-bytecode-asm) does support it

Warning: File for type '[Insert class here]' created in the last round will not be subject to annotation processing

I switched an existing code base to Java 7 and I keep getting this warning:
warning: File for type '[Insert class here]' created in the last round
will not be subject to annotation processing.
A quick search reveals that no one has hit this warning.
It's not documented in the javac compiler source either:
From OpenJDK\langtools\src\share\classes\com\sun\tools\javac\processing\JavacFiler.java
private JavaFileObject createSourceOrClassFile(boolean isSourceFile, String name) throws IOException {
checkNameAndExistence(name, isSourceFile);
Location loc = (isSourceFile ? SOURCE_OUTPUT : CLASS_OUTPUT);
JavaFileObject.Kind kind = (isSourceFile ?
JavaFileObject.Kind.SOURCE :
JavaFileObject.Kind.CLASS);
JavaFileObject fileObject =
fileManager.getJavaFileForOutput(loc, name, kind, null);
checkFileReopening(fileObject, true);
if (lastRound) // <-------------------------------TRIGGERS WARNING
log.warning("proc.file.create.last.round", name);
if (isSourceFile)
aggregateGeneratedSourceNames.add(name);
else
aggregateGeneratedClassNames.add(name);
openTypeNames.add(name);
return new FilerOutputJavaFileObject(name, fileObject);
}
What does this mean and what steps can I take to clear this warning?
Thanks.
The warning
warning: File for type '[Insert class here]' created in the last round
will not be subject to annotation processing
means that your were running an annotation processor creating a new class or source file using a javax.annotation.processing.Filer implementation (provided through the javax.annotation.processing.ProcessingEnvironment) although the processing tool already decided its "in the last round".
This may be problem (and thus the warning) because the generated file itself may contain annotations being ignored by the annotation processor (because it is not going to do a further round).
The above ought to answer the first part of your question
What does this mean and what steps can I take to clear this warning?
(you figured this out already by yourself, didn't you :-))
What possible steps to take? Check your annotation processors:
1) Do you really have to use filer.createClassFile / filer.createSourceFile on the very last round of the annotaion processor? Usually one uses the filer object inside of a code block like
for (TypeElement annotation : annotations) {
...
}
(in method process). This ensures that the annotation processor will not be in its last round (the last round always being the one having an empty set of annotations).
2) If you really can't avoid writing your generated files in the last round and these files are source files, trick the annotation processor and use the method "createResource" of the filer object (take "SOURCE_OUTPUT" as location).
In OpenJDK test case this warning produced because processor uses "processingOver()" to write new file exactly at last round.
public boolean process(Set<? extends TypeElement> elems, RoundEnvironment renv) {
if (renv.processingOver()) { // Write only at last round
Filer filer = processingEnv.getFiler();
Messager messager = processingEnv.getMessager();
try {
JavaFileObject fo = filer.createSourceFile("Gen");
Writer out = fo.openWriter();
out.write("class Gen { }");
out.close();
messager.printMessage(Diagnostic.Kind.NOTE, "File 'Gen' created");
} catch (IOException e) {
messager.printMessage(Diagnostic.Kind.ERROR, e.toString());
}
}
return false;
}
I modified original example code a bit. Added diagnostic note "File 'Gen' created", replaced "*" mask with "org.junit.runner.RunWith" and set return value to "true". Produced compiler log was:
Round 1:
input files: {ProcFileCreateLastRound}
annotations: [org.junit.runner.RunWith]
last round: false
Processor AnnoProc matches [org.junit.runner.RunWith] and returns true.
Round 2:
input files: {}
annotations: []
last round: true
Note: File 'Gen' created
Compilation completed successfully with 1 warning
0 errors
1 warning
Warning: File for type 'Gen' created in the last round will not be subject to annotation processing.
If we remove my custom note from log, it's hard to tell that file 'Gen' was actually created on 'Round 2' - last round. So, basic advice applies: if in doubt - add more logs.
Where is also a little bit of useful info on this page:
http://docs.oracle.com/javase/7/docs/technotes/tools/solaris/javac.html
Read section about "ANNOTATION PROCESSING" and try to get more info with compiler options:
-XprintProcessorInfo
Print information about which annotations a processor is asked to process.
-XprintRounds Print information about initial and subsequent annotation processing rounds.
I poked around the java 7 compiler options and I found this:
-implicit:{class,none}
Controls the generation of class files for implicitly loaded source files. To automatically generate class files, use -implicit:class. To suppress class file generation, use -implicit:none. If this option is not specified, the default is to automatically generate class files. In this case, the compiler will issue a warning if any such class files are generated when also doing annotation processing. The warning will not be issued if this option is set explicitly. See Searching For Types.
Source
Can you try and implicitly declare the class file.

Preserving parameter/argument names in compiled Java classes

When I compile something like this:
public class MyClass
{
void myMethod(String name, String options, String query, String comment)
{
...
}
}
and compile this to a class file, it seems that argument names are lost. That is, when some other Java code references MyClass and wants to call or overwrite myMethod, my IDE (currently Eclipse) seems to get this method signature from the class-file:
void myMethod(String arg0, String arg1, String arg2, String arg3);
I know that Eclipse (and possibly other IDEs too) allows me to provide a link to the source or the Javadoc (as Bishiboosh pointed out) of MyClass and can take advantage of this. But I'm curious if there is some way to tell javac to include the names into the class-file, so that users of that class can see the argument names even if they only have the class file.
Solution for classes
When I compile a class with java -g:vars, the names of parameters are included in the class file. -g:vars seems to be equivalent to Eclipse -> project properties -> Java compiler -> Add variable attributes to generated class files.
This solution was suggested by several authors, but the answer from Nick finally made me believe.
On my machine, Eclipse sometimes used this info, sometimes it didn't, which was probably my fault or a bug in Eclipse, but not a problem with the class files or the compile. Anyway, now I know that the information is definitely present.
But no solution for interfaces
While this works (kind of) fine for classes, it's not working for interfaces.
For me, the logical reason seems to be, that -g:vars only provides the names of local variables, which is what the documentation for javac also states. In the body of a method, it's parameters are very similar to local variables, thus they are covered by -g:vars. interface methods don't have bodies, so they can't have local variables.
My initial question only asked for classes, because I was not aware that there might be any difference.
Class file format
As gid pointed out, the class file format does not support storrage of parameter names. I found a section in the class file spec that descibes a data struture which should holf the parameter names of methods, but this is definitely not used when compiling interfaces.
When compiling a class, I can't tell if the mentioned data structure is used, or if Eclipse infers the parameter names from the usage of parameters inside the method body. An expert could clarify this, but it's not that relevant I think.
To preserve names in the class file for debugging purposes try project properties, Java compiler, then "Add variable attributes to generated class files" (See Eclipse Help).
Compiling the following source:
public class StackOverflowTest {
public void test(String foo, String bar) {
// blah
}
}
Is decompiled into:
// Compiled from StackOverflowTest.java (version 1.5 : 49.0, super bit)
public class StackOverflowTest {
// Method descriptor #6 ()V
// Stack: 1, Locals: 1
public StackOverflowTest();
0 aload_0 [this]
1 invokespecial java.lang.Object() [8]
4 return
Line numbers:
[pc: 0, line: 1]
Local variable table:
[pc: 0, pc: 5] local: this index: 0 type: StackOverflowTest
// Method descriptor #15 (Ljava/lang/String;Ljava/lang/String;)V
// Stack: 0, Locals: 3
public void test(java.lang.String foo, java.lang.String bar);
0 return
Line numbers:
[pc: 0, line: 4]
Local variable table:
[pc: 0, pc: 1] local: this index: 0 type: StackOverflowTest
[pc: 0, pc: 1] local: foo index: 1 type: java.lang.String
[pc: 0, pc: 1] local: bar index: 2 type: java.lang.String
}
See the parameter names are preserved in the class files.
I would suggest you look into how your source is being compiled, which version it is compiled for etc.
EDIT:
Ah, I see this is different for interfaces - they don't seem to have this information available for the debugger which I guess makes sense. I don't think there'll be a way round this, if you just want to see the parameter names when you're editing source you'll need to go the javadoc route as Nagrom_17 suggests (attach the source).
You don't specially need the source to make arg names appear in Eclipse...If you specify the Javadoc, Eclipse will display the args.
It might help to compile with debug support, which stores all names in the .class file.
though I don't know whether Eclipse takes that into account.
Eclipse will pick up the names of arguments if you include debug information in the class file: javac -g:vars should be enough.
There is no support in the class file data structure for storing the parameter names to any method, no matter what javac options you use.
In order to see the original names in an IDE you have to supply them with either the javadoc or the source.
If you have a particular need to get at them at runtime it is possible to add annotations to parameters, but you'll have to create your own as there isn't a standard set to use.
Sorry can't be more helpful
EDIT:
I stand completely corrected...the class file format does clearly have space for named parameters (JLS 4.7)
What I can't see is how the hell you can get at them using java.lang.reflect.*
You don't need a separate Javadoc file you can create 'inline' javadocs in Eclipse using a special comment with two asterisks(*) after the first slash of a multi-line comment.
example code:
public class MyClass
{
/**
* documentation of your method
*
* #param name a String describing the name
* #param options used to describe current option
* #param query
* #param comment
* #return void
*/
void myMethod(String name, String options, String query, String comment)
{
...
}
}

Categories

Resources