1/*
2 * Copyright (C) 2022 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17import {Timestamp} from 'common/time';
18import {
19  CustomQueryParserResultTypeMap,
20  CustomQueryType,
21} from 'trace/custom_query';
22import {FrameMap} from 'trace/frame_map';
23import {FrameMapBuilder} from 'trace/frame_map_builder';
24import {
25  AbsoluteEntryIndex,
26  AbsoluteFrameIndex,
27  EntriesRange,
28} from 'trace/index_types';
29import {Parser} from 'trace/parser';
30import {Trace} from 'trace/trace';
31import {TraceType} from 'trace/trace_type';
32import {ParserBuilder} from './parser_builder';
33
34export class TraceBuilder<T> {
35  private type = TraceType.SURFACE_FLINGER;
36  private parser?: Parser<T>;
37  private parserCustomQueryResult = new Map<CustomQueryType, {}>();
38  private entries?: T[];
39  private timestamps?: Timestamp[];
40  private frameMap?: FrameMap;
41  private frameMapBuilder?: FrameMapBuilder;
42  private descriptors: string[] = [];
43
44  setType(type: TraceType): TraceBuilder<T> {
45    this.type = type;
46    return this;
47  }
48
49  setParser(parser: Parser<T>): TraceBuilder<T> {
50    this.parser = parser;
51    return this;
52  }
53
54  setEntries(entries: T[]): TraceBuilder<T> {
55    this.entries = entries;
56    return this;
57  }
58
59  setTimestamps(timestamps: Timestamp[]): TraceBuilder<T> {
60    this.timestamps = timestamps;
61    return this;
62  }
63
64  setFrameMap(frameMap?: FrameMap): TraceBuilder<T> {
65    this.frameMap = frameMap;
66    return this;
67  }
68
69  setFrame(entry: AbsoluteEntryIndex, frame: AbsoluteFrameIndex) {
70    if (!this.entries) {
71      throw new Error(`Can't set frames before specifying the entries`);
72    }
73    if (!this.frameMapBuilder) {
74      this.frameMapBuilder = new FrameMapBuilder(this.entries.length, 1000);
75    }
76    this.frameMapBuilder.setFrames(entry, {start: frame, end: frame + 1});
77    return this;
78  }
79
80  setParserCustomQueryResult<Q extends CustomQueryType>(
81    type: Q,
82    result: CustomQueryParserResultTypeMap[Q],
83  ): TraceBuilder<T> {
84    this.parserCustomQueryResult.set(type, result);
85    return this;
86  }
87
88  build(): Trace<T> {
89    if (!this.parser) {
90      this.parser = this.createParser();
91    }
92
93    const entriesRange: EntriesRange = {
94      start: 0,
95      end: this.parser.getLengthEntries(),
96    };
97    const trace = new Trace<T>(
98      this.type,
99      this.parser,
100      this.descriptors,
101      undefined,
102      entriesRange,
103    );
104
105    const frameMap = this.getFrameMap();
106    if (frameMap) {
107      trace.setFrameInfo(frameMap, frameMap.getFullTraceFramesRange());
108    }
109
110    return trace;
111  }
112
113  private createParser(): Parser<T> {
114    const builder = new ParserBuilder<T>().setType(this.type);
115
116    if (this.timestamps) {
117      builder.setTimestamps(this.timestamps);
118    }
119
120    if (this.entries) {
121      builder.setEntries(this.entries);
122    }
123
124    this.parserCustomQueryResult?.forEach((result, queryType) => {
125      builder.setCustomQueryResult(queryType, result as any);
126    });
127
128    return builder.build();
129  }
130
131  private getFrameMap(): FrameMap | undefined {
132    if (this.frameMap && this.frameMapBuilder) {
133      throw new Error(
134        `Cannot set a full frame map as well as individual entry's frames. Pick one of the two options.`,
135      );
136    }
137    if (this.frameMap) {
138      return this.frameMap;
139    }
140    if (this.frameMapBuilder) {
141      return this.frameMapBuilder.build();
142    }
143    return undefined;
144  }
145}
146