/* * Copyright (c) 2018 - present Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. */ package codetoanalyze.java.litho; import com.facebook.litho.Column; import com.facebook.litho.Component; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; enum ResType { SOME, NONE } @Target({ ElementType.PARAMETER, ElementType.FIELD }) @Retention(RetentionPolicy.CLASS) @interface Prop { ResType resType() default ResType.NONE; boolean optional() default false; } class MyComponent extends Component { @Prop Object prop1; // implicitly non-optional @Prop(optional = true) Object prop2; // explicitly optional @Prop(optional = false) Object prop3; // explicitly non-optional Object nonProp; public Builder create() { return new Builder(); } static class Builder extends Component.Builder { MyComponent mMyComponent; public Builder prop1(Object o) { this.mMyComponent.prop1 = o; return this; } public Builder prop2(Object o) { this.mMyComponent.prop2 = o; return this; } public Builder prop3(Object o) { this.mMyComponent.prop3 = o; return this; } public MyComponent build() { return mMyComponent; } } } /** using @Prop(resType = ..) allows you to set the Prop with any of .propname, * .propnameRes, or .propnameAttr */ class ResPropComponent extends Component { @Prop(resType = ResType.SOME) Object prop; // implicitly non-optional with resType public Builder create() { return new Builder(); } static class Builder extends Component.Builder { ResPropComponent mResPropComponent; public Builder prop(Object o) { this.mResPropComponent.prop = o; return this; } public Builder propRes(Object o) { this.mResPropComponent.prop = o; return this; } public Builder propAttr(Object o) { this.mResPropComponent.prop = o; return this; } public ResPropComponent build() { return mResPropComponent; } } } public class RequiredProps { public MyComponent mMyComponent; public ResPropComponent mResPropComponent; public MyComponent buildWithAllOk() { return mMyComponent .create() .prop1(new Object()) .prop2(new Object()) .prop3(new Object()) .build(); } // prop 2 is optional public MyComponent buildWithout2Ok() { return mMyComponent .create() .prop1(new Object()) .prop3(new Object()) .build(); } // prop 1 is required public MyComponent buildWithout1Bad() { return mMyComponent .create() .prop2(new Object()) .prop3(new Object()) .build(); } // prop3 is required public MyComponent buildWithout3Bad() { return mMyComponent .create() .prop1(new Object()) .prop2(new Object()) .build(); } private static MyComponent.Builder setProp1(MyComponent.Builder builder) { return builder.prop1(new Object()); } private static MyComponent.Builder setProp3(MyComponent.Builder builder) { return builder.prop3(new Object()); } public MyComponent setProp1InCalleeOk() { return setProp1( mMyComponent .create() .prop2(new Object())) .prop3(new Object()) .build(); } public MyComponent setProp3InCalleeOk() { return setProp3( mMyComponent .create() .prop1(new Object()) .prop2(new Object())) .build(); } public MyComponent setProp3InCalleeButForgetProp1Bad() { return setProp3(mMyComponent.create()) .prop2(new Object()) .build(); } public MyComponent setRequiredOnOneBranchBad(boolean b) { MyComponent.Builder builder = mMyComponent.create(); if (b) { builder = builder.prop1(new Object()); } return builder.prop2(new Object()).prop3(new Object()).build(); } public MyComponent FP_setRequiredOnBothBranchesOk(boolean b) { MyComponent.Builder builder = mMyComponent.create(); if (b) { builder = builder.prop1(new Object()); } else { builder = builder.prop1(new Object()); } return builder.prop2(new Object()).prop3(new Object()).build(); } // don't want to report here; want to report at clients that don't pass prop1 private MyComponent buildSuffix(MyComponent.Builder builder) { return builder.prop2(new Object()).prop3(new Object()).build(); } // shouldn't report here; prop 1 passed public MyComponent callBuildSuffixWithRequiredOk() { return buildSuffix(mMyComponent.create().prop1(new Object())); } // should report here; forgot prop 1 public MyComponent callBuildSuffixWithoutRequiredBad() { return buildSuffix(mMyComponent.create()); } public Object generalTypeForgot3Bad() { MyComponent.Builder builder1 = mMyComponent.create(); Component.Builder builder2 = (Component.Builder) builder1.prop1(new Object()); // don't fail to find required @Prop's for MyComponent.Builder even though the static type that // build is invoked on is [builder2] return builder2.build(); } public void buildWithColumnChildBad() { Column.Builder builder = Column.create(); MyComponent.Builder childBuilder = mMyComponent.create().prop1(new Object()); // forgot prop 3, and builder.child() will invoke build() on childBuilder builder.child(childBuilder); } public void buildPropResWithNormalOk() { mResPropComponent .create() .prop(new Object()) .build(); } public void buildPropResWithResOk() { mResPropComponent .create() .propRes(new Object()) .build(); } public void buildPropResWithAttrOk() { mResPropComponent .create() .propAttr(new Object()) .build(); } public void buildPropResMissingBad() { mResPropComponent .create() .build(); } }