//printenv
// JAVA_HOME=/usr/java/jdk1.7.0_67-cloudera
// PATH=/usr/java/jdk1.7.0_67-cloudera/bin
// export HADOOP_CLASSPATH=${JAVA_HOME}/lib/tools.jar
// hadoop com.sun.tools.javac.Main WordCount3.java
// jar cf WordCount2.jar WordCount2*.class
// hadoop jar WordCount2.jar WordCount2 big.txt outWCBig
// hadoop jar WordCount2.jar WordCount2 t8.shakespeare.txt outWCShake
// t8.shakespeare
// Remove the previous results.
// $ hadoop fs -rm -r -f /user/cloudera/wordcount/output
import java.io.* ;
import java.util.StringTokenizer;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.conf.Configuration ;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.jobWordCount;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.lib.chain.ChainMapper;
import org.apache.hadoop.util.GenericOptionsParser;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.SequenceFileInputFormat;
import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.SequenceFileOutputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
import org.apache.hadoop.mapreduce.lib.partition.InputSampler;
import org.apache.hadoop.mapreduce.lib.partition.TotalOrderPartitioner;
public class WordCount2 extends Configured implements Tool
{
private static final Logger LOG = Logger.getLogger(WordCount2.class);
public static void main(String[] args) throws Exception {
int exitCode = ToolRunner.run(new WordCount2(), args);
System.exit(exitCode);
}
public static boolean isNullOrEmpty(String str) {
if(str != null && !str.trim().isEmpty())
return false;
return true;
}
public static class PunctuationMapper
extends Mapper<Object, Text, NullWritable, Text> {
private Text punctd = new Text();
//private static fin//al String PunctuationMarks="\"\'\\[\\]\\\\!$&@~#%:;`<>(){}/!|?*-+=^,.";
private static final String PunctuationMarks="\"\\[\\]\\\\!$&@~#%:;`<>(){}/!|?*-+=^,.";
//DON'T
public void map(Object key, Text value, Context context) throws IOException, InterruptedException {
String word= value.toString();
word=word.replaceAll("-", "");
//word=word.replaceAll("'", "");
word=word.replaceAll("["+PunctuationMarks+"]", " ");
//word = word.replaceAll("[^a-zA-Z\\s+]", " ").toLowerCase();
//word = curr.trim();
//punctd.set(Regex.Replace(word.toString(),[^\w\d\s-]," "));
//String word_punc=word.replaceAll("[^\\w\\d\\s-]", " ");
//word=word.replace( "/\s\s+/g", "" );// -> 390ms
punctd.set(word);
context.write(new IntWritable(1), punctd);
}
}
public static class TrimMapper
extends Mapper<Object, Text, NullWritable, Text> {
private Text trimd = new Text();
public void map(Object key, Text value, Context context) throws IOException, InterruptedException {
String word= value.toString().trim();
//word=word.replaceAll( "/\s\s+/g", "" );// -> 390ms
word=word.replaceAll("^ +| +$|( )+", "$1");
trimd.set(word);
context.write(new IntWritable(1), trimd);
}
}
public static class LowerCaseMapper
extends Mapper<Object, Text, NullWritable, Text> {
private Text lowercased = new Text();
public void map(Object key, Text value, Context context) throws IOException, InterruptedException {
lowercased.set(value.toString().toLowerCase());
context.write(new IntWritable(1), lowercased);
}
}
public static class TokenizerMapper
extends Mapper<IntWritable, Text, Text, IntWritable>{
private static final java.util.regex.Pattern WORD_BOUNDARY = java.util.regex.Pattern.compile("\\s");
private final static IntWritable one = new IntWritable(1);
private Text word = new Text();
public void map(IntWritable key, Text lineText, Context context
) throws IOException, InterruptedException {
String line = lineText.toString();
Text currentWord = new Text();
for (String word : WORD_BOUNDARY.split(line)) {
if (WordCount2.isNullOrEmpty(word)) {
continue;
}
currentWord = new Text(word);
context.write(currentWord,one);
}
}
}
public static class IntSumReducer
extends Reducer<Text,IntWritable,Text,IntWritable> {
private IntWritable result = new IntWritable();
public void reduce(Text key_word, Iterable<IntWritable> counts,
Context context
) throws IOException, InterruptedException {
int sum = 0;
for (IntWritable count : counts) {
sum += count.get();
}
result.set(sum);
context.write(key_word, result);
}
}
//////////////////////////////////////
public static class TopTenMapper extends Mapper<Object, Text, NullWritable, Text>
{
private TreeMap<Integer, Text> topN = new TreeMap<Integer, Text>(); //Collections.reverseOrder()
private final static IntWritable one = new IntWritable(1);
private Text word = new Text();
public void map(Object key, Text value, Context context)
throws IOException, InterruptedException {
// (word, count) tuple
String[] words = value.toString().split("\t") ;
if (words.length < 2) {
return;
}
topN.put(Integer.parseInt(words[1]), new Text(value));
if (topN.size() > 10) {
topN.remove(topN.firstKey());
//topN.remove(topN.lastKey());
}
}
@Override
protected void cleanup(Context context) throws IOException,
InterruptedException {
for (Text t : topN.values()) {
context.write(NullWritable.get(), t);
}
}
}
public static class TopTenReducer extends
Reducer<NullWritable, Text, NullWritable, Text> {
private TreeMap<Integer, Text> topN = new TreeMap<Integer, Text>();
@Override
public void reduce(NullWritable key, Iterable<Text> values,
Context context) throws IOException, InterruptedException {
for (Text value : values) {
String[] words = value.toString().split("\t") ;
topN.put(Integer.parseInt(words[1]), new Text(value));
if (topN.size() > 10) {
topN.remove(topN.firstKey());
}
}
for (Text word : topN.descendingMap().values()) {
context.write(NullWritable.get(), word);
}
}
}
//////////////////////////////////////
public int run(String[] args) throws Exception {
Configuration conf = getConf();
//////////////
FileSystem fs = FileSystem.get(conf);
Path tmpPath = new Path("/w1/tmp");
fs.delete(tmpPath, true);
Path inputPath = new Path(args[0]);
//Path partitionFile = new Path(args[1] + "_partitions.lst");
//Path outputStage = new Path(args[1] + "_staging");
Path outputStage = new Path("/w1/tmp");
Path outputOrder = new Path(args[1]);
//////////////
args = new GenericOptionsParser(conf, args).getRemainingArgs();
// creating a word count jobWordCount
jobWordCount jobWordCount = jobWordCount.getInstance(conf,"wordcount");
//jobWordCount jobWordCount = jobWordCount.getInstance(getConf(), "wordcount");
//jobWordCount.setJarByClass(WordCount2.class);
jobWordCount.setJarByClass(this.getClass());
// Use TextInputFormat, the default unless jobWordCount.setInputFormatClass is used
//public static class PunctuationMapper
//extends Mapper<Object, Text, NullWritable, Text>
Configuration punctuationMapperConf = new Configuration(false);
ChainMapper.addMapper(jobWordCount,
PunctuationMapper.class,
Object.class, Text.class,
IntWritable.class, Text.class,
punctuationMapperConf);
//public static class TrimMapper
//extends Mapper<Object, Text, NullWritable, Text>
Configuration trimMapperConf = new Configuration(false);
ChainMapper.addMapper(jobWordCount,
TrimMapper.class,
Object.class, Text.class,
IntWritable.class, Text.class,
trimMapperConf);
//public static class LowerCaseMapper
//extends Mapper<Object, Text, NullWritable, Text>
Configuration lowerCaseMapperConf = new Configuration(false);
ChainMapper.addMapper(jobWordCount,
LowerCaseMapper.class,
Object.class, Text.class,
//IntWritable.class, Text.class,
IntWritable.class, Text.class,
lowerCaseMapperConf);
//public static class TokenizerMapper
// extends Mapper<IntWritable, Text, Text, IntWritable>
Configuration tokenizerConf = new Configuration(false);
ChainMapper.addMapper(jobWordCount,
TokenizerMapper.class,
IntWritable.class,Text.class,
Text.class, IntWritable.class,
tokenizerConf);
//public static class IntSumReducer
//extends Reducer<Text,IntWritable,Text,IntWritable>
jobWordCount.setReducerClass(IntSumReducer.class);
jobWordCount.setOutputKeyClass(Text.class);
jobWordCount.setOutputValueClass(IntWritable.class);
//FileInputFormat.addInputPath(jobWordCount, new Path(args[0]));
TextInputFormat.setInputPaths(jobWordCount, inputPath);
//FileOutputFormat.setOutputPath(jobWordCount, new Path(args[1]));
FileOutputFormat.setOutputPath(jobWordCount, tmpPath);
// Set the output format to a sequence file
//jobWordCount.setOutputFormatClass(SequenceFileOutputFormat.class);
//SequenceFileOutputFormat.setOutputPath(jobWordCount, outputStage);
int code = jobWordCount.waitForCompletion(true) ? 0 : 1;
if (code == 0) {
//Now that we have extracted column to sort
Job orderJob = new Job(conf, "TopWords");
orderJob.setJarByClass(WordCount2.class);
// Here, use the identity mapper to output the key/value pairs in
// the SequenceFile
orderJob.setMapperClass(TopTenMapper.class);
orderJob.setReducerClass(TopTenReducer.class);
//********************
//public static class TopTenMapper
//extends Mapper<Object, Text, NullWritable, Text>
jobB.setMapOutputKeyClass(NullWritable.class);
jobB.setMapOutputValueClass(Text.class);
//********************
// Set the number of reduce tasks to an appropriate number for the
// amount of data being sorted
orderJob.setNumReduceTasks(10);
// Use Hadoop's TotalOrderPartitioner class
//orderJob.setPartitionerClass(TotalOrderPartitioner.class);
// Set the partition file
//TotalOrderPartitioner.setPartitionFile(orderJob.getConfiguration(),
// partitionFile);
//********************
//public static class TopTenReducer
//extends Reducer<NullWritable, Text, NullWritable, Text>
//orderJob.setOutputKeyClass(Text.class);
//orderJob.setOutputValueClass(IntWritable.class);
//********************
orderJob.setOutputKeyClass(NullWritable.class);
orderJob.setOutputValueClass(Text.class);
//********************
// Set the input to the previous job's output
//orderJob.setInputFormatClass(SequenceFileInputFormat.class);
orderJob.setInputFormatClass(KeyValueTextInputFormat.class);
orderJob.setOutputFormatClass(TextOutputFormat.class);
//SequenceFileInputFormat.setInputPaths(orderJob, outputStage);
// Set the output path to the command line parameter
TextOutputFormat.setOutputPath(orderJob, outputOrder);
// Set the separator to an empty string
//orderJob.getConfiguration().set(
// "mapred.textoutputformat.separator", "");
// Use the InputSampler to go through the output of the previous
// job, sample it, and create the partition file
//InputSampler.writePartitionFile(orderJob,
// new InputSampler.RandomSampler(.1, 10000));
FileInputFormat.setInputPaths(orderJob, tmpPath);
FileOutputFormat.setOutputPath(orderJob, new Path(args[1]));
// Submit the job
code = orderJob.waitForCompletion(true) ? 0 : 2;
}
// Clean up the partition file and the staging directory
// FileSystem.get(new Configuration()).delete(partitionFile, false);
// FileSystem.get(new Configuration()).delete(outputStage, true);
System.exit(code);
return (jobWordCount.waitForCompletion(true) ? 0 : 1);
}
}
Ly9wcmludGVudgovLyBKQVZBX0hPTUU9L3Vzci9qYXZhL2pkazEuNy4wXzY3LWNsb3VkZXJhCi8vIFBBVEg9L3Vzci9qYXZhL2pkazEuNy4wXzY3LWNsb3VkZXJhL2JpbgovLyBleHBvcnQgSEFET09QX0NMQVNTUEFUSD0ke0pBVkFfSE9NRX0vbGliL3Rvb2xzLmphcgoKLy8gaGFkb29wIGNvbS5zdW4udG9vbHMuamF2YWMuTWFpbiBXb3JkQ291bnQzLmphdmEKLy8gamFyIGNmIFdvcmRDb3VudDIuamFyIFdvcmRDb3VudDIqLmNsYXNzCi8vIGhhZG9vcCBqYXIgV29yZENvdW50Mi5qYXIgV29yZENvdW50MiBiaWcudHh0IG91dFdDQmlnCi8vIGhhZG9vcCBqYXIgV29yZENvdW50Mi5qYXIgV29yZENvdW50MiB0OC5zaGFrZXNwZWFyZS50eHQgb3V0V0NTaGFrZQoKLy8gdDguc2hha2VzcGVhcmUKCi8vIFJlbW92ZSB0aGUgcHJldmlvdXMgcmVzdWx0cy4KLy8gJCBoYWRvb3AgZnMgLXJtIC1yIC1mIC91c2VyL2Nsb3VkZXJhL3dvcmRjb3VudC9vdXRwdXQKIAoKaW1wb3J0IGphdmEuaW8uKiA7CmltcG9ydCBqYXZhLnV0aWwuU3RyaW5nVG9rZW5pemVyOwoKaW1wb3J0IG9yZy5hcGFjaGUuaGFkb29wLmNvbmYuQ29uZmlndXJlZDsKaW1wb3J0IG9yZy5hcGFjaGUuaGFkb29wLmNvbmYuQ29uZmlndXJhdGlvbiA7CmltcG9ydCBvcmcuYXBhY2hlLmhhZG9vcC5mcy5QYXRoOwppbXBvcnQgb3JnLmFwYWNoZS5oYWRvb3AuaW8uSW50V3JpdGFibGU7CmltcG9ydCBvcmcuYXBhY2hlLmhhZG9vcC5pby5Mb25nV3JpdGFibGU7CmltcG9ydCBvcmcuYXBhY2hlLmhhZG9vcC5pby5UZXh0OwppbXBvcnQgb3JnLmFwYWNoZS5oYWRvb3AubWFwcmVkdWNlLmpvYldvcmRDb3VudDsKaW1wb3J0IG9yZy5hcGFjaGUuaGFkb29wLm1hcHJlZHVjZS5NYXBwZXI7CmltcG9ydCBvcmcuYXBhY2hlLmhhZG9vcC5tYXByZWR1Y2UuUmVkdWNlcjsKaW1wb3J0IG9yZy5hcGFjaGUuaGFkb29wLm1hcHJlZHVjZS5saWIuaW5wdXQuRmlsZUlucHV0Rm9ybWF0OwppbXBvcnQgb3JnLmFwYWNoZS5oYWRvb3AubWFwcmVkdWNlLmxpYi5vdXRwdXQuRmlsZU91dHB1dEZvcm1hdDsKaW1wb3J0IG9yZy5hcGFjaGUuaGFkb29wLm1hcHJlZHVjZS5saWIuY2hhaW4uQ2hhaW5NYXBwZXI7CmltcG9ydCBvcmcuYXBhY2hlLmhhZG9vcC51dGlsLkdlbmVyaWNPcHRpb25zUGFyc2VyOwppbXBvcnQgb3JnLmFwYWNoZS5oYWRvb3AudXRpbC5Ub29sOwppbXBvcnQgb3JnLmFwYWNoZS5oYWRvb3AudXRpbC5Ub29sUnVubmVyOwoKCmltcG9ydCBvcmcuYXBhY2hlLmhhZG9vcC5tYXByZWR1Y2UuSm9iOwppbXBvcnQgb3JnLmFwYWNoZS5oYWRvb3AubWFwcmVkdWNlLmxpYi5pbnB1dC5TZXF1ZW5jZUZpbGVJbnB1dEZvcm1hdDsKaW1wb3J0IG9yZy5hcGFjaGUuaGFkb29wLm1hcHJlZHVjZS5saWIuaW5wdXQuVGV4dElucHV0Rm9ybWF0OwppbXBvcnQgb3JnLmFwYWNoZS5oYWRvb3AubWFwcmVkdWNlLmxpYi5vdXRwdXQuU2VxdWVuY2VGaWxlT3V0cHV0Rm9ybWF0OwppbXBvcnQgb3JnLmFwYWNoZS5oYWRvb3AubWFwcmVkdWNlLmxpYi5vdXRwdXQuVGV4dE91dHB1dEZvcm1hdDsKaW1wb3J0IG9yZy5hcGFjaGUuaGFkb29wLm1hcHJlZHVjZS5saWIucGFydGl0aW9uLklucHV0U2FtcGxlcjsKaW1wb3J0IG9yZy5hcGFjaGUuaGFkb29wLm1hcHJlZHVjZS5saWIucGFydGl0aW9uLlRvdGFsT3JkZXJQYXJ0aXRpb25lcjsKCnB1YmxpYyBjbGFzcyBXb3JkQ291bnQyIGV4dGVuZHMgQ29uZmlndXJlZCBpbXBsZW1lbnRzIFRvb2wKewoJcHJpdmF0ZSBzdGF0aWMgZmluYWwgTG9nZ2VyIExPRyA9IExvZ2dlci5nZXRMb2dnZXIoV29yZENvdW50Mi5jbGFzcyk7CgkKCXB1YmxpYyBzdGF0aWMgdm9pZCBtYWluKFN0cmluZ1tdIGFyZ3MpIHRocm93cyBFeGNlcHRpb24gewogICAgICAgIGludCBleGl0Q29kZSA9IFRvb2xSdW5uZXIucnVuKG5ldyBXb3JkQ291bnQyKCksIGFyZ3MpOwogICAgICAgIFN5c3RlbS5leGl0KGV4aXRDb2RlKTsKICAgIH0KCQoJcHVibGljIHN0YXRpYyBib29sZWFuIGlzTnVsbE9yRW1wdHkoU3RyaW5nIHN0cikgewogICAgICAgIGlmKHN0ciAhPSBudWxsICYmICFzdHIudHJpbSgpLmlzRW1wdHkoKSkKICAgICAgICAgICAgcmV0dXJuIGZhbHNlOwogICAgICAgIHJldHVybiB0cnVlOwogICAgfQoJCiAKICBwdWJsaWMgc3RhdGljIGNsYXNzIFB1bmN0dWF0aW9uTWFwcGVyIAogICAgICBleHRlbmRzIE1hcHBlcjxPYmplY3QsIFRleHQsIE51bGxXcml0YWJsZSwgVGV4dD4gewoKICAgIHByaXZhdGUgVGV4dCBwdW5jdGQgPSBuZXcgVGV4dCgpOyAgCgkvL3ByaXZhdGUgc3RhdGljIGZpbi8vYWwgU3RyaW5nIFB1bmN0dWF0aW9uTWFya3M9IlwiXCdcXFtcXF1cXFxcISQmQH4jJTo7YDw+KCl7fS8hfD8qLSs9XiwuIjsKCXByaXZhdGUgc3RhdGljIGZpbmFsIFN0cmluZyBQdW5jdHVhdGlvbk1hcmtzPSJcIlxcW1xcXVxcXFwhJCZAfiMlOjtgPD4oKXt9LyF8PyotKz1eLC4iOwoJLy9ET04nVAogICAgcHVibGljIHZvaWQgbWFwKE9iamVjdCBrZXksIFRleHQgdmFsdWUsIENvbnRleHQgY29udGV4dCkgdGhyb3dzIElPRXhjZXB0aW9uLCBJbnRlcnJ1cHRlZEV4Y2VwdGlvbiB7CiAgICAgIFN0cmluZyB3b3JkPSB2YWx1ZS50b1N0cmluZygpOwoJICB3b3JkPXdvcmQucmVwbGFjZUFsbCgiLSIsICIiKTsKCSAgLy93b3JkPXdvcmQucmVwbGFjZUFsbCgiJyIsICIiKTsKCSAgd29yZD13b3JkLnJlcGxhY2VBbGwoIlsiK1B1bmN0dWF0aW9uTWFya3MrIl0iLCAiICIpOwoJICAvL3dvcmQgPSB3b3JkLnJlcGxhY2VBbGwoIlteYS16QS1aXFxzK10iLCAiICIpLnRvTG93ZXJDYXNlKCk7CgkgIC8vd29yZCA9IGN1cnIudHJpbSgpOwoJICAvL3B1bmN0ZC5zZXQoUmVnZXguUmVwbGFjZSh3b3JkLnRvU3RyaW5nKCksW15cd1xkXHMtXSwiICIpKTsKCSAgLy9TdHJpbmcgd29yZF9wdW5jPXdvcmQucmVwbGFjZUFsbCgiW15cXHdcXGRcXHMtXSIsICIgIik7CgkgIC8vd29yZD13b3JkLnJlcGxhY2UoICIvXHNccysvZyIsICIiICk7Ly8gICAgIC0+ICAzOTBtcwoJICBwdW5jdGQuc2V0KHdvcmQpOyAgICAgIAoJICBjb250ZXh0LndyaXRlKG5ldyBJbnRXcml0YWJsZSgxKSwgcHVuY3RkKTsJICAKCSAgfQogIH0gCiAgCiAgIHB1YmxpYyBzdGF0aWMgY2xhc3MgVHJpbU1hcHBlciAKICAgICAgZXh0ZW5kcyBNYXBwZXI8T2JqZWN0LCBUZXh0LCBOdWxsV3JpdGFibGUsIFRleHQ+IHsKCiAgICBwcml2YXRlIFRleHQgdHJpbWQgPSBuZXcgVGV4dCgpOyAgCQogICAgcHVibGljIHZvaWQgbWFwKE9iamVjdCBrZXksIFRleHQgdmFsdWUsIENvbnRleHQgY29udGV4dCkgdGhyb3dzIElPRXhjZXB0aW9uLCBJbnRlcnJ1cHRlZEV4Y2VwdGlvbiB7CiAgICAgIFN0cmluZyB3b3JkPSB2YWx1ZS50b1N0cmluZygpLnRyaW0oKTsJICAJICAKCSAgLy93b3JkPXdvcmQucmVwbGFjZUFsbCggIi9cc1xzKy9nIiwgIiIgKTsvLyAgICAgLT4gIDM5MG1zCiAgICAgIHdvcmQ9d29yZC5yZXBsYWNlQWxsKCJeICt8ICskfCggKSsiLCAiJDEiKTsKCSAgdHJpbWQuc2V0KHdvcmQpOyAgICAgIAoJICBjb250ZXh0LndyaXRlKG5ldyBJbnRXcml0YWJsZSgxKSwgdHJpbWQpOwkgIAoJICB9CiAgfSAgIAogICBwdWJsaWMgc3RhdGljIGNsYXNzIExvd2VyQ2FzZU1hcHBlciAKICAgICAgZXh0ZW5kcyBNYXBwZXI8T2JqZWN0LCBUZXh0LCBOdWxsV3JpdGFibGUsIFRleHQ+IHsKCiAgICBwcml2YXRlIFRleHQgbG93ZXJjYXNlZCA9IG5ldyBUZXh0KCk7CiAgICBwdWJsaWMgdm9pZCBtYXAoT2JqZWN0IGtleSwgVGV4dCB2YWx1ZSwgQ29udGV4dCBjb250ZXh0KSB0aHJvd3MgSU9FeGNlcHRpb24sIEludGVycnVwdGVkRXhjZXB0aW9uIHsKICAgICAgICBsb3dlcmNhc2VkLnNldCh2YWx1ZS50b1N0cmluZygpLnRvTG93ZXJDYXNlKCkpOwogICAgICAgIGNvbnRleHQud3JpdGUobmV3IEludFdyaXRhYmxlKDEpLCBsb3dlcmNhc2VkKTsKICAgIH0KICB9ICAKCiAgcHVibGljIHN0YXRpYyBjbGFzcyBUb2tlbml6ZXJNYXBwZXIKICAgICAgIGV4dGVuZHMgTWFwcGVyPEludFdyaXRhYmxlLCBUZXh0LCBUZXh0LCBJbnRXcml0YWJsZT57CgkgICAKCXByaXZhdGUgc3RhdGljIGZpbmFsIGphdmEudXRpbC5yZWdleC5QYXR0ZXJuIFdPUkRfQk9VTkRBUlkgPSBqYXZhLnV0aWwucmVnZXguUGF0dGVybi5jb21waWxlKCJcXHMiKTsKICAgIAoJcHJpdmF0ZSBmaW5hbCBzdGF0aWMgSW50V3JpdGFibGUgb25lID0gbmV3IEludFdyaXRhYmxlKDEpOwogICAgcHJpdmF0ZSBUZXh0IHdvcmQgPSBuZXcgVGV4dCgpOwoKICAgIHB1YmxpYyB2b2lkIG1hcChJbnRXcml0YWJsZSBrZXksIFRleHQgbGluZVRleHQsIENvbnRleHQgY29udGV4dAogICAgICAgICAgICAgICAgICAgICkgdGhyb3dzIElPRXhjZXB0aW9uLCBJbnRlcnJ1cHRlZEV4Y2VwdGlvbiB7CgkgIFN0cmluZyBsaW5lID0gbGluZVRleHQudG9TdHJpbmcoKTsKICAgICAgVGV4dCBjdXJyZW50V29yZCA9IG5ldyBUZXh0KCk7CiAgICAgIGZvciAoU3RyaW5nIHdvcmQgOiBXT1JEX0JPVU5EQVJZLnNwbGl0KGxpbmUpKSB7CiAgICAgICAgaWYgKFdvcmRDb3VudDIuaXNOdWxsT3JFbXB0eSh3b3JkKSkgewogICAgICAgICAgICBjb250aW51ZTsKICAgICAgICB9CiAgICAgICAgICAgIGN1cnJlbnRXb3JkID0gbmV3IFRleHQod29yZCk7CiAgICAgICAgICAgIGNvbnRleHQud3JpdGUoY3VycmVudFdvcmQsb25lKTsKICAgICAgICB9ICAgICAgCiAgICB9CiAgfQoKICBwdWJsaWMgc3RhdGljIGNsYXNzIEludFN1bVJlZHVjZXIKICAgICAgIGV4dGVuZHMgUmVkdWNlcjxUZXh0LEludFdyaXRhYmxlLFRleHQsSW50V3JpdGFibGU+IHsKICAgIHByaXZhdGUgSW50V3JpdGFibGUgcmVzdWx0ID0gbmV3IEludFdyaXRhYmxlKCk7CgogICAgcHVibGljIHZvaWQgcmVkdWNlKFRleHQga2V5X3dvcmQsIEl0ZXJhYmxlPEludFdyaXRhYmxlPiBjb3VudHMsCiAgICAgICAgICAgICAgICAgICAgICAgQ29udGV4dCBjb250ZXh0CiAgICAgICAgICAgICAgICAgICAgICAgKSB0aHJvd3MgSU9FeGNlcHRpb24sIEludGVycnVwdGVkRXhjZXB0aW9uIHsKICAgICAgaW50IHN1bSA9IDA7CiAgICAgIGZvciAoSW50V3JpdGFibGUgY291bnQgOiBjb3VudHMpIHsKICAgICAgICBzdW0gKz0gY291bnQuZ2V0KCk7CiAgICAgIH0KICAgICAgcmVzdWx0LnNldChzdW0pOwogICAgICBjb250ZXh0LndyaXRlKGtleV93b3JkLCByZXN1bHQpOwogICAgfQogIH0KICAvLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLwogIHB1YmxpYyBzdGF0aWMgY2xhc3MgVG9wVGVuTWFwcGVyIGV4dGVuZHMgTWFwcGVyPE9iamVjdCwgVGV4dCwgTnVsbFdyaXRhYmxlLCBUZXh0PgogICAgewogICAgICAgIHByaXZhdGUgVHJlZU1hcDxJbnRlZ2VyLCBUZXh0PiB0b3BOID0gbmV3IFRyZWVNYXA8SW50ZWdlciwgVGV4dD4oKTsgLy9Db2xsZWN0aW9ucy5yZXZlcnNlT3JkZXIoKQoKICAgICAgICBwcml2YXRlIGZpbmFsIHN0YXRpYyBJbnRXcml0YWJsZSBvbmUgPSBuZXcgSW50V3JpdGFibGUoMSk7CiAgICAgICAgcHJpdmF0ZSBUZXh0IHdvcmQgPSBuZXcgVGV4dCgpOwogICAgICAgIHB1YmxpYyB2b2lkIG1hcChPYmplY3Qga2V5LCBUZXh0IHZhbHVlLCBDb250ZXh0IGNvbnRleHQpCiAgICAgICAgICAgICAgIHRocm93cyBJT0V4Y2VwdGlvbiwgSW50ZXJydXB0ZWRFeGNlcHRpb24gewogICAgICAgICAgICAvLyAod29yZCwgY291bnQpIHR1cGxlCiAgICAgICAgICAgIFN0cmluZ1tdIHdvcmRzID0gdmFsdWUudG9TdHJpbmcoKS5zcGxpdCgiXHQiKSA7CiAgICAgICAgICAgICAgICBpZiAod29yZHMubGVuZ3RoIDwgMikgewogICAgICAgICAgICAgICAgICAgIHJldHVybjsKICAgICAgICAgICAgfQoKICAgICAgICAgICAgdG9wTi5wdXQoSW50ZWdlci5wYXJzZUludCh3b3Jkc1sxXSksIG5ldyBUZXh0KHZhbHVlKSk7CgogICAgICAgICAgICBpZiAodG9wTi5zaXplKCkgPiAxMCkgewogICAgICAgICAgICAgICAgICAgIHRvcE4ucmVtb3ZlKHRvcE4uZmlyc3RLZXkoKSk7CgkJCQkJLy90b3BOLnJlbW92ZSh0b3BOLmxhc3RLZXkoKSk7CiAgICAgICAgICAgIH0KICAgICAgICB9CQoJCgogICAgICAgIEBPdmVycmlkZQogICAgICAgIHByb3RlY3RlZCB2b2lkIGNsZWFudXAoQ29udGV4dCBjb250ZXh0KSB0aHJvd3MgSU9FeGNlcHRpb24sCiAgICAgICAgICAgICAgICBJbnRlcnJ1cHRlZEV4Y2VwdGlvbiB7CiAgICAgICAgICAgIGZvciAoVGV4dCB0IDogdG9wTi52YWx1ZXMoKSkgewogICAgICAgICAgICAgICAgY29udGV4dC53cml0ZShOdWxsV3JpdGFibGUuZ2V0KCksIHQpOwogICAgICAgICAgICB9CiAgICAgICAgfQogICAgfQogIAogIHB1YmxpYyBzdGF0aWMgY2xhc3MgVG9wVGVuUmVkdWNlciBleHRlbmRzCiAgICAgICAgICAgIFJlZHVjZXI8TnVsbFdyaXRhYmxlLCBUZXh0LCBOdWxsV3JpdGFibGUsIFRleHQ+IHsKCiAgICAgICAgcHJpdmF0ZSBUcmVlTWFwPEludGVnZXIsIFRleHQ+IHRvcE4gPSBuZXcgVHJlZU1hcDxJbnRlZ2VyLCBUZXh0PigpOwoKICAgICAgICBAT3ZlcnJpZGUKICAgICAgICBwdWJsaWMgdm9pZCByZWR1Y2UoTnVsbFdyaXRhYmxlIGtleSwgSXRlcmFibGU8VGV4dD4gdmFsdWVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICBDb250ZXh0IGNvbnRleHQpIHRocm93cyBJT0V4Y2VwdGlvbiwgSW50ZXJydXB0ZWRFeGNlcHRpb24gewogICAgICAgICAgICBmb3IgKFRleHQgdmFsdWUgOiB2YWx1ZXMpIHsKICAgICAgICAgICAgICAgIFN0cmluZ1tdIHdvcmRzID0gdmFsdWUudG9TdHJpbmcoKS5zcGxpdCgiXHQiKSA7CgogICAgICAgICAgICAgICAgdG9wTi5wdXQoSW50ZWdlci5wYXJzZUludCh3b3Jkc1sxXSksIG5ldyBUZXh0KHZhbHVlKSk7CgogICAgICAgICAgICAgICAgaWYgKHRvcE4uc2l6ZSgpID4gMTApIHsKICAgICAgICAgICAgICAgICAgICB0b3BOLnJlbW92ZSh0b3BOLmZpcnN0S2V5KCkpOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CgogICAgICAgICAgICBmb3IgKFRleHQgd29yZCA6IHRvcE4uZGVzY2VuZGluZ01hcCgpLnZhbHVlcygpKSB7CiAgICAgICAgICAgICAgICBjb250ZXh0LndyaXRlKE51bGxXcml0YWJsZS5nZXQoKSwgd29yZCk7CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICB9CiAgLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8KCiAgICBwdWJsaWMgaW50IHJ1bihTdHJpbmdbXSBhcmdzKSB0aHJvd3MgRXhjZXB0aW9uIHsKICAgICAgICBDb25maWd1cmF0aW9uIGNvbmYgPSBnZXRDb25mKCk7CgkJLy8vLy8vLy8vLy8vLy8KCQlGaWxlU3lzdGVtIGZzID0gRmlsZVN5c3RlbS5nZXQoY29uZik7CgkJUGF0aCB0bXBQYXRoID0gbmV3IFBhdGgoIi93MS90bXAiKTsKCQlmcy5kZWxldGUodG1wUGF0aCwgdHJ1ZSk7CgkJCgkJUGF0aCBpbnB1dFBhdGggPSBuZXcgUGF0aChhcmdzWzBdKTsKCQkvL1BhdGggcGFydGl0aW9uRmlsZSA9IG5ldyBQYXRoKGFyZ3NbMV0gKyAiX3BhcnRpdGlvbnMubHN0Iik7CgkJLy9QYXRoIG91dHB1dFN0YWdlID0gbmV3IFBhdGgoYXJnc1sxXSArICJfc3RhZ2luZyIpOwoJCVBhdGggb3V0cHV0U3RhZ2UgPSBuZXcgUGF0aCgiL3cxL3RtcCIpOwoJCVBhdGggb3V0cHV0T3JkZXIgPSBuZXcgUGF0aChhcmdzWzFdKTsKCQkvLy8vLy8vLy8vLy8vLwoKICAgICAgICBhcmdzID0gbmV3IEdlbmVyaWNPcHRpb25zUGFyc2VyKGNvbmYsIGFyZ3MpLmdldFJlbWFpbmluZ0FyZ3MoKTsKCQkKCQkvLyBjcmVhdGluZyBhIHdvcmQgY291bnQgam9iV29yZENvdW50CiAgICAgICAgam9iV29yZENvdW50IGpvYldvcmRDb3VudCA9IGpvYldvcmRDb3VudC5nZXRJbnN0YW5jZShjb25mLCJ3b3JkY291bnQiKTsKCQkvL2pvYldvcmRDb3VudCBqb2JXb3JkQ291bnQgPSBqb2JXb3JkQ291bnQuZ2V0SW5zdGFuY2UoZ2V0Q29uZigpLCAid29yZGNvdW50Iik7CiAgICAgICAgLy9qb2JXb3JkQ291bnQuc2V0SmFyQnlDbGFzcyhXb3JkQ291bnQyLmNsYXNzKTsKCQlqb2JXb3JkQ291bnQuc2V0SmFyQnlDbGFzcyh0aGlzLmdldENsYXNzKCkpOwogICAgLy8gVXNlIFRleHRJbnB1dEZvcm1hdCwgdGhlIGRlZmF1bHQgdW5sZXNzIGpvYldvcmRDb3VudC5zZXRJbnB1dEZvcm1hdENsYXNzIGlzIHVzZWQKICAgICAgICAKCQkvL3B1YmxpYyBzdGF0aWMgY2xhc3MgUHVuY3R1YXRpb25NYXBwZXIgCgkJLy9leHRlbmRzIE1hcHBlcjxPYmplY3QsIFRleHQsIE51bGxXcml0YWJsZSwgVGV4dD4gCgkJQ29uZmlndXJhdGlvbiBwdW5jdHVhdGlvbk1hcHBlckNvbmYgPSBuZXcgQ29uZmlndXJhdGlvbihmYWxzZSk7CiAgICAgICAgQ2hhaW5NYXBwZXIuYWRkTWFwcGVyKGpvYldvcmRDb3VudCwKICAgICAgICAgIFB1bmN0dWF0aW9uTWFwcGVyLmNsYXNzLAogICAgICAgICAgT2JqZWN0LmNsYXNzLCBUZXh0LmNsYXNzLAogICAgICAgICAgSW50V3JpdGFibGUuY2xhc3MsIFRleHQuY2xhc3MsCiAgICAgICAgICBwdW5jdHVhdGlvbk1hcHBlckNvbmYpOwoJCSAgCgkJICAvL3B1YmxpYyBzdGF0aWMgY2xhc3MgVHJpbU1hcHBlciAKICAgICAgLy9leHRlbmRzIE1hcHBlcjxPYmplY3QsIFRleHQsIE51bGxXcml0YWJsZSwgVGV4dD4KCQkgIENvbmZpZ3VyYXRpb24gdHJpbU1hcHBlckNvbmYgPSBuZXcgQ29uZmlndXJhdGlvbihmYWxzZSk7CiAgICAgICAgQ2hhaW5NYXBwZXIuYWRkTWFwcGVyKGpvYldvcmRDb3VudCwKICAgICAgICAgIFRyaW1NYXBwZXIuY2xhc3MsCiAgICAgICAgICBPYmplY3QuY2xhc3MsIFRleHQuY2xhc3MsCiAgICAgICAgICBJbnRXcml0YWJsZS5jbGFzcywgVGV4dC5jbGFzcywKICAgICAgICAgIHRyaW1NYXBwZXJDb25mKTsKCQkKCQkvL3B1YmxpYyBzdGF0aWMgY2xhc3MgTG93ZXJDYXNlTWFwcGVyIAogICAgICAvL2V4dGVuZHMgTWFwcGVyPE9iamVjdCwgVGV4dCwgTnVsbFdyaXRhYmxlLCBUZXh0PiAJCQoJCUNvbmZpZ3VyYXRpb24gbG93ZXJDYXNlTWFwcGVyQ29uZiA9IG5ldyBDb25maWd1cmF0aW9uKGZhbHNlKTsKICAgICAgICBDaGFpbk1hcHBlci5hZGRNYXBwZXIoam9iV29yZENvdW50LAogICAgICAgICAgTG93ZXJDYXNlTWFwcGVyLmNsYXNzLAogICAgICAgICAgT2JqZWN0LmNsYXNzLCBUZXh0LmNsYXNzLAoJCSAgLy9JbnRXcml0YWJsZS5jbGFzcywgVGV4dC5jbGFzcywKICAgICAgICAgIEludFdyaXRhYmxlLmNsYXNzLCBUZXh0LmNsYXNzLAogICAgICAgICAgbG93ZXJDYXNlTWFwcGVyQ29uZik7ICAKCQkKCQkvL3B1YmxpYyBzdGF0aWMgY2xhc3MgVG9rZW5pemVyTWFwcGVyCiAgICAgIC8vIGV4dGVuZHMgTWFwcGVyPEludFdyaXRhYmxlLCBUZXh0LCBUZXh0LCBJbnRXcml0YWJsZT4KICAgICAgICBDb25maWd1cmF0aW9uIHRva2VuaXplckNvbmYgPSBuZXcgQ29uZmlndXJhdGlvbihmYWxzZSk7CiAgICAgICAgQ2hhaW5NYXBwZXIuYWRkTWFwcGVyKGpvYldvcmRDb3VudCwKICAgICAgICAgIFRva2VuaXplck1hcHBlci5jbGFzcywKICAgICAgICAgIEludFdyaXRhYmxlLmNsYXNzLFRleHQuY2xhc3MsCiAgICAgICAgICBUZXh0LmNsYXNzLCBJbnRXcml0YWJsZS5jbGFzcywKICAgICAgICAgIHRva2VuaXplckNvbmYpOwoKCQkgIC8vcHVibGljIHN0YXRpYyBjbGFzcyBJbnRTdW1SZWR1Y2VyCiAgICAgICAvL2V4dGVuZHMgUmVkdWNlcjxUZXh0LEludFdyaXRhYmxlLFRleHQsSW50V3JpdGFibGU+CSAgIAogICAgICAgIGpvYldvcmRDb3VudC5zZXRSZWR1Y2VyQ2xhc3MoSW50U3VtUmVkdWNlci5jbGFzcyk7ICAgICAgCgkJam9iV29yZENvdW50LnNldE91dHB1dEtleUNsYXNzKFRleHQuY2xhc3MpOwogICAgICAgIGpvYldvcmRDb3VudC5zZXRPdXRwdXRWYWx1ZUNsYXNzKEludFdyaXRhYmxlLmNsYXNzKTsKCQkKICAgICAgICAvL0ZpbGVJbnB1dEZvcm1hdC5hZGRJbnB1dFBhdGgoam9iV29yZENvdW50LCBuZXcgUGF0aChhcmdzWzBdKSk7CiAgICAgICAgVGV4dElucHV0Rm9ybWF0LnNldElucHV0UGF0aHMoam9iV29yZENvdW50LCBpbnB1dFBhdGgpOwoJCQoJCS8vRmlsZU91dHB1dEZvcm1hdC5zZXRPdXRwdXRQYXRoKGpvYldvcmRDb3VudCwgbmV3IFBhdGgoYXJnc1sxXSkpOwoJCUZpbGVPdXRwdXRGb3JtYXQuc2V0T3V0cHV0UGF0aChqb2JXb3JkQ291bnQsIHRtcFBhdGgpOwoJCS8vIFNldCB0aGUgb3V0cHV0IGZvcm1hdCB0byBhIHNlcXVlbmNlIGZpbGUKCQkvL2pvYldvcmRDb3VudC5zZXRPdXRwdXRGb3JtYXRDbGFzcyhTZXF1ZW5jZUZpbGVPdXRwdXRGb3JtYXQuY2xhc3MpOwoJCS8vU2VxdWVuY2VGaWxlT3V0cHV0Rm9ybWF0LnNldE91dHB1dFBhdGgoam9iV29yZENvdW50LCBvdXRwdXRTdGFnZSk7CgoJCWludCBjb2RlID0gam9iV29yZENvdW50LndhaXRGb3JDb21wbGV0aW9uKHRydWUpID8gMCA6IDE7CgkJCgkJaWYgKGNvZGUgPT0gMCkgewoJCQkKCQkJLy9Ob3cgdGhhdCB3ZSBoYXZlIGV4dHJhY3RlZCBjb2x1bW4gdG8gc29ydAoJCQkJCQkKCQkJSm9iIG9yZGVySm9iID0gbmV3IEpvYihjb25mLCAiVG9wV29yZHMiKTsKCQkJb3JkZXJKb2Iuc2V0SmFyQnlDbGFzcyhXb3JkQ291bnQyLmNsYXNzKTsKCQkJCgkJCS8vIEhlcmUsIHVzZSB0aGUgaWRlbnRpdHkgbWFwcGVyIHRvIG91dHB1dCB0aGUga2V5L3ZhbHVlIHBhaXJzIGluCgkJCS8vIHRoZSBTZXF1ZW5jZUZpbGUKCQkJb3JkZXJKb2Iuc2V0TWFwcGVyQ2xhc3MoVG9wVGVuTWFwcGVyLmNsYXNzKTsKCQkJb3JkZXJKb2Iuc2V0UmVkdWNlckNsYXNzKFRvcFRlblJlZHVjZXIuY2xhc3MpOwoJCQkvLyoqKioqKioqKioqKioqKioqKioqCgkJCS8vcHVibGljIHN0YXRpYyBjbGFzcyBUb3BUZW5NYXBwZXIgCgkJCS8vZXh0ZW5kcyBNYXBwZXI8T2JqZWN0LCBUZXh0LCBOdWxsV3JpdGFibGUsIFRleHQ+CgkJCQoJCQlqb2JCLnNldE1hcE91dHB1dEtleUNsYXNzKE51bGxXcml0YWJsZS5jbGFzcyk7CgkJCWpvYkIuc2V0TWFwT3V0cHV0VmFsdWVDbGFzcyhUZXh0LmNsYXNzKTsKCQkJLy8qKioqKioqKioqKioqKioqKioqKgoJCQkvLyBTZXQgdGhlIG51bWJlciBvZiByZWR1Y2UgdGFza3MgdG8gYW4gYXBwcm9wcmlhdGUgbnVtYmVyIGZvciB0aGUKCQkJLy8gYW1vdW50IG9mIGRhdGEgYmVpbmcgc29ydGVkCgkJCW9yZGVySm9iLnNldE51bVJlZHVjZVRhc2tzKDEwKTsKCQkJLy8gVXNlIEhhZG9vcCdzIFRvdGFsT3JkZXJQYXJ0aXRpb25lciBjbGFzcwoJCQkvL29yZGVySm9iLnNldFBhcnRpdGlvbmVyQ2xhc3MoVG90YWxPcmRlclBhcnRpdGlvbmVyLmNsYXNzKTsKCQkJLy8gU2V0IHRoZSBwYXJ0aXRpb24gZmlsZQoJCQkvL1RvdGFsT3JkZXJQYXJ0aXRpb25lci5zZXRQYXJ0aXRpb25GaWxlKG9yZGVySm9iLmdldENvbmZpZ3VyYXRpb24oKSwKCQkJLy8JCXBhcnRpdGlvbkZpbGUpOwoJCQkvLyoqKioqKioqKioqKioqKioqKioqCgkJCS8vcHVibGljIHN0YXRpYyBjbGFzcyBUb3BUZW5SZWR1Y2VyIAoJCQkvL2V4dGVuZHMgUmVkdWNlcjxOdWxsV3JpdGFibGUsIFRleHQsIE51bGxXcml0YWJsZSwgVGV4dD4KCQkJCgkJCS8vb3JkZXJKb2Iuc2V0T3V0cHV0S2V5Q2xhc3MoVGV4dC5jbGFzcyk7CgkJCS8vb3JkZXJKb2Iuc2V0T3V0cHV0VmFsdWVDbGFzcyhJbnRXcml0YWJsZS5jbGFzcyk7ICAKCQkJLy8qKioqKioqKioqKioqKioqKioqKgkJCgkJCW9yZGVySm9iLnNldE91dHB1dEtleUNsYXNzKE51bGxXcml0YWJsZS5jbGFzcyk7CgkJCW9yZGVySm9iLnNldE91dHB1dFZhbHVlQ2xhc3MoVGV4dC5jbGFzcyk7CgkJCS8vKioqKioqKioqKioqKioqKioqKioKCQkJLy8gU2V0IHRoZSBpbnB1dCB0byB0aGUgcHJldmlvdXMgam9iJ3Mgb3V0cHV0CgkJCS8vb3JkZXJKb2Iuc2V0SW5wdXRGb3JtYXRDbGFzcyhTZXF1ZW5jZUZpbGVJbnB1dEZvcm1hdC5jbGFzcyk7CgkJCW9yZGVySm9iLnNldElucHV0Rm9ybWF0Q2xhc3MoS2V5VmFsdWVUZXh0SW5wdXRGb3JtYXQuY2xhc3MpOwoJCQlvcmRlckpvYi5zZXRPdXRwdXRGb3JtYXRDbGFzcyhUZXh0T3V0cHV0Rm9ybWF0LmNsYXNzKTsKCQkJCgkJCS8vU2VxdWVuY2VGaWxlSW5wdXRGb3JtYXQuc2V0SW5wdXRQYXRocyhvcmRlckpvYiwgb3V0cHV0U3RhZ2UpOwoJCQkvLyBTZXQgdGhlIG91dHB1dCBwYXRoIHRvIHRoZSBjb21tYW5kIGxpbmUgcGFyYW1ldGVyCgkJCVRleHRPdXRwdXRGb3JtYXQuc2V0T3V0cHV0UGF0aChvcmRlckpvYiwgb3V0cHV0T3JkZXIpOwoJCQkvLyBTZXQgdGhlIHNlcGFyYXRvciB0byBhbiBlbXB0eSBzdHJpbmcKCQkJLy9vcmRlckpvYi5nZXRDb25maWd1cmF0aW9uKCkuc2V0KAoJCQkvLwkJIm1hcHJlZC50ZXh0b3V0cHV0Zm9ybWF0LnNlcGFyYXRvciIsICIiKTsKCQkJLy8gVXNlIHRoZSBJbnB1dFNhbXBsZXIgdG8gZ28gdGhyb3VnaCB0aGUgb3V0cHV0IG9mIHRoZSBwcmV2aW91cwoJCQkvLyBqb2IsIHNhbXBsZSBpdCwgYW5kIGNyZWF0ZSB0aGUgcGFydGl0aW9uIGZpbGUKCQkJLy9JbnB1dFNhbXBsZXIud3JpdGVQYXJ0aXRpb25GaWxlKG9yZGVySm9iLAoJCQkvLwkJbmV3IElucHV0U2FtcGxlci5SYW5kb21TYW1wbGVyKC4xLCAxMDAwMCkpOwoJCQkgIEZpbGVJbnB1dEZvcm1hdC5zZXRJbnB1dFBhdGhzKG9yZGVySm9iLCB0bXBQYXRoKTsKCQkJICBGaWxlT3V0cHV0Rm9ybWF0LnNldE91dHB1dFBhdGgob3JkZXJKb2IsIG5ldyBQYXRoKGFyZ3NbMV0pKTsKCgkJCSAgLy8gU3VibWl0IHRoZSBqb2IKCQkJY29kZSA9IG9yZGVySm9iLndhaXRGb3JDb21wbGV0aW9uKHRydWUpID8gMCA6IDI7CgkJfQoKCQkvLyBDbGVhbiB1cCB0aGUgcGFydGl0aW9uIGZpbGUgYW5kIHRoZSBzdGFnaW5nIGRpcmVjdG9yeQoJCS8vIEZpbGVTeXN0ZW0uZ2V0KG5ldyBDb25maWd1cmF0aW9uKCkpLmRlbGV0ZShwYXJ0aXRpb25GaWxlLCBmYWxzZSk7CgkJLy8gRmlsZVN5c3RlbS5nZXQobmV3IENvbmZpZ3VyYXRpb24oKSkuZGVsZXRlKG91dHB1dFN0YWdlLCB0cnVlKTsKCQlTeXN0ZW0uZXhpdChjb2RlKTsKCQkKICAgICAgICByZXR1cm4gKGpvYldvcmRDb3VudC53YWl0Rm9yQ29tcGxldGlvbih0cnVlKSA/IDAgOiAxKTsKICAgIH0gICAKfSAK