Giter Site home page Giter Site logo

freebuilder's People

Contributors

alicederyn avatar casidiablo avatar crogers avatar dorireuv avatar eischet avatar hassanmansoor avatar hdurer avatar j-baker avatar lvjp avatar masterav10 avatar matvore avatar sdqali avatar thespags avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

freebuilder's Issues

Nested @FreeBuilder Bar in a separate file doesn't generate get getBarBuilder

For

@FreeBuilder
public interface Foo {
  Bar getBar();
  static class Builder extends Foo_Builder() {}
  @FreeBuilder
  public interface Bar {
   String getBlah();
   static class Builder extends Foo_Bar_Builder() {}
  }
}

this works fine:
java new Foo.Builder().getBarBuilder()

But for:

@FreeBuilder
public interface Foo {
  Bar getBar();
  static class Builder extends Foo_Builder() { }
}
@FreeBuilder
public interface Bar {
 String getBlah();
 static class Builder extends Bar_Builder() {}
}

java new Foo.Builder().getBarBuilder() doesn't exist.

Enable compilation of tests with Java 8

I'm having trouble getting FreeBuilder to compile on my Mac:

% java -version
java version "1.8.0_60"
Java(TM) SE Runtime Environment (build 1.8.0_60-b27)
Java HotSpot(TM) 64-Bit Server VM (build 25.60-b23, mixed mode)

% mvn -version
Apache Maven 3.3.3 (7994120775791599e205a5524ec3e0dfe41d4a06; 2015-04-22T04:57:37-07:00)
Maven home: /usr/local/Cellar/maven/3.3.3/libexec
Java version: 1.8.0_60, vendor: Oracle Corporation
Java home: /Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre
Default locale: en_US, platform encoding: UTF-8
OS name: "mac os x", version: "10.10.4", arch: "x86_64", family: "mac"

% mvn test-compile
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building @FreeBuilder 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ freebuilder ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /Users/paul/projects/FreeBuilder/src/main/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.2:compile (default-compile) @ freebuilder ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ freebuilder ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /Users/paul/projects/FreeBuilder/src/test/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.2:testCompile (default-testCompile) @ freebuilder ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 42 source files to /Users/paul/projects/FreeBuilder/target/test-classes
[INFO] -------------------------------------------------------------
[ERROR] COMPILATION ERROR :
[INFO] -------------------------------------------------------------
[ERROR] /Users/paul/projects/FreeBuilder/src/test/java/org/inferred/freebuilder/processor/util/PackageElementImpl.java:[34,8] org.inferred.freebuilder.processor.util.PackageElementImpl is not abstract and does not override abstract met
hod <A>getAnnotationsByType(java.lang.Class<A>) in javax.lang.model.AnnotatedConstruct
[ERROR] /Users/paul/projects/FreeBuilder/src/test/java/org/inferred/freebuilder/processor/util/PackageElementImpl.java:[114,3] org.inferred.freebuilder.processor.util.PackageElementImpl.PackageTypeImpl is not abstract and does not over
ride abstract method <A>getAnnotationsByType(java.lang.Class<A>) in javax.lang.model.AnnotatedConstruct
[ERROR] /Users/paul/projects/FreeBuilder/src/test/java/org/inferred/freebuilder/processor/GenericTypeElementImpl.java:[90,3] org.inferred.freebuilder.processor.GenericTypeElementImpl.GenericTypeMirrorImpl is not abstract and does not o
verride abstract method <A>getAnnotationsByType(java.lang.Class<A>) in javax.lang.model.AnnotatedConstruct
[ERROR] /Users/paul/projects/FreeBuilder/src/test/java/org/inferred/freebuilder/processor/util/ClassTypeImpl.java:[42,8] org.inferred.freebuilder.processor.util.ClassTypeImpl is not abstract and does not override abstract method <A>get
AnnotationsByType(java.lang.Class<A>) in javax.lang.model.AnnotatedConstruct
[ERROR] /Users/paul/projects/FreeBuilder/src/test/java/org/inferred/freebuilder/processor/util/ClassTypeImpl.java:[118,10] org.inferred.freebuilder.processor.util.ClassTypeImpl.ClassElementImpl is not abstract and does not override abs
tract method <A>getAnnotationsByType(java.lang.Class<A>) in javax.lang.model.AnnotatedConstruct
[ERROR] /Users/paul/projects/FreeBuilder/src/test/java/org/inferred/freebuilder/processor/util/NoTypes.java:[26,3] <anonymous org.inferred.freebuilder.processor.util.NoTypes$1> is not abstract and does not override abstract method <A>g
etAnnotationsByType(java.lang.Class<A>) in javax.lang.model.AnnotatedConstruct
[INFO] 6 errors

It seems like java 8 changed Element to extend from the interface AnnotatedConstruct, which has new methods that need to be implemented:

https://docs.oracle.com/javase/7/docs/api/javax/lang/model/element/Element.html
https://docs.oracle.com/javase/8/docs/api/javax/lang/model/element/Element.html

Feature request: hasX() methods

One issue I came across when looking into migrating to @Freebuilder is that there is no (easy) way to test whether a field has been set on a builder. The builder class has a getFoo() method, but it returns an IllegalStateException if the field "foo" is not set. I could catch this exception and then set a value in the exception handler, but that's ugly. It would be much better to generate a method "hasFoo()" (or some better name) for a field called "foo" which returned true if the field had been set.

The normal way of setting a default value for a field, in the constructor, does not work in the case in which you only want to set the default value if it has not been set by the time build is called. The case I had was in testing, where I extended the builder in a test-only subclass which used a factory to create objects with sequentially-increasing IDs. If the factory creates an object, it increments the ID, so we don't want to set the field until and unless we get to build() and the field has not yet been set.

If you lazily set the field, won't you end up accidentally reusing IDs if the user reuses the Builder?

There are a couple of reasons why it's difficult to add hasX to @Freebuilder in particular. First, there's no way to tell if a field has a default value assigned in the constructor, so you get hasX methods that are both pointless and potentially confusing. Secondly, customisation is done via overrides, so any extra method makes it harder to do.

In your specific case, I suggest adding a boolean hasX field and overriding setX and getX to respect it; this also lets you call super.setX in your build method without changing the state of hasX.

Essentially, what I am looking for is the ability to read the Set of unset fields from subclasses of the generated class. I do think it would be useful to have a way of determining "if I call build, will it work?" without resorting to catching exceptions. Even this may not be perfect if there is any validation done in an overridden build() method.

Set<Foo::Builder::Properties> unsetProperties() would solve the "pointless methods" issue, but it's a big API to commit to. At the moment, we can switch to "null means unset" internally to trade off space (one less field, one less type) against readability; exposing the enum pins our implementation. Having a single method is definitely a good direction to explore in the design space, though.

Underriding breaks partials

User-provided implementations of toString, hashCode and equals should always be overridden on the Partial type (unless declared final), as they should not throw UnsupportedOperationExceptions (which requires care in the user code). On the Value type, equals should be overridden to ensure Partials are treated correctly, then defer to the user implementation.

If the method name is 'getValue' and the return type is a '@FreeBuilder' interface type,the Autogenerated code has error.

environment
eclipse Mars.1 Release (4.5.1)
freebuilder-1.2-rc1.jar
jdk1.8

example

@FreeBuilder
public interface Value {
    class Builder extends Value_Builder{}
}

@FreeBuilder
public interface Person {

    Value getValue();

    class Builder extends Person_Builder{
        public static Builder of(){
            return new Builder();
        }

        public static Builder of(Person metadata){
            return new Builder().mergeFrom(metadata);
        }

        public static Builder of(Builder builder){
            return new Builder().mergeFrom(builder);
        }

    }
}

Person_Builder can't be compiled.

Reason
The reason is the variable value is ambiguous. as show below.

the Autogenerated method mergeFrom

  /**
   * Sets all property values using the given {@code Person} as a template.
   */
  public Person.Builder mergeFrom(Person value) {
    value.mergeFrom(value.getValue());
    return (Person.Builder) this;
  }

Support SortedMap properties

The issue that has blocked this is setting the comparator; TreeMap's comparator is immutable, but we want to return an unmodifiable view from getMySortedMapProperty(). Options:

  1. Provide a custom SortedMap implementation in freebuilder.jar with a mutable comparator.
    Disadvantage: we don't currently expect users to ship freebuilder.jar.
  2. Write out a SortedMap proxy inner class.
    Disadvantage: lots of code for a rarely-used feature.
  3. Allow the comparator to be set only once, delaying TreeMap construction until then. The setter should probably be protected.
    Disadvantages:
    • users that want a mutable comparator will have to write one from scratch;
    • mergeFrom won't copy the comparator; and
    • no obvious path to migrate to solutions 1/2 in future, as users may rely on the behaviour of mergeFrom.

@FreeBuilder raises warnings against supertypes

FreeBuilder raises errors on the method causing the problem, but does not first check they are on the @FreeBuilder-annotated type, erroneously raise warnings/errors against inoffensive supertypes with no indication of which subclass is at fault.

Getter method name check is too broad

It currently treats names like 'get()' or 'getter()' as property getters, but the latter is questionable, and the former generates broken code. Should probably ensure there is a non-lowercase character following 'get'/'is'.

Make Guava an optional dependency

Using Guava's Immutable types internally is beneficial for code cleanliness, and more efficient when users are using Guava already (as copying is reduced), but Guava is a very large dependency. We should generate JDK-only code if Guava is not available at compile-time.

add newBuilder method to the source

@FreeBuilder
public interface Bean {
    String getName();
    class Builder extends Bean_Builder {
    }
}

after compile

@FreeBuilder
public interface Bean {
    String getName();

    class Builder extends Bean_Builder {

    }

    static Builder newBuilder() {
        return new Builder();
    }
}

User errors are swamped by cascading failures

FreeBuilder is designed to output a stub builder when the user supplies an erroneous type (e.g. private constructor, @Nullable getter) so the error message(s) won't be swamped by references to undefined methods.

Unfortunately, javac seems to discard all generated code once an error is issued (by any annotation processor). We should see if there is anything that can be done on our side. For instance, does postponing errors to the final round work?

Feature request: Custom extended type support

Current thinking is to support custom "single-property builder" templates, with the API cloned onto the enclosing builder. For instance, a Set template might look like:

public class SetPropertyTemplate<T>
    extends AbstractPropertyTemplate<Set<T>, SetPropertyTemplate<T>> {
  private final HashSet<T> property = new HashSet<>();
  public void addProperty(T element) {
    property.add(checkNotNull(element));
  }
  public void addAllProperty(Iterable<T> elements) {
    for (T element : elements) {
      addProperty(element);
    }
  }
  public void clearProperty() {
    property.clear();
  }
  public Set<T> getProperty() {
    return unmodifiableSet(property);
  }
  @Override public void mergeFrom(Set<T> elements) {
    addAllProperty(elements);
  }
  @Override public ImmutableSet<T> getImmutableValue() {
    return ImmutableSet.copyOf(property);
  }
  @Override public void clear() {
    property.clear();
  }
}

Here, "Property" is a magic string that gets replaced by the actual property name when the API is cloned, so for instance a set property called Values would result in methods addValues, addAllValues, clearValues and getValues. It should also be possible to inline the template definition as part of code generation, making custom extensions indistinguishable from built-in extensions. We could even use templates for all existing customization.

A key motivating example is values like IDs and timestamps that are generally minted at build time, but should not be overridden during deserialization.

FreeBuilder does not work when a builder is in the default package

If a source file is in the default package, FreeBuilder errors. The error message I receive is super unhelpful. Whilst it's maybe fair to refuse to compile in the default package (I had forgotten to specify the package in the Eclipse UI) the error message should be better.

