Add mechanism to specify fallback values in the pp variables
This commit is contained in:
parent
9bb2d5d593
commit
c49a7db192
|
@ -42,11 +42,14 @@ The part you have to copy is
|
|||
|
||||
<a id="variables" />
|
||||
|
||||
###### Available variables:
|
||||
- **${modelName}** - the name of the recorded model
|
||||
- **${modelDisplayName}** - the name of the recorded model, which is shown on the webpage. Might be the same as
|
||||
${modelName}
|
||||
- **${modelSanitizedName}** - sanitized name of the model. The following characters are replaced by an underscore:
|
||||
\\, /, ', " and space
|
||||
- **${modelGroupName}** - name of the model group, if the model is part of a group
|
||||
- **${modelGroupId}** - the unique ID of the model group, if the model is part of a group
|
||||
- **${siteName}** - the name of the cam site, the model streams on
|
||||
- **${siteSanitizedName}** - sanitized name of the site. The following characters are replaced by an underscore:
|
||||
\\, /, ', " and space
|
||||
|
@ -116,4 +119,15 @@ The part you have to copy is
|
|||
</table>
|
||||
|
||||
|
||||
For more information see: [DateTimeFormatter](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/time/format/DateTimeFormatter.html)
|
||||
For more information see: [DateTimeFormatter](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/time/format/DateTimeFormatter.html)
|
||||
###### Fallback values:
|
||||
You can define a fallback value for each variable in case there is no value available for the variable. The syntax is
|
||||
|
||||
${placeholder?foobar}
|
||||
|
||||
Let's for example say you have created some model groups. For models, which are part of a group, you want to use the group name. But for models, which
|
||||
are not part of a group you want to use the sanitized name. You can achieve that by using the following expression:
|
||||
|
||||
${modelGroupName?${modelSanitizedName}}
|
||||
|
||||
It can be read like "use the modelGroupName, but if that is not available use modelSanitizedName".
|
|
@ -3,107 +3,153 @@ package ctbrec.recorder.postprocessing;
|
|||
import static ctbrec.StringUtil.*;
|
||||
import static java.util.Optional.*;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Function;
|
||||
|
||||
import ctbrec.Config;
|
||||
import ctbrec.Model;
|
||||
import ctbrec.ModelGroup;
|
||||
import ctbrec.Recording;
|
||||
import ctbrec.StringUtil;
|
||||
import ctbrec.sites.Site;
|
||||
|
||||
public abstract class AbstractPlaceholderAwarePostProcessor extends AbstractPostProcessor {
|
||||
|
||||
public static final String[] PLACE_HOLDERS = {
|
||||
"${modelName}",
|
||||
"${modelDisplayName}",
|
||||
"${modelSanitizedName}",
|
||||
"${siteName}",
|
||||
"${siteSanitizedName}",
|
||||
"${utcDateTime}",
|
||||
"${localDateTime}",
|
||||
"${epochSecond}",
|
||||
"${fileSuffix}",
|
||||
"${modelNotes}",
|
||||
"${recordingNotes}",
|
||||
"${recordingsDir}",
|
||||
"${absolutePath}",
|
||||
"${absoluteParentPath}"
|
||||
};
|
||||
|
||||
public String fillInPlaceHolders(String input, PostProcessingContext ctx) {
|
||||
Recording rec = ctx.getRecording();
|
||||
Config config = ctx.getConfig();
|
||||
// @formatter:off
|
||||
String output = input
|
||||
.replace("${modelName}", ofNullable(rec.getModel().getName()).orElse("modelName"))
|
||||
.replace("${modelDisplayName}", ofNullable(rec.getModel().getDisplayName()).orElse("displayName"))
|
||||
.replace("${modelSanitizedName}", ofNullable(rec.getModel().getSanitizedNamed()).orElse("sanitizedName"))
|
||||
.replace("${siteName}", ofNullable(rec.getModel().getSite()).map(Site::getName).orElse("site"))
|
||||
.replace("${siteSanitizedName}", getSanitizedSiteName(rec))
|
||||
.replace("${fileSuffix}", getFileSuffix(rec))
|
||||
.replace("${epochSecond}", Long.toString(rec.getStartDate().getEpochSecond()))
|
||||
.replace("${modelNotes}", sanitize(config.getModelNotes(rec.getModel())))
|
||||
.replace("${recordingNotes}", getSanitizedRecordingNotes(rec))
|
||||
.replace("${recordingsDir}", config.getSettings().recordingsDir)
|
||||
.replace("${absolutePath}", rec.getPostProcessedFile().getAbsolutePath())
|
||||
.replace("${absoluteParentPath}", rec.getPostProcessedFile().getParentFile().getAbsolutePath())
|
||||
;
|
||||
Optional<ModelGroup> modelGroup = config.getModelGroup(rec.getModel());
|
||||
|
||||
output = replaceUtcDateTime(rec, output);
|
||||
output = replaceLocalDateTime(rec, output);
|
||||
Map<String, Function<String, Optional<String>>> placeholderValueSuppliers = new HashMap<>();
|
||||
placeholderValueSuppliers.put("modelName", r -> ofNullable(rec.getModel().getName()));
|
||||
placeholderValueSuppliers.put("modelDisplayName", r -> ofNullable(rec.getModel().getDisplayName()));
|
||||
placeholderValueSuppliers.put("modelSanitizedName", r -> getSanitizedName(rec.getModel()));
|
||||
placeholderValueSuppliers.put("siteName", r -> ofNullable(rec.getModel().getSite()).map(Site::getName));
|
||||
placeholderValueSuppliers.put("siteSanitizedName", r -> getSanitizedSiteName(rec));
|
||||
placeholderValueSuppliers.put("fileSuffix", r -> getFileSuffix(rec));
|
||||
placeholderValueSuppliers.put("epochSecond", r -> ofNullable(rec.getStartDate()).map(Instant::getEpochSecond).map(l -> Long.toString(l))); // NOSONAR
|
||||
placeholderValueSuppliers.put("modelNotes", r -> getSanitizedModelNotes(config, rec.getModel()));
|
||||
placeholderValueSuppliers.put("recordingNotes", r -> getSanitizedRecordingNotes(rec));
|
||||
placeholderValueSuppliers.put("recordingsDir", r -> Optional.of(config.getSettings().recordingsDir));
|
||||
placeholderValueSuppliers.put("absolutePath", r -> Optional.of(rec.getPostProcessedFile().getAbsolutePath()));
|
||||
placeholderValueSuppliers.put("absoluteParentPath", r -> Optional.of(rec.getPostProcessedFile().getParentFile().getAbsolutePath()));
|
||||
placeholderValueSuppliers.put("modelGroupName", r -> modelGroup.map(ModelGroup::getName));
|
||||
placeholderValueSuppliers.put("modelGroupId", r -> modelGroup.map(ModelGroup::getId).map(UUID::toString));
|
||||
placeholderValueSuppliers.put("utcDateTime", pattern -> replaceUtcDateTime(rec, pattern));
|
||||
placeholderValueSuppliers.put("localDateTime", pattern -> replaceLocalDateTime(rec, pattern));
|
||||
|
||||
String output = fillInPlaceHolders(input, placeholderValueSuppliers);
|
||||
return output;
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
private String replaceUtcDateTime(Recording rec, String filename) {
|
||||
return replaceDateTime(rec, filename, "utcDateTime", ZoneOffset.UTC);
|
||||
}
|
||||
|
||||
private String replaceLocalDateTime(Recording rec, String filename) {
|
||||
return replaceDateTime(rec, filename, "localDateTime", ZoneId.systemDefault());
|
||||
}
|
||||
|
||||
private String replaceDateTime(Recording rec, String filename, String placeHolder, ZoneId zone) {
|
||||
String pattern = "yyyy-MM-dd_HH-mm-ss";
|
||||
Pattern regex = Pattern.compile("\\$\\{" + placeHolder + "(?:\\((.*?)\\))?\\}");
|
||||
Matcher m = regex.matcher(filename);
|
||||
while (m.find()) {
|
||||
String p = m.group(1);
|
||||
if (p != null) {
|
||||
pattern = p;
|
||||
}
|
||||
String formattedDate = getDateTime(rec, pattern, zone);
|
||||
filename = m.replaceFirst(formattedDate);
|
||||
m = regex.matcher(filename);
|
||||
}
|
||||
return filename;
|
||||
}
|
||||
|
||||
private String getDateTime(Recording rec, String pattern, ZoneId zone) {
|
||||
return DateTimeFormatter.ofPattern(pattern)
|
||||
.withLocale(Locale.getDefault())
|
||||
.withZone(zone)
|
||||
.format(rec.getStartDate());
|
||||
}
|
||||
|
||||
private CharSequence getFileSuffix(Recording rec) {
|
||||
if(rec.isSingleFile()) {
|
||||
String filename = rec.getPostProcessedFile().getName();
|
||||
return filename.substring(filename.lastIndexOf('.') + 1);
|
||||
private Optional<String> getSanitizedName(Model model) {
|
||||
String name = model.getSanitizedNamed();
|
||||
if (StringUtil.isBlank(name)) {
|
||||
return Optional.empty();
|
||||
} else {
|
||||
return "";
|
||||
return Optional.of(name);
|
||||
}
|
||||
}
|
||||
|
||||
private CharSequence getSanitizedSiteName(Recording rec) {
|
||||
return sanitize(ofNullable(rec.getModel().getSite()).map(Site::getName).orElse(""));
|
||||
private String fillInPlaceHolders(String input, Map<String, Function<String, Optional<String>>> placeholderValueSuppliers) {
|
||||
boolean somethingReplaced = false;
|
||||
do {
|
||||
somethingReplaced = false;
|
||||
int end = input.indexOf("}");
|
||||
if (end > 0) {
|
||||
int start = input.substring(0, end).lastIndexOf("${");
|
||||
if (start >= 0) {
|
||||
String placeholder = input.substring(start, end + 1);
|
||||
String placeholderName = placeholder.substring(2, placeholder.length() - 1);
|
||||
String defaultValue = null;
|
||||
String expression = null;
|
||||
int questionMark = placeholder.indexOf('?');
|
||||
if (questionMark > 0) {
|
||||
placeholderName = placeholder.substring(2, questionMark);
|
||||
defaultValue = placeholder.substring(questionMark + 1, placeholder.length() - 1);
|
||||
}
|
||||
int bracket = placeholder.indexOf('(');
|
||||
if (bracket > 0) {
|
||||
placeholderName = placeholder.substring(2, bracket);
|
||||
expression = placeholder.substring(bracket + 1, placeholder.indexOf(')', bracket));
|
||||
}
|
||||
|
||||
final String name = placeholderName;
|
||||
Optional<String> optionalValue = placeholderValueSuppliers.getOrDefault(name, r -> Optional.of(name)).apply(expression);
|
||||
String value = optionalValue.orElse(defaultValue);
|
||||
StringBuilder sb = new StringBuilder(input);
|
||||
String output = sb.replace(start, end+1, value).toString();
|
||||
somethingReplaced = !Objects.equals(input, output);
|
||||
input = output;
|
||||
}
|
||||
}
|
||||
} while (somethingReplaced);
|
||||
return input;
|
||||
}
|
||||
|
||||
private CharSequence getSanitizedRecordingNotes(Recording rec) {
|
||||
return sanitize(ofNullable(rec.getNote()).orElse(""));
|
||||
private Optional<String> replaceUtcDateTime(Recording rec, String pattern) {
|
||||
return replaceDateTime(rec, pattern, ZoneOffset.UTC);
|
||||
}
|
||||
|
||||
private Optional<String> replaceLocalDateTime(Recording rec, String filename) {
|
||||
return replaceDateTime(rec, filename, ZoneId.systemDefault());
|
||||
}
|
||||
|
||||
private Optional<String> replaceDateTime(Recording rec, String pattern, ZoneId zone) {
|
||||
pattern = pattern != null ? pattern : "yyyy-MM-dd_HH-mm-ss";
|
||||
return getDateTime(rec, pattern, zone);
|
||||
}
|
||||
|
||||
private Optional<String> getDateTime(Recording rec, String pattern, ZoneId zone) {
|
||||
return Optional.ofNullable(rec.getStartDate()) //
|
||||
.map(DateTimeFormatter.ofPattern(pattern) //
|
||||
.withLocale(Locale.getDefault()) //
|
||||
.withZone(zone) //
|
||||
::format);
|
||||
}
|
||||
|
||||
private Optional<String> getFileSuffix(Recording rec) {
|
||||
if (rec.isSingleFile()) {
|
||||
String filename = rec.getPostProcessedFile().getName();
|
||||
return Optional.of(filename.substring(filename.lastIndexOf('.') + 1));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
private Optional<String> getSanitizedSiteName(Recording rec) {
|
||||
Optional<String> name = ofNullable(rec.getModel().getSite()).map(Site::getName);
|
||||
if (name.isPresent()) {
|
||||
return Optional.of(sanitize(name.get()));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
private Optional<String> getSanitizedRecordingNotes(Recording rec) {
|
||||
Optional<String> notes = ofNullable(rec.getNote());
|
||||
if (notes.isPresent()) {
|
||||
return Optional.of(sanitize(notes.get()));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
private Optional<String> getSanitizedModelNotes(Config config, Model m) {
|
||||
Optional<String> notes = ofNullable(config.getModelNotes(m));
|
||||
if (notes.isPresent()) {
|
||||
return Optional.of(sanitize(notes.get()));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -132,4 +132,22 @@ public class AbstractPlaceholderAwarePostProcessorTest extends AbstractPpTest {
|
|||
String input = "asdf_${modelNotes}_asdf";
|
||||
assertEquals("asdf_tag,_foo,_bar_asdf", placeHolderAwarePp.fillInPlaceHolders(input, createPostProcessingContext(rec, null, config)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPlaceholderDefaultValues() throws IOException {
|
||||
String input = "asdf_${modelGroupName?${modelSanitizedName?anonymous}}_asdf";
|
||||
PostProcessingContext ctx = createPostProcessingContext(rec, null, config);
|
||||
ctx.getRecording().getModel().setName(null);
|
||||
assertEquals("asdf_anonymous_asdf", placeHolderAwarePp.fillInPlaceHolders(input, ctx));
|
||||
|
||||
input = "asdf_${modelGroupName?${utcDateTime(yyyy)?anonymous}}_asdf";
|
||||
assertEquals("asdf_2021_asdf", placeHolderAwarePp.fillInPlaceHolders(input, ctx));
|
||||
|
||||
ctx.getRecording().setStartDate(null);
|
||||
input = "asdf_${modelGroupName?${utcDateTime(yyyy)?anonymous}}_asdf";
|
||||
assertEquals("asdf_anonymous_asdf", placeHolderAwarePp.fillInPlaceHolders(input, ctx));
|
||||
|
||||
input = "asdf_${modelGroupName?${utcDateTime?anonymous}}_asdf";
|
||||
assertEquals("asdf_anonymous_asdf", placeHolderAwarePp.fillInPlaceHolders(input, ctx));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue