square / javapoet Goto Github PK
View Code? Open in Web Editor NEWA Java API for generating .java source files.
License: Apache License 2.0
A Java API for generating .java source files.
License: Apache License 2.0
Can we do a slightly more paused merge process - merging early means that nits and style fixes needed a subsequent pull request and code we wouldn't want in gets in, even if just for a little while.
Maybe we can adopt a "ready to merge" (RTM) convention - like an author's LGTM since not all authors can self-merge.
If you write something like this:
methodBody
.addSnippet("if (%s == null) {", identifier)
.addSnippet("throw new NullPointerException(\"Null %s\");", identifier)
.addSnippet("}");
then you might expect that JavaWriter would automatically indent the throw statement but you would be wrong. Since we don't require people to manage indentation manually anywhere else, we shouldn't require it here either. Perhaps BlockWriter should have a method allowing you to write something like this:
methodBody
.addBlock("if (%s == null)", identifier)
.addSnippet("throw new NullPointerException(\"Null %s\");", identifier);
where .addBlock would return a nested BlockWriter. Some tweaks on this idea would be needed to get the usual formatting for if/else, try/catch/finally, etc.
May I ask again if you could deploy some snapshot to https://oss.sonatype.org/content/repositories/snapshots/com/squareup/ ?
This is blocking downstream projects (e.g. Wire, Dagger 2, bullet) from adopting JavaWriter 3.
They are exactly the same except for return type. Have the same implementation twice is bad news bears. Either refactor into a common-base class, make one extend the other, or just use MethodWriter
for constructors.
ClassWriter has a List<TypeVariableName> typeVariables
but no way to set it. Also, since interfaces can also have type parameters, this doesn't belong exclusively on ClassWriter.
Currently emitAnnotation will do a toString() on the value of an annotation attribute. However this becomes a nuisance if the value itself is an annotation instance with its own values.
Is there perhaps a technique (and if not please add it!) to easily define a new annotation as the value of an emitted annotation, that would function in the same way as an emitted annotation?
Javadoc on type, field, method. Would be nice to have top-of-file command for license and/or generated warning as well. In its dumbest form this just need to take a string (or aggregate strings).
Interesting things here might be @param
support on a ParameterWriter
(VariableWriter
subclass)? @throws
on a ThrowsWriter
? @return
somewhere?
You would expect this to work so that you could use the result with TypeWriter.addMethod(TypeMirror, String) to define a method that returns a type variable.
As suggested in #109, VoidTypeName
suggests that void
is a type, which it is not, and allows users to accidentally ask for void fields and parameters. The only place where void
can appear is to specify the result of a method in MethodWriter
, when the method does not return a value. As an alternative design, we could delete VoidTypeName
and instead have the returnType
parameter of the MethodWriter
constructor be an Optional<TypeName>
; or have a second constructor that omits the returnType
for use when the method is void; or introduce a new supertype of TypeName
called MethodReturn
, with a new class VoidReturn
that is also a subclass of TypeNameOrVoid
.
Currently, the field names are directly derived from the .proto
files. However, it would be better if while generating the code JavaWriter could follow some conventions like:
Right now, a field declared as request_id
remains as request_id
in Java Code and sometimes feels weird to be calling a function like request_id(21)
javax.lang.model.type.UnknownTypeException: Unknown type: java.lang.Number&java.lang.Runnable
at javax.lang.model.util.AbstractTypeVisitor6.visitUnknown(AbstractTypeVisitor6.java:150)
at javax.lang.model.util.AbstractTypeVisitor6.visitIntersection(AbstractTypeVisitor6.java:133)
at com.sun.tools.javac.code.Type$IntersectionClassType.accept(Type.java:1019)
at com.squareup.javawriter.TypeNames.forTypeMirror(TypeNames.java:52)
$ mvn -v
Apache Maven 3.2.3 (33f8c3e1027c3ddde99d3cdebad2656a31e8fdf4; 2014-08-11T13:58:10-07:00)
Maven home: /usr/local/Cellar/maven/3.2.3/libexec
Java version: 1.8.0_25, vendor: Oracle Corporation
Java home: /Library/Java/JavaVirtualMachines/jdk1.8.0_25.jdk/Contents/Home/jre
Default locale: en_US, platform encoding: UTF-8
OS name: "mac os x", version: "10.9.5", arch: "x86_64", family: "mac"
cc @tbroyer
See: c268516#commitcomment-7929594
Easy to fix.
would be cool to have something publish snapshots under here: https://oss.sonatype.org/content/repositories/snapshots/com/squareup/
A cool feature would be the stable generation of a version UID for serialization. We see other code gen implementations change this value all the time because they use things like hash functions on the string output (e.g., jOOQ) which unnecessarily churns this value.
Currently only logic for a single value exists.
if (!memberMap.isEmpty()) {
appendable.append('(');
if (memberMap.size() == 1) {
Entry<String, Writable> onlyEntry = Iterables.getOnlyElement(memberMap.entrySet());
if (!onlyEntry.getKey().equals("value")) {
appendable.append(onlyEntry.getKey()).append(" = ");
}
onlyEntry.getValue().write(appendable, context);
}
appendable.append(')');
}
Some failing tests:
@Test public void interfaceStaticMethod() throws IOException {
javaWriter.emitPackage("com.squareup");
javaWriter.beginType("com.squareup.Foo", "interface", EnumSet.noneOf(Modifier.class));
javaWriter.beginMethod("Foo", "empty", EnumSet.of(STATIC));
javaWriter.emitStatement("return new Foo() {}");
javaWriter.endMethod();
javaWriter.endType();
assertCode(""
+ "package com.squareup;\n"
+ "\n"
+ "interface Foo {\n"
+ " static Foo empty() {\n"
+ " return new Foo() {};\n"
+ " }\n"
+ "}\n");
}
@Test public void interfaceDefaultMethod() throws IOException {
javaWriter.emitPackage("com.squareup");
javaWriter.beginType("com.squareup.Foo", "interface", EnumSet.noneOf(Modifier.class));
javaWriter.beginMethod("String", "asString", EnumSet.of(DEFAULT));
javaWriter.emitStatement("return toString()");
javaWriter.endMethod();
javaWriter.endType();
assertCode(""
+ "package com.squareup;\n"
+ "\n"
+ "interface Foo {\n"
+ " default String asString() {\n"
+ " return toString();\n"
+ " }\n"
+ "}\n");
}
If you are generating a subclass of a class like this...
// Parent.java
class Parent {
static class Optional {}
}
// Generated Child.java
class Child extends Parent {
java.util.Optional<String> optionalString() {...}
}
...then it is not correct to import java.util.Optional and abbreviate to "Optional optionalString()", because the inherited nested Optional takes precedence over the import statement. Since JavaWriter typically doesn't know anything about Parent in this case, there should be some way of telling it to fully-qualify a type even if it would otherwise be inclined to import it.
This is an actual case that occurs with AutoValue in code at Google. (AutoValue doesn't use JavaWriter and can't as long as this bug remains.)
TypeWriter has a field Optional supertype but no way to set it. Also, it probably belongs to ClassWriter rather than TypeWriter, since other kinds of types don't have settable supertypes.
public static final Set<String> test = ImmutableSet.<String>builder()
.add("Hello")
.add("World")
.build();
@JakeWharton ping? I can't release this, and HEAD now has a bunch of important fixes and API improvements we'd like to consume.
There is no way from outside the JavaWriter package to obtain an instance of PrimitiveName. The static methods should be public.
I'd like to be able to add throws SomeException
clauses to method declarations. I've been poking around and didn't see this functionality. Do you guys not do this at Square (you must be emiting control flows for exceptions)?
ClassWriter cw = ClassWriter.forClassName(ClassName.bestGuessFromString("example.Test"));
cw.addMethod(VoidName.VOID, "test");
System.out.println(cw);
package example;
class Test {
void test();
}
This is unfortunate, unexpected behavior. Only if the method is declared abstract should this behavior happen.
Not quite sure the right approach to take with this. We can either propagate these implementation details downward into Modifiable
or switch to private fields with public getters (e.g., Set<Modifier> modifiers()
) which can be overridden to return subsets.
Right now static methods are emitted below constructors.
Seems like the only logical grouping we should make. There are other one's in practice (public static methods before lesser scoped static methods, public instance methods before lesser scoped instance methods, etc.) but I don't think they should be addressed at this time (if ever).
There were many missing types from this set in various classes. See #130.
compressType("java.lang.annotation.Annotation") returns "annotation.Annotation" which does not compile :(
Test case is generating an implementation of Collections.addAll
.
If JavaWriter
provided a StringBuilder
that could be written to instead of writing to the Writer
, then it could reduce the amount of loops I need to generate code. For example, if I have a list of "model"s. I need to generate a field for each model, and also an if-else-if-else block for each model. Ideally I could iterate the model list once, but if you have to write the lines sequentially through the JavaWriter
, then the loop has to be repeated in the different locations in the "file".
I have a workaround that helps in some cases, which is I create a StringBuilder
and build into it, but this doesn't resolve all situations, because the JavaWriter
doesn't allow arbitrary String
insertion.
To be verbose, I have an idea for adding a class that is essentially a StringBuilder
that can't be constructed outside of JavaWriter
, but can be passed in place of a Writer
, allowing chunks of code to be generated in it. Then, the StringBuilder
-like class can be written by the JavaWriter
where the block should be in the final generated file. The library doesn't manage the StringBuilder
-like class, only an agreement that the String
in it is built to be valid Java. I'm curious if the library is interested in such a feature.
Writing Something like this:
javaWriter.beginMethod("void", "save", Sets.newHashSet(Modifier.PUBLIC), "ContentValues contentValues");
Will thrown an ArrayIndexOutOfBoundsException
instead of checking to ensure the parameters are perfectly even. May I suggest a throwable with a better message?
ClassSpec c = ClassSpec.builder("Taco") // proto-like pattern
.addMethod(MethodSpec.builder("toString")
.addAnnotation(Override.class)
.modifiers(PUBLIC, FINAL)
.returns(String.class)
.code("return \"taco\"")
.build())
.addMethod(MethodSpec.builderOverriding(executableElement)
.addSnippet(Snippet.format("return 0;"))
.build())
.build()
To be set on JavaWriter
public JavaWriter addStaticImport(Class<?> cls, String methodName) { }
private final SetMultimap<ClassName, String> staticImports = LinkedHashMultimap.create();
Should be a bit more straightforward to emit since the logic for actually choosing which imports only applies to the current and parent contexts in the same file. A much smaller scope than regular import resolution.
You would expect to be able to write classWriter.addMethod(int.class, "hashCode") but actually it produces a NullPointerException.
In both v3 and v2 knowledge of how contents were being emitted leaks around too far for my liking. In almost all cases of writing you have a destination folder and want normal Java folder and file naming semantics inferred from package and class name.
The one potential exception to this rule is annotation processors which go through the Filer
. I don't think we necessarily need to cover that use case, but if it can be done easily that would be a nice touch.
Currently it looks like there's no way to add annotations (like @NonNull
, @Nullable
) to method parameters.
There are parts of code to which we can apply sensible wrapping. Right column break to be configured on JavaWriter
.
Candidates include:
The end-of-line comment has little value with it's current behavior.
w.emitStatement("foo()");
w.emitEndOfLineComment("Call foo!");
produces something like:
...
foo();
// Call foo!
...
Its Javadoc is also wrong.
java.lang.reflect.Modifier is pretty gross and makes for a bunch of code littered with ints and ORs. In the land of annotation processing we have a better alternative: http://docs.oracle.com/javase/6/docs/api/javax/lang/model/element/Modifier.html
I'd like to add versions of all of the JavaWriter methods that take Set instead of int. Then, ideally we'd phase out the int methods at some point in the future.
Since it's a fair bit of work, I wanted some consensus before bothering to do all of the work. Any objections?
This will allow generics over array types as well as generics over primitives for Java 10(ish).
Lambdas are interesting syntactical elements. Should we have first-party support for them or just rely on normal Snippet
behavior for them?
A first-party type for supporting lambdas would deal with:
(Runnable r) -> r.run()
).I'm not sure how important it is to be able to generate classes in the default package, but it currently doesn't work because of a couple of issues in JavaWriter.java: it should not generate a package statement in this case, and it should consider className.packageName().isEmpty() as an additional reason not to import className.
Currently it doesn't look like there is an easy way to write
MyInterface foo = new MyInterface() {
@Override
public void bar() {
// do something awesome
}
};
A possible api would be
beginAnonymousInnerClass(String kind);
beginAnonymousInnerClass(String fieldType, String fieldName, Set<Modifier> modifiers, String kind);
endAnonymousInnerClass();
The extended begin method would result in
modifiers fieldType fieldName = new kind() {
I think BlockWriter
might be more useful if it was in control if emitting braces. This is a trivial change for methods and ctors, but I think the value is in user snippets as well.
For example,
if (thing) %s
Could take a BlockWriter
and emit
if (thing) {
doSomething();
doSomethingElse();
return true;
}
This would work well for switch
as well:
case "hello": %s
case "hi": %s
Could emit:
case "hello": {
String name = getName();
return "Hello, " + name;
}
case "hi": {
String name = getName();
return "Hi, " + name;
}
I'm now in the process, but this is a tracking bug.
Question for @JakeWharton, @swankjesse, @gk5885 - are we wanting square/javawriter to be the source of this? In this case, I'll make sure that google/javawriter is a fork of square/javawriter, and we do pulls that way. Or do we wnat to go the other direction, or what? I'm ok with any of it, just want to know which way to orient these repos.
This issue will serve as a working space for what a potential version 3.0 would look like with a new, more dynamic, and more powerful API.
Currently, type compression via imports requires knowledge of all types up before the body of the file. In practice, this usually requires an awkward two-pass approach where you determine the imports based on a pass of dynamic contents and then emit the body based on a second pass.
The goal of this is to always emit fully-qualified types from the API and rely on a post-compression phase for emitting imports.
There are multiple ways to approach this. A non-exhaustive list:
Objects which represents pieces of code lend themselves naturally to writing emission code in Java. The wins should hopefully be fairly obvious.
There is a potential for API explosion here to cover the diversity of the Java language. A potential also exists for adding an unacceptable amount of boilerplate to the API.
Which of the following API design is better on interface
and abstract
methods that will support annotated parameters?
javaWriter.beginMethodSignature(returnType, methodName, modifiers);
javaWriter.emitAnnotation(annotationType);
javaWriter.emitParam(paramType, paramName);
javaWriter.endMethodSignature();
or
javaWriter.beginMethodSignature(returnType, methodName, modifiers);
javaWriter.emitAnnotation(annotationType);
javaWriter.emitParam(paramType, paramName);
javaWriter.endMethodSignature();
javaWriter.endMethod(); // <== Regardless of the type definition or modifier??
Hi,
i use eclipse w/o gradle or maven. I addede groundy as library and compiler as annotation processor to the project. Everything works right except @param.
when i try to use @onProgress or @para i get error.
as i understand compiler use annotation Param but it doesn't present in his classpath.
i tried to add it to the jar but eclipse gets crash.
i can't understand why because in my own processor(AnnotatedSQL project) i use annotaions in compiler without problems
Error:
Errors occurred during the build.
Errors running builder 'Java Builder' on project .
com/telly/groundy/annotations/Param
Thanks,
Gennadiy
When there exists the same named simple type in the package of the class being written and also imports, parameterized types should retain the package.
Failing test case below:
@Test public void compressSimpleNameCollisionInSamePackage() throws IOException {
javaWriter.emitPackage("denominator");
javaWriter.emitImports("javax.inject.Provider", "dagger.internal.Binding");
String actual = javaWriter.compressType("dagger.internal.Binding<denominator.Provider>");
assertThat(actual).isEqualTo("Binding<denominator.Provider>");
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.