Internal error: java.lang.IllegalStateException
@Freebuilder
^
at org.inferred.freebuilder.shaded.com.google.common.base.Preconditions.checkState(Preconditions.java:161)
at org.inferred.freebuilder.processor.Analyser.generatedBuilderSimpleName(Analyser.java:686)
at org.inferred.freebuilder.processor.Analyser.analyse(Analyser.java:149)
at org.inferred.freebuilder.processor.Processor.process(Processor.java:77)
at com.sun.tools.javac.processing.JavacProcessingEnvironment.callProcessor(JavacProcessingEnvironment.java:793)
at com.sun.tools.javac.processing.JavacProcessingEnvironment.discoverAndRunProcs(JavacProcessingEnvironment.java:722)
at com.sun.tools.javac.processing.JavacProcessingEnvironment.access$1700(JavacProcessingEnvironment.java:97)
at com.sun.tools.javac.processing.JavacProcessingEnvironment$Round.run(JavacProcessingEnvironment.java:1029)
at com.sun.tools.javac.processing.JavacProcessingEnvironment.doProcessing(JavacProcessingEnvironment.java:1163)
at com.sun.tools.javac.main.JavaCompiler.processAnnotations(JavaCompiler.java:1108)
at com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:824)
at com.sun.tools.javac.main.Main.compile(Main.java:439)
at com.sun.tools.javac.api.JavacTaskImpl.call(JavacTaskImpl.java:132)
at org.gradle.api.internal.tasks.compile.JdkJavaCompiler.execute(JdkJavaCompiler.java:45)
at org.gradle.api.internal.tasks.compile.JdkJavaCompiler.execute(JdkJavaCompiler.java:33)
at org.gradle.api.internal.tasks.compile.NormalizingJavaCompiler.delegateAndHandleErrors(NormalizingJavaCompiler.java:101)
at org.gradle.api.internal.tasks.compile.NormalizingJavaCompiler.execute(NormalizingJavaCompiler.java:50)
at org.gradle.api.internal.tasks.compile.NormalizingJavaCompiler.execute(NormalizingJavaCompiler.java:36)
at org.gradle.api.internal.tasks.compile.CleaningJavaCompilerSupport.execute(CleaningJavaCompilerSupport.java:34)
at org.gradle.api.internal.tasks.compile.CleaningJavaCompilerSupport.execute(CleaningJavaCompilerSupport.java:25)
at org.gradle.api.tasks.compile.JavaCompile.performCompilation(JavaCompile.java:157)
at org.gradle.api.tasks.compile.JavaCompile.compile(JavaCompile.java:139)
at org.gradle.api.tasks.compile.JavaCompile.compile(JavaCompile.java:93)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:75)
at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory$IncrementalTaskAction.doExecute(AnnotationProcessingTaskFactory.java:243)
at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory$StandardTaskAction.execute(AnnotationProcessingTaskFactory.java:219)
at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory$IncrementalTaskAction.execute(AnnotationProcessingTaskFactory.java:230)
at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory$StandardTaskAction.execute(AnnotationProcessingTaskFactory.java:208)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeAction(ExecuteActionsTaskExecuter.java:80)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:61)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:46)
at org.gradle.api.internal.tasks.execution.PostExecutionAnalysisTaskExecuter.execute(PostExecutionAnalysisTaskExecuter.java:35)
at org.gradle.api.internal.tasks.execution.SkipUpToDateTaskExecuter.execute(SkipUpToDateTaskExecuter.java:64)
at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.execute(ValidatingTaskExecuter.java:58)
at org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter.execute(SkipEmptySourceFilesTaskExecuter.java:52)
at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:52)
at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:53)
at org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter.execute(ExecuteAtMostOnceTaskExecuter.java:43)
at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker.execute(DefaultTaskGraphExecuter.java:203)
at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker.execute(DefaultTaskGraphExecuter.java:185)
at org.gradle.execution.taskgraph.AbstractTaskPlanExecutor$TaskExecutorWorker.processTask(AbstractTaskPlanExecutor.java:62)
at org.gradle.execution.taskgraph.AbstractTaskPlanExecutor$TaskExecutorWorker.run(AbstractTaskPlanExecutor.java:50)
at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor.process(DefaultTaskPlanExecutor.java:25)
at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter.execute(DefaultTaskGraphExecuter.java:110)
at org.gradle.execution.SelectedTaskExecutionAction.execute(SelectedTaskExecutionAction.java:37)
at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:37)
at org.gradle.execution.DefaultBuildExecuter.access$000(DefaultBuildExecuter.java:23)
at org.gradle.execution.DefaultBuildExecuter$1.proceed(DefaultBuildExecuter.java:43)
at org.gradle.execution.DryRunBuildExecutionAction.execute(DryRunBuildExecutionAction.java:32)
at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:37)
at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:30)
at org.gradle.initialization.DefaultGradleLauncher$4.run(DefaultGradleLauncher.java:158)
at org.gradle.internal.Factories$1.create(Factories.java:22)
at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:90)
at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:52)
at org.gradle.initialization.DefaultGradleLauncher.doBuildStages(DefaultGradleLauncher.java:155)
at org.gradle.initialization.DefaultGradleLauncher.access$200(DefaultGradleLauncher.java:36)
at org.gradle.initialization.DefaultGradleLauncher$1.create(DefaultGradleLauncher.java:103)
at org.gradle.initialization.DefaultGradleLauncher$1.create(DefaultGradleLauncher.java:97)
at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:90)
at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:62)
at org.gradle.initialization.DefaultGradleLauncher.doBuild(DefaultGradleLauncher.java:97)
at org.gradle.initialization.DefaultGradleLauncher.run(DefaultGradleLauncher.java:86)
at org.gradle.launcher.exec.InProcessBuildActionExecuter$DefaultBuildController.run(InProcessBuildActionExecuter.java:102)
at org.gradle.tooling.internal.provider.ExecuteBuildActionRunner.run(ExecuteBuildActionRunner.java:28)
at org.gradle.launcher.exec.ChainingBuildActionRunner.run(ChainingBuildActionRunner.java:35)
at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:47)
at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:32)
at org.gradle.launcher.exec.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:77)
at org.gradle.launcher.exec.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:47)
at org.gradle.launcher.daemon.server.exec.ExecuteBuild.doBuild(ExecuteBuild.java:52)
at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:36)
at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
at org.gradle.launcher.daemon.server.exec.WatchForDisconnection.execute(WatchForDisconnection.java:37)
at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
at org.gradle.launcher.daemon.server.exec.ResetDeprecationLogger.execute(ResetDeprecationLogger.java:26)
at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
at org.gradle.launcher.daemon.server.exec.RequestStopIfSingleUsedDaemon.execute(RequestStopIfSingleUsedDaemon.java:34)
at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.call(ForwardClientInput.java:74)
at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.call(ForwardClientInput.java:72)
at org.gradle.util.Swapper.swap(Swapper.java:38)
at org.gradle.launcher.daemon.server.exec.ForwardClientInput.execute(ForwardClientInput.java:72)
at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
at org.gradle.launcher.daemon.server.health.DaemonHealthTracker.execute(DaemonHealthTracker.java:47)
at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
at org.gradle.launcher.daemon.server.exec.LogToClient.doBuild(LogToClient.java:66)
at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:36)
at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
at org.gradle.launcher.daemon.server.exec.EstablishBuildEnvironment.doBuild(EstablishBuildEnvironment.java:71)
at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:36)
at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
at org.gradle.launcher.daemon.server.health.HintGCAfterBuild.execute(HintGCAfterBuild.java:41)
at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
at org.gradle.launcher.daemon.server.exec.StartBuildOrRespondWithBusy$1.run(StartBuildOrRespondWithBusy.java:50)
at org.gradle.launcher.daemon.server.DaemonStateCoordinator$1.run(DaemonStateCoordinator.java:246)
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:54)
at org.gradle.internal.concurrent.StoppableExecutorImpl$1.run(StoppableExecutorImpl.java:40)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)

