semantifold 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -2
- package/build/index.d.ts +12 -8
- package/build/index.d.ts.map +1 -1
- package/build/index.js +10 -7
- package/build/src/backends/android.d.ts +49 -0
- package/build/src/backends/android.d.ts.map +1 -0
- package/build/src/backends/android.js +1274 -0
- package/build/src/backends/identifiers.d.ts.map +1 -1
- package/build/src/backends/identifiers.js +7 -1
- package/build/src/backends/kotlin.d.ts +10 -0
- package/build/src/backends/kotlin.d.ts.map +1 -1
- package/build/src/backends/kotlin.js +11 -9
- package/build/src/backends/program.d.ts +6 -4
- package/build/src/backends/program.d.ts.map +1 -1
- package/build/src/backends/program.js +5 -4
- package/build/src/backends/shared.d.ts.map +1 -1
- package/build/src/backends/shared.js +17 -1
- package/build/src/backends/writer.d.ts +21 -1
- package/build/src/backends/writer.d.ts.map +1 -1
- package/build/src/backends/writer.js +25 -2
- package/build/src/language-registry.d.ts.map +1 -1
- package/build/src/language-registry.js +11 -1
- package/build/src/semantic/types.d.ts +182 -2
- package/build/src/semantic/types.d.ts.map +1 -1
- package/build/src/semantic/types.js +44 -2
- package/docs/android.md +79 -0
- package/docs/architecture.md +2 -0
- package/docs/ios.md +1 -1
- package/docs/language-support.md +8 -1
- package/docs/source-maps.md +2 -0
- package/docs/specification.md +5 -0
- package/docs/testing.md +15 -0
- package/index.js +9 -6
- package/package.json +1 -1
- package/src/backends/android.js +1370 -0
- package/src/backends/identifiers.js +6 -0
- package/src/backends/kotlin.js +11 -8
- package/src/backends/program.js +4 -3
- package/src/backends/shared.js +19 -0
- package/src/backends/writer.js +27 -2
- package/src/language-registry.js +10 -0
- package/src/semantic/types.js +46 -1
|
@@ -0,0 +1,1274 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { isDenseArray } from "../array.js";
|
|
4
|
+
import { findPortableArtifactPathConflict, isSafeArtifactPath } from "../artifact-path.js";
|
|
5
|
+
import { createByteMapping } from "../binary-mapping.js";
|
|
6
|
+
import { SemantifoldDiagnostic, unsupportedCapability } from "../diagnostic.js";
|
|
7
|
+
import { finalizeMapping, toSourceMapV3 } from "../mapping.js";
|
|
8
|
+
import { hasOnlyUnicodeScalars } from "../semantic/scalars.js";
|
|
9
|
+
import { preflightSemanticProgram } from "./program.js";
|
|
10
|
+
import { emitScalarType } from "./scalars.js";
|
|
11
|
+
import { kotlinRuntime } from "./kotlin-runtime.js";
|
|
12
|
+
import { emitKotlinBlock } from "./kotlin.js";
|
|
13
|
+
import { SourceWriter } from "./writer.js";
|
|
14
|
+
const projectRoot = "generated/android-app";
|
|
15
|
+
const generatorVersion = "0.4.0";
|
|
16
|
+
/** @type {Readonly<Set<import("../semantic/types.js").SemanticLanguage>>} */
|
|
17
|
+
const androidSourceLanguages = new Set([
|
|
18
|
+
"php", "ruby", "javascript", "typescript", "java", "kotlin", "python", "csharp", "go", "c", "cpp", "rust", "swift", "dart"
|
|
19
|
+
]);
|
|
20
|
+
const requestFields = new Set(["assets", "configuration", "language", "module", "program", "resources", "role"]);
|
|
21
|
+
const configurationFields = new Set([
|
|
22
|
+
"activityClassName", "applicationId", "buildToolsVersion", "compileSdk", "displayName", "minimumSdk", "namespace",
|
|
23
|
+
"orientation", "packageName", "permissions", "productName", "targetSdk", "theme", "versionCode", "versionName"
|
|
24
|
+
]);
|
|
25
|
+
const resourceFields = new Set(["content", "mediaType", "path", "sha256"]);
|
|
26
|
+
const kotlinKeywords = new Set([
|
|
27
|
+
"as", "break", "class", "continue", "do", "else", "false", "for", "fun", "if", "in", "interface", "is", "null",
|
|
28
|
+
"object", "package", "return", "super", "this", "throw", "true", "try", "typealias", "typeof", "val", "var", "when", "while"
|
|
29
|
+
]);
|
|
30
|
+
const androidScaffoldSymbols = new Set([
|
|
31
|
+
"Activity", "Base64", "Bundle", "IllegalStateException", "Instrumentation", "Intent", "R", "SemantifoldEntryTest",
|
|
32
|
+
"SemantifoldUiInstrumentation", "StandardCharsets", "String", "SuppressLint", "Test", "TextView", "Throwable", "View"
|
|
33
|
+
]);
|
|
34
|
+
const mediaTypePattern = /^[A-Za-z0-9!#$&^_.+-]+\/[A-Za-z0-9!#$&^_.+-]+(?:;[\u0020-\u007e]+)?$/u;
|
|
35
|
+
const semanticVersionPattern = /^(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)(?:-(?:0|[1-9][0-9]*|[A-Za-z-][A-Za-z0-9-]*)(?:\.(?:0|[1-9][0-9]*|[A-Za-z-][A-Za-z0-9-]*))*)?(?:\+[A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)*)?$/u;
|
|
36
|
+
const toolchain = Object.freeze({
|
|
37
|
+
androidGradlePlugin: "8.11.1",
|
|
38
|
+
archives: {
|
|
39
|
+
buildTools: {
|
|
40
|
+
revision: "35.0.0",
|
|
41
|
+
sha256: "bd3a4966912eb8b30ed0d00b0cda6b6543b949d5ffe00bea54c04c81e1561d88",
|
|
42
|
+
url: "https://dl.google.com/android/repository/build-tools_r35_linux.zip"
|
|
43
|
+
},
|
|
44
|
+
commandLineTools: {
|
|
45
|
+
revision: "11076708",
|
|
46
|
+
sha256: "2d2d50857e4eb553af5a6dc3ad507a17adf43d115264b1afc116f95c92e5e258",
|
|
47
|
+
url: "https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip"
|
|
48
|
+
},
|
|
49
|
+
emulator: {
|
|
50
|
+
buildId: "13610412",
|
|
51
|
+
revision: "35.6.11",
|
|
52
|
+
sha256: "2fe2b56fe93ce75e1d478a40162131381d911c355efeaedb54dd1e0d0897a5cf",
|
|
53
|
+
url: "https://edgedl.me.gvt1.com/edgedl/android/repository/emulator-linux_x64-13610412.zip"
|
|
54
|
+
},
|
|
55
|
+
platform: {
|
|
56
|
+
revision: "35-r2",
|
|
57
|
+
sha256: "0988cacad01b38a18a47bac14a0695f246bc76c1b06c0eeb8eb0dc825ab0c8e0",
|
|
58
|
+
url: "https://dl.google.com/android/repository/platform-35_r02.zip"
|
|
59
|
+
},
|
|
60
|
+
platformTools: {
|
|
61
|
+
revision: "37.0.1",
|
|
62
|
+
sha256: "d230f13842f60f782a8645f9c813f8f845bf36089ea7289f28c48f17979313f1",
|
|
63
|
+
url: "https://dl.google.com/android/repository/platform-tools_r37.0.1-linux.zip"
|
|
64
|
+
},
|
|
65
|
+
systemImage: {
|
|
66
|
+
revision: "35-google_apis-x86_64-r9",
|
|
67
|
+
sha256: "c67b9ba0ff5bc0eb6d046871bfa228af14d4d47b02f0cdae94f048e511b7566e",
|
|
68
|
+
url: "https://dl.google.com/android/repository/sys-img/google_apis/x86_64-35_r09.zip"
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
buildTools: "35.0.0",
|
|
72
|
+
commandLineTools: "11076708",
|
|
73
|
+
compileSdk: 35,
|
|
74
|
+
emulator: "35.6.11",
|
|
75
|
+
gradle: "8.13",
|
|
76
|
+
java: "21.0.8",
|
|
77
|
+
kotlin: "2.2.10",
|
|
78
|
+
kotlinCompilerSha256: "302d1d8e671e5c3207e6ed62ff11fb555462a628e22a1158254dcaaf7e7394bc",
|
|
79
|
+
systemImage: "system-images;android-35;google_apis;x86_64",
|
|
80
|
+
targetSdk: 35
|
|
81
|
+
});
|
|
82
|
+
const excludedPathPatterns = Object.freeze([".gradle/**", "**/build/**", "local.properties"]);
|
|
83
|
+
const androidKotlinRuntime = `@file:Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
|
|
84
|
+
|
|
85
|
+
package __PACKAGE__.semantic
|
|
86
|
+
|
|
87
|
+
${kotlinRuntime.replaceAll("private ", "internal ")}
|
|
88
|
+
internal class SemantifoldOutputSink {
|
|
89
|
+
private val values: java.util.ArrayList<String> = java.util.ArrayList<String>()
|
|
90
|
+
|
|
91
|
+
fun reset() {
|
|
92
|
+
values.clear()
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
fun write(value: Long) {
|
|
96
|
+
values.add(value.toString())
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
fun write(value: Boolean) {
|
|
100
|
+
values.add(if (value) "true" else "false")
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
fun write(value: String) {
|
|
104
|
+
values.add(value)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
fun snapshot(): java.util.ArrayList<String> = java.util.ArrayList<String>(values)
|
|
108
|
+
}
|
|
109
|
+
`;
|
|
110
|
+
/** @typedef {{content: string | Uint8Array, contentKind: "binary" | "text", fullPath: string, mediaType: string, path: string, sha256: string}} AndroidInputFile */
|
|
111
|
+
/** @typedef {{end: number, field: string, path: string, start: number}} ConfigurationSpan */
|
|
112
|
+
/**
|
|
113
|
+
* Preflights a complete Android application request before allocating a source writer.
|
|
114
|
+
* @param {Record<string, unknown>} input - Candidate application request.
|
|
115
|
+
* @returns {{assets: AndroidInputFile[], configuration: import("../semantic/types.js").AndroidApplicationConfiguration, modulePaths: Map<string, string>, modules: import("../semantic/types.js").SemanticModule[], packagePath: string, program: import("../semantic/types.js").SemanticProgram, resources: AndroidInputFile[], sources: {content: string, filename: string, language?: import("../semantic/types.js").SemanticLanguage}[]}} Prepared application.
|
|
116
|
+
*/
|
|
117
|
+
export function preflightAndroidApplication(input) {
|
|
118
|
+
if (!isPlainObject(input) || Object.keys(input).some(key => !requestFields.has(key)) ||
|
|
119
|
+
Boolean(input.module) == Boolean(input.program)) {
|
|
120
|
+
invalidInput("Android application generation requires a closed request with exactly one semantic module or program.");
|
|
121
|
+
}
|
|
122
|
+
if (input.language !== undefined && input.language != "android" || input.role !== undefined && input.role != "application") {
|
|
123
|
+
invalidInput("Android application generation accepts only the Android application role.");
|
|
124
|
+
}
|
|
125
|
+
const configuration = normalizeConfiguration(input.configuration);
|
|
126
|
+
const program = input.program ?? normalizeSingleModule(
|
|
127
|
+
/** @type {import("../semantic/types.js").SemanticModule | undefined} */ (input.module));
|
|
128
|
+
const prepared = preflightSemanticProgram({
|
|
129
|
+
backendLanguage: "kotlin",
|
|
130
|
+
diagnosticLanguage: "android",
|
|
131
|
+
program: /** @type {import("../semantic/types.js").SemanticProgram} */ (program),
|
|
132
|
+
sourceLanguages: androidSourceLanguages
|
|
133
|
+
});
|
|
134
|
+
validateActivityClassName(configuration.activityClassName, prepared.program.entryModule);
|
|
135
|
+
const packagePath = configuration.packageName.replaceAll(".", "/");
|
|
136
|
+
const semanticRoot = `${projectRoot}/app/src/main/kotlin/${packagePath}/semantic`;
|
|
137
|
+
const modulePaths = new Map(prepared.modules.map(module => [
|
|
138
|
+
/** @type {string} */ (Reflect.get(module, "id")),
|
|
139
|
+
`${semanticRoot}/${moduleName(/** @type {string} */ (Reflect.get(module, "id")))}.kt`
|
|
140
|
+
]));
|
|
141
|
+
const resources = normalizeInputFiles(input.resources, "resource");
|
|
142
|
+
const assets = normalizeInputFiles(input.assets, "asset");
|
|
143
|
+
const fixedPaths = applicationPaths(configuration, modulePaths);
|
|
144
|
+
const completePaths = [
|
|
145
|
+
...fixedPaths,
|
|
146
|
+
...resources.map(({ fullPath }) => fullPath),
|
|
147
|
+
...assets.map(({ fullPath }) => fullPath)
|
|
148
|
+
];
|
|
149
|
+
const conflict = findPortableArtifactPathConflict(completePaths);
|
|
150
|
+
if (conflict) {
|
|
151
|
+
invalidPath(`Application artifact path '${conflict.path}' has a ${conflict.kind} conflict${conflict.other ? ` with '${conflict.other}'` : ""}.`);
|
|
152
|
+
}
|
|
153
|
+
return { ...prepared, assets, configuration, modulePaths, packagePath, resources };
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Generates one complete deterministic Android/Kotlin project.
|
|
157
|
+
* @param {Record<string, unknown>} input - Application generation request.
|
|
158
|
+
* @returns {{artifacts: import("../semantic/types.js").GeneratedSetArtifact[], metadata: {applicationManifest: string, platformQualification: string}, target: "android"}} Candidate artifact set.
|
|
159
|
+
*/
|
|
160
|
+
export function generateAndroidApplication(input) {
|
|
161
|
+
const prepared = preflightAndroidApplication(input);
|
|
162
|
+
const configurationSpans = /** @type {ConfigurationSpan[]} */ ([]);
|
|
163
|
+
const configuration = prepared.configuration;
|
|
164
|
+
const packagePath = prepared.packagePath;
|
|
165
|
+
const semanticArtifacts = renderSemanticArtifacts(prepared);
|
|
166
|
+
const artifacts = /** @type {import("../semantic/types.js").GeneratedSetArtifact[]} */ ([
|
|
167
|
+
configuredText(`${projectRoot}/app/build.gradle.kts`, "manifest", renderAppBuild(configuration, configurationSpans), "Pinned dependency-minimal Android application build."),
|
|
168
|
+
configuredText(`${projectRoot}/app/src/androidTest/AndroidManifest.xml`, "manifest", renderAndroidTestManifest(configuration, configurationSpans), "Platform-only Android instrumentation declaration."),
|
|
169
|
+
configuredText(`${projectRoot}/app/src/androidTest/kotlin/${packagePath}/SemantifoldUiInstrumentation.kt`, "support", renderInstrumentation(configuration, configurationSpans), "Platform-only accessibility and lifecycle assertion."),
|
|
170
|
+
configuredText(`${projectRoot}/app/src/main/AndroidManifest.xml`, "manifest", renderAndroidManifest(configuration, configurationSpans), "Closed allowlisted Android application manifest."),
|
|
171
|
+
configuredText(`${projectRoot}/app/src/main/kotlin/${packagePath}/${configuration.activityClassName}.kt`, "entry", renderActivity(configuration, prepared.program.entryModule, configurationSpans), "Synthetic Android activity lifecycle and native output view binding."),
|
|
172
|
+
...semanticArtifacts,
|
|
173
|
+
syntheticText(`${projectRoot}/app/src/main/res/layout/activity_main.xml`, "resource", renderLayout(), "Synthetic one-view Android layout and accessibility identity."),
|
|
174
|
+
configuredText(`${projectRoot}/app/src/main/res/values/strings.xml`, "resource", renderStrings(configuration, configurationSpans), "Escaped application labels derived from configuration."),
|
|
175
|
+
syntheticText(`${projectRoot}/app/src/main/res/values/themes.xml`, "resource", renderTheme(), "Closed platform-native application theme."),
|
|
176
|
+
syntheticText(`${projectRoot}/app/src/main/res/xml/backup_rules.xml`, "resource", renderBackupRules(), "Closed API 23-30 full-backup exclusions."),
|
|
177
|
+
syntheticText(`${projectRoot}/app/src/main/res/xml/data_extraction_rules.xml`, "resource", renderDataExtractionRules(), "Closed Android 12+ cloud-backup and device-transfer exclusions."),
|
|
178
|
+
configuredText(`${projectRoot}/app/src/test/kotlin/${packagePath}/SemantifoldEntryTest.kt`, "support", renderUnitTest(configuration, prepared.program.entryModule, configurationSpans), "JUnit test-scope-only semantic determinism assertion."),
|
|
179
|
+
syntheticText(`${projectRoot}/build.gradle.kts`, "manifest", renderRootBuild(), "Pinned Android and Kotlin build plugins."),
|
|
180
|
+
syntheticText(`${projectRoot}/gradle.properties`, "support", renderGradleProperties(), "Pinned hermetic Gradle process properties."),
|
|
181
|
+
configuredText(`${projectRoot}/settings.gradle.kts`, "manifest", renderSettings(configuration, configurationSpans), "Closed official Gradle plugin and dependency repositories."),
|
|
182
|
+
...prepared.resources.map(inputFileArtifact),
|
|
183
|
+
...prepared.assets.map(inputFileArtifact)
|
|
184
|
+
]);
|
|
185
|
+
artifacts.sort((left, right) => left.path < right.path ? -1 : left.path > right.path ? 1 : 0);
|
|
186
|
+
const manifestPath = `${projectRoot}/semantifold-project.json`;
|
|
187
|
+
artifacts.push(syntheticText(manifestPath, "manifest", renderProjectManifest(prepared, artifacts, manifestPath, configurationSpans), "Versioned Android project ownership, toolchain, and provenance manifest."));
|
|
188
|
+
return {
|
|
189
|
+
artifacts,
|
|
190
|
+
metadata: { applicationManifest: manifestPath, platformQualification: "tensorbuzz-kvm" },
|
|
191
|
+
target: "android"
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Renders the mapped semantic Kotlin artifacts for every program module.
|
|
196
|
+
* @param {ReturnType<typeof preflightAndroidApplication>} prepared - Prepared application.
|
|
197
|
+
* @returns {import("../semantic/types.js").GeneratedSetArtifact[]} Semantic Kotlin artifacts.
|
|
198
|
+
*/
|
|
199
|
+
function renderSemanticArtifacts(prepared) {
|
|
200
|
+
const semanticRoot = `${projectRoot}/app/src/main/kotlin/${prepared.packagePath}/semantic`;
|
|
201
|
+
/** @type {import("../semantic/types.js").GeneratedSetArtifact[]} */
|
|
202
|
+
const artifacts = [syntheticText(`${semanticRoot}/SemantifoldRuntime.kt`, "support", androidKotlinRuntime.replaceAll("__PACKAGE__", prepared.configuration.packageName), "Shared Android application output capture and checked Kotlin scalar support.")];
|
|
203
|
+
const declarations = new Map();
|
|
204
|
+
for (const module of prepared.modules) {
|
|
205
|
+
for (const declaration of module.functions)
|
|
206
|
+
declarations.set(declaration.id, {
|
|
207
|
+
declaration,
|
|
208
|
+
moduleId: /** @type {string} */ (Reflect.get(module, "id"))
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
for (const module of prepared.modules) {
|
|
212
|
+
const moduleId = /** @type {string} */ (Reflect.get(module, "id"));
|
|
213
|
+
const filename = /** @type {string} */ (prepared.modulePaths.get(moduleId));
|
|
214
|
+
const sinkName = outputSinkName(module);
|
|
215
|
+
const writer = new SourceWriter({
|
|
216
|
+
callArgumentSuffix: sinkName,
|
|
217
|
+
callNameResolver: expression => {
|
|
218
|
+
const resolved = declarations.get(expression.resolution?.declarationId);
|
|
219
|
+
if (!resolved)
|
|
220
|
+
throw new TypeError(`Unknown validated Android function '${expression.resolution?.declarationId}'.`);
|
|
221
|
+
return `SemantifoldModule${moduleName(resolved.moduleId)}.${resolved.declaration.name}`;
|
|
222
|
+
},
|
|
223
|
+
filename,
|
|
224
|
+
language: "kotlin",
|
|
225
|
+
methodStringEquality: true,
|
|
226
|
+
module,
|
|
227
|
+
program: prepared.program,
|
|
228
|
+
programPaths: prepared.modulePaths,
|
|
229
|
+
sources: prepared.sources
|
|
230
|
+
});
|
|
231
|
+
writer.synthetic(`@file:Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")\n\npackage ${prepared.configuration.packageName}.semantic\n\n`, "Android semantic package namespace", [module], [""]);
|
|
232
|
+
emitSemanticModule(writer, module, moduleId, moduleId == prepared.program.entryModule, sinkName);
|
|
233
|
+
const mapping = finalizeMapping(writer.finish());
|
|
234
|
+
artifacts.push({
|
|
235
|
+
content: mapping.generated.content,
|
|
236
|
+
contentKind: "text",
|
|
237
|
+
mediaType: "text/x-kotlin",
|
|
238
|
+
ownership: "generated",
|
|
239
|
+
path: filename,
|
|
240
|
+
provenance: { kind: "text", mapping, sourceMap: toSourceMapV3(mapping), sourceMapFilename: `${filename}.map` },
|
|
241
|
+
role: "source"
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
return artifacts;
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Emits one namespaced semantic module and its entry bridge when applicable.
|
|
248
|
+
* @param {SourceWriter} writer - Mapped Kotlin writer.
|
|
249
|
+
* @param {import("../semantic/types.js").SemanticModule} module - Prepared module.
|
|
250
|
+
* @param {string} moduleId - Stable module identity.
|
|
251
|
+
* @param {boolean} entry - Whether this module owns the entry block.
|
|
252
|
+
* @param {string} sinkName - Collision-safe output sink binding.
|
|
253
|
+
*/
|
|
254
|
+
function emitSemanticModule(writer, module, moduleId, entry, sinkName) {
|
|
255
|
+
writer.synthetic(`internal object SemantifoldModule${moduleName(moduleId)} {\n`, "Android semantic module namespace", [module], [""]);
|
|
256
|
+
module.functions.forEach((declaration, functionIndex) => {
|
|
257
|
+
const path = `/functions/${functionIndex}`;
|
|
258
|
+
writer.synthetic(" ", "Android namespaced function indentation", [declaration], [path]);
|
|
259
|
+
writer.mapped("fun", { mappingKind: "anchor", node: declaration, path });
|
|
260
|
+
writer.synthetic(" ", "declaration spacing", [declaration], [path]);
|
|
261
|
+
writer.mapped(declaration.name, { mappingKind: "exact", node: declaration, path, role: "name" });
|
|
262
|
+
writer.synthetic("(", "function parameter scaffold", [declaration], [path]);
|
|
263
|
+
declaration.parameters.forEach((parameter, parameterIndex) => {
|
|
264
|
+
const parameterPath = `${path}/parameters/${parameterIndex}`;
|
|
265
|
+
if (parameterIndex)
|
|
266
|
+
writer.synthetic(", ", "parameter separator", [parameter], [parameterPath]);
|
|
267
|
+
writer.mapped(parameter.name, { mappingKind: "exact", node: parameter, path: parameterPath, role: "name" });
|
|
268
|
+
writer.synthetic(": ", "parameter annotation scaffold", [parameter], [parameterPath]);
|
|
269
|
+
writer.mapped(emitScalarType("kotlin", parameter.type), { mappingKind: "exact", node: parameter.type,
|
|
270
|
+
path: `${parameterPath}/type`, role: "type" });
|
|
271
|
+
});
|
|
272
|
+
if (declaration.parameters.length)
|
|
273
|
+
writer.synthetic(", ", "output sink parameter separator", [declaration], [path]);
|
|
274
|
+
writer.synthetic(`${sinkName}: SemantifoldOutputSink): `, "shared Android output sink parameter", [declaration], [path]);
|
|
275
|
+
writer.mapped(emitScalarType("kotlin", declaration.returnType), { mappingKind: "exact", node: declaration.returnType,
|
|
276
|
+
path: `${path}/returnType`, role: "type" });
|
|
277
|
+
writer.synthetic(" {\n", "Kotlin function body scaffold", [declaration], [path]);
|
|
278
|
+
emitKotlinBlock(writer, declaration.body, " ", `${path}/body`, `${sinkName}.write`);
|
|
279
|
+
writer.synthetic(" }\n", "Kotlin function body scaffold", [declaration], [path]);
|
|
280
|
+
});
|
|
281
|
+
if (entry) {
|
|
282
|
+
writer.synthetic(" fun semantifoldEntry(): java.util.ArrayList<String> {\n", "Android semantic entry scaffold", [module.entryPoint], ["/entryPoint"]);
|
|
283
|
+
writer.synthetic(` val ${sinkName} = SemantifoldOutputSink()\n`, "Android output sink allocation", [module.entryPoint], ["/entryPoint"]);
|
|
284
|
+
writer.synthetic(` ${sinkName}.reset()\n`, "Android lifecycle-safe output reset", [module.entryPoint], ["/entryPoint"]);
|
|
285
|
+
emitKotlinBlock(writer, module.entryPoint.body, " ", "/entryPoint/body", `${sinkName}.write`);
|
|
286
|
+
writer.synthetic(` return ${sinkName}.snapshot()\n`, "Android semantic output return", [module.entryPoint], ["/entryPoint"]);
|
|
287
|
+
writer.synthetic(" }\n", "Android semantic entry scaffold", [module.entryPoint], ["/entryPoint"]);
|
|
288
|
+
}
|
|
289
|
+
writer.synthetic("}\n", "Android semantic module namespace", [module], [""]);
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Lists every fixed generated path before any writer is allocated.
|
|
293
|
+
* @param {import("../semantic/types.js").AndroidApplicationConfiguration} configuration - Normalized application configuration.
|
|
294
|
+
* @param {Map<string, string>} modulePaths - Semantic module output paths.
|
|
295
|
+
* @returns {string[]} Fixed owned artifact paths.
|
|
296
|
+
*/
|
|
297
|
+
function applicationPaths(configuration, modulePaths) {
|
|
298
|
+
const packagePath = configuration.packageName.replaceAll(".", "/");
|
|
299
|
+
return [
|
|
300
|
+
`${projectRoot}/app/build.gradle.kts`,
|
|
301
|
+
`${projectRoot}/app/src/androidTest/AndroidManifest.xml`,
|
|
302
|
+
`${projectRoot}/app/src/androidTest/kotlin/${packagePath}/SemantifoldUiInstrumentation.kt`,
|
|
303
|
+
`${projectRoot}/app/src/main/AndroidManifest.xml`,
|
|
304
|
+
`${projectRoot}/app/src/main/kotlin/${packagePath}/${configuration.activityClassName}.kt`,
|
|
305
|
+
...modulePaths.values(),
|
|
306
|
+
`${projectRoot}/app/src/main/kotlin/${packagePath}/semantic/SemantifoldRuntime.kt`,
|
|
307
|
+
`${projectRoot}/app/src/main/res/layout/activity_main.xml`,
|
|
308
|
+
`${projectRoot}/app/src/main/res/values/strings.xml`,
|
|
309
|
+
`${projectRoot}/app/src/main/res/values/themes.xml`,
|
|
310
|
+
`${projectRoot}/app/src/main/res/xml/backup_rules.xml`,
|
|
311
|
+
`${projectRoot}/app/src/main/res/xml/data_extraction_rules.xml`,
|
|
312
|
+
`${projectRoot}/app/src/test/kotlin/${packagePath}/SemantifoldEntryTest.kt`,
|
|
313
|
+
`${projectRoot}/build.gradle.kts`,
|
|
314
|
+
`${projectRoot}/gradle.properties`,
|
|
315
|
+
`${projectRoot}/settings.gradle.kts`,
|
|
316
|
+
`${projectRoot}/semantifold-project.json`
|
|
317
|
+
];
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Renders the pinned root plugin declarations.
|
|
321
|
+
* @returns {string} Root Gradle Kotlin script.
|
|
322
|
+
*/
|
|
323
|
+
function renderRootBuild() {
|
|
324
|
+
return `plugins {
|
|
325
|
+
id("com.android.application") version "${toolchain.androidGradlePlugin}" apply false
|
|
326
|
+
id("org.jetbrains.kotlin.android") version "${toolchain.kotlin}" apply false
|
|
327
|
+
}
|
|
328
|
+
`;
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Renders closed project settings and repositories.
|
|
332
|
+
* @param {import("../semantic/types.js").AndroidApplicationConfiguration} configuration - Normalized configuration.
|
|
333
|
+
* @param {ConfigurationSpan[]} spans - Collected configuration provenance spans.
|
|
334
|
+
* @returns {string} Settings Gradle Kotlin script.
|
|
335
|
+
*/
|
|
336
|
+
function renderSettings(configuration, spans) {
|
|
337
|
+
return configured(`${projectRoot}/settings.gradle.kts`, spans, [`pluginManagement {
|
|
338
|
+
repositories {
|
|
339
|
+
google()
|
|
340
|
+
mavenCentral()
|
|
341
|
+
gradlePluginPortal()
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
dependencyResolutionManagement {
|
|
346
|
+
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
|
347
|
+
repositories {
|
|
348
|
+
google()
|
|
349
|
+
mavenCentral()
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
rootProject.name = "`, field("productName", kotlinEscape(configuration.productName)), `"
|
|
354
|
+
include(":app")
|
|
355
|
+
`]);
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Renders deterministic Gradle process properties.
|
|
359
|
+
* @returns {string} Gradle properties file.
|
|
360
|
+
*/
|
|
361
|
+
function renderGradleProperties() {
|
|
362
|
+
return `org.gradle.caching=false
|
|
363
|
+
org.gradle.configuration-cache=false
|
|
364
|
+
org.gradle.daemon=false
|
|
365
|
+
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 -Duser.language=en -Duser.country=US -Duser.timezone=UTC
|
|
366
|
+
org.gradle.parallel=false
|
|
367
|
+
kotlin.stdlib.default.dependency=false
|
|
368
|
+
kotlin.code.style=official
|
|
369
|
+
`;
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Renders the application module build contract.
|
|
373
|
+
* @param {import("../semantic/types.js").AndroidApplicationConfiguration} configuration - Normalized configuration.
|
|
374
|
+
* @param {ConfigurationSpan[]} spans - Collected configuration provenance spans.
|
|
375
|
+
* @returns {string} Application Gradle Kotlin script.
|
|
376
|
+
*/
|
|
377
|
+
function renderAppBuild(configuration, spans) {
|
|
378
|
+
return configured(`${projectRoot}/app/build.gradle.kts`, spans, [
|
|
379
|
+
`import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
|
380
|
+
import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile
|
|
381
|
+
|
|
382
|
+
plugins {
|
|
383
|
+
id("com.android.application")
|
|
384
|
+
id("org.jetbrains.kotlin.android")
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
dependencies {
|
|
388
|
+
testRuntimeOnly("org.hamcrest:hamcrest-core:1.3") {
|
|
389
|
+
isTransitive = false
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
val semantifoldKotlinHome = providers.environmentVariable("SEMANTIFOLD_KOTLIN_HOME").orNull
|
|
394
|
+
?: throw GradleException("SEMANTIFOLD_ANDROID_INFRASTRUCTURE: SEMANTIFOLD_KOTLIN_HOME is required")
|
|
395
|
+
val semantifoldKotlinStdlib = file("$semantifoldKotlinHome/lib/kotlin-stdlib.jar")
|
|
396
|
+
if (!semantifoldKotlinStdlib.isFile) {
|
|
397
|
+
throw GradleException("SEMANTIFOLD_ANDROID_INFRASTRUCTURE: pinned Kotlin compile library is missing")
|
|
398
|
+
}
|
|
399
|
+
val semantifoldAcceptanceRoot = providers.environmentVariable("SEMANTIFOLD_ANDROID_ACCEPTANCE_ROOT").orNull
|
|
400
|
+
?: throw GradleException("SEMANTIFOLD_ANDROID_INFRASTRUCTURE: SEMANTIFOLD_ANDROID_ACCEPTANCE_ROOT is required")
|
|
401
|
+
val semantifoldDebugKeystore = file("$semantifoldAcceptanceRoot/debug.keystore")
|
|
402
|
+
if (!semantifoldDebugKeystore.isFile) {
|
|
403
|
+
throw GradleException("SEMANTIFOLD_ANDROID_INFRASTRUCTURE: ephemeral debug keystore is missing")
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
android {
|
|
407
|
+
namespace = "`,
|
|
408
|
+
field("namespace", configuration.namespace), `"\n compileSdk = `, field("compileSdk", String(configuration.compileSdk)),
|
|
409
|
+
`\n buildToolsVersion = "`, field("buildToolsVersion", configuration.buildToolsVersion), `"\n\n defaultConfig {\n applicationId = "`,
|
|
410
|
+
field("applicationId", configuration.applicationId), `"\n minSdk = `, field("minimumSdk", String(configuration.minimumSdk)),
|
|
411
|
+
`\n targetSdk = `, field("targetSdk", String(configuration.targetSdk)), `\n versionCode = `,
|
|
412
|
+
field("versionCode", String(configuration.versionCode)), `\n versionName = "`, field("versionName", kotlinEscape(configuration.versionName)),
|
|
413
|
+
`"\n testApplicationId = "`, field("applicationId", configuration.applicationId),
|
|
414
|
+
`.test"\n testInstrumentationRunner = "`, field("packageName", configuration.packageName),
|
|
415
|
+
`.SemantifoldUiInstrumentation"\n }\n\n signingConfigs {\n getByName("debug") {\n storeFile = semantifoldDebugKeystore\n storePassword = "android"\n keyAlias = "androiddebugkey"\n keyPassword = "android"\n }\n }\n buildTypes {\n getByName("debug") {\n signingConfig = signingConfigs.getByName("debug")\n }\n }\n buildFeatures {\n buildConfig = false\n }\n compileOptions {\n sourceCompatibility = JavaVersion.VERSION_21\n targetCompatibility = JavaVersion.VERSION_21\n }\n lint {\n abortOnError = true\n checkDependencies = true\n disable += "MissingApplicationIcon"\n disable += "OldTargetApi"\n disable += "GradleDependency"\n warningsAsErrors = true\n }\n packaging {\n resources.excludes += setOf("META-INF/LICENSE*", "META-INF/NOTICE*")\n }\n}\n\ntasks.withType<KotlinJvmCompile>().configureEach {\n libraries.from(files(semantifoldKotlinStdlib))\n compilerOptions {\n allWarningsAsErrors.set(true)\n freeCompilerArgs.addAll("-Xno-call-assertions", "-Xno-param-assertions", "-Xno-receiver-assertions")\n jvmTarget.set(JvmTarget.JVM_21)\n }\n}\n\ndependencies {\n testImplementation("junit:junit:4.13.2") {\n isTransitive = false\n }\n}\n`
|
|
416
|
+
]);
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* Renders the allowlisted Android application manifest.
|
|
420
|
+
* @param {import("../semantic/types.js").AndroidApplicationConfiguration} configuration - Normalized configuration.
|
|
421
|
+
* @param {ConfigurationSpan[]} spans - Collected configuration provenance spans.
|
|
422
|
+
* @returns {string} Android application manifest.
|
|
423
|
+
*/
|
|
424
|
+
function renderAndroidManifest(configuration, spans) {
|
|
425
|
+
const orientation = configuration.orientation == "unspecified" ? "" : ` android:screenOrientation="${configuration.orientation}"`;
|
|
426
|
+
return configured(`${projectRoot}/app/src/main/AndroidManifest.xml`, spans, [
|
|
427
|
+
`<?xml version="1.0" encoding="utf-8"?>\n<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n <application\n android:allowBackup="false"\n android:dataExtractionRules="@xml/data_extraction_rules"\n android:fullBackupContent="@xml/backup_rules"\n android:label="@string/app_name"\n android:supportsRtl="true"\n android:theme="@style/Theme.Semantifold">\n <activity\n android:name="`,
|
|
428
|
+
field("packageName", configuration.packageName), `.`, field("activityClassName", configuration.activityClassName),
|
|
429
|
+
`"\n android:exported="true"`,
|
|
430
|
+
orientation ? field("orientation", orientation) : "", `>\n <intent-filter>\n <action android:name="android.intent.action.MAIN" />\n <category android:name="android.intent.category.LAUNCHER" />\n </intent-filter>\n </activity>\n </application>\n</manifest>\n`
|
|
431
|
+
]);
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Renders explicit API 23-30 exclusions for every full-backup storage domain.
|
|
435
|
+
* @returns {string} Android full-backup rules XML.
|
|
436
|
+
*/
|
|
437
|
+
function renderBackupRules() {
|
|
438
|
+
return `<?xml version="1.0" encoding="utf-8"?>
|
|
439
|
+
<full-backup-content>
|
|
440
|
+
<exclude domain="root" path="." />
|
|
441
|
+
<exclude domain="file" path="." />
|
|
442
|
+
<exclude domain="database" path="." />
|
|
443
|
+
<exclude domain="sharedpref" path="." />
|
|
444
|
+
<exclude domain="external" path="." />
|
|
445
|
+
<exclude domain="device_root" path="." />
|
|
446
|
+
<exclude domain="device_file" path="." />
|
|
447
|
+
<exclude domain="device_database" path="." />
|
|
448
|
+
<exclude domain="device_sharedpref" path="." />
|
|
449
|
+
</full-backup-content>
|
|
450
|
+
`;
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* Renders explicit Android 12+ exclusions for every cloud-backup and device-transfer storage domain.
|
|
454
|
+
* @returns {string} Android data extraction rules XML.
|
|
455
|
+
*/
|
|
456
|
+
function renderDataExtractionRules() {
|
|
457
|
+
return `<?xml version="1.0" encoding="utf-8"?>
|
|
458
|
+
<data-extraction-rules>
|
|
459
|
+
<cloud-backup>
|
|
460
|
+
<exclude domain="root" path="." />
|
|
461
|
+
<exclude domain="file" path="." />
|
|
462
|
+
<exclude domain="database" path="." />
|
|
463
|
+
<exclude domain="sharedpref" path="." />
|
|
464
|
+
<exclude domain="external" path="." />
|
|
465
|
+
<exclude domain="device_root" path="." />
|
|
466
|
+
<exclude domain="device_file" path="." />
|
|
467
|
+
<exclude domain="device_database" path="." />
|
|
468
|
+
<exclude domain="device_sharedpref" path="." />
|
|
469
|
+
</cloud-backup>
|
|
470
|
+
<device-transfer>
|
|
471
|
+
<exclude domain="root" path="." />
|
|
472
|
+
<exclude domain="file" path="." />
|
|
473
|
+
<exclude domain="database" path="." />
|
|
474
|
+
<exclude domain="sharedpref" path="." />
|
|
475
|
+
<exclude domain="external" path="." />
|
|
476
|
+
<exclude domain="device_root" path="." />
|
|
477
|
+
<exclude domain="device_file" path="." />
|
|
478
|
+
<exclude domain="device_database" path="." />
|
|
479
|
+
<exclude domain="device_sharedpref" path="." />
|
|
480
|
+
</device-transfer>
|
|
481
|
+
</data-extraction-rules>
|
|
482
|
+
`;
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* Renders the platform-only instrumentation manifest.
|
|
486
|
+
* @param {import("../semantic/types.js").AndroidApplicationConfiguration} configuration - Normalized configuration.
|
|
487
|
+
* @param {ConfigurationSpan[]} spans - Collected configuration provenance spans.
|
|
488
|
+
* @returns {string} Android test manifest.
|
|
489
|
+
*/
|
|
490
|
+
function renderAndroidTestManifest(configuration, spans) {
|
|
491
|
+
return configured(`${projectRoot}/app/src/androidTest/AndroidManifest.xml`, spans, [
|
|
492
|
+
`<?xml version="1.0" encoding="utf-8"?>\n<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n <instrumentation\n android:name="`,
|
|
493
|
+
field("packageName", configuration.packageName), `.SemantifoldUiInstrumentation"\n android:targetPackage="`,
|
|
494
|
+
field("applicationId", xmlEscape(configuration.applicationId)), `" />\n</manifest>\n`
|
|
495
|
+
]);
|
|
496
|
+
}
|
|
497
|
+
/**
|
|
498
|
+
* Renders the synthetic launcher activity.
|
|
499
|
+
* @param {import("../semantic/types.js").AndroidApplicationConfiguration} configuration - Normalized configuration.
|
|
500
|
+
* @param {string} entryModule - Selected semantic entry module identity.
|
|
501
|
+
* @param {ConfigurationSpan[]} spans - Collected configuration provenance spans.
|
|
502
|
+
* @returns {string} Kotlin launcher activity source.
|
|
503
|
+
*/
|
|
504
|
+
function renderActivity(configuration, entryModule, spans) {
|
|
505
|
+
const resourceImport = configuration.namespace == configuration.packageName
|
|
506
|
+
? []
|
|
507
|
+
: ["import ", field("namespace", configuration.namespace), ".R\n"];
|
|
508
|
+
return configured(`${projectRoot}/app/src/main/kotlin/${configuration.packageName.replaceAll(".", "/")}/${configuration.activityClassName}.kt`, spans, [
|
|
509
|
+
"package ", field("packageName", configuration.packageName), `
|
|
510
|
+
|
|
511
|
+
import android.app.Activity
|
|
512
|
+
import android.annotation.SuppressLint
|
|
513
|
+
import android.os.Bundle
|
|
514
|
+
import android.widget.TextView
|
|
515
|
+
`, ...resourceImport, `import `, field("packageName", configuration.packageName),
|
|
516
|
+
`.semantic.SemantifoldModule${moduleName(entryModule)}
|
|
517
|
+
|
|
518
|
+
class `, field("activityClassName", configuration.activityClassName), ` : Activity() {
|
|
519
|
+
@SuppressLint("SetTextI18n")
|
|
520
|
+
override fun onCreate(savedInstanceState: Bundle?) {
|
|
521
|
+
super.onCreate(savedInstanceState)
|
|
522
|
+
setContentView(R.layout.activity_main)
|
|
523
|
+
val lines = SemantifoldModule${moduleName(entryModule)}.semantifoldEntry()
|
|
524
|
+
val output = java.lang.StringBuilder()
|
|
525
|
+
var index = 0
|
|
526
|
+
while (index < lines.size) {
|
|
527
|
+
if (index > 0) output.append('\\n')
|
|
528
|
+
output.append(lines[index])
|
|
529
|
+
index += 1
|
|
530
|
+
}
|
|
531
|
+
findViewById<TextView>(R.id.semantifold_output).text = output.toString()
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
`
|
|
535
|
+
]);
|
|
536
|
+
}
|
|
537
|
+
/**
|
|
538
|
+
* Renders the single accessibility-important native text view.
|
|
539
|
+
* @returns {string} Android layout XML.
|
|
540
|
+
*/
|
|
541
|
+
function renderLayout() {
|
|
542
|
+
return `<?xml version="1.0" encoding="utf-8"?>
|
|
543
|
+
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
|
544
|
+
android:id="@+id/semantifold_output"
|
|
545
|
+
android:layout_width="match_parent"
|
|
546
|
+
android:layout_height="match_parent"
|
|
547
|
+
android:gravity="center"
|
|
548
|
+
android:importantForAccessibility="yes"
|
|
549
|
+
android:textIsSelectable="true" />
|
|
550
|
+
`;
|
|
551
|
+
}
|
|
552
|
+
/**
|
|
553
|
+
* Renders configuration-derived string resources.
|
|
554
|
+
* @param {import("../semantic/types.js").AndroidApplicationConfiguration} configuration - Normalized configuration.
|
|
555
|
+
* @param {ConfigurationSpan[]} spans - Collected configuration provenance spans.
|
|
556
|
+
* @returns {string} Android string resource XML.
|
|
557
|
+
*/
|
|
558
|
+
function renderStrings(configuration, spans) {
|
|
559
|
+
return configured(`${projectRoot}/app/src/main/res/values/strings.xml`, spans, [
|
|
560
|
+
`<?xml version="1.0" encoding="utf-8"?>\n<resources>\n <string name="app_name">`,
|
|
561
|
+
field("displayName", xmlEscape(configuration.displayName)), `</string>\n</resources>\n`
|
|
562
|
+
]);
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* Renders the fixed platform-native baseline theme.
|
|
566
|
+
* @returns {string} Android theme resource XML.
|
|
567
|
+
*/
|
|
568
|
+
function renderTheme() {
|
|
569
|
+
return `<?xml version="1.0" encoding="utf-8"?>
|
|
570
|
+
<resources>
|
|
571
|
+
<style name="Theme.Semantifold" parent="android:style/Theme.Material.Light.NoActionBar">
|
|
572
|
+
<item name="android:fontFamily">sans</item>
|
|
573
|
+
<item name="android:windowActionModeOverlay">true</item>
|
|
574
|
+
</style>
|
|
575
|
+
</resources>
|
|
576
|
+
`;
|
|
577
|
+
}
|
|
578
|
+
/**
|
|
579
|
+
* Renders the sole JUnit test-scope semantic assertion.
|
|
580
|
+
* @param {import("../semantic/types.js").AndroidApplicationConfiguration} configuration - Normalized configuration.
|
|
581
|
+
* @param {string} entryModule - Selected semantic entry module identity.
|
|
582
|
+
* @param {ConfigurationSpan[]} spans - Collected configuration provenance spans.
|
|
583
|
+
* @returns {string} Kotlin unit test source.
|
|
584
|
+
*/
|
|
585
|
+
function renderUnitTest(configuration, entryModule, spans) {
|
|
586
|
+
return configured(`${projectRoot}/app/src/test/kotlin/${configuration.packageName.replaceAll(".", "/")}/SemantifoldEntryTest.kt`, spans, [
|
|
587
|
+
"package ", field("packageName", configuration.packageName), `
|
|
588
|
+
|
|
589
|
+
import `, field("packageName", configuration.packageName), `.semantic.SemantifoldModule${moduleName(entryModule)}
|
|
590
|
+
import org.junit.Test
|
|
591
|
+
|
|
592
|
+
class SemantifoldEntryTest {
|
|
593
|
+
@Test
|
|
594
|
+
fun entryIsDeterministicAcrossFreshRuns() {
|
|
595
|
+
val firstOutput = SemantifoldModule${moduleName(entryModule)}.semantifoldEntry()
|
|
596
|
+
val secondOutput = SemantifoldModule${moduleName(entryModule)}.semantifoldEntry()
|
|
597
|
+
if (!firstOutput.equals(secondOutput)) {
|
|
598
|
+
throw AssertionError("Semantic entry output differs across fresh runs.")
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
`
|
|
603
|
+
]);
|
|
604
|
+
}
|
|
605
|
+
/**
|
|
606
|
+
* Renders the framework-only instrumentation runner.
|
|
607
|
+
* @param {import("../semantic/types.js").AndroidApplicationConfiguration} configuration - Normalized configuration.
|
|
608
|
+
* @param {ConfigurationSpan[]} spans - Collected configuration provenance spans.
|
|
609
|
+
* @returns {string} Kotlin instrumentation source.
|
|
610
|
+
*/
|
|
611
|
+
function renderInstrumentation(configuration, spans) {
|
|
612
|
+
const resourceImport = configuration.namespace == configuration.packageName
|
|
613
|
+
? []
|
|
614
|
+
: ["import ", field("namespace", configuration.namespace), ".R\n"];
|
|
615
|
+
return configured(`${projectRoot}/app/src/androidTest/kotlin/${configuration.packageName.replaceAll(".", "/")}/SemantifoldUiInstrumentation.kt`, spans, [
|
|
616
|
+
"package ", field("packageName", configuration.packageName), `
|
|
617
|
+
|
|
618
|
+
import android.app.Activity
|
|
619
|
+
import android.app.Instrumentation
|
|
620
|
+
import android.content.Intent
|
|
621
|
+
import android.os.Bundle
|
|
622
|
+
import android.util.Base64
|
|
623
|
+
import android.view.View
|
|
624
|
+
import android.widget.TextView
|
|
625
|
+
import java.nio.charset.StandardCharsets
|
|
626
|
+
`, ...resourceImport, `
|
|
627
|
+
class SemantifoldUiInstrumentation : Instrumentation() {
|
|
628
|
+
private var inputArguments: Bundle? = null
|
|
629
|
+
|
|
630
|
+
override fun onCreate(arguments: Bundle?) {
|
|
631
|
+
inputArguments = arguments
|
|
632
|
+
start()
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
override fun onStart() {
|
|
636
|
+
try {
|
|
637
|
+
val providedArguments = inputArguments
|
|
638
|
+
?: throw IllegalStateException("missing instrumentation arguments")
|
|
639
|
+
val encoded = providedArguments.getString("expected_output_base64")
|
|
640
|
+
?: throw IllegalStateException("missing expected_output_base64 instrumentation argument")
|
|
641
|
+
val expected = String(Base64.decode(encoded, Base64.NO_WRAP), StandardCharsets.UTF_8)
|
|
642
|
+
val launch = targetContext.packageManager.getLaunchIntentForPackage(targetContext.packageName)
|
|
643
|
+
?: throw IllegalStateException("generated application has no launcher intent")
|
|
644
|
+
launch.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
|
|
645
|
+
val activity = startActivitySync(launch) as `, field("activityClassName", configuration.activityClassName), `
|
|
646
|
+
assertOutput(activity, expected)
|
|
647
|
+
val monitor = addMonitor(`, field("activityClassName", configuration.activityClassName), `::class.java.name, null, false)
|
|
648
|
+
runOnMainSync { activity.recreate() }
|
|
649
|
+
waitForIdleSync()
|
|
650
|
+
val recreated = waitForMonitorWithTimeout(monitor, 10_000)
|
|
651
|
+
?: throw IllegalStateException("activity was not recreated")
|
|
652
|
+
assertOutput(recreated, expected)
|
|
653
|
+
finish(Activity.RESULT_OK, Bundle())
|
|
654
|
+
} catch (error: Throwable) {
|
|
655
|
+
val result = Bundle()
|
|
656
|
+
result.putString("semantifold_failure", android.util.Log.getStackTraceString(error))
|
|
657
|
+
finish(Activity.RESULT_CANCELED, result)
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
private fun assertOutput(activity: Activity, expected: String) {
|
|
662
|
+
waitForIdleSync()
|
|
663
|
+
val actual = activity.findViewById<TextView>(R.id.semantifold_output)
|
|
664
|
+
?: throw IllegalStateException("missing semantifold_output view")
|
|
665
|
+
if (actual.visibility != View.VISIBLE) throw IllegalStateException("output view is not visible")
|
|
666
|
+
if (actual.javaClass != TextView::class.java) throw IllegalStateException("output view is not a native TextView")
|
|
667
|
+
if (!actual.text.toString().equals(expected)) throw IllegalStateException("output text mismatch")
|
|
668
|
+
if (actual.contentDescription != null) throw IllegalStateException("output content description must stay absent")
|
|
669
|
+
if (actual.importantForAccessibility != View.IMPORTANT_FOR_ACCESSIBILITY_YES) {
|
|
670
|
+
throw IllegalStateException("output view is not accessibility-important")
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
`
|
|
675
|
+
]);
|
|
676
|
+
}
|
|
677
|
+
/**
|
|
678
|
+
* Renders the ownership, toolchain, and provenance manifest.
|
|
679
|
+
* @param {ReturnType<typeof preflightAndroidApplication>} prepared - Prepared application.
|
|
680
|
+
* @param {import("../semantic/types.js").GeneratedSetArtifact[]} artifacts - Owned project artifacts.
|
|
681
|
+
* @param {string} manifestPath - Ownership manifest path.
|
|
682
|
+
* @param {ConfigurationSpan[]} spans - Configuration provenance spans.
|
|
683
|
+
* @returns {string} Canonical project manifest JSON.
|
|
684
|
+
*/
|
|
685
|
+
function renderProjectManifest(prepared, artifacts, manifestPath, spans) {
|
|
686
|
+
const inputPaths = new Set([...prepared.resources, ...prepared.assets].map(({ fullPath }) => fullPath));
|
|
687
|
+
const semanticArtifacts = artifacts.filter(artifact => artifact.provenance.kind == "text");
|
|
688
|
+
const ownedPaths = [...artifacts.map(({ path }) => path), manifestPath];
|
|
689
|
+
return stringifyCanonicalJson({
|
|
690
|
+
configuration: prepared.configuration,
|
|
691
|
+
generator: { name: "semantifold", version: generatorVersion },
|
|
692
|
+
ownership: {
|
|
693
|
+
excludedPathPatterns,
|
|
694
|
+
ownedPaths,
|
|
695
|
+
ownedRoot: projectRoot,
|
|
696
|
+
sha256: artifacts.map(artifact => ({ path: artifact.path, sha256: hashContent(artifact.content) }))
|
|
697
|
+
},
|
|
698
|
+
provenance: {
|
|
699
|
+
configuration: Object.keys(prepared.configuration).sort().map(fieldName => ({
|
|
700
|
+
field: fieldName,
|
|
701
|
+
manifestPointer: `/configuration/${fieldName}`,
|
|
702
|
+
outputs: configurationOutputs(spans, fieldName)
|
|
703
|
+
})),
|
|
704
|
+
inputs: [...prepared.resources, ...prepared.assets].map(item => ({
|
|
705
|
+
contentKind: item.contentKind,
|
|
706
|
+
mediaType: item.mediaType,
|
|
707
|
+
path: item.fullPath,
|
|
708
|
+
sha256: item.sha256
|
|
709
|
+
})),
|
|
710
|
+
semanticSources: prepared.sources.map(source => ({
|
|
711
|
+
artifacts: semanticArtifacts.filter(artifact => artifact.provenance.kind == "text" &&
|
|
712
|
+
artifact.provenance.mapping.spans.some(({ origin }) => originReferencesFilename(origin, source.filename)))
|
|
713
|
+
.map(({ path }) => path),
|
|
714
|
+
filename: source.filename,
|
|
715
|
+
...(source.language === undefined ? {} : { language: source.language }),
|
|
716
|
+
sha256: createHash("sha256").update(source.content).digest("hex")
|
|
717
|
+
})),
|
|
718
|
+
syntheticScaffolding: [...artifacts.filter(artifact => artifact.provenance.kind == "synthetic" &&
|
|
719
|
+
!inputPaths.has(artifact.path)).map(({ path }) => path), manifestPath]
|
|
720
|
+
},
|
|
721
|
+
schema: "SemantifoldAndroidProject",
|
|
722
|
+
semanticProgram: semanticProgramManifest(prepared),
|
|
723
|
+
target: "android",
|
|
724
|
+
toolchain,
|
|
725
|
+
version: 1
|
|
726
|
+
});
|
|
727
|
+
}
|
|
728
|
+
/**
|
|
729
|
+
* Renders semantic module and source identities for the project manifest.
|
|
730
|
+
* @param {ReturnType<typeof preflightAndroidApplication>} prepared - Prepared application.
|
|
731
|
+
* @returns {Record<string, unknown>} Semantic program manifest.
|
|
732
|
+
*/
|
|
733
|
+
function semanticProgramManifest(prepared) {
|
|
734
|
+
const sourceContent = new Map(prepared.sources.map(source => [source.filename, source.content]));
|
|
735
|
+
const order = new Map(prepared.program.modules.map((module, index) => [module.id, index]));
|
|
736
|
+
const modules = prepared.program.modules.map(module => {
|
|
737
|
+
const source = prepared.program.sources.find(candidate => candidate.filename == module.sourceFilename);
|
|
738
|
+
const content = sourceContent.get(module.sourceFilename);
|
|
739
|
+
if (!source || content === undefined)
|
|
740
|
+
throw new TypeError(`Preflighted Android module '${module.id}' lost source ownership.`);
|
|
741
|
+
return {
|
|
742
|
+
dependencies: [...new Set(module.imports.map(({ moduleId }) => moduleId))]
|
|
743
|
+
.sort((left, right) => /** @type {number} */ (order.get(left)) - /** @type {number} */ (order.get(right))),
|
|
744
|
+
generatedArtifacts: [prepared.modulePaths.get(module.id)],
|
|
745
|
+
id: module.id,
|
|
746
|
+
namespace: `SemantifoldModule${moduleName(module.id)}`,
|
|
747
|
+
source: { filename: source.filename, id: source.id, sha256: createHash("sha256").update(content).digest("hex") }
|
|
748
|
+
};
|
|
749
|
+
});
|
|
750
|
+
const entryId = prepared.program.entryModule;
|
|
751
|
+
return {
|
|
752
|
+
entry: {
|
|
753
|
+
activityArtifact: `${projectRoot}/app/src/main/kotlin/${prepared.packagePath}/${prepared.configuration.activityClassName}.kt`,
|
|
754
|
+
generatedArtifact: prepared.modulePaths.get(entryId),
|
|
755
|
+
generatedFunctionIdentity: `SemantifoldModule${moduleName(entryId)}.semantifoldEntry`,
|
|
756
|
+
moduleId: entryId
|
|
757
|
+
},
|
|
758
|
+
modules,
|
|
759
|
+
schema: "SemantifoldAndroidSemanticProgram",
|
|
760
|
+
sources: prepared.program.sources.map(source => ({
|
|
761
|
+
filename: source.filename,
|
|
762
|
+
id: source.id,
|
|
763
|
+
language: source.language,
|
|
764
|
+
moduleIds: modules.filter(module => module.source.filename == source.filename).map(({ id }) => id),
|
|
765
|
+
ownership: source.ownership ?? "application",
|
|
766
|
+
sha256: createHash("sha256").update(/** @type {string} */ (sourceContent.get(source.filename))).digest("hex")
|
|
767
|
+
})),
|
|
768
|
+
version: 1
|
|
769
|
+
};
|
|
770
|
+
}
|
|
771
|
+
/**
|
|
772
|
+
* Validates and normalizes the closed Android configuration.
|
|
773
|
+
* @param {unknown} candidate - Candidate configuration.
|
|
774
|
+
* @returns {import("../semantic/types.js").AndroidApplicationConfiguration} Normalized configuration.
|
|
775
|
+
*/
|
|
776
|
+
function normalizeConfiguration(candidate) {
|
|
777
|
+
if (candidate === undefined)
|
|
778
|
+
candidate = {};
|
|
779
|
+
if (!isPlainObject(candidate) || Object.keys(candidate).some(key => !configurationFields.has(key))) {
|
|
780
|
+
invalidConfiguration("Android application configuration must be a closed plain object.");
|
|
781
|
+
}
|
|
782
|
+
const configuration = /** @type {import("../semantic/types.js").AndroidApplicationConfigurationInput} */ (candidate);
|
|
783
|
+
const applicationId = configuration.applicationId ?? "dev.semantifold.generated";
|
|
784
|
+
const namespace = configuration.namespace ?? applicationId;
|
|
785
|
+
const packageName = configuration.packageName ?? namespace;
|
|
786
|
+
const activityClassName = configuration.activityClassName ?? "MainActivity";
|
|
787
|
+
const productName = configuration.productName ?? "Semantifold";
|
|
788
|
+
const displayName = configuration.displayName ?? "Semantifold";
|
|
789
|
+
const minimumSdk = configuration.minimumSdk ?? 23;
|
|
790
|
+
const targetSdk = configuration.targetSdk ?? toolchain.targetSdk;
|
|
791
|
+
const compileSdk = configuration.compileSdk ?? toolchain.compileSdk;
|
|
792
|
+
const buildToolsVersion = configuration.buildToolsVersion ?? toolchain.buildTools;
|
|
793
|
+
const versionCode = configuration.versionCode ?? 1;
|
|
794
|
+
const versionName = configuration.versionName ?? "1.0.0";
|
|
795
|
+
const orientation = configuration.orientation ?? "unspecified";
|
|
796
|
+
const theme = configuration.theme ?? "system";
|
|
797
|
+
for (const [label, value] of [["Application ID", applicationId], ["Namespace", namespace], ["Package name", packageName]]) {
|
|
798
|
+
if (typeof value != "string" || !isPackageName(value))
|
|
799
|
+
invalidConfiguration(`${label} must be a lowercase Java package identity.`);
|
|
800
|
+
}
|
|
801
|
+
if (typeof activityClassName != "string" || !/^[A-Z][A-Za-z0-9]*$/u.test(activityClassName) || kotlinKeywords.has(activityClassName)) {
|
|
802
|
+
invalidConfiguration("Activity class name must be an ASCII upper-camel Kotlin identifier.");
|
|
803
|
+
}
|
|
804
|
+
for (const [label, value] of [["Product name", productName], ["Display name", displayName]]) {
|
|
805
|
+
if (typeof value != "string" || value.length == 0 || value.trim() != value || [...value].length > 64 ||
|
|
806
|
+
!hasOnlyUnicodeScalars(value) || /[\p{Cc}\p{Zl}\p{Zp}\uFFFE\uFFFF]/u.test(value)) {
|
|
807
|
+
invalidConfiguration(`${label} must be a non-empty single-line Unicode scalar string of at most 64 characters.`);
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
if (/[\\/:<>"?*|]/u.test(productName)) {
|
|
811
|
+
invalidConfiguration("Product name contains a character forbidden by the Gradle project-name contract.");
|
|
812
|
+
}
|
|
813
|
+
if (minimumSdk !== 23 || targetSdk !== 35 || compileSdk !== 35 || minimumSdk > targetSdk || targetSdk > compileSdk) {
|
|
814
|
+
invalidConfiguration("SDK levels must use the qualified minimum 23, target 35, and compile 35 matrix.");
|
|
815
|
+
}
|
|
816
|
+
if (buildToolsVersion != toolchain.buildTools)
|
|
817
|
+
invalidConfiguration("Build Tools version must be exactly '35.0.0'.");
|
|
818
|
+
if (!Number.isSafeInteger(versionCode) || versionCode < 1 || versionCode > 2_100_000_000) {
|
|
819
|
+
invalidConfiguration("Version code must be a positive Android-safe integer.");
|
|
820
|
+
}
|
|
821
|
+
if (typeof versionName != "string" || !semanticVersionPattern.test(versionName)) {
|
|
822
|
+
invalidConfiguration("Version name must be a canonical non-empty semantic version string.");
|
|
823
|
+
}
|
|
824
|
+
if (orientation != "unspecified")
|
|
825
|
+
invalidConfiguration("The baseline orientation is exactly 'unspecified'.");
|
|
826
|
+
if (theme != "system")
|
|
827
|
+
invalidConfiguration("The baseline theme is exactly 'system'.");
|
|
828
|
+
if (configuration.permissions !== undefined &&
|
|
829
|
+
(!isDenseArray(configuration.permissions) || configuration.permissions.length != 0)) {
|
|
830
|
+
invalidConfiguration("The baseline Android permission set must be empty.");
|
|
831
|
+
}
|
|
832
|
+
return {
|
|
833
|
+
activityClassName,
|
|
834
|
+
applicationId,
|
|
835
|
+
buildToolsVersion,
|
|
836
|
+
compileSdk,
|
|
837
|
+
displayName,
|
|
838
|
+
minimumSdk,
|
|
839
|
+
namespace,
|
|
840
|
+
orientation,
|
|
841
|
+
packageName,
|
|
842
|
+
productName,
|
|
843
|
+
targetSdk,
|
|
844
|
+
theme,
|
|
845
|
+
versionCode,
|
|
846
|
+
versionName
|
|
847
|
+
};
|
|
848
|
+
}
|
|
849
|
+
/**
|
|
850
|
+
* Validates, snapshots, and orders caller-provided inputs.
|
|
851
|
+
* @param {unknown} candidate - Candidate resource or asset list.
|
|
852
|
+
* @param {"asset" | "resource"} kind - Android input kind.
|
|
853
|
+
* @returns {AndroidInputFile[]} Normalized inputs.
|
|
854
|
+
*/
|
|
855
|
+
function normalizeInputFiles(candidate, kind) {
|
|
856
|
+
if (candidate === undefined)
|
|
857
|
+
return [];
|
|
858
|
+
if (!isDenseArray(candidate))
|
|
859
|
+
invalidResource(`Application ${kind}s must be a dense array.`);
|
|
860
|
+
/** @type {AndroidInputFile[]} */
|
|
861
|
+
const files = [];
|
|
862
|
+
for (let index = 0; index < candidate.length; index += 1) {
|
|
863
|
+
const item = candidate[index];
|
|
864
|
+
if (!isPlainObject(item) || Object.keys(item).length != resourceFields.size ||
|
|
865
|
+
Object.keys(item).some(key => !resourceFields.has(key)))
|
|
866
|
+
invalidResource(`${capitalize(kind)} ${index} must use the closed input schema.`);
|
|
867
|
+
const path = item.path;
|
|
868
|
+
const content = item.content;
|
|
869
|
+
const mediaType = item.mediaType;
|
|
870
|
+
const sha256 = item.sha256;
|
|
871
|
+
if (!isSafeArtifactPath(path) || kind == "resource" && !isAllowedResourcePath(path)) {
|
|
872
|
+
invalidResource(`${capitalize(kind)} ${index} has an unsafe or unsupported path.`);
|
|
873
|
+
}
|
|
874
|
+
if (typeof content != "string" && !(content instanceof Uint8Array) || typeof content == "string" &&
|
|
875
|
+
(content.length == 0 || !hasOnlyUnicodeScalars(content)) || content instanceof Uint8Array && content.byteLength == 0) {
|
|
876
|
+
invalidResource(`${capitalize(kind)} '${path}' requires non-empty exact content.`);
|
|
877
|
+
}
|
|
878
|
+
if (typeof mediaType != "string" || !mediaTypePattern.test(mediaType) || /[\r\n]/u.test(mediaType)) {
|
|
879
|
+
invalidResource(`${capitalize(kind)} '${path}' requires an explicit valid media type.`);
|
|
880
|
+
}
|
|
881
|
+
if (kind == "resource" && path.startsWith("drawable-") && !["image/png", "image/webp"].includes(mediaType)) {
|
|
882
|
+
invalidResource(`Drawable resource '${path}' must use PNG or WebP media type.`);
|
|
883
|
+
}
|
|
884
|
+
const snapshot = typeof content == "string" ? content : new Uint8Array(content);
|
|
885
|
+
const actualHash = createHash("sha256").update(snapshot).digest("hex");
|
|
886
|
+
if (typeof sha256 != "string" || !/^[0-9a-f]{64}$/u.test(sha256) || sha256 != actualHash) {
|
|
887
|
+
invalidResource(`${capitalize(kind)} '${path}' SHA-256 does not match its exact content.`);
|
|
888
|
+
}
|
|
889
|
+
const fullPath = `${projectRoot}/app/src/main/${kind == "resource" ? "res" : "assets"}/${path}`;
|
|
890
|
+
files.push({ content: snapshot, contentKind: typeof snapshot == "string" ? "text" : "binary", fullPath, mediaType, path, sha256 });
|
|
891
|
+
}
|
|
892
|
+
files.sort((left, right) => left.fullPath < right.fullPath ? -1 : left.fullPath > right.fullPath ? 1 : 0);
|
|
893
|
+
const conflict = findPortableArtifactPathConflict(files.map(({ fullPath }) => fullPath));
|
|
894
|
+
if (conflict)
|
|
895
|
+
invalidResource(`${capitalize(kind)} path '${conflict.path}' has a ${conflict.kind} conflict.`);
|
|
896
|
+
if (kind == "resource")
|
|
897
|
+
validateResourceIdentifiers(files);
|
|
898
|
+
return files;
|
|
899
|
+
}
|
|
900
|
+
/**
|
|
901
|
+
* Rejects two files that Android would compile to the same resource identifier in one configuration.
|
|
902
|
+
* @param {AndroidInputFile[]} files - Validated resource inputs.
|
|
903
|
+
*/
|
|
904
|
+
function validateResourceIdentifiers(files) {
|
|
905
|
+
const identifiers = new Map();
|
|
906
|
+
for (const file of files) {
|
|
907
|
+
const separator = file.path.indexOf("/");
|
|
908
|
+
const directory = file.path.slice(0, separator);
|
|
909
|
+
const filename = file.path.slice(separator + 1);
|
|
910
|
+
const extension = filename.indexOf(".");
|
|
911
|
+
const name = extension == -1 ? filename : filename.slice(0, extension);
|
|
912
|
+
const resourceType = directory.split("-", 1)[0];
|
|
913
|
+
const key = `${directory}/${name}`;
|
|
914
|
+
const previous = identifiers.get(key);
|
|
915
|
+
if (previous) {
|
|
916
|
+
invalidResource(`Android resource identifier '${resourceType}/${name}' collides between '${previous}' and '${file.path}' in configuration '${directory}'.`);
|
|
917
|
+
}
|
|
918
|
+
identifiers.set(key, file.path);
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
/**
|
|
922
|
+
* Tests whether a caller resource uses the narrow allowlisted resource grammar.
|
|
923
|
+
* @param {string} value - Resource path relative to res.
|
|
924
|
+
* @returns {boolean} Whether the path is supported.
|
|
925
|
+
*/
|
|
926
|
+
function isAllowedResourcePath(value) {
|
|
927
|
+
if (/^raw\/[a-z][a-z0-9_]*(?:\.[a-z0-9]+)?$/u.test(value))
|
|
928
|
+
return true;
|
|
929
|
+
return /^drawable-(?:mdpi|hdpi|xhdpi|xxhdpi|xxxhdpi)\/[a-z][a-z0-9_]*\.(?:png|webp)$/u.test(value);
|
|
930
|
+
}
|
|
931
|
+
/**
|
|
932
|
+
* Converts a validated input snapshot into an owned artifact.
|
|
933
|
+
* @param {AndroidInputFile} item - Validated caller input.
|
|
934
|
+
* @returns {import("../semantic/types.js").GeneratedSetArtifact} Owned input artifact.
|
|
935
|
+
*/
|
|
936
|
+
function inputFileArtifact(item) {
|
|
937
|
+
const reason = `Exact caller-provided Android input (sha256:${item.sha256}).`;
|
|
938
|
+
if (typeof item.content == "string") {
|
|
939
|
+
return {
|
|
940
|
+
...syntheticText(item.fullPath, "resource", item.content, reason),
|
|
941
|
+
mediaType: item.mediaType
|
|
942
|
+
};
|
|
943
|
+
}
|
|
944
|
+
const content = new Uint8Array(item.content);
|
|
945
|
+
return {
|
|
946
|
+
content,
|
|
947
|
+
contentKind: "binary",
|
|
948
|
+
mediaType: item.mediaType,
|
|
949
|
+
ownership: "generated",
|
|
950
|
+
path: item.fullPath,
|
|
951
|
+
provenance: { kind: "bytes", mapping: createByteMapping({
|
|
952
|
+
byteLength: content.byteLength,
|
|
953
|
+
path: item.fullPath,
|
|
954
|
+
ranges: [{ generated: { end: content.byteLength, start: 0 },
|
|
955
|
+
origin: { kind: "synthetic", reason, relatedOrigins: [] }, role: "resource" }]
|
|
956
|
+
}) },
|
|
957
|
+
role: "resource"
|
|
958
|
+
};
|
|
959
|
+
}
|
|
960
|
+
/**
|
|
961
|
+
* Creates a deterministic synthetic text artifact.
|
|
962
|
+
* @param {string} path - Artifact path.
|
|
963
|
+
* @param {import("../semantic/types.js").GeneratedArtifactRole} role - Artifact role.
|
|
964
|
+
* @param {string} content - UTF-8 artifact content.
|
|
965
|
+
* @param {string} reason - Synthetic provenance reason.
|
|
966
|
+
* @returns {import("../semantic/types.js").GeneratedSetArtifact} Text artifact.
|
|
967
|
+
*/
|
|
968
|
+
function syntheticText(path, role, content, reason) {
|
|
969
|
+
return { content, contentKind: "text", mediaType: mediaTypeFor(path), ownership: "generated", path,
|
|
970
|
+
provenance: { kind: "synthetic", reason, relatedOrigins: [] }, role };
|
|
971
|
+
}
|
|
972
|
+
/**
|
|
973
|
+
* Creates a configuration-derived text artifact.
|
|
974
|
+
* @param {string} path - Artifact path.
|
|
975
|
+
* @param {import("../semantic/types.js").GeneratedArtifactRole} role - Artifact role.
|
|
976
|
+
* @param {string} content - UTF-8 artifact content.
|
|
977
|
+
* @param {string} reason - Synthetic provenance reason.
|
|
978
|
+
* @returns {import("../semantic/types.js").GeneratedSetArtifact} Text artifact.
|
|
979
|
+
*/
|
|
980
|
+
function configuredText(path, role, content, reason) {
|
|
981
|
+
return syntheticText(path, role, content, reason);
|
|
982
|
+
}
|
|
983
|
+
/**
|
|
984
|
+
* Derives the media type for a generated text path.
|
|
985
|
+
* @param {string} path - Generated artifact path.
|
|
986
|
+
* @returns {string} Artifact media type.
|
|
987
|
+
*/
|
|
988
|
+
function mediaTypeFor(path) {
|
|
989
|
+
if (path.endsWith(".json"))
|
|
990
|
+
return "application/json";
|
|
991
|
+
if (path.endsWith(".xml"))
|
|
992
|
+
return "application/xml";
|
|
993
|
+
if (path.endsWith(".kt") || path.endsWith(".kts"))
|
|
994
|
+
return "text/x-kotlin";
|
|
995
|
+
return "text/plain";
|
|
996
|
+
}
|
|
997
|
+
/**
|
|
998
|
+
* Marks one rendered value as configuration-derived.
|
|
999
|
+
* @param {string} fieldName - Configuration field name.
|
|
1000
|
+
* @param {string} value - Rendered field value.
|
|
1001
|
+
* @returns {{field: string, value: string}} Marked configuration value.
|
|
1002
|
+
*/
|
|
1003
|
+
function field(fieldName, value) {
|
|
1004
|
+
return { field: fieldName, value };
|
|
1005
|
+
}
|
|
1006
|
+
/**
|
|
1007
|
+
* Joins rendered parts while recording configuration causality.
|
|
1008
|
+
* @param {string} path - Output artifact path.
|
|
1009
|
+
* @param {ConfigurationSpan[]} spans - Mutable provenance span collector.
|
|
1010
|
+
* @param {(string | {field: string, value: string})[]} parts - Synthetic and marked parts.
|
|
1011
|
+
* @returns {string} Rendered text.
|
|
1012
|
+
*/
|
|
1013
|
+
function configured(path, spans, parts) {
|
|
1014
|
+
let content = "";
|
|
1015
|
+
for (const part of parts) {
|
|
1016
|
+
if (typeof part == "string")
|
|
1017
|
+
content += part;
|
|
1018
|
+
else {
|
|
1019
|
+
const start = content.length;
|
|
1020
|
+
content += part.value;
|
|
1021
|
+
spans.push({ end: content.length, field: part.field, path, start });
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
return content;
|
|
1025
|
+
}
|
|
1026
|
+
/**
|
|
1027
|
+
* Groups configuration spans by artifact.
|
|
1028
|
+
* @param {ConfigurationSpan[]} spans - All rendered configuration spans.
|
|
1029
|
+
* @param {string} fieldName - Requested configuration field.
|
|
1030
|
+
* @returns {{path: string, ranges: {end: number, start: number}[]}[]} Output ranges.
|
|
1031
|
+
*/
|
|
1032
|
+
function configurationOutputs(spans, fieldName) {
|
|
1033
|
+
const grouped = new Map();
|
|
1034
|
+
for (const span of spans) {
|
|
1035
|
+
if (span.field != fieldName)
|
|
1036
|
+
continue;
|
|
1037
|
+
const ranges = grouped.get(span.path) ?? [];
|
|
1038
|
+
ranges.push({ end: span.end, start: span.start });
|
|
1039
|
+
grouped.set(span.path, ranges);
|
|
1040
|
+
}
|
|
1041
|
+
return [...grouped].map(([path, ranges]) => ({ path, ranges }));
|
|
1042
|
+
}
|
|
1043
|
+
/**
|
|
1044
|
+
* Tests whether a mapping origin refers to one semantic source file.
|
|
1045
|
+
* @param {import("../semantic/types.js").SemanticOrigin} origin - Mapping origin.
|
|
1046
|
+
* @param {string} filename - Semantic source filename.
|
|
1047
|
+
* @returns {boolean} Whether the origin owns the filename.
|
|
1048
|
+
*/
|
|
1049
|
+
function originReferencesFilename(origin, filename) {
|
|
1050
|
+
if (origin.kind == "source")
|
|
1051
|
+
return origin.location.filename == filename;
|
|
1052
|
+
if (origin.kind == "derived")
|
|
1053
|
+
return origin.origins.some(({ location }) => location.filename == filename);
|
|
1054
|
+
return origin.relatedOrigins.some(({ location }) => location.filename == filename);
|
|
1055
|
+
}
|
|
1056
|
+
/**
|
|
1057
|
+
* Hashes exact text or bytes for ownership records.
|
|
1058
|
+
* @param {string | Uint8Array} content - Exact artifact content.
|
|
1059
|
+
* @returns {string} Lowercase SHA-256 digest.
|
|
1060
|
+
*/
|
|
1061
|
+
function hashContent(content) {
|
|
1062
|
+
return createHash("sha256").update(content).digest("hex");
|
|
1063
|
+
}
|
|
1064
|
+
/**
|
|
1065
|
+
* Serializes JSON with recursively stable object-key ordering.
|
|
1066
|
+
* @param {unknown} value - JSON-compatible value.
|
|
1067
|
+
* @returns {string} Canonical indented JSON with trailing newline.
|
|
1068
|
+
*/
|
|
1069
|
+
function stringifyCanonicalJson(value) {
|
|
1070
|
+
return `${JSON.stringify(sortJson(value), null, 2)}\n`;
|
|
1071
|
+
}
|
|
1072
|
+
/**
|
|
1073
|
+
* Recursively sorts object keys without changing array order.
|
|
1074
|
+
* @param {unknown} value - JSON-compatible value.
|
|
1075
|
+
* @returns {unknown} Canonically ordered value.
|
|
1076
|
+
*/
|
|
1077
|
+
function sortJson(value) {
|
|
1078
|
+
if (Array.isArray(value))
|
|
1079
|
+
return value.map(sortJson);
|
|
1080
|
+
if (!isPlainObject(value))
|
|
1081
|
+
return value;
|
|
1082
|
+
return Object.fromEntries(Object.keys(value).sort().map(key => [key, sortJson(value[key])]));
|
|
1083
|
+
}
|
|
1084
|
+
/**
|
|
1085
|
+
* Tests a lowercase Kotlin-compatible package identity.
|
|
1086
|
+
* @param {string} value - Candidate package name.
|
|
1087
|
+
* @returns {boolean} Whether the name is accepted.
|
|
1088
|
+
*/
|
|
1089
|
+
function isPackageName(value) {
|
|
1090
|
+
const parts = value.split(".");
|
|
1091
|
+
return parts.length >= 2 && parts.every(part => /^[a-z][a-z0-9_]*$/u.test(part) && !kotlinKeywords.has(part));
|
|
1092
|
+
}
|
|
1093
|
+
/**
|
|
1094
|
+
* Rejects launcher names that shadow an unqualified symbol in generated same-package Kotlin.
|
|
1095
|
+
* @param {string} activityClassName - Validated launcher class name.
|
|
1096
|
+
* @param {string} entryModule - Selected semantic entry module.
|
|
1097
|
+
*/
|
|
1098
|
+
function validateActivityClassName(activityClassName, entryModule) {
|
|
1099
|
+
const semanticEntrySymbol = `SemantifoldModule${moduleName(entryModule)}`;
|
|
1100
|
+
if (androidScaffoldSymbols.has(activityClassName) || activityClassName == semanticEntrySymbol) {
|
|
1101
|
+
invalidConfiguration(`Activity class name '${activityClassName}' collides with generated Kotlin symbol '${activityClassName}'.`);
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
/**
|
|
1105
|
+
* Allocates one module-wide output sink that cannot shadow a source binding.
|
|
1106
|
+
* @param {import("../semantic/types.js").SemanticModule} module - Prepared semantic module.
|
|
1107
|
+
* @returns {string} Collision-safe Kotlin binding.
|
|
1108
|
+
*/
|
|
1109
|
+
function outputSinkName(module) {
|
|
1110
|
+
const bindings = new Set(module.functions.flatMap(declaration => declaration.parameters.map(({ name }) => name)));
|
|
1111
|
+
for (const declaration of module.functions)
|
|
1112
|
+
collectBlockBindingNames(declaration.body, bindings);
|
|
1113
|
+
collectBlockBindingNames(module.entryPoint.body, bindings);
|
|
1114
|
+
let candidate = "semantifoldOutput";
|
|
1115
|
+
while (bindings.has(candidate))
|
|
1116
|
+
candidate += "_";
|
|
1117
|
+
return candidate;
|
|
1118
|
+
}
|
|
1119
|
+
/**
|
|
1120
|
+
* Collects local bindings recursively from the supported semantic block subset.
|
|
1121
|
+
* @param {import("../semantic/types.js").Block} block - Semantic block.
|
|
1122
|
+
* @param {Set<string>} bindings - Mutable binding-name set.
|
|
1123
|
+
*/
|
|
1124
|
+
function collectBlockBindingNames(block, bindings) {
|
|
1125
|
+
for (const statement of block.statements) {
|
|
1126
|
+
if (statement.kind == "LocalDeclaration")
|
|
1127
|
+
bindings.add(statement.name);
|
|
1128
|
+
else if (statement.kind == "IfStatement") {
|
|
1129
|
+
collectBlockBindingNames(statement.consequent, bindings);
|
|
1130
|
+
if (statement.alternate)
|
|
1131
|
+
collectBlockBindingNames(statement.alternate, bindings);
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
/**
|
|
1136
|
+
* Maps a preflighted module identity to a stable Kotlin suffix.
|
|
1137
|
+
* @param {string} id - Semantic module identity.
|
|
1138
|
+
* @returns {string} Kotlin object-name suffix.
|
|
1139
|
+
*/
|
|
1140
|
+
function moduleName(id) {
|
|
1141
|
+
return id.split(/[._-]+/u).map(part => `${part[0].toUpperCase()}${part.slice(1)}`).join("");
|
|
1142
|
+
}
|
|
1143
|
+
/**
|
|
1144
|
+
* Wraps legacy single-module input in the canonical program contract.
|
|
1145
|
+
* @param {import("../semantic/types.js").SemanticModule | undefined} candidate - Candidate semantic module.
|
|
1146
|
+
* @returns {import("../semantic/types.js").SemanticProgram} Canonical single-module program.
|
|
1147
|
+
*/
|
|
1148
|
+
function normalizeSingleModule(candidate) {
|
|
1149
|
+
if (!isPlainObject(candidate))
|
|
1150
|
+
unsupportedCapability("android", "missing or invalid module", undefined);
|
|
1151
|
+
const module = structuredClone(candidate);
|
|
1152
|
+
const sources = module.provenance?.sources;
|
|
1153
|
+
if (!Array.isArray(sources) || sources.length != 1 || sources[0].content === null ||
|
|
1154
|
+
sources[0].filename != module.location?.filename || typeof sources[0].language != "string") {
|
|
1155
|
+
unsupportedCapability("android", "single-module source ownership", module.location);
|
|
1156
|
+
}
|
|
1157
|
+
rekeyModuleDeclarations(module, "main");
|
|
1158
|
+
const exports = [
|
|
1159
|
+
...(module.records ?? []).map(declaration => applicationExport(declaration, "record")),
|
|
1160
|
+
...(module.classes ?? []).map(declaration => applicationExport(declaration, "class")),
|
|
1161
|
+
...(module.errors ?? []).map(declaration => applicationExport(declaration, "error")),
|
|
1162
|
+
...module.functions.map(declaration => applicationExport(declaration, "function"))
|
|
1163
|
+
];
|
|
1164
|
+
const programModule = { ...module, exports, id: "main", imports: [], sourceFilename: sources[0].filename };
|
|
1165
|
+
return { entryModule: "main", kind: "Program", modules: [programModule], sources: structuredClone(sources) };
|
|
1166
|
+
}
|
|
1167
|
+
/**
|
|
1168
|
+
* Creates an application-local export record for single-module compatibility.
|
|
1169
|
+
* @param {{id?: string, location: import("../semantic/types.js").SourceLocation, name: string}} declaration - Semantic declaration.
|
|
1170
|
+
* @param {import("../semantic/types.js").SemanticDeclarationKind} symbolKind - Declaration kind.
|
|
1171
|
+
* @returns {import("../semantic/types.js").SemanticExport} Semantic export.
|
|
1172
|
+
*/
|
|
1173
|
+
function applicationExport(declaration, symbolKind) {
|
|
1174
|
+
return { declarationId: /** @type {string} */ (declaration.id), exportedName: declaration.name,
|
|
1175
|
+
kind: /** @type {const} */ ("Export"), location: declaration.location, symbolKind };
|
|
1176
|
+
}
|
|
1177
|
+
/**
|
|
1178
|
+
* Qualifies single-module declaration identities before program preflight.
|
|
1179
|
+
* @param {import("../semantic/types.js").SemanticModule} module - Cloned semantic module.
|
|
1180
|
+
* @param {string} moduleId - Canonical module identity.
|
|
1181
|
+
*/
|
|
1182
|
+
function rekeyModuleDeclarations(module, moduleId) {
|
|
1183
|
+
const declarations = [...module.records ?? [], ...module.classes ?? [], ...module.errors ?? [], ...module.functions];
|
|
1184
|
+
const replacements = new Map(declarations.filter(declaration => typeof declaration.id == "string")
|
|
1185
|
+
.map(declaration => [declaration.id, `${moduleId}#${declaration.id}`]));
|
|
1186
|
+
for (const declaration of declarations)
|
|
1187
|
+
if (typeof declaration.id == "string")
|
|
1188
|
+
declaration.id = replacements.get(declaration.id);
|
|
1189
|
+
const seen = new Set();
|
|
1190
|
+
const identityFields = new Set(["classId", "declarationId", "field", "method", "parameterId"]);
|
|
1191
|
+
/**
|
|
1192
|
+
* Rewrites declaration references throughout one semantic value graph.
|
|
1193
|
+
* @param {unknown} value - Candidate nested semantic value.
|
|
1194
|
+
*/
|
|
1195
|
+
const visit = value => {
|
|
1196
|
+
if (!value || typeof value != "object" || seen.has(value))
|
|
1197
|
+
return;
|
|
1198
|
+
seen.add(value);
|
|
1199
|
+
for (const [key, nested] of Object.entries(value)) {
|
|
1200
|
+
if (!Array.isArray(value) && identityFields.has(key) && typeof nested == "string" && replacements.has(nested)) {
|
|
1201
|
+
Reflect.set(value, key, replacements.get(nested));
|
|
1202
|
+
}
|
|
1203
|
+
else
|
|
1204
|
+
visit(nested);
|
|
1205
|
+
}
|
|
1206
|
+
};
|
|
1207
|
+
visit(module);
|
|
1208
|
+
}
|
|
1209
|
+
/**
|
|
1210
|
+
* Escapes a configuration value for XML text or attributes.
|
|
1211
|
+
* @param {string} value - Raw configuration value.
|
|
1212
|
+
* @returns {string} XML-safe value.
|
|
1213
|
+
*/
|
|
1214
|
+
function xmlEscape(value) {
|
|
1215
|
+
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">")
|
|
1216
|
+
.replaceAll('"', """).replaceAll("'", "'");
|
|
1217
|
+
}
|
|
1218
|
+
/**
|
|
1219
|
+
* Escapes a configuration value for a Kotlin string literal body.
|
|
1220
|
+
* @param {string} value - Raw configuration value.
|
|
1221
|
+
* @returns {string} Kotlin-safe value.
|
|
1222
|
+
*/
|
|
1223
|
+
function kotlinEscape(value) {
|
|
1224
|
+
return value.replaceAll("\\", "\\\\").replaceAll('"', '\\"').replaceAll("$", "\\$");
|
|
1225
|
+
}
|
|
1226
|
+
/**
|
|
1227
|
+
* Uppercases the initial ASCII character of a known non-empty word.
|
|
1228
|
+
* @param {string} value - Non-empty word.
|
|
1229
|
+
* @returns {string} Capitalized word.
|
|
1230
|
+
*/
|
|
1231
|
+
function capitalize(value) {
|
|
1232
|
+
return `${value[0].toUpperCase()}${value.slice(1)}`;
|
|
1233
|
+
}
|
|
1234
|
+
/**
|
|
1235
|
+
* Tests whether a value is a plain data object.
|
|
1236
|
+
* @param {unknown} value - Candidate value.
|
|
1237
|
+
* @returns {value is Record<string, unknown>} Whether the value is a plain object.
|
|
1238
|
+
*/
|
|
1239
|
+
function isPlainObject(value) {
|
|
1240
|
+
return Boolean(value) && typeof value == "object" && !Array.isArray(value) && Object.getPrototypeOf(value) == Object.prototype;
|
|
1241
|
+
}
|
|
1242
|
+
/**
|
|
1243
|
+
* Throws a stable invalid-request diagnostic.
|
|
1244
|
+
* @param {string} message - Diagnostic message.
|
|
1245
|
+
* @returns {never} Never returns.
|
|
1246
|
+
*/
|
|
1247
|
+
function invalidInput(message) {
|
|
1248
|
+
throw new SemantifoldDiagnostic({ code: "INVALID_APPLICATION_INPUT", language: "android", message });
|
|
1249
|
+
}
|
|
1250
|
+
/**
|
|
1251
|
+
* Throws a stable invalid-configuration diagnostic.
|
|
1252
|
+
* @param {string} message - Diagnostic message.
|
|
1253
|
+
* @returns {never} Never returns.
|
|
1254
|
+
*/
|
|
1255
|
+
function invalidConfiguration(message) {
|
|
1256
|
+
throw new SemantifoldDiagnostic({ code: "INVALID_APPLICATION_CONFIGURATION", language: "android", message });
|
|
1257
|
+
}
|
|
1258
|
+
/**
|
|
1259
|
+
* Throws a stable invalid-resource diagnostic.
|
|
1260
|
+
* @param {string} message - Diagnostic message.
|
|
1261
|
+
* @returns {never} Never returns.
|
|
1262
|
+
*/
|
|
1263
|
+
function invalidResource(message) {
|
|
1264
|
+
throw new SemantifoldDiagnostic({ code: "INVALID_APPLICATION_RESOURCE", language: "android", message });
|
|
1265
|
+
}
|
|
1266
|
+
/**
|
|
1267
|
+
* Throws a stable invalid-path diagnostic.
|
|
1268
|
+
* @param {string} message - Diagnostic message.
|
|
1269
|
+
* @returns {never} Never returns.
|
|
1270
|
+
*/
|
|
1271
|
+
function invalidPath(message) {
|
|
1272
|
+
throw new SemantifoldDiagnostic({ code: "INVALID_APPLICATION_PATH", language: "android", message });
|
|
1273
|
+
}
|
|
1274
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYW5kcm9pZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9iYWNrZW5kcy9hbmRyb2lkLmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLFlBQVk7QUFFWixPQUFPLEVBQUMsVUFBVSxFQUFDLE1BQU0sYUFBYSxDQUFBO0FBQ3RDLE9BQU8sRUFBQyxZQUFZLEVBQUMsTUFBTSxhQUFhLENBQUE7QUFDeEMsT0FBTyxFQUFDLGdDQUFnQyxFQUFFLGtCQUFrQixFQUFDLE1BQU0scUJBQXFCLENBQUE7QUFDeEYsT0FBTyxFQUFDLGlCQUFpQixFQUFDLE1BQU0sc0JBQXNCLENBQUE7QUFDdEQsT0FBTyxFQUFDLHFCQUFxQixFQUFFLHFCQUFxQixFQUFDLE1BQU0sa0JBQWtCLENBQUE7QUFDN0UsT0FBTyxFQUFDLGVBQWUsRUFBRSxhQUFhLEVBQUMsTUFBTSxlQUFlLENBQUE7QUFDNUQsT0FBTyxFQUFDLHFCQUFxQixFQUFDLE1BQU0sd0JBQXdCLENBQUE7QUFDNUQsT0FBTyxFQUFDLHdCQUF3QixFQUFDLE1BQU0sY0FBYyxDQUFBO0FBQ3JELE9BQU8sRUFBQyxjQUFjLEVBQUMsTUFBTSxjQUFjLENBQUE7QUFDM0MsT0FBTyxFQUFDLGFBQWEsRUFBQyxNQUFNLHFCQUFxQixDQUFBO0FBQ2pELE9BQU8sRUFBQyxlQUFlLEVBQUMsTUFBTSxhQUFhLENBQUE7QUFDM0MsT0FBTyxFQUFDLFlBQVksRUFBQyxNQUFNLGFBQWEsQ0FBQTtBQUV4QyxNQUFNLFdBQVcsR0FBRyx1QkFBdUIsQ0FBQTtBQUMzQyxNQUFNLGdCQUFnQixHQUFHLE9BQU8sQ0FBQTtBQUNoQyw2RUFBNkU7QUFDN0UsTUFBTSxzQkFBc0IsR0FBRyxJQUFJLEdBQUcsQ0FBQztJQUNyQyxLQUFLLEVBQUUsTUFBTSxFQUFFLFlBQVksRUFBRSxZQUFZLEVBQUUsTUFBTSxFQUFFLFFBQVEsRUFBRSxRQUFRLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBRSxHQUFHLEVBQUUsS0FB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SUFDMUIsT0FBTyxPQUFPLENBQUMsS0FBSyxDQUFDLElBQUksT0FBTyxLQUFLLElBQUksUUFBUSxJQUFJLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsSUFBSSxNQUFNLENBQUMsY0FBYyxDQUFDLEtBQUssQ0FBQyxJQUFJLE1BQU0sQ0FBQyxTQUFTLENBQUE7QUFDaEksQ0FBQztBQUVEOzs7O0dBSUc7QUFDSCxTQUFTLFlBQVksQ0FBQyxPQUFPO0lBQzNCLE1BQU0sSUFBSSxxQkFBcUIsQ0FBQyxFQUFDLElBQUksRUFBRSwyQkFBMkIsRUFBRSxRQUFRLEVBQUUsU0FBUyxFQUFFLE9BQU8sRUFBQyxDQUFDLENBQUE7QUFDcEcsQ0FBQztBQUVEOzs7O0dBSUc7QUFDSCxTQUFTLG9CQUFvQixDQUFDLE9BQU87SUFDbkMsTUFBTSxJQUFJLHFCQUFxQixDQUFDLEVBQUMsSUFBSSxFQUFFLG1DQUFtQyxFQUFFLFFBQVEsRUFBRSxTQUFTLEVBQUUsT0FBTyxFQUFDLENBQUMsQ0FBQTtBQUM1RyxDQUFDO0FBRUQ7Ozs7R0FJRztBQUNILFNBQVMsZUFBZSxDQUFDLE9BQU87SUFDOUIsTUFBTSxJQUFJLHFCQUFxQixDQUFDLEVBQUMsSUFBSSxFQUFFLDhCQUE4QixFQUFFLFFBQVEsRUFBRSxTQUFTLEVBQUUsT0FBTyxFQUFDLENBQUMsQ0FBQTtBQUN2RyxDQUFDO0FBRUQ7Ozs7R0FJRztBQUNILFNBQVMsV0FBVyxDQUFDLE9BQU87SUFDMUIsTUFBTSxJQUFJLHFCQUFxQixDQUFDLEVBQUMsSUFBSSxFQUFFLDBCQUEwQixFQUFFLFFBQVEsRUFBRSxTQUFTLEVBQUUsT0FBTyxFQUFDLENBQUMsQ0FBQTtBQUNuRyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQHRzLWNoZWNrXG5cbmltcG9ydCB7Y3JlYXRlSGFzaH0gZnJvbSBcIm5vZGU6Y3J5cHRvXCJcbmltcG9ydCB7aXNEZW5zZUFycmF5fSBmcm9tIFwiLi4vYXJyYXkuanNcIlxuaW1wb3J0IHtmaW5kUG9ydGFibGVBcnRpZmFjdFBhdGhDb25mbGljdCwgaXNTYWZlQXJ0aWZhY3RQYXRofSBmcm9tIFwiLi4vYXJ0aWZhY3QtcGF0aC5qc1wiXG5pbXBvcnQge2NyZWF0ZUJ5dGVNYXBwaW5nfSBmcm9tIFwiLi4vYmluYXJ5LW1hcHBpbmcuanNcIlxuaW1wb3J0IHtTZW1hbnRpZm9sZERpYWdub3N0aWMsIHVuc3VwcG9ydGVkQ2FwYWJpbGl0eX0gZnJvbSBcIi4uL2RpYWdub3N0aWMuanNcIlxuaW1wb3J0IHtmaW5hbGl6ZU1hcHBpbmcsIHRvU291cmNlTWFwVjN9IGZyb20gXCIuLi9tYXBwaW5nLmpzXCJcbmltcG9ydCB7aGFzT25seVVuaWNvZGVTY2FsYXJzfSBmcm9tIFwiLi4vc2VtYW50aWMvc2NhbGFycy5qc1wiXG5pbXBvcnQge3ByZWZsaWdodFNlbWFudGljUHJvZ3JhbX0gZnJvbSBcIi4vcHJvZ3JhbS5qc1wiXG5pbXBvcnQge2VtaXRTY2FsYXJUeXBlfSBmcm9tIFwiLi9zY2FsYXJzLmpzXCJcbmltcG9ydCB7a290bGluUnVudGltZX0gZnJvbSBcIi4va290bGluLXJ1bnRpbWUuanNcIlxuaW1wb3J0IHtlbWl0S290bGluQmxvY2t9IGZyb20gXCIuL2tvdGxpbi5qc1wiXG5pbXBvcnQge1NvdXJjZVdyaXRlcn0gZnJvbSBcIi4vd3JpdGVyLmpzXCJcblxuY29uc3QgcHJvamVjdFJvb3QgPSBcImdlbmVyYXRlZC9hbmRyb2lkLWFwcFwiXG5jb25zdCBnZW5lcmF0b3JWZXJzaW9uID0gXCIwLjQuMFwiXG4vKiogQHR5cGUge1JlYWRvbmx5PFNldDxpbXBvcnQoXCIuLi9zZW1hbnRpYy90eXBlcy5qc1wiKS5TZW1hbnRpY0xhbmd1YWdlPj59ICovXG5jb25zdCBhbmRyb2lkU291cmNlTGFuZ3VhZ2VzID0gbmV3IFNldChbXG4gIFwicGhwXCIsIFwicnVieVwiLCBcImphdmFzY3JpcHRcIiwgXCJ0eXBlc2NyaXB0XCIsIFwiamF2YVwiLCBcImtvdGxpblwiLCBcInB5dGhvblwiLCBcImNzaGFycFwiLCBcImdvXCIsIFwiY1wiLCBcImNwcFwiLCBcInJ1c3RcIiwgXCJzd2lmdFwiLCBcImRhcnRcIlxuXSlcbmNvbnN0IHJlcXVlc3RGaWVsZHMgPSBuZXcgU2V0KFtcImFzc2V0c1wiLCBcImNvbmZpZ3VyYXRpb25cIiwgXCJsYW5ndWFnZVwiLCBcIm1vZHVsZVwiLCBcInByb2dyYW1cIiwgXCJyZXNvdXJjZXNcIiwgXCJyb2xlXCJdKVxuY29uc3QgY29uZmlndXJhdGlvbkZpZWxkcyA9IG5ldyBTZXQoW1xuICBcImFjdGl2aXR5Q2xhc3NOYW1lXCIsIFwiYXBwbGljYXRpb25JZFwiLCBcImJ1aWxkVG9vbHNWZXJzaW9uXCIsIFwiY29tcGlsZVNka1wiLCBcImRpc3BsYXlOYW1lXCIsIFwibWluaW11bVNka1wiLCBcIm5hbWVzcGFjZVwiLFxuICBcIm9yaWVudGF0aW9uXCIsIFwicGFja2FnZU5hbWVcIiwgXCJwZXJtaXNzaW9uc1wiLCBcInByb2R1Y3ROYW1lXCIsIFwidGFyZ2V0U2RrXCIsIFwidGhlbWVcIiwgXCJ2ZXJzaW9uQ29kZVwiLCBcInZlcnNpb25OYW1lXCJcbl0pXG5jb25zdCByZXNvdXJjZUZpZWxkcyA9IG5ldyBTZXQoW1wiY29udGVudFwiLCBcIm1lZGlhVHlwZVwiLCBcInBhdGhcIiwgXCJzaGEyNTZcIl0pXG5jb25zdCBrb3RsaW5LZXl3b3JkcyA9IG5ldyBTZXQoW1xuICBcImFzXCIsIFwiYnJlYWtcIiwgXCJjbGFzc1wiLCBcImNvbnRpbnVlXCIsIFwiZG9cIiwgXCJlbHNlXCIsIFwiZmFsc2VcIiwgXCJmb3JcIiwgXCJmdW5cIiwgXCJpZlwiLCBcImluXCIsIFwiaW50ZXJmYWNlXCIsIFwiaXNcIiwgXCJudWxsXCIsXG4gIFwib2JqZWN0XCIsIFwicGFja2FnZVwiLCBcInJldHVyblwiLCBcInN1cGVyXCIsIFwidGhpc1wiLCBcInRocm93XCIsIFwidHJ1ZVwiLCBcInRyeVwiLCBcInR5cGVhbGlhc1wiLCBcInR5cGVvZlwiLCBcInZhbFwiLCBcInZhclwiLCBcIndoZW5cIiwgXCJ3aGlsZVwiXG5dKVxuY29uc3QgYW5kcm9pZFNjYWZmb2xkU3ltYm9scyA9IG5ldyBTZXQoW1xuICBcIkFjdGl2aXR5XCIsIFwiQmFzZTY0XCIsIFwiQnVuZGxlXCIsIFwiSWxsZWdhbFN0YXRlRXhjZXB0aW9uXCIsIFwiSW5zdHJ1bWVudGF0aW9uXCIsIFwiSW50ZW50XCIsIFwiUlwiLCBcIlNlbWFudGlmb2xkRW50cnlUZXN0XCIsXG4gIFwiU2VtYW50aWZvbGRVaUluc3RydW1lbnRhdGlvblwiLCBcIlN0YW5kYXJkQ2hhcnNldHNcIiwgXCJTdHJpbmdcIiwgXCJTdXBwcmVzc0xpbnRcIiwgXCJUZXN0XCIsIFwiVGV4dFZpZXdcIiwgXCJUaHJvd2FibGVcIiwgXCJWaWV3XCJcbl0pXG5jb25zdCBtZWRpYVR5cGVQYXR0ZXJuID0gL15bQS1aYS16MC05ISMkJl5fListXStcXC9bQS1aYS16MC05ISMkJl5fListXSsoPzo7W1xcdTAwMjAtXFx1MDA3ZV0rKT8kL3VcbmNvbnN0IHNlbWFudGljVmVyc2lvblBhdHRlcm4gPSAvXig/OjB8WzEtOV1bMC05XSopXFwuKD86MHxbMS05XVswLTldKilcXC4oPzowfFsxLTldWzAtOV0qKSg/Oi0oPzowfFsxLTldWzAtOV0qfFtBLVphLXotXVtBLVphLXowLTktXSopKD86XFwuKD86MHxbMS05XVswLTldKnxbQS1aYS16LV1bQS1aYS16MC05LV0qKSkqKT8oPzpcXCtbQS1aYS16MC05LV0rKD86XFwuW0EtWmEtejAtOS1dKykqKT8kL3VcbmNvbnN0IHRvb2xjaGFpbiA9IE9iamVjdC5mcmVlemUoe1xuICBhbmRyb2lkR3JhZGxlUGx1Z2luOiBcIjguMTEuMVwiLFxuICBhcmNoaXZlczoge1xuICAgIGJ1aWxkVG9vbHM6IHtcbiAgICAgIHJldmlzaW9uOiBcIjM1LjAuMFwiLFxuICAgICAgc2hhMjU2OiBcImJkM2E0OTY2OTEyZWI4YjMwZWQwZDAwYjBjZGE2YjY1NDNiOTQ5ZDVmZmUwMGJlYTU0YzA0YzgxZTE1NjFkODhcIixcbiAgICAgIHVybDogXCJodHRwczovL2RsLmdvb2dsZS5jb20vYW5kcm9pZC9yZXBvc2l0b3J5L2J1aWxkLXRvb2xzX3IzNV9saW51eC56aXBcIlxuICAgIH0sXG4gICAgY29tbWFuZExpbmVUb29sczoge1xuICAgICAgcmV2aXNpb246IFwiMTEwNzY3MDhcIixcbiAgICAgIHNoYTI1NjogXCIyZDJkNTA4NTdlNGViNTUzYWY1YTZkYzNhZDUwN2ExN2FkZjQzZDExNTI2NGIxYWZjMTE2Zjk1YzkyZTVlMjU4XCIsXG4gICAgICB1cmw6IFwiaHR0cHM6Ly9kbC5nb29nbGUuY29tL2FuZHJvaWQvcmVwb3NpdG9yeS9jb21tYW5kbGluZXRvb2xzLWxpbnV4LTExMDc2NzA4X2xhdGVzdC56aXBcIlxuICAgIH0sXG4gICAgZW11bGF0b3I6IHtcbiAgICAgIGJ1aWxkSWQ6IFwiMTM2MTA0MTJcIixcbiAgICAgIHJldmlzaW9uOiBcIjM1LjYuMTFcIixcbiAgICAgIHNoYTI1NjogXCIyZmUyYjU2ZmU5M2NlNzVlMWQ0NzhhNDAxNjIxMzEzODFkOTExYzM1NWVmZWFlZGI1NGRkMWUwZDA4OTdhNWNmXCIsXG4gICAgICB1cmw6IFwiaHR0cHM6Ly9lZGdlZGwubWUuZ3Z0MS5jb20vZWRnZWRsL2FuZHJvaWQvcmVwb3NpdG9yeS9lbXVsYXRvci1saW51eF94NjQtMTM2MTA0MTIuemlwXCJcbiAgICB9LFxuICAgIHBsYXRmb3JtOiB7XG4gICAgICByZXZpc2lvbjogXCIzNS1yMlwiLFxuICAgICAgc2hhMjU2OiBcIjA5ODhjYWNhZDAxYjM4YTE4YTQ3YmFjMTRhMDY5NWYyNDZiYzc2YzFiMDZjMGVlYjhlYjBkYzgyNWFiMGM4ZTBcIixcbiAgICAgIHVybDogXCJodHRwczovL2RsLmdvb2dsZS5jb20vYW5kcm9pZC9yZXBvc2l0b3J5L3BsYXRmb3JtLTM1X3IwMi56aXBcIlxuICAgIH0sXG4gICAgcGxhdGZvcm1Ub29sczoge1xuICAgICAgcmV2aXNpb246IFwiMzcuMC4xXCIsXG4gICAgICBzaGEyNTY6IFwiZDIzMGYxMzg0MmY2MGY3ODJhODY0NWY5YzgxM2Y4Zjg0NWJmMzYwODllYTcyODlmMjhjNDhmMTc5NzkzMTNmMVwiLFxuICAgICAgdXJsOiBcImh0dHBzOi8vZGwuZ29vZ2xlLmNvbS9hbmRyb2lkL3JlcG9zaXRvcnkvcGxhdGZvcm0tdG9vbHNfcjM3LjAuMS1saW51eC56aXBcIlxuICAgIH0sXG4gICAgc3lzdGVtSW1hZ2U6IHtcbiAgICAgIHJldmlzaW9uOiBcIjM1LWdvb2dsZV9hcGlzLXg4Nl82NC1yOVwiLFxuICAgICAgc2hhMjU2OiBcImM2N2I5YmEwZmY1YmMwZWI2ZDA0Njg3MWJmYTIyOGFmMTRkNGQ0N2IwMmYwY2RhZTk0ZjA0OGU1MTFiNzU2NmVcIixcbiAgICAgIHVybDogXCJodHRwczovL2RsLmdvb2dsZS5jb20vYW5kcm9pZC9yZXBvc2l0b3J5L3N5cy1pbWcvZ29vZ2xlX2FwaXMveDg2XzY0LTM1X3IwOS56aXBcIlxuICAgIH1cbiAgfSxcbiAgYnVpbGRUb29sczogXCIzNS4wLjBcIixcbiAgY29tbWFuZExpbmVUb29sczogXCIxMTA3NjcwOFwiLFxuICBjb21waWxlU2RrOiAzNSxcbiAgZW11bGF0b3I6IFwiMzUuNi4xMVwiLFxuICBncmFkbGU6IFwiOC4xM1wiLFxuICBqYXZhOiBcIjIxLjAuOFwiLFxuICBrb3RsaW46IFwiMi4yLjEwXCIsXG4gIGtvdGxpbkNvbXBpbGVyU2hhMjU2OiBcIjMwMmQxZDhlNjcxZTVjMzIwN2U2ZWQ2MmZmMTFmYjU1NTQ2MmE2MjhlMjJhMTE1ODI1NGRjYWFmN2U3Mzk0YmNcIixcbiAgc3lzdGVtSW1hZ2U6IFwic3lzdGVtLWltYWdlczthbmRyb2lkLTM1O2dvb2dsZV9hcGlzO3g4Nl82NFwiLFxuICB0YXJnZXRTZGs6IDM1XG59KVxuY29uc3QgZXhjbHVkZWRQYXRoUGF0dGVybnMgPSBPYmplY3QuZnJlZXplKFtcIi5ncmFkbGUvKipcIiwgXCIqKi9idWlsZC8qKlwiLCBcImxvY2FsLnByb3BlcnRpZXNcIl0pXG5jb25zdCBhbmRyb2lkS290bGluUnVudGltZSA9IGBAZmlsZTpTdXBwcmVzcyhcIlBMQVRGT1JNX0NMQVNTX01BUFBFRF9UT19LT1RMSU5cIilcblxucGFja2FnZSBfX1BBQ0tBR0VfXy5zZW1hbnRpY1xuXG4ke2tvdGxpblJ1bnRpbWUucmVwbGFjZUFsbChcInByaXZhdGUgXCIsIFwiaW50ZXJuYWwgXCIpfVxuaW50ZXJuYWwgY2xhc3MgU2VtYW50aWZvbGRPdXRwdXRTaW5rIHtcbiAgcHJpdmF0ZSB2YWwgdmFsdWVzOiBqYXZhLnV0aWwuQXJyYXlMaXN0PFN0cmluZz4gPSBqYXZhLnV0aWwuQXJyYXlMaXN0PFN0cmluZz4oKVxuXG4gIGZ1biByZXNldCgpIHtcbiAgICB2YWx1ZXMuY2xlYXIoKVxuICB9XG5cbiAgZnVuIHdyaXRlKHZhbHVlOiBMb25nKSB7XG4gICAgdmFsdWVzLmFkZCh2YWx1ZS50b1N0cmluZygpKVxuICB9XG5cbiAgZnVuIHdyaXRlKHZhbHVlOiBCb29sZWFuKSB7XG4gICAgdmFsdWVzLmFkZChpZiAodmFsdWUpIFwidHJ1ZVwiIGVsc2UgXCJmYWxzZVwiKVxuICB9XG5cbiAgZnVuIHdyaXRlKHZhbHVlOiBTdHJpbmcpIHtcbiAgICB2YWx1ZXMuYWRkKHZhbHVlKVxuICB9XG5cbiAgZnVuIHNuYXBzaG90KCk6IGphdmEudXRpbC5BcnJheUxpc3Q8U3RyaW5nPiA9IGphdmEudXRpbC5BcnJheUxpc3Q8U3RyaW5nPih2YWx1ZXMpXG59XG5gXG5cbi8qKiBAdHlwZWRlZiB7e2NvbnRlbnQ6IHN0cmluZyB8IFVpbnQ4QXJyYXksIGNvbnRlbnRLaW5kOiBcImJpbmFyeVwiIHwgXCJ0ZXh0XCIsIGZ1bGxQYXRoOiBzdHJpbmcsIG1lZGlhVHlwZTogc3RyaW5nLCBwYXRoOiBzdHJpbmcsIHNoYTI1Njogc3RyaW5nfX0gQW5kcm9pZElucHV0RmlsZSAqL1xuLyoqIEB0eXBlZGVmIHt7ZW5kOiBudW1iZXIsIGZpZWxkOiBzdHJpbmcsIHBhdGg6IHN0cmluZywgc3RhcnQ6IG51bWJlcn19IENvbmZpZ3VyYXRpb25TcGFuICovXG5cbi8qKlxuICogUHJlZmxpZ2h0cyBhIGNvbXBsZXRlIEFuZHJvaWQgYXBwbGljYXRpb24gcmVxdWVzdCBiZWZvcmUgYWxsb2NhdGluZyBhIHNvdXJjZSB3cml0ZXIuXG4gKiBAcGFyYW0ge1JlY29yZDxzdHJpbmcsIHVua25vd24+fSBpbnB1dCAtIENhbmRpZGF0ZSBhcHBsaWNhdGlvbiByZXF1ZXN0LlxuICogQHJldHVybnMge3thc3NldHM6IEFuZHJvaWRJbnB1dEZpbGVbXSwgY29uZmlndXJhdGlvbjogaW1wb3J0KFwiLi4vc2VtYW50aWMvdHlwZXMuanNcIikuQW5kcm9pZEFwcGxpY2F0aW9uQ29uZmlndXJhdGlvbiwgbW9kdWxlUGF0aHM6IE1hcDxzdHJpbmcsIHN0cmluZz4sIG1vZHVsZXM6IGltcG9ydChcIi4uL3NlbWFudGljL3R5cGVzLmpzXCIpLlNlbWFudGljTW9kdWxlW10sIHBhY2thZ2VQYXRoOiBzdHJpbmcsIHByb2dyYW06IGltcG9ydChcIi4uL3NlbWFudGljL3R5cGVzLmpzXCIpLlNlbWFudGljUHJvZ3JhbSwgcmVzb3VyY2VzOiBBbmRyb2lkSW5wdXRGaWxlW10sIHNvdXJjZXM6IHtjb250ZW50OiBzdHJpbmcsIGZpbGVuYW1lOiBzdHJpbmcsIGxhbmd1YWdlPzogaW1wb3J0KFwiLi4vc2VtYW50aWMvdHlwZXMuanNcIikuU2VtYW50aWNMYW5ndWFnZX1bXX19IFByZXBhcmVkIGFwcGxpY2F0aW9uLlxuICovXG5leHBvcnQgZnVuY3Rpb24gcHJlZmxpZ2h0QW5kcm9pZEFwcGxpY2F0aW9uKGlucHV0KSB7XG4gIGlmICghaXNQbGFpbk9iamVjdChpbnB1dCkgfHwgT2JqZWN0LmtleXMoaW5wdXQpLnNvbWUoa2V5ID0+ICFyZXF1ZXN0RmllbGRzLmhhcyhrZXkpKSB8fFxuICAgIEJvb2xlYW4oaW5wdXQubW9kdWxlKSA9PSBCb29sZWFuKGlucHV0LnByb2dyYW0pKSB7XG4gICAgaW52YWxpZElucHV0KFwiQW5kcm9pZCBhcHBsaWNhdGlvbiBnZW5lcmF0aW9uIHJlcXVpcmVzIGEgY2xvc2VkIHJlcXVlc3Qgd2l0aCBleGFjdGx5IG9uZSBzZW1hbnRpYyBtb2R1bGUgb3IgcHJvZ3JhbS5cIilcbiAgfVxuICBpZiAoaW5wdXQubGFuZ3VhZ2UgIT09IHVuZGVmaW5lZCAmJiBpbnB1dC5sYW5ndWFnZSAhPSBcImFuZHJvaWRcIiB8fCBpbnB1dC5yb2xlICE9PSB1bmRlZmluZWQgJiYgaW5wdXQucm9sZSAhPSBcImFwcGxpY2F0aW9uXCIpIHtcbiAgICBpbnZhbGlkSW5wdXQoXCJBbmRyb2lkIGFwcGxpY2F0aW9uIGdlbmVyYXRpb24gYWNjZXB0cyBvbmx5IHRoZSBBbmRyb2lkIGFwcGxpY2F0aW9uIHJvbGUuXCIpXG4gIH1cbiAgY29uc3QgY29uZmlndXJhdGlvbiA9IG5vcm1hbGl6ZUNvbmZpZ3VyYXRpb24oaW5wdXQuY29uZmlndXJhdGlvbilcbiAgY29uc3QgcHJvZ3JhbSA9IGlucHV0LnByb2dyYW0gPz8gbm9ybWFsaXplU2luZ2xlTW9kdWxlKFxuICAgIC8qKiBAdHlwZSB7aW1wb3J0KFwiLi4vc2VtYW50aWMvdHlwZXMuanNcIikuU2VtYW50aWNNb2R1bGUgfCB1bmRlZmluZWR9ICovIChpbnB1dC5tb2R1bGUpKVxuICBjb25zdCBwcmVwYXJlZCA9IHByZWZsaWdodFNlbWFudGljUHJvZ3JhbSh7XG4gICAgYmFja2VuZExhbmd1YWdlOiBcImtvdGxpblwiLFxuICAgIGRpYWdub3N0aWNMYW5ndWFnZTogXCJhbmRyb2lkXCIsXG4gICAgcHJvZ3JhbTogLyoqIEB0eXBlIHtpbXBvcnQoXCIuLi9zZW1hbnRpYy90eXBlcy5qc1wiKS5TZW1hbnRpY1Byb2dyYW19ICovIChwcm9ncmFtKSxcbiAgICBzb3VyY2VMYW5ndWFnZXM6IGFuZHJvaWRTb3VyY2VMYW5ndWFnZXNcbiAgfSlcbiAgdmFsaWRhdGVBY3Rpdml0eUNsYXNzTmFtZShjb25maWd1cmF0aW9uLmFjdGl2aXR5Q2xhc3NOYW1lLCBwcmVwYXJlZC5wcm9ncmFtLmVudHJ5TW9kdWxlKVxuICBjb25zdCBwYWNrYWdlUGF0aCA9IGNvbmZpZ3VyYXRpb24ucGFja2FnZU5hbWUucmVwbGFjZUFsbChcIi5cIiwgXCIvXCIpXG4gIGNvbnN0IHNlbWFudGljUm9vdCA9IGAke3Byb2plY3RSb290fS9hcHAvc3JjL21haW4va290bGluLyR7cGFja2FnZVBhdGh9L3NlbWFudGljYFxuICBjb25zdCBtb2R1bGVQYXRocyA9IG5ldyBNYXAocHJlcGFyZWQubW9kdWxlcy5tYXAobW9kdWxlID0+IFtcbiAgICAvKiogQHR5cGUge3N0cmluZ30gKi8gKFJlZmxlY3QuZ2V0KG1vZHVsZSwgXCJpZFwiKSksXG4gICAgYCR7c2VtYW50aWNSb290fS8ke21vZHVsZU5hbWUoLyoqIEB0eXBlIHtzdHJpbmd9ICovIChSZWZsZWN0LmdldChtb2R1bGUsIFwiaWRcIikpKX0ua3RgXG4gIF0pKVxuICBjb25zdCByZXNvdXJjZXMgPSBub3JtYWxpemVJbnB1dEZpbGVzKGlucHV0LnJlc291cmNlcywgXCJyZXNvdXJjZVwiKVxuICBjb25zdCBhc3NldHMgPSBub3JtYWxpemVJbnB1dEZpbGVzKGlucHV0LmFzc2V0cywgXCJhc3NldFwiKVxuICBjb25zdCBmaXhlZFBhdGhzID0gYXBwbGljYXRpb25QYXRocyhjb25maWd1cmF0aW9uLCBtb2R1bGVQYXRocylcbiAgY29uc3QgY29tcGxldGVQYXRocyA9IFtcbiAgICAuLi5maXhlZFBhdGhzLFxuICAgIC4uLnJlc291cmNlcy5tYXAoKHtmdWxsUGF0aH0pID0+IGZ1bGxQYXRoKSxcbiAgICAuLi5hc3NldHMubWFwKCh7ZnVsbFBhdGh9KSA9PiBmdWxsUGF0aClcbiAgXVxuICBjb25zdCBjb25mbGljdCA9IGZpbmRQb3J0YWJsZUFydGlmYWN0UGF0aENvbmZsaWN0KGNvbXBsZXRlUGF0aHMpXG5cbiAgaWYgKGNvbmZsaWN0KSB7XG4gICAgaW52YWxpZFBhdGgoYEFwcGxpY2F0aW9uIGFydGlmYWN0IHBhdGggJyR7Y29uZmxpY3QucGF0aH0nIGhhcyBhICR7Y29uZmxpY3Qua2luZH0gY29uZmxpY3Qke2NvbmZsaWN0Lm90aGVyID8gYCB3aXRoICcke2NvbmZsaWN0Lm90aGVyfSdgIDogXCJcIn0uYClcbiAgfVxuXG4gIHJldHVybiB7Li4ucHJlcGFyZWQsIGFzc2V0cywgY29uZmlndXJhdGlvbiwgbW9kdWxlUGF0aHMsIHBhY2thZ2VQYXRoLCByZXNvdXJjZXN9XG59XG5cbi8qKlxuICogR2VuZXJhdGVzIG9uZSBjb21wbGV0ZSBkZXRlcm1pbmlzdGljIEFuZHJvaWQvS290bGluIHByb2plY3QuXG4gKiBAcGFyYW0ge1JlY29yZDxzdHJpbmcsIHVua25vd24+fSBpbnB1dCAtIEFwcGxpY2F0aW9uIGdlbmVyYXRpb24gcmVxdWVzdC5cbiAqIEByZXR1cm5zIHt7YXJ0aWZhY3RzOiBpbXBvcnQoXCIuLi9zZW1hbnRpYy90eXBlcy5qc1wiKS5HZW5lcmF0ZWRTZXRBcnRpZmFjdFtdLCBtZXRhZGF0YToge2FwcGxpY2F0aW9uTWFuaWZlc3Q6IHN0cmluZywgcGxhdGZvcm1RdWFsaWZpY2F0aW9uOiBzdHJpbmd9LCB0YXJnZXQ6IFwiYW5kcm9pZFwifX0gQ2FuZGlkYXRlIGFydGlmYWN0IHNldC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdlbmVyYXRlQW5kcm9pZEFwcGxpY2F0aW9uKGlucHV0KSB7XG4gIGNvbnN0IHByZXBhcmVkID0gcHJlZmxpZ2h0QW5kcm9pZEFwcGxpY2F0aW9uKGlucHV0KVxuICBjb25zdCBjb25maWd1cmF0aW9uU3BhbnMgPSAvKiogQHR5cGUge0NvbmZpZ3VyYXRpb25TcGFuW119ICovIChbXSlcbiAgY29uc3QgY29uZmlndXJhdGlvbiA9IHByZXBhcmVkLmNvbmZpZ3VyYXRpb25cbiAgY29uc3QgcGFja2FnZVBhdGggPSBwcmVwYXJlZC5wYWNrYWdlUGF0aFxuICBjb25zdCBzZW1hbnRpY0FydGlmYWN0cyA9IHJlbmRlclNlbWFudGljQXJ0aWZhY3RzKHByZXBhcmVkKVxuICBjb25zdCBhcnRpZmFjdHMgPSAvKiogQHR5cGUge2ltcG9ydChcIi4uL3NlbWFudGljL3R5cGVzLmpzXCIpLkdlbmVyYXRlZFNldEFydGlmYWN0W119ICovIChbXG4gICAgY29uZmlndXJlZFRleHQoYCR7cHJvamVjdFJvb3R9L2FwcC9idWlsZC5ncmFkbGUua3RzYCwgXCJtYW5pZmVzdFwiLCByZW5kZXJBcHBCdWlsZChjb25maWd1cmF0aW9uLCBjb25maWd1cmF0aW9uU3BhbnMpLFxuICAgICAgXCJQaW5uZWQgZGVwZW5kZW5jeS1taW5pbWFsIEFuZHJvaWQgYXBwbGljYXRpb24gYnVpbGQuXCIpLFxuICAgIGNvbmZpZ3VyZWRUZXh0KGAke3Byb2plY3RSb290fS9hcHAvc3JjL2FuZHJvaWRUZXN0L0FuZHJvaWRNYW5pZmVzdC54bWxgLCBcIm1hbmlmZXN0XCIsXG4gICAgICByZW5kZXJBbmRyb2lkVGVzdE1hbmlmZXN0KGNvbmZpZ3VyYXRpb24sIGNvbmZpZ3VyYXRpb25TcGFucyksIFwiUGxhdGZvcm0tb25seSBBbmRyb2lkIGluc3RydW1lbnRhdGlvbiBkZWNsYXJhdGlvbi5cIiksXG4gICAgY29uZmlndXJlZFRleHQoYCR7cHJvamVjdFJvb3R9L2FwcC9zcmMvYW5kcm9pZFRlc3Qva290bGluLyR7cGFja2FnZVBhdGh9L1NlbWFudGlmb2xkVWlJbnN0cnVtZW50YXRpb24ua3RgLCBcInN1cHBvcnRcIixcbiAgICAgIHJlbmRlckluc3RydW1lbnRhdGlvbihjb25maWd1cmF0aW9uLCBjb25maWd1cmF0aW9uU3BhbnMpLCBcIlBsYXRmb3JtLW9ubHkgYWNjZXNzaWJpbGl0eSBhbmQgbGlmZWN5Y2xlIGFzc2VydGlvbi5cIiksXG4gICAgY29uZmlndXJlZFRleHQoYCR7cHJvamVjdFJvb3R9L2FwcC9zcmMvbWFpbi9BbmRyb2lkTWFuaWZlc3QueG1sYCwgXCJtYW5pZmVzdFwiLFxuICAgICAgcmVuZGVyQW5kcm9pZE1hbmlmZXN0KGNvbmZpZ3VyYXRpb24sIGNvbmZpZ3VyYXRpb25TcGFucyksIFwiQ2xvc2VkIGFsbG93bGlzdGVkIEFuZHJvaWQgYXBwbGljYXRpb24gbWFuaWZlc3QuXCIpLFxuICAgIGNvbmZpZ3VyZWRUZXh0KGAke3Byb2plY3RSb290fS9hcHAvc3JjL21haW4va290bGluLyR7cGFja2FnZVBhdGh9LyR7Y29uZmlndXJhdGlvbi5hY3Rpdml0eUNsYXNzTmFtZX0ua3RgLCBcImVudHJ5XCIsXG4gICAgICByZW5kZXJBY3Rpdml0eShjb25maWd1cmF0aW9uLCBwcmVwYXJlZC5wcm9ncmFtLmVudHJ5TW9kdWxlLCBjb25maWd1cmF0aW9uU3BhbnMpLFxuICAgICAgXCJTeW50aGV0aWMgQW5kcm9pZCBhY3Rpdml0eSBsaWZlY3ljbGUgYW5kIG5hdGl2ZSBvdXRwdXQgdmlldyBiaW5kaW5nLlwiKSxcbiAgICAuLi5zZW1hbnRpY0FydGlmYWN0cyxcbiAgICBzeW50aGV0aWNUZXh0KGAke3Byb2plY3RSb290fS9hcHAvc3JjL21haW4vcmVzL2xheW91dC9hY3Rpdml0eV9tYWluLnhtbGAsIFwicmVzb3VyY2VcIiwgcmVuZGVyTGF5b3V0KCksXG4gICAgICBcIlN5bnRoZXRpYyBvbmUtdmlldyBBbmRyb2lkIGxheW91dCBhbmQgYWNjZXNzaWJpbGl0eSBpZGVudGl0eS5cIiksXG4gICAgY29uZmlndXJlZFRleHQoYCR7cHJvamVjdFJvb3R9L2FwcC9zcmMvbWFpbi9yZXMvdmFsdWVzL3N0cmluZ3MueG1sYCwgXCJyZXNvdXJjZVwiLFxuICAgICAgcmVuZGVyU3RyaW5ncyhjb25maWd1cmF0aW9uLCBjb25maWd1cmF0aW9uU3BhbnMpLCBcIkVzY2FwZWQgYXBwbGljYXRpb24gbGFiZWxzIGRlcml2ZWQgZnJvbSBjb25maWd1cmF0aW9uLlwiKSxcbiAgICBzeW50aGV0aWNUZXh0KGAke3Byb2plY3RSb290fS9hcHAvc3JjL21haW4vcmVzL3ZhbHVlcy90aGVtZXMueG1sYCwgXCJyZXNvdXJjZVwiLCByZW5kZXJUaGVtZSgpLFxuICAgICAgXCJDbG9zZWQgcGxhdGZvcm0tbmF0aXZlIGFwcGxpY2F0aW9uIHRoZW1lLlwiKSxcbiAgICBzeW50aGV0aWNUZXh0KGAke3Byb2plY3RSb290fS9hcHAvc3JjL21haW4vcmVzL3htbC9iYWNrdXBfcnVsZXMueG1sYCwgXCJyZXNvdXJjZVwiLCByZW5kZXJCYWNrdXBSdWxlcygpLFxuICAgICAgXCJDbG9zZWQgQVBJIDIzLTMwIGZ1bGwtYmFja3VwIGV4Y2x1c2lvbnMuXCIpLFxuICAgIHN5bnRoZXRpY1RleHQoYCR7cHJvamVjdFJvb3R9L2FwcC9zcmMvbWFpbi9yZXMveG1sL2RhdGFfZXh0cmFjdGlvbl9ydWxlcy54bWxgLCBcInJlc291cmNlXCIsIHJlbmRlckRhdGFFeHRyYWN0aW9uUnVsZXMoKSxcbiAgICAgIFwiQ2xvc2VkIEFuZHJvaWQgMTIrIGNsb3VkLWJhY2t1cCBhbmQgZGV2aWNlLXRyYW5zZmVyIGV4Y2x1c2lvbnMuXCIpLFxuICAgIGNvbmZpZ3VyZWRUZXh0KGAke3Byb2plY3RSb290fS9hcHAvc3JjL3Rlc3Qva290bGluLyR7cGFja2FnZVBhdGh9L1NlbWFudGlmb2xkRW50cnlUZXN0Lmt0YCwgXCJzdXBwb3J0XCIsXG4gICAgICByZW5kZXJVbml0VGVzdChjb25maWd1cmF0aW9uLCBwcmVwYXJlZC5wcm9ncmFtLmVudHJ5TW9kdWxlLCBjb25maWd1cmF0aW9uU3BhbnMpLFxuICAgICAgXCJKVW5pdCB0ZXN0LXNjb3BlLW9ubHkgc2VtYW50aWMgZGV0ZXJtaW5pc20gYXNzZXJ0aW9uLlwiKSxcbiAgICBzeW50aGV0aWNUZXh0KGAke3Byb2plY3RSb290fS9idWlsZC5ncmFkbGUua3RzYCwgXCJtYW5pZmVzdFwiLCByZW5kZXJSb290QnVpbGQoKSxcbiAgICAgIFwiUGlubmVkIEFuZHJvaWQgYW5kIEtvdGxpbiBidWlsZCBwbHVnaW5zLlwiKSxcbiAgICBzeW50aGV0aWNUZXh0KGAke3Byb2plY3RSb290fS9ncmFkbGUucHJvcGVydGllc2AsIFwic3VwcG9ydFwiLCByZW5kZXJHcmFkbGVQcm9wZXJ0aWVzKCksXG4gICAgICBcIlBpbm5lZCBoZXJtZXRpYyBHcmFkbGUgcHJvY2VzcyBwcm9wZXJ0aWVzLlwiKSxcbiAgICBjb25maWd1cmVkVGV4dChgJHtwcm9qZWN0Um9vdH0vc2V0dGluZ3MuZ3JhZGxlLmt0c2AsIFwibWFuaWZlc3RcIiwgcmVuZGVyU2V0dGluZ3MoY29uZmlndXJhdGlvbiwgY29uZmlndXJhdGlvblNwYW5zKSxcbiAgICAgIFwiQ2xvc2VkIG9mZmljaWFsIEdyYWRsZSBwbHVnaW4gYW5kIGRlcGVuZGVuY3kgcmVwb3NpdG9yaWVzLlwiKSxcbiAgICAuLi5wcmVwYXJlZC5yZXNvdXJjZXMubWFwKGlucHV0RmlsZUFydGlmYWN0KSxcbiAgICAuLi5wcmVwYXJlZC5hc3NldHMubWFwKGlucHV0RmlsZUFydGlmYWN0KVxuICBdKVxuXG4gIGFydGlmYWN0cy5zb3J0KChsZWZ0LCByaWdodCkgPT4gbGVmdC5wYXRoIDwgcmlnaHQucGF0aCA/IC0xIDogbGVmdC5wYXRoID4gcmlnaHQucGF0aCA/IDEgOiAwKVxuICBjb25zdCBtYW5pZmVzdFBhdGggPSBgJHtwcm9qZWN0Um9vdH0vc2VtYW50aWZvbGQtcHJvamVjdC5qc29uYFxuXG4gIGFydGlmYWN0cy5wdXNoKHN5bnRoZXRpY1RleHQobWFuaWZlc3RQYXRoLCBcIm1hbmlmZXN0XCIsXG4gICAgcmVuZGVyUHJvamVjdE1hbmlmZXN0KHByZXBhcmVkLCBhcnRpZmFjdHMsIG1hbmlmZXN0UGF0aCwgY29uZmlndXJhdGlvblNwYW5zKSxcbiAgICBcIlZlcnNpb25lZCBBbmRyb2lkIHByb2plY3Qgb3duZXJzaGlwLCB0b29sY2hhaW4sIGFuZCBwcm92ZW5hbmNlIG1hbmlmZXN0LlwiKSlcblxuICByZXR1cm4ge1xuICAgIGFydGlmYWN0cyxcbiAgICBtZXRhZGF0YToge2FwcGxpY2F0aW9uTWFuaWZlc3Q6IG1hbmlmZXN0UGF0aCwgcGxhdGZvcm1RdWFsaWZpY2F0aW9uOiBcInRlbnNvcmJ1enota3ZtXCJ9LFxuICAgIHRhcmdldDogXCJhbmRyb2lkXCJcbiAgfVxufVxuXG4vKipcbiAqIFJlbmRlcnMgdGhlIG1hcHBlZCBzZW1hbnRpYyBLb3RsaW4gYXJ0aWZhY3RzIGZvciBldmVyeSBwcm9ncmFtIG1vZHVsZS5cbiAqIEBwYXJhbSB7UmV0dXJuVHlwZTx0eXBlb2YgcHJlZmxpZ2h0QW5kcm9pZEFwcGxpY2F0aW9uPn0gcHJlcGFyZWQgLSBQcmVwYXJlZCBhcHBsaWNhdGlvbi5cbiAqIEByZXR1cm5zIHtpbXBvcnQoXCIuLi9zZW1hbnRpYy90eXBlcy5qc1wiKS5HZW5lcmF0ZWRTZXRBcnRpZmFjdFtdfSBTZW1hbnRpYyBLb3RsaW4gYXJ0aWZhY3RzLlxuICovXG5mdW5jdGlvbiByZW5kZXJTZW1hbnRpY0FydGlmYWN0cyhwcmVwYXJlZCkge1xuICBjb25zdCBzZW1hbnRpY1Jvb3QgPSBgJHtwcm9qZWN0Um9vdH0vYXBwL3NyYy9tYWluL2tvdGxpbi8ke3ByZXBhcmVkLnBhY2thZ2VQYXRofS9zZW1hbnRpY2BcbiAgLyoqIEB0eXBlIHtpbXBvcnQoXCIuLi9zZW1hbnRpYy90eXBlcy5qc1wiKS5HZW5lcmF0ZWRTZXRBcnRpZmFjdFtdfSAqL1xuICBjb25zdCBhcnRpZmFjdHMgPSBbc3ludGhldGljVGV4dChgJHtzZW1hbnRpY1Jvb3R9L1NlbWFudGlmb2xkUnVudGltZS5rdGAsIFwic3VwcG9ydFwiLFxuICAgIGFuZHJvaWRLb3RsaW5SdW50aW1lLnJlcGxhY2VBbGwoXCJfX1BBQ0tBR0VfX1wiLCBwcmVwYXJlZC5jb25maWd1cmF0aW9uLnBhY2thZ2VOYW1lKSxcbiAgICBcIlNoYXJlZCBBbmRyb2lkIGFwcGxpY2F0aW9uIG91dHB1dCBjYXB0dXJlIGFuZCBjaGVja2VkIEtvdGxpbiBzY2FsYXIgc3VwcG9ydC5cIildXG4gIGNvbnN0IGRlY2xhcmF0aW9ucyA9IG5ldyBNYXAoKVxuXG4gIGZvciAoY29uc3QgbW9kdWxlIG9mIHByZXBhcmVkLm1vZHVsZXMpIHtcbiAgICBmb3IgKGNvbnN0IGRlY2xhcmF0aW9uIG9mIG1vZHVsZS5mdW5jdGlvbnMpIGRlY2xhcmF0aW9ucy5zZXQoZGVjbGFyYXRpb24uaWQsIHtcbiAgICAgIGRlY2xhcmF0aW9uLFxuICAgICAgbW9kdWxlSWQ6IC8qKiBAdHlwZSB7c3RyaW5nfSAqLyAoUmVmbGVjdC5nZXQobW9kdWxlLCBcImlkXCIpKVxuICAgIH0pXG4gIH1cbiAgZm9yIChjb25zdCBtb2R1bGUgb2YgcHJlcGFyZWQubW9kdWxlcykge1xuICAgIGNvbnN0IG1vZHVsZUlkID0gLyoqIEB0eXBlIHtzdHJpbmd9ICovIChSZWZsZWN0LmdldChtb2R1bGUsIFwiaWRcIikpXG4gICAgY29uc3QgZmlsZW5hbWUgPSAvKiogQHR5cGUge3N0cmluZ30gKi8gKHByZXBhcmVkLm1vZHVsZVBhdGhzLmdldChtb2R1bGVJZCkpXG4gICAgY29uc3Qgc2lua05hbWUgPSBvdXRwdXRTaW5rTmFtZShtb2R1bGUpXG4gICAgY29uc3Qgd3JpdGVyID0gbmV3IFNvdXJjZVdyaXRlcih7XG4gICAgICBjYWxsQXJndW1lbnRTdWZmaXg6IHNpbmtOYW1lLFxuICAgICAgY2FsbE5hbWVSZXNvbHZlcjogZXhwcmVzc2lvbiA9PiB7XG4gICAgICAgIGNvbnN0IHJlc29sdmVkID0gZGVjbGFyYXRpb25zLmdldChleHByZXNzaW9uLnJlc29sdXRpb24/LmRlY2xhcmF0aW9uSWQpXG5cbiAgICAgICAgaWYgKCFyZXNvbHZlZCkgdGhyb3cgbmV3IFR5cGVFcnJvcihgVW5rbm93biB2YWxpZGF0ZWQgQW5kcm9pZCBmdW5jdGlvbiAnJHtleHByZXNzaW9uLnJlc29sdXRpb24/LmRlY2xhcmF0aW9uSWR9Jy5gKVxuXG4gICAgICAgIHJldHVybiBgU2VtYW50aWZvbGRNb2R1bGUke21vZHVsZU5hbWUocmVzb2x2ZWQubW9kdWxlSWQpfS4ke3Jlc29sdmVkLmRlY2xhcmF0aW9uLm5hbWV9YFxuICAgICAgfSxcbiAgICAgIGZpbGVuYW1lLFxuICAgICAgbGFuZ3VhZ2U6IFwia290bGluXCIsXG4gICAgICBtZXRob2RTdHJpbmdFcXVhbGl0eTogdHJ1ZSxcbiAgICAgIG1vZHVsZSxcbiAgICAgIHByb2dyYW06IHByZXBhcmVkLnByb2dyYW0sXG4gICAgICBwcm9ncmFtUGF0aHM6IHByZXBhcmVkLm1vZHVsZVBhdGhzLFxuICAgICAgc291cmNlczogcHJlcGFyZWQuc291cmNlc1xuICAgIH0pXG5cbiAgICB3cml0ZXIuc3ludGhldGljKGBAZmlsZTpTdXBwcmVzcyhcIlBMQVRGT1JNX0NMQVNTX01BUFBFRF9UT19LT1RMSU5cIilcXG5cXG5wYWNrYWdlICR7cHJlcGFyZWQuY29uZmlndXJhdGlvbi5wYWNrYWdlTmFtZX0uc2VtYW50aWNcXG5cXG5gLFxuICAgICAgXCJBbmRyb2lkIHNlbWFudGljIHBhY2thZ2UgbmFtZXNwYWNlXCIsIFttb2R1bGVdLCBbXCJcIl0pXG4gICAgZW1pdFNlbWFudGljTW9kdWxlKHdyaXRlciwgbW9kdWxlLCBtb2R1bGVJZCwgbW9kdWxlSWQgPT0gcHJlcGFyZWQucHJvZ3JhbS5lbnRyeU1vZHVsZSwgc2lua05hbWUpXG4gICAgY29uc3QgbWFwcGluZyA9IGZpbmFsaXplTWFwcGluZyh3cml0ZXIuZmluaXNoKCkpXG5cbiAgICBhcnRpZmFjdHMucHVzaCh7XG4gICAgICBjb250ZW50OiBtYXBwaW5nLmdlbmVyYXRlZC5jb250ZW50LFxuICAgICAgY29udGVudEtpbmQ6IFwidGV4dFwiLFxuICAgICAgbWVkaWFUeXBlOiBcInRleHQveC1rb3RsaW5cIixcbiAgICAgIG93bmVyc2hpcDogXCJnZW5lcmF0ZWRcIixcbiAgICAgIHBhdGg6IGZpbGVuYW1lLFxuICAgICAgcHJvdmVuYW5jZToge2tpbmQ6IFwidGV4dFwiLCBtYXBwaW5nLCBzb3VyY2VNYXA6IHRvU291cmNlTWFwVjMobWFwcGluZyksIHNvdXJjZU1hcEZpbGVuYW1lOiBgJHtmaWxlbmFtZX0ubWFwYH0sXG4gICAgICByb2xlOiBcInNvdXJjZVwiXG4gICAgfSlcbiAgfVxuXG4gIHJldHVybiBhcnRpZmFjdHNcbn1cblxuLyoqXG4gKiBFbWl0cyBvbmUgbmFtZXNwYWNlZCBzZW1hbnRpYyBtb2R1bGUgYW5kIGl0cyBlbnRyeSBicmlkZ2Ugd2hlbiBhcHBsaWNhYmxlLlxuICogQHBhcmFtIHtTb3VyY2VXcml0ZXJ9IHdyaXRlciAtIE1hcHBlZCBLb3RsaW4gd3JpdGVyLlxuICogQHBhcmFtIHtpbXBvcnQoXCIuLi9zZW1hbnRpYy90eXBlcy5qc1wiKS5TZW1hbnRpY01vZHVsZX0gbW9kdWxlIC0gUHJlcGFyZWQgbW9kdWxlLlxuICogQHBhcmFtIHtzdHJpbmd9IG1vZHVsZUlkIC0gU3RhYmxlIG1vZHVsZSBpZGVudGl0eS5cbiAqIEBwYXJhbSB7Ym9vbGVhbn0gZW50cnkgLSBXaGV0aGVyIHRoaXMgbW9kdWxlIG93bnMgdGhlIGVudHJ5IGJsb2NrLlxuICogQHBhcmFtIHtzdHJpbmd9IHNpbmtOYW1lIC0gQ29sbGlzaW9uLXNhZmUgb3V0cHV0IHNpbmsgYmluZGluZy5cbiAqL1xuZnVuY3Rpb24gZW1pdFNlbWFudGljTW9kdWxlKHdyaXRlciwgbW9kdWxlLCBtb2R1bGVJZCwgZW50cnksIHNpbmtOYW1lKSB7XG4gIHdyaXRlci5zeW50aGV0aWMoYGludGVybmFsIG9iamVjdCBTZW1hbnRpZm9sZE1vZHVsZSR7bW9kdWxlTmFtZShtb2R1bGVJZCl9IHtcXG5gLFxuICAgIFwiQW5kcm9pZCBzZW1hbnRpYyBtb2R1bGUgbmFtZXNwYWNlXCIsIFttb2R1bGVdLCBbXCJcIl0pXG4gIG1vZHVsZS5mdW5jdGlvbnMuZm9yRWFjaCgoZGVjbGFyYXRpb24sIGZ1bmN0aW9uSW5kZXgpID0+IHtcbiAgICBjb25zdCBwYXRoID0gYC9mdW5jdGlvbnMvJHtmdW5jdGlvbkluZGV4fWBcblxuICAgIHdyaXRlci5zeW50aGV0aWMoXCIgIFwiLCBcIkFuZHJvaWQgbmFtZXNwYWNlZCBmdW5jdGlvbiBpbmRlbnRhdGlvblwiLCBbZGVjbGFyYXRpb25dLCBbcGF0aF0pXG4gICAgd3JpdGVyLm1hcHBlZChcImZ1blwiLCB7bWFwcGluZ0tpbmQ6IFwiYW5jaG9yXCIsIG5vZGU6IGRlY2xhcmF0aW9uLCBwYXRofSlcbiAgICB3cml0ZXIuc3ludGhldGljKFwiIFwiLCBcImRlY2xhcmF0aW9uIHNwYWNpbmdcIiwgW2RlY2xhcmF0aW9uXSwgW3BhdGhdKVxuICAgIHdyaXRlci5tYXBwZWQoZGVjbGFyYXRpb24ubmFtZSwge21hcHBpbmdLaW5kOiBcImV4YWN0XCIsIG5vZGU6IGRlY2xhcmF0aW9uLCBwYXRoLCByb2xlOiBcIm5hbWVcIn0pXG4gICAgd3JpdGVyLnN5bnRoZXRpYyhcIihcIiwgXCJmdW5jdGlvbiBwYXJhbWV0ZXIgc2NhZmZvbGRcIiwgW2RlY2xhcmF0aW9uXSwgW3BhdGhdKVxuICAgIGRlY2xhcmF0aW9uLnBhcmFtZXRlcnMuZm9yRWFjaCgocGFyYW1ldGVyLCBwYXJhbWV0ZXJJbmRleCkgPT4ge1xuICAgICAgY29uc3QgcGFyYW1ldGVyUGF0aCA9IGAke3BhdGh9L3BhcmFtZXRlcnMvJHtwYXJhbWV0ZXJJbmRleH1gXG5cbiAgICAgIGlmIChwYXJhbWV0ZXJJbmRleCkgd3JpdGVyLnN5bnRoZXRpYyhcIiwgXCIsIFwicGFyYW1ldGVyIHNlcGFyYXRvclwiLCBbcGFyYW1ldGVyXSwgW3BhcmFtZXRlclBhdGhdKVxuICAgICAgd3JpdGVyLm1hcHBlZChwYXJhbWV0ZXIubmFtZSwge21hcHBpbmdLaW5kOiBcImV4YWN0XCIsIG5vZGU6IHBhcmFtZXRlciwgcGF0aDogcGFyYW1ldGVyUGF0aCwgcm9sZTogXCJuYW1lXCJ9KVxuICAgICAgd3JpdGVyLnN5bnRoZXRpYyhcIjogXCIsIFwicGFyYW1ldGVyIGFubm90YXRpb24gc2NhZmZvbGRcIiwgW3BhcmFtZXRlcl0sIFtwYXJhbWV0ZXJQYXRoXSlcbiAgICAgIHdyaXRlci5tYXBwZWQoZW1pdFNjYWxhclR5cGUoXCJrb3RsaW5cIiwgcGFyYW1ldGVyLnR5cGUpLCB7bWFwcGluZ0tpbmQ6IFwiZXhhY3RcIiwgbm9kZTogcGFyYW1ldGVyLnR5cGUsXG4gICAgICAgIHBhdGg6IGAke3BhcmFtZXRlclBhdGh9L3R5cGVgLCByb2xlOiBcInR5cGVcIn0pXG4gICAgfSlcbiAgICBpZiAoZGVjbGFyYXRpb24ucGFyYW1ldGVycy5sZW5ndGgpIHdyaXRlci5zeW50aGV0aWMoXCIsIFwiLCBcIm91dHB1dCBzaW5rIHBhcmFtZXRlciBzZXBhcmF0b3JcIiwgW2RlY2xhcmF0aW9uXSwgW3BhdGhdKVxuICAgIHdyaXRlci5zeW50aGV0aWMoYCR7c2lua05hbWV9OiBTZW1hbnRpZm9sZE91dHB1dFNpbmspOiBgLCBcInNoYXJlZCBBbmRyb2lkIG91dHB1dCBzaW5rIHBhcmFtZXRlclwiLCBbZGVjbGFyYXRpb25dLCBbcGF0aF0pXG4gICAgd3JpdGVyLm1hcHBlZChlbWl0U2NhbGFyVHlwZShcImtvdGxpblwiLCBkZWNsYXJhdGlvbi5yZXR1cm5UeXBlKSwge21hcHBpbmdLaW5kOiBcImV4YWN0XCIsIG5vZGU6IGRlY2xhcmF0aW9uLnJldHVyblR5cGUsXG4gICAgICBwYXRoOiBgJHtwYXRofS9yZXR1cm5UeXBlYCwgcm9sZTogXCJ0eXBlXCJ9KVxuICAgIHdyaXRlci5zeW50aGV0aWMoXCIge1xcblwiLCBcIktvdGxpbiBmdW5jdGlvbiBib2R5IHNjYWZmb2xkXCIsIFtkZWNsYXJhdGlvbl0sIFtwYXRoXSlcbiAgICBlbWl0S290bGluQmxvY2sod3JpdGVyLCBkZWNsYXJhdGlvbi5ib2R5LCBcIiAgICBcIiwgYCR7cGF0aH0vYm9keWAsIGAke3NpbmtOYW1lfS53cml0ZWApXG4gICAgd3JpdGVyLnN5bnRoZXRpYyhcIiAgfVxcblwiLCBcIktvdGxpbiBmdW5jdGlvbiBib2R5IHNjYWZmb2xkXCIsIFtkZWNsYXJhdGlvbl0sIFtwYXRoXSlcbiAgfSlcbiAgaWYgKGVudHJ5KSB7XG4gICAgd3JpdGVyLnN5bnRoZXRpYyhcIiAgZnVuIHNlbWFudGlmb2xkRW50cnkoKTogamF2YS51dGlsLkFycmF5TGlzdDxTdHJpbmc+IHtcXG5cIiwgXCJBbmRyb2lkIHNlbWFudGljIGVudHJ5IHNjYWZmb2xkXCIsIFttb2R1bGUuZW50cnlQb2ludF0sIFtcIi9lbnRyeVBvaW50XCJdKVxuICAgIHdyaXRlci5zeW50aGV0aWMoYCAgICB2YWwgJHtzaW5rTmFtZX0gPSBTZW1hbnRpZm9sZE91dHB1dFNpbmsoKVxcbmAsIFwiQW5kcm9pZCBvdXRwdXQgc2luayBhbGxvY2F0aW9uXCIsXG4gICAgICBbbW9kdWxlLmVudHJ5UG9pbnRdLCBbXCIvZW50cnlQb2ludFwiXSlcbiAgICB3cml0ZXIuc3ludGhldGljKGAgICAgJHtzaW5rTmFtZX0ucmVzZXQoKVxcbmAsIFwiQW5kcm9pZCBsaWZlY3ljbGUtc2FmZSBvdXRwdXQgcmVzZXRcIiwgW21vZHVsZS5lbnRyeVBvaW50XSwgW1wiL2VudHJ5UG9pbnRcIl0pXG4gICAgZW1pdEtvdGxpbkJsb2NrKHdyaXRlciwgbW9kdWxlLmVudHJ5UG9pbnQuYm9keSwgXCIgICAgXCIsIFwiL2VudHJ5UG9pbnQvYm9keVwiLCBgJHtzaW5rTmFtZX0ud3JpdGVgKVxuICAgIHdyaXRlci5zeW50aGV0aWMoYCAgICByZXR1cm4gJHtzaW5rTmFtZX0uc25hcHNob3QoKVxcbmAsIFwiQW5kcm9pZCBzZW1hbnRpYyBvdXRwdXQgcmV0dXJuXCIsXG4gICAgICBbbW9kdWxlLmVudHJ5UG9pbnRdLCBbXCIvZW50cnlQb2ludFwiXSlcbiAgICB3cml0ZXIuc3ludGhldGljKFwiICB9XFxuXCIsIFwiQW5kcm9pZCBzZW1hbnRpYyBlbnRyeSBzY2FmZm9sZFwiLCBbbW9kdWxlLmVudHJ5UG9pbnRdLCBbXCIvZW50cnlQb2ludFwiXSlcbiAgfVxuICB3cml0ZXIuc3ludGhldGljKFwifVxcblwiLCBcIkFuZHJvaWQgc2VtYW50aWMgbW9kdWxlIG5hbWVzcGFjZVwiLCBbbW9kdWxlXSwgW1wiXCJdKVxufVxuXG4vKipcbiAqIExpc3RzIGV2ZXJ5IGZpeGVkIGdlbmVyYXRlZCBwYXRoIGJlZm9yZSBhbnkgd3JpdGVyIGlzIGFsbG9jYXRlZC5cbiAqIEBwYXJhbSB7aW1wb3J0KFwiLi4vc2VtYW50aWMvdHlwZXMuanNcIikuQW5kcm9pZEFwcGxpY2F0aW9uQ29uZmlndXJhdGlvbn0gY29uZmlndXJhdGlvbiAtIE5vcm1hbGl6ZWQgYXBwbGljYXRpb24gY29uZmlndXJhdGlvbi5cbiAqIEBwYXJhbSB7TWFwPHN0cmluZywgc3RyaW5nPn0gbW9kdWxlUGF0aHMgLSBTZW1hbnRpYyBtb2R1bGUgb3V0cHV0IHBhdGhzLlxuICogQHJldHVybnMge3N0cmluZ1tdfSBGaXhlZCBvd25lZCBhcnRpZmFjdCBwYXRocy5cbiAqL1xuZnVuY3Rpb24gYXBwbGljYXRpb25QYXRocyhjb25maWd1cmF0aW9uLCBtb2R1bGVQYXRocykge1xuICBjb25zdCBwYWNrYWdlUGF0aCA9IGNvbmZpZ3VyYXRpb24ucGFja2FnZU5hbWUucmVwbGFjZUFsbChcIi5cIiwgXCIvXCIpXG5cbiAgcmV0dXJuIFtcbiAgICBgJHtwcm9qZWN0Um9vdH0vYXBwL2J1aWxkLmdyYWRsZS5rdHNgLFxuICAgIGAke3Byb2plY3RSb290fS9hcHAvc3JjL2FuZHJvaWRUZXN0L0FuZHJvaWRNYW5pZmVzdC54bWxgLFxuICAgIGAke3Byb2plY3RSb290fS9hcHAvc3JjL2FuZHJvaWRUZXN0L2tvdGxpbi8ke3BhY2thZ2VQYXRofS9TZW1hbnRpZm9sZFVpSW5zdHJ1bWVudGF0aW9uLmt0YCxcbiAgICBgJHtwcm9qZWN0Um9vdH0vYXBwL3NyYy9tYWluL0FuZHJvaWRNYW5pZmVzdC54bWxgLFxuICAgIGAke3Byb2plY3RSb290fS9hcHAvc3JjL21haW4va290bGluLyR7cGFja2FnZVBhdGh9LyR7Y29uZmlndXJhdGlvbi5hY3Rpdml0eUNsYXNzTmFtZX0ua3RgLFxuICAgIC4uLm1vZHVsZVBhdGhzLnZhbHVlcygpLFxuICAgIGAke3Byb2plY3RSb290fS9hcHAvc3JjL21haW4va290bGluLyR7cGFja2FnZVBhdGh9L3NlbWFudGljL1NlbWFudGlmb2xkUnVudGltZS5rdGAsXG4gICAgYCR7cHJvamVjdFJvb3R9L2FwcC9zcmMvbWFpbi9yZXMvbGF5b3V0L2FjdGl2aXR5X21haW4ueG1sYCxcbiAgICBgJHtwcm9qZWN0Um9vdH0vYXBwL3NyYy9tYWluL3Jlcy92YWx1ZXMvc3RyaW5ncy54bWxgLFxuICAgIGAke3Byb2plY3RSb290fS9hcHAvc3JjL21haW4vcmVzL3ZhbHVlcy90aGVtZXMueG1sYCxcbiAgICBgJHtwcm9qZWN0Um9vdH0vYXBwL3NyYy9tYWluL3Jlcy94bWwvYmFja3VwX3J1bGVzLnhtbGAsXG4gICAgYCR7cHJvamVjdFJvb3R9L2FwcC9zcmMvbWFpbi9yZXMveG1sL2RhdGFfZXh0cmFjdGlvbl9ydWxlcy54bWxgLFxuICAgIGAke3Byb2plY3RSb290fS9hcHAvc3JjL3Rlc3Qva290bGluLyR7cGFja2FnZVBhdGh9L1NlbWFudGlmb2xkRW50cnlUZXN0Lmt0YCxcbiAgICBgJHtwcm9qZWN0Um9vdH0vYnVpbGQuZ3JhZGxlLmt0c2AsXG4gICAgYCR7cHJvamVjdFJvb3R9L2dyYWRsZS5wcm9wZXJ0aWVzYCxcbiAgICBgJHtwcm9qZWN0Um9vdH0vc2V0dGluZ3MuZ3JhZGxlLmt0c2AsXG4gICAgYCR7cHJvamVjdFJvb3R9L3NlbWFudGlmb2xkLXByb2plY3QuanNvbmBcbiAgXVxufVxuXG4vKipcbiAqIFJlbmRlcnMgdGhlIHBpbm5lZCByb290IHBsdWdpbiBkZWNsYXJhdGlvbnMuXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBSb290IEdyYWRsZSBLb3RsaW4gc2NyaXB0LlxuICovXG5mdW5jdGlvbiByZW5kZXJSb290QnVpbGQoKSB7XG4gIHJldHVybiBgcGx1Z2lucyB7XG4gIGlkKFwiY29tLmFuZHJvaWQuYXBwbGljYXRpb25cIikgdmVyc2lvbiBcIiR7dG9vbGNoYWluLmFuZHJvaWRHcmFkbGVQbHVnaW59XCIgYXBwbHkgZmFsc2VcbiAgaWQoXCJvcmcuamV0YnJhaW5zLmtvdGxpbi5hbmRyb2lkXCIpIHZlcnNpb24gXCIke3Rvb2xjaGFpbi5rb3RsaW59XCIgYXBwbHkgZmFsc2Vcbn1cbmBcbn1cblxuLyoqXG4gKiBSZW5kZXJzIGNsb3NlZCBwcm9qZWN0IHNldHRpbmdzIGFuZCByZXBvc2l0b3JpZXMuXG4gKiBAcGFyYW0ge2ltcG9ydChcIi4uL3NlbWFudGljL3R5cGVzLmpzXCIpLkFuZHJvaWRBcHBsaWNhdGlvbkNvbmZpZ3VyYXRpb259IGNvbmZpZ3VyYXRpb24gLSBOb3JtYWxpemVkIGNvbmZpZ3VyYXRpb24uXG4gKiBAcGFyYW0ge0NvbmZpZ3VyYXRpb25TcGFuW119IHNwYW5zIC0gQ29sbGVjdGVkIGNvbmZpZ3VyYXRpb24gcHJvdmVuYW5jZSBzcGFucy5cbiAqIEByZXR1cm5zIHtzdHJpbmd9IFNldHRpbmdzIEdyYWRsZSBLb3RsaW4gc2NyaXB0LlxuICovXG5mdW5jdGlvbiByZW5kZXJTZXR0aW5ncyhjb25maWd1cmF0aW9uLCBzcGFucykge1xuICByZXR1cm4gY29uZmlndXJlZChgJHtwcm9qZWN0Um9vdH0vc2V0dGluZ3MuZ3JhZGxlLmt0c2AsIHNwYW5zLCBbYHBsdWdpbk1hbmFnZW1lbnQge1xuICByZXBvc2l0b3JpZXMge1xuICAgIGdvb2dsZSgpXG4gICAgbWF2ZW5DZW50cmFsKClcbiAgICBncmFkbGVQbHVnaW5Qb3J0YWwoKVxuICB9XG59XG5cbmRlcGVuZGVuY3lSZXNvbHV0aW9uTWFuYWdlbWVudCB7XG4gIHJlcG9zaXRvcmllc01vZGUuc2V0KFJlcG9zaXRvcmllc01vZGUuRkFJTF9PTl9QUk9KRUNUX1JFUE9TKVxuICByZXBvc2l0b3JpZXMge1xuICAgIGdvb2dsZSgpXG4gICAgbWF2ZW5DZW50cmFsKClcbiAgfVxufVxuXG5yb290UHJvamVjdC5uYW1lID0gXCJgLCBmaWVsZChcInByb2R1Y3ROYW1lXCIsIGtvdGxpbkVzY2FwZShjb25maWd1cmF0aW9uLnByb2R1Y3ROYW1lKSksIGBcIlxuaW5jbHVkZShcIjphcHBcIilcbmBdKVxufVxuXG4vKipcbiAqIFJlbmRlcnMgZGV0ZXJtaW5pc3RpYyBHcmFkbGUgcHJvY2VzcyBwcm9wZXJ0aWVzLlxuICogQHJldHVybnMge3N0cmluZ30gR3JhZGxlIHByb3BlcnRpZXMgZmlsZS5cbiAqL1xuZnVuY3Rpb24gcmVuZGVyR3JhZGxlUHJvcGVydGllcygpIHtcbiAgcmV0dXJuIGBvcmcuZ3JhZGxlLmNhY2hpbmc9ZmFsc2Vcbm9yZy5ncmFkbGUuY29uZmlndXJhdGlvbi1jYWNoZT1mYWxzZVxub3JnLmdyYWRsZS5kYWVtb249ZmFsc2Vcbm9yZy5ncmFkbGUuanZtYXJncz0tWG14MjA0OG0gLURmaWxlLmVuY29kaW5nPVVURi04IC1EdXNlci5sYW5ndWFnZT1lbiAtRHVzZXIuY291bnRyeT1VUyAtRHVzZXIudGltZXpvbmU9VVRDXG5vcmcuZ3JhZGxlLnBhcmFsbGVsPWZhbHNlXG5rb3RsaW4uc3RkbGliLmRlZmF1bHQuZGVwZW5kZW5jeT1mYWxzZVxua290bGluLmNvZGUuc3R5bGU9b2ZmaWNpYWxcbmBcbn1cblxuLyoqXG4gKiBSZW5kZXJzIHRoZSBhcHBsaWNhdGlvbiBtb2R1bGUgYnVpbGQgY29udHJhY3QuXG4gKiBAcGFyYW0ge2ltcG9ydChcIi4uL3NlbWFudGljL3R5cGVzLmpzXCIpLkFuZHJvaWRBcHBsaWNhdGlvbkNvbmZpZ3VyYXRpb259IGNvbmZpZ3VyYXRpb24gLSBOb3JtYWxpemVkIGNvbmZpZ3VyYXRpb24uXG4gKiBAcGFyYW0ge0NvbmZpZ3VyYXRpb25TcGFuW119IHNwYW5zIC0gQ29sbGVjdGVkIGNvbmZpZ3VyYXRpb24gcHJvdmVuYW5jZSBzcGFucy5cbiAqIEByZXR1cm5zIHtzdHJpbmd9IEFwcGxpY2F0aW9uIEdyYWRsZSBLb3RsaW4gc2NyaXB0LlxuICovXG5mdW5jdGlvbiByZW5kZXJBcHBCdWlsZChjb25maWd1cmF0aW9uLCBzcGFucykge1xuICByZXR1cm4gY29uZmlndXJlZChgJHtwcm9qZWN0Um9vdH0vYXBwL2J1aWxkLmdyYWRsZS5rdHNgLCBzcGFucywgW1xuICAgIGBpbXBvcnQgb3JnLmpldGJyYWlucy5rb3RsaW4uZ3JhZGxlLmRzbC5Kdm1UYXJnZXRcbmltcG9ydCBvcmcuamV0YnJhaW5zLmtvdGxpbi5ncmFkbGUudGFza3MuS290bGluSnZtQ29tcGlsZVxuXG5wbHVnaW5zIHtcbiAgaWQoXCJjb20uYW5kcm9pZC5hcHBsaWNhdGlvblwiKVxuICBpZChcIm9yZy5qZXRicmFpbnMua290bGluLmFuZHJvaWRcIilcbn1cblxuZGVwZW5kZW5jaWVzIHtcbiAgdGVzdFJ1bnRpbWVPbmx5KFwib3JnLmhhbWNyZXN0OmhhbWNyZXN0LWNvcmU6MS4zXCIpIHtcbiAgICBpc1RyYW5zaXRpdmUgPSBmYWxzZVxuICB9XG59XG5cbnZhbCBzZW1hbnRpZm9sZEtvdGxpbkhvbWUgPSBwcm92aWRlcnMuZW52aXJvbm1lbnRWYXJpYWJsZShcIlNFTUFOVElGT0xEX0tPVExJTl9IT01FXCIpLm9yTnVsbFxuICA/OiB0aHJvdyBHcmFkbGVFeGNlcHRpb24oXCJTRU1BTlRJRk9MRF9BTkRST0lEX0lORlJBU1RSVUNUVVJFOiBTRU1BTlRJRk9MRF9LT1RMSU5fSE9NRSBpcyByZXF1aXJlZFwiKVxudmFsIHNlbWFudGlmb2xkS290bGluU3RkbGliID0gZmlsZShcIiRzZW1hbnRpZm9sZEtvdGxpbkhvbWUvbGliL2tvdGxpbi1zdGRsaWIuamFyXCIpXG5pZiAoIXNlbWFudGlmb2xkS290bGluU3RkbGliLmlzRmlsZSkge1xuICB0aHJvdyBHcmFkbGVFeGNlcHRpb24oXCJTRU1BTlRJRk9MRF9BTkRST0lEX0lORlJBU1RSVUNUVVJFOiBwaW5uZWQgS290bGluIGNvbXBpbGUgbGlicmFyeSBpcyBtaXNzaW5nXCIpXG59XG52YWwgc2VtYW50aWZvbGRBY2NlcHRhbmNlUm9vdCA9IHByb3ZpZGVycy5lbnZpcm9ubWVudFZhcmlhYmxlKFwiU0VNQU5USUZPTERfQU5EUk9JRF9BQ0NFUFRBTkNFX1JPT1RcIikub3JOdWxsXG4gID86IHRocm93IEdyYWRsZUV4Y2VwdGlvbihcIlNFTUFOVElGT0xEX0FORFJPSURfSU5GUkFTVFJVQ1RVUkU6IFNFTUFOVElGT0xEX0FORFJPSURfQUNDRVBUQU5DRV9ST09UIGlzIHJlcXVpcmVkXCIpXG52YWwgc2VtYW50aWZvbGREZWJ1Z0tleXN0b3JlID0gZmlsZShcIiRzZW1hbnRpZm9sZEFjY2VwdGFuY2VSb290L2RlYnVnLmtleXN0b3JlXCIpXG5pZiAoIXNlbWFudGlmb2xkRGVidWdLZXlzdG9yZS5pc0ZpbGUpIHtcbiAgdGhyb3cgR3JhZGxlRXhjZXB0aW9uKFwiU0VNQU5USUZPTERfQU5EUk9JRF9JTkZSQVNUUlVDVFVSRTogZXBoZW1lcmFsIGRlYnVnIGtleXN0b3JlIGlzIG1pc3NpbmdcIilcbn1cblxuYW5kcm9pZCB7XG4gIG5hbWVzcGFjZSA9IFwiYCxcbiAgICBmaWVsZChcIm5hbWVzcGFjZVwiLCBjb25maWd1cmF0aW9uLm5hbWVzcGFjZSksIGBcIlxcbiAgY29tcGlsZVNkayA9IGAsIGZpZWxkKFwiY29tcGlsZVNka1wiLCBTdHJpbmcoY29uZmlndXJhdGlvbi5jb21waWxlU2RrKSksXG4gICAgYFxcbiAgYnVpbGRUb29sc1ZlcnNpb24gPSBcImAsIGZpZWxkKFwiYnVpbGRUb29sc1ZlcnNpb25cIiwgY29uZmlndXJhdGlvbi5idWlsZFRvb2xzVmVyc2lvbiksIGBcIlxcblxcbiAgZGVmYXVsdENvbmZpZyB7XFxuICAgIGFwcGxpY2F0aW9uSWQgPSBcImAsXG4gICAgZmllbGQoXCJhcHBsaWNhdGlvbklkXCIsIGNvbmZpZ3VyYXRpb24uYXBwbGljYXRpb25JZCksIGBcIlxcbiAgICBtaW5TZGsgPSBgLCBmaWVsZChcIm1pbmltdW1TZGtcIiwgU3RyaW5nKGNvbmZpZ3VyYXRpb24ubWluaW11bVNkaykpLFxuICAgIGBcXG4gICAgdGFyZ2V0U2RrID0gYCwgZmllbGQoXCJ0YXJnZXRTZGtcIiwgU3RyaW5nKGNvbmZpZ3VyYXRpb24udGFyZ2V0U2RrKSksIGBcXG4gICAgdmVyc2lvbkNvZGUgPSBgLFxuICAgIGZpZWxkKFwidmVyc2lvbkNvZGVcIiwgU3RyaW5nKGNvbmZpZ3VyYXRpb24udmVyc2lvbkNvZGUpKSwgYFxcbiAgICB2ZXJzaW9uTmFtZSA9IFwiYCwgZmllbGQoXCJ2ZXJzaW9uTmFtZVwiLCBrb3RsaW5Fc2NhcGUoY29uZmlndXJhdGlvbi52ZXJzaW9uTmFtZSkpLFxuICAgIGBcIlxcbiAgICB0ZXN0QXBwbGljYXRpb25JZCA9IFwiYCwgZmllbGQoXCJhcHBsaWNhdGlvbklkXCIsIGNvbmZpZ3VyYXRpb24uYXBwbGljYXRpb25JZCksXG4gICAgYC50ZXN0XCJcXG4gICAgdGVzdEluc3RydW1lbnRhdGlvblJ1bm5lciA9IFwiYCwgZmllbGQoXCJwYWNrYWdlTmFtZVwiLCBjb25maWd1cmF0aW9uLnBhY2thZ2VOYW1lKSxcbiAgICBgLlNlbWFudGlmb2xkVWlJbnN0cnVtZW50YXRpb25cIlxcbiAgfVxcblxcbiAgc2lnbmluZ0NvbmZpZ3Mge1xcbiAgICBnZXRCeU5hbWUoXCJkZWJ1Z1wiKSB7XFxuICAgICAgc3RvcmVGaWxlID0gc2VtYW50aWZvbGREZWJ1Z0tleXN0b3JlXFxuICAgICAgc3RvcmVQYXNzd29yZCA9IFwiYW5kcm9pZFwiXFxuICAgICAga2V5QWxpYXMgPSBcImFuZHJvaWRkZWJ1Z2tleVwiXFxuICAgICAga2V5UGFzc3dvcmQgPSBcImFuZHJvaWRcIlxcbiAgICB9XFxuICB9XFxuICBidWlsZFR5cGVzIHtcXG4gICAgZ2V0QnlOYW1lKFwiZGVidWdcIikge1xcbiAgICAgIHNpZ25pbmdDb25maWcgPSBzaWduaW5nQ29uZmlncy5nZXRCeU5hbWUoXCJkZWJ1Z1wiKVxcbiAgICB9XFxuICB9XFxuICBidWlsZEZlYXR1cmVzIHtcXG4gICAgYnVpbGRDb25maWcgPSBmYWxzZVxcbiAgfVxcbiAgY29tcGlsZU9wdGlvbnMge1xcbiAgICBzb3VyY2VDb21wYXRpYmlsaXR5ID0gSmF2YVZlcnNpb24uVkVSU0lPTl8yMVxcbiAgICB0YXJnZXRDb21wYXRpYmlsaXR5ID0gSmF2YVZlcnNpb24uVkVSU0lPTl8yMVxcbiAgfVxcbiAgbGludCB7XFxuICAgIGFib3J0T25FcnJvciA9IHRydWVcXG4gICAgY2hlY2tEZXBlbmRlbmNpZXMgPSB0cnVlXFxuICAgIGRpc2FibGUgKz0gXCJNaXNzaW5nQXBwbGljYXRpb25JY29uXCJcXG4gICAgZGlzYWJsZSArPSBcIk9sZFRhcmdldEFwaVwiXFxuICAgIGRpc2FibGUgKz0gXCJHcmFkbGVEZXBlbmRlbmN5XCJcXG4gICAgd2FybmluZ3NBc0Vycm9ycyA9IHRydWVcXG4gIH1cXG4gIHBhY2thZ2luZyB7XFxuICAgIHJlc291cmNlcy5leGNsdWRlcyArPSBzZXRPZihcIk1FVEEtSU5GL0xJQ0VOU0UqXCIsIFwiTUVUQS1JTkYvTk9USUNFKlwiKVxcbiAgfVxcbn1cXG5cXG50YXNrcy53aXRoVHlwZTxLb3RsaW5Kdm1Db21waWxlPigpLmNvbmZpZ3VyZUVhY2gge1xcbiAgbGlicmFyaWVzLmZyb20oZmlsZXMoc2VtYW50aWZvbGRLb3RsaW5TdGRsaWIpKVxcbiAgY29tcGlsZXJPcHRpb25zIHtcXG4gICAgYWxsV2FybmluZ3NBc0Vycm9ycy5zZXQodHJ1ZSlcXG4gICAgZnJlZUNvbXBpbGVyQXJncy5hZGRBbGwoXCItWG5vLWNhbGwtYXNzZXJ0aW9uc1wiLCBcIi1Ybm8tcGFyYW0tYXNzZXJ0aW9uc1wiLCBcIi1Ybm8tcmVjZWl2ZXItYXNzZXJ0aW9uc1wiKVxcbiAgICBqdm1UYXJnZXQuc2V0KEp2bVRhcmdldC5KVk1fMjEpXFxuICB9XFxufVxcblxcbmRlcGVuZGVuY2llcyB7XFxuICB0ZXN0SW1wbGVtZW50YXRpb24oXCJqdW5pdDpqdW5pdDo0LjEzLjJcIikge1xcbiAgICBpc1RyYW5zaXRpdmUgPSBmYWxzZVxcbiAgfVxcbn1cXG5gXG4gIF0pXG59XG5cbi8qKlxuICogUmVuZGVycyB0aGUgYWxsb3dsaXN0ZWQgQW5kcm9pZCBhcHBsaWNhdGlvbiBtYW5pZmVzdC5cbiAqIEBwYXJhbSB7aW1wb3J0KFwiLi4vc2VtYW50aWMvdHlwZXMuanNcIikuQW5kcm9pZEFwcGxpY2F0aW9uQ29uZmlndXJhdGlvbn0gY29uZmlndXJhdGlvbiAtIE5vcm1hbGl6ZWQgY29uZmlndXJhdGlvbi5cbiAqIEBwYXJhbSB7Q29uZmlndXJhdGlvblNwYW5bXX0gc3BhbnMgLSBDb2xsZWN0ZWQgY29uZmlndXJhdGlvbiBwcm92ZW5hbmNlIHNwYW5zLlxuICogQHJldHVybnMge3N0cmluZ30gQW5kcm9pZCBhcHBsaWNhdGlvbiBtYW5pZmVzdC5cbiAqL1xuZnVuY3Rpb24gcmVuZGVyQW5kcm9pZE1hbmlmZXN0KGNvbmZpZ3VyYXRpb24sIHNwYW5zKSB7XG4gIGNvbnN0IG9yaWVudGF0aW9uID0gY29uZmlndXJhdGlvbi5vcmllbnRhdGlvbiA9PSBcInVuc3BlY2lmaWVkXCIgPyBcIlwiIDogYCBhbmRyb2lkOnNjcmVlbk9yaWVudGF0aW9uPVwiJHtjb25maWd1cmF0aW9uLm9yaWVudGF0aW9ufVwiYFxuXG4gIHJldHVybiBjb25maWd1cmVkKGAke3Byb2plY3RSb290fS9hcHAvc3JjL21haW4vQW5kcm9pZE1hbmlmZXN0LnhtbGAsIHNwYW5zLCBbXG4gICAgYDw/eG1sIHZlcnNpb249XCIxLjBcIiBlbmNvZGluZz1cInV0Zi04XCI/PlxcbjxtYW5pZmVzdCB4bWxuczphbmRyb2lkPVwiaHR0cDovL3NjaGVtYXMuYW5kcm9pZC5jb20vYXBrL3Jlcy9hbmRyb2lkXCI+XFxuICA8YXBwbGljYXRpb25cXG4gICAgYW5kcm9pZDphbGxvd0JhY2t1cD1cImZhbHNlXCJcXG4gICAgYW5kcm9pZDpkYXRhRXh0cmFjdGlvblJ1bGVzPVwiQHhtbC9kYXRhX2V4dHJhY3Rpb25fcnVsZXNcIlxcbiAgICBhbmRyb2lkOmZ1bGxCYWNrdXBDb250ZW50PVwiQHhtbC9iYWNrdXBfcnVsZXNcIlxcbiAgICBhbmRyb2lkOmxhYmVsPVwiQHN0cmluZy9hcHBfbmFtZVwiXFxuICAgIGFuZHJvaWQ6c3VwcG9ydHNSdGw9XCJ0cnVlXCJcXG4gICAgYW5kcm9pZDp0aGVtZT1cIkBzdHlsZS9UaGVtZS5TZW1hbnRpZm9sZFwiPlxcbiAgICA8YWN0aXZpdHlcXG4gICAgICBhbmRyb2lkOm5hbWU9XCJgLFxuICAgIGZpZWxkKFwicGFja2FnZU5hbWVcIiwgY29uZmlndXJhdGlvbi5wYWNrYWdlTmFtZSksIGAuYCwgZmllbGQoXCJhY3Rpdml0eUNsYXNzTmFtZVwiLCBjb25maWd1cmF0aW9uLmFjdGl2aXR5Q2xhc3NOYW1lKSxcbiAgICBgXCJcXG4gICAgICBhbmRyb2lkOmV4cG9ydGVkPVwidHJ1ZVwiYCxcbiAgICBvcmllbnRhdGlvbiA/IGZpZWxkKFwib3JpZW50YXRpb25cIiwgb3JpZW50YXRpb24pIDogXCJcIiwgYD5cXG4gICAgICA8aW50ZW50LWZpbHRlcj5cXG4gICAgICAgIDxhY3Rpb24gYW5kcm9pZDpuYW1lPVwiYW5kcm9pZC5pbnRlbnQuYWN0aW9uLk1BSU5cIiAvPlxcbiAgICAgICAgPGNhdGVnb3J5IGFuZHJvaWQ6bmFtZT1cImFuZHJvaWQuaW50ZW50LmNhdGVnb3J5LkxBVU5DSEVSXCIgLz5cXG4gICAgICA8L2ludGVudC1maWx0ZXI+XFxuICAgIDwvYWN0aXZpdHk+XFxuICA8L2FwcGxpY2F0aW9uPlxcbjwvbWFuaWZlc3Q+XFxuYFxuICBdKVxufVxuXG4vKipcbiAqIFJlbmRlcnMgZXhwbGljaXQgQVBJIDIzLTMwIGV4Y2x1c2lvbnMgZm9yIGV2ZXJ5IGZ1bGwtYmFja3VwIHN0b3JhZ2UgZG9tYWluLlxuICogQHJldHVybnMge3N0cmluZ30gQW5kcm9pZCBmdWxsLWJhY2t1cCBydWxlcyBYTUwuXG4gKi9cbmZ1bmN0aW9uIHJlbmRlckJhY2t1cFJ1bGVzKCkge1xuICByZXR1cm4gYDw/eG1sIHZlcnNpb249XCIxLjBcIiBlbmNvZGluZz1cInV0Zi04XCI/PlxuPGZ1bGwtYmFja3VwLWNvbnRlbnQ+XG4gIDxleGNsdWRlIGRvbWFpbj1cInJvb3RcIiBwYXRoPVwiLlwiIC8+XG4gIDxleGNsdWRlIGRvbWFpbj1cImZpbGVcIiBwYXRoPVwiLlwiIC8+XG4gIDxleGNsdWRlIGRvbWFpbj1cImRhdGFiYXNlXCIgcGF0aD1cIi5cIiAvPlxuICA8ZXhjbHVkZSBkb21haW49XCJzaGFyZWRwcmVmXCIgcGF0aD1cIi5cIiAvPlxuICA8ZXhjbHVkZSBkb21haW49XCJleHRlcm5hbFwiIHBhdGg9XCIuXCIgLz5cbiAgPGV4Y2x1ZGUgZG9tYWluPVwiZGV2aWNlX3Jvb3RcIiBwYXRoPVwiLlwiIC8+XG4gIDxleGNsdWRlIGRvbWFpbj1cImRldmljZV9maWxlXCIgcGF0aD1cIi5cIiAvPlxuICA8ZXhjbHVkZSBkb21haW49XCJkZXZpY2VfZGF0YWJhc2VcIiBwYXRoPVwiLlwiIC8+XG4gIDxleGNsdWRlIGRvbWFpbj1cImRldmljZV9zaGFyZWRwcmVmXCIgcGF0aD1cIi5cIiAvPlxuPC9mdWxsLWJhY2t1cC1jb250ZW50PlxuYFxufVxuXG4vKipcbiAqIFJlbmRlcnMgZXhwbGljaXQgQW5kcm9pZCAxMisgZXhjbHVzaW9ucyBmb3IgZXZlcnkgY2xvdWQtYmFja3VwIGFuZCBkZXZpY2UtdHJhbnNmZXIgc3RvcmFnZSBkb21haW4uXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBBbmRyb2lkIGRhdGEgZXh0cmFjdGlvbiBydWxlcyBYTUwuXG4gKi9cbmZ1bmN0aW9uIHJlbmRlckRhdGFFeHRyYWN0aW9uUnVsZXMoKSB7XG4gIHJldHVybiBgPD94bWwgdmVyc2lvbj1cIjEuMFwiIGVuY29kaW5nPVwidXRmLThcIj8+XG48ZGF0YS1leHRyYWN0aW9uLXJ1bGVzPlxuICA8Y2xvdWQtYmFja3VwPlxuICAgIDxleGNsdWRlIGRvbWFpbj1cInJvb3RcIiBwYXRoPVwiLlwiIC8+XG4gICAgPGV4Y2x1ZGUgZG9tYWluPVwiZmlsZVwiIHBhdGg9XCIuXCIgLz5cbiAgICA8ZXhjbHVkZSBkb21haW49XCJkYXRhYmFzZVwiIHBhdGg9XCIuXCIgLz5cbiAgICA8ZXhjbHVkZSBkb21haW49XCJzaGFyZWRwcmVmXCIgcGF0aD1cIi5cIiAvPlxuICAgIDxleGNsdWRlIGRvbWFpbj1cImV4dGVybmFsXCIgcGF0aD1cIi5cIiAvPlxuICAgIDxleGNsdWRlIGRvbWFpbj1cImRldmljZV9yb290XCIgcGF0aD1cIi5cIiAvPlxuICAgIDxleGNsdWRlIGRvbWFpbj1cImRldmljZV9maWxlXCIgcGF0aD1cIi5cIiAvPlxuICAgIDxleGNsdWRlIGRvbWFpbj1cImRldmljZV9kYXRhYmFzZVwiIHBhdGg9XCIuXCIgLz5cbiAgICA8ZXhjbHVkZSBkb21haW49XCJkZXZpY2Vfc2hhcmVkcHJlZlwiIHBhdGg9XCIuXCIgLz5cbiAgPC9jbG91ZC1iYWNrdXA+XG4gIDxkZXZpY2UtdHJhbnNmZXI+XG4gICAgPGV4Y2x1ZGUgZG9tYWluPVwicm9vdFwiIHBhdGg9XCIuXCIgLz5cbiAgICA8ZXhjbHVkZSBkb21haW49XCJmaWxlXCIgcGF0aD1cIi5cIiAvPlxuICAgIDxleGNsdWRlIGRvbWFpbj1cImRhdGFiYXNlXCIgcGF0aD1cIi5cIiAvPlxuICAgIDxleGNsdWRlIGRvbWFpbj1cInNoYXJlZHByZWZcIiBwYXRoPVwiLlwiIC8+XG4gICAgPGV4Y2x1ZGUgZG9tYWluPVwiZXh0ZXJuYWxcIiBwYXRoPVwiLlwiIC8+XG4gICAgPGV4Y2x1ZGUgZG9tYWluPVwiZGV2aWNlX3Jvb3RcIiBwYXRoPVwiLlwiIC8+XG4gICAgPGV4Y2x1ZGUgZG9tYWluPVwiZGV2aWNlX2ZpbGVcIiBwYXRoPVwiLlwiIC8+XG4gICAgPGV4Y2x1ZGUgZG9tYWluPVwiZGV2aWNlX2RhdGFiYXNlXCIgcGF0aD1cIi5cIiAvPlxuICAgIDxleGNsdWRlIGRvbWFpbj1cImRldmljZV9zaGFyZWRwcmVmXCIgcGF0aD1cIi5cIiAvPlxuICA8L2RldmljZS10cmFuc2Zlcj5cbjwvZGF0YS1leHRyYWN0aW9uLXJ1bGVzPlxuYFxufVxuXG4vKipcbiAqIFJlbmRlcnMgdGhlIHBsYXRmb3JtLW9ubHkgaW5zdHJ1bWVudGF0aW9uIG1hbmlmZXN0LlxuICogQHBhcmFtIHtpbXBvcnQoXCIuLi9zZW1hbnRpYy90eXBlcy5qc1wiKS5BbmRyb2lkQXBwbGljYXRpb25Db25maWd1cmF0aW9ufSBjb25maWd1cmF0aW9uIC0gTm9ybWFsaXplZCBjb25maWd1cmF0aW9uLlxuICogQHBhcmFtIHtDb25maWd1cmF0aW9uU3BhbltdfSBzcGFucyAtIENvbGxlY3RlZCBjb25maWd1cmF0aW9uIHByb3ZlbmFuY2Ugc3BhbnMuXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBBbmRyb2lkIHRlc3QgbWFuaWZlc3QuXG4gKi9cbmZ1bmN0aW9uIHJlbmRlckFuZHJvaWRUZXN0TWFuaWZlc3QoY29uZmlndXJhdGlvbiwgc3BhbnMpIHtcbiAgcmV0dXJuIGNvbmZpZ3VyZWQoYCR7cHJvamVjdFJvb3R9L2FwcC9zcmMvYW5kcm9pZFRlc3QvQW5kcm9pZE1hbmlmZXN0LnhtbGAsIHNwYW5zLCBbXG4gICAgYDw/eG1sIHZlcnNpb249XCIxLjBcIiBlbmNvZGluZz1cInV0Zi04XCI/PlxcbjxtYW5pZmVzdCB4bWxuczphbmRyb2lkPVwiaHR0cDovL3NjaGVtYXMuYW5kcm9pZC5jb20vYXBrL3Jlcy9hbmRyb2lkXCI+XFxuICA8aW5zdHJ1bWVudGF0aW9uXFxuICAgIGFuZHJvaWQ6bmFtZT1cImAsXG4gICAgZmllbGQoXCJwYWNrYWdlTmFtZVwiLCBjb25maWd1cmF0aW9uLnBhY2thZ2VOYW1lKSwgYC5TZW1hbnRpZm9sZFVpSW5zdHJ1bWVudGF0aW9uXCJcXG4gICAgYW5kcm9pZDp0YXJnZXRQYWNrYWdlPVwiYCxcbiAgICBmaWVsZChcImFwcGxpY2F0aW9uSWRcIiwgeG1sRXNjYXBlKGNvbmZpZ3VyYXRpb24uYXBwbGljYXRpb25JZCkpLCBgXCIgLz5cXG48L21hbmlmZXN0PlxcbmBcbiAgXSlcbn1cblxuLyoqXG4gKiBSZW5kZXJzIHRoZSBzeW50aGV0aWMgbGF1bmNoZXIgYWN0aXZpdHkuXG4gKiBAcGFyYW0ge2ltcG9ydChcIi4uL3NlbWFudGljL3R5cGVzLmpzXCIpLkFuZHJvaWRBcHBsaWNhdGlvbkNvbmZpZ3VyYXRpb259IGNvbmZpZ3VyYXRpb24gLSBOb3JtYWxpemVkIGNvbmZpZ3VyYXRpb24uXG4gKiBAcGFyYW0ge3N0cmluZ30gZW50cnlNb2R1bGUgLSBTZWxlY3RlZCBzZW1hbnRpYyBlbnRyeSBtb2R1bGUgaWRlbnRpdHkuXG4gKiBAcGFyYW0ge0NvbmZpZ3VyYXRpb25TcGFuW119IHNwYW5zIC0gQ29sbGVjdGVkIGNvbmZpZ3VyYXRpb24gcHJvdmVuYW5jZSBzcGFucy5cbiAqIEByZXR1cm5zIHtzdHJpbmd9IEtvdGxpbiBsYXVuY2hlciBhY3Rpdml0eSBzb3VyY2UuXG4gKi9cbmZ1bmN0aW9uIHJlbmRlckFjdGl2aXR5KGNvbmZpZ3VyYXRpb24sIGVudHJ5TW9kdWxlLCBzcGFucykge1xuICBjb25zdCByZXNvdXJjZUltcG9ydCA9IGNvbmZpZ3VyYXRpb24ubmFtZXNwYWNlID09IGNvbmZpZ3VyYXRpb24ucGFja2FnZU5hbWVcbiAgICA/IFtdXG4gICAgOiBbXCJpbXBvcnQgXCIsIGZpZWxkKFwibmFtZXNwYWNlXCIsIGNvbmZpZ3VyYXRpb24ubmFtZXNwYWNlKSwgXCIuUlxcblwiXVxuXG4gIHJldHVybiBjb25maWd1cmVkKGAke3Byb2plY3RSb290fS9hcHAvc3JjL21haW4va290bGluLyR7Y29uZmlndXJhdGlvbi5wYWNrYWdlTmFtZS5yZXBsYWNlQWxsKFwiLlwiLCBcIi9cIil9LyR7Y29uZmlndXJhdGlvbi5hY3Rpdml0eUNsYXNzTmFtZX0ua3RgLCBzcGFucywgW1xuICAgIFwicGFja2FnZSBcIiwgZmllbGQoXCJwYWNrYWdlTmFtZVwiLCBjb25maWd1cmF0aW9uLnBhY2thZ2VOYW1lKSwgYFxuXG5pbXBvcnQgYW5kcm9pZC5hcHAuQWN0aXZpdHlcbmltcG9ydCBhbmRyb2lkLmFubm90YXRpb24uU3VwcHJlc3NMaW50XG5pbXBvcnQgYW5kcm9pZC5vcy5CdW5kbGVcbmltcG9ydCBhbmRyb2lkLndpZGdldC5UZXh0Vmlld1xuYCwgLi4ucmVzb3VyY2VJbXBvcnQsIGBpbXBvcnQgYCwgZmllbGQoXCJwYWNrYWdlTmFtZVwiLCBjb25maWd1cmF0aW9uLnBhY2thZ2VOYW1lKSxcbiAgICBgLnNlbWFudGljLlNlbWFudGlmb2xkTW9kdWxlJHttb2R1bGVOYW1lKGVudHJ5TW9kdWxlKX1cblxuY2xhc3MgYCwgZmllbGQoXCJhY3Rpdml0eUNsYXNzTmFtZVwiLCBjb25maWd1cmF0aW9uLmFjdGl2aXR5Q2xhc3NOYW1lKSwgYCA6IEFjdGl2aXR5KCkge1xuICBAU3VwcHJlc3NMaW50KFwiU2V0VGV4dEkxOG5cIilcbiAgb3ZlcnJpZGUgZnVuIG9uQ3JlYXRlKHNhdmVkSW5zdGFuY2VTdGF0ZTogQnVuZGxlPykge1xuICAgIHN1cGVyLm9uQ3JlYXRlKHNhdmVkSW5zdGFuY2VTdGF0ZSlcbiAgICBzZXRDb250ZW50VmlldyhSLmxheW91dC5hY3Rpdml0eV9tYWluKVxuICAgIHZhbCBsaW5lcyA9IFNlbWFudGlmb2xkTW9kdWxlJHttb2R1bGVOYW1lKGVudHJ5TW9kdWxlKX0uc2VtYW50aWZvbGRFbnRyeSgpXG4gICAgdmFsIG91dHB1dCA9IGphdmEubGFuZy5TdHJpbmdCdWlsZGVyKClcbiAgICB2YXIgaW5kZXggPSAwXG4gICAgd2hpbGUgKGluZGV4IDwgbGluZXMuc2l6ZSkge1xuICAgICAgaWYgKGluZGV4ID4gMCkgb3V0cHV0LmFwcGVuZCgnXFxcXG4nKVxuICAgICAgb3V0cHV0LmFwcGVuZChsaW5lc1tpbmRleF0pXG4gICAgICBpbmRleCArPSAxXG4gICAgfVxuICAgIGZpbmRWaWV3QnlJZDxUZXh0Vmlldz4oUi5pZC5zZW1hbnRpZm9sZF9vdXRwdXQpLnRleHQgPSBvdXRwdXQudG9TdHJpbmcoKVxuICB9XG59XG5gXG4gIF0pXG59XG5cbi8qKlxuICogUmVuZGVycyB0aGUgc2luZ2xlIGFjY2Vzc2liaWxpdHktaW1wb3J0YW50IG5hdGl2ZSB0ZXh0IHZpZXcuXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBBbmRyb2lkIGxheW91dCBYTUwuXG4gKi9cbmZ1bmN0aW9uIHJlbmRlckxheW91dCgpIHtcbiAgcmV0dXJuIGA8P3htbCB2ZXJzaW9uPVwiMS4wXCIgZW5jb2Rpbmc9XCJ1dGYtOFwiPz5cbjxUZXh0VmlldyB4bWxuczphbmRyb2lkPVwiaHR0cDovL3NjaGVtYXMuYW5kcm9pZC5jb20vYXBrL3Jlcy9hbmRyb2lkXCJcbiAgYW5kcm9pZDppZD1cIkAraWQvc2VtYW50aWZvbGRfb3V0cHV0XCJcbiAgYW5kcm9pZDpsYXlvdXRfd2lkdGg9XCJtYXRjaF9wYXJlbnRcIlxuICBhbmRyb2lkOmxheW91dF9oZWlnaHQ9XCJtYXRjaF9wYXJlbnRcIlxuICBhbmRyb2lkOmdyYXZpdHk9XCJjZW50ZXJcIlxuICBhbmRyb2lkOmltcG9ydGFudEZvckFjY2Vzc2liaWxpdHk9XCJ5ZXNcIlxuICBhbmRyb2lkOnRleHRJc1NlbGVjdGFibGU9XCJ0cnVlXCIgLz5cbmBcbn1cblxuLyoqXG4gKiBSZW5kZXJzIGNvbmZpZ3VyYXRpb24tZGVyaXZlZCBzdHJpbmcgcmVzb3VyY2VzLlxuICogQHBhcmFtIHtpbXBvcnQoXCIuLi9zZW1hbnRpYy90eXBlcy5qc1wiKS5BbmRyb2lkQXBwbGljYXRpb25Db25maWd1cmF0aW9ufSBjb25maWd1cmF0aW9uIC0gTm9ybWFsaXplZCBjb25maWd1cmF0aW9uLlxuICogQHBhcmFtIHtDb25maWd1cmF0aW9uU3BhbltdfSBzcGFucyAtIENvbGxlY3RlZCBjb25maWd1cmF0aW9uIHByb3ZlbmFuY2Ugc3BhbnMuXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBBbmRyb2lkIHN0cmluZyByZXNvdXJjZSBYTUwuXG4gKi9cbmZ1bmN0aW9uIHJlbmRlclN0cmluZ3MoY29uZmlndXJhdGlvbiwgc3BhbnMpIHtcbiAgcmV0dXJuIGNvbmZpZ3VyZWQoYCR7cHJvamVjdFJvb3R9L2FwcC9zcmMvbWFpbi9yZXMvdmFsdWVzL3N0cmluZ3MueG1sYCwgc3BhbnMsIFtcbiAgICBgPD94bWwgdmVyc2lvbj1cIjEuMFwiIGVuY29kaW5nPVwidXRmLThcIj8+XFxuPHJlc291cmNlcz5cXG4gIDxzdHJpbmcgbmFtZT1cImFwcF9uYW1lXCI+YCxcbiAgICBmaWVsZChcImRpc3BsYXlOYW1lXCIsIHhtbEVzY2FwZShjb25maWd1cmF0aW9uLmRpc3BsYXlOYW1lKSksIGA8L3N0cmluZz5cXG48L3Jlc291cmNlcz5cXG5gXG4gIF0pXG59XG5cbi8qKlxuICogUmVuZGVycyB0aGUgZml4ZWQgcGxhdGZvcm0tbmF0aXZlIGJhc2VsaW5lIHRoZW1lLlxuICogQHJldHVybnMge3N0cmluZ30gQW5kcm9pZCB0aGVtZSByZXNvdXJjZSBYTUwuXG4gKi9cbmZ1bmN0aW9uIHJlbmRlclRoZW1lKCkge1xuICByZXR1cm4gYDw/eG1sIHZlcnNpb249XCIxLjBcIiBlbmNvZGluZz1cInV0Zi04XCI/PlxuPHJlc291cmNlcz5cbiAgPHN0eWxlIG5hbWU9XCJUaGVtZS5TZW1hbnRpZm9sZFwiIHBhcmVudD1cImFuZHJvaWQ6c3R5bGUvVGhlbWUuTWF0ZXJpYWwuTGlnaHQuTm9BY3Rpb25CYXJcIj5cbiAgICA8aXRlbSBuYW1lPVwiYW5kcm9pZDpmb250RmFtaWx5XCI+c2FuczwvaXRlbT5cbiAgICA8aXRlbSBuYW1lPVwiYW5kcm9pZDp3aW5kb3dBY3Rpb25Nb2RlT3ZlcmxheVwiPnRydWU8L2l0ZW0+XG4gIDwvc3R5bGU+XG48L3Jlc291cmNlcz5cbmBcbn1cblxuLyoqXG4gKiBSZW5kZXJzIHRoZSBzb2xlIEpVbml0IHRlc3Qtc2NvcGUgc2VtYW50aWMgYXNzZXJ0aW9uLlxuICogQHBhcmFtIHtpbXBvcnQoXCIuLi9zZW1hbnRpYy90eXBlcy5qc1wiKS5BbmRyb2lkQXBwbGljYXRpb25Db25maWd1cmF0aW9ufSBjb25maWd1cmF0aW9uIC0gTm9ybWFsaXplZCBjb25maWd1cmF0aW9uLlxuICogQHBhcmFtIHtzdHJpbmd9IGVudHJ5TW9kdWxlIC0gU2VsZWN0ZWQgc2VtYW50aWMgZW50cnkgbW9kdWxlIGlkZW50aXR5LlxuICogQHBhcmFtIHtDb25maWd1cmF0aW9uU3BhbltdfSBzcGFucyAtIENvbGxlY3RlZCBjb25maWd1cmF0aW9uIHByb3ZlbmFuY2Ugc3BhbnMuXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBLb3RsaW4gdW5pdCB0ZXN0IHNvdXJjZS5cbiAqL1xuZnVuY3Rpb24gcmVuZGVyVW5pdFRlc3QoY29uZmlndXJhdGlvbiwgZW50cnlNb2R1bGUsIHNwYW5zKSB7XG4gIHJldHVybiBjb25maWd1cmVkKGAke3Byb2plY3RSb290fS9hcHAvc3JjL3Rlc3Qva290bGluLyR7Y29uZmlndXJhdGlvbi5wYWNrYWdlTmFtZS5yZXBsYWNlQWxsKFwiLlwiLCBcIi9cIil9L1NlbWFudGlmb2xkRW50cnlUZXN0Lmt0YCwgc3BhbnMsIFtcbiAgICBcInBhY2thZ2UgXCIsIGZpZWxkKFwicGFja2FnZU5hbWVcIiwgY29uZmlndXJhdGlvbi5wYWNrYWdlTmFtZSksIGBcblxuaW1wb3J0IGAsIGZpZWxkKFwicGFja2FnZU5hbWVcIiwgY29uZmlndXJhdGlvbi5wYWNrYWdlTmFtZSksIGAuc2VtYW50aWMuU2VtYW50aWZvbGRNb2R1bGUke21vZHVsZU5hbWUoZW50cnlNb2R1bGUpfVxuaW1wb3J0IG9yZy5qdW5pdC5UZXN0XG5cbmNsYXNzIFNlbWFudGlmb2xkRW50cnlUZXN0IHtcbiAgQFRlc3RcbiAgZnVuIGVudHJ5SXNEZXRlcm1pbmlzdGljQWNyb3NzRnJlc2hSdW5zKCkge1xuICAgIHZhbCBmaXJzdE91dHB1dCA9IFNlbWFudGlmb2xkTW9kdWxlJHttb2R1bGVOYW1lKGVudHJ5TW9kdWxlKX0uc2VtYW50aWZvbGRFbnRyeSgpXG4gICAgdmFsIHNlY29uZE91dHB1dCA9IFNlbWFudGlmb2xkTW9kdWxlJHttb2R1bGVOYW1lKGVudHJ5TW9kdWxlKX0uc2VtYW50aWZvbGRFbnRyeSgpXG4gICAgaWYgKCFmaXJzdE91dHB1dC5lcXVhbHMoc2Vjb25kT3V0cHV0KSkge1xuICAgICAgdGhyb3cgQXNzZXJ0aW9uRXJyb3IoXCJTZW1hbnRpYyBlbnRyeSBvdXRwdXQgZGlmZmVycyBhY3Jvc3MgZnJlc2ggcnVucy5cIilcbiAgICB9XG4gIH1cbn1cbmBcbiAgXSlcbn1cblxuLyoqXG4gKiBSZW5kZXJzIHRoZSBmcmFtZXdvcmstb25seSBpbnN0cnVtZW50YXRpb24gcnVubmVyLlxuICogQHBhcmFtIHtpbXBvcnQoXCIuLi9zZW1hbnRpYy90eXBlcy5qc1wiKS5BbmRyb2lkQXBwbGljYXRpb25Db25maWd1cmF0aW9ufSBjb25maWd1cmF0aW9uIC0gTm9ybWFsaXplZCBjb25maWd1cmF0aW9uLlxuICogQHBhcmFtIHtDb25maWd1cmF0aW9uU3BhbltdfSBzcGFucyAtIENvbGxlY3RlZCBjb25maWd1cmF0aW9uIHByb3ZlbmFuY2Ugc3BhbnMuXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBLb3RsaW4gaW5zdHJ1bWVudGF0aW9uIHNvdXJjZS5cbiAqL1xuZnVuY3Rpb24gcmVuZGVySW5zdHJ1bWVudGF0aW9uKGNvbmZpZ3VyYXRpb24sIHNwYW5zKSB7XG4gIGNvbnN0IHJlc291cmNlSW1wb3J0ID0gY29uZmlndXJhdGlvbi5uYW1lc3BhY2UgPT0gY29uZmlndXJhdGlvbi5wYWNrYWdlTmFtZVxuICAgID8gW11cbiAgICA6IFtcImltcG9ydCBcIiwgZmllbGQoXCJuYW1lc3BhY2VcIiwgY29uZmlndXJhdGlvbi5uYW1lc3BhY2UpLCBcIi5SXFxuXCJdXG5cbiAgcmV0dXJuIGNvbmZpZ3VyZWQoYCR7cHJvamVjdFJvb3R9L2FwcC9zcmMvYW5kcm9pZFRlc3Qva290bGluLyR7Y29uZmlndXJhdGlvbi5wYWNrYWdlTmFtZS5yZXBsYWNlQWxsKFwiLlwiLCBcIi9cIil9L1NlbWFudGlmb2xkVWlJbnN0cnVtZW50YXRpb24ua3RgLCBzcGFucywgW1xuICAgIFwicGFja2FnZSBcIiwgZmllbGQoXCJwYWNrYWdlTmFtZVwiLCBjb25maWd1cmF0aW9uLnBhY2thZ2VOYW1lKSwgYFxuXG5pbXBvcnQgYW5kcm9pZC5hcHAuQWN0aXZpdHlcbmltcG9ydCBhbmRyb2lkLmFwcC5JbnN0cnVtZW50YXRpb25cbmltcG9ydCBhbmRyb2lkLmNvbnRlbnQuSW50ZW50XG5pbXBvcnQgYW5kcm9pZC5vcy5CdW5kbGVcbmltcG9ydCBhbmRyb2lkLnV0aWwuQmFzZTY0XG5pbXBvcnQgYW5kcm9pZC52aWV3LlZpZXdcbmltcG9ydCBhbmRyb2lkLndpZGdldC5UZXh0Vmlld1xuaW1wb3J0IGphdmEubmlvLmNoYXJzZXQuU3RhbmRhcmRDaGFyc2V0c1xuYCwgLi4ucmVzb3VyY2VJbXBvcnQsIGBcbmNsYXNzIFNlbWFudGlmb2xkVWlJbnN0cnVtZW50YXRpb24gOiBJbnN0cnVtZW50YXRpb24oKSB7XG4gIHByaXZhdGUgdmFyIGlucHV0QXJndW1lbnRzOiBCdW5kbGU/ID0gbnVsbFxuXG4gIG92ZXJyaWRlIGZ1biBvbkNyZWF0ZShhcmd1bWVudHM6IEJ1bmRsZT8pIHtcbiAgICBpbnB1dEFyZ3VtZW50cyA9IGFyZ3VtZW50c1xuICAgIHN0YXJ0KClcbiAgfVxuXG4gIG92ZXJyaWRlIGZ1biBvblN0YXJ0KCkge1xuICAgIHRyeSB7XG4gICAgICB2YWwgcHJvdmlkZWRBcmd1bWVudHMgPSBpbnB1dEFyZ3VtZW50c1xuICAgICAgICA/OiB0aHJvdyBJbGxlZ2FsU3RhdGVFeGNlcHRpb24oXCJtaXNzaW5nIGluc3RydW1lbnRhdGlvbiBhcmd1bWVudHNcIilcbiAgICAgIHZhbCBlbmNvZGVkID0gcHJvdmlkZWRBcmd1bWVudHMuZ2V0U3RyaW5nKFwiZXhwZWN0ZWRfb3V0cHV0X2Jhc2U2NFwiKVxuICAgICAgICA/OiB0aHJvdyBJbGxlZ2FsU3RhdGVFeGNlcHRpb24oXCJtaXNzaW5nIGV4cGVjdGVkX291dHB1dF9iYXNlNjQgaW5zdHJ1bWVudGF0aW9uIGFyZ3VtZW50XCIpXG4gICAgICB2YWwgZXhwZWN0ZWQgPSBTdHJpbmcoQmFzZTY0LmRlY29kZShlbmNvZGVkLCBCYXNlNjQuTk9fV1JBUCksIFN0YW5kYXJkQ2hhcnNldHMuVVRGXzgpXG4gICAgICB2YWwgbGF1bmNoID0gdGFyZ2V0Q29udGV4dC5wYWNrYWdlTWFuYWdlci5nZXRMYXVuY2hJbnRlbnRGb3JQYWNrYWdlKHRhcmdldENvbnRleHQucGFja2FnZU5hbWUpXG4gICAgICAgID86IHRocm93IElsbGVnYWxTdGF0ZUV4Y2VwdGlvbihcImdlbmVyYXRlZCBhcHBsaWNhdGlvbiBoYXMgbm8gbGF1bmNoZXIgaW50ZW50XCIpXG4gICAgICBsYXVuY2guYWRkRmxhZ3MoSW50ZW50LkZMQUdfQUNUSVZJVFlfTkVXX1RBU0sgb3IgSW50ZW50LkZMQUdfQUNUSVZJVFlfQ0xFQVJfVEFTSylcbiAgICAgIHZhbCBhY3Rpdml0eSA9IHN0YXJ0QWN0aXZpdHlTeW5jKGxhdW5jaCkgYXMgYCwgZmllbGQoXCJhY3Rpdml0eUNsYXNzTmFtZVwiLCBjb25maWd1cmF0aW9uLmFjdGl2aXR5Q2xhc3NOYW1lKSwgYFxuICAgICAgYXNzZXJ0T3V0cHV0KGFjdGl2aXR5LCBleHBlY3RlZClcbiAgICAgIHZhbCBtb25pdG9yID0gYWRkTW9uaXRvcihgLCBmaWVsZChcImFjdGl2aXR5Q2xhc3NOYW1lXCIsIGNvbmZpZ3VyYXRpb24uYWN0aXZpdHlDbGFzc05hbWUpLCBgOjpjbGFzcy5qYXZhLm5hbWUsIG51bGwsIGZhbHNlKVxuICAgICAgcnVuT25NYWluU3luYyB7IGFjdGl2aXR5LnJlY3JlYXRlKCkgfVxuICAgICAgd2FpdEZvcklkbGVTeW5jKClcbiAgICAgIHZhbCByZWNyZWF0ZWQgPSB3YWl0Rm9yTW9uaXRvcldpdGhUaW1lb3V0KG1vbml0b3IsIDEwXzAwMClcbiAgICAgICAgPzogdGhyb3cgSWxsZWdhbFN0YXRlRXhjZXB0aW9uKFwiYWN0aXZpdHkgd2FzIG5vdCByZWNyZWF0ZWRcIilcbiAgICAgIGFzc2VydE91dHB1dChyZWNyZWF0ZWQsIGV4cGVjdGVkKVxuICAgICAgZmluaXNoKEFjdGl2aXR5LlJFU1VMVF9PSywgQnVuZGxlKCkpXG4gICAgfSBjYXRjaCAoZXJyb3I6IFRocm93YWJsZSkge1xuICAgICAgdmFsIHJlc3VsdCA9IEJ1bmRsZSgpXG4gICAgICByZXN1bHQucHV0U3RyaW5nKFwic2VtYW50aWZvbGRfZmFpbHVyZVwiLCBhbmRyb2lkLnV0aWwuTG9nLmdldFN0YWNrVHJhY2VTdHJpbmcoZXJyb3IpKVxuICAgICAgZmluaXNoKEFjdGl2aXR5LlJFU1VMVF9DQU5DRUxFRCwgcmVzdWx0KVxuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgZnVuIGFzc2VydE91dHB1dChhY3Rpdml0eTogQWN0aXZpdHksIGV4cGVjdGVkOiBTdHJpbmcpIHtcbiAgICB3YWl0Rm9ySWRsZVN5bmMoKVxuICAgIHZhbCBhY3R1YWwgPSBhY3Rpdml0eS5maW5kVmlld0J5SWQ8VGV4dFZpZXc+KFIuaWQuc2VtYW50aWZvbGRfb3V0cHV0KVxuICAgICAgPzogdGhyb3cgSWxsZWdhbFN0YXRlRXhjZXB0aW9uKFwibWlzc2luZyBzZW1hbnRpZm9sZF9vdXRwdXQgdmlld1wiKVxuICAgIGlmIChhY3R1YWwudmlzaWJpbGl0eSAhPSBWaWV3LlZJU0lCTEUpIHRocm93IElsbGVnYWxTdGF0ZUV4Y2VwdGlvbihcIm91dHB1dCB2aWV3IGlzIG5vdCB2aXNpYmxlXCIpXG4gICAgaWYgKGFjdHVhbC5qYXZhQ2xhc3MgIT0gVGV4dFZpZXc6OmNsYXNzLmphdmEpIHRocm93IElsbGVnYWxTdGF0ZUV4Y2VwdGlvbihcIm91dHB1dCB2aWV3IGlzIG5vdCBhIG5hdGl2ZSBUZXh0Vmlld1wiKVxuICAgIGlmICghYWN0dWFsLnRleHQudG9TdHJpbmcoKS5lcXVhbHMoZXhwZWN0ZWQpKSB0aHJvdyBJbGxlZ2FsU3RhdGVFeGNlcHRpb24oXCJvdXRwdXQgdGV4dCBtaXNtYXRjaFwiKVxuICAgIGlmIChhY3R1YWwuY29udGVudERlc2NyaXB0aW9uICE9IG51bGwpIHRocm93IElsbGVnYWxTdGF0ZUV4Y2VwdGlvbihcIm91dHB1dCBjb250ZW50IGRlc2NyaXB0aW9uIG11c3Qgc3RheSBhYnNlbnRcIilcbiAgICBpZiAoYWN0dWFsLmltcG9ydGFudEZvckFjY2Vzc2liaWxpdHkgIT0gVmlldy5JTVBPUlRBTlRfRk9SX0FDQ0VTU0lCSUxJVFlfWUVTKSB7XG4gICAgICB0aHJvdyBJbGxlZ2FsU3RhdGVFeGNlcHRpb24oXCJvdXRwdXQgdmlldyBpcyBub3QgYWNjZXNzaWJpbGl0eS1pbXBvcnRhbnRcIilcbiAgICB9XG4gIH1cbn1cbmBcbiAgXSlcbn1cblxuLyoqXG4gKiBSZW5kZXJzIHRoZSBvd25lcnNoaXAsIHRvb2xjaGFpbiwgYW5kIHByb3ZlbmFuY2UgbWFuaWZlc3QuXG4gKiBAcGFyYW0ge1JldHVyblR5cGU8dHlwZW9mIHByZWZsaWdodEFuZHJvaWRBcHBsaWNhdGlvbj59IHByZXBhcmVkIC0gUHJlcGFyZWQgYXBwbGljYXRpb24uXG4gKiBAcGFyYW0ge2ltcG9ydChcIi4uL3NlbWFudGljL3R5cGVzLmpzXCIpLkdlbmVyYXRlZFNldEFydGlmYWN0W119IGFydGlmYWN0cyAtIE93bmVkIHByb2plY3QgYXJ0aWZhY3RzLlxuICogQHBhcmFtIHtzdHJpbmd9IG1hbmlmZXN0UGF0aCAtIE93bmVyc2hpcCBtYW5pZmVzdCBwYXRoLlxuICogQHBhcmFtIHtDb25maWd1cmF0aW9uU3BhbltdfSBzcGFucyAtIENvbmZpZ3VyYXRpb24gcHJvdmVuYW5jZSBzcGFucy5cbiAqIEByZXR1cm5zIHtzdHJpbmd9IENhbm9uaWNhbCBwcm9qZWN0IG1hbmlmZXN0IEpTT04uXG4gKi9cbmZ1bmN0aW9uIHJlbmRlclByb2plY3RNYW5pZmVzdChwcmVwYXJlZCwgYXJ0aWZhY3RzLCBtYW5pZmVzdFBhdGgsIHNwYW5zKSB7XG4gIGNvbnN0IGlucHV0UGF0aHMgPSBuZXcgU2V0KFsuLi5wcmVwYXJlZC5yZXNvdXJjZXMsIC4uLnByZXBhcmVkLmFzc2V0c10ubWFwKCh7ZnVsbFBhdGh9KSA9PiBmdWxsUGF0aCkpXG4gIGNvbnN0IHNlbWFudGljQXJ0aWZhY3RzID0gYXJ0aWZhY3RzLmZpbHRlcihhcnRpZmFjdCA9PiBhcnRpZmFjdC5wcm92ZW5hbmNlLmtpbmQgPT0gXCJ0ZXh0XCIpXG4gIGNvbnN0IG93bmVkUGF0aHMgPSBbLi4uYXJ0aWZhY3RzLm1hcCgoe3BhdGh9KSA9PiBwYXRoKSwgbWFuaWZlc3RQYXRoXVxuXG4gIHJldHVybiBzdHJpbmdpZnlDYW5vbmljYWxKc29uKHtcbiAgICBjb25maWd1cmF0aW9uOiBwcmVwYXJlZC5jb25maWd1cmF0aW9uLFxuICAgIGdlbmVyYXRvcjoge25hbWU6IFwic2VtYW50aWZvbGRcIiwgdmVyc2lvbjogZ2VuZXJhdG9yVmVyc2lvbn0sXG4gICAgb3duZXJzaGlwOiB7XG4gICAgICBleGNsdWRlZFBhdGhQYXR0ZXJucyxcbiAgICAgIG93bmVkUGF0aHMsXG4gICAgICBvd25lZFJvb3Q6IHByb2plY3RSb290LFxuICAgICAgc2hhMjU2OiBhcnRpZmFjdHMubWFwKGFydGlmYWN0ID0+ICh7cGF0aDogYXJ0aWZhY3QucGF0aCwgc2hhMjU2OiBoYXNoQ29udGVudChhcnRpZmFjdC5jb250ZW50KX0pKVxuICAgIH0sXG4gICAgcHJvdmVuYW5jZToge1xuICAgICAgY29uZmlndXJhdGlvbjogT2JqZWN0LmtleXMocHJlcGFyZWQuY29uZmlndXJhdGlvbikuc29ydCgpLm1hcChmaWVsZE5hbWUgPT4gKHtcbiAgICAgICAgZmllbGQ6IGZpZWxkTmFtZSxcbiAgICAgICAgbWFuaWZlc3RQb2ludGVyOiBgL2NvbmZpZ3VyYXRpb24vJHtmaWVsZE5hbWV9YCxcbiAgICAgICAgb3V0cHV0czogY29uZmlndXJhdGlvbk91dHB1dHMoc3BhbnMsIGZpZWxkTmFtZSlcbiAgICAgIH0pKSxcbiAgICAgIGlucHV0czogWy4uLnByZXBhcmVkLnJlc291cmNlcywgLi4ucHJlcGFyZWQuYXNzZXRzXS5tYXAoaXRlbSA9PiAoe1xuICAgICAgICBjb250ZW50S2luZDogaXRlbS5jb250ZW50S2luZCxcbiAgICAgICAgbWVkaWFUeXBlOiBpdGVtLm1lZGlhVHlwZSxcbiAgICAgICAgcGF0aDogaXRlbS5mdWxsUGF0aCxcbiAgICAgICAgc2hhMjU2OiBpdGVtLnNoYTI1NlxuICAgICAgfSkpLFxuICAgICAgc2VtYW50aWNTb3VyY2VzOiBwcmVwYXJlZC5zb3VyY2VzLm1hcChzb3VyY2UgPT4gKHtcbiAgICAgICAgYXJ0aWZhY3RzOiBzZW1hbnRpY0FydGlmYWN0cy5maWx0ZXIoYXJ0aWZhY3QgPT4gYXJ0aWZhY3QucHJvdmVuYW5jZS5raW5kID09IFwidGV4dFwiICYmXG4gICAgICAgICAgYXJ0aWZhY3QucHJvdmVuYW5jZS5tYXBwaW5nLnNwYW5zLnNvbWUoKHtvcmlnaW59KSA9PiBvcmlnaW5SZWZlcmVuY2VzRmlsZW5hbWUob3JpZ2luLCBzb3VyY2UuZmlsZW5hbWUpKSlcbiAgICAgICAgICAubWFwKCh7cGF0aH0pID0+IHBhdGgpLFxuICAgICAgICBmaWxlbmFtZTogc291cmNlLmZpbGVuYW1lLFxuICAgICAgICAuLi4oc291cmNlLmxhbmd1YWdlID09PSB1bmRlZmluZWQgPyB7fSA6IHtsYW5ndWFnZTogc291cmNlLmxhbmd1YWdlfSksXG4gICAgICAgIHNoYTI1NjogY3JlYXRlSGFzaChcInNoYTI1NlwiKS51cGRhdGUoc291cmNlLmNvbnRlbnQpLmRpZ2VzdChcImhleFwiKVxuICAgICAgfSkpLFxuICAgICAgc3ludGhldGljU2NhZmZvbGRpbmc6IFsuLi5hcnRpZmFjdHMuZmlsdGVyKGFydGlmYWN0ID0+IGFydGlmYWN0LnByb3ZlbmFuY2Uua2luZCA9PSBcInN5bnRoZXRpY1wiICYmXG4gICAgICAgICFpbnB1dFBhdGhzLmhhcyhhcnRpZmFjdC5wYXRoKSkubWFwKCh7cGF0aH0pID0+IHBhdGgpLCBtYW5pZmVzdFBhdGhdXG4gICAgfSxcbiAgICBzY2hlbWE6IFwiU2VtYW50aWZvbGRBbmRyb2lkUHJvamVjdFwiLFxuICAgIHNlbWFudGljUHJvZ3JhbTogc2VtYW50aWNQcm9ncmFtTWFuaWZlc3QocHJlcGFyZWQpLFxuICAgIHRhcmdldDogXCJhbmRyb2lkXCIsXG4gICAgdG9vbGNoYWluLFxuICAgIHZlcnNpb246IDFcbiAgfSlcbn1cblxuLyoqXG4gKiBSZW5kZXJzIHNlbWFudGljIG1vZHVsZSBhbmQgc291cmNlIGlkZW50aXRpZXMgZm9yIHRoZSBwcm9qZWN0IG1hbmlmZXN0LlxuICogQHBhcmFtIHtSZXR1cm5UeXBlPHR5cGVvZiBwcmVmbGlnaHRBbmRyb2lkQXBwbGljYXRpb24+fSBwcmVwYXJlZCAtIFByZXBhcmVkIGFwcGxpY2F0aW9uLlxuICogQHJldHVybnMge1JlY29yZDxzdHJpbmcsIHVua25vd24+fSBTZW1hbnRpYyBwcm9ncmFtIG1hbmlmZXN0LlxuICovXG5mdW5jdGlvbiBzZW1hbnRpY1Byb2dyYW1NYW5pZmVzdChwcmVwYXJlZCkge1xuICBjb25zdCBzb3VyY2VDb250ZW50ID0gbmV3IE1hcChwcmVwYXJlZC5zb3VyY2VzLm1hcChzb3VyY2UgPT4gW3NvdXJjZS5maWxlbmFtZSwgc291cmNlLmNvbnRlbnRdKSlcbiAgY29uc3Qgb3JkZXIgPSBuZXcgTWFwKHByZXBhcmVkLnByb2dyYW0ubW9kdWxlcy5tYXAoKG1vZHVsZSwgaW5kZXgpID0+IFttb2R1bGUuaWQsIGluZGV4XSkpXG4gIGNvbnN0IG1vZHVsZXMgPSBwcmVwYXJlZC5wcm9ncmFtLm1vZHVsZXMubWFwKG1vZHVsZSA9PiB7XG4gICAgY29uc3Qgc291cmNlID0gcHJlcGFyZWQucHJvZ3JhbS5zb3VyY2VzLmZpbmQoY2FuZGlkYXRlID0+IGNhbmRpZGF0ZS5maWxlbmFtZSA9PSBtb2R1bGUuc291cmNlRmlsZW5hbWUpXG4gICAgY29uc3QgY29udGVudCA9IHNvdXJjZUNvbnRlbnQuZ2V0KG1vZHVsZS5zb3VyY2VGaWxlbmFtZSlcblxuICAgIGlmICghc291cmNlIHx8IGNvbnRlbnQgPT09IHVuZGVmaW5lZCkgdGhyb3cgbmV3IFR5cGVFcnJvcihgUHJlZmxpZ2h0ZWQgQW5kcm9pZCBtb2R1bGUgJyR7bW9kdWxlLmlkfScgbG9zdCBzb3VyY2Ugb3duZXJzaGlwLmApXG5cbiAgICByZXR1cm4ge1xuICAgICAgZGVwZW5kZW5jaWVzOiBbLi4ubmV3IFNldChtb2R1bGUuaW1wb3J0cy5tYXAoKHttb2R1bGVJZH0pID0+IG1vZHVsZUlkKSldXG4gICAgICAgIC5zb3J0KChsZWZ0LCByaWdodCkgPT4gLyoqIEB0eXBlIHtudW1iZXJ9ICovIChvcmRlci5nZXQobGVmdCkpIC0gLyoqIEB0eXBlIHtudW1iZXJ9ICovIChvcmRlci5nZXQocmlnaHQpKSksXG4gICAgICBnZW5lcmF0ZWRBcnRpZmFjdHM6IFtwcmVwYXJlZC5tb2R1bGVQYXRocy5nZXQobW9kdWxlLmlkKV0sXG4gICAgICBpZDogbW9kdWxlLmlkLFxuICAgICAgbmFtZXNwYWNlOiBgU2VtYW50aWZvbGRNb2R1bGUke21vZHVsZU5hbWUobW9kdWxlLmlkKX1gLFxuICAgICAgc291cmNlOiB7ZmlsZW5hbWU6IHNvdXJjZS5maWxlbmFtZSwgaWQ6IHNvdXJjZS5pZCwgc2hhMjU2OiBjcmVhdGVIYXNoKFwic2hhMjU2XCIpLnVwZGF0ZShjb250ZW50KS5kaWdlc3QoXCJoZXhcIil9XG4gICAgfVxuICB9KVxuICBjb25zdCBlbnRyeUlkID0gcHJlcGFyZWQucHJvZ3JhbS5lbnRyeU1vZHVsZVxuXG4gIHJldHVybiB7XG4gICAgZW50cnk6IHtcbiAgICAgIGFjdGl2aXR5QXJ0aWZhY3Q6IGAke3Byb2plY3RSb290fS9hcHAvc3JjL21haW4va290bGluLyR7cHJlcGFyZWQucGFja2FnZVBhdGh9LyR7cHJlcGFyZWQuY29uZmlndXJhdGlvbi5hY3Rpdml0eUNsYXNzTmFtZX0ua3RgLFxuICAgICAgZ2VuZXJhdGVkQXJ0aWZhY3Q6IHByZXBhcmVkLm1vZHVsZVBhdGhzLmdldChlbnRyeUlkKSxcbiAgICAgIGdlbmVyYXRlZEZ1bmN0aW9uSWRlbnRpdHk6IGBTZW1hbnRpZm9sZE1vZHVsZSR7bW9kdWxlTmFtZShlbnRyeUlkKX0uc2VtYW50aWZvbGRFbnRyeWAsXG4gICAgICBtb2R1bGVJZDogZW50cnlJZFxuICAgIH0sXG4gICAgbW9kdWxlcyxcbiAgICBzY2hlbWE6IFwiU2VtYW50aWZvbGRBbmRyb2lkU2VtYW50aWNQcm9ncmFtXCIsXG4gICAgc291cmNlczogcHJlcGFyZWQucHJvZ3JhbS5zb3VyY2VzLm1hcChzb3VyY2UgPT4gKHtcbiAgICAgIGZpbGVuYW1lOiBzb3VyY2UuZmlsZW5hbWUsXG4gICAgICBpZDogc291cmNlLmlkLFxuICAgICAgbGFuZ3VhZ2U6IHNvdXJjZS5sYW5ndWFnZSxcbiAgICAgIG1vZHVsZUlkczogbW9kdWxlcy5maWx0ZXIobW9kdWxlID0+IG1vZHVsZS5zb3VyY2UuZmlsZW5hbWUgPT0gc291cmNlLmZpbGVuYW1lKS5tYXAoKHtpZH0pID0+IGlkKSxcbiAgICAgIG93bmVyc2hpcDogc291cmNlLm93bmVyc2hpcCA/PyBcImFwcGxpY2F0aW9uXCIsXG4gICAgICBzaGEyNTY6IGNyZWF0ZUhhc2goXCJzaGEyNTZcIikudXBkYXRlKC8qKiBAdHlwZSB7c3RyaW5nfSAqLyAoc291cmNlQ29udGVudC5nZXQoc291cmNlLmZpbGVuYW1lKSkpLmRpZ2VzdChcImhleFwiKVxuICAgIH0pKSxcbiAgICB2ZXJzaW9uOiAxXG4gIH1cbn1cblxuLyoqXG4gKiBWYWxpZGF0ZXMgYW5kIG5vcm1hbGl6ZXMgdGhlIGNsb3NlZCBBbmRyb2lkIGNvbmZpZ3VyYXRpb24uXG4gKiBAcGFyYW0ge3Vua25vd259IGNhbmRpZGF0ZSAtIENhbmRpZGF0ZSBjb25maWd1cmF0aW9uLlxuICogQHJldHVybnMge2ltcG9ydChcIi4uL3NlbWFudGljL3R5cGVzLmpzXCIpLkFuZHJvaWRBcHBsaWNhdGlvbkNvbmZpZ3VyYXRpb259IE5vcm1hbGl6ZWQgY29uZmlndXJhdGlvbi5cbiAqL1xuZnVuY3Rpb24gbm9ybWFsaXplQ29uZmlndXJhdGlvbihjYW5kaWRhdGUpIHtcbiAgaWYgKGNhbmRpZGF0ZSA9PT0gdW5kZWZpbmVkKSBjYW5kaWRhdGUgPSB7fVxuICBpZiAoIWlzUGxhaW5PYmplY3QoY2FuZGlkYXRlKSB8fCBPYmplY3Qua2V5cyhjYW5kaWRhdGUpLnNvbWUoa2V5ID0+ICFjb25maWd1cmF0aW9uRmllbGRzLmhhcyhrZXkpKSkge1xuICAgIGludmFsaWRDb25maWd1cmF0aW9uKFwiQW5kcm9pZCBhcHBsaWNhdGlvbiBjb25maWd1cmF0aW9uIG11c3QgYmUgYSBjbG9zZWQgcGxhaW4gb2JqZWN0LlwiKVxuICB9XG4gIGNvbnN0IGNvbmZpZ3VyYXRpb24gPSAvKiogQHR5cGUge2ltcG9ydChcIi4uL3NlbWFudGljL3R5cGVzLmpzXCIpLkFuZHJvaWRBcHBsaWNhdGlvbkNvbmZpZ3VyYXRpb25JbnB1dH0gKi8gKGNhbmRpZGF0ZSlcbiAgY29uc3QgYXBwbGljYXRpb25JZCA9IGNvbmZpZ3VyYXRpb24uYXBwbGljYXRpb25JZCA/PyBcImRldi5zZW1hbnRpZm9sZC5nZW5lcmF0ZWRcIlxuICBjb25zdCBuYW1lc3BhY2UgPSBjb25maWd1cmF0aW9uLm5hbWVzcGFjZSA/PyBhcHBsaWNhdGlvbklkXG4gIGNvbnN0IHBhY2thZ2VOYW1lID0gY29uZmlndXJhdGlvbi5wYWNrYWdlTmFtZSA/PyBuYW1lc3BhY2VcbiAgY29uc3QgYWN0aXZpdHlDbGFzc05hbWUgPSBjb25maWd1cmF0aW9uLmFjdGl2aXR5Q2xhc3NOYW1lID8/IFwiTWFpbkFjdGl2aXR5XCJcbiAgY29uc3QgcHJvZHVjdE5hbWUgPSBjb25maWd1cmF0aW9uLnByb2R1Y3ROYW1lID8/IFwiU2VtYW50aWZvbGRcIlxuICBjb25zdCBkaXNwbGF5TmFtZSA9IGNvbmZpZ3VyYXRpb24uZGlzcGxheU5hbWUgPz8gXCJTZW1hbnRpZm9sZFwiXG4gIGNvbnN0IG1pbmltdW1TZGsgPSBjb25maWd1cmF0aW9uLm1pbmltdW1TZGsgPz8gMjNcbiAgY29uc3QgdGFyZ2V0U2RrID0gY29uZmlndXJhdGlvbi50YXJnZXRTZGsgPz8gdG9vbGNoYWluLnRhcmdldFNka1xuICBjb25zdCBjb21waWxlU2RrID0gY29uZmlndXJhdGlvbi5jb21waWxlU2RrID8/IHRvb2xjaGFpbi5jb21waWxlU2RrXG4gIGNvbnN0IGJ1aWxkVG9vbHNWZXJzaW9uID0gY29uZmlndXJhdGlvbi5idWlsZFRvb2xzVmVyc2lvbiA/PyB0b29sY2hhaW4uYnVpbGRUb29sc1xuICBjb25zdCB2ZXJzaW9uQ29kZSA9IGNvbmZpZ3VyYXRpb24udmVyc2lvbkNvZGUgPz8gMVxuICBjb25zdCB2ZXJzaW9uTmFtZSA9IGNvbmZpZ3VyYXRpb24udmVyc2lvbk5hbWUgPz8gXCIxLjAuMFwiXG4gIGNvbnN0IG9yaWVudGF0aW9uID0gY29uZmlndXJhdGlvbi5vcmllbnRhdGlvbiA/PyBcInVuc3BlY2lmaWVkXCJcbiAgY29uc3QgdGhlbWUgPSBjb25maWd1cmF0aW9uLnRoZW1lID8/IFwic3lzdGVtXCJcblxuICBmb3IgKGNvbnN0IFtsYWJlbCwgdmFsdWVdIG9mIFtbXCJBcHBsaWNhdGlvbiBJRFwiLCBhcHBsaWNhdGlvbklkXSwgW1wiTmFtZXNwYWNlXCIsIG5hbWVzcGFjZV0sIFtcIlBhY2thZ2UgbmFtZVwiLCBwYWNrYWdlTmFtZV1dKSB7XG4gICAgaWYgKHR5cGVvZiB2YWx1ZSAhPSBcInN0cmluZ1wiIHx8ICFpc1BhY2thZ2VOYW1lKHZhbHVlKSkgaW52YWxpZENvbmZpZ3VyYXRpb24oYCR7bGFiZWx9IG11c3QgYmUgYSBsb3dlcmNhc2UgSmF2YSBwYWNrYWdlIGlkZW50aXR5LmApXG4gIH1cbiAgaWYgKHR5cGVvZiBhY3Rpdml0eUNsYXNzTmFtZSAhPSBcInN0cmluZ1wiIHx8ICEvXltBLVpdW0EtWmEtejAtOV0qJC91LnRlc3QoYWN0aXZpdHlDbGFzc05hbWUpIHx8IGtvdGxpbktleXdvcmRzLmhhcyhhY3Rpdml0eUNsYXNzTmFtZSkpIHtcbiAgICBpbnZhbGlkQ29uZmlndXJhdGlvbihcIkFjdGl2aXR5IGNsYXNzIG5hbWUgbXVzdCBiZSBhbiBBU0NJSSB1cHBlci1jYW1lbCBLb3RsaW4gaWRlbnRpZmllci5cIilcbiAgfVxuICBmb3IgKGNvbnN0IFtsYWJlbCwgdmFsdWVdIG9mIFtbXCJQcm9kdWN0IG5hbWVcIiwgcHJvZHVjdE5hbWVdLCBbXCJEaXNwbGF5IG5hbWVcIiwgZGlzcGxheU5hbWVdXSkge1xuICAgIGlmICh0eXBlb2YgdmFsdWUgIT0gXCJzdHJpbmdcIiB8fCB2YWx1ZS5sZW5ndGggPT0gMCB8fCB2YWx1ZS50cmltKCkgIT0gdmFsdWUgfHwgWy4uLnZhbHVlXS5sZW5ndGggPiA2NCB8fFxuICAgICAgIWhhc09ubHlVbmljb2RlU2NhbGFycyh2YWx1ZSkgfHwgL1tcXHB7Q2N9XFxwe1psfVxccHtacH1cXHVGRkZFXFx1RkZGRl0vdS50ZXN0KHZhbHVlKSkge1xuICAgICAgaW52YWxpZENvbmZpZ3VyYXRpb24oYCR7bGFiZWx9IG11c3QgYmUgYSBub24tZW1wdHkgc2luZ2xlLWxpbmUgVW5pY29kZSBzY2FsYXIgc3RyaW5nIG9mIGF0IG1vc3QgNjQgY2hhcmFjdGVycy5gKVxuICAgIH1cbiAgfVxuICBpZiAoL1tcXFxcLzo8PlwiPyp8XS91LnRlc3QocHJvZHVjdE5hbWUpKSB7XG4gICAgaW52YWxpZENvbmZpZ3VyYXRpb24oXCJQcm9kdWN0IG5hbWUgY29udGFpbnMgYSBjaGFyYWN0ZXIgZm9yYmlkZGVuIGJ5IHRoZSBHcmFkbGUgcHJvamVjdC1uYW1lIGNvbnRyYWN0LlwiKVxuICB9XG4gIGlmIChtaW5pbXVtU2RrICE9PSAyMyB8fCB0YXJnZXRTZGsgIT09IDM1IHx8IGNvbXBpbGVTZGsgIT09IDM1IHx8IG1pbmltdW1TZGsgPiB0YXJnZXRTZGsgfHwgdGFyZ2V0U2RrID4gY29tcGlsZVNkaykge1xuICAgIGludmFsaWRDb25maWd1cmF0aW9uKFwiU0RLIGxldmVscyBtdXN0IHVzZSB0aGUgcXVhbGlmaWVkIG1pbmltdW0gMjMsIHRhcmdldCAzNSwgYW5kIGNvbXBpbGUgMzUgbWF0cml4LlwiKVxuICB9XG4gIGlmIChidWlsZFRvb2xzVmVyc2lvbiAhPSB0b29sY2hhaW4uYnVpbGRUb29scykgaW52YWxpZENvbmZpZ3VyYXRpb24oXCJCdWlsZCBUb29scyB2ZXJzaW9uIG11c3QgYmUgZXhhY3RseSAnMzUuMC4wJy5cIilcbiAgaWYgKCFOdW1iZXIuaXNTYWZlSW50ZWdlcih2ZXJzaW9uQ29kZSkgfHwgdmVyc2lvbkNvZGUgPCAxIHx8IHZlcnNpb25Db2RlID4gMl8xMDBfMDAwXzAwMCkge1xuICAgIGludmFsaWRDb25maWd1cmF0aW9uKFwiVmVyc2lvbiBjb2RlIG11c3QgYmUgYSBwb3NpdGl2ZSBBbmRyb2lkLXNhZmUgaW50ZWdlci5cIilcbiAgfVxuICBpZiAodHlwZW9mIHZlcnNpb25OYW1lICE9IFwic3RyaW5nXCIgfHwgIXNlbWFudGljVmVyc2lvblBhdHRlcm4udGVzdCh2ZXJzaW9uTmFtZSkpIHtcbiAgICBpbnZhbGlkQ29uZmlndXJhdGlvbihcIlZlcnNpb24gbmFtZSBtdXN0IGJlIGEgY2Fub25pY2FsIG5vbi1lbXB0eSBzZW1hbnRpYyB2ZXJzaW9uIHN0cmluZy5cIilcbiAgfVxuICBpZiAob3JpZW50YXRpb24gIT0gXCJ1bnNwZWNpZmllZFwiKSBpbnZhbGlkQ29uZmlndXJhdGlvbihcIlRoZSBiYXNlbGluZSBvcmllbnRhdGlvbiBpcyBleGFjdGx5ICd1bnNwZWNpZmllZCcuXCIpXG4gIGlmICh0aGVtZSAhPSBcInN5c3RlbVwiKSBpbnZhbGlkQ29uZmlndXJhdGlvbihcIlRoZSBiYXNlbGluZSB0aGVtZSBpcyBleGFjdGx5ICdzeXN0ZW0nLlwiKVxuICBpZiAoY29uZmlndXJhdGlvbi5wZXJtaXNzaW9ucyAhPT0gdW5kZWZpbmVkICYmXG4gICAgKCFpc0RlbnNlQXJyYXkoY29uZmlndXJhdGlvbi5wZXJtaXNzaW9ucykgfHwgY29uZmlndXJhdGlvbi5wZXJtaXNzaW9ucy5sZW5ndGggIT0gMCkpIHtcbiAgICBpbnZhbGlkQ29uZmlndXJhdGlvbihcIlRoZSBiYXNlbGluZSBBbmRyb2lkIHBlcm1pc3Npb24gc2V0IG11c3QgYmUgZW1wdHkuXCIpXG4gIH1cblxuICByZXR1cm4ge1xuICAgIGFjdGl2aXR5Q2xhc3NOYW1lLFxuICAgIGFwcGxpY2F0aW9uSWQsXG4gICAgYnVpbGRUb29sc1ZlcnNpb24sXG4gICAgY29tcGlsZVNkayxcbiAgICBkaXNwbGF5TmFtZSxcbiAgICBtaW5pbXVtU2RrLFxuICAgIG5hbWVzcGFjZSxcbiAgICBvcmllbnRhdGlvbixcbiAgICBwYWNrYWdlTmFtZSxcbiAgICBwcm9kdWN0TmFtZSxcbiAgICB0YXJnZXRTZGssXG4gICAgdGhlbWUsXG4gICAgdmVyc2lvbkNvZGUsXG4gICAgdmVyc2lvbk5hbWVcbiAgfVxufVxuXG4vKipcbiAqIFZhbGlkYXRlcywgc25hcHNob3RzLCBhbmQgb3JkZXJzIGNhbGxlci1wcm92aWRlZCBpbnB1dHMuXG4gKiBAcGFyYW0ge3Vua25vd259IGNhbmRpZGF0ZSAtIENhbmRpZGF0ZSByZXNvdXJjZSBvciBhc3NldCBsaXN0LlxuICogQHBhcmFtIHtcImFzc2V0XCIgfCBcInJlc291cmNlXCJ9IGtpbmQgLSBBbmRyb2lkIGlucHV0IGtpbmQuXG4gKiBAcmV0dXJucyB7QW5kcm9pZElucHV0RmlsZVtdfSBOb3JtYWxpemVkIGlucHV0cy5cbiAqL1xuZnVuY3Rpb24gbm9ybWFsaXplSW5wdXRGaWxlcyhjYW5kaWRhdGUsIGtpbmQpIHtcbiAgaWYgKGNhbmRpZGF0ZSA9PT0gdW5kZWZpbmVkKSByZXR1cm4gW11cbiAgaWYgKCFpc0RlbnNlQXJyYXkoY2FuZGlkYXRlKSkgaW52YWxpZFJlc291cmNlKGBBcHBsaWNhdGlvbiAke2tpbmR9cyBtdXN0IGJlIGEgZGVuc2UgYXJyYXkuYClcbiAgLyoqIEB0eXBlIHtBbmRyb2lkSW5wdXRGaWxlW119ICovXG4gIGNvbnN0IGZpbGVzID0gW11cblxuICBmb3IgKGxldCBpbmRleCA9IDA7IGluZGV4IDwgY2FuZGlkYXRlLmxlbmd0aDsgaW5kZXggKz0gMSkge1xuICAgIGNvbnN0IGl0ZW0gPSBjYW5kaWRhdGVbaW5kZXhdXG5cbiAgICBpZiAoIWlzUGxhaW5PYmplY3QoaXRlbSkgfHwgT2JqZWN0LmtleXMoaXRlbSkubGVuZ3RoICE9IHJlc291cmNlRmllbGRzLnNpemUgfHxcbiAgICAgIE9iamVjdC5rZXlzKGl0ZW0pLnNvbWUoa2V5ID0+ICFyZXNvdXJjZUZpZWxkcy5oYXMoa2V5KSkpIGludmFsaWRSZXNvdXJjZShgJHtjYXBpdGFsaXplKGtpbmQpfSAke2luZGV4fSBtdXN0IHVzZSB0aGUgY2xvc2VkIGlucHV0IHNjaGVtYS5gKVxuICAgIGNvbnN0IHBhdGggPSBpdGVtLnBhdGhcbiAgICBjb25zdCBjb250ZW50ID0gaXRlbS5jb250ZW50XG4gICAgY29uc3QgbWVkaWFUeXBlID0gaXRlbS5tZWRpYVR5cGVcbiAgICBjb25zdCBzaGEyNTYgPSBpdGVtLnNoYTI1NlxuXG4gICAgaWYgKCFpc1NhZmVBcnRpZmFjdFBhdGgocGF0aCkgfHwga2luZCA9PSBcInJlc291cmNlXCIgJiYgIWlzQWxsb3dlZFJlc291cmNlUGF0aChwYXRoKSkge1xuICAgICAgaW52YWxpZFJlc291cmNlKGAke2NhcGl0YWxpemUoa2luZCl9ICR7aW5kZXh9IGhhcyBhbiB1bnNhZmUgb3IgdW5zdXBwb3J0ZWQgcGF0aC5gKVxuICAgIH1cbiAgICBpZiAodHlwZW9mIGNvbnRlbnQgIT0gXCJzdHJpbmdcIiAmJiAhKGNvbnRlbnQgaW5zdGFuY2VvZiBVaW50OEFycmF5KSB8fCB0eXBlb2YgY29udGVudCA9PSBcInN0cmluZ1wiICYmXG4gICAgICAoY29udGVudC5sZW5ndGggPT0gMCB8fCAhaGFzT25seVVuaWNvZGVTY2FsYXJzKGNvbnRlbnQpKSB8fCBjb250ZW50IGluc3RhbmNlb2YgVWludDhBcnJheSAmJiBjb250ZW50LmJ5dGVMZW5ndGggPT0gMCkge1xuICAgICAgaW52YWxpZFJlc291cmNlKGAke2NhcGl0YWxpemUoa2luZCl9ICcke3BhdGh9JyByZXF1aXJlcyBub24tZW1wdHkgZXhhY3QgY29udGVudC5gKVxuICAgIH1cbiAgICBpZiAodHlwZW9mIG1lZGlhVHlwZSAhPSBcInN0cmluZ1wiIHx8ICFtZWRpYVR5cGVQYXR0ZXJuLnRlc3QobWVkaWFUeXBlKSB8fCAvW1xcclxcbl0vdS50ZXN0KG1lZGlhVHlwZSkpIHtcbiAgICAgIGludmFsaWRSZXNvdXJjZShgJHtjYXBpdGFsaXplKGtpbmQpfSAnJHtwYXRofScgcmVxdWlyZXMgYW4gZXhwbGljaXQgdmFsaWQgbWVkaWEgdHlwZS5gKVxuICAgIH1cbiAgICBpZiAoa2luZCA9PSBcInJlc291cmNlXCIgJiYgcGF0aC5zdGFydHNXaXRoKFwiZHJhd2FibGUtXCIpICYmICFbXCJpbWFnZS9wbmdcIiwgXCJpbWFnZS93ZWJwXCJdLmluY2x1ZGVzKG1lZGlhVHlwZSkpIHtcbiAgICAgIGludmFsaWRSZXNvdXJjZShgRHJhd2FibGUgcmVzb3VyY2UgJyR7cGF0aH0nIG11c3QgdXNlIFBORyBvciBXZWJQIG1lZGlhIHR5cGUuYClcbiAgICB9XG4gICAgY29uc3Qgc25hcHNob3QgPSB0eXBlb2YgY29udGVudCA9PSBcInN0cmluZ1wiID8gY29udGVudCA6IG5ldyBVaW50OEFycmF5KGNvbnRlbnQpXG4gICAgY29uc3QgYWN0dWFsSGFzaCA9IGNyZWF0ZUhhc2goXCJzaGEyNTZcIikudXBkYXRlKHNuYXBzaG90KS5kaWdlc3QoXCJoZXhcIilcblxuICAgIGlmICh0eXBlb2Ygc2hhMjU2ICE9IFwic3RyaW5nXCIgfHwgIS9eWzAtOWEtZl17NjR9JC91LnRlc3Qoc2hhMjU2KSB8fCBzaGEyNTYgIT0gYWN0dWFsSGFzaCkge1xuICAgICAgaW52YWxpZFJlc291cmNlKGAke2NhcGl0YWxpemUoa2luZCl9ICcke3BhdGh9JyBTSEEtMjU2IGRvZXMgbm90IG1hdGNoIGl0cyBleGFjdCBjb250ZW50LmApXG4gICAgfVxuICAgIGNvbnN0IGZ1bGxQYXRoID0gYCR7cHJvamVjdFJvb3R9L2FwcC9zcmMvbWFpbi8ke2tpbmQgPT0gXCJyZXNvdXJjZVwiID8gXCJyZXNcIiA6IFwiYXNzZXRzXCJ9LyR7cGF0aH1gXG5cbiAgICBmaWxlcy5wdXNoKHtjb250ZW50OiBzbmFwc2hvdCwgY29udGVudEtpbmQ6IHR5cGVvZiBzbmFwc2hvdCA9PSBcInN0cmluZ1wiID8gXCJ0ZXh0XCIgOiBcImJpbmFyeVwiLCBmdWxsUGF0aCwgbWVkaWFUeXBlLCBwYXRoLCBzaGEyNTZ9KVxuICB9XG4gIGZpbGVzLnNvcnQoKGxlZnQsIHJpZ2h0KSA9PiBsZWZ0LmZ1bGxQYXRoIDwgcmlnaHQuZnVsbFBhdGggPyAtMSA6IGxlZnQuZnVsbFBhdGggPiByaWdodC5mdWxsUGF0aCA/IDEgOiAwKVxuICBjb25zdCBjb25mbGljdCA9IGZpbmRQb3J0YWJsZUFydGlmYWN0UGF0aENvbmZsaWN0KGZpbGVzLm1hcCgoe2Z1bGxQYXRofSkgPT4gZnVsbFBhdGgpKVxuXG4gIGlmIChjb25mbGljdCkgaW52YWxpZFJlc291cmNlKGAke2NhcGl0YWxpemUoa2luZCl9IHBhdGggJyR7Y29uZmxpY3QucGF0aH0nIGhhcyBhICR7Y29uZmxpY3Qua2luZH0gY29uZmxpY3QuYClcbiAgaWYgKGtpbmQgPT0gXCJyZXNvdXJjZVwiKSB2YWxpZGF0ZVJlc291cmNlSWRlbnRpZmllcnMoZmlsZXMpXG5cbiAgcmV0dXJuIGZpbGVzXG59XG5cbi8qKlxuICogUmVqZWN0cyB0d28gZmlsZXMgdGhhdCBBbmRyb2lkIHdvdWxkIGNvbXBpbGUgdG8gdGhlIHNhbWUgcmVzb3VyY2UgaWRlbnRpZmllciBpbiBvbmUgY29uZmlndXJhdGlvbi5cbiAqIEBwYXJhbSB7QW5kcm9pZElucHV0RmlsZVtdfSBmaWxlcyAtIFZhbGlkYXRlZCByZXNvdXJjZSBpbnB1dHMuXG4gKi9cbmZ1bmN0aW9uIHZhbGlkYXRlUmVzb3VyY2VJZGVudGlmaWVycyhmaWxlcykge1xuICBjb25zdCBpZGVudGlmaWVycyA9IG5ldyBNYXAoKVxuXG4gIGZvciAoY29uc3QgZmlsZSBvZiBmaWxlcykge1xuICAgIGNvbnN0IHNlcGFyYXRvciA9IGZpbGUucGF0aC5pbmRleE9mKFwiL1wiKVxuICAgIGNvbnN0IGRpcmVjdG9yeSA9IGZpbGUucGF0aC5zbGljZSgwLCBzZXBhcmF0b3IpXG4gICAgY29uc3QgZmlsZW5hbWUgPSBmaWxlLnBhdGguc2xpY2Uoc2VwYXJhdG9yICsgMSlcbiAgICBjb25zdCBleHRlbnNpb24gPSBmaWxlbmFtZS5pbmRleE9mKFwiLlwiKVxuICAgIGNvbnN0IG5hbWUgPSBleHRlbnNpb24gPT0gLTEgPyBmaWxlbmFtZSA6IGZpbGVuYW1lLnNsaWNlKDAsIGV4dGVuc2lvbilcbiAgICBjb25zdCByZXNvdXJjZVR5cGUgPSBkaXJlY3Rvcnkuc3BsaXQoXCItXCIsIDEpWzBdXG4gICAgY29uc3Qga2V5ID0gYCR7ZGlyZWN0b3J5fS8ke25hbWV9YFxuICAgIGNvbnN0IHByZXZpb3VzID0gaWRlbnRpZmllcnMuZ2V0KGtleSlcblxuICAgIGlmIChwcmV2aW91cykge1xuICAgICAgaW52YWxpZFJlc291cmNlKGBBbmRyb2lkIHJlc291cmNlIGlkZW50aWZpZXIgJyR7cmVzb3VyY2VUeXBlfS8ke25hbWV9JyBjb2xsaWRlcyBiZXR3ZWVuICcke3ByZXZpb3VzfScgYW5kICcke2ZpbGUucGF0aH0nIGluIGNvbmZpZ3VyYXRpb24gJyR7ZGlyZWN0b3J5fScuYClcbiAgICB9XG4gICAgaWRlbnRpZmllcnMuc2V0KGtleSwgZmlsZS5wYXRoKVxuICB9XG59XG5cbi8qKlxuICogVGVzdHMgd2hldGhlciBhIGNhbGxlciByZXNvdXJjZSB1c2VzIHRoZSBuYXJyb3cgYWxsb3dsaXN0ZWQgcmVzb3VyY2UgZ3JhbW1hci5cbiAqIEBwYXJhbSB7c3RyaW5nfSB2YWx1ZSAtIFJlc291cmNlIHBhdGggcmVsYXRpdmUgdG8gcmVzLlxuICogQHJldHVybnMge2Jvb2xlYW59IFdoZXRoZXIgdGhlIHBhdGggaXMgc3VwcG9ydGVkLlxuICovXG5mdW5jdGlvbiBpc0FsbG93ZWRSZXNvdXJjZVBhdGgodmFsdWUpIHtcbiAgaWYgKC9ecmF3XFwvW2Etel1bYS16MC05X10qKD86XFwuW2EtejAtOV0rKT8kL3UudGVzdCh2YWx1ZSkpIHJldHVybiB0cnVlXG5cbiAgcmV0dXJuIC9eZHJhd2FibGUtKD86bWRwaXxoZHBpfHhoZHBpfHh4aGRwaXx4eHhoZHBpKVxcL1thLXpdW2EtejAtOV9dKlxcLig/OnBuZ3x3ZWJwKSQvdS50ZXN0KHZhbHVlKVxufVxuXG4vKipcbiAqIENvbnZlcnRzIGEgdmFsaWRhdGVkIGlucHV0IHNuYXBzaG90IGludG8gYW4gb3duZWQgYXJ0aWZhY3QuXG4gKiBAcGFyYW0ge0FuZHJvaWRJbnB1dEZpbGV9IGl0ZW0gLSBWYWxpZGF0ZWQgY2FsbGVyIGlucHV0LlxuICogQHJldHVybnMge2ltcG9ydChcIi4uL3NlbWFudGljL3R5cGVzLmpzXCIpLkdlbmVyYXRlZFNldEFydGlmYWN0fSBPd25lZCBpbnB1dCBhcnRpZmFjdC5cbiAqL1xuZnVuY3Rpb24gaW5wdXRGaWxlQXJ0aWZhY3QoaXRlbSkge1xuICBjb25zdCByZWFzb24gPSBgRXhhY3QgY2FsbGVyLXByb3ZpZGVkIEFuZHJvaWQgaW5wdXQgKHNoYTI1Njoke2l0ZW0uc2hhMjU2fSkuYFxuXG4gIGlmICh0eXBlb2YgaXRlbS5jb250ZW50ID09IFwic3RyaW5nXCIpIHtcbiAgICByZXR1cm4ge1xuICAgICAgLi4uc3ludGhldGljVGV4dChpdGVtLmZ1bGxQYXRoLCBcInJlc291cmNlXCIsIGl0ZW0uY29udGVudCwgcmVhc29uKSxcbiAgICAgIG1lZGlhVHlwZTogaXRlbS5tZWRpYVR5cGVcbiAgICB9XG4gIH1cbiAgY29uc3QgY29udGVudCA9IG5ldyBVaW50OEFycmF5KGl0ZW0uY29udGVudClcblxuICByZXR1cm4ge1xuICAgIGNvbnRlbnQsXG4gICAgY29udGVudEtpbmQ6IFwiYmluYXJ5XCIsXG4gICAgbWVkaWFUeXBlOiBpdGVtLm1lZGlhVHlwZSxcbiAgICBvd25lcnNoaXA6IFwiZ2VuZXJhdGVkXCIsXG4gICAgcGF0aDogaXRlbS5mdWxsUGF0aCxcbiAgICBwcm92ZW5hbmNlOiB7a2luZDogXCJieXRlc1wiLCBtYXBwaW5nOiBjcmVhdGVCeXRlTWFwcGluZyh7XG4gICAgICBieXRlTGVuZ3RoOiBjb250ZW50LmJ5dGVMZW5ndGgsXG4gICAgICBwYXRoOiBpdGVtLmZ1bGxQYXRoLFxuICAgICAgcmFuZ2VzOiBbe2dlbmVyYXRlZDoge2VuZDogY29udGVudC5ieXRlTGVuZ3RoLCBzdGFydDogMH0sXG4gICAgICAgIG9yaWdpbjoge2tpbmQ6IFwic3ludGhldGljXCIsIHJlYXNvbiwgcmVsYXRlZE9yaWdpbnM6IFtdfSwgcm9sZTogXCJyZXNvdXJjZVwifV1cbiAgICB9KX0sXG4gICAgcm9sZTogXCJyZXNvdXJjZVwiXG4gIH1cbn1cblxuLyoqXG4gKiBDcmVhdGVzIGEgZGV0ZXJtaW5pc3RpYyBzeW50aGV0aWMgdGV4dCBhcnRpZmFjdC5cbiAqIEBwYXJhbSB7c3RyaW5nfSBwYXRoIC0gQXJ0aWZhY3QgcGF0aC5cbiAqIEBwYXJhbSB7aW1wb3J0KFwiLi4vc2VtYW50aWMvdHlwZXMuanNcIikuR2VuZXJhdGVkQXJ0aWZhY3RSb2xlfSByb2xlIC0gQXJ0aWZhY3Qgcm9sZS5cbiAqIEBwYXJhbSB7c3RyaW5nfSBjb250ZW50IC0gVVRGLTggYXJ0aWZhY3QgY29udGVudC5cbiAqIEBwYXJhbSB7c3RyaW5nfSByZWFzb24gLSBTeW50aGV0aWMgcHJvdmVuYW5jZSByZWFzb24uXG4gKiBAcmV0dXJucyB7aW1wb3J0KFwiLi4vc2VtYW50aWMvdHlwZXMuanNcIikuR2VuZXJhdGVkU2V0QXJ0aWZhY3R9IFRleHQgYXJ0aWZhY3QuXG4gKi9cbmZ1bmN0aW9uIHN5bnRoZXRpY1RleHQocGF0aCwgcm9sZSwgY29udGVudCwgcmVhc29uKSB7XG4gIHJldHVybiB7Y29udGVudCwgY29udGVudEtpbmQ6IFwidGV4dFwiLCBtZWRpYVR5cGU6IG1lZGlhVHlwZUZvcihwYXRoKSwgb3duZXJzaGlwOiBcImdlbmVyYXRlZFwiLCBwYXRoLFxuICAgIHByb3ZlbmFuY2U6IHtraW5kOiBcInN5bnRoZXRpY1wiLCByZWFzb24sIHJlbGF0ZWRPcmlnaW5zOiBbXX0sIHJvbGV9XG59XG5cbi8qKlxuICogQ3JlYXRlcyBhIGNvbmZpZ3VyYXRpb24tZGVyaXZlZCB0ZXh0IGFydGlmYWN0LlxuICogQHBhcmFtIHtzdHJpbmd9IHBhdGggLSBBcnRpZmFjdCBwYXRoLlxuICogQHBhcmFtIHtpbXBvcnQoXCIuLi9zZW1hbnRpYy90eXBlcy5qc1wiKS5HZW5lcmF0ZWRBcnRpZmFjdFJvbGV9IHJvbGUgLSBBcnRpZmFjdCByb2xlLlxuICogQHBhcmFtIHtzdHJpbmd9IGNvbnRlbnQgLSBVVEYtOCBhcnRpZmFjdCBjb250ZW50LlxuICogQHBhcmFtIHtzdHJpbmd9IHJlYXNvbiAtIFN5bnRoZXRpYyBwcm92ZW5hbmNlIHJlYXNvbi5cbiAqIEByZXR1cm5zIHtpbXBvcnQoXCIuLi9zZW1hbnRpYy90eXBlcy5qc1wiKS5HZW5lcmF0ZWRTZXRBcnRpZmFjdH0gVGV4dCBhcnRpZmFjdC5cbiAqL1xuZnVuY3Rpb24gY29uZmlndXJlZFRleHQocGF0aCwgcm9sZSwgY29udGVudCwgcmVhc29uKSB7XG4gIHJldHVybiBzeW50aGV0aWNUZXh0KHBhdGgsIHJvbGUsIGNvbnRlbnQsIHJlYXNvbilcbn1cblxuLyoqXG4gKiBEZXJpdmVzIHRoZSBtZWRpYSB0eXBlIGZvciBhIGdlbmVyYXRlZCB0ZXh0IHBhdGguXG4gKiBAcGFyYW0ge3N0cmluZ30gcGF0aCAtIEdlbmVyYXRlZCBhcnRpZmFjdCBwYXRoLlxuICogQHJldHVybnMge3N0cmluZ30gQXJ0aWZhY3QgbWVkaWEgdHlwZS5cbiAqL1xuZnVuY3Rpb24gbWVkaWFUeXBlRm9yKHBhdGgpIHtcbiAgaWYgKHBhdGguZW5kc1dpdGgoXCIuanNvblwiKSkgcmV0dXJuIFwiYXBwbGljYXRpb24vanNvblwiXG4gIGlmIChwYXRoLmVuZHNXaXRoKFwiLnhtbFwiKSkgcmV0dXJuIFwiYXBwbGljYXRpb24veG1sXCJcbiAgaWYgKHBhdGguZW5kc1dpdGgoXCIua3RcIikgfHwgcGF0aC5lbmRzV2l0aChcIi5rdHNcIikpIHJldHVybiBcInRleHQveC1rb3RsaW5cIlxuXG4gIHJldHVybiBcInRleHQvcGxhaW5cIlxufVxuXG4vKipcbiAqIE1hcmtzIG9uZSByZW5kZXJlZCB2YWx1ZSBhcyBjb25maWd1cmF0aW9uLWRlcml2ZWQuXG4gKiBAcGFyYW0ge3N0cmluZ30gZmllbGROYW1lIC0gQ29uZmlndXJhdGlvbiBmaWVsZCBuYW1lLlxuICogQHBhcmFtIHtzdHJpbmd9IHZhbHVlIC0gUmVuZGVyZWQgZmllbGQgdmFsdWUuXG4gKiBAcmV0dXJucyB7e2ZpZWxkOiBzdHJpbmcsIHZhbHVlOiBzdHJpbmd9fSBNYXJrZWQgY29uZmlndXJhdGlvbiB2YWx1ZS5cbiAqL1xuZnVuY3Rpb24gZmllbGQoZmllbGROYW1lLCB2YWx1ZSkge1xuICByZXR1cm4ge2ZpZWxkOiBmaWVsZE5hbWUsIHZhbHVlfVxufVxuXG4vKipcbiAqIEpvaW5zIHJlbmRlcmVkIHBhcnRzIHdoaWxlIHJlY29yZGluZyBjb25maWd1cmF0aW9uIGNhdXNhbGl0eS5cbiAqIEBwYXJhbSB7c3RyaW5nfSBwYXRoIC0gT3V0cHV0IGFydGlmYWN0IHBhdGguXG4gKiBAcGFyYW0ge0NvbmZpZ3VyYXRpb25TcGFuW119IHNwYW5zIC0gTXV0YWJsZSBwcm92ZW5hbmNlIHNwYW4gY29sbGVjdG9yLlxuICogQHBhcmFtIHsoc3RyaW5nIHwge2ZpZWxkOiBzdHJpbmcsIHZhbHVlOiBzdHJpbmd9KVtdfSBwYXJ0cyAtIFN5bnRoZXRpYyBhbmQgbWFya2VkIHBhcnRzLlxuICogQHJldHVybnMge3N0cmluZ30gUmVuZGVyZWQgdGV4dC5cbiAqL1xuZnVuY3Rpb24gY29uZmlndXJlZChwYXRoLCBzcGFucywgcGFydHMpIHtcbiAgbGV0IGNvbnRlbnQgPSBcIlwiXG5cbiAgZm9yIChjb25zdCBwYXJ0IG9mIHBhcnRzKSB7XG4gICAgaWYgKHR5cGVvZiBwYXJ0ID09IFwic3RyaW5nXCIpIGNvbnRlbnQgKz0gcGFydFxuICAgIGVsc2Uge1xuICAgICAgY29uc3Qgc3RhcnQgPSBjb250ZW50Lmxlbmd0aFxuXG4gICAgICBjb250ZW50ICs9IHBhcnQudmFsdWVcbiAgICAgIHNwYW5zLnB1c2goe2VuZDogY29udGVudC5sZW5ndGgsIGZpZWxkOiBwYXJ0LmZpZWxkLCBwYXRoLCBzdGFydH0pXG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIGNvbnRlbnRcbn1cblxuLyoqXG4gKiBHcm91cHMgY29uZmlndXJhdGlvbiBzcGFucyBieSBhcnRpZmFjdC5cbiAqIEBwYXJhbSB7Q29uZmlndXJhdGlvblNwYW5bXX0gc3BhbnMgLSBBbGwgcmVuZGVyZWQgY29uZmlndXJhdGlvbiBzcGFucy5cbiAqIEBwYXJhbSB7c3RyaW5nfSBmaWVsZE5hbWUgLSBSZXF1ZXN0ZWQgY29uZmlndXJhdGlvbiBmaWVsZC5cbiAqIEByZXR1cm5zIHt7cGF0aDogc3RyaW5nLCByYW5nZXM6IHtlbmQ6IG51bWJlciwgc3RhcnQ6IG51bWJlcn1bXX1bXX0gT3V0cHV0IHJhbmdlcy5cbiAqL1xuZnVuY3Rpb24gY29uZmlndXJhdGlvbk91dHB1dHMoc3BhbnMsIGZpZWxkTmFtZSkge1xuICBjb25zdCBncm91cGVkID0gbmV3IE1hcCgpXG5cbiAgZm9yIChjb25zdCBzcGFuIG9mIHNwYW5zKSB7XG4gICAgaWYgKHNwYW4uZmllbGQgIT0gZmllbGROYW1lKSBjb250aW51ZVxuICAgIGNvbnN0IHJhbmdlcyA9IGdyb3VwZWQuZ2V0KHNwYW4ucGF0aCkgPz8gW11cblxuICAgIHJhbmdlcy5wdXNoKHtlbmQ6IHNwYW4uZW5kLCBzdGFydDogc3Bhbi5zdGFydH0pXG4gICAgZ3JvdXBlZC5zZXQoc3Bhbi5wYXRoLCByYW5nZXMpXG4gIH1cblxuICByZXR1cm4gWy4uLmdyb3VwZWRdLm1hcCgoW3BhdGgsIHJhbmdlc10pID0+ICh7cGF0aCwgcmFuZ2VzfSkpXG59XG5cbi8qKlxuICogVGVzdHMgd2hldGhlciBhIG1hcHBpbmcgb3JpZ2luIHJlZmVycyB0byBvbmUgc2VtYW50aWMgc291cmNlIGZpbGUuXG4gKiBAcGFyYW0ge2ltcG9ydChcIi4uL3NlbWFudGljL3R5cGVzLmpzXCIpLlNlbWFudGljT3JpZ2lufSBvcmlnaW4gLSBNYXBwaW5nIG9yaWdpbi5cbiAqIEBwYXJhbSB7c3RyaW5nfSBmaWxlbmFtZSAtIFNlbWFudGljIHNvdXJjZSBmaWxlbmFtZS5cbiAqIEByZXR1cm5zIHtib29sZWFufSBXaGV0aGVyIHRoZSBvcmlnaW4gb3ducyB0aGUgZmlsZW5hbWUuXG4gKi9cbmZ1bmN0aW9uIG9yaWdpblJlZmVyZW5jZXNGaWxlbmFtZShvcmlnaW4sIGZpbGVuYW1lKSB7XG4gIGlmIChvcmlnaW4ua2luZCA9PSBcInNvdXJjZVwiKSByZXR1cm4gb3JpZ2luLmxvY2F0aW9uLmZpbGVuYW1lID09IGZpbGVuYW1lXG4gIGlmIChvcmlnaW4ua2luZCA9PSBcImRlcml2ZWRcIikgcmV0dXJuIG9yaWdpbi5vcmlnaW5zLnNvbWUoKHtsb2NhdGlvbn0pID0+IGxvY2F0aW9uLmZpbGVuYW1lID09IGZpbGVuYW1lKVxuXG4gIHJldHVybiBvcmlnaW4ucmVsYXRlZE9yaWdpbnMuc29tZSgoe2xvY2F0aW9ufSkgPT4gbG9jYXRpb24uZmlsZW5hbWUgPT0gZmlsZW5hbWUpXG59XG5cbi8qKlxuICogSGFzaGVzIGV4YWN0IHRleHQgb3IgYnl0ZXMgZm9yIG93bmVyc2hpcCByZWNvcmRzLlxuICogQHBhcmFtIHtzdHJpbmcgfCBVaW50OEFycmF5fSBjb250ZW50IC0gRXhhY3QgYXJ0aWZhY3QgY29udGVudC5cbiAqIEByZXR1cm5zIHtzdHJpbmd9IExvd2VyY2FzZSBTSEEtMjU2IGRpZ2VzdC5cbiAqL1xuZnVuY3Rpb24gaGFzaENvbnRlbnQoY29udGVudCkge1xuICByZXR1cm4gY3JlYXRlSGFzaChcInNoYTI1NlwiKS51cGRhdGUoY29udGVudCkuZGlnZXN0KFwiaGV4XCIpXG59XG5cbi8qKlxuICogU2VyaWFsaXplcyBKU09OIHdpdGggcmVjdXJzaXZlbHkgc3RhYmxlIG9iamVjdC1rZXkgb3JkZXJpbmcuXG4gKiBAcGFyYW0ge3Vua25vd259IHZhbHVlIC0gSlNPTi1jb21wYXRpYmxlIHZhbHVlLlxuICogQHJldHVybnMge3N0cmluZ30gQ2Fub25pY2FsIGluZGVudGVkIEpTT04gd2l0aCB0cmFpbGluZyBuZXdsaW5lLlxuICovXG5mdW5jdGlvbiBzdHJpbmdpZnlDYW5vbmljYWxKc29uKHZhbHVlKSB7XG4gIHJldHVybiBgJHtKU09OLnN0cmluZ2lmeShzb3J0SnNvbih2YWx1ZSksIG51bGwsIDIpfVxcbmBcbn1cblxuLyoqXG4gKiBSZWN1cnNpdmVseSBzb3J0cyBvYmplY3Qga2V5cyB3aXRob3V0IGNoYW5naW5nIGFycmF5IG9yZGVyLlxuICogQHBhcmFtIHt1bmtub3dufSB2YWx1ZSAtIEpTT04tY29tcGF0aWJsZSB2YWx1ZS5cbiAqIEByZXR1cm5zIHt1bmtub3dufSBDYW5vbmljYWxseSBvcmRlcmVkIHZhbHVlLlxuICovXG5mdW5jdGlvbiBzb3J0SnNvbih2YWx1ZSkge1xuICBpZiAoQXJyYXkuaXNBcnJheSh2YWx1ZSkpIHJldHVybiB2YWx1ZS5tYXAoc29ydEpzb24pXG4gIGlmICghaXNQbGFpbk9iamVjdCh2YWx1ZSkpIHJldHVybiB2YWx1ZVxuXG4gIHJldHVybiBPYmplY3QuZnJvbUVudHJpZXMoT2JqZWN0LmtleXModmFsdWUpLnNvcnQoKS5tYXAoa2V5ID0+IFtrZXksIHNvcnRKc29uKHZhbHVlW2tleV0pXSkpXG59XG5cbi8qKlxuICogVGVzdHMgYSBsb3dlcmNhc2UgS290bGluLWNvbXBhdGlibGUgcGFja2FnZSBpZGVudGl0eS5cbiAqIEBwYXJhbSB7c3RyaW5nfSB2YWx1ZSAtIENhbmRpZGF0ZSBwYWNrYWdlIG5hbWUuXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gV2hldGhlciB0aGUgbmFtZSBpcyBhY2NlcHRlZC5cbiAqL1xuZnVuY3Rpb24gaXNQYWNrYWdlTmFtZSh2YWx1ZSkge1xuICBjb25zdCBwYXJ0cyA9IHZhbHVlLnNwbGl0KFwiLlwiKVxuXG4gIHJldHVybiBwYXJ0cy5sZW5ndGggPj0gMiAmJiBwYXJ0cy5ldmVyeShwYXJ0ID0+IC9eW2Etel1bYS16MC05X10qJC91LnRlc3QocGFydCkgJiYgIWtvdGxpbktleXdvcmRzLmhhcyhwYXJ0KSlcbn1cblxuLyoqXG4gKiBSZWplY3RzIGxhdW5jaGVyIG5hbWVzIHRoYXQgc2hhZG93IGFuIHVucXVhbGlmaWVkIHN5bWJvbCBpbiBnZW5lcmF0ZWQgc2FtZS1wYWNrYWdlIEtvdGxpbi5cbiAqIEBwYXJhbSB7c3RyaW5nfSBhY3Rpdml0eUNsYXNzTmFtZSAtIFZhbGlkYXRlZCBsYXVuY2hlciBjbGFzcyBuYW1lLlxuICogQHBhcmFtIHtzdHJpbmd9IGVudHJ5TW9kdWxlIC0gU2VsZWN0ZWQgc2VtYW50aWMgZW50cnkgbW9kdWxlLlxuICovXG5mdW5jdGlvbiB2YWxpZGF0ZUFjdGl2aXR5Q2xhc3NOYW1lKGFjdGl2aXR5Q2xhc3NOYW1lLCBlbnRyeU1vZHVsZSkge1xuICBjb25zdCBzZW1hbnRpY0VudHJ5U3ltYm9sID0gYFNlbWFudGlmb2xkTW9kdWxlJHttb2R1bGVOYW1lKGVudHJ5TW9kdWxlKX1gXG5cbiAgaWYgKGFuZHJvaWRTY2FmZm9sZFN5bWJvbHMuaGFzKGFjdGl2aXR5Q2xhc3NOYW1lKSB8fCBhY3Rpdml0eUNsYXNzTmFtZSA9PSBzZW1hbnRpY0VudHJ5U3ltYm9sKSB7XG4gICAgaW52YWxpZENvbmZpZ3VyYXRpb24oYEFjdGl2aXR5IGNsYXNzIG5hbWUgJyR7YWN0aXZpdHlDbGFzc05hbWV9JyBjb2xsaWRlcyB3aXRoIGdlbmVyYXRlZCBLb3RsaW4gc3ltYm9sICcke2FjdGl2aXR5Q2xhc3NOYW1lfScuYClcbiAgfVxufVxuXG4vKipcbiAqIEFsbG9jYXRlcyBvbmUgbW9kdWxlLXdpZGUgb3V0cHV0IHNpbmsgdGhhdCBjYW5ub3Qgc2hhZG93IGEgc291cmNlIGJpbmRpbmcuXG4gKiBAcGFyYW0ge2ltcG9ydChcIi4uL3NlbWFudGljL3R5cGVzLmpzXCIpLlNlbWFudGljTW9kdWxlfSBtb2R1bGUgLSBQcmVwYXJlZCBzZW1hbnRpYyBtb2R1bGUuXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBDb2xsaXNpb24tc2FmZSBLb3RsaW4gYmluZGluZy5cbiAqL1xuZnVuY3Rpb24gb3V0cHV0U2lua05hbWUobW9kdWxlKSB7XG4gIGNvbnN0IGJpbmRpbmdzID0gbmV3IFNldChtb2R1bGUuZnVuY3Rpb25zLmZsYXRNYXAoZGVjbGFyYXRpb24gPT4gZGVjbGFyYXRpb24ucGFyYW1ldGVycy5tYXAoKHtuYW1lfSkgPT4gbmFtZSkpKVxuXG4gIGZvciAoY29uc3QgZGVjbGFyYXRpb24gb2YgbW9kdWxlLmZ1bmN0aW9ucykgY29sbGVjdEJsb2NrQmluZGluZ05hbWVzKGRlY2xhcmF0aW9uLmJvZHksIGJpbmRpbmdzKVxuICBjb2xsZWN0QmxvY2tCaW5kaW5nTmFtZXMobW9kdWxlLmVudHJ5UG9pbnQuYm9keSwgYmluZGluZ3MpXG4gIGxldCBjYW5kaWRhdGUgPSBcInNlbWFudGlmb2xkT3V0cHV0XCJcblxuICB3aGlsZSAoYmluZGluZ3MuaGFzKGNhbmRpZGF0ZSkpIGNhbmRpZGF0ZSArPSBcIl9cIlxuXG4gIHJldHVybiBjYW5kaWRhdGVcbn1cblxuLyoqXG4gKiBDb2xsZWN0cyBsb2NhbCBiaW5kaW5ncyByZWN1cnNpdmVseSBmcm9tIHRoZSBzdXBwb3J0ZWQgc2VtYW50aWMgYmxvY2sgc3Vic2V0LlxuICogQHBhcmFtIHtpbXBvcnQoXCIuLi9zZW1hbnRpYy90eXBlcy5qc1wiKS5CbG9ja30gYmxvY2sgLSBTZW1hbnRpYyBibG9jay5cbiAqIEBwYXJhbSB7U2V0PHN0cmluZz59IGJpbmRpbmdzIC0gTXV0YWJsZSBiaW5kaW5nLW5hbWUgc2V0LlxuICovXG5mdW5jdGlvbiBjb2xsZWN0QmxvY2tCaW5kaW5nTmFtZXMoYmxvY2ssIGJpbmRpbmdzKSB7XG4gIGZvciAoY29uc3Qgc3RhdGVtZW50IG9mIGJsb2NrLnN0YXRlbWVudHMpIHtcbiAgICBpZiAoc3RhdGVtZW50LmtpbmQgPT0gXCJMb2NhbERlY2xhcmF0aW9uXCIpIGJpbmRpbmdzLmFkZChzdGF0ZW1lbnQubmFtZSlcbiAgICBlbHNlIGlmIChzdGF0ZW1lbnQua2luZCA9PSBcIklmU3RhdGVtZW50XCIpIHtcbiAgICAgIGNvbGxlY3RCbG9ja0JpbmRpbmdOYW1lcyhzdGF0ZW1lbnQuY29uc2VxdWVudCwgYmluZGluZ3MpXG4gICAgICBpZiAoc3RhdGVtZW50LmFsdGVybmF0ZSkgY29sbGVjdEJsb2NrQmluZGluZ05hbWVzKHN0YXRlbWVudC5hbHRlcm5hdGUsIGJpbmRpbmdzKVxuICAgIH1cbiAgfVxufVxuXG4vKipcbiAqIE1hcHMgYSBwcmVmbGlnaHRlZCBtb2R1bGUgaWRlbnRpdHkgdG8gYSBzdGFibGUgS290bGluIHN1ZmZpeC5cbiAqIEBwYXJhbSB7c3RyaW5nfSBpZCAtIFNlbWFudGljIG1vZHVsZSBpZGVudGl0eS5cbiAqIEByZXR1cm5zIHtzdHJpbmd9IEtvdGxpbiBvYmplY3QtbmFtZSBzdWZmaXguXG4gKi9cbmZ1bmN0aW9uIG1vZHVsZU5hbWUoaWQpIHtcbiAgcmV0dXJuIGlkLnNwbGl0KC9bLl8tXSsvdSkubWFwKHBhcnQgPT4gYCR7cGFydFswXS50b1VwcGVyQ2FzZSgpfSR7cGFydC5zbGljZSgxKX1gKS5qb2luKFwiXCIpXG59XG5cbi8qKlxuICogV3JhcHMgbGVnYWN5IHNpbmdsZS1tb2R1bGUgaW5wdXQgaW4gdGhlIGNhbm9uaWNhbCBwcm9ncmFtIGNvbnRyYWN0LlxuICogQHBhcmFtIHtpbXBvcnQoXCIuLi9zZW1hbnRpYy90eXBlcy5qc1wiKS5TZW1hbnRpY01vZHVsZSB8IHVuZGVmaW5lZH0gY2FuZGlkYXRlIC0gQ2FuZGlkYXRlIHNlbWFudGljIG1vZHVsZS5cbiAqIEByZXR1cm5zIHtpbXBvcnQoXCIuLi9zZW1hbnRpYy90eXBlcy5qc1wiKS5TZW1hbnRpY1Byb2dyYW19IENhbm9uaWNhbCBzaW5nbGUtbW9kdWxlIHByb2dyYW0uXG4gKi9cbmZ1bmN0aW9uIG5vcm1hbGl6ZVNpbmdsZU1vZHVsZShjYW5kaWRhdGUpIHtcbiAgaWYgKCFpc1BsYWluT2JqZWN0KGNhbmRpZGF0ZSkpIHVuc3VwcG9ydGVkQ2FwYWJpbGl0eShcImFuZHJvaWRcIiwgXCJtaXNzaW5nIG9yIGludmFsaWQgbW9kdWxlXCIsIHVuZGVmaW5lZClcbiAgY29uc3QgbW9kdWxlID0gc3RydWN0dXJlZENsb25lKGNhbmRpZGF0ZSlcbiAgY29uc3Qgc291cmNlcyA9IG1vZHVsZS5wcm92ZW5hbmNlPy5zb3VyY2VzXG5cbiAgaWYgKCFBcnJheS5pc0FycmF5KHNvdXJjZXMpIHx8IHNvdXJjZXMubGVuZ3RoICE9IDEgfHwgc291cmNlc1swXS5jb250ZW50ID09PSBudWxsIHx8XG4gICAgc291cmNlc1swXS5maWxlbmFtZSAhPSBtb2R1bGUubG9jYXRpb24/LmZpbGVuYW1lIHx8IHR5cGVvZiBzb3VyY2VzWzBdLmxhbmd1YWdlICE9IFwic3RyaW5nXCIpIHtcbiAgICB1bnN1cHBvcnRlZENhcGFiaWxpdHkoXCJhbmRyb2lkXCIsIFwic2luZ2xlLW1vZHVsZSBzb3VyY2Ugb3duZXJzaGlwXCIsIG1vZHVsZS5sb2NhdGlvbilcbiAgfVxuICByZWtleU1vZHVsZURlY2xhcmF0aW9ucyhtb2R1bGUsIFwibWFpblwiKVxuICBjb25zdCBleHBvcnRzID0gW1xuICAgIC4uLihtb2R1bGUucmVjb3JkcyA/PyBbXSkubWFwKGRlY2xhcmF0aW9uID0+IGFwcGxpY2F0aW9uRXhwb3J0KGRlY2xhcmF0aW9uLCBcInJlY29yZFwiKSksXG4gICAgLi4uKG1vZHVsZS5jbGFzc2VzID8/IFtdKS5tYXAoZGVjbGFyYXRpb24gPT4gYXBwbGljYXRpb25FeHBvcnQoZGVjbGFyYXRpb24sIFwiY2xhc3NcIikpLFxuICAgIC4uLihtb2R1bGUuZXJyb3JzID8/IFtdKS5tYXAoZGVjbGFyYXRpb24gPT4gYXBwbGljYXRpb25FeHBvcnQoZGVjbGFyYXRpb24sIFwiZXJyb3JcIikpLFxuICAgIC4uLm1vZHVsZS5mdW5jdGlvbnMubWFwKGRlY2xhcmF0aW9uID0+IGFwcGxpY2F0aW9uRXhwb3J0KGRlY2xhcmF0aW9uLCBcImZ1bmN0aW9uXCIpKVxuICBdXG4gIGNvbnN0IHByb2dyYW1Nb2R1bGUgPSB7Li4ubW9kdWxlLCBleHBvcnRzLCBpZDogXCJtYWluXCIsIGltcG9ydHM6IFtdLCBzb3VyY2VGaWxlbmFtZTogc291cmNlc1swXS5maWxlbmFtZX1cblxuICByZXR1cm4ge2VudHJ5TW9kdWxlOiBcIm1haW5cIiwga2luZDogXCJQcm9ncmFtXCIsIG1vZHVsZXM6IFtwcm9ncmFtTW9kdWxlXSwgc291cmNlczogc3RydWN0dXJlZENsb25lKHNvdXJjZXMpfVxufVxuXG4vKipcbiAqIENyZWF0ZXMgYW4gYXBwbGljYXRpb24tbG9jYWwgZXhwb3J0IHJlY29yZCBmb3Igc2luZ2xlLW1vZHVsZSBjb21wYXRpYmlsaXR5LlxuICogQHBhcmFtIHt7aWQ/OiBzdHJpbmcsIGxvY2F0aW9uOiBpbXBvcnQoXCIuLi9zZW1hbnRpYy90eXBlcy5qc1wiKS5Tb3VyY2VMb2NhdGlvbiwgbmFtZTogc3RyaW5nfX0gZGVjbGFyYXRpb24gLSBTZW1hbnRpYyBkZWNsYXJhdGlvbi5cbiAqIEBwYXJhbSB7aW1wb3J0KFwiLi4vc2VtYW50aWMvdHlwZXMuanNcIikuU2VtYW50aWNEZWNsYXJhdGlvbktpbmR9IHN5bWJvbEtpbmQgLSBEZWNsYXJhdGlvbiBraW5kLlxuICogQHJldHVybnMge2ltcG9ydChcIi4uL3NlbWFudGljL3R5cGVzLmpzXCIpLlNlbWFudGljRXhwb3J0fSBTZW1hbnRpYyBleHBvcnQuXG4gKi9cbmZ1bmN0aW9uIGFwcGxpY2F0aW9uRXhwb3J0KGRlY2xhcmF0aW9uLCBzeW1ib2xLaW5kKSB7XG4gIHJldHVybiB7ZGVjbGFyYXRpb25JZDogLyoqIEB0eXBlIHtzdHJpbmd9ICovIChkZWNsYXJhdGlvbi5pZCksIGV4cG9ydGVkTmFtZTogZGVjbGFyYXRpb24ubmFtZSxcbiAgICBraW5kOiAvKiogQHR5cGUge2NvbnN0fSAqLyAoXCJFeHBvcnRcIiksIGxvY2F0aW9uOiBkZWNsYXJhdGlvbi5sb2NhdGlvbiwgc3ltYm9sS2luZH1cbn1cblxuLyoqXG4gKiBRdWFsaWZpZXMgc2luZ2xlLW1vZHVsZSBkZWNsYXJhdGlvbiBpZGVudGl0aWVzIGJlZm9yZSBwcm9ncmFtIHByZWZsaWdodC5cbiAqIEBwYXJhbSB7aW1wb3J0KFwiLi4vc2VtYW50aWMvdHlwZXMuanNcIikuU2VtYW50aWNNb2R1bGV9IG1vZHVsZSAtIENsb25lZCBzZW1hbnRpYyBtb2R1bGUuXG4gKiBAcGFyYW0ge3N0cmluZ30gbW9kdWxlSWQgLSBDYW5vbmljYWwgbW9kdWxlIGlkZW50aXR5LlxuICovXG5mdW5jdGlvbiByZWtleU1vZHVsZURlY2xhcmF0aW9ucyhtb2R1bGUsIG1vZHVsZUlkKSB7XG4gIGNvbnN0IGRlY2xhcmF0aW9ucyA9IFsuLi5tb2R1bGUucmVjb3JkcyA/PyBbXSwgLi4ubW9kdWxlLmNsYXNzZXMgPz8gW10sIC4uLm1vZHVsZS5lcnJvcnMgPz8gW10sIC4uLm1vZHVsZS5mdW5jdGlvbnNdXG4gIGNvbnN0IHJlcGxhY2VtZW50cyA9IG5ldyBNYXAoZGVjbGFyYXRpb25zLmZpbHRlcihkZWNsYXJhdGlvbiA9PiB0eXBlb2YgZGVjbGFyYXRpb24uaWQgPT0gXCJzdHJpbmdcIilcbiAgICAubWFwKGRlY2xhcmF0aW9uID0+IFtkZWNsYXJhdGlvbi5pZCwgYCR7bW9kdWxlSWR9IyR7ZGVjbGFyYXRpb24uaWR9YF0pKVxuXG4gIGZvciAoY29uc3QgZGVjbGFyYXRpb24gb2YgZGVjbGFyYXRpb25zKSBpZiAodHlwZW9mIGRlY2xhcmF0aW9uLmlkID09IFwic3RyaW5nXCIpIGRlY2xhcmF0aW9uLmlkID0gcmVwbGFjZW1lbnRzLmdldChkZWNsYXJhdGlvbi5pZClcbiAgY29uc3Qgc2VlbiA9IG5ldyBTZXQoKVxuICBjb25zdCBpZGVudGl0eUZpZWxkcyA9IG5ldyBTZXQoW1wiY2xhc3NJZFwiLCBcImRlY2xhcmF0aW9uSWRcIiwgXCJmaWVsZFwiLCBcIm1ldGhvZFwiLCBcInBhcmFtZXRlcklkXCJdKVxuICAvKipcbiAgICogUmV3cml0ZXMgZGVjbGFyYXRpb24gcmVmZXJlbmNlcyB0aHJvdWdob3V0IG9uZSBzZW1hbnRpYyB2YWx1ZSBncmFwaC5cbiAgICogQHBhcmFtIHt1bmtub3dufSB2YWx1ZSAtIENhbmRpZGF0ZSBuZXN0ZWQgc2VtYW50aWMgdmFsdWUuXG4gICAqL1xuICBjb25zdCB2aXNpdCA9IHZhbHVlID0+IHtcbiAgICBpZiAoIXZhbHVlIHx8IHR5cGVvZiB2YWx1ZSAhPSBcIm9iamVjdFwiIHx8IHNlZW4uaGFzKHZhbHVlKSkgcmV0dXJuXG4gICAgc2Vlbi5hZGQodmFsdWUpXG4gICAgZm9yIChjb25zdCBba2V5LCBuZXN0ZWRdIG9mIE9iamVjdC5lbnRyaWVzKHZhbHVlKSkge1xuICAgICAgaWYgKCFBcnJheS5pc0FycmF5KHZhbHVlKSAmJiBpZGVudGl0eUZpZWxkcy5oYXMoa2V5KSAmJiB0eXBlb2YgbmVzdGVkID09IFwic3RyaW5nXCIgJiYgcmVwbGFjZW1lbnRzLmhhcyhuZXN0ZWQpKSB7XG4gICAgICAgIFJlZmxlY3Quc2V0KHZhbHVlLCBrZXksIHJlcGxhY2VtZW50cy5nZXQobmVzdGVkKSlcbiAgICAgIH0gZWxzZSB2aXNpdChuZXN0ZWQpXG4gICAgfVxuICB9XG5cbiAgdmlzaXQobW9kdWxlKVxufVxuXG4vKipcbiAqIEVzY2FwZXMgYSBjb25maWd1cmF0aW9uIHZhbHVlIGZvciBYTUwgdGV4dCBvciBhdHRyaWJ1dGVzLlxuICogQHBhcmFtIHtzdHJpbmd9IHZhbHVlIC0gUmF3IGNvbmZpZ3VyYXRpb24gdmFsdWUuXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBYTUwtc2FmZSB2YWx1ZS5cbiAqL1xuZnVuY3Rpb24geG1sRXNjYXBlKHZhbHVlKSB7XG4gIHJldHVybiB2YWx1ZS5yZXBsYWNlQWxsKFwiJlwiLCBcIiZhbXA7XCIpLnJlcGxhY2VBbGwoXCI8XCIsIFwiJmx0O1wiKS5yZXBsYWNlQWxsKFwiPlwiLCBcIiZndDtcIilcbiAgICAucmVwbGFjZUFsbCgnXCInLCBcIiZxdW90O1wiKS5yZXBsYWNlQWxsKFwiJ1wiLCBcIiZhcG9zO1wiKVxufVxuXG4vKipcbiAqIEVzY2FwZXMgYSBjb25maWd1cmF0aW9uIHZhbHVlIGZvciBhIEtvdGxpbiBzdHJpbmcgbGl0ZXJhbCBib2R5LlxuICogQHBhcmFtIHtzdHJpbmd9IHZhbHVlIC0gUmF3IGNvbmZpZ3VyYXRpb24gdmFsdWUuXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBLb3RsaW4tc2FmZSB2YWx1ZS5cbiAqL1xuZnVuY3Rpb24ga290bGluRXNjYXBlKHZhbHVlKSB7XG4gIHJldHVybiB2YWx1ZS5yZXBsYWNlQWxsKFwiXFxcXFwiLCBcIlxcXFxcXFxcXCIpLnJlcGxhY2VBbGwoJ1wiJywgJ1xcXFxcIicpLnJlcGxhY2VBbGwoXCIkXCIsIFwiXFxcXCRcIilcbn1cblxuLyoqXG4gKiBVcHBlcmNhc2VzIHRoZSBpbml0aWFsIEFTQ0lJIGNoYXJhY3RlciBvZiBhIGtub3duIG5vbi1lbXB0eSB3b3JkLlxuICogQHBhcmFtIHtzdHJpbmd9IHZhbHVlIC0gTm9uLWVtcHR5IHdvcmQuXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBDYXBpdGFsaXplZCB3b3JkLlxuICovXG5mdW5jdGlvbiBjYXBpdGFsaXplKHZhbHVlKSB7XG4gIHJldHVybiBgJHt2YWx1ZVswXS50b1VwcGVyQ2FzZSgpfSR7dmFsdWUuc2xpY2UoMSl9YFxufVxuXG4vKipcbiAqIFRlc3RzIHdoZXRoZXIgYSB2YWx1ZSBpcyBhIHBsYWluIGRhdGEgb2JqZWN0LlxuICogQHBhcmFtIHt1bmtub3dufSB2YWx1ZSAtIENhbmRpZGF0ZSB2YWx1ZS5cbiAqIEByZXR1cm5zIHt2YWx1ZSBpcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPn0gV2hldGhlciB0aGUgdmFsdWUgaXMgYSBwbGFpbiBvYmplY3QuXG4gKi9cbmZ1bmN0aW9uIGlzUGxhaW5PYmplY3QodmFsdWUpIHtcbiAgcmV0dXJuIEJvb2xlYW4odmFsdWUpICYmIHR5cGVvZiB2YWx1ZSA9PSBcIm9iamVjdFwiICYmICFBcnJheS5pc0FycmF5KHZhbHVlKSAmJiBPYmplY3QuZ2V0UHJvdG90eXBlT2YodmFsdWUpID09IE9iamVjdC5wcm90b3R5cGVcbn1cblxuLyoqXG4gKiBUaHJvd3MgYSBzdGFibGUgaW52YWxpZC1yZXF1ZXN0IGRpYWdub3N0aWMuXG4gKiBAcGFyYW0ge3N0cmluZ30gbWVzc2FnZSAtIERpYWdub3N0aWMgbWVzc2FnZS5cbiAqIEByZXR1cm5zIHtuZXZlcn0gTmV2ZXIgcmV0dXJucy5cbiAqL1xuZnVuY3Rpb24gaW52YWxpZElucHV0KG1lc3NhZ2UpIHtcbiAgdGhyb3cgbmV3IFNlbWFudGlmb2xkRGlhZ25vc3RpYyh7Y29kZTogXCJJTlZBTElEX0FQUExJQ0FUSU9OX0lOUFVUXCIsIGxhbmd1YWdlOiBcImFuZHJvaWRcIiwgbWVzc2FnZX0pXG59XG5cbi8qKlxuICogVGhyb3dzIGEgc3RhYmxlIGludmFsaWQtY29uZmlndXJhdGlvbiBkaWFnbm9zdGljLlxuICogQHBhcmFtIHtzdHJpbmd9IG1lc3NhZ2UgLSBEaWFnbm9zdGljIG1lc3NhZ2UuXG4gKiBAcmV0dXJucyB7bmV2ZXJ9IE5ldmVyIHJldHVybnMuXG4gKi9cbmZ1bmN0aW9uIGludmFsaWRDb25maWd1cmF0aW9uKG1lc3NhZ2UpIHtcbiAgdGhyb3cgbmV3IFNlbWFudGlmb2xkRGlhZ25vc3RpYyh7Y29kZTogXCJJTlZBTElEX0FQUExJQ0FUSU9OX0NPTkZJR1VSQVRJT05cIiwgbGFuZ3VhZ2U6IFwiYW5kcm9pZFwiLCBtZXNzYWdlfSlcbn1cblxuLyoqXG4gKiBUaHJvd3MgYSBzdGFibGUgaW52YWxpZC1yZXNvdXJjZSBkaWFnbm9zdGljLlxuICogQHBhcmFtIHtzdHJpbmd9IG1lc3NhZ2UgLSBEaWFnbm9zdGljIG1lc3NhZ2UuXG4gKiBAcmV0dXJucyB7bmV2ZXJ9IE5ldmVyIHJldHVybnMuXG4gKi9cbmZ1bmN0aW9uIGludmFsaWRSZXNvdXJjZShtZXNzYWdlKSB7XG4gIHRocm93IG5ldyBTZW1hbnRpZm9sZERpYWdub3N0aWMoe2NvZGU6IFwiSU5WQUxJRF9BUFBMSUNBVElPTl9SRVNPVVJDRVwiLCBsYW5ndWFnZTogXCJhbmRyb2lkXCIsIG1lc3NhZ2V9KVxufVxuXG4vKipcbiAqIFRocm93cyBhIHN0YWJsZSBpbnZhbGlkLXBhdGggZGlhZ25vc3RpYy5cbiAqIEBwYXJhbSB7c3RyaW5nfSBtZXNzYWdlIC0gRGlhZ25vc3RpYyBtZXNzYWdlLlxuICogQHJldHVybnMge25ldmVyfSBOZXZlciByZXR1cm5zLlxuICovXG5mdW5jdGlvbiBpbnZhbGlkUGF0aChtZXNzYWdlKSB7XG4gIHRocm93IG5ldyBTZW1hbnRpZm9sZERpYWdub3N0aWMoe2NvZGU6IFwiSU5WQUxJRF9BUFBMSUNBVElPTl9QQVRIXCIsIGxhbmd1YWdlOiBcImFuZHJvaWRcIiwgbWVzc2FnZX0pXG59XG4iXX0=
|