1717 */
1818package org .apache .beam .sdk .transforms ;
1919
20+ import static org .apache .beam .sdk .util .Preconditions .checkStateNotNull ;
2021import static org .apache .beam .vendor .guava .v32_1_2_jre .com .google .common .base .Preconditions .checkNotNull ;
2122import static org .apache .beam .vendor .guava .v32_1_2_jre .com .google .common .base .Preconditions .checkState ;
2223
2930import org .apache .beam .sdk .transforms .DoFn .OutputReceiver ;
3031import org .apache .beam .sdk .transforms .windowing .BoundedWindow ;
3132import org .apache .beam .sdk .transforms .windowing .PaneInfo ;
33+ import org .apache .beam .sdk .util .WindowedValue ;
34+ import org .apache .beam .sdk .values .OutputBuilder ;
3235import org .apache .beam .sdk .values .Row ;
3336import org .apache .beam .sdk .values .TupleTag ;
3437import org .checkerframework .checker .nullness .qual .Nullable ;
4043 "nullness" // TODO(https://github.com/apache/beam/issues/20497)
4144})
4245public class DoFnOutputReceivers {
43- private static class RowOutputReceiver <T > implements OutputReceiver <Row > {
44- WindowedContextOutputReceiver <T > outputReceiver ;
45- SchemaCoder <T > schemaCoder ;
4646
47- public RowOutputReceiver (
48- DoFn <?, ?>.WindowedContext context ,
49- @ Nullable TupleTag <T > outputTag ,
50- SchemaCoder <T > schemaCoder ) {
51- outputReceiver = new WindowedContextOutputReceiver <>(context , outputTag );
52- this .schemaCoder = checkNotNull (schemaCoder );
47+ /**
48+ * OutputBuilder implementations may extend this class to simplify wrapping of another
49+ * OutputBuilder.
50+ *
51+ * <p>Because a common cause of delegation is to adjust the type of output received, extenders
52+ * <i>must</i> implement {#link setValue} and {#link getValue}.
53+ */
54+ public abstract static class DelegatingOutputBuilder <InnerT , OuterT >
55+ implements OutputBuilder <OuterT > {
56+
57+ private final OutputBuilder <InnerT > delegate ;
58+
59+ public DelegatingOutputBuilder (OutputBuilder <InnerT > delegate ) {
60+ this .delegate = delegate ;
5361 }
5462
63+ protected OutputBuilder <InnerT > getDelegate () {
64+ return delegate ;
65+ }
66+
67+ @ Override
68+ public abstract OutputBuilder <OuterT > setValue (OuterT value );
69+
5570 @ Override
56- public void output (Row output ) {
57- outputReceiver .output (schemaCoder .getFromRowFunction ().apply (output ));
71+ public OutputBuilder <OuterT > setTimestamp (Instant timestamp ) {
72+ delegate .setTimestamp (timestamp );
73+ return this ;
5874 }
5975
6076 @ Override
61- public void outputWithTimestamp (Row output , Instant timestamp ) {
62- outputReceiver .outputWithTimestamp (schemaCoder .getFromRowFunction ().apply (output ), timestamp );
77+ public OutputBuilder <OuterT > setWindow (BoundedWindow window ) {
78+ delegate .setWindow (window );
79+ return this ;
6380 }
6481
6582 @ Override
66- public void outputWindowedValue (
67- Row output ,
68- Instant timestamp ,
69- Collection <? extends BoundedWindow > windows ,
70- PaneInfo paneInfo ) {
71- outputReceiver .outputWindowedValue (
72- schemaCoder .getFromRowFunction ().apply (output ), timestamp , windows , paneInfo );
83+ public OutputBuilder <OuterT > setWindows (Collection <? extends BoundedWindow > windows ) {
84+ delegate .setWindows (windows );
85+ return this ;
86+ }
87+
88+ @ Override
89+ public OutputBuilder <OuterT > setPaneInfo (PaneInfo paneInfo ) {
90+ delegate .setPaneInfo (paneInfo );
91+ return this ;
92+ }
93+
94+ @ Override
95+ public abstract OuterT getValue ();
96+
97+ @ Override
98+ public Instant getTimestamp () {
99+ return delegate .getTimestamp ();
100+ }
101+
102+ @ Override
103+ public Collection <? extends BoundedWindow > getWindows () {
104+ return delegate .getWindows ();
105+ }
106+
107+ @ Override
108+ public PaneInfo getPaneInfo () {
109+ return delegate .getPaneInfo ();
110+ }
111+
112+ @ Override
113+ public void output () {
114+ delegate .output ();
73115 }
74116 }
75117
76- private static class WindowedContextOutputReceiver <T > implements OutputReceiver <T > {
118+ private static class RowOutputBuilder <T > extends DelegatingOutputBuilder <T , Row >
119+ implements OutputBuilder <Row > {
120+
121+ private @ Nullable Row currentRow ;
122+ private final SchemaCoder <T > schemaCoder ;
123+
124+ RowOutputBuilder (OutputBuilder <T > delegate , SchemaCoder <T > schemaCoder ) {
125+ super (delegate );
126+ this .schemaCoder = schemaCoder ;
127+ }
128+
129+ @ Override
130+ public OutputBuilder <Row > setValue (Row value ) {
131+ currentRow = value ;
132+ getDelegate ().setValue (schemaCoder .getFromRowFunction ().apply (value ));
133+ return (OutputBuilder <Row >) this ;
134+ }
135+
136+ @ Override
137+ public Row getValue () {
138+ checkStateNotNull (currentRow , "getValue() called before value set" );
139+ return currentRow ;
140+ }
141+ }
142+
143+ private static class RowOutputReceiver <T > implements OutputReceiver <Row > {
144+ WindowedContextOutputReceiver <T > outputReceiver ;
145+ SchemaCoder <T > schemaCoder ;
146+
147+ @ Override
148+ public OutputBuilder <Row > builder () {
149+ return new RowOutputBuilder <>(outputReceiver .builder (), schemaCoder );
150+ }
151+
152+ public RowOutputReceiver (
153+ DoFn <?, ?>.WindowedContext context ,
154+ @ Nullable TupleTag <T > outputTag ,
155+ SchemaCoder <T > schemaCoder ) {
156+ outputReceiver = new WindowedContextOutputReceiver <>(context , outputTag );
157+ this .schemaCoder = checkNotNull (schemaCoder );
158+ }
159+ }
160+
161+ /**
162+ * OutputReceiver that delegates all its core functionality to DoFn.WindowedContext which predates
163+ * OutputReceiver and has most of the same methods.
164+ */
165+ private static class WindowedContextOutputReceiver <T >
166+ implements OutputReceiver <T >, WindowedValue .Outputter <T > {
77167 DoFn <?, ?>.WindowedContext context ;
78168 @ Nullable TupleTag <T > outputTag ;
79169
@@ -84,34 +174,26 @@ public WindowedContextOutputReceiver(
84174 }
85175
86176 @ Override
87- public void output (T output ) {
88- if (outputTag != null ) {
89- context .output (outputTag , output );
90- } else {
91- ((DoFn <?, T >.WindowedContext ) context ).output (output );
92- }
93- }
94-
95- @ Override
96- public void outputWithTimestamp (T output , Instant timestamp ) {
97- if (outputTag != null ) {
98- context .outputWithTimestamp (outputTag , output , timestamp );
99- } else {
100- ((DoFn <?, T >.WindowedContext ) context ).outputWithTimestamp (output , timestamp );
101- }
177+ public OutputBuilder <T > builder () {
178+ return WindowedValue .builder (this );
102179 }
103180
104181 @ Override
105- public void outputWindowedValue (
106- T output ,
107- Instant timestamp ,
108- Collection <? extends BoundedWindow > windows ,
109- PaneInfo paneInfo ) {
182+ public void output (WindowedValue .Builder <T > outputBuilder ) {
110183 if (outputTag != null ) {
111- context .outputWindowedValue (outputTag , output , timestamp , windows , paneInfo );
184+ context .outputWindowedValue (
185+ outputTag ,
186+ outputBuilder .getValue (),
187+ outputBuilder .getTimestamp (),
188+ outputBuilder .getWindows (),
189+ outputBuilder .getPaneInfo ());
112190 } else {
113191 ((DoFn <?, T >.WindowedContext ) context )
114- .outputWindowedValue (output , timestamp , windows , paneInfo );
192+ .outputWindowedValue (
193+ outputBuilder .getValue (),
194+ outputBuilder .getTimestamp (),
195+ outputBuilder .getWindows (),
196+ outputBuilder .getPaneInfo ());
115197 }
116198 }
117199 }
0 commit comments