Infrastructure: Sporadic test failures on JDK 8

I've been sporadically seeing tests fail on JDK 8 with the following error:

java.lang.AssertionError: Filling jar:file:/build/work/6bd2fd14349c0a5c80d2dde58b4f5754/google3/runfiles/google3/third_party/java/jdk/jdk8-64/lib/ct.sym!/META-INF/sym/rt.jar/java/util/List.class during ZipFileIndexFileObject[/build/work/6bd2fd14349c0a5c80d2dde58b4f5754/google3/runfiles/google3/third_party/java/jdk/jdk8-64/lib/ct.sym(META-INF/sym/rt.jar/java/util/MissingResourceException.class)]

One of these Google results is suggesting a race condition on a ClassLoader, which may be relevant, as the test harness runs a compiler thread parallel to the test thread.

Opt out a property for equals/hashCode

I would like the ability to define a value class where a certain field isn't included in equality.

For example, I recently came to a need to define a helper class that wraps the input object T and its original index in the input List.

IndexedFoo {
  int index();
  Foo foo();
}

Multimap<IndexedFoo, Foo> overlappings = ...;

I want IndexedFoo's equality to be based on the index, but not the Foo object.

Feature request: @Nullable support

Motivation

The hardest null bugs to spot are the ones where a method in a different package can return nulls. If you see foo.getBar(), it's unlikely you'll remember the contract of Foo well enough to spot that bar might be null. By contrast, if you see foo.getBar().orNull(), it's impossible to miss—and Optional gives us that. Immutable data types are very likely to be used in exactly these problem areas, so we made a deliberate decision to postpone null support.

