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.Random;
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.LV1;
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);
final boolean isGUI = false;
frame.setVisible(isGUI);
if (isGUI) {
}
int maxDots = 0;
int maxTime = 0;
double resetRate = .001;
final double stayRate = .2;
final double forwardRate = .9;
final double hungryRate = .8;
while (true) {
if (random.nextDouble() < stayRate) {
player.stay(log);
} else {
final char down = data[player.y + 1][player.x];
final char left = data[player.y][player.x - 1];
final char up = data[player.y - 1][player.x];
final char right = data[player.y][player.x + 1];
final int numOfWalls = (down == '#' ? 1 : 0) + (left == '#' ? 1 : 0)
+ (up == '#' ? 1 : 0) + (right == '#' ? 1 : 0);
if (numOfWalls < 2) {
rollBackPoint = log.toString();
}
final int numOfDots = (down == '.' ? 1 : 0) + (left == '.' ? 1 : 0)
+ (up == '.' ? 1 : 0) + (right == '.' ? 1 : 0);
if (numOfDots > 0 && random.nextDouble() < hungryRate) {
loop: while (true) {
switch (random.nextInt(4)) {
case 0:
if (down == '.') {
player.moveDown(data, log);
break loop;
}
break;
case 1:
if (left == '.') {
player.moveLeft(data, log);
break loop;
}
break;
case 2:
if (up == '.') {
player.moveUp(data, log);
break loop;
}
break;
case 3:
if (right == '.') {
player.moveRight(data, log);
break loop;
}
break;
}
}
} else {
loop: while (true) {
final boolean isForward = random.nextDouble() < forwardRate;
switch (random.nextInt(4)) {
case 0:
if (down != '#') {
if (isForward && player.lastY == player.y + 1) {
break;
}
player.moveDown(data, log);
break loop;
}
break;
case 1:
if (left != '#') {
if (isForward && player.lastX == player.x - 1) {
break;
}
player.moveLeft(data, log);
break loop;
}
break;
case 2:
if (up != '#') {
if (isForward && player.lastY == player.y - 1) {
break;
}
player.moveUp(data, log);
break loop;
}
break;
case 3:
if (right != '#') {
if (isForward && player.lastX == player.x + 1) {
break;
}
player.moveRight(data, log);
break loop;
}
break;
}
}
}
}
moveEnemies(data, player, enemies, time, isDead);
getDot(data, player, dots);
time.incrementAndGet();
if (time.get() >= timeLimit) {
isDead.set(true);
}
if (dots.get() == stage.dots) {
isDead.set(true);
}
if (isDead.get()) {
if (dots.get() > maxDots) {
System.
out.
printf("time: %d/%d, score: %d/%d, log: %s\n", time.
get(),
timeLimit, dots.get(), stage.dots, log);
maxDots = dots.get();
}
if (dots.get() == stage.dots && timeLimit - time.get() > maxTime) {
System.
out.
printf("clear! time: %d, score: %d, log: %s\n",
timeLimit - time.get(), dots.get(), log);
maxTime = timeLimit - time.get();
}
if (random.nextDouble() < resetRate) {
initialize(width, height, data, stage, player, enemies, time, dots, log,
isDead, statusLabel, timeLimit);
resetRate *= .9;
} else {
if (random.nextBoolean()) {
playBack(log.subSequence(0, log.length() * 99 / 100).toString(), stage,
timeLimit, width, height, data, player, enemies, time, dots, log,
isDead, statusLabel, playPanel);
} else {
playBack(rollBackPoint, stage, timeLimit, width, height, data, player,
enemies, time, dots, log, isDead, statusLabel, playPanel);
}
}
}
if (isGUI) {
playPanel.repaint();
statusLabel.setText(new Formatter().format("time: %d, score: %d, log: %s",
timeLimit - time.get(), dots.get(), log).toString());
}
}
}
/**
* 初期化
* @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());
}
}
aW1wb3J0IGphdmEuYXd0LkJvcmRlckxheW91dDsKaW1wb3J0IGphdmEuYXd0LkNvbG9yOwppbXBvcnQgamF2YS5hd3QuRGltZW5zaW9uOwppbXBvcnQgamF2YS5hd3QuR3JhcGhpY3M7CmltcG9ydCBqYXZhLmF3dC5HcmFwaGljczJEOwppbXBvcnQgamF2YS5hd3QuUmVuZGVyaW5nSGludHM7CmltcG9ydCBqYXZhLmF3dC5Ub29sa2l0OwppbXBvcnQgamF2YS5hd3QuZGF0YXRyYW5zZmVyLlN0cmluZ1NlbGVjdGlvbjsKaW1wb3J0IGphdmEuYXd0LmV2ZW50LktleUFkYXB0ZXI7CmltcG9ydCBqYXZhLmF3dC5ldmVudC5LZXlFdmVudDsKaW1wb3J0IGphdmEudXRpbC5BcnJheUxpc3Q7CmltcG9ydCBqYXZhLnV0aWwuRm9ybWF0dGVyOwppbXBvcnQgamF2YS51dGlsLkxpc3Q7CmltcG9ydCBqYXZhLnV0aWwuUmFuZG9tOwppbXBvcnQgamF2YS51dGlsLmNvbmN1cnJlbnQuYXRvbWljLkF0b21pY0Jvb2xlYW47CmltcG9ydCBqYXZhLnV0aWwuY29uY3VycmVudC5hdG9taWMuQXRvbWljSW50ZWdlcjsKCmltcG9ydCBqYXZheC5zd2luZy5KRnJhbWU7CmltcG9ydCBqYXZheC5zd2luZy5KTGFiZWw7CmltcG9ydCBqYXZheC5zd2luZy5KUGFuZWw7CmltcG9ydCBqYXZheC5zd2luZy5XaW5kb3dDb25zdGFudHM7CgovKioKICog44Oh44Kk44Oz44Kv44Op44K5CiAqLwpwdWJsaWMgY2xhc3MgTWFpbiB7CgoJLyoqCgkgKiAx5L2T44Gu5pW1CgkgKi8KCXN0YXRpYyBjbGFzcyBFbmVteSB7CgoJCS8qKgoJCSAqIOaVteOBrueoruWIpQoJCSAqLwoJCWVudW0gS2luZCB7CgkJCS8qKgoJCQkgKiDmlbVWCgkJCSAqLwoJCQlWLAoJCQkvKioKCQkJICog5pW1SAoJCQkgKi8KCQkJSCwKCQkJLyoqCgkJCSAqIOaVtUwKCQkJICovCgkJCUwsCgkJCS8qKgoJCQkgKiDmlbVSCgkJCSAqLwoJCQlSLAoJCQkvKioKCQkJICog5pW1SuOBjOaVtUzjgavjgarjgaPjgabjgYTjgovnirbmhYsKCQkJICovCgkJCUpMLAoJCQkvKioKCQkJICog5pW1SuOBjOaVtVLjgavjgarjgaPjgabjgYTjgovnirbmhYsKCQkJICovCgkJCUpSLAoJCX0KCgkJLyoqCgkJICogWOW6p+aomQoJCSAqLwoJCWludCB4OwoJCS8qKgoJCSAqIFnluqfmqJkKCQkgKi8KCQlpbnQgeTsKCQkvKioKCQkgKiDnm7TliY3jga5Y5bqn5qiZCgkJICovCgkJaW50IGxhc3RYOwoJCS8qKgoJCSAqIOebtOWJjeOBrlnluqfmqJkKCQkgKi8KCQlpbnQgbGFzdFk7CgkJLyoqCgkJICog56iu6aGeCgkJICovCgkJS2luZCBraW5kOwoKCQkvKioKCQkgKiDmlbXjgpLliJ3mnJ/ljJYKCQkgKiBAcGFyYW0geCBY5bqn5qiZCgkJICogQHBhcmFtIHkgWeW6p+aomQoJCSAqIEBwYXJhbSBraW5kIOeorumhngoJCSAqLwoJCUVuZW15KGZpbmFsIGludCB4LCBmaW5hbCBpbnQgeSwgZmluYWwgS2luZCBraW5kKSB7CgkJCXRoaXMueCA9IHg7CgkJCXRoaXMueSA9IHk7CgkJCXRoaXMubGFzdFggPSB4OwoJCQl0aGlzLmxhc3RZID0geTsKCQkJdGhpcy5raW5kID0ga2luZDsKCQl9CgoJCS8qKgoJCSAqIOenu+WLlQoJCSAqIEBwYXJhbSBkYXRhIOODh+ODvOOCvwoJCSAqIEBwYXJhbSBwbGF5ZXIg44OX44Os44Kk44OkCgkJICogQHBhcmFtIHRpbWUg5pmC5Yi7CgkJICovCgkJdm9pZCBtb3ZlKGZpbmFsIGNoYXJbXVtdIGRhdGEsIGZpbmFsIFBsYXllciBwbGF5ZXIsIGZpbmFsIEF0b21pY0ludGVnZXIgdGltZSkgewoJCQlmaW5hbCBjaGFyIGRvd24gPSBkYXRhW3RoaXMueSArIDFdW3RoaXMueF07CgkJCWZpbmFsIGNoYXIgbGVmdCA9IGRhdGFbdGhpcy55XVt0aGlzLnggLSAxXTsKCQkJZmluYWwgY2hhciB1cCA9IGRhdGFbdGhpcy55IC0gMV1bdGhpcy54XTsKCQkJZmluYWwgY2hhciByaWdodCA9IGRhdGFbdGhpcy55XVt0aGlzLnggKyAxXTsKCQkJaWYgKHRpbWUuZ2V0KCkgPT0gMCkgewoJCQkJLyoKCQkJCSAqIOaZguWIuyB0ID0gMCDjgavjgYrjgYTjgabjga/jgIHliJ3mnJ/kvY3nva7jga4g5LiL44CB5bem44CB5LiK44CB5Y+zIOOBrumghuOBp+acgOWIneOBq+mAsuWFpeWPr+iDveOBquODnuOCueOBruaWueWQkeOBq+enu+WLleOBl+OBvuOBmeOAggoJCQkJICovCgkJCQlpZiAoZG93biAhPSAnIycpIHsKCQkJCQl0aGlzLnkrKzsKCQkJCX0gZWxzZSBpZiAobGVmdCAhPSAnIycpIHsKCQkJCQl0aGlzLngtLTsKCQkJCX0gZWxzZSBpZiAodXAgIT0gJyMnKSB7CgkJCQkJdGhpcy55LS07CgkJCQl9IGVsc2UgaWYgKHJpZ2h0ICE9ICcjJykgewoJCQkJCXRoaXMueCsrOwoJCQkJfSBlbHNlIHsKCQkJCQlTeXN0ZW0uZXJyLnByaW50Zigi5YuV44GR44G+44Gb44KT44CCdCA9ICVkLCB4ID0gJWQsIHkgPSAlZCwga2luZCA9ICVzXG4iLCB0aW1lLmdldCgpLAoJCQkJCQkJdGhpcy54LCB0aGlzLnksIHRoaXMua2luZCk7CgkJCQl9CgkJCX0gZWxzZSB7CgkJCQlmaW5hbCBpbnQgbnVtT2ZXYWxscyA9IChkb3duID09ICcjJyA/IDEgOiAwKSArIChsZWZ0ID09ICcjJyA/IDEgOiAwKQoJCQkJCQkrICh1cCA9PSAnIycgPyAxIDogMCkgKyAocmlnaHQgPT0gJyMnID8gMSA6IDApOwoJCQkJaWYgKG51bU9mV2FsbHMgPT0gMykgewoJCQkJCS8qCgkJCQkJICog6KGM44GN5q2i44G+44KK44Oe44K544Gu5aC05ZCI44CB5ZSv5LiA6YCy5YWl5Y+v6IO944Gq6Zqj5o6l44Oe44K544Gr56e75YuV44GX44G+44GZ44CCCgkJCQkJICovCgkJCQkJaWYgKGRvd24gIT0gJyMnKSB7CgkJCQkJCXRoaXMueSsrOwoJCQkJCX0gZWxzZSBpZiAobGVmdCAhPSAnIycpIHsKCQkJCQkJdGhpcy54LS07CgkJCQkJfSBlbHNlIGlmICh1cCAhPSAnIycpIHsKCQkJCQkJdGhpcy55LS07CgkJCQkJfSBlbHNlIGlmIChyaWdodCAhPSAnIycpIHsKCQkJCQkJdGhpcy54Kys7CgkJCQkJfSBlbHNlIHsKCQkJCQkJU3lzdGVtLmVyci5wcmludGYoIuWLleOBkeOBvuOBm+OCk+OAgnQgPSAlZCwgeCA9ICVkLCB5ID0gJWQsIGtpbmQgPSAlc1xuIiwgdGltZS5nZXQoKSwKCQkJCQkJCQl0aGlzLngsIHRoaXMueSwgdGhpcy5raW5kKTsKCQkJCQl9CgkJCQl9IGVsc2UgaWYgKG51bU9mV2FsbHMgPT0gMikgewoJCQkJCS8qCgkJCQkJICog6YCa6Lev44Oe44K544Gu5aC05ZCI44CB5pmC5Yi7IHQtMSDjgavlsYXjgZ/jg57jgrnku6XlpJbjga7pgLLlhaXlj6/og73jgarpmqPmjqXjgZnjgovjg57jgrnjgavnp7vli5XjgZfjgb7jgZnjgIIKCQkJCQkgKi8KCQkJCQlpZiAoZG93biAhPSAnIycgJiYgdGhpcy5sYXN0WSAhPSB0aGlzLnkgKyAxKSB7CgkJCQkJCXRoaXMueSsrOwoJCQkJCX0gZWxzZSBpZiAobGVmdCAhPSAnIycgJiYgdGhpcy5sYXN0WCAhPSB0aGlzLnggLSAxKSB7CgkJCQkJCXRoaXMueC0tOwoJCQkJCX0gZWxzZSBpZiAodXAgIT0gJyMnICYmIHRoaXMubGFzdFkgIT0gdGhpcy55IC0gMSkgewoJCQkJCQl0aGlzLnktLTsKCQkJCQl9IGVsc2UgaWYgKHJpZ2h0ICE9ICcjJyAmJiB0aGlzLmxhc3RYICE9IHRoaXMueCArIDEpIHsKCQkJCQkJdGhpcy54Kys7CgkJCQkJfSBlbHNlIHsKCQkJCQkJU3lzdGVtLmVyci5wcmludGYoIuWLleOBkeOBvuOBm+OCk+OAgnQgPSAlZCwgeCA9ICVkLCB5ID0gJWQsIGtpbmQgPSAlc1xuIiwgdGltZS5nZXQoKSwKCQkJCQkJCQl0aGlzLngsIHRoaXMueSwgdGhpcy5raW5kKTsKCQkJCQl9CgkJCQl9IGVsc2UgaWYgKG51bU9mV2FsbHMgPT0gMSB8fCBudW1PZldhbGxzID09IDApIHsKCQkJCQkvKgoJCQkJCSAqIOS6pOW3rueCueODnuOCueOBruWgtOWQiOOBr+OAgeaVteOBrueoruWIpeOBq+W/nOOBmOOBn+OCouODq+OCtOODquOCuuODoOOBq+W/nOOBmOOBpuenu+WLleaWueWQkeOCkuaxuuWumuOBl+OBvuOBmeOAggoJCQkJCSAqLwoJCQkJCWZpbmFsIGludCBkWCA9IHBsYXllci5sYXN0WCAtIHRoaXMueDsKCQkJCQlmaW5hbCBpbnQgZFkgPSBwbGF5ZXIubGFzdFkgLSB0aGlzLnk7CgkJCQkJZmluYWwgaW50IHNpZ25EWCA9IChpbnQpIE1hdGguc2lnbnVtKGRYKTsKCQkJCQlmaW5hbCBpbnQgc2lnbkRZID0gKGludCkgTWF0aC5zaWdudW0oZFkpOwoJCQkJCXN3aXRjaCAodGhpcy5raW5kKSB7CgkJCQkJY2FzZSBWOgoJCQkJCQkvKgoJCQkJCQkgKiDmlbXjgYvjgonopovjgZ/oh6rmqZ/jga7nm7jlr77kvY3nva7jgpIgKGR4LCBkeSkg44Go6KGo44GZ44KC44Gu44Go44GX44G+44GZ44CC5qyh44Gu44Or44O844Or44KS5LiK44GL44KJ6aCG44Gr6YGp55So44GX44CB5pyA5Yid44Gr6YG444Gw44KM44Gf5pa55ZCR44Gr56e75YuV44GX44G+44GZ44CCCgkJCQkJCSAqIDEuIGR5IOKJoCAwIOOBp+OBi+OBpCBkeSDjga7nrKblj7fmlrnlkJHjgavjgYLjgovjg57jgrnjgYzpgLLlhaXlj6/og73jgafjgYLjgozjgbDjgIHjgZ3jga7mlrnlkJHjgavnp7vli5XjgZfjgb7jgZnjgIIKCQkJCQkJICogMi4gZHgg4omgIDAg44Gn44GL44GkIGR4IOOBruespuWPt+aWueWQkeOBq+OBguOCi+ODnuOCueOBjOmAsuWFpeWPr+iDveOBp+OBguOCjOOBsOOAgeOBneOBruaWueWQkeOBq+enu+WLleOBl+OBvuOBmeOAggoJCQkJCQkgKiAzLiDnj77lnKjkvY3nva7jga4g5LiL44CB5bem44CB5LiK44CB5Y+zIOOBrumghuOBp+acgOWIneOBq+mAsuWFpeWPr+iDveOBquODnuOCueOBruaWueWQkeOBq+enu+WLleOBmeOCi+OAggoJCQkJCQkgKi8KCQkJCQkJaWYgKGRZICE9IDAgJiYgZGF0YVt0aGlzLnkgKyBzaWduRFldW3RoaXMueF0gIT0gJyMnKSB7CgkJCQkJCQl0aGlzLnkgKz0gc2lnbkRZOwoJCQkJCQl9IGVsc2UgaWYgKGRYICE9IDAgJiYgZGF0YVt0aGlzLnldW3RoaXMueCArIHNpZ25EWF0gIT0gJyMnKSB7CgkJCQkJCQl0aGlzLnggKz0gc2lnbkRYOwoJCQkJCQl9IGVsc2UgewoJCQkJCQkJaWYgKGRvd24gIT0gJyMnKSB7CgkJCQkJCQkJdGhpcy55Kys7CgkJCQkJCQl9IGVsc2UgaWYgKGxlZnQgIT0gJyMnKSB7CgkJCQkJCQkJdGhpcy54LS07CgkJCQkJCQl9IGVsc2UgaWYgKHJpZ2h0ICE9ICcjJykgewoJCQkJCQkJCXRoaXMueCsrOwoJCQkJCQkJfSBlbHNlIGlmICh1cCAhPSAnIycpIHsKCQkJCQkJCQl0aGlzLnktLTsKCQkJCQkJCX0gZWxzZSB7CgkJCQkJCQkJU3lzdGVtLmVyci5wcmludGYoIuWLleOBkeOBvuOBm+OCk+OAgnQgPSAlZCwgeCA9ICVkLCB5ID0gJWQsIGtpbmQgPSAlc1xuIiwKCQkJCQkJCQkJCXRpbWUuZ2V0KCksIHRoaXMueCwgdGhpcy55LCB0aGlzLmtpbmQpOwoJCQkJCQkJfQoJCQkJCQl9CgkJCQkJCWJyZWFrOwoJCQkJCWNhc2UgSDoKCQkJCQkJLyoKCQkJCQkJICog5pW1IFYg44Go44G744G85ZCM44GY44Gn44GZ44CC5ZSv5LiA55Ww44Gq44KL44Gu44GvIOOAgemAsuihjOaWueWQkeOCkuaxuuOCgeOCi+ODq+ODvOODq+OBruOBhuOBoeOAgQoJCQkJCQkgKiDmnIDliJ3jga7kuozjgaTjga7jg6vjg7zjg6vjga7pgannlKjpoIbluo/jgYzlhaXjgozmm7/jgo/jgovjgajjgZPjgo3jgafjgZnjgIIKCQkJCQkJICog44GZ44Gq44KP44Gh44CB5YWI44GrIGR4IOKJoCAwIOOBruODgeOCp+ODg+OCr+OCkuihjOOBquOBhOOAgeasoeOBqyBkeSDiiaAgMCDjga7jg4Hjgqfjg4Pjgq/jgpLooYzjgYTjgb7jgZnjgIIKCQkJCQkJICovCgkJCQkJCWlmIChkWCAhPSAwICYmIGRhdGFbdGhpcy55XVt0aGlzLnggKyBzaWduRFhdICE9ICcjJykgewoJCQkJCQkJdGhpcy54ICs9IHNpZ25EWDsKCQkJCQkJfSBlbHNlIGlmIChkWSAhPSAwICYmIGRhdGFbdGhpcy55ICsgc2lnbkRZXVt0aGlzLnhdICE9ICcjJykgewoJCQkJCQkJdGhpcy55ICs9IHNpZ25EWTsKCQkJCQkJfSBlbHNlIHsKCQkJCQkJCWlmIChkb3duICE9ICcjJykgewoJCQkJCQkJCXRoaXMueSsrOwoJCQkJCQkJfSBlbHNlIGlmIChsZWZ0ICE9ICcjJykgewoJCQkJCQkJCXRoaXMueC0tOwoJCQkJCQkJfSBlbHNlIGlmIChyaWdodCAhPSAnIycpIHsKCQkJCQkJCQl0aGlzLngrKzsKCQkJCQkJCX0gZWxzZSBpZiAodXAgIT0gJyMnKSB7CgkJCQkJCQkJdGhpcy55LS07CgkJCQkJCQl9IGVsc2UgewoJCQkJCQkJCVN5c3RlbS5lcnIucHJpbnRmKCLli5XjgZHjgb7jgZvjgpPjgIJ0ID0gJWQsIHggPSAlZCwgeSA9ICVkLCBraW5kID0gJXNcbiIsCgkJCQkJCQkJCQl0aW1lLmdldCgpLCB0aGlzLngsIHRoaXMueSwgdGhpcy5raW5kKTsKCQkJCQkJCX0KCQkJCQkJfQoJCQkJCQlicmVhazsKCQkJCQljYXNlIEw6CgkJCQkJY2FzZSBKTDoKCQkJCQkJLyoKCQkJCQkJICog54++5Zyo5L2N572u44G444Gu6YCy5YWl5pa55ZCR44GL44KJ6KaL44Gm55u45a++55qE44GrIOW3puOAgeWJjeOAgeWPsyDjga7poIbjgafmnIDliJ3jgavpgLLlhaXlj6/og73jgarjg57jgrnjga7mlrnlkJHjgavnp7vli5XjgZfjgb7jgZnjgIIKCQkJCQkJICovCgkJCQkJCWlmICh0aGlzLmxhc3RZICsgMSA9PSB0aGlzLnkpIHsKCQkJCQkJCWlmIChyaWdodCAhPSAnIycpIHsKCQkJCQkJCQl0aGlzLngrKzsKCQkJCQkJCX0gZWxzZSBpZiAoZG93biAhPSAnIycpIHsKCQkJCQkJCQl0aGlzLnkrKzsKCQkJCQkJCX0gZWxzZSBpZiAobGVmdCAhPSAnIycpIHsKCQkJCQkJCQl0aGlzLngtLTsKCQkJCQkJCX0gZWxzZSB7CgkJCQkJCQkJU3lzdGVtLmVyci5wcmludGYoIuWLleOBkeOBvuOBm+OCk+OAgnQgPSAlZCwgeCA9ICVkLCB5ID0gJWQsIGtpbmQgPSAlc1xuIiwKCQkJCQkJCQkJCXRpbWUuZ2V0KCksIHRoaXMueCwgdGhpcy55LCB0aGlzLmtpbmQpOwoJCQkJCQkJfQoJCQkJCQl9IGVsc2UgaWYgKHRoaXMubGFzdFggLSAxID09IHRoaXMueCkgewoJCQkJCQkJaWYgKGRvd24gIT0gJyMnKSB7CgkJCQkJCQkJdGhpcy55Kys7CgkJCQkJCQl9IGVsc2UgaWYgKGxlZnQgIT0gJyMnKSB7CgkJCQkJCQkJdGhpcy54LS07CgkJCQkJCQl9IGVsc2UgaWYgKHVwICE9ICcjJykgewoJCQkJCQkJCXRoaXMueS0tOwoJCQkJCQkJfSBlbHNlIHsKCQkJCQkJCQlTeXN0ZW0uZXJyLnByaW50Zigi5YuV44GR44G+44Gb44KT44CCdCA9ICVkLCB4ID0gJWQsIHkgPSAlZCwga2luZCA9ICVzXG4iLAoJCQkJCQkJCQkJdGltZS5nZXQoKSwgdGhpcy54LCB0aGlzLnksIHRoaXMua2luZCk7CgkJCQkJCQl9CgkJCQkJCX0gZWxzZSBpZiAodGhpcy5sYXN0WSAtIDEgPT0gdGhpcy55KSB7CgkJCQkJCQlpZiAobGVmdCAhPSAnIycpIHsKCQkJCQkJCQl0aGlzLngtLTsKCQkJCQkJCX0gZWxzZSBpZiAodXAgIT0gJyMnKSB7CgkJCQkJCQkJdGhpcy55LS07CgkJCQkJCQl9IGVsc2UgaWYgKHJpZ2h0ICE9ICcjJykgewoJCQkJCQkJCXRoaXMueCsrOwoJCQkJCQkJfSBlbHNlIHsKCQkJCQkJCQlTeXN0ZW0uZXJyLnByaW50Zigi5YuV44GR44G+44Gb44KT44CCdCA9ICVkLCB4ID0gJWQsIHkgPSAlZCwga2luZCA9ICVzXG4iLAoJCQkJCQkJCQkJdGltZS5nZXQoKSwgdGhpcy54LCB0aGlzLnksIHRoaXMua2luZCk7CgkJCQkJCQl9CgkJCQkJCX0gZWxzZSBpZiAodGhpcy5sYXN0WCArIDEgPT0gdGhpcy54KSB7CgkJCQkJCQlpZiAodXAgIT0gJyMnKSB7CgkJCQkJCQkJdGhpcy55LS07CgkJCQkJCQl9IGVsc2UgaWYgKHJpZ2h0ICE9ICcjJykgewoJCQkJCQkJCXRoaXMueCsrOwoJCQkJCQkJfSBlbHNlIGlmIChkb3duICE9ICcjJykgewoJCQkJCQkJCXRoaXMueSsrOwoJCQkJCQkJfSBlbHNlIHsKCQkJCQkJCQlTeXN0ZW0uZXJyLnByaW50Zigi5YuV44GR44G+44Gb44KT44CCdCA9ICVkLCB4ID0gJWQsIHkgPSAlZCwga2luZCA9ICVzXG4iLAoJCQkJCQkJCQkJdGltZS5nZXQoKSwgdGhpcy54LCB0aGlzLnksIHRoaXMua2luZCk7CgkJCQkJCQl9CgkJCQkJCX0gZWxzZSB7CgkJCQkJCQlTeXN0ZW0uZXJyLnByaW50Zigi5q2i44G+44Gj44Gm44GE44Gf77yfdCA9ICVkLCB4ID0gJWQsIHkgPSAlZCwga2luZCA9ICVzXG4iLAoJCQkJCQkJCQl0aW1lLmdldCgpLCB0aGlzLngsIHRoaXMueSwgdGhpcy5raW5kKTsKCQkJCQkJfQoJCQkJCQlicmVhazsKCQkJCQljYXNlIFI6CgkJCQkJY2FzZSBKUjoKCQkJCQkJLyoKCQkJCQkJICog54++5Zyo5L2N572u44G444Gu6YCy5YWl5pa55ZCR44GL44KJ6KaL44Gm55u45a++55qE44GrIOWPs+OAgeWJjeOAgeW3piDjga7poIbjgafmnIDliJ3jgavpgLLlhaXlj6/og73jgarjg57jgrnjga7mlrnlkJHjgavnp7vli5XjgZfjgb7jgZnjgIIKCQkJCQkJICovCgkJCQkJCWlmICh0aGlzLmxhc3RZICsgMSA9PSB0aGlzLnkpIHsKCQkJCQkJCWlmIChsZWZ0ICE9ICcjJykgewoJCQkJCQkJCXRoaXMueC0tOwoJCQkJCQkJfSBlbHNlIGlmIChkb3duICE9ICcjJykgewoJCQkJCQkJCXRoaXMueSsrOwoJCQkJCQkJfSBlbHNlIGlmIChyaWdodCAhPSAnIycpIHsKCQkJCQkJCQl0aGlzLngrKzsKCQkJCQkJCX0gZWxzZSB7CgkJCQkJCQkJU3lzdGVtLmVyci5wcmludGYoIuWLleOBkeOBvuOBm+OCk+OAgnQgPSAlZCwgeCA9ICVkLCB5ID0gJWQsIGtpbmQgPSAlc1xuIiwKCQkJCQkJCQkJCXRpbWUuZ2V0KCksIHRoaXMueCwgdGhpcy55LCB0aGlzLmtpbmQpOwoJCQkJCQkJfQoJCQkJCQl9IGVsc2UgaWYgKHRoaXMubGFzdFggLSAxID09IHRoaXMueCkgewoJCQkJCQkJaWYgKHVwICE9ICcjJykgewoJCQkJCQkJCXRoaXMueS0tOwoJCQkJCQkJfSBlbHNlIGlmIChsZWZ0ICE9ICcjJykgewoJCQkJCQkJCXRoaXMueC0tOwoJCQkJCQkJfSBlbHNlIGlmIChkb3duICE9ICcjJykgewoJCQkJCQkJCXRoaXMueSsrOwoJCQkJCQkJfSBlbHNlIHsKCQkJCQkJCQlTeXN0ZW0uZXJyLnByaW50Zigi5YuV44GR44G+44Gb44KT44CCdCA9ICVkLCB4ID0gJWQsIHkgPSAlZCwga2luZCA9ICVzXG4iLAoJCQkJCQkJCQkJdGltZS5nZXQoKSwgdGhpcy54LCB0aGlzLnksIHRoaXMua2luZCk7CgkJCQkJCQl9CgkJCQkJCX0gZWxzZSBpZiAodGhpcy5sYXN0WSAtIDEgPT0gdGhpcy55KSB7CgkJCQkJCQlpZiAocmlnaHQgIT0gJyMnKSB7CgkJCQkJCQkJdGhpcy54Kys7CgkJCQkJCQl9IGVsc2UgaWYgKHVwICE9ICcjJykgewoJCQkJCQkJCXRoaXMueS0tOwoJCQkJCQkJfSBlbHNlIGlmIChsZWZ0ICE9ICcjJykgewoJCQkJCQkJCXRoaXMueC0tOwoJCQkJCQkJfSBlbHNlIHsKCQkJCQkJCQlTeXN0ZW0uZXJyLnByaW50Zigi5YuV44GR44G+44Gb44KT44CCdCA9ICVkLCB4ID0gJWQsIHkgPSAlZCwga2luZCA9ICVzXG4iLAoJCQkJCQkJCQkJdGltZS5nZXQoKSwgdGhpcy54LCB0aGlzLnksIHRoaXMua2luZCk7CgkJCQkJCQl9CgkJCQkJCX0gZWxzZSBpZiAodGhpcy5sYXN0WCArIDEgPT0gdGhpcy54KSB7CgkJCQkJCQlpZiAoZG93biAhPSAnIycpIHsKCQkJCQkJCQl0aGlzLnkrKzsKCQkJCQkJCX0gZWxzZSBpZiAocmlnaHQgIT0gJyMnKSB7CgkJCQkJCQkJdGhpcy54Kys7CgkJCQkJCQl9IGVsc2UgaWYgKHVwICE9ICcjJykgewoJCQkJCQkJCXRoaXMueS0tOwoJCQkJCQkJfSBlbHNlIHsKCQkJCQkJCQlTeXN0ZW0uZXJyLnByaW50Zigi5YuV44GR44G+44Gb44KT44CCdCA9ICVkLCB4ID0gJWQsIHkgPSAlZCwga2luZCA9ICVzXG4iLAoJCQkJCQkJCQkJdGltZS5nZXQoKSwgdGhpcy54LCB0aGlzLnksIHRoaXMua2luZCk7CgkJCQkJCQl9CgkJCQkJCX0gZWxzZSB7CgkJCQkJCQlTeXN0ZW0uZXJyLnByaW50Zigi5q2i44G+44Gj44Gm44GE44Gf77yfdCA9ICVkLCB4ID0gJWQsIHkgPSAlZCwga2luZCA9ICVzXG4iLAoJCQkJCQkJCQl0aW1lLmdldCgpLCB0aGlzLngsIHRoaXMueSwgdGhpcy5raW5kKTsKCQkJCQkJfQoJCQkJCQlicmVhazsKCQkJCQl9CgkJCQkJLyoKCQkJCQkgKiDkuqTlt67ngrnjg57jgrnjgavlhaXjgovjgZ/jgbPjgavjgIHmnIDliJ3jga/mlbVM44Gu6KGM5YuV44CB5qyh5Zue44Gv5pW1UuOBruihjOWLleOAgeOBleOCieOBq+asoeWbnuOBr+OBvuOBn+aVtUzjga7ooYzli5XjgIHjgajnubDjgorov5TjgZfjgb7jgZnjgIIKCQkJCQkgKi8KCQkJCQl0aGlzLmtpbmQgPSB0aGlzLmtpbmQgPT0gS2luZC5KTCA/IEtpbmQuSlIgOiAodGhpcy5raW5kID09IEtpbmQuSlIgPyBLaW5kLkpMCgkJCQkJCQk6IHRoaXMua2luZCk7CgkJCQl9IGVsc2UgewoJCQkJCVN5c3RlbS5lcnIucHJpbnRmKCLli5XjgZHjgb7jgZvjgpPjgIJ0ID0gJWQsIHggPSAlZCwgeSA9ICVkLCBraW5kID0gJXNcbiIsIHRpbWUuZ2V0KCksCgkJCQkJCQl0aGlzLngsIHRoaXMueSwgdGhpcy5raW5kKTsKCQkJCX0KCQkJfQoJCX0KCgkJLyoqCgkJICogQHBhcmFtIHBsYXllciDjg5fjg6zjgqTjg6QKCQkgKiBAcmV0dXJuIOauuuOBl+OBn+OBi+OBqeOBhuOBiwoJCSAqLwoJCWJvb2xlYW4ga2lsbGVkKGZpbmFsIFBsYXllciBwbGF5ZXIpIHsKCQkJaWYgKHRoaXMueCA9PSBwbGF5ZXIueCAmJiB0aGlzLnkgPT0gcGxheWVyLnkpIHsKCQkJCXJldHVybiB0cnVlOwoJCQl9IGVsc2UgaWYgKHRoaXMueCA9PSBwbGF5ZXIubGFzdFggJiYgdGhpcy55ID09IHBsYXllci5sYXN0WSAmJiB0aGlzLmxhc3RYID09IHBsYXllci54CgkJCQkJJiYgdGhpcy5sYXN0WSA9PSBwbGF5ZXIueSkgewoJCQkJcmV0dXJuIHRydWU7CgkJCX0gZWxzZSB7CgkJCQlyZXR1cm4gZmFsc2U7CgkJCX0KCQl9CgoJCUBPdmVycmlkZQoJCXB1YmxpYyBTdHJpbmcgdG9TdHJpbmcoKSB7CgkJCXJldHVybiBuZXcgRm9ybWF0dGVyKCkuZm9ybWF0KCJbeCA9ICVkLCB5ID0gJWQsIGtpbmQgPSAlc10iLCB0aGlzLngsIHRoaXMueSwgdGhpcy5raW5kKQoJCQkJCS50b1N0cmluZygpOwoJCX0KCX0KCgkvKioKCSAqIOODl+ODrOOCpOODpAoJICovCglzdGF0aWMgY2xhc3MgUGxheWVyIHsKCQkvKioKCQkgKiBY5bqn5qiZCgkJICovCgkJaW50IHggPSAtMTsKCQkvKioKCQkgKiBZ5bqn5qiZCgkJICovCgkJaW50IHkgPSAtMTsKCQkvKioKCQkgKiDnm7TliY3jga5Y5bqn5qiZCgkJICovCgkJaW50IGxhc3RYID0gLTE7CgkJLyoqCgkJICog55u05YmN44GuWeW6p+aomQoJCSAqLwoJCWludCBsYXN0WSA9IC0xOwoKCQkvKioKCQkgKiDkuIrjgbjnp7vli5UKCQkgKiBAcGFyYW0gZGF0YSDjg4fjg7zjgr8KCQkgKiBAcGFyYW0gbG9nIOODreOCsAoJCSAqLwoJCXZvaWQgbW92ZURvd24oZmluYWwgY2hhcltdW10gZGF0YSwgZmluYWwgU3RyaW5nQnVpbGRlciBsb2cpIHsKCQkJdGhpcy5sYXN0WCA9IHRoaXMueDsKCQkJdGhpcy5sYXN0WSA9IHRoaXMueTsKCQkJaWYgKGRhdGFbdGhpcy55ICsgMV1bdGhpcy54XSAhPSAnIycpIHsKCQkJCXRoaXMueSsrOwoJCQl9CgkJCWxvZy5hcHBlbmQoJ2onKTsKCQl9CgoJCS8qKgoJCSAqIOW3puOBuOenu+WLlQoJCSAqIEBwYXJhbSBkYXRhIOODh+ODvOOCvwoJCSAqIEBwYXJhbSBsb2cg44Ot44KwCgkJICovCgkJdm9pZCBtb3ZlTGVmdChmaW5hbCBjaGFyW11bXSBkYXRhLCBmaW5hbCBTdHJpbmdCdWlsZGVyIGxvZykgewoJCQl0aGlzLmxhc3RYID0gdGhpcy54OwoJCQl0aGlzLmxhc3RZID0gdGhpcy55OwoJCQlpZiAoZGF0YVt0aGlzLnldW3RoaXMueCAtIDFdICE9ICcjJykgewoJCQkJdGhpcy54LS07CgkJCX0KCQkJbG9nLmFwcGVuZCgnaCcpOwoJCX0KCgkJLyoqCgkJICog5LiK44G456e75YuVCgkJICogQHBhcmFtIGRhdGEg44OH44O844K/CgkJICogQHBhcmFtIGxvZyDjg63jgrAKCQkgKi8KCQl2b2lkIG1vdmVVcChmaW5hbCBjaGFyW11bXSBkYXRhLCBmaW5hbCBTdHJpbmdCdWlsZGVyIGxvZykgewoJCQl0aGlzLmxhc3RYID0gdGhpcy54OwoJCQl0aGlzLmxhc3RZID0gdGhpcy55OwoJCQlpZiAoZGF0YVt0aGlzLnkgLSAxXVt0aGlzLnhdICE9ICcjJykgewoJCQkJdGhpcy55LS07CgkJCX0KCQkJbG9nLmFwcGVuZCgnaycpOwoJCX0KCgkJLyoqCgkJICog5Y+z44G456e75YuVCgkJICogQHBhcmFtIGRhdGEg44OH44O844K/CgkJICogQHBhcmFtIGxvZyDjg63jgrAKCQkgKi8KCQl2b2lkIG1vdmVSaWdodChmaW5hbCBjaGFyW11bXSBkYXRhLCBmaW5hbCBTdHJpbmdCdWlsZGVyIGxvZykgewoJCQl0aGlzLmxhc3RYID0gdGhpcy54OwoJCQl0aGlzLmxhc3RZID0gdGhpcy55OwoJCQlpZiAoZGF0YVt0aGlzLnldW3RoaXMueCArIDFdICE9ICcjJykgewoJCQkJdGhpcy54Kys7CgkJCX0KCQkJbG9nLmFwcGVuZCgnbCcpOwoJCX0KCgkJLyoqCgkJICog55WZ44G+44KLCgkJICogQHBhcmFtIGxvZyDjg63jgrAKCQkgKi8KCQl2b2lkIHN0YXkoZmluYWwgU3RyaW5nQnVpbGRlciBsb2cpIHsKCQkJdGhpcy5sYXN0WCA9IHRoaXMueDsKCQkJdGhpcy5sYXN0WSA9IHRoaXMueTsKCQkJbG9nLmFwcGVuZCgnLicpOwoJCX0KCgkJQE92ZXJyaWRlCgkJcHVibGljIFN0cmluZyB0b1N0cmluZygpIHsKCQkJcmV0dXJuIG5ldyBGb3JtYXR0ZXIoKS5mb3JtYXQoIlt4ID0gJWQsIHkgPSAlZCwgbFggPSAlZCwgbFkgPSAlZF0iLCB0aGlzLngsIHRoaXMueSwKCQkJCQl0aGlzLmxhc3RYLCB0aGlzLmxhc3RZKS50b1N0cmluZygpOwoJCX0KCX0KCgkvKioKCSAqIOOCueODhuODvOOCuAoJICovCgllbnVtIFN0YWdlIHsKCQkvKioKCQkgKiDlhaXlipsxCgkJICovCgkJTFYxKDUwLCAxMSwgNywgIiMjIyMjIyMjIyMjIiArICIjLlYuLiMuLkguIyIgKyAiIy4jIy4uLiMjLiMiICsgIiNMIy4uIy4uUi4jIgoJCQkJKyAiIy4jLiMjIy4jLiMiICsgIiMuLi4uQC4uLi4jIiArICIjIyMjIyMjIyMjIyIpLAoJCS8qKgoJCSAqIOWFpeWKmzIKCQkgKi8KCQlMVjIoMzAwLCAyMCwgMTcsICIjIyMjIyMjIyMjIyMjIyMjIyMjIyIgKyAiIyMjLi4uLi5MLi4uLi4uLi4uLiMiICsgIiMjIy4jIy4jIy4jI0wjIy4jIy4jIgoJCQkJKyAiIyMjLiMjLiMjLiMjLiMjLiMjLiMiICsgIiMuTC4uLi4uLi4uLi4uLi4uLi4jIiArICIjLiMjLiMjLiMjLiMjLiMjLiMjIyIKCQkJCSsgIiMuIyMuIyNMIyMuIyMuIyMuIyMjIiArICIjLi4uLi4uLi4uLi4uLi4uLi5MIyIgKyAiIy4jLiMuI0ojIyMjSiMuIy4jLiMiCgkJCQkrICIjTC4uLi4uLi4uLi4uLi4uLi4uIyIgKyAiIyMjLiMjLiMjLiMjLiMjLiMjLiMiICsgIiMjIy4jIy4jI1IjIy4jIy4jIy4jIgoJCQkJKyAiIy4uLi4uLi4uLi4uLi4uLi5SLiMiICsgIiMuIyMuIyMuIyMuIyNSIyMuIyMjIiArICIjLiMjLiMjLiMjLiMjLiMjLiMjIyIKCQkJCSsgIiNALi4uLlIuLi4uLi4uLi4uIyMjIiArICIjIyMjIyMjIyMjIyMjIyMjIyMjIyIpLAoJCS8qKgoJCSAqIOWFpeWKmzMKCQkgKi8KCQlMVjMoNzAwLCA1OCwgMTcsICIjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIgoJCQkJKyAiIy4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uIyIKCQkJCSsgIiMuIyMjLiMjIyMjIyMjIy4jIyMjIyMjIyMjIyMjIyMuIyMjIyMjIyMuIyMjLiMjIyMjLiMjIyMjLiMiCgkJCQkrICIjLiMjIy4jIyMjIyMjIyMuIyMjIyMjIyMjIyMjIyMjLiMjIyMjIyMjLiMjIy4jIyMjIy4jIyMjIy4jIgoJCQkJKyAiIy4uLi4uIyMjIyMjIyMjLi4uLkouLi4uLi4uLi4uLi4uSi4uLi4uLi4jIyMuLi4uLi4uLi4uLi4uIyIKCQkJCSsgIiMjIyMjLiMjIy4uLi4uLi4jIyMjIyMjLiMjIyMjIyMuIyMjIyMjIyMuIyMjLiMjIyMjIyMuIyMjIyMiCgkJCQkrICIjIyMjIy4jIyMuIyMjIyNKIyMjIyMjIy4jIyMjIyMjLiMjIyMjIyMjLiMjIy4jIyAgICMjLiMjIyMjIgoJCQkJKyAiIyMjIyMuIyMjTCMjIyMjLiMjICAgIyNMIyMgICAjIy4jIyAgICAjIy4jIyMuIyMgICAjIy4jIyMjIyIKCQkJCSsgIiMjIyMjLiMjIy4uSCMjIy4jIyAgICMjLiMjICAgIyMuIyMjIyMjIyMuIyMjLiMjIyMjIyNKIyMjIyMiCgkJCQkrICIjIyMjIy4jIyMjIyMjIyMuIyMgICAjI0wjIyAgICMjLiMjIyMjIyMjLiMjIy4jIyNWLi4uLiMjIyMjIgoJCQkJKyAiIyMjIyMuIyMjIyMjIyMjLiMjIyMjIyMuIyMjIyMjIy4uLi4uLi4uLi4jIyMuIyMjIyMjIy4jIyMjIyIKCQkJCSsgIiMjIyMjLiMjIyMjIyMjIy4jIyMjIyMjLiMjIyMjIyMuIyMjIyMjIyMuIyMjLiMjIyMjIyMuIyMjIyMiCgkJCQkrICIjLi4uLi4uLi4uLi4uLi4uLi4uLi4uTC4uLi4uLi4uLiMjIyMjIyMjLi4uLi4uLi4uLlIuLi4uLi4jIgoJCQkJKyAiI0wjIyMjLiMjIyMjIyMjIyMuIyMuIyMjIyMjIyMjIy4uLi4jIy4uLi4jIyMjIyMjIyMuIyMjIyMuIyIKCQkJCSsgIiMuIyMjIy4jIyMjIyMjIyMjLiMjLiMjIyMjIyMjIyMuIyMuIyMuIyMuIyMjIyMjIyMjLiMjIyMjLiMiCgkJCQkrICIjLi4uLi4uLi4uLi4uLi4uLi4jIy4uLi4uLi4uLi4uLiMjLi5ALiMjLi4uLi4uLi4uLi4uLi4uUi4jIgoJCQkJKyAiIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyIpOwoJCS8qKgoJCSAqIOWItumZkOaZgumWkwoJCSAqLwoJCWludCB0aW1lTGltaXQ7CgkJLyoqCgkJICog5bmFCgkJICovCgkJaW50IHdpZHRoOwoJCS8qKgoJCSAqIOmrmOOBlQoJCSAqLwoJCWludCBoZWlnaHQ7CgkJLyoqCgkJICog44OH44O844K/CgkJICovCgkJY2hhcltdW10gZGF0YTsKCQkvKioKCQkgKiDngrkKCQkgKi8KCQlpbnQgZG90czsKCgkJLyoqCgkJICog44K544OG44O844K444KS5Yid5pyf5YyWCgkJICogQHBhcmFtIHRpbWVMaW1pdCDliLbpmZDmmYLplpMKCQkgKiBAcGFyYW0gd2lkdGgg5bmFCgkJICogQHBhcmFtIGhlaWdodCDpq5jjgZUKCQkgKiBAcGFyYW0gc3RyaW5nIOWFpeWKm+aWh+Wtl+WIlwoJCSAqLwoJCVN0YWdlKGZpbmFsIGludCB0aW1lTGltaXQsIGZpbmFsIGludCB3aWR0aCwgZmluYWwgaW50IGhlaWdodCwgZmluYWwgU3RyaW5nIHN0cmluZykgewoJCQl0aGlzLnRpbWVMaW1pdCA9IHRpbWVMaW1pdDsKCQkJdGhpcy53aWR0aCA9IHdpZHRoOwoJCQl0aGlzLmhlaWdodCA9IGhlaWdodDsKCQkJdGhpcy5kYXRhID0gbmV3IGNoYXJbaGVpZ2h0XVt3aWR0aF07CgkJCXRoaXMuZG90cyA9IDA7CgkJCWZvciAoaW50IGkgPSAwOyBpIDwgaGVpZ2h0OyBpKyspIHsKCQkJCWZvciAoaW50IGogPSAwOyBqIDwgd2lkdGg7IGorKykgewoJCQkJCWZpbmFsIGNoYXIgY2hhcmFjdGVyID0gc3RyaW5nLmNoYXJBdChpICogd2lkdGggKyBqKTsKCQkJCQl0aGlzLmRhdGFbaV1bal0gPSBjaGFyYWN0ZXI7CgkJCQkJaWYgKGNoYXJhY3RlciA9PSAnLicpIHsKCQkJCQkJdGhpcy5kb3RzKys7CgkJCQkJfQoJCQkJfQoJCQl9CgkJfTsKCX0KCgkvKioKCSAqIOODoeOCpOODs+ODoeOCveODg+ODiQoJICogQHBhcmFtIGFyZ3Mg44Kz44Oe44Oz44OJ44Op44Kk44Oz5byV5pWwCgkgKiBAdGhyb3dzIEludGVycnVwdGVkRXhjZXB0aW9uIOWJsuOCiui+vOOBv+S+i+WklgoJICovCglwdWJsaWMgc3RhdGljIHZvaWQgbWFpbihmaW5hbCBTdHJpbmdbXSBhcmdzKSB0aHJvd3MgSW50ZXJydXB0ZWRFeGNlcHRpb24gewoJCWZpbmFsIFN0YWdlIHN0YWdlID0gU3RhZ2UuTFYxOwoJCWZpbmFsIGludCB0aW1lTGltaXQgPSBzdGFnZS50aW1lTGltaXQ7CgkJZmluYWwgaW50IHdpZHRoID0gc3RhZ2Uud2lkdGg7CgkJZmluYWwgaW50IGhlaWdodCA9IHN0YWdlLmhlaWdodDsKCQlmaW5hbCBjaGFyW11bXSBkYXRhID0gbmV3IGNoYXJbaGVpZ2h0XVt3aWR0aF07CgkJZmluYWwgUGxheWVyIHBsYXllciA9IG5ldyBQbGF5ZXIoKTsKCQlmaW5hbCBMaXN0PEVuZW15PiBlbmVtaWVzID0gbmV3IEFycmF5TGlzdDxFbmVteT4oKTsKCQlmaW5hbCBBdG9taWNJbnRlZ2VyIHRpbWUgPSBuZXcgQXRvbWljSW50ZWdlcigpOwoJCWZpbmFsIEF0b21pY0ludGVnZXIgZG90cyA9IG5ldyBBdG9taWNJbnRlZ2VyKCk7CgkJZmluYWwgU3RyaW5nQnVpbGRlciBsb2cgPSBuZXcgU3RyaW5nQnVpbGRlcigpOwoJCWZpbmFsIEF0b21pY0Jvb2xlYW4gaXNEZWFkID0gbmV3IEF0b21pY0Jvb2xlYW4oKTsKCQlmaW5hbCBKRnJhbWUgZnJhbWUgPSBuZXcgSkZyYW1lKHN0YWdlLnRvU3RyaW5nKCkpOwoJCWZpbmFsIGludCBzaXplID0gMjA7CgkJZmluYWwgSkxhYmVsIHN0YXR1c0xhYmVsID0gbmV3IEpMYWJlbCgi44GT44GT44Gr54++5Zyo44Gu54q25oWL44GM6KGo56S644GV44KM44G+44GZ44CCIik7CgkJc3RhdHVzTGFiZWwuc2V0T3BhcXVlKHRydWUpOwoJCXN0YXR1c0xhYmVsLnNldEJhY2tncm91bmQoQ29sb3IuV0hJVEUpOwoJCWZyYW1lLmFkZChzdGF0dXNMYWJlbCwgQm9yZGVyTGF5b3V0Lk5PUlRIKTsKCQlmaW5hbCBKUGFuZWwgcGxheVBhbmVsID0gbmV3IEpQYW5lbCgpIHsKCQkJQE92ZXJyaWRlCgkJCXByb3RlY3RlZCB2b2lkIHBhaW50Q29tcG9uZW50KGZpbmFsIEdyYXBoaWNzIGdyYXBoaWNzKSB7CgkJCQlzdXBlci5wYWludENvbXBvbmVudChncmFwaGljcyk7CgkJCQlmaW5hbCBHcmFwaGljczJEIGcgPSAoR3JhcGhpY3MyRCkgZ3JhcGhpY3M7CgkJCQlnLnNldFJlbmRlcmluZ0hpbnQoUmVuZGVyaW5nSGludHMuS0VZX0FOVElBTElBU0lORywKCQkJCQkJUmVuZGVyaW5nSGludHMuVkFMVUVfQU5USUFMSUFTX09OKTsKCQkJCWcuc2V0Q29sb3IoQ29sb3IuUkVEKTsKCQkJCWcuZmlsbE92YWwocGxheWVyLnggKiBzaXplLCBwbGF5ZXIueSAqIHNpemUsIHNpemUsIHNpemUpOwoJCQkJZm9yIChmaW5hbCBFbmVteSBlbmVteSA6IGVuZW1pZXMpIHsKCQkJCQlzd2l0Y2ggKGVuZW15LmtpbmQpIHsKCQkJCQljYXNlIFY6CgkJCQkJCWcuc2V0Q29sb3IoQ29sb3IuR1JFRU4pOwoJCQkJCQlicmVhazsKCQkJCQljYXNlIEg6CgkJCQkJCWcuc2V0Q29sb3IoQ29sb3IuQkxVRSk7CgkJCQkJCWJyZWFrOwoJCQkJCWNhc2UgTDoKCQkJCQkJZy5zZXRDb2xvcihDb2xvci5PUkFOR0UpOwoJCQkJCQlicmVhazsKCQkJCQljYXNlIFI6CgkJCQkJCWcuc2V0Q29sb3IoQ29sb3IuUElOSyk7CgkJCQkJCWJyZWFrOwoJCQkJCWNhc2UgSkw6CgkJCQkJY2FzZSBKUjoKCQkJCQkJZy5zZXRDb2xvcihDb2xvci5DWUFOKTsKCQkJCQkJYnJlYWs7CgkJCQkJZGVmYXVsdDoKCQkJCQkJZy5zZXRDb2xvcihDb2xvci5CTEFDSyk7CgkJCQkJfQoJCQkJCWcuZmlsbE92YWwoZW5lbXkueCAqIHNpemUsIGVuZW15LnkgKiBzaXplLCBzaXplLCBzaXplKTsKCQkJCX0KCQkJCWcuc2V0Q29sb3IoQ29sb3IuQkxBQ0spOwoJCQkJZm9yIChmaW5hbCBFbmVteSBlbmVteSA6IGVuZW1pZXMpIHsKCQkJCQlnLmRyYXdTdHJpbmcoZW5lbXkua2luZC50b1N0cmluZygpLCBlbmVteS54ICogc2l6ZSArIHNpemUgLyAyCgkJCQkJCQktIGcuZ2V0Rm9udE1ldHJpY3MoKS5zdHJpbmdXaWR0aChlbmVteS5raW5kLnRvU3RyaW5nKCkpIC8gMiwgZW5lbXkueQoJCQkJCQkJKiBzaXplICsgZy5nZXRGb250TWV0cmljcygpLmdldEFzY2VudCgpKTsKCQkJCX0KCQkJCWcuZHJhd1N0cmluZygiUCIsIHBsYXllci54ICogc2l6ZSArIHNpemUgLyAyIC0gZy5nZXRGb250TWV0cmljcygpLnN0cmluZ1dpZHRoKCJQIikKCQkJCQkJLyAyLCBwbGF5ZXIueSAqIHNpemUgKyBnLmdldEZvbnRNZXRyaWNzKCkuZ2V0QXNjZW50KCkpOwoJCQkJZm9yIChpbnQgaSA9IDA7IGkgPCBoZWlnaHQ7IGkrKykgewoJCQkJCWZvciAoaW50IGogPSAwOyBqIDwgd2lkdGg7IGorKykgewoJCQkJCQlzd2l0Y2ggKGRhdGFbaV1bal0pIHsKCQkJCQkJY2FzZSAnIyc6CgkJCQkJCQlnLnNldENvbG9yKENvbG9yLkdSQVkpOwoJCQkJCQkJZy5maWxsUmVjdChqICogc2l6ZSwgaSAqIHNpemUsIHNpemUsIHNpemUpOwoJCQkJCQkJYnJlYWs7CgkJCQkJCWNhc2UgJy4nOgoJCQkJCQkJZy5zZXRDb2xvcihDb2xvci5CTEFDSyk7CgkJCQkJCQlnLmZpbGxPdmFsKGogKiBzaXplICsgc2l6ZSAvIDIgLSAyLCBpICogc2l6ZSArIHNpemUgLyAyIC0gMiwgNCwgNCk7CgkJCQkJCQlicmVhazsKCQkJCQkJfQoJCQkJCX0KCQkJCX0KCQkJfQoJCX07CgkJcGxheVBhbmVsLnNldEZvY3VzYWJsZSh0cnVlKTsKCQlwbGF5UGFuZWwuYWRkS2V5TGlzdGVuZXIobmV3IEtleUFkYXB0ZXIoKSB7CgkJCUBPdmVycmlkZQoJCQlwdWJsaWMgdm9pZCBrZXlQcmVzc2VkKGZpbmFsIEtleUV2ZW50IGUpIHsKCQkJCXN3aXRjaCAoZS5nZXRLZXlDb2RlKCkpIHsKCQkJCWNhc2UgS2V5RXZlbnQuVktfRVNDQVBFOgoJCQkJCWluaXRpYWxpemUod2lkdGgsIGhlaWdodCwgZGF0YSwgc3RhZ2UsIHBsYXllciwgZW5lbWllcywgdGltZSwgZG90cywgbG9nLAoJCQkJCQkJaXNEZWFkLCBzdGF0dXNMYWJlbCwgdGltZUxpbWl0KTsKCQkJCQlwbGF5UGFuZWwucmVwYWludCgpOwoJCQkJCWJyZWFrOwoJCQkJY2FzZSBLZXlFdmVudC5WS19aOgoJCQkJCXBsYXlCYWNrKGxvZy50b1N0cmluZygpLnJlcGxhY2VGaXJzdCgiLiQiLCAiIiksIHN0YWdlLCB0aW1lTGltaXQsIHdpZHRoLAoJCQkJCQkJaGVpZ2h0LCBkYXRhLCBwbGF5ZXIsIGVuZW1pZXMsIHRpbWUsIGRvdHMsIGxvZywgaXNEZWFkLCBzdGF0dXNMYWJlbCwKCQkJCQkJCXBsYXlQYW5lbCk7CgkJCQkJcGxheVBhbmVsLnJlcGFpbnQoKTsKCQkJCQl1cGRhdGVTdGF0dXNMYWJlbChzdGF0dXNMYWJlbCwgc3RhZ2UsIHRpbWVMaW1pdCwgdGltZSwgZG90cywgbG9nKTsKCQkJCQlicmVhazsKCQkJCWNhc2UgS2V5RXZlbnQuVktfMToKCQkJCQlicmVhazsKCQkJCWNhc2UgS2V5RXZlbnQuVktfMjoKCQkJCQlicmVhazsKCQkJCWNhc2UgS2V5RXZlbnQuVktfMzoKCQkJCX0KCQkJCWlmICghaXNEZWFkLmdldCgpKSB7CgkJCQkJc3dpdGNoIChlLmdldEtleUNvZGUoKSkgewoJCQkJCWNhc2UgS2V5RXZlbnQuVktfRE9XTjoKCQkJCQljYXNlIEtleUV2ZW50LlZLX0o6CgkJCQkJCXBsYXllci5tb3ZlRG93bihkYXRhLCBsb2cpOwoJCQkJCQlicmVhazsKCQkJCQljYXNlIEtleUV2ZW50LlZLX0xFRlQ6CgkJCQkJY2FzZSBLZXlFdmVudC5WS19IOgoJCQkJCQlwbGF5ZXIubW92ZUxlZnQoZGF0YSwgbG9nKTsKCQkJCQkJYnJlYWs7CgkJCQkJY2FzZSBLZXlFdmVudC5WS19VUDoKCQkJCQljYXNlIEtleUV2ZW50LlZLX0s6CgkJCQkJCXBsYXllci5tb3ZlVXAoZGF0YSwgbG9nKTsKCQkJCQkJYnJlYWs7CgkJCQkJY2FzZSBLZXlFdmVudC5WS19SSUdIVDoKCQkJCQljYXNlIEtleUV2ZW50LlZLX0w6CgkJCQkJCXBsYXllci5tb3ZlUmlnaHQoZGF0YSwgbG9nKTsKCQkJCQkJYnJlYWs7CgkJCQkJY2FzZSBLZXlFdmVudC5WS19TUEFDRToKCQkJCQljYXNlIEtleUV2ZW50LlZLX0VOVEVSOgoJCQkJCWNhc2UgS2V5RXZlbnQuVktfUEVSSU9EOgoJCQkJCQlwbGF5ZXIuc3RheShsb2cpOwoJCQkJCQlicmVhazsKCQkJCQlkZWZhdWx0OgoJCQkJCQlyZXR1cm47CgkJCQkJfQoJCQkJCW1vdmVFbmVtaWVzKGRhdGEsIHBsYXllciwgZW5lbWllcywgdGltZSwgaXNEZWFkKTsKCQkJCQlnZXREb3QoZGF0YSwgcGxheWVyLCBkb3RzKTsKCQkJCQl0aW1lLmluY3JlbWVudEFuZEdldCgpOwoJCQkJCWlmICh0aW1lLmdldCgpID49IHRpbWVMaW1pdCkgewoJCQkJCQlpc0RlYWQuc2V0KHRydWUpOwoJCQkJCX0KCQkJCQlpZiAoZG90cy5nZXQoKSA9PSBzdGFnZS5kb3RzKSB7CgkJCQkJCVN5c3RlbS5vdXQucHJpbnRmKCIoY2xlYXJlZCl0aW1lOiAlZC8lZCwgc2NvcmU6ICVkLyVkLCBsb2c6ICVzXG4iLAoJCQkJCQkJCXRpbWUuZ2V0KCksIHRpbWVMaW1pdCwgZG90cy5nZXQoKSwgc3RhZ2UuZG90cywgbG9nKTsKCQkJCQkJaXNEZWFkLnNldCh0cnVlKTsKCQkJCQl9CgkJCQkJcGxheVBhbmVsLnJlcGFpbnQoKTsKCQkJCQl1cGRhdGVTdGF0dXNMYWJlbChzdGF0dXNMYWJlbCwgc3RhZ2UsIHRpbWVMaW1pdCwgdGltZSwgZG90cywgbG9nKTsKCQkJCQlpZiAoaXNEZWFkLmdldCgpKSB7CgkJCQkJCXN0YXR1c0xhYmVsLnNldFRleHQoKGRvdHMuZ2V0KCkgPT0gc3RhZ2UuZG90cyA/ICIoY2xlYXJlZCkiIDogIihkZWFkKSIpCgkJCQkJCQkJKyBzdGF0dXNMYWJlbC5nZXRUZXh0KCkpOwoJCQkJCQlUb29sa2l0LmdldERlZmF1bHRUb29sa2l0KCkuZ2V0U3lzdGVtQ2xpcGJvYXJkKCkKCQkJCQkJCQkuc2V0Q29udGVudHMobmV3IFN0cmluZ1NlbGVjdGlvbihsb2cudG9TdHJpbmcoKSksIG51bGwpOwoJCQkJCX0KCQkJCX0KCQkJfQoJCX0pOwoJCXBsYXlQYW5lbC5zZXRQcmVmZXJyZWRTaXplKG5ldyBEaW1lbnNpb24od2lkdGggKiBzaXplLCBoZWlnaHQgKiBzaXplKSk7CgkJcGxheVBhbmVsLnNldEJhY2tncm91bmQoQ29sb3IuV0hJVEUpOwoJCWZyYW1lLmFkZChwbGF5UGFuZWwpOwoJCWZyYW1lLnBhY2soKTsKCQlmcmFtZS5zZXRMb2NhdGlvbkJ5UGxhdGZvcm0odHJ1ZSk7CgkJZnJhbWUuc2V0RGVmYXVsdENsb3NlT3BlcmF0aW9uKFdpbmRvd0NvbnN0YW50cy5FWElUX09OX0NMT1NFKTsKCQlpbml0aWFsaXplKHdpZHRoLCBoZWlnaHQsIGRhdGEsIHN0YWdlLCBwbGF5ZXIsIGVuZW1pZXMsIHRpbWUsIGRvdHMsIGxvZywgaXNEZWFkLAoJCQkJc3RhdHVzTGFiZWwsIHRpbWVMaW1pdCk7CgkJZmluYWwgYm9vbGVhbiBpc0dVSSA9IGZhbHNlOwoJCWZyYW1lLnNldFZpc2libGUoaXNHVUkpOwoJCWlmIChpc0dVSSkgewoJCQlUaHJlYWQuc2xlZXAoMTAwMCk7CgkJfQoJCWZpbmFsIFJhbmRvbSByYW5kb20gPSBuZXcgUmFuZG9tKCk7CgkJaW50IG1heERvdHMgPSAwOwoJCWludCBtYXhUaW1lID0gMDsKCQlkb3VibGUgcmVzZXRSYXRlID0gLjAwMTsKCQlmaW5hbCBkb3VibGUgc3RheVJhdGUgPSAuMjsKCQlmaW5hbCBkb3VibGUgZm9yd2FyZFJhdGUgPSAuOTsKCQlmaW5hbCBkb3VibGUgaHVuZ3J5UmF0ZSA9IC44OwoJCVN0cmluZyByb2xsQmFja1BvaW50ID0gIiI7CgkJd2hpbGUgKHRydWUpIHsKCQkJaWYgKHJhbmRvbS5uZXh0RG91YmxlKCkgPCBzdGF5UmF0ZSkgewoJCQkJcGxheWVyLnN0YXkobG9nKTsKCQkJfSBlbHNlIHsKCQkJCWZpbmFsIGNoYXIgZG93biA9IGRhdGFbcGxheWVyLnkgKyAxXVtwbGF5ZXIueF07CgkJCQlmaW5hbCBjaGFyIGxlZnQgPSBkYXRhW3BsYXllci55XVtwbGF5ZXIueCAtIDFdOwoJCQkJZmluYWwgY2hhciB1cCA9IGRhdGFbcGxheWVyLnkgLSAxXVtwbGF5ZXIueF07CgkJCQlmaW5hbCBjaGFyIHJpZ2h0ID0gZGF0YVtwbGF5ZXIueV1bcGxheWVyLnggKyAxXTsKCQkJCWZpbmFsIGludCBudW1PZldhbGxzID0gKGRvd24gPT0gJyMnID8gMSA6IDApICsgKGxlZnQgPT0gJyMnID8gMSA6IDApCgkJCQkJCSsgKHVwID09ICcjJyA/IDEgOiAwKSArIChyaWdodCA9PSAnIycgPyAxIDogMCk7CgkJCQlpZiAobnVtT2ZXYWxscyA8IDIpIHsKCQkJCQlyb2xsQmFja1BvaW50ID0gbG9nLnRvU3RyaW5nKCk7CgkJCQl9CgkJCQlmaW5hbCBpbnQgbnVtT2ZEb3RzID0gKGRvd24gPT0gJy4nID8gMSA6IDApICsgKGxlZnQgPT0gJy4nID8gMSA6IDApCgkJCQkJCSsgKHVwID09ICcuJyA/IDEgOiAwKSArIChyaWdodCA9PSAnLicgPyAxIDogMCk7CgkJCQlpZiAobnVtT2ZEb3RzID4gMCAmJiByYW5kb20ubmV4dERvdWJsZSgpIDwgaHVuZ3J5UmF0ZSkgewoJCQkJCWxvb3A6IHdoaWxlICh0cnVlKSB7CgkJCQkJCXN3aXRjaCAocmFuZG9tLm5leHRJbnQoNCkpIHsKCQkJCQkJY2FzZSAwOgoJCQkJCQkJaWYgKGRvd24gPT0gJy4nKSB7CgkJCQkJCQkJcGxheWVyLm1vdmVEb3duKGRhdGEsIGxvZyk7CgkJCQkJCQkJYnJlYWsgbG9vcDsKCQkJCQkJCX0KCQkJCQkJCWJyZWFrOwoJCQkJCQljYXNlIDE6CgkJCQkJCQlpZiAobGVmdCA9PSAnLicpIHsKCQkJCQkJCQlwbGF5ZXIubW92ZUxlZnQoZGF0YSwgbG9nKTsKCQkJCQkJCQlicmVhayBsb29wOwoJCQkJCQkJfQoJCQkJCQkJYnJlYWs7CgkJCQkJCWNhc2UgMjoKCQkJCQkJCWlmICh1cCA9PSAnLicpIHsKCQkJCQkJCQlwbGF5ZXIubW92ZVVwKGRhdGEsIGxvZyk7CgkJCQkJCQkJYnJlYWsgbG9vcDsKCQkJCQkJCX0KCQkJCQkJCWJyZWFrOwoJCQkJCQljYXNlIDM6CgkJCQkJCQlpZiAocmlnaHQgPT0gJy4nKSB7CgkJCQkJCQkJcGxheWVyLm1vdmVSaWdodChkYXRhLCBsb2cpOwoJCQkJCQkJCWJyZWFrIGxvb3A7CgkJCQkJCQl9CgkJCQkJCQlicmVhazsKCQkJCQkJfQoJCQkJCX0KCQkJCX0gZWxzZSB7CgkJCQkJbG9vcDogd2hpbGUgKHRydWUpIHsKCQkJCQkJZmluYWwgYm9vbGVhbiBpc0ZvcndhcmQgPSByYW5kb20ubmV4dERvdWJsZSgpIDwgZm9yd2FyZFJhdGU7CgkJCQkJCXN3aXRjaCAocmFuZG9tLm5leHRJbnQoNCkpIHsKCQkJCQkJY2FzZSAwOgoJCQkJCQkJaWYgKGRvd24gIT0gJyMnKSB7CgkJCQkJCQkJaWYgKGlzRm9yd2FyZCAmJiBwbGF5ZXIubGFzdFkgPT0gcGxheWVyLnkgKyAxKSB7CgkJCQkJCQkJCWJyZWFrOwoJCQkJCQkJCX0KCQkJCQkJCQlwbGF5ZXIubW92ZURvd24oZGF0YSwgbG9nKTsKCQkJCQkJCQlicmVhayBsb29wOwoJCQkJCQkJfQoJCQkJCQkJYnJlYWs7CgkJCQkJCWNhc2UgMToKCQkJCQkJCWlmIChsZWZ0ICE9ICcjJykgewoJCQkJCQkJCWlmIChpc0ZvcndhcmQgJiYgcGxheWVyLmxhc3RYID09IHBsYXllci54IC0gMSkgewoJCQkJCQkJCQlicmVhazsKCQkJCQkJCQl9CgkJCQkJCQkJcGxheWVyLm1vdmVMZWZ0KGRhdGEsIGxvZyk7CgkJCQkJCQkJYnJlYWsgbG9vcDsKCQkJCQkJCX0KCQkJCQkJCWJyZWFrOwoJCQkJCQljYXNlIDI6CgkJCQkJCQlpZiAodXAgIT0gJyMnKSB7CgkJCQkJCQkJaWYgKGlzRm9yd2FyZCAmJiBwbGF5ZXIubGFzdFkgPT0gcGxheWVyLnkgLSAxKSB7CgkJCQkJCQkJCWJyZWFrOwoJCQkJCQkJCX0KCQkJCQkJCQlwbGF5ZXIubW92ZVVwKGRhdGEsIGxvZyk7CgkJCQkJCQkJYnJlYWsgbG9vcDsKCQkJCQkJCX0KCQkJCQkJCWJyZWFrOwoJCQkJCQljYXNlIDM6CgkJCQkJCQlpZiAocmlnaHQgIT0gJyMnKSB7CgkJCQkJCQkJaWYgKGlzRm9yd2FyZCAmJiBwbGF5ZXIubGFzdFggPT0gcGxheWVyLnggKyAxKSB7CgkJCQkJCQkJCWJyZWFrOwoJCQkJCQkJCX0KCQkJCQkJCQlwbGF5ZXIubW92ZVJpZ2h0KGRhdGEsIGxvZyk7CgkJCQkJCQkJYnJlYWsgbG9vcDsKCQkJCQkJCX0KCQkJCQkJCWJyZWFrOwoJCQkJCQl9CgkJCQkJfQoJCQkJfQoJCQl9CgkJCW1vdmVFbmVtaWVzKGRhdGEsIHBsYXllciwgZW5lbWllcywgdGltZSwgaXNEZWFkKTsKCQkJZ2V0RG90KGRhdGEsIHBsYXllciwgZG90cyk7CgkJCXRpbWUuaW5jcmVtZW50QW5kR2V0KCk7CgkJCWlmICh0aW1lLmdldCgpID49IHRpbWVMaW1pdCkgewoJCQkJaXNEZWFkLnNldCh0cnVlKTsKCQkJfQoJCQlpZiAoZG90cy5nZXQoKSA9PSBzdGFnZS5kb3RzKSB7CgkJCQlpc0RlYWQuc2V0KHRydWUpOwoJCQl9CgkJCWlmIChpc0RlYWQuZ2V0KCkpIHsKCQkJCWlmIChkb3RzLmdldCgpID4gbWF4RG90cykgewoJCQkJCVN5c3RlbS5vdXQucHJpbnRmKCJ0aW1lOiAlZC8lZCwgc2NvcmU6ICVkLyVkLCBsb2c6ICVzXG4iLCB0aW1lLmdldCgpLAoJCQkJCQkJdGltZUxpbWl0LCBkb3RzLmdldCgpLCBzdGFnZS5kb3RzLCBsb2cpOwoJCQkJCW1heERvdHMgPSBkb3RzLmdldCgpOwoJCQkJfQoJCQkJaWYgKGRvdHMuZ2V0KCkgPT0gc3RhZ2UuZG90cyAmJiB0aW1lTGltaXQgLSB0aW1lLmdldCgpID4gbWF4VGltZSkgewoJCQkJCVN5c3RlbS5vdXQucHJpbnRmKCJjbGVhciEgdGltZTogJWQsIHNjb3JlOiAlZCwgbG9nOiAlc1xuIiwKCQkJCQkJCXRpbWVMaW1pdCAtIHRpbWUuZ2V0KCksIGRvdHMuZ2V0KCksIGxvZyk7CgkJCQkJbWF4VGltZSA9IHRpbWVMaW1pdCAtIHRpbWUuZ2V0KCk7CgkJCQl9CgkJCQlpZiAocmFuZG9tLm5leHREb3VibGUoKSA8IHJlc2V0UmF0ZSkgewoJCQkJCWluaXRpYWxpemUod2lkdGgsIGhlaWdodCwgZGF0YSwgc3RhZ2UsIHBsYXllciwgZW5lbWllcywgdGltZSwgZG90cywgbG9nLAoJCQkJCQkJaXNEZWFkLCBzdGF0dXNMYWJlbCwgdGltZUxpbWl0KTsKCQkJCQlyZXNldFJhdGUgKj0gLjk7CgkJCQl9IGVsc2UgewoJCQkJCWlmIChyYW5kb20ubmV4dEJvb2xlYW4oKSkgewoJCQkJCQlwbGF5QmFjayhsb2cuc3ViU2VxdWVuY2UoMCwgbG9nLmxlbmd0aCgpICogOTkgLyAxMDApLnRvU3RyaW5nKCksIHN0YWdlLAoJCQkJCQkJCXRpbWVMaW1pdCwgd2lkdGgsIGhlaWdodCwgZGF0YSwgcGxheWVyLCBlbmVtaWVzLCB0aW1lLCBkb3RzLCBsb2csCgkJCQkJCQkJaXNEZWFkLCBzdGF0dXNMYWJlbCwgcGxheVBhbmVsKTsKCQkJCQl9IGVsc2UgewoJCQkJCQlwbGF5QmFjayhyb2xsQmFja1BvaW50LCBzdGFnZSwgdGltZUxpbWl0LCB3aWR0aCwgaGVpZ2h0LCBkYXRhLCBwbGF5ZXIsCgkJCQkJCQkJZW5lbWllcywgdGltZSwgZG90cywgbG9nLCBpc0RlYWQsIHN0YXR1c0xhYmVsLCBwbGF5UGFuZWwpOwoJCQkJCX0KCQkJCX0KCgkJCX0KCQkJaWYgKGlzR1VJKSB7CgkJCQlwbGF5UGFuZWwucmVwYWludCgpOwoJCQkJc3RhdHVzTGFiZWwuc2V0VGV4dChuZXcgRm9ybWF0dGVyKCkuZm9ybWF0KCJ0aW1lOiAlZCwgc2NvcmU6ICVkLCBsb2c6ICVzIiwKCQkJCQkJdGltZUxpbWl0IC0gdGltZS5nZXQoKSwgZG90cy5nZXQoKSwgbG9nKS50b1N0cmluZygpKTsKCQkJCVRocmVhZC5zbGVlcCgxMCk7CgkJCX0KCQl9Cgl9CgoJLyoqCgkgKiDliJ3mnJ/ljJYKCSAqIEBwYXJhbSB3aWR0aCDluYUKCSAqIEBwYXJhbSBoZWlnaHQg6auY44GVCgkgKiBAcGFyYW0gZGF0YSDjg4fjg7zjgr8KCSAqIEBwYXJhbSBzdGFnZSDjgrnjg4bjg7zjgrgKCSAqIEBwYXJhbSBwbGF5ZXIg44OX44Os44Kk44OkCgkgKiBAcGFyYW0gZW5lbWllcyDmlbUKCSAqIEBwYXJhbSB0aW1lIOaZguWIuwoJICogQHBhcmFtIGRvdHMg54K5CgkgKiBAcGFyYW0gbG9nIOODreOCsAoJICogQHBhcmFtIGlzRGVhZCDmrbvjgpPjgafjgYTjgovjgYvjganjgYbjgYsKCSAqIEBwYXJhbSBzdGF0dXNMYWJlbCDjg6njg5njg6sKCSAqIEBwYXJhbSB0aW1lTGltaXQg5Yi26ZmQ5pmC6ZaTCgkgKi8KCXN0YXRpYyB2b2lkIGluaXRpYWxpemUoZmluYWwgaW50IHdpZHRoLCBmaW5hbCBpbnQgaGVpZ2h0LCBmaW5hbCBjaGFyW11bXSBkYXRhLAoJCQlmaW5hbCBTdGFnZSBzdGFnZSwgZmluYWwgUGxheWVyIHBsYXllciwgZmluYWwgTGlzdDxFbmVteT4gZW5lbWllcywKCQkJZmluYWwgQXRvbWljSW50ZWdlciB0aW1lLCBmaW5hbCBBdG9taWNJbnRlZ2VyIGRvdHMsIGZpbmFsIFN0cmluZ0J1aWxkZXIgbG9nLAoJCQlmaW5hbCBBdG9taWNCb29sZWFuIGlzRGVhZCwgZmluYWwgSkxhYmVsIHN0YXR1c0xhYmVsLCBmaW5hbCBpbnQgdGltZUxpbWl0KSB7CgkJZW5lbWllcy5jbGVhcigpOwoJCWZvciAoaW50IGkgPSAwOyBpIDwgaGVpZ2h0OyBpKyspIHsKCQkJZm9yIChpbnQgaiA9IDA7IGogPCB3aWR0aDsgaisrKSB7CgkJCQlmaW5hbCBjaGFyIGtpbmQgPSBzdGFnZS5kYXRhW2ldW2pdOwoJCQkJc3dpdGNoIChraW5kKSB7CgkJCQljYXNlICdWJzoKCQkJCQlkYXRhW2ldW2pdID0gJyAnOwoJCQkJCWVuZW1pZXMuYWRkKG5ldyBFbmVteShqLCBpLCBFbmVteS5LaW5kLlYpKTsKCQkJCQlicmVhazsKCQkJCWNhc2UgJ0gnOgoJCQkJCWRhdGFbaV1bal0gPSAnICc7CgkJCQkJZW5lbWllcy5hZGQobmV3IEVuZW15KGosIGksIEVuZW15LktpbmQuSCkpOwoJCQkJCWJyZWFrOwoJCQkJY2FzZSAnTCc6CgkJCQkJZGF0YVtpXVtqXSA9ICcgJzsKCQkJCQllbmVtaWVzLmFkZChuZXcgRW5lbXkoaiwgaSwgRW5lbXkuS2luZC5MKSk7CgkJCQkJYnJlYWs7CgkJCQljYXNlICdSJzoKCQkJCQlkYXRhW2ldW2pdID0gJyAnOwoJCQkJCWVuZW1pZXMuYWRkKG5ldyBFbmVteShqLCBpLCBFbmVteS5LaW5kLlIpKTsKCQkJCQlicmVhazsKCQkJCWNhc2UgJ0onOgoJCQkJCWRhdGFbaV1bal0gPSAnICc7CgkJCQkJZW5lbWllcy5hZGQobmV3IEVuZW15KGosIGksIEVuZW15LktpbmQuSkwpKTsKCQkJCQlicmVhazsKCQkJCWNhc2UgJ0AnOgoJCQkJCWRhdGFbaV1bal0gPSAnICc7CgkJCQkJcGxheWVyLnggPSBqOwoJCQkJCXBsYXllci55ID0gaTsKCQkJCQlwbGF5ZXIubGFzdFggPSBwbGF5ZXIueDsKCQkJCQlwbGF5ZXIubGFzdFkgPSBwbGF5ZXIueTsKCQkJCQlicmVhazsKCQkJCWNhc2UgJyMnOgoJCQkJY2FzZSAnLic6CgkJCQljYXNlICcgJzoKCQkJCQlkYXRhW2ldW2pdID0gc3RhZ2UuZGF0YVtpXVtqXTsKCQkJCQlicmVhazsKCQkJCWRlZmF1bHQ6CgkJCQkJU3lzdGVtLmVyci5wcmludGYoInVua25vd24gZGF0YTogJyVzJywgeCA9ICVkLCB5ID0gJWRcbiIsIHN0YWdlLmRhdGFbaV1bal0sIGosCgkJCQkJCQlpKTsKCQkJCX0KCQkJfQoJCX0KCQl0aW1lLnNldCgwKTsKCQlkb3RzLnNldCgwKTsKCQlsb2cuc2V0TGVuZ3RoKDApOwoJCWlzRGVhZC5zZXQoZmFsc2UpOwoJCXVwZGF0ZVN0YXR1c0xhYmVsKHN0YXR1c0xhYmVsLCBzdGFnZSwgdGltZUxpbWl0LCB0aW1lLCBkb3RzLCBsb2cpOwoJfQoKCS8qKgoJICog44Oq44OX44Os44KkCgkgKiBAcGFyYW0gc3RyaW5nIOWLleS9nOOCkuaMh+ekuuOBmeOCi+aWh+Wtl+WIlwoJICogQHBhcmFtIHN0YWdlIOOCueODhuODvOOCuAoJICogQHBhcmFtIHRpbWVMaW1pdCDliLbpmZDmmYLplpMKCSAqIEBwYXJhbSB3aWR0aCDluYUKCSAqIEBwYXJhbSBoZWlnaHQg6auY44GVCgkgKiBAcGFyYW0gZGF0YSDjg4fjg7zjgr8KCSAqIEBwYXJhbSBwbGF5ZXIg44OX44Os44Kk44OkCgkgKiBAcGFyYW0gZW5lbWllcyDmlbUKCSAqIEBwYXJhbSB0aW1lIOaZguWIuwoJICogQHBhcmFtIGRvdHMg54K5CgkgKiBAcGFyYW0gbG9nIOODreOCsAoJICogQHBhcmFtIGlzRGVhZCDmrbvjgpPjgafjgYTjgovjgYvjganjgYbjgYsKCSAqIEBwYXJhbSBzdGF0dXNMYWJlbCDjg6njg5njg6sKCSAqIEBwYXJhbSBwbGF5UGFuZWwg44OR44ON44OrCgkgKi8KCXN0YXRpYyB2b2lkIHBsYXlCYWNrKGZpbmFsIFN0cmluZyBzdHJpbmcsIGZpbmFsIFN0YWdlIHN0YWdlLCBmaW5hbCBpbnQgdGltZUxpbWl0LAoJCQlmaW5hbCBpbnQgd2lkdGgsIGZpbmFsIGludCBoZWlnaHQsIGZpbmFsIGNoYXJbXVtdIGRhdGEsIGZpbmFsIFBsYXllciBwbGF5ZXIsCgkJCWZpbmFsIExpc3Q8RW5lbXk+IGVuZW1pZXMsIGZpbmFsIEF0b21pY0ludGVnZXIgdGltZSwgZmluYWwgQXRvbWljSW50ZWdlciBkb3RzLAoJCQlmaW5hbCBTdHJpbmdCdWlsZGVyIGxvZywgZmluYWwgQXRvbWljQm9vbGVhbiBpc0RlYWQsIGZpbmFsIEpMYWJlbCBzdGF0dXNMYWJlbCwKCQkJZmluYWwgSlBhbmVsIHBsYXlQYW5lbCkgewoJCWluaXRpYWxpemUod2lkdGgsIGhlaWdodCwgZGF0YSwgc3RhZ2UsIHBsYXllciwgZW5lbWllcywgdGltZSwgZG90cywgbG9nLCBpc0RlYWQsCgkJCQlzdGF0dXNMYWJlbCwgdGltZUxpbWl0KTsKCQlmb3IgKGludCBpID0gMDsgaSA8IHN0cmluZy5sZW5ndGgoKTsgaSsrKSB7CgkJCWlmICghaXNEZWFkLmdldCgpKSB7CgkJCQlzd2l0Y2ggKHN0cmluZy5jaGFyQXQoaSkpIHsKCQkJCWNhc2UgJ2onOgoJCQkJCXBsYXllci5tb3ZlRG93bihkYXRhLCBsb2cpOwoJCQkJCWJyZWFrOwoJCQkJY2FzZSAnaCc6CgkJCQkJcGxheWVyLm1vdmVMZWZ0KGRhdGEsIGxvZyk7CgkJCQkJYnJlYWs7CgkJCQljYXNlICdrJzoKCQkJCQlwbGF5ZXIubW92ZVVwKGRhdGEsIGxvZyk7CgkJCQkJYnJlYWs7CgkJCQljYXNlICdsJzoKCQkJCQlwbGF5ZXIubW92ZVJpZ2h0KGRhdGEsIGxvZyk7CgkJCQkJYnJlYWs7CgkJCQljYXNlICcuJzoKCQkJCQlwbGF5ZXIuc3RheShsb2cpOwoJCQkJCWJyZWFrOwoJCQkJZGVmYXVsdDoKCQkJCQlTeXN0ZW0uZXJyLnByaW50ZigidW5rbm93biBkYXRhOiAlc1xuIiwgc3RyaW5nLmNoYXJBdChpKSk7CgkJCQl9CgkJCQltb3ZlRW5lbWllcyhkYXRhLCBwbGF5ZXIsIGVuZW1pZXMsIHRpbWUsIGlzRGVhZCk7CgkJCQlnZXREb3QoZGF0YSwgcGxheWVyLCBkb3RzKTsKCQkJCXRpbWUuaW5jcmVtZW50QW5kR2V0KCk7CgkJCQlpZiAodGltZS5nZXQoKSA+PSB0aW1lTGltaXQpIHsKCQkJCQlpc0RlYWQuc2V0KHRydWUpOwoJCQkJfQoJCQl9CgkJfQoJfQoKCS8qKgoJICog54K544KS6aOf44G544KLCgkgKiBAcGFyYW0gZGF0YSDjg4fjg7zjgr8KCSAqIEBwYXJhbSBwbGF5ZXIg44OX44Os44Kk44OkCgkgKiBAcGFyYW0gZG90cyDngrkKCSAqLwoJc3RhdGljIHZvaWQgZ2V0RG90KGZpbmFsIGNoYXJbXVtdIGRhdGEsIGZpbmFsIFBsYXllciBwbGF5ZXIsIGZpbmFsIEF0b21pY0ludGVnZXIgZG90cykgewoJCWlmIChkYXRhW3BsYXllci55XVtwbGF5ZXIueF0gPT0gJy4nKSB7CgkJCWRhdGFbcGxheWVyLnldW3BsYXllci54XSA9ICcgJzsKCQkJZG90cy5pbmNyZW1lbnRBbmRHZXQoKTsKCQl9Cgl9CgoJLyoqCgkgKiDmlbXjgpLnp7vli5UKCSAqIEBwYXJhbSBkYXRhIOODh+ODvOOCvwoJICogQHBhcmFtIHBsYXllciDjg5fjg6zjgqTjg6QKCSAqIEBwYXJhbSBlbmVtaWVzIOaVtQoJICogQHBhcmFtIHRpbWUg5pmC5Yi7CgkgKiBAcGFyYW0gaXNEZWFkIOatu+OCk+OBp+OBhOOCi+OBi+OBqeOBhuOBiwoJICovCglzdGF0aWMgdm9pZCBtb3ZlRW5lbWllcyhmaW5hbCBjaGFyW11bXSBkYXRhLCBmaW5hbCBQbGF5ZXIgcGxheWVyLCBmaW5hbCBMaXN0PEVuZW15PiBlbmVtaWVzLAoJCQlmaW5hbCBBdG9taWNJbnRlZ2VyIHRpbWUsIGZpbmFsIEF0b21pY0Jvb2xlYW4gaXNEZWFkKSB7CgkJZm9yIChmaW5hbCBFbmVteSBlbmVteSA6IGVuZW1pZXMpIHsKCQkJZmluYWwgaW50IHggPSBlbmVteS54OwoJCQlmaW5hbCBpbnQgeSA9IGVuZW15Lnk7CgkJCWVuZW15Lm1vdmUoZGF0YSwgcGxheWVyLCB0aW1lKTsKCQkJZW5lbXkubGFzdFggPSB4OwoJCQllbmVteS5sYXN0WSA9IHk7CgkJCWlmIChlbmVteS5raWxsZWQocGxheWVyKSkgewoJCQkJaXNEZWFkLnNldCh0cnVlKTsKCQkJfQoJCX0KCX0KCgkvKioKCSAqIOePvuWcqOOBrueKtuaFi+OCkuihqOekuuOBmeOCi+ODqeODmeODq+OCkuabtOaWsAoJICogQHBhcmFtIHN0YXR1c0xhYmVsIOODqeODmeODqwoJICogQHBhcmFtIHN0YWdlIOOCueODhuODvOOCuAoJICogQHBhcmFtIHRpbWVMaW1pdCDliLbpmZDmmYLplpMKCSAqIEBwYXJhbSB0aW1lIOaZguWIuwoJICogQHBhcmFtIGRvdHMg54K5CgkgKiBAcGFyYW0gbG9nIOODreOCsAoJICovCglzdGF0aWMgdm9pZCB1cGRhdGVTdGF0dXNMYWJlbChmaW5hbCBKTGFiZWwgc3RhdHVzTGFiZWwsIGZpbmFsIFN0YWdlIHN0YWdlLCBmaW5hbCBpbnQgdGltZUxpbWl0LAoJCQlmaW5hbCBBdG9taWNJbnRlZ2VyIHRpbWUsIGZpbmFsIEF0b21pY0ludGVnZXIgZG90cywgZmluYWwgU3RyaW5nQnVpbGRlciBsb2cpIHsKCQlzdGF0dXNMYWJlbC5zZXRUZXh0KG5ldyBGb3JtYXR0ZXIoKS5mb3JtYXQoInRpbWU6ICVkLyVkLCBzY29yZTogJWQvJWQsIGxvZzogJXMiLAoJCQkJdGltZS5nZXQoKSwgdGltZUxpbWl0LCBkb3RzLmdldCgpLCBzdGFnZS5kb3RzLCBsb2cpLnRvU3RyaW5nKCkpOwoJfQp9