import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Toolkit;
import java.awt.datatransfer.StringSelection;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.Formatter;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.WindowConstants;
/**
* メインクラス
*/
public class Main {
/**
* 1体の敵
*/
static class Enemy {
/**
* 敵の種別
*/
enum Kind {
/**
* 敵V
*/
V,
/**
* 敵H
*/
H,
/**
* 敵L
*/
L,
/**
* 敵R
*/
R,
/**
* 敵Jが敵Lになっている状態
*/
JL,
/**
* 敵Jが敵Rになっている状態
*/
JR,
}
/**
* X座標
*/
int x;
/**
* Y座標
*/
int y;
/**
* 直前のX座標
*/
int lastX;
/**
* 直前のY座標
*/
int lastY;
/**
* 種類
*/
Kind kind;
/**
* 敵を初期化
* @param x X座標
* @param y Y座標
* @param kind 種類
*/
Enemy(final int x, final int y, final Kind kind) {
this.x = x;
this.y = y;
this.lastX = x;
this.lastY = y;
this.kind = kind;
}
/**
* 移動
* @param data データ
* @param player プレイヤ
* @param time 時刻
*/
void move(final char[][] data, final Player player, final AtomicInteger time) {
final char down = data[this.y + 1][this.x];
final char left = data[this.y][this.x - 1];
final char up = data[this.y - 1][this.x];
final char right = data[this.y][this.x + 1];
if (time.get() == 0) {
/*
* 時刻 t = 0 においては、初期位置の 下、左、上、右 の順で最初に進入可能なマスの方向に移動します。
*/
if (down != '#') {
this.y++;
} else if (left != '#') {
this.x--;
} else if (up != '#') {
this.y--;
} else if (right != '#') {
this.x++;
} else {
System.
err.
printf("動けません。t = %d, x = %d, y = %d, kind = %s\n", time.
get(),
this.x, this.y, this.kind);
}
} else {
final int numOfWalls = (down == '#' ? 1 : 0) + (left == '#' ? 1 : 0)
+ (up == '#' ? 1 : 0) + (right == '#' ? 1 : 0);
if (numOfWalls == 3) {
/*
* 行き止まりマスの場合、唯一進入可能な隣接マスに移動します。
*/
if (down != '#') {
this.y++;
} else if (left != '#') {
this.x--;
} else if (up != '#') {
this.y--;
} else if (right != '#') {
this.x++;
} else {
System.
err.
printf("動けません。t = %d, x = %d, y = %d, kind = %s\n", time.
get(),
this.x, this.y, this.kind);
}
} else if (numOfWalls == 2) {
/*
* 通路マスの場合、時刻 t-1 に居たマス以外の進入可能な隣接するマスに移動します。
*/
if (down != '#' && this.lastY != this.y + 1) {
this.y++;
} else if (left != '#' && this.lastX != this.x - 1) {
this.x--;
} else if (up != '#' && this.lastY != this.y - 1) {
this.y--;
} else if (right != '#' && this.lastX != this.x + 1) {
this.x++;
} else {
System.
err.
printf("動けません。t = %d, x = %d, y = %d, kind = %s\n", time.
get(),
this.x, this.y, this.kind);
}
} else if (numOfWalls == 1 || numOfWalls == 0) {
/*
* 交差点マスの場合は、敵の種別に応じたアルゴリズムに応じて移動方向を決定します。
*/
final int dX = player.lastX - this.x;
final int dY = player.lastY - this.y;
final int signDX
= (int) Math.
signum(dX
); final int signDY
= (int) Math.
signum(dY
); switch (this.kind) {
case V:
/*
* 敵から見た自機の相対位置を (dx, dy) と表すものとします。次のルールを上から順に適用し、最初に選ばれた方向に移動します。
* 1. dy ≠ 0 でかつ dy の符号方向にあるマスが進入可能であれば、その方向に移動します。
* 2. dx ≠ 0 でかつ dx の符号方向にあるマスが進入可能であれば、その方向に移動します。
* 3. 現在位置の 下、左、上、右 の順で最初に進入可能なマスの方向に移動する。
*/
if (dY != 0 && data[this.y + signDY][this.x] != '#') {
this.y += signDY;
} else if (dX != 0 && data[this.y][this.x + signDX] != '#') {
this.x += signDX;
} else {
if (down != '#') {
this.y++;
} else if (left != '#') {
this.x--;
} else if (right != '#') {
this.x++;
} else if (up != '#') {
this.y--;
} else {
System.
err.
printf("動けません。t = %d, x = %d, y = %d, kind = %s\n",
time.get(), this.x, this.y, this.kind);
}
}
break;
case H:
/*
* 敵 V とほぼ同じです。唯一異なるのは 、進行方向を決めるルールのうち、
* 最初の二つのルールの適用順序が入れ替わるところです。
* すなわち、先に dx ≠ 0 のチェックを行ない、次に dy ≠ 0 のチェックを行います。
*/
if (dX != 0 && data[this.y][this.x + signDX] != '#') {
this.x += signDX;
} else if (dY != 0 && data[this.y + signDY][this.x] != '#') {
this.y += signDY;
} else {
if (down != '#') {
this.y++;
} else if (left != '#') {
this.x--;
} else if (right != '#') {
this.x++;
} else if (up != '#') {
this.y--;
} else {
System.
err.
printf("動けません。t = %d, x = %d, y = %d, kind = %s\n",
time.get(), this.x, this.y, this.kind);
}
}
break;
case L:
case JL:
/*
* 現在位置への進入方向から見て相対的に 左、前、右 の順で最初に進入可能なマスの方向に移動します。
*/
if (this.lastY + 1 == this.y) {
if (right != '#') {
this.x++;
} else if (down != '#') {
this.y++;
} else if (left != '#') {
this.x--;
} else {
System.
err.
printf("動けません。t = %d, x = %d, y = %d, kind = %s\n",
time.get(), this.x, this.y, this.kind);
}
} else if (this.lastX - 1 == this.x) {
if (down != '#') {
this.y++;
} else if (left != '#') {
this.x--;
} else if (up != '#') {
this.y--;
} else {
System.
err.
printf("動けません。t = %d, x = %d, y = %d, kind = %s\n",
time.get(), this.x, this.y, this.kind);
}
} else if (this.lastY - 1 == this.y) {
if (left != '#') {
this.x--;
} else if (up != '#') {
this.y--;
} else if (right != '#') {
this.x++;
} else {
System.
err.
printf("動けません。t = %d, x = %d, y = %d, kind = %s\n",
time.get(), this.x, this.y, this.kind);
}
} else if (this.lastX + 1 == this.x) {
if (up != '#') {
this.y--;
} else if (right != '#') {
this.x++;
} else if (down != '#') {
this.y++;
} else {
System.
err.
printf("動けません。t = %d, x = %d, y = %d, kind = %s\n",
time.get(), this.x, this.y, this.kind);
}
} else {
System.
err.
printf("止まっていた?t = %d, x = %d, y = %d, kind = %s\n",
time.get(), this.x, this.y, this.kind);
}
break;
case R:
case JR:
/*
* 現在位置への進入方向から見て相対的に 右、前、左 の順で最初に進入可能なマスの方向に移動します。
*/
if (this.lastY + 1 == this.y) {
if (left != '#') {
this.x--;
} else if (down != '#') {
this.y++;
} else if (right != '#') {
this.x++;
} else {
System.
err.
printf("動けません。t = %d, x = %d, y = %d, kind = %s\n",
time.get(), this.x, this.y, this.kind);
}
} else if (this.lastX - 1 == this.x) {
if (up != '#') {
this.y--;
} else if (left != '#') {
this.x--;
} else if (down != '#') {
this.y++;
} else {
System.
err.
printf("動けません。t = %d, x = %d, y = %d, kind = %s\n",
time.get(), this.x, this.y, this.kind);
}
} else if (this.lastY - 1 == this.y) {
if (right != '#') {
this.x++;
} else if (up != '#') {
this.y--;
} else if (left != '#') {
this.x--;
} else {
System.
err.
printf("動けません。t = %d, x = %d, y = %d, kind = %s\n",
time.get(), this.x, this.y, this.kind);
}
} else if (this.lastX + 1 == this.x) {
if (down != '#') {
this.y++;
} else if (right != '#') {
this.x++;
} else if (up != '#') {
this.y--;
} else {
System.
err.
printf("動けません。t = %d, x = %d, y = %d, kind = %s\n",
time.get(), this.x, this.y, this.kind);
}
} else {
System.
err.
printf("止まっていた?t = %d, x = %d, y = %d, kind = %s\n",
time.get(), this.x, this.y, this.kind);
}
break;
}
/*
* 交差点マスに入るたびに、最初は敵Lの行動、次回は敵Rの行動、さらに次回はまた敵Lの行動、と繰り返します。
*/
this.kind = this.kind == Kind.JL ? Kind.JR : (this.kind == Kind.JR ? Kind.JL
: this.kind);
} else {
System.
err.
printf("動けません。t = %d, x = %d, y = %d, kind = %s\n", time.
get(),
this.x, this.y, this.kind);
}
}
}
/**
* @param player プレイヤ
* @return 殺したかどうか
*/
boolean killed(final Player player) {
if (this.x == player.x && this.y == player.y) {
return true;
} else if (this.x == player.lastX && this.y == player.lastY && this.lastX == player.x
&& this.lastY == player.y) {
return true;
} else {
return false;
}
}
@Override
return new Formatter().format("[x = %d, y = %d, kind = %s]", this.x, this.y, this.kind)
.toString();
}
}
/**
* プレイヤ
*/
static class Player {
/**
* X座標
*/
int x = -1;
/**
* Y座標
*/
int y = -1;
/**
* 直前のX座標
*/
int lastX = -1;
/**
* 直前のY座標
*/
int lastY = -1;
/**
* 上へ移動
* @param data データ
* @param log ログ
*/
void moveDown(final char[][] data, final StringBuilder log) {
this.lastX = this.x;
this.lastY = this.y;
if (data[this.y + 1][this.x] != '#') {
this.y++;
}
log.append('j');
}
/**
* 左へ移動
* @param data データ
* @param log ログ
*/
void moveLeft(final char[][] data, final StringBuilder log) {
this.lastX = this.x;
this.lastY = this.y;
if (data[this.y][this.x - 1] != '#') {
this.x--;
}
log.append('h');
}
/**
* 上へ移動
* @param data データ
* @param log ログ
*/
void moveUp(final char[][] data, final StringBuilder log) {
this.lastX = this.x;
this.lastY = this.y;
if (data[this.y - 1][this.x] != '#') {
this.y--;
}
log.append('k');
}
/**
* 右へ移動
* @param data データ
* @param log ログ
*/
void moveRight(final char[][] data, final StringBuilder log) {
this.lastX = this.x;
this.lastY = this.y;
if (data[this.y][this.x + 1] != '#') {
this.x++;
}
log.append('l');
}
/**
* 留まる
* @param log ログ
*/
void stay(final StringBuilder log) {
this.lastX = this.x;
this.lastY = this.y;
log.append('.');
}
@Override
return new Formatter().format("[x = %d, y = %d, lX = %d, lY = %d]", this.x, this.y,
this.lastX, this.lastY).toString();
}
}
/**
* ステージ
*/
enum Stage {
/**
* 入力1
*/
LV1(50, 11, 7, "###########" + "#.V..#..H.#" + "#.##...##.#" + "#L#..#..R.#"
+ "#.#.###.#.#" + "#....@....#" + "###########"),
/**
* 入力2
*/
LV2(300, 20, 17, "####################" + "###.....L..........#" + "###.##.##.##L##.##.#"
+ "###.##.##.##.##.##.#" + "#.L................#" + "#.##.##.##.##.##.###"
+ "#.##.##L##.##.##.###" + "#.................L#" + "#.#.#.#J####J#.#.#.#"
+ "#L.................#" + "###.##.##.##.##.##.#" + "###.##.##R##.##.##.#"
+ "#................R.#" + "#.##.##.##.##R##.###" + "#.##.##.##.##.##.###"
+ "#@....R..........###" + "####################"),
/**
* 入力3
*/
LV3(700, 58, 17, "##########################################################"
+ "#........................................................#"
+ "#.###.#########.###############.########.###.#####.#####.#"
+ "#.###.#########.###############.########.###.#####.#####.#"
+ "#.....#########....J.............J.......###.............#"
+ "#####.###.......#######.#######.########.###.#######.#####"
+ "#####.###.#####J#######.#######.########.###.## ##.#####"
+ "#####.###L#####.## ##L## ##.## ##.###.## ##.#####"
+ "#####.###..H###.## ##.## ##.########.###.#######J#####"
+ "#####.#########.## ##L## ##.########.###.###V....#####"
+ "#####.#########.#######.#######..........###.#######.#####"
+ "#####.#########.#######.#######.########.###.#######.#####"
+ "#.....................L.........########..........R......#"
+ "#L####.##########.##.##########....##....#########.#####.#"
+ "#.####.##########.##.##########.##.##.##.#########.#####.#"
+ "#.................##............##..@.##...............R.#"
+ "##########################################################");
/**
* 制限時間
*/
int timeLimit;
/**
* 幅
*/
int width;
/**
* 高さ
*/
int height;
/**
* データ
*/
char[][] data;
/**
* 点
*/
int dots;
/**
* ステージを初期化
* @param timeLimit 制限時間
* @param width 幅
* @param height 高さ
* @param string 入力文字列
*/
Stage
(final int timeLimit,
final int width,
final int height,
final String string
) { this.timeLimit = timeLimit;
this.width = width;
this.height = height;
this.data = new char[height][width];
this.dots = 0;
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
final char character = string.charAt(i * width + j);
this.data[i][j] = character;
if (character == '.') {
this.dots++;
}
}
}
};
}
/**
* メインメソッド
* @param args コマンドライン引数
* @throws InterruptedException 割り込み例外
*/
final Stage stage = Stage.LV3;
final int timeLimit = stage.timeLimit;
final int width = stage.width;
final int height = stage.height;
final char[][] data = new char[height][width];
final Player player = new Player();
final List<Enemy> enemies = new ArrayList<Enemy>();
final AtomicInteger time = new AtomicInteger();
final AtomicInteger dots = new AtomicInteger();
final StringBuilder log = new StringBuilder();
final AtomicBoolean isDead = new AtomicBoolean();
final int size = 20;
statusLabel.setOpaque(true);
statusLabel.
setBackground(Color.
WHITE); @Override
protected void paintComponent
(final Graphics graphics
) { super.paintComponent(graphics);
g.fillOval(player.x * size, player.y * size, size, size);
for (final Enemy enemy : enemies) {
switch (enemy.kind) {
case V:
break;
case H:
break;
case L:
g.
setColor(Color.
ORANGE); break;
case R:
break;
case JL:
case JR:
break;
default:
}
g.fillOval(enemy.x * size, enemy.y * size, size, size);
}
for (final Enemy enemy : enemies) {
g.drawString(enemy.kind.toString(), enemy.x * size + size / 2
- g.getFontMetrics().stringWidth(enemy.kind.toString()) / 2, enemy.y
* size + g.getFontMetrics().getAscent());
}
g.drawString("P", player.x * size + size / 2 - g.getFontMetrics().stringWidth("P")
/ 2, player.y * size + g.getFontMetrics().getAscent());
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
switch (data[i][j]) {
case '#':
g.fillRect(j * size, i * size, size, size);
break;
case '.':
g.fillOval(j * size + size / 2 - 2, i * size + size / 2 - 2, 4, 4);
break;
}
}
}
}
};
playPanel.setFocusable(true);
@Override
public void keyPressed
(final KeyEvent e
) { switch (e.getKeyCode()) {
initialize(width, height, data, stage, player, enemies, time, dots, log,
isDead, statusLabel, timeLimit);
playPanel.repaint();
break;
playBack(log.toString().replaceFirst(".$", ""), stage, timeLimit, width,
height, data, player, enemies, time, dots, log, isDead, statusLabel,
playPanel);
playPanel.repaint();
updateStatusLabel(statusLabel, stage, timeLimit, time, dots, log);
break;
break;
break;
}
if (!isDead.get()) {
switch (e.getKeyCode()) {
player.moveDown(data, log);
break;
player.moveLeft(data, log);
break;
player.moveUp(data, log);
break;
player.moveRight(data, log);
break;
player.stay(log);
break;
default:
return;
}
moveEnemies(data, player, enemies, time, isDead);
getDot(data, player, dots);
time.incrementAndGet();
if (time.get() >= timeLimit) {
isDead.set(true);
}
if (dots.get() == stage.dots) {
System.
out.
printf("(cleared)time: %d/%d, score: %d/%d, log: %s\n",
time.get(), timeLimit, dots.get(), stage.dots, log);
isDead.set(true);
}
playPanel.repaint();
updateStatusLabel(statusLabel, stage, timeLimit, time, dots, log);
if (isDead.get()) {
statusLabel.setText((dots.get() == stage.dots ? "(cleared)" : "(dead)")
+ statusLabel.getText());
Toolkit.
getDefaultToolkit().
getSystemClipboard() }
}
}
});
playPanel.
setPreferredSize(new Dimension(width
* size, height
* size
)); playPanel.
setBackground(Color.
WHITE); frame.add(playPanel);
frame.pack();
frame.setLocationByPlatform(true);
initialize(width, height, data, stage, player, enemies, time, dots, log, isDead,
statusLabel, timeLimit);
frame.setVisible(true);
}
/**
* 初期化
* @param width 幅
* @param height 高さ
* @param data データ
* @param stage ステージ
* @param player プレイヤ
* @param enemies 敵
* @param time 時刻
* @param dots 点
* @param log ログ
* @param isDead 死んでいるかどうか
* @param statusLabel ラベル
* @param timeLimit 制限時間
*/
static void initialize(final int width, final int height, final char[][] data,
final Stage stage, final Player player, final List<Enemy> enemies,
final AtomicInteger time, final AtomicInteger dots, final StringBuilder log,
final AtomicBoolean isDead,
final JLabel statusLabel,
final int timeLimit
) { enemies.clear();
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
final char kind = stage.data[i][j];
switch (kind) {
case 'V':
data[i][j] = ' ';
enemies.add(new Enemy(j, i, Enemy.Kind.V));
break;
case 'H':
data[i][j] = ' ';
enemies.add(new Enemy(j, i, Enemy.Kind.H));
break;
case 'L':
data[i][j] = ' ';
enemies.add(new Enemy(j, i, Enemy.Kind.L));
break;
case 'R':
data[i][j] = ' ';
enemies.add(new Enemy(j, i, Enemy.Kind.R));
break;
case 'J':
data[i][j] = ' ';
enemies.add(new Enemy(j, i, Enemy.Kind.JL));
break;
case '@':
data[i][j] = ' ';
player.x = j;
player.y = i;
player.lastX = player.x;
player.lastY = player.y;
break;
case '#':
case '.':
case ' ':
data[i][j] = stage.data[i][j];
break;
default:
System.
err.
printf("unknown data: '%s', x = %d, y = %d\n", stage.
data[i
][j
], j,
i);
}
}
}
time.set(0);
dots.set(0);
log.setLength(0);
isDead.set(false);
updateStatusLabel(statusLabel, stage, timeLimit, time, dots, log);
}
/**
* リプレイ
* @param string 動作を指示する文字列
* @param stage ステージ
* @param timeLimit 制限時間
* @param width 幅
* @param height 高さ
* @param data データ
* @param player プレイヤ
* @param enemies 敵
* @param time 時刻
* @param dots 点
* @param log ログ
* @param isDead 死んでいるかどうか
* @param statusLabel ラベル
* @param playPanel パネル
*/
static void playBack
(final String string,
final Stage stage,
final int timeLimit,
final int width, final int height, final char[][] data, final Player player,
final List<Enemy> enemies, final AtomicInteger time, final AtomicInteger dots,
final StringBuilder log,
final AtomicBoolean isDead,
final JLabel statusLabel,
initialize(width, height, data, stage, player, enemies, time, dots, log, isDead,
statusLabel, timeLimit);
for (int i = 0; i < string.length(); i++) {
if (!isDead.get()) {
switch (string.charAt(i)) {
case 'j':
player.moveDown(data, log);
break;
case 'h':
player.moveLeft(data, log);
break;
case 'k':
player.moveUp(data, log);
break;
case 'l':
player.moveRight(data, log);
break;
case '.':
player.stay(log);
break;
default:
System.
err.
printf("unknown data: %s\n", string.
charAt(i
)); }
moveEnemies(data, player, enemies, time, isDead);
getDot(data, player, dots);
time.incrementAndGet();
if (time.get() >= timeLimit) {
isDead.set(true);
}
}
}
}
/**
* 点を食べる
* @param data データ
* @param player プレイヤ
* @param dots 点
*/
static void getDot(final char[][] data, final Player player, final AtomicInteger dots) {
if (data[player.y][player.x] == '.') {
data[player.y][player.x] = ' ';
dots.incrementAndGet();
}
}
/**
* 敵を移動
* @param data データ
* @param player プレイヤ
* @param enemies 敵
* @param time 時刻
* @param isDead 死んでいるかどうか
*/
static void moveEnemies(final char[][] data, final Player player, final List<Enemy> enemies,
final AtomicInteger time, final AtomicBoolean isDead) {
for (final Enemy enemy : enemies) {
final int x = enemy.x;
final int y = enemy.y;
enemy.move(data, player, time);
enemy.lastX = x;
enemy.lastY = y;
if (enemy.killed(player)) {
isDead.set(true);
}
}
}
/**
* 現在の状態を表示するラベルを更新
* @param statusLabel ラベル
* @param stage ステージ
* @param timeLimit 制限時間
* @param time 時刻
* @param dots 点
* @param log ログ
*/
static void updateStatusLabel
(final JLabel statusLabel,
final Stage stage,
final int timeLimit,
final AtomicInteger time, final AtomicInteger dots, final StringBuilder log) {
statusLabel.setText(new Formatter().format("time: %d/%d, score: %d/%d, log: %s",
time.get(), timeLimit, dots.get(), stage.dots, log).toString());
}
}
aW1wb3J0IGphdmEuYXd0LkJvcmRlckxheW91dDsKaW1wb3J0IGphdmEuYXd0LkNvbG9yOwppbXBvcnQgamF2YS5hd3QuRGltZW5zaW9uOwppbXBvcnQgamF2YS5hd3QuR3JhcGhpY3M7CmltcG9ydCBqYXZhLmF3dC5HcmFwaGljczJEOwppbXBvcnQgamF2YS5hd3QuUmVuZGVyaW5nSGludHM7CmltcG9ydCBqYXZhLmF3dC5Ub29sa2l0OwppbXBvcnQgamF2YS5hd3QuZGF0YXRyYW5zZmVyLlN0cmluZ1NlbGVjdGlvbjsKaW1wb3J0IGphdmEuYXd0LmV2ZW50LktleUFkYXB0ZXI7CmltcG9ydCBqYXZhLmF3dC5ldmVudC5LZXlFdmVudDsKaW1wb3J0IGphdmEudXRpbC5BcnJheUxpc3Q7CmltcG9ydCBqYXZhLnV0aWwuRm9ybWF0dGVyOwppbXBvcnQgamF2YS51dGlsLkxpc3Q7CmltcG9ydCBqYXZhLnV0aWwuY29uY3VycmVudC5hdG9taWMuQXRvbWljQm9vbGVhbjsKaW1wb3J0IGphdmEudXRpbC5jb25jdXJyZW50LmF0b21pYy5BdG9taWNJbnRlZ2VyOwoKaW1wb3J0IGphdmF4LnN3aW5nLkpGcmFtZTsKaW1wb3J0IGphdmF4LnN3aW5nLkpMYWJlbDsKaW1wb3J0IGphdmF4LnN3aW5nLkpQYW5lbDsKaW1wb3J0IGphdmF4LnN3aW5nLldpbmRvd0NvbnN0YW50czsKCi8qKgogKiDjg6HjgqTjg7Pjgq/jg6njgrkKICovCnB1YmxpYyBjbGFzcyBNYWluIHsKCgkvKioKCSAqIDHkvZPjga7mlbUKCSAqLwoJc3RhdGljIGNsYXNzIEVuZW15IHsKCgkJLyoqCgkJICog5pW144Gu56iu5YilCgkJICovCgkJZW51bSBLaW5kIHsKCQkJLyoqCgkJCSAqIOaVtVYKCQkJICovCgkJCVYsCgkJCS8qKgoJCQkgKiDmlbVICgkJCSAqLwoJCQlILAoJCQkvKioKCQkJICog5pW1TAoJCQkgKi8KCQkJTCwKCQkJLyoqCgkJCSAqIOaVtVIKCQkJICovCgkJCVIsCgkJCS8qKgoJCQkgKiDmlbVK44GM5pW1TOOBq+OBquOBo+OBpuOBhOOCi+eKtuaFiwoJCQkgKi8KCQkJSkwsCgkJCS8qKgoJCQkgKiDmlbVK44GM5pW1UuOBq+OBquOBo+OBpuOBhOOCi+eKtuaFiwoJCQkgKi8KCQkJSlIsCgkJfQoKCQkvKioKCQkgKiBY5bqn5qiZCgkJICovCgkJaW50IHg7CgkJLyoqCgkJICogWeW6p+aomQoJCSAqLwoJCWludCB5OwoJCS8qKgoJCSAqIOebtOWJjeOBrljluqfmqJkKCQkgKi8KCQlpbnQgbGFzdFg7CgkJLyoqCgkJICog55u05YmN44GuWeW6p+aomQoJCSAqLwoJCWludCBsYXN0WTsKCQkvKioKCQkgKiDnqK7poZ4KCQkgKi8KCQlLaW5kIGtpbmQ7CgoJCS8qKgoJCSAqIOaVteOCkuWIneacn+WMlgoJCSAqIEBwYXJhbSB4IFjluqfmqJkKCQkgKiBAcGFyYW0geSBZ5bqn5qiZCgkJICogQHBhcmFtIGtpbmQg56iu6aGeCgkJICovCgkJRW5lbXkoZmluYWwgaW50IHgsIGZpbmFsIGludCB5LCBmaW5hbCBLaW5kIGtpbmQpIHsKCQkJdGhpcy54ID0geDsKCQkJdGhpcy55ID0geTsKCQkJdGhpcy5sYXN0WCA9IHg7CgkJCXRoaXMubGFzdFkgPSB5OwoJCQl0aGlzLmtpbmQgPSBraW5kOwoJCX0KCgkJLyoqCgkJICog56e75YuVCgkJICogQHBhcmFtIGRhdGEg44OH44O844K/CgkJICogQHBhcmFtIHBsYXllciDjg5fjg6zjgqTjg6QKCQkgKiBAcGFyYW0gdGltZSDmmYLliLsKCQkgKi8KCQl2b2lkIG1vdmUoZmluYWwgY2hhcltdW10gZGF0YSwgZmluYWwgUGxheWVyIHBsYXllciwgZmluYWwgQXRvbWljSW50ZWdlciB0aW1lKSB7CgkJCWZpbmFsIGNoYXIgZG93biA9IGRhdGFbdGhpcy55ICsgMV1bdGhpcy54XTsKCQkJZmluYWwgY2hhciBsZWZ0ID0gZGF0YVt0aGlzLnldW3RoaXMueCAtIDFdOwoJCQlmaW5hbCBjaGFyIHVwID0gZGF0YVt0aGlzLnkgLSAxXVt0aGlzLnhdOwoJCQlmaW5hbCBjaGFyIHJpZ2h0ID0gZGF0YVt0aGlzLnldW3RoaXMueCArIDFdOwoJCQlpZiAodGltZS5nZXQoKSA9PSAwKSB7CgkJCQkvKgoJCQkJICog5pmC5Yi7IHQgPSAwIOOBq+OBiuOBhOOBpuOBr+OAgeWIneacn+S9jee9ruOBriDkuIvjgIHlt6bjgIHkuIrjgIHlj7Mg44Gu6aCG44Gn5pyA5Yid44Gr6YCy5YWl5Y+v6IO944Gq44Oe44K544Gu5pa55ZCR44Gr56e75YuV44GX44G+44GZ44CCCgkJCQkgKi8KCQkJCWlmIChkb3duICE9ICcjJykgewoJCQkJCXRoaXMueSsrOwoJCQkJfSBlbHNlIGlmIChsZWZ0ICE9ICcjJykgewoJCQkJCXRoaXMueC0tOwoJCQkJfSBlbHNlIGlmICh1cCAhPSAnIycpIHsKCQkJCQl0aGlzLnktLTsKCQkJCX0gZWxzZSBpZiAocmlnaHQgIT0gJyMnKSB7CgkJCQkJdGhpcy54Kys7CgkJCQl9IGVsc2UgewoJCQkJCVN5c3RlbS5lcnIucHJpbnRmKCLli5XjgZHjgb7jgZvjgpPjgIJ0ID0gJWQsIHggPSAlZCwgeSA9ICVkLCBraW5kID0gJXNcbiIsIHRpbWUuZ2V0KCksCgkJCQkJCQl0aGlzLngsIHRoaXMueSwgdGhpcy5raW5kKTsKCQkJCX0KCQkJfSBlbHNlIHsKCQkJCWZpbmFsIGludCBudW1PZldhbGxzID0gKGRvd24gPT0gJyMnID8gMSA6IDApICsgKGxlZnQgPT0gJyMnID8gMSA6IDApCgkJCQkJCSsgKHVwID09ICcjJyA/IDEgOiAwKSArIChyaWdodCA9PSAnIycgPyAxIDogMCk7CgkJCQlpZiAobnVtT2ZXYWxscyA9PSAzKSB7CgkJCQkJLyoKCQkJCQkgKiDooYzjgY3mraLjgb7jgorjg57jgrnjga7loLTlkIjjgIHllK/kuIDpgLLlhaXlj6/og73jgarpmqPmjqXjg57jgrnjgavnp7vli5XjgZfjgb7jgZnjgIIKCQkJCQkgKi8KCQkJCQlpZiAoZG93biAhPSAnIycpIHsKCQkJCQkJdGhpcy55Kys7CgkJCQkJfSBlbHNlIGlmIChsZWZ0ICE9ICcjJykgewoJCQkJCQl0aGlzLngtLTsKCQkJCQl9IGVsc2UgaWYgKHVwICE9ICcjJykgewoJCQkJCQl0aGlzLnktLTsKCQkJCQl9IGVsc2UgaWYgKHJpZ2h0ICE9ICcjJykgewoJCQkJCQl0aGlzLngrKzsKCQkJCQl9IGVsc2UgewoJCQkJCQlTeXN0ZW0uZXJyLnByaW50Zigi5YuV44GR44G+44Gb44KT44CCdCA9ICVkLCB4ID0gJWQsIHkgPSAlZCwga2luZCA9ICVzXG4iLCB0aW1lLmdldCgpLAoJCQkJCQkJCXRoaXMueCwgdGhpcy55LCB0aGlzLmtpbmQpOwoJCQkJCX0KCQkJCX0gZWxzZSBpZiAobnVtT2ZXYWxscyA9PSAyKSB7CgkJCQkJLyoKCQkJCQkgKiDpgJrot6/jg57jgrnjga7loLTlkIjjgIHmmYLliLsgdC0xIOOBq+WxheOBn+ODnuOCueS7peWkluOBrumAsuWFpeWPr+iDveOBqumao+aOpeOBmeOCi+ODnuOCueOBq+enu+WLleOBl+OBvuOBmeOAggoJCQkJCSAqLwoJCQkJCWlmIChkb3duICE9ICcjJyAmJiB0aGlzLmxhc3RZICE9IHRoaXMueSArIDEpIHsKCQkJCQkJdGhpcy55Kys7CgkJCQkJfSBlbHNlIGlmIChsZWZ0ICE9ICcjJyAmJiB0aGlzLmxhc3RYICE9IHRoaXMueCAtIDEpIHsKCQkJCQkJdGhpcy54LS07CgkJCQkJfSBlbHNlIGlmICh1cCAhPSAnIycgJiYgdGhpcy5sYXN0WSAhPSB0aGlzLnkgLSAxKSB7CgkJCQkJCXRoaXMueS0tOwoJCQkJCX0gZWxzZSBpZiAocmlnaHQgIT0gJyMnICYmIHRoaXMubGFzdFggIT0gdGhpcy54ICsgMSkgewoJCQkJCQl0aGlzLngrKzsKCQkJCQl9IGVsc2UgewoJCQkJCQlTeXN0ZW0uZXJyLnByaW50Zigi5YuV44GR44G+44Gb44KT44CCdCA9ICVkLCB4ID0gJWQsIHkgPSAlZCwga2luZCA9ICVzXG4iLCB0aW1lLmdldCgpLAoJCQkJCQkJCXRoaXMueCwgdGhpcy55LCB0aGlzLmtpbmQpOwoJCQkJCX0KCQkJCX0gZWxzZSBpZiAobnVtT2ZXYWxscyA9PSAxIHx8IG51bU9mV2FsbHMgPT0gMCkgewoJCQkJCS8qCgkJCQkJICog5Lqk5beu54K544Oe44K544Gu5aC05ZCI44Gv44CB5pW144Gu56iu5Yil44Gr5b+c44GY44Gf44Ki44Or44K044Oq44K644Og44Gr5b+c44GY44Gm56e75YuV5pa55ZCR44KS5rG65a6a44GX44G+44GZ44CCCgkJCQkJICovCgkJCQkJZmluYWwgaW50IGRYID0gcGxheWVyLmxhc3RYIC0gdGhpcy54OwoJCQkJCWZpbmFsIGludCBkWSA9IHBsYXllci5sYXN0WSAtIHRoaXMueTsKCQkJCQlmaW5hbCBpbnQgc2lnbkRYID0gKGludCkgTWF0aC5zaWdudW0oZFgpOwoJCQkJCWZpbmFsIGludCBzaWduRFkgPSAoaW50KSBNYXRoLnNpZ251bShkWSk7CgkJCQkJc3dpdGNoICh0aGlzLmtpbmQpIHsKCQkJCQljYXNlIFY6CgkJCQkJCS8qCgkJCQkJCSAqIOaVteOBi+OCieimi+OBn+iHquapn+OBruebuOWvvuS9jee9ruOCkiAoZHgsIGR5KSDjgajooajjgZnjgoLjga7jgajjgZfjgb7jgZnjgILmrKHjga7jg6vjg7zjg6vjgpLkuIrjgYvjgonpoIbjgavpgannlKjjgZfjgIHmnIDliJ3jgavpgbjjgbDjgozjgZ/mlrnlkJHjgavnp7vli5XjgZfjgb7jgZnjgIIKCQkJCQkJICogMS4gZHkg4omgIDAg44Gn44GL44GkIGR5IOOBruespuWPt+aWueWQkeOBq+OBguOCi+ODnuOCueOBjOmAsuWFpeWPr+iDveOBp+OBguOCjOOBsOOAgeOBneOBruaWueWQkeOBq+enu+WLleOBl+OBvuOBmeOAggoJCQkJCQkgKiAyLiBkeCDiiaAgMCDjgafjgYvjgaQgZHgg44Gu56ym5Y+35pa55ZCR44Gr44GC44KL44Oe44K544GM6YCy5YWl5Y+v6IO944Gn44GC44KM44Gw44CB44Gd44Gu5pa55ZCR44Gr56e75YuV44GX44G+44GZ44CCCgkJCQkJCSAqIDMuIOePvuWcqOS9jee9ruOBriDkuIvjgIHlt6bjgIHkuIrjgIHlj7Mg44Gu6aCG44Gn5pyA5Yid44Gr6YCy5YWl5Y+v6IO944Gq44Oe44K544Gu5pa55ZCR44Gr56e75YuV44GZ44KL44CCCgkJCQkJCSAqLwoJCQkJCQlpZiAoZFkgIT0gMCAmJiBkYXRhW3RoaXMueSArIHNpZ25EWV1bdGhpcy54XSAhPSAnIycpIHsKCQkJCQkJCXRoaXMueSArPSBzaWduRFk7CgkJCQkJCX0gZWxzZSBpZiAoZFggIT0gMCAmJiBkYXRhW3RoaXMueV1bdGhpcy54ICsgc2lnbkRYXSAhPSAnIycpIHsKCQkJCQkJCXRoaXMueCArPSBzaWduRFg7CgkJCQkJCX0gZWxzZSB7CgkJCQkJCQlpZiAoZG93biAhPSAnIycpIHsKCQkJCQkJCQl0aGlzLnkrKzsKCQkJCQkJCX0gZWxzZSBpZiAobGVmdCAhPSAnIycpIHsKCQkJCQkJCQl0aGlzLngtLTsKCQkJCQkJCX0gZWxzZSBpZiAocmlnaHQgIT0gJyMnKSB7CgkJCQkJCQkJdGhpcy54Kys7CgkJCQkJCQl9IGVsc2UgaWYgKHVwICE9ICcjJykgewoJCQkJCQkJCXRoaXMueS0tOwoJCQkJCQkJfSBlbHNlIHsKCQkJCQkJCQlTeXN0ZW0uZXJyLnByaW50Zigi5YuV44GR44G+44Gb44KT44CCdCA9ICVkLCB4ID0gJWQsIHkgPSAlZCwga2luZCA9ICVzXG4iLAoJCQkJCQkJCQkJdGltZS5nZXQoKSwgdGhpcy54LCB0aGlzLnksIHRoaXMua2luZCk7CgkJCQkJCQl9CgkJCQkJCX0KCQkJCQkJYnJlYWs7CgkJCQkJY2FzZSBIOgoJCQkJCQkvKgoJCQkJCQkgKiDmlbUgViDjgajjgbvjgbzlkIzjgZjjgafjgZnjgILllK/kuIDnlbDjgarjgovjga7jga8g44CB6YCy6KGM5pa55ZCR44KS5rG644KB44KL44Or44O844Or44Gu44GG44Gh44CBCgkJCQkJCSAqIOacgOWIneOBruS6jOOBpOOBruODq+ODvOODq+OBrumBqeeUqOmghuW6j+OBjOWFpeOCjOabv+OCj+OCi+OBqOOBk+OCjeOBp+OBmeOAggoJCQkJCQkgKiDjgZnjgarjgo/jgaHjgIHlhYjjgasgZHgg4omgIDAg44Gu44OB44Kn44OD44Kv44KS6KGM44Gq44GE44CB5qyh44GrIGR5IOKJoCAwIOOBruODgeOCp+ODg+OCr+OCkuihjOOBhOOBvuOBmeOAggoJCQkJCQkgKi8KCQkJCQkJaWYgKGRYICE9IDAgJiYgZGF0YVt0aGlzLnldW3RoaXMueCArIHNpZ25EWF0gIT0gJyMnKSB7CgkJCQkJCQl0aGlzLnggKz0gc2lnbkRYOwoJCQkJCQl9IGVsc2UgaWYgKGRZICE9IDAgJiYgZGF0YVt0aGlzLnkgKyBzaWduRFldW3RoaXMueF0gIT0gJyMnKSB7CgkJCQkJCQl0aGlzLnkgKz0gc2lnbkRZOwoJCQkJCQl9IGVsc2UgewoJCQkJCQkJaWYgKGRvd24gIT0gJyMnKSB7CgkJCQkJCQkJdGhpcy55Kys7CgkJCQkJCQl9IGVsc2UgaWYgKGxlZnQgIT0gJyMnKSB7CgkJCQkJCQkJdGhpcy54LS07CgkJCQkJCQl9IGVsc2UgaWYgKHJpZ2h0ICE9ICcjJykgewoJCQkJCQkJCXRoaXMueCsrOwoJCQkJCQkJfSBlbHNlIGlmICh1cCAhPSAnIycpIHsKCQkJCQkJCQl0aGlzLnktLTsKCQkJCQkJCX0gZWxzZSB7CgkJCQkJCQkJU3lzdGVtLmVyci5wcmludGYoIuWLleOBkeOBvuOBm+OCk+OAgnQgPSAlZCwgeCA9ICVkLCB5ID0gJWQsIGtpbmQgPSAlc1xuIiwKCQkJCQkJCQkJCXRpbWUuZ2V0KCksIHRoaXMueCwgdGhpcy55LCB0aGlzLmtpbmQpOwoJCQkJCQkJfQoJCQkJCQl9CgkJCQkJCWJyZWFrOwoJCQkJCWNhc2UgTDoKCQkJCQljYXNlIEpMOgoJCQkJCQkvKgoJCQkJCQkgKiDnj77lnKjkvY3nva7jgbjjga7pgLLlhaXmlrnlkJHjgYvjgonopovjgabnm7jlr77nmoTjgasg5bem44CB5YmN44CB5Y+zIOOBrumghuOBp+acgOWIneOBq+mAsuWFpeWPr+iDveOBquODnuOCueOBruaWueWQkeOBq+enu+WLleOBl+OBvuOBmeOAggoJCQkJCQkgKi8KCQkJCQkJaWYgKHRoaXMubGFzdFkgKyAxID09IHRoaXMueSkgewoJCQkJCQkJaWYgKHJpZ2h0ICE9ICcjJykgewoJCQkJCQkJCXRoaXMueCsrOwoJCQkJCQkJfSBlbHNlIGlmIChkb3duICE9ICcjJykgewoJCQkJCQkJCXRoaXMueSsrOwoJCQkJCQkJfSBlbHNlIGlmIChsZWZ0ICE9ICcjJykgewoJCQkJCQkJCXRoaXMueC0tOwoJCQkJCQkJfSBlbHNlIHsKCQkJCQkJCQlTeXN0ZW0uZXJyLnByaW50Zigi5YuV44GR44G+44Gb44KT44CCdCA9ICVkLCB4ID0gJWQsIHkgPSAlZCwga2luZCA9ICVzXG4iLAoJCQkJCQkJCQkJdGltZS5nZXQoKSwgdGhpcy54LCB0aGlzLnksIHRoaXMua2luZCk7CgkJCQkJCQl9CgkJCQkJCX0gZWxzZSBpZiAodGhpcy5sYXN0WCAtIDEgPT0gdGhpcy54KSB7CgkJCQkJCQlpZiAoZG93biAhPSAnIycpIHsKCQkJCQkJCQl0aGlzLnkrKzsKCQkJCQkJCX0gZWxzZSBpZiAobGVmdCAhPSAnIycpIHsKCQkJCQkJCQl0aGlzLngtLTsKCQkJCQkJCX0gZWxzZSBpZiAodXAgIT0gJyMnKSB7CgkJCQkJCQkJdGhpcy55LS07CgkJCQkJCQl9IGVsc2UgewoJCQkJCQkJCVN5c3RlbS5lcnIucHJpbnRmKCLli5XjgZHjgb7jgZvjgpPjgIJ0ID0gJWQsIHggPSAlZCwgeSA9ICVkLCBraW5kID0gJXNcbiIsCgkJCQkJCQkJCQl0aW1lLmdldCgpLCB0aGlzLngsIHRoaXMueSwgdGhpcy5raW5kKTsKCQkJCQkJCX0KCQkJCQkJfSBlbHNlIGlmICh0aGlzLmxhc3RZIC0gMSA9PSB0aGlzLnkpIHsKCQkJCQkJCWlmIChsZWZ0ICE9ICcjJykgewoJCQkJCQkJCXRoaXMueC0tOwoJCQkJCQkJfSBlbHNlIGlmICh1cCAhPSAnIycpIHsKCQkJCQkJCQl0aGlzLnktLTsKCQkJCQkJCX0gZWxzZSBpZiAocmlnaHQgIT0gJyMnKSB7CgkJCQkJCQkJdGhpcy54Kys7CgkJCQkJCQl9IGVsc2UgewoJCQkJCQkJCVN5c3RlbS5lcnIucHJpbnRmKCLli5XjgZHjgb7jgZvjgpPjgIJ0ID0gJWQsIHggPSAlZCwgeSA9ICVkLCBraW5kID0gJXNcbiIsCgkJCQkJCQkJCQl0aW1lLmdldCgpLCB0aGlzLngsIHRoaXMueSwgdGhpcy5raW5kKTsKCQkJCQkJCX0KCQkJCQkJfSBlbHNlIGlmICh0aGlzLmxhc3RYICsgMSA9PSB0aGlzLngpIHsKCQkJCQkJCWlmICh1cCAhPSAnIycpIHsKCQkJCQkJCQl0aGlzLnktLTsKCQkJCQkJCX0gZWxzZSBpZiAocmlnaHQgIT0gJyMnKSB7CgkJCQkJCQkJdGhpcy54Kys7CgkJCQkJCQl9IGVsc2UgaWYgKGRvd24gIT0gJyMnKSB7CgkJCQkJCQkJdGhpcy55Kys7CgkJCQkJCQl9IGVsc2UgewoJCQkJCQkJCVN5c3RlbS5lcnIucHJpbnRmKCLli5XjgZHjgb7jgZvjgpPjgIJ0ID0gJWQsIHggPSAlZCwgeSA9ICVkLCBraW5kID0gJXNcbiIsCgkJCQkJCQkJCQl0aW1lLmdldCgpLCB0aGlzLngsIHRoaXMueSwgdGhpcy5raW5kKTsKCQkJCQkJCX0KCQkJCQkJfSBlbHNlIHsKCQkJCQkJCVN5c3RlbS5lcnIucHJpbnRmKCLmraLjgb7jgaPjgabjgYTjgZ/vvJ90ID0gJWQsIHggPSAlZCwgeSA9ICVkLCBraW5kID0gJXNcbiIsCgkJCQkJCQkJCXRpbWUuZ2V0KCksIHRoaXMueCwgdGhpcy55LCB0aGlzLmtpbmQpOwoJCQkJCQl9CgkJCQkJCWJyZWFrOwoJCQkJCWNhc2UgUjoKCQkJCQljYXNlIEpSOgoJCQkJCQkvKgoJCQkJCQkgKiDnj77lnKjkvY3nva7jgbjjga7pgLLlhaXmlrnlkJHjgYvjgonopovjgabnm7jlr77nmoTjgasg5Y+z44CB5YmN44CB5bemIOOBrumghuOBp+acgOWIneOBq+mAsuWFpeWPr+iDveOBquODnuOCueOBruaWueWQkeOBq+enu+WLleOBl+OBvuOBmeOAggoJCQkJCQkgKi8KCQkJCQkJaWYgKHRoaXMubGFzdFkgKyAxID09IHRoaXMueSkgewoJCQkJCQkJaWYgKGxlZnQgIT0gJyMnKSB7CgkJCQkJCQkJdGhpcy54LS07CgkJCQkJCQl9IGVsc2UgaWYgKGRvd24gIT0gJyMnKSB7CgkJCQkJCQkJdGhpcy55Kys7CgkJCQkJCQl9IGVsc2UgaWYgKHJpZ2h0ICE9ICcjJykgewoJCQkJCQkJCXRoaXMueCsrOwoJCQkJCQkJfSBlbHNlIHsKCQkJCQkJCQlTeXN0ZW0uZXJyLnByaW50Zigi5YuV44GR44G+44Gb44KT44CCdCA9ICVkLCB4ID0gJWQsIHkgPSAlZCwga2luZCA9ICVzXG4iLAoJCQkJCQkJCQkJdGltZS5nZXQoKSwgdGhpcy54LCB0aGlzLnksIHRoaXMua2luZCk7CgkJCQkJCQl9CgkJCQkJCX0gZWxzZSBpZiAodGhpcy5sYXN0WCAtIDEgPT0gdGhpcy54KSB7CgkJCQkJCQlpZiAodXAgIT0gJyMnKSB7CgkJCQkJCQkJdGhpcy55LS07CgkJCQkJCQl9IGVsc2UgaWYgKGxlZnQgIT0gJyMnKSB7CgkJCQkJCQkJdGhpcy54LS07CgkJCQkJCQl9IGVsc2UgaWYgKGRvd24gIT0gJyMnKSB7CgkJCQkJCQkJdGhpcy55Kys7CgkJCQkJCQl9IGVsc2UgewoJCQkJCQkJCVN5c3RlbS5lcnIucHJpbnRmKCLli5XjgZHjgb7jgZvjgpPjgIJ0ID0gJWQsIHggPSAlZCwgeSA9ICVkLCBraW5kID0gJXNcbiIsCgkJCQkJCQkJCQl0aW1lLmdldCgpLCB0aGlzLngsIHRoaXMueSwgdGhpcy5raW5kKTsKCQkJCQkJCX0KCQkJCQkJfSBlbHNlIGlmICh0aGlzLmxhc3RZIC0gMSA9PSB0aGlzLnkpIHsKCQkJCQkJCWlmIChyaWdodCAhPSAnIycpIHsKCQkJCQkJCQl0aGlzLngrKzsKCQkJCQkJCX0gZWxzZSBpZiAodXAgIT0gJyMnKSB7CgkJCQkJCQkJdGhpcy55LS07CgkJCQkJCQl9IGVsc2UgaWYgKGxlZnQgIT0gJyMnKSB7CgkJCQkJCQkJdGhpcy54LS07CgkJCQkJCQl9IGVsc2UgewoJCQkJCQkJCVN5c3RlbS5lcnIucHJpbnRmKCLli5XjgZHjgb7jgZvjgpPjgIJ0ID0gJWQsIHggPSAlZCwgeSA9ICVkLCBraW5kID0gJXNcbiIsCgkJCQkJCQkJCQl0aW1lLmdldCgpLCB0aGlzLngsIHRoaXMueSwgdGhpcy5raW5kKTsKCQkJCQkJCX0KCQkJCQkJfSBlbHNlIGlmICh0aGlzLmxhc3RYICsgMSA9PSB0aGlzLngpIHsKCQkJCQkJCWlmIChkb3duICE9ICcjJykgewoJCQkJCQkJCXRoaXMueSsrOwoJCQkJCQkJfSBlbHNlIGlmIChyaWdodCAhPSAnIycpIHsKCQkJCQkJCQl0aGlzLngrKzsKCQkJCQkJCX0gZWxzZSBpZiAodXAgIT0gJyMnKSB7CgkJCQkJCQkJdGhpcy55LS07CgkJCQkJCQl9IGVsc2UgewoJCQkJCQkJCVN5c3RlbS5lcnIucHJpbnRmKCLli5XjgZHjgb7jgZvjgpPjgIJ0ID0gJWQsIHggPSAlZCwgeSA9ICVkLCBraW5kID0gJXNcbiIsCgkJCQkJCQkJCQl0aW1lLmdldCgpLCB0aGlzLngsIHRoaXMueSwgdGhpcy5raW5kKTsKCQkJCQkJCX0KCQkJCQkJfSBlbHNlIHsKCQkJCQkJCVN5c3RlbS5lcnIucHJpbnRmKCLmraLjgb7jgaPjgabjgYTjgZ/vvJ90ID0gJWQsIHggPSAlZCwgeSA9ICVkLCBraW5kID0gJXNcbiIsCgkJCQkJCQkJCXRpbWUuZ2V0KCksIHRoaXMueCwgdGhpcy55LCB0aGlzLmtpbmQpOwoJCQkJCQl9CgkJCQkJCWJyZWFrOwoJCQkJCX0KCQkJCQkvKgoJCQkJCSAqIOS6pOW3rueCueODnuOCueOBq+WFpeOCi+OBn+OBs+OBq+OAgeacgOWIneOBr+aVtUzjga7ooYzli5XjgIHmrKHlm57jga/mlbVS44Gu6KGM5YuV44CB44GV44KJ44Gr5qyh5Zue44Gv44G+44Gf5pW1TOOBruihjOWLleOAgeOBqOe5sOOCiui/lOOBl+OBvuOBmeOAggoJCQkJCSAqLwoJCQkJCXRoaXMua2luZCA9IHRoaXMua2luZCA9PSBLaW5kLkpMID8gS2luZC5KUiA6ICh0aGlzLmtpbmQgPT0gS2luZC5KUiA/IEtpbmQuSkwKCQkJCQkJCTogdGhpcy5raW5kKTsKCQkJCX0gZWxzZSB7CgkJCQkJU3lzdGVtLmVyci5wcmludGYoIuWLleOBkeOBvuOBm+OCk+OAgnQgPSAlZCwgeCA9ICVkLCB5ID0gJWQsIGtpbmQgPSAlc1xuIiwgdGltZS5nZXQoKSwKCQkJCQkJCXRoaXMueCwgdGhpcy55LCB0aGlzLmtpbmQpOwoJCQkJfQoJCQl9CgkJfQoKCQkvKioKCQkgKiBAcGFyYW0gcGxheWVyIOODl+ODrOOCpOODpAoJCSAqIEByZXR1cm4g5q6644GX44Gf44GL44Gp44GG44GLCgkJICovCgkJYm9vbGVhbiBraWxsZWQoZmluYWwgUGxheWVyIHBsYXllcikgewoJCQlpZiAodGhpcy54ID09IHBsYXllci54ICYmIHRoaXMueSA9PSBwbGF5ZXIueSkgewoJCQkJcmV0dXJuIHRydWU7CgkJCX0gZWxzZSBpZiAodGhpcy54ID09IHBsYXllci5sYXN0WCAmJiB0aGlzLnkgPT0gcGxheWVyLmxhc3RZICYmIHRoaXMubGFzdFggPT0gcGxheWVyLngKCQkJCQkmJiB0aGlzLmxhc3RZID09IHBsYXllci55KSB7CgkJCQlyZXR1cm4gdHJ1ZTsKCQkJfSBlbHNlIHsKCQkJCXJldHVybiBmYWxzZTsKCQkJfQoJCX0KCgkJQE92ZXJyaWRlCgkJcHVibGljIFN0cmluZyB0b1N0cmluZygpIHsKCQkJcmV0dXJuIG5ldyBGb3JtYXR0ZXIoKS5mb3JtYXQoIlt4ID0gJWQsIHkgPSAlZCwga2luZCA9ICVzXSIsIHRoaXMueCwgdGhpcy55LCB0aGlzLmtpbmQpCgkJCQkJLnRvU3RyaW5nKCk7CgkJfQoJfQoKCS8qKgoJICog44OX44Os44Kk44OkCgkgKi8KCXN0YXRpYyBjbGFzcyBQbGF5ZXIgewoJCS8qKgoJCSAqIFjluqfmqJkKCQkgKi8KCQlpbnQgeCA9IC0xOwoJCS8qKgoJCSAqIFnluqfmqJkKCQkgKi8KCQlpbnQgeSA9IC0xOwoJCS8qKgoJCSAqIOebtOWJjeOBrljluqfmqJkKCQkgKi8KCQlpbnQgbGFzdFggPSAtMTsKCQkvKioKCQkgKiDnm7TliY3jga5Z5bqn5qiZCgkJICovCgkJaW50IGxhc3RZID0gLTE7CgoJCS8qKgoJCSAqIOS4iuOBuOenu+WLlQoJCSAqIEBwYXJhbSBkYXRhIOODh+ODvOOCvwoJCSAqIEBwYXJhbSBsb2cg44Ot44KwCgkJICovCgkJdm9pZCBtb3ZlRG93bihmaW5hbCBjaGFyW11bXSBkYXRhLCBmaW5hbCBTdHJpbmdCdWlsZGVyIGxvZykgewoJCQl0aGlzLmxhc3RYID0gdGhpcy54OwoJCQl0aGlzLmxhc3RZID0gdGhpcy55OwoJCQlpZiAoZGF0YVt0aGlzLnkgKyAxXVt0aGlzLnhdICE9ICcjJykgewoJCQkJdGhpcy55Kys7CgkJCX0KCQkJbG9nLmFwcGVuZCgnaicpOwoJCX0KCgkJLyoqCgkJICog5bem44G456e75YuVCgkJICogQHBhcmFtIGRhdGEg44OH44O844K/CgkJICogQHBhcmFtIGxvZyDjg63jgrAKCQkgKi8KCQl2b2lkIG1vdmVMZWZ0KGZpbmFsIGNoYXJbXVtdIGRhdGEsIGZpbmFsIFN0cmluZ0J1aWxkZXIgbG9nKSB7CgkJCXRoaXMubGFzdFggPSB0aGlzLng7CgkJCXRoaXMubGFzdFkgPSB0aGlzLnk7CgkJCWlmIChkYXRhW3RoaXMueV1bdGhpcy54IC0gMV0gIT0gJyMnKSB7CgkJCQl0aGlzLngtLTsKCQkJfQoJCQlsb2cuYXBwZW5kKCdoJyk7CgkJfQoKCQkvKioKCQkgKiDkuIrjgbjnp7vli5UKCQkgKiBAcGFyYW0gZGF0YSDjg4fjg7zjgr8KCQkgKiBAcGFyYW0gbG9nIOODreOCsAoJCSAqLwoJCXZvaWQgbW92ZVVwKGZpbmFsIGNoYXJbXVtdIGRhdGEsIGZpbmFsIFN0cmluZ0J1aWxkZXIgbG9nKSB7CgkJCXRoaXMubGFzdFggPSB0aGlzLng7CgkJCXRoaXMubGFzdFkgPSB0aGlzLnk7CgkJCWlmIChkYXRhW3RoaXMueSAtIDFdW3RoaXMueF0gIT0gJyMnKSB7CgkJCQl0aGlzLnktLTsKCQkJfQoJCQlsb2cuYXBwZW5kKCdrJyk7CgkJfQoKCQkvKioKCQkgKiDlj7Pjgbjnp7vli5UKCQkgKiBAcGFyYW0gZGF0YSDjg4fjg7zjgr8KCQkgKiBAcGFyYW0gbG9nIOODreOCsAoJCSAqLwoJCXZvaWQgbW92ZVJpZ2h0KGZpbmFsIGNoYXJbXVtdIGRhdGEsIGZpbmFsIFN0cmluZ0J1aWxkZXIgbG9nKSB7CgkJCXRoaXMubGFzdFggPSB0aGlzLng7CgkJCXRoaXMubGFzdFkgPSB0aGlzLnk7CgkJCWlmIChkYXRhW3RoaXMueV1bdGhpcy54ICsgMV0gIT0gJyMnKSB7CgkJCQl0aGlzLngrKzsKCQkJfQoJCQlsb2cuYXBwZW5kKCdsJyk7CgkJfQoKCQkvKioKCQkgKiDnlZnjgb7jgosKCQkgKiBAcGFyYW0gbG9nIOODreOCsAoJCSAqLwoJCXZvaWQgc3RheShmaW5hbCBTdHJpbmdCdWlsZGVyIGxvZykgewoJCQl0aGlzLmxhc3RYID0gdGhpcy54OwoJCQl0aGlzLmxhc3RZID0gdGhpcy55OwoJCQlsb2cuYXBwZW5kKCcuJyk7CgkJfQoKCQlAT3ZlcnJpZGUKCQlwdWJsaWMgU3RyaW5nIHRvU3RyaW5nKCkgewoJCQlyZXR1cm4gbmV3IEZvcm1hdHRlcigpLmZvcm1hdCgiW3ggPSAlZCwgeSA9ICVkLCBsWCA9ICVkLCBsWSA9ICVkXSIsIHRoaXMueCwgdGhpcy55LAoJCQkJCXRoaXMubGFzdFgsIHRoaXMubGFzdFkpLnRvU3RyaW5nKCk7CgkJfQoJfQoKCS8qKgoJICog44K544OG44O844K4CgkgKi8KCWVudW0gU3RhZ2UgewoJCS8qKgoJCSAqIOWFpeWKmzEKCQkgKi8KCQlMVjEoNTAsIDExLCA3LCAiIyMjIyMjIyMjIyMiICsgIiMuVi4uIy4uSC4jIiArICIjLiMjLi4uIyMuIyIgKyAiI0wjLi4jLi5SLiMiCgkJCQkrICIjLiMuIyMjLiMuIyIgKyAiIy4uLi5ALi4uLiMiICsgIiMjIyMjIyMjIyMjIiksCgkJLyoqCgkJICog5YWl5YqbMgoJCSAqLwoJCUxWMigzMDAsIDIwLCAxNywgIiMjIyMjIyMjIyMjIyMjIyMjIyMjIiArICIjIyMuLi4uLkwuLi4uLi4uLi4uIyIgKyAiIyMjLiMjLiMjLiMjTCMjLiMjLiMiCgkJCQkrICIjIyMuIyMuIyMuIyMuIyMuIyMuIyIgKyAiIy5MLi4uLi4uLi4uLi4uLi4uLiMiICsgIiMuIyMuIyMuIyMuIyMuIyMuIyMjIgoJCQkJKyAiIy4jIy4jI0wjIy4jIy4jIy4jIyMiICsgIiMuLi4uLi4uLi4uLi4uLi4uLkwjIiArICIjLiMuIy4jSiMjIyNKIy4jLiMuIyIKCQkJCSsgIiNMLi4uLi4uLi4uLi4uLi4uLi4jIiArICIjIyMuIyMuIyMuIyMuIyMuIyMuIyIgKyAiIyMjLiMjLiMjUiMjLiMjLiMjLiMiCgkJCQkrICIjLi4uLi4uLi4uLi4uLi4uLlIuIyIgKyAiIy4jIy4jIy4jIy4jI1IjIy4jIyMiICsgIiMuIyMuIyMuIyMuIyMuIyMuIyMjIgoJCQkJKyAiI0AuLi4uUi4uLi4uLi4uLi4jIyMiICsgIiMjIyMjIyMjIyMjIyMjIyMjIyMjIiksCgkJLyoqCgkJICog5YWl5YqbMwoJCSAqLwoJCUxWMyg3MDAsIDU4LCAxNywgIiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMiCgkJCQkrICIjLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4jIgoJCQkJKyAiIy4jIyMuIyMjIyMjIyMjLiMjIyMjIyMjIyMjIyMjIy4jIyMjIyMjIy4jIyMuIyMjIyMuIyMjIyMuIyIKCQkJCSsgIiMuIyMjLiMjIyMjIyMjIy4jIyMjIyMjIyMjIyMjIyMuIyMjIyMjIyMuIyMjLiMjIyMjLiMjIyMjLiMiCgkJCQkrICIjLi4uLi4jIyMjIyMjIyMuLi4uSi4uLi4uLi4uLi4uLi5KLi4uLi4uLiMjIy4uLi4uLi4uLi4uLi4jIgoJCQkJKyAiIyMjIyMuIyMjLi4uLi4uLiMjIyMjIyMuIyMjIyMjIy4jIyMjIyMjIy4jIyMuIyMjIyMjIy4jIyMjIyIKCQkJCSsgIiMjIyMjLiMjIy4jIyMjI0ojIyMjIyMjLiMjIyMjIyMuIyMjIyMjIyMuIyMjLiMjICAgIyMuIyMjIyMiCgkJCQkrICIjIyMjIy4jIyNMIyMjIyMuIyMgICAjI0wjIyAgICMjLiMjICAgICMjLiMjIy4jIyAgICMjLiMjIyMjIgoJCQkJKyAiIyMjIyMuIyMjLi5IIyMjLiMjICAgIyMuIyMgICAjIy4jIyMjIyMjIy4jIyMuIyMjIyMjI0ojIyMjIyIKCQkJCSsgIiMjIyMjLiMjIyMjIyMjIy4jIyAgICMjTCMjICAgIyMuIyMjIyMjIyMuIyMjLiMjI1YuLi4uIyMjIyMiCgkJCQkrICIjIyMjIy4jIyMjIyMjIyMuIyMjIyMjIy4jIyMjIyMjLi4uLi4uLi4uLiMjIy4jIyMjIyMjLiMjIyMjIgoJCQkJKyAiIyMjIyMuIyMjIyMjIyMjLiMjIyMjIyMuIyMjIyMjIy4jIyMjIyMjIy4jIyMuIyMjIyMjIy4jIyMjIyIKCQkJCSsgIiMuLi4uLi4uLi4uLi4uLi4uLi4uLi5MLi4uLi4uLi4uIyMjIyMjIyMuLi4uLi4uLi4uUi4uLi4uLiMiCgkJCQkrICIjTCMjIyMuIyMjIyMjIyMjIy4jIy4jIyMjIyMjIyMjLi4uLiMjLi4uLiMjIyMjIyMjIy4jIyMjIy4jIgoJCQkJKyAiIy4jIyMjLiMjIyMjIyMjIyMuIyMuIyMjIyMjIyMjIy4jIy4jIy4jIy4jIyMjIyMjIyMuIyMjIyMuIyIKCQkJCSsgIiMuLi4uLi4uLi4uLi4uLi4uLiMjLi4uLi4uLi4uLi4uIyMuLkAuIyMuLi4uLi4uLi4uLi4uLi5SLiMiCgkJCQkrICIjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIik7CgkJLyoqCgkJICog5Yi26ZmQ5pmC6ZaTCgkJICovCgkJaW50IHRpbWVMaW1pdDsKCQkvKioKCQkgKiDluYUKCQkgKi8KCQlpbnQgd2lkdGg7CgkJLyoqCgkJICog6auY44GVCgkJICovCgkJaW50IGhlaWdodDsKCQkvKioKCQkgKiDjg4fjg7zjgr8KCQkgKi8KCQljaGFyW11bXSBkYXRhOwoJCS8qKgoJCSAqIOeCuQoJCSAqLwoJCWludCBkb3RzOwoKCQkvKioKCQkgKiDjgrnjg4bjg7zjgrjjgpLliJ3mnJ/ljJYKCQkgKiBAcGFyYW0gdGltZUxpbWl0IOWItumZkOaZgumWkwoJCSAqIEBwYXJhbSB3aWR0aCDluYUKCQkgKiBAcGFyYW0gaGVpZ2h0IOmrmOOBlQoJCSAqIEBwYXJhbSBzdHJpbmcg5YWl5Yqb5paH5a2X5YiXCgkJICovCgkJU3RhZ2UoZmluYWwgaW50IHRpbWVMaW1pdCwgZmluYWwgaW50IHdpZHRoLCBmaW5hbCBpbnQgaGVpZ2h0LCBmaW5hbCBTdHJpbmcgc3RyaW5nKSB7CgkJCXRoaXMudGltZUxpbWl0ID0gdGltZUxpbWl0OwoJCQl0aGlzLndpZHRoID0gd2lkdGg7CgkJCXRoaXMuaGVpZ2h0ID0gaGVpZ2h0OwoJCQl0aGlzLmRhdGEgPSBuZXcgY2hhcltoZWlnaHRdW3dpZHRoXTsKCQkJdGhpcy5kb3RzID0gMDsKCQkJZm9yIChpbnQgaSA9IDA7IGkgPCBoZWlnaHQ7IGkrKykgewoJCQkJZm9yIChpbnQgaiA9IDA7IGogPCB3aWR0aDsgaisrKSB7CgkJCQkJZmluYWwgY2hhciBjaGFyYWN0ZXIgPSBzdHJpbmcuY2hhckF0KGkgKiB3aWR0aCArIGopOwoJCQkJCXRoaXMuZGF0YVtpXVtqXSA9IGNoYXJhY3RlcjsKCQkJCQlpZiAoY2hhcmFjdGVyID09ICcuJykgewoJCQkJCQl0aGlzLmRvdHMrKzsKCQkJCQl9CgkJCQl9CgkJCX0KCQl9OwoJfQoKCS8qKgoJICog44Oh44Kk44Oz44Oh44K944OD44OJCgkgKiBAcGFyYW0gYXJncyDjgrPjg57jg7Pjg4njg6njgqTjg7PlvJXmlbAKCSAqIEB0aHJvd3MgSW50ZXJydXB0ZWRFeGNlcHRpb24g5Ymy44KK6L6844G/5L6L5aSWCgkgKi8KCXB1YmxpYyBzdGF0aWMgdm9pZCBtYWluKGZpbmFsIFN0cmluZ1tdIGFyZ3MpIHRocm93cyBJbnRlcnJ1cHRlZEV4Y2VwdGlvbiB7CgkJZmluYWwgU3RhZ2Ugc3RhZ2UgPSBTdGFnZS5MVjM7CgkJZmluYWwgaW50IHRpbWVMaW1pdCA9IHN0YWdlLnRpbWVMaW1pdDsKCQlmaW5hbCBpbnQgd2lkdGggPSBzdGFnZS53aWR0aDsKCQlmaW5hbCBpbnQgaGVpZ2h0ID0gc3RhZ2UuaGVpZ2h0OwoJCWZpbmFsIGNoYXJbXVtdIGRhdGEgPSBuZXcgY2hhcltoZWlnaHRdW3dpZHRoXTsKCQlmaW5hbCBQbGF5ZXIgcGxheWVyID0gbmV3IFBsYXllcigpOwoJCWZpbmFsIExpc3Q8RW5lbXk+IGVuZW1pZXMgPSBuZXcgQXJyYXlMaXN0PEVuZW15PigpOwoJCWZpbmFsIEF0b21pY0ludGVnZXIgdGltZSA9IG5ldyBBdG9taWNJbnRlZ2VyKCk7CgkJZmluYWwgQXRvbWljSW50ZWdlciBkb3RzID0gbmV3IEF0b21pY0ludGVnZXIoKTsKCQlmaW5hbCBTdHJpbmdCdWlsZGVyIGxvZyA9IG5ldyBTdHJpbmdCdWlsZGVyKCk7CgkJZmluYWwgQXRvbWljQm9vbGVhbiBpc0RlYWQgPSBuZXcgQXRvbWljQm9vbGVhbigpOwoJCWZpbmFsIEpGcmFtZSBmcmFtZSA9IG5ldyBKRnJhbWUoc3RhZ2UudG9TdHJpbmcoKSk7CgkJZmluYWwgaW50IHNpemUgPSAyMDsKCQlmaW5hbCBKTGFiZWwgc3RhdHVzTGFiZWwgPSBuZXcgSkxhYmVsKCLjgZPjgZPjgavnj77lnKjjga7nirbmhYvjgYzooajnpLrjgZXjgozjgb7jgZnjgIIiKTsKCQlzdGF0dXNMYWJlbC5zZXRPcGFxdWUodHJ1ZSk7CgkJc3RhdHVzTGFiZWwuc2V0QmFja2dyb3VuZChDb2xvci5XSElURSk7CgkJZnJhbWUuYWRkKHN0YXR1c0xhYmVsLCBCb3JkZXJMYXlvdXQuTk9SVEgpOwoJCWZpbmFsIEpQYW5lbCBwbGF5UGFuZWwgPSBuZXcgSlBhbmVsKCkgewoJCQlAT3ZlcnJpZGUKCQkJcHJvdGVjdGVkIHZvaWQgcGFpbnRDb21wb25lbnQoZmluYWwgR3JhcGhpY3MgZ3JhcGhpY3MpIHsKCQkJCXN1cGVyLnBhaW50Q29tcG9uZW50KGdyYXBoaWNzKTsKCQkJCWZpbmFsIEdyYXBoaWNzMkQgZyA9IChHcmFwaGljczJEKSBncmFwaGljczsKCQkJCWcuc2V0UmVuZGVyaW5nSGludChSZW5kZXJpbmdIaW50cy5LRVlfQU5USUFMSUFTSU5HLAoJCQkJCQlSZW5kZXJpbmdIaW50cy5WQUxVRV9BTlRJQUxJQVNfT04pOwoJCQkJZy5zZXRDb2xvcihDb2xvci5SRUQpOwoJCQkJZy5maWxsT3ZhbChwbGF5ZXIueCAqIHNpemUsIHBsYXllci55ICogc2l6ZSwgc2l6ZSwgc2l6ZSk7CgkJCQlmb3IgKGZpbmFsIEVuZW15IGVuZW15IDogZW5lbWllcykgewoJCQkJCXN3aXRjaCAoZW5lbXkua2luZCkgewoJCQkJCWNhc2UgVjoKCQkJCQkJZy5zZXRDb2xvcihDb2xvci5HUkVFTik7CgkJCQkJCWJyZWFrOwoJCQkJCWNhc2UgSDoKCQkJCQkJZy5zZXRDb2xvcihDb2xvci5CTFVFKTsKCQkJCQkJYnJlYWs7CgkJCQkJY2FzZSBMOgoJCQkJCQlnLnNldENvbG9yKENvbG9yLk9SQU5HRSk7CgkJCQkJCWJyZWFrOwoJCQkJCWNhc2UgUjoKCQkJCQkJZy5zZXRDb2xvcihDb2xvci5QSU5LKTsKCQkJCQkJYnJlYWs7CgkJCQkJY2FzZSBKTDoKCQkJCQljYXNlIEpSOgoJCQkJCQlnLnNldENvbG9yKENvbG9yLkNZQU4pOwoJCQkJCQlicmVhazsKCQkJCQlkZWZhdWx0OgoJCQkJCQlnLnNldENvbG9yKENvbG9yLkJMQUNLKTsKCQkJCQl9CgkJCQkJZy5maWxsT3ZhbChlbmVteS54ICogc2l6ZSwgZW5lbXkueSAqIHNpemUsIHNpemUsIHNpemUpOwoJCQkJfQoJCQkJZy5zZXRDb2xvcihDb2xvci5CTEFDSyk7CgkJCQlmb3IgKGZpbmFsIEVuZW15IGVuZW15IDogZW5lbWllcykgewoJCQkJCWcuZHJhd1N0cmluZyhlbmVteS5raW5kLnRvU3RyaW5nKCksIGVuZW15LnggKiBzaXplICsgc2l6ZSAvIDIKCQkJCQkJCS0gZy5nZXRGb250TWV0cmljcygpLnN0cmluZ1dpZHRoKGVuZW15LmtpbmQudG9TdHJpbmcoKSkgLyAyLCBlbmVteS55CgkJCQkJCQkqIHNpemUgKyBnLmdldEZvbnRNZXRyaWNzKCkuZ2V0QXNjZW50KCkpOwoJCQkJfQoJCQkJZy5kcmF3U3RyaW5nKCJQIiwgcGxheWVyLnggKiBzaXplICsgc2l6ZSAvIDIgLSBnLmdldEZvbnRNZXRyaWNzKCkuc3RyaW5nV2lkdGgoIlAiKQoJCQkJCQkvIDIsIHBsYXllci55ICogc2l6ZSArIGcuZ2V0Rm9udE1ldHJpY3MoKS5nZXRBc2NlbnQoKSk7CgkJCQlmb3IgKGludCBpID0gMDsgaSA8IGhlaWdodDsgaSsrKSB7CgkJCQkJZm9yIChpbnQgaiA9IDA7IGogPCB3aWR0aDsgaisrKSB7CgkJCQkJCXN3aXRjaCAoZGF0YVtpXVtqXSkgewoJCQkJCQljYXNlICcjJzoKCQkJCQkJCWcuc2V0Q29sb3IoQ29sb3IuR1JBWSk7CgkJCQkJCQlnLmZpbGxSZWN0KGogKiBzaXplLCBpICogc2l6ZSwgc2l6ZSwgc2l6ZSk7CgkJCQkJCQlicmVhazsKCQkJCQkJY2FzZSAnLic6CgkJCQkJCQlnLnNldENvbG9yKENvbG9yLkJMQUNLKTsKCQkJCQkJCWcuZmlsbE92YWwoaiAqIHNpemUgKyBzaXplIC8gMiAtIDIsIGkgKiBzaXplICsgc2l6ZSAvIDIgLSAyLCA0LCA0KTsKCQkJCQkJCWJyZWFrOwoJCQkJCQl9CgkJCQkJfQoJCQkJfQoJCQl9CgkJfTsKCQlwbGF5UGFuZWwuc2V0Rm9jdXNhYmxlKHRydWUpOwoJCXBsYXlQYW5lbC5hZGRLZXlMaXN0ZW5lcihuZXcgS2V5QWRhcHRlcigpIHsKCQkJQE92ZXJyaWRlCgkJCXB1YmxpYyB2b2lkIGtleVByZXNzZWQoZmluYWwgS2V5RXZlbnQgZSkgewoJCQkJc3dpdGNoIChlLmdldEtleUNvZGUoKSkgewoJCQkJY2FzZSBLZXlFdmVudC5WS19FU0NBUEU6CgkJCQkJaW5pdGlhbGl6ZSh3aWR0aCwgaGVpZ2h0LCBkYXRhLCBzdGFnZSwgcGxheWVyLCBlbmVtaWVzLCB0aW1lLCBkb3RzLCBsb2csCgkJCQkJCQlpc0RlYWQsIHN0YXR1c0xhYmVsLCB0aW1lTGltaXQpOwoJCQkJCXBsYXlQYW5lbC5yZXBhaW50KCk7CgkJCQkJYnJlYWs7CgkJCQljYXNlIEtleUV2ZW50LlZLX1o6CgkJCQkJcGxheUJhY2sobG9nLnRvU3RyaW5nKCkucmVwbGFjZUZpcnN0KCIuJCIsICIiKSwgc3RhZ2UsIHRpbWVMaW1pdCwgd2lkdGgsCgkJCQkJCQloZWlnaHQsIGRhdGEsIHBsYXllciwgZW5lbWllcywgdGltZSwgZG90cywgbG9nLCBpc0RlYWQsIHN0YXR1c0xhYmVsLAoJCQkJCQkJcGxheVBhbmVsKTsKCQkJCQlwbGF5UGFuZWwucmVwYWludCgpOwoJCQkJCXVwZGF0ZVN0YXR1c0xhYmVsKHN0YXR1c0xhYmVsLCBzdGFnZSwgdGltZUxpbWl0LCB0aW1lLCBkb3RzLCBsb2cpOwoJCQkJCWJyZWFrOwoJCQkJY2FzZSBLZXlFdmVudC5WS18xOgoJCQkJCWJyZWFrOwoJCQkJY2FzZSBLZXlFdmVudC5WS18yOgoJCQkJCWJyZWFrOwoJCQkJY2FzZSBLZXlFdmVudC5WS18zOgoJCQkJfQoJCQkJaWYgKCFpc0RlYWQuZ2V0KCkpIHsKCQkJCQlzd2l0Y2ggKGUuZ2V0S2V5Q29kZSgpKSB7CgkJCQkJY2FzZSBLZXlFdmVudC5WS19ET1dOOgoJCQkJCWNhc2UgS2V5RXZlbnQuVktfSjoKCQkJCQkJcGxheWVyLm1vdmVEb3duKGRhdGEsIGxvZyk7CgkJCQkJCWJyZWFrOwoJCQkJCWNhc2UgS2V5RXZlbnQuVktfTEVGVDoKCQkJCQljYXNlIEtleUV2ZW50LlZLX0g6CgkJCQkJCXBsYXllci5tb3ZlTGVmdChkYXRhLCBsb2cpOwoJCQkJCQlicmVhazsKCQkJCQljYXNlIEtleUV2ZW50LlZLX1VQOgoJCQkJCWNhc2UgS2V5RXZlbnQuVktfSzoKCQkJCQkJcGxheWVyLm1vdmVVcChkYXRhLCBsb2cpOwoJCQkJCQlicmVhazsKCQkJCQljYXNlIEtleUV2ZW50LlZLX1JJR0hUOgoJCQkJCWNhc2UgS2V5RXZlbnQuVktfTDoKCQkJCQkJcGxheWVyLm1vdmVSaWdodChkYXRhLCBsb2cpOwoJCQkJCQlicmVhazsKCQkJCQljYXNlIEtleUV2ZW50LlZLX1NQQUNFOgoJCQkJCWNhc2UgS2V5RXZlbnQuVktfRU5URVI6CgkJCQkJY2FzZSBLZXlFdmVudC5WS19QRVJJT0Q6CgkJCQkJCXBsYXllci5zdGF5KGxvZyk7CgkJCQkJCWJyZWFrOwoJCQkJCWRlZmF1bHQ6CgkJCQkJCXJldHVybjsKCQkJCQl9CgkJCQkJbW92ZUVuZW1pZXMoZGF0YSwgcGxheWVyLCBlbmVtaWVzLCB0aW1lLCBpc0RlYWQpOwoJCQkJCWdldERvdChkYXRhLCBwbGF5ZXIsIGRvdHMpOwoJCQkJCXRpbWUuaW5jcmVtZW50QW5kR2V0KCk7CgkJCQkJaWYgKHRpbWUuZ2V0KCkgPj0gdGltZUxpbWl0KSB7CgkJCQkJCWlzRGVhZC5zZXQodHJ1ZSk7CgkJCQkJfQoJCQkJCWlmIChkb3RzLmdldCgpID09IHN0YWdlLmRvdHMpIHsKCQkJCQkJU3lzdGVtLm91dC5wcmludGYoIihjbGVhcmVkKXRpbWU6ICVkLyVkLCBzY29yZTogJWQvJWQsIGxvZzogJXNcbiIsCgkJCQkJCQkJdGltZS5nZXQoKSwgdGltZUxpbWl0LCBkb3RzLmdldCgpLCBzdGFnZS5kb3RzLCBsb2cpOwoJCQkJCQlpc0RlYWQuc2V0KHRydWUpOwoJCQkJCX0KCQkJCQlwbGF5UGFuZWwucmVwYWludCgpOwoJCQkJCXVwZGF0ZVN0YXR1c0xhYmVsKHN0YXR1c0xhYmVsLCBzdGFnZSwgdGltZUxpbWl0LCB0aW1lLCBkb3RzLCBsb2cpOwoJCQkJCWlmIChpc0RlYWQuZ2V0KCkpIHsKCQkJCQkJc3RhdHVzTGFiZWwuc2V0VGV4dCgoZG90cy5nZXQoKSA9PSBzdGFnZS5kb3RzID8gIihjbGVhcmVkKSIgOiAiKGRlYWQpIikKCQkJCQkJCQkrIHN0YXR1c0xhYmVsLmdldFRleHQoKSk7CgkJCQkJCVRvb2xraXQuZ2V0RGVmYXVsdFRvb2xraXQoKS5nZXRTeXN0ZW1DbGlwYm9hcmQoKQoJCQkJCQkJCS5zZXRDb250ZW50cyhuZXcgU3RyaW5nU2VsZWN0aW9uKGxvZy50b1N0cmluZygpKSwgbnVsbCk7CgkJCQkJfQoJCQkJfQoJCQl9CgkJfSk7CgkJcGxheVBhbmVsLnNldFByZWZlcnJlZFNpemUobmV3IERpbWVuc2lvbih3aWR0aCAqIHNpemUsIGhlaWdodCAqIHNpemUpKTsKCQlwbGF5UGFuZWwuc2V0QmFja2dyb3VuZChDb2xvci5XSElURSk7CgkJZnJhbWUuYWRkKHBsYXlQYW5lbCk7CgkJZnJhbWUucGFjaygpOwoJCWZyYW1lLnNldExvY2F0aW9uQnlQbGF0Zm9ybSh0cnVlKTsKCQlmcmFtZS5zZXREZWZhdWx0Q2xvc2VPcGVyYXRpb24oV2luZG93Q29uc3RhbnRzLkVYSVRfT05fQ0xPU0UpOwoJCWluaXRpYWxpemUod2lkdGgsIGhlaWdodCwgZGF0YSwgc3RhZ2UsIHBsYXllciwgZW5lbWllcywgdGltZSwgZG90cywgbG9nLCBpc0RlYWQsCgkJCQlzdGF0dXNMYWJlbCwgdGltZUxpbWl0KTsKCQlmcmFtZS5zZXRWaXNpYmxlKHRydWUpOwoJfQoKCS8qKgoJICog5Yid5pyf5YyWCgkgKiBAcGFyYW0gd2lkdGgg5bmFCgkgKiBAcGFyYW0gaGVpZ2h0IOmrmOOBlQoJICogQHBhcmFtIGRhdGEg44OH44O844K/CgkgKiBAcGFyYW0gc3RhZ2Ug44K544OG44O844K4CgkgKiBAcGFyYW0gcGxheWVyIOODl+ODrOOCpOODpAoJICogQHBhcmFtIGVuZW1pZXMg5pW1CgkgKiBAcGFyYW0gdGltZSDmmYLliLsKCSAqIEBwYXJhbSBkb3RzIOeCuQoJICogQHBhcmFtIGxvZyDjg63jgrAKCSAqIEBwYXJhbSBpc0RlYWQg5q2744KT44Gn44GE44KL44GL44Gp44GG44GLCgkgKiBAcGFyYW0gc3RhdHVzTGFiZWwg44Op44OZ44OrCgkgKiBAcGFyYW0gdGltZUxpbWl0IOWItumZkOaZgumWkwoJICovCglzdGF0aWMgdm9pZCBpbml0aWFsaXplKGZpbmFsIGludCB3aWR0aCwgZmluYWwgaW50IGhlaWdodCwgZmluYWwgY2hhcltdW10gZGF0YSwKCQkJZmluYWwgU3RhZ2Ugc3RhZ2UsIGZpbmFsIFBsYXllciBwbGF5ZXIsIGZpbmFsIExpc3Q8RW5lbXk+IGVuZW1pZXMsCgkJCWZpbmFsIEF0b21pY0ludGVnZXIgdGltZSwgZmluYWwgQXRvbWljSW50ZWdlciBkb3RzLCBmaW5hbCBTdHJpbmdCdWlsZGVyIGxvZywKCQkJZmluYWwgQXRvbWljQm9vbGVhbiBpc0RlYWQsIGZpbmFsIEpMYWJlbCBzdGF0dXNMYWJlbCwgZmluYWwgaW50IHRpbWVMaW1pdCkgewoJCWVuZW1pZXMuY2xlYXIoKTsKCQlmb3IgKGludCBpID0gMDsgaSA8IGhlaWdodDsgaSsrKSB7CgkJCWZvciAoaW50IGogPSAwOyBqIDwgd2lkdGg7IGorKykgewoJCQkJZmluYWwgY2hhciBraW5kID0gc3RhZ2UuZGF0YVtpXVtqXTsKCQkJCXN3aXRjaCAoa2luZCkgewoJCQkJY2FzZSAnVic6CgkJCQkJZGF0YVtpXVtqXSA9ICcgJzsKCQkJCQllbmVtaWVzLmFkZChuZXcgRW5lbXkoaiwgaSwgRW5lbXkuS2luZC5WKSk7CgkJCQkJYnJlYWs7CgkJCQljYXNlICdIJzoKCQkJCQlkYXRhW2ldW2pdID0gJyAnOwoJCQkJCWVuZW1pZXMuYWRkKG5ldyBFbmVteShqLCBpLCBFbmVteS5LaW5kLkgpKTsKCQkJCQlicmVhazsKCQkJCWNhc2UgJ0wnOgoJCQkJCWRhdGFbaV1bal0gPSAnICc7CgkJCQkJZW5lbWllcy5hZGQobmV3IEVuZW15KGosIGksIEVuZW15LktpbmQuTCkpOwoJCQkJCWJyZWFrOwoJCQkJY2FzZSAnUic6CgkJCQkJZGF0YVtpXVtqXSA9ICcgJzsKCQkJCQllbmVtaWVzLmFkZChuZXcgRW5lbXkoaiwgaSwgRW5lbXkuS2luZC5SKSk7CgkJCQkJYnJlYWs7CgkJCQljYXNlICdKJzoKCQkJCQlkYXRhW2ldW2pdID0gJyAnOwoJCQkJCWVuZW1pZXMuYWRkKG5ldyBFbmVteShqLCBpLCBFbmVteS5LaW5kLkpMKSk7CgkJCQkJYnJlYWs7CgkJCQljYXNlICdAJzoKCQkJCQlkYXRhW2ldW2pdID0gJyAnOwoJCQkJCXBsYXllci54ID0gajsKCQkJCQlwbGF5ZXIueSA9IGk7CgkJCQkJcGxheWVyLmxhc3RYID0gcGxheWVyLng7CgkJCQkJcGxheWVyLmxhc3RZID0gcGxheWVyLnk7CgkJCQkJYnJlYWs7CgkJCQljYXNlICcjJzoKCQkJCWNhc2UgJy4nOgoJCQkJY2FzZSAnICc6CgkJCQkJZGF0YVtpXVtqXSA9IHN0YWdlLmRhdGFbaV1bal07CgkJCQkJYnJlYWs7CgkJCQlkZWZhdWx0OgoJCQkJCVN5c3RlbS5lcnIucHJpbnRmKCJ1bmtub3duIGRhdGE6ICclcycsIHggPSAlZCwgeSA9ICVkXG4iLCBzdGFnZS5kYXRhW2ldW2pdLCBqLAoJCQkJCQkJaSk7CgkJCQl9CgkJCX0KCQl9CgkJdGltZS5zZXQoMCk7CgkJZG90cy5zZXQoMCk7CgkJbG9nLnNldExlbmd0aCgwKTsKCQlpc0RlYWQuc2V0KGZhbHNlKTsKCQl1cGRhdGVTdGF0dXNMYWJlbChzdGF0dXNMYWJlbCwgc3RhZ2UsIHRpbWVMaW1pdCwgdGltZSwgZG90cywgbG9nKTsKCX0KCgkvKioKCSAqIOODquODl+ODrOOCpAoJICogQHBhcmFtIHN0cmluZyDli5XkvZzjgpLmjIfnpLrjgZnjgovmloflrZfliJcKCSAqIEBwYXJhbSBzdGFnZSDjgrnjg4bjg7zjgrgKCSAqIEBwYXJhbSB0aW1lTGltaXQg5Yi26ZmQ5pmC6ZaTCgkgKiBAcGFyYW0gd2lkdGgg5bmFCgkgKiBAcGFyYW0gaGVpZ2h0IOmrmOOBlQoJICogQHBhcmFtIGRhdGEg44OH44O844K/CgkgKiBAcGFyYW0gcGxheWVyIOODl+ODrOOCpOODpAoJICogQHBhcmFtIGVuZW1pZXMg5pW1CgkgKiBAcGFyYW0gdGltZSDmmYLliLsKCSAqIEBwYXJhbSBkb3RzIOeCuQoJICogQHBhcmFtIGxvZyDjg63jgrAKCSAqIEBwYXJhbSBpc0RlYWQg5q2744KT44Gn44GE44KL44GL44Gp44GG44GLCgkgKiBAcGFyYW0gc3RhdHVzTGFiZWwg44Op44OZ44OrCgkgKiBAcGFyYW0gcGxheVBhbmVsIOODkeODjeODqwoJICovCglzdGF0aWMgdm9pZCBwbGF5QmFjayhmaW5hbCBTdHJpbmcgc3RyaW5nLCBmaW5hbCBTdGFnZSBzdGFnZSwgZmluYWwgaW50IHRpbWVMaW1pdCwKCQkJZmluYWwgaW50IHdpZHRoLCBmaW5hbCBpbnQgaGVpZ2h0LCBmaW5hbCBjaGFyW11bXSBkYXRhLCBmaW5hbCBQbGF5ZXIgcGxheWVyLAoJCQlmaW5hbCBMaXN0PEVuZW15PiBlbmVtaWVzLCBmaW5hbCBBdG9taWNJbnRlZ2VyIHRpbWUsIGZpbmFsIEF0b21pY0ludGVnZXIgZG90cywKCQkJZmluYWwgU3RyaW5nQnVpbGRlciBsb2csIGZpbmFsIEF0b21pY0Jvb2xlYW4gaXNEZWFkLCBmaW5hbCBKTGFiZWwgc3RhdHVzTGFiZWwsCgkJCWZpbmFsIEpQYW5lbCBwbGF5UGFuZWwpIHsKCQlpbml0aWFsaXplKHdpZHRoLCBoZWlnaHQsIGRhdGEsIHN0YWdlLCBwbGF5ZXIsIGVuZW1pZXMsIHRpbWUsIGRvdHMsIGxvZywgaXNEZWFkLAoJCQkJc3RhdHVzTGFiZWwsIHRpbWVMaW1pdCk7CgkJZm9yIChpbnQgaSA9IDA7IGkgPCBzdHJpbmcubGVuZ3RoKCk7IGkrKykgewoJCQlpZiAoIWlzRGVhZC5nZXQoKSkgewoJCQkJc3dpdGNoIChzdHJpbmcuY2hhckF0KGkpKSB7CgkJCQljYXNlICdqJzoKCQkJCQlwbGF5ZXIubW92ZURvd24oZGF0YSwgbG9nKTsKCQkJCQlicmVhazsKCQkJCWNhc2UgJ2gnOgoJCQkJCXBsYXllci5tb3ZlTGVmdChkYXRhLCBsb2cpOwoJCQkJCWJyZWFrOwoJCQkJY2FzZSAnayc6CgkJCQkJcGxheWVyLm1vdmVVcChkYXRhLCBsb2cpOwoJCQkJCWJyZWFrOwoJCQkJY2FzZSAnbCc6CgkJCQkJcGxheWVyLm1vdmVSaWdodChkYXRhLCBsb2cpOwoJCQkJCWJyZWFrOwoJCQkJY2FzZSAnLic6CgkJCQkJcGxheWVyLnN0YXkobG9nKTsKCQkJCQlicmVhazsKCQkJCWRlZmF1bHQ6CgkJCQkJU3lzdGVtLmVyci5wcmludGYoInVua25vd24gZGF0YTogJXNcbiIsIHN0cmluZy5jaGFyQXQoaSkpOwoJCQkJfQoJCQkJbW92ZUVuZW1pZXMoZGF0YSwgcGxheWVyLCBlbmVtaWVzLCB0aW1lLCBpc0RlYWQpOwoJCQkJZ2V0RG90KGRhdGEsIHBsYXllciwgZG90cyk7CgkJCQl0aW1lLmluY3JlbWVudEFuZEdldCgpOwoJCQkJaWYgKHRpbWUuZ2V0KCkgPj0gdGltZUxpbWl0KSB7CgkJCQkJaXNEZWFkLnNldCh0cnVlKTsKCQkJCX0KCQkJfQoJCX0KCX0KCgkvKioKCSAqIOeCueOCkumjn+OBueOCiwoJICogQHBhcmFtIGRhdGEg44OH44O844K/CgkgKiBAcGFyYW0gcGxheWVyIOODl+ODrOOCpOODpAoJICogQHBhcmFtIGRvdHMg54K5CgkgKi8KCXN0YXRpYyB2b2lkIGdldERvdChmaW5hbCBjaGFyW11bXSBkYXRhLCBmaW5hbCBQbGF5ZXIgcGxheWVyLCBmaW5hbCBBdG9taWNJbnRlZ2VyIGRvdHMpIHsKCQlpZiAoZGF0YVtwbGF5ZXIueV1bcGxheWVyLnhdID09ICcuJykgewoJCQlkYXRhW3BsYXllci55XVtwbGF5ZXIueF0gPSAnICc7CgkJCWRvdHMuaW5jcmVtZW50QW5kR2V0KCk7CgkJfQoJfQoKCS8qKgoJICog5pW144KS56e75YuVCgkgKiBAcGFyYW0gZGF0YSDjg4fjg7zjgr8KCSAqIEBwYXJhbSBwbGF5ZXIg44OX44Os44Kk44OkCgkgKiBAcGFyYW0gZW5lbWllcyDmlbUKCSAqIEBwYXJhbSB0aW1lIOaZguWIuwoJICogQHBhcmFtIGlzRGVhZCDmrbvjgpPjgafjgYTjgovjgYvjganjgYbjgYsKCSAqLwoJc3RhdGljIHZvaWQgbW92ZUVuZW1pZXMoZmluYWwgY2hhcltdW10gZGF0YSwgZmluYWwgUGxheWVyIHBsYXllciwgZmluYWwgTGlzdDxFbmVteT4gZW5lbWllcywKCQkJZmluYWwgQXRvbWljSW50ZWdlciB0aW1lLCBmaW5hbCBBdG9taWNCb29sZWFuIGlzRGVhZCkgewoJCWZvciAoZmluYWwgRW5lbXkgZW5lbXkgOiBlbmVtaWVzKSB7CgkJCWZpbmFsIGludCB4ID0gZW5lbXkueDsKCQkJZmluYWwgaW50IHkgPSBlbmVteS55OwoJCQllbmVteS5tb3ZlKGRhdGEsIHBsYXllciwgdGltZSk7CgkJCWVuZW15Lmxhc3RYID0geDsKCQkJZW5lbXkubGFzdFkgPSB5OwoJCQlpZiAoZW5lbXkua2lsbGVkKHBsYXllcikpIHsKCQkJCWlzRGVhZC5zZXQodHJ1ZSk7CgkJCX0KCQl9Cgl9CgoJLyoqCgkgKiDnj77lnKjjga7nirbmhYvjgpLooajnpLrjgZnjgovjg6njg5njg6vjgpLmm7TmlrAKCSAqIEBwYXJhbSBzdGF0dXNMYWJlbCDjg6njg5njg6sKCSAqIEBwYXJhbSBzdGFnZSDjgrnjg4bjg7zjgrgKCSAqIEBwYXJhbSB0aW1lTGltaXQg5Yi26ZmQ5pmC6ZaTCgkgKiBAcGFyYW0gdGltZSDmmYLliLsKCSAqIEBwYXJhbSBkb3RzIOeCuQoJICogQHBhcmFtIGxvZyDjg63jgrAKCSAqLwoJc3RhdGljIHZvaWQgdXBkYXRlU3RhdHVzTGFiZWwoZmluYWwgSkxhYmVsIHN0YXR1c0xhYmVsLCBmaW5hbCBTdGFnZSBzdGFnZSwgZmluYWwgaW50IHRpbWVMaW1pdCwKCQkJZmluYWwgQXRvbWljSW50ZWdlciB0aW1lLCBmaW5hbCBBdG9taWNJbnRlZ2VyIGRvdHMsIGZpbmFsIFN0cmluZ0J1aWxkZXIgbG9nKSB7CgkJc3RhdHVzTGFiZWwuc2V0VGV4dChuZXcgRm9ybWF0dGVyKCkuZm9ybWF0KCJ0aW1lOiAlZC8lZCwgc2NvcmU6ICVkLyVkLCBsb2c6ICVzIiwKCQkJCXRpbWUuZ2V0KCksIHRpbWVMaW1pdCwgZG90cy5nZXQoKSwgc3RhZ2UuZG90cywgbG9nKS50b1N0cmluZygpKTsKCX0KfQ==