That said, with Java 8, we may yet see a truly effective compile-time elimination of NullPointerExceptions from Java code, at which point the key objection to @nullable is no longer valid.

If you have strong reasons for requiring @nullable support sooner, please add a comment to this feature request.

Porting existing null-based data types

If you're upgrading an existing @Nullable-returning data type to @Freebuilder, Eclipse's "Rename Method" and "Inline Method" refactoring tools make it a cinch to move over to Optional:

  • Rename getBar() to getNullableBar()
  • Create a new, Optional-returning getBar()
  • Change getNullableBar() to simply call getBar().orNull()
  • Inline all instances of getNullableBar()

If you're upgrading an existing Builder, FreeBuilder generates a null-accepting setNullableBar, so just rename the existing setter method accordingly.

Map putAll generates FindBugs warning

A field like public abstract ImmutableMap<String, String> getFields(); generates a method like:

  public My.Builder putAllFields(Map<? extends String, ? extends String> map) {
    for (String key : map.keySet()) {
      putFields(key, map.get(key));
    }
    return (My.Builder) this;
  }

FindBugs complains about inefficient use of keySet() and map.get() instead of entrySet().

I tried to submit a patch, but I had a lot of trouble getting this code running locally. I think you essentially want to change https://github.com/google/FreeBuilder/blob/25fac1babaef8363ea10ddd71b4ee921d1f536b0/src/main/java/org/inferred/freebuilder/processor/MapPropertyFactory.java#L162-L164 to something like:

          .addLine("  for (Map.Entry<%s, %s> entry : map.entrySet()) {", unboxedKeyType.or(keyType), unboxedValueType.or(valueType))
          .addLine("    %s%s(entry.getKey(), entry.getValue());", PUT_PREFIX, property.getCapitalizedName())
          .addLine("  }")

Feature request: JSR-305–style constraint annotations

Especially @MatchesPattern and @Nonnegative.

Note: Optional<@Nonnegative Integer> can be stored internally as an int instead of an Integer, as -1 can represent Optional.absent. This kind of savings may be too rare an opportunity to pursue, though.

Cyclic dependencies on generated buildable types not supported

Cyclic dependencies between API-linked types generated by other annotation processors (e.g. a FreeBuilder clone) are problematic, as both types need to detect the other is a buildable type before they can complete their API. One solution may be to generate the non-property-dependent parts of the API on round 1, and delay generation of the rest by using the superclass trick a second time (Type.Builder -> Type_Builder -> Type_Builder_Superclass)

Feature request: Update methods

Java 8 lambdas enable a new idiom: update methods for each field, taking a Function<T, T>. These are particularly useful for nested builders, replacing merge in many cases:

foo.updateBar(b -> b.setBaz(42));

Fix type ID for Jackson

If a property is annotated @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS), Jackson will serialize the runtime class name to JSON, which is of course an implementation detail (Foo_Builder$Value). This can be fixed by adding a method, annotated @JsonTypeId, to the type, which returns the name of the publicly-visible abstract value type instead of the FreeBuilder-generated subclass.

While this can be done by hand, it would be helpful if FreeBuilder generated this automatically.

error-prone errors on code generated for array properties

ACTUAL:

compiling

@FreeBuilder
public abstract static class SomeClass {
  public abstract byte[] getBytes();

  static class Builder extends SomeClass_Builder {}
}

results in:

SomeClass_Builder.java:114: error: [ArrayEquals] Reference equality used to compare arrays
      if (!bytes.equals(other.bytes)) {

EXPECTED:

  • byte[] supported

    OR

  • a graceful error message during compilation that suggests to use List.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.