/* * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ 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; import java.util.ArrayList; import java.util.List; enum ResType { SOME, NONE } @Target({ElementType.PARAMETER, ElementType.FIELD}) @Retention(RetentionPolicy.CLASS) @interface Prop { ResType resType() default ResType.NONE; boolean optional() default false; String varArg() default ""; } @Target({ElementType.PARAMETER, ElementType.FIELD}) @Retention(RetentionPolicy.CLASS) @interface TreeProp { 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; } } } class MyTreeComponent extends Component { @TreeProp Object prop1; // implicitly non-optional Object nonProp; public Builder create() { return new Builder(); } static class Builder extends Component.Builder { MyTreeComponent mMyTreeComponent; public Builder prop1(Object o) { this.mMyTreeComponent.prop1 = o; return this; } public MyTreeComponent build() { return mMyTreeComponent; } } } /** * 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 Builder propDip(Object o) { this.mResPropComponent.prop = o; return this; } public Builder propPx(Object o) { this.mResPropComponent.prop = o; return this; } public Builder propSp(Object o) { this.mResPropComponent.prop = o; return this; } public ResPropComponent build() { return mResPropComponent; } } } /** varArg test */ class VarArgPropComponent extends Component { @Prop(varArg = "prop") List props; public Builder create() { return new Builder(); } static class Builder extends Component.Builder { VarArgPropComponent mVarArgPropComponent; public Builder prop(Object prop) { if (prop == null) { return this; } if (this.mVarArgPropComponent.props == null) { this.mVarArgPropComponent.props = new ArrayList(); } this.mVarArgPropComponent.props.add(prop); return this; } public Builder propAttr(Object prop) { if (prop == null) { return this; } if (this.mVarArgPropComponent.props == null) { this.mVarArgPropComponent.props = new ArrayList(); } this.mVarArgPropComponent.props.add(prop); return this; } public Builder propsAttr(List props) { if (props == null) { return this; } if (this.mVarArgPropComponent.props == null || this.mVarArgPropComponent.props.isEmpty()) { this.mVarArgPropComponent.props = props; } else { this.mVarArgPropComponent.props.addAll(props); } return this; } public Builder props(List props) { if (props == null) { return this; } if (this.mVarArgPropComponent.props == null || this.mVarArgPropComponent.props.isEmpty()) { this.mVarArgPropComponent.props = props; } else { this.mVarArgPropComponent.props.addAll(props); } return this; } public VarArgPropComponent build() { return mVarArgPropComponent; } } } public class RequiredProps { public MyComponent mMyComponent; public ResPropComponent mResPropComponent; public VarArgPropComponent mVarArgPropComponent; 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 buildPropResWithDipOk() { mResPropComponent.create().propDip(new Object()).build(); } public void buildPropResWithPxOk() { mResPropComponent.create().propPx(new Object()).build(); } public void buildPropResWithSpOk() { mResPropComponent.create().propSp(new Object()).build(); } public void buildPropResMissingBad() { mResPropComponent.create().build(); } public void buildPropVarArgNormalOk() { mVarArgPropComponent.create().props(new ArrayList()).build(); } public void buildPropVarArgElementOk() { mVarArgPropComponent.create().prop(new Object()).build(); } public void buildPropVarArgAttrElementOk() { mVarArgPropComponent.create().propAttr(new Object()).build(); } public void buildPropVarArgNormalAttrElementOk() { mVarArgPropComponent.create().propsAttr(new ArrayList()).build(); } public void buildPropVarArgMissingBad() { mVarArgPropComponent.create().build(); } public class NonRequiredTreeProps { public MyTreeComponent mMyTreeComponent; public MyTreeComponent buildWithoutOk() { return mMyTreeComponent.create().build(); } } }