Генерация кода с использованием строкового шаблона

Я пытаюсь использовать строковый шаблон для генерации кода Pig/Hadoop. Так как я новичок, сам не разобрался. Любая помощь будет оценена.

У меня есть список LocalDate, как показано ниже

List<LocalDate> dates = Arrays.asList("20100101", "20100102").stream().map(d -> LocalDate.parse(d,formatter)).collect(Collectors.toList());

В списке может быть 1 дата или несколько дат.

Если список «даты» содержит более одного элемента, я хотел бы сгенерировать:

SPLIT finalizedEvents INTO splitByDay_20100101 IF dataDate == 20100101,
                  INTO splitByDay_20100102 IF dataDate == 20100102, ....; // for all date in "dates" list
// similarly for all dates
// formatting substitution variable e.g. 2010/01/01 instead of 20100101 is needed
STORE splitByDay_20100101 INTO '/a/b/2010/01/01' USING AvroStorage();
STORE splitByDay_20100102 INTO '/a/b/2010/01/02' USING AvroStorage();

Если список «даты» содержит только один элемент, я хотел бы сгенерировать (предположим, даты = [ 20100101] )

splitByDay_20100101 = FOREACH finalizedEvents GENERATE $0..;
STORE splitByDay_20100101 INTO '/a/b/2010/01/01' USING AvroStorage();

До сих пор я сделал что-то вроде следующего, но не уверен, как сделать условные выражения

ST e = new ST("SPLIT finalizedEvents INTO <[dates]:{ d | IF split_<d> BY daysSinceEpoch == <d>}; separator=\", \">;");
e.add("dates", dates);
System.out.println(e.render());

person user3138594    schedule 18.11.2016    source источник


Ответы (1)


Вот что у меня получилось (подробное объяснение ниже):

Java-код:

List<LocalDate> dates = new ArrayList<>();
dates.add(LocalDate.parse("20100101", DateTimeFormatter.BASIC_ISO_DATE));
dates.add(LocalDate.parse("20100201", DateTimeFormatter.BASIC_ISO_DATE));

List<List<Character>> charListList = new ArrayList<>();
for (LocalDate date : dates) {
    List<Character> charList = new ArrayList<>();
    char[] dateCharArray = date.toString().toCharArray();
    for (char c : dateCharArray) {
        charList.add(c);
    }
    charListList.add(charList);
}

STGroup dateGroup = new STGroupFile("./src/com/stackoverflow/DateList/dates.stg");
ST dateTemp = dateGroup.getInstanceOf("writeCode");
dateTemp.add("formattedDates", charListList);
dateTemp.add("isSingle", charListList.size() == 1);

System.out.println(dateTemp.render());


Код StringTemplate (dates.stg):

writeCode(formattedDates, isSingle) ::= <<
<if(isSingle)><writeSingleStuff(formattedDates)>
<else><writeMultipleStuff(formattedDates)>
<endif>
<writeStoreList(formattedDates)>
>>


writeSingleStuff(date)::= "<date:{d|splitByDay_<wordReplaceWSlash(d)> = FOREACH finalizedEvents GENERATE $0..;}>"

writeMultipleStuff(rawDates)::= "SPLIT finalizedEvents <rawDates:{d|INTO splitByDay_<wordReplaceWEmpty(d)> IF dataDate == <wordReplaceWEmpty(d)>}; separator=\", \">;"

writeStoreList(formattedDates)::= "<formattedDates:{d|STORE splitByDay_<wordReplaceWEmpty(d)> INTO '/a/b/<wordReplaceWSlash(d)>' USING AvroStorage();<\n>}>"


wordReplaceWSlash(word) ::= "<word:{char|<charReplaceWSlash(char)>}>"

charReplaceWSlash(theChar) ::= <%<charReplaceWSlashMap.(theChar)>%>

charReplaceWSlashMap ::= [
    "-":"/",
    default:{<theChar>}
]


wordReplaceWEmpty(word) ::= "<word:{char|<charReplaceWEmpty(char)>}>"

charReplaceWEmpty(theChar) ::= <%<charReplaceWEmptyMap.(theChar)>%>

charReplaceWEmptyMap ::= [
    "-":"",
    default:{<theChar>}
]


Что делает код:

Java-код:

List<LocalDate> dates = new ArrayList<>();
dates.add(LocalDate.parse("20100101", DateTimeFormatter.BASIC_ISO_DATE));
dates.add(LocalDate.parse("20100201", DateTimeFormatter.BASIC_ISO_DATE));

Это список с LocalDates, который мы хотим использовать в качестве входных данных для шаблонов. Я отформатировал их с помощью DateTimeFormatter.BASIC_ISO_DATE, чтобы, например. 20100101 становится 2010-01-01. Это понадобится нам позже, потому что мы сообщим StringTemplate заменить - либо на /, либо на пустую строку, чтобы получить два типа форматов даты, которые нам нужны (и я не нашел средство форматирования, которое изначально получает 2010/01/01).
Два разных подхода:

  • Замените - на / в коде Java, а не в StringTemplate. Затем нам нужно было только заменить / пустой строкой, если нам нужен этот формат даты.
  • добавьте в шаблон год, месяц и день как три разные переменные. Затем мы могли бы объединить строки и добавить /, когда нам нужно.


List<List<Character>> charListList = new ArrayList<>();
for (LocalDate date : dates) {
    List<Character> charList = new ArrayList<>();
    char[] dateCharArray = date.toString().toCharArray();
    for (char c : dateCharArray) {
        charList.add(c);
    }
    charListList.add(charList);
}

Это не очень красиво, но:
У нас должен быть список (а не массив) символов даты, чтобы мы могли "перебирать" его. в шаблоне строки. И нет прямого способа преобразовать char[] в список. В конце концов, нам нужно собрать все эти списки в один список, чтобы мы могли генерировать код для каждой имеющейся у нас даты (charListList).


STGroup dateGroup = new STGroupFile("./src/com/stackoverflow/DateList/dates.stg");
ST dateTemp = dateGroup.getInstanceOf("writeCode");
dateTemp.add("formattedDates", charListList);
dateTemp.add("isSingle", charListList.size() == 1);

System.out.println(dateTemp.render());

Здесь мы заполняем шаблон значениями. Мы должны сообщить StringTemplate здесь, есть ли у нас ровно одна дата в нашем charListList, потому что StringTemplate (преднамеренно) не может этого сделать.


StringTemplateCode:

writeCode(formattedDates, isSingle) ::= <<
<if(isSingle)><writeSingleStuff(formattedDates)>
<else><writeMultipleStuff(formattedDates)>
<endif>
<writeStoreList(formattedDates)>
>>

Это «корневой» шаблон, который просто делегирует работу другим шаблонам. Он обрабатывает различие регистра между одной или несколькими датами.


writeSingleStuff(date)::= "<date:{d|splitByDay_<wordReplaceWSlash(d)> = FOREACH finalizedEvents GENERATE $0..;}>"

writeMultipleStuff(rawDates)::= "SPLIT finalizedEvents <rawDates:{d|INTO splitByDay_<wordReplaceWEmpty(d)> IF dataDate == <wordReplaceWEmpty(d)>}; separator=\", \">;"

writeStoreList(dates)::= "<dates:{d|STORE splitByDay_<wordReplaceWEmpty(d)> INTO '/a/b/<wordReplaceWSlash(d)>' USING AvroStorage();<\n>}>"

С помощью этих трех строк мы пишем код, специфичный для одного элемента, для нескольких элементов, и код, который является общим.
Хотя мы знаем, что date имеет только один элемент, когда мы вводим writeSingleStuff, мы должны перебрать список.


wordReplaceWSlash(word) ::= "<word:{char|<charReplaceWSlash(char)>}>"

charReplaceWSlash(theChar) ::= <%<charReplaceWSlashMap.(theChar)>%>

charReplaceWSlashMap ::= [
    "-":"/",
    default:{<theChar>}
]


wordReplaceWEmpty(word) ::= "<word:{char|<charReplaceWEmpty(char)>}>"

charReplaceWEmpty(theChar) ::= <%<charReplaceWEmptyMap.(theChar)>%>

charReplaceWEmptyMap ::= [
    "-":"",
    default:{<theChar>}
]

Это две группы шаблонов, которые почти делают одно и то же: заменяют каждый символ - в каждом «слове» либо на /, либо на пустую строку. Для этого мы используем небольшой словарь, который заменяет каждый символ, кроме -, самим собой.

person Colophonius    schedule 05.02.2017