org.klojang.templates.TemplateUtils Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of klojang-templates Show documentation
Show all versions of klojang-templates Show documentation
Zero-Logic Templates for Java Programmers
The newest version!
package org.klojang.templates;
import org.klojang.check.Check;
import org.klojang.check.Tag;
import org.klojang.collections.WiredList;
import org.klojang.path.Path;
import org.klojang.templates.x.MTag;
import org.klojang.util.Tuple2;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
import static java.util.Arrays.copyOfRange;
import static java.util.stream.Collectors.toList;
import static org.klojang.check.CommonChecks.empty;
import static org.klojang.check.CommonChecks.in;
import static org.klojang.templates.x.Messages.ERR_BAD_NAME;
import static org.klojang.templates.x.Messages.ERR_NO_SUCH_TEMPLATE;
import static org.klojang.util.CollectionMethods.implode;
import static org.klojang.util.StringMethods.count;
import static org.klojang.util.StringMethods.substringBefore;
/**
* Utility class extending the functionality of the {@link Template} class.
*
* @author Ayco Holleman
*/
public final class TemplateUtils {
private TemplateUtils() {
throw new UnsupportedOperationException();
}
/**
* Returns the fully-qualified name of the specified template, relative to the root
* template. If the template is the root template,
* {@link Template#ROOT_TEMPLATE_NAME} is returned. Otherwise the fully-qualified name
* is the {@linkplain org.klojang.path.Path path} from the specified template to the
* root template, in reverse direction.
*
* {@code
* String src = """
* ~%%begin:companies%
* ~%foo%
* ~%%begin:departments%
* ~%bar%
* ~%%end:departments%
* ~%%end:companies%
* """;
* Template t = Template.fromString(src);
* System.out.println(TemplateUtils.getFQN(t); // "{root}"
* t = t.getNestedTemplate("companies");
* System.out.println(TemplateUtils.getFQN(t); // "companies"
* t = t.getNestedTemplate("departments");
* System.out.println(TemplateUtils.getFQN(t); // "companies.departments"
* }
*
* @param template the template for which to retrieve the fully-qualified name
* @return the fully-qualified name of the template
*/
public static String getFQN(Template template) {
Check.notNull(template);
if (template.getParent() == null) {
return template.getName();
}
WiredList wl = new WiredList<>();
for (Template t = template; t.getParent() != null; t = t.getParent()) {
wl.prepend(t.getName());
}
return implode(wl, ".");
}
/**
* Returns the fully-qualified name of the specified name. The provided name supposedly
* is the name of a variable or nested template inside the specified template. If the
* specified template is the root template, this method simply returns the name as-is.
* Otherwise it prefixes the template's FQN (and a dot character) to the name.
*
* @param template the template for which to retrieve the fully-qualified name
* @param name the name of a template variable or nested template
* @return its fully-qualified name
*/
public static String getFQN(Template template, String name) {
Check.notNull(template, MTag.TEMPLATE);
Check.notNull(name, Tag.NAME);
return (template.getParent() == null) ? name : getFQN(template) + '.' + name;
}
/**
* Returns a depth-first view of all variable occurrences within the specified template.
* The returned {@code List} is created on demand and modifiable.
*
* @param template the template to collect the variable occurrences from
* @return a depth-first view of all variable occurrences within the specified template
*/
public static List getAllVariableOccurrences(Template template) {
Check.notNull(template);
ArrayList list = new ArrayList<>();
collectOccurrences(template, list);
return list;
}
private static void collectOccurrences(Template template,
ArrayList list) {
for (Part part : template.parts()) {
if (part instanceof VariablePart vp) {
list.add(vp.toOccurrence());
} else if (part instanceof NestedTemplatePart ntp) {
collectOccurrences(ntp.getTemplate(), list);
}
}
}
/**
* Returns the specified template and all templates descending from it. The specified
* template will come first in de returned list and the descendant templates are
* retrieved in breadth-first order. The returned {@code List} is created on demand and
* modifiable.
*
* @param template the template
* @return a {@code List} containing the {@code Template} and its descendants
*/
public static List getTemplateHierarchy(Template template) {
Check.notNull(template);
ArrayList tmpls = new ArrayList<>();
tmpls.add(template);
collectTemplates(template, tmpls);
return tmpls;
}
private static void collectTemplates(Template t0, ArrayList tmpls) {
tmpls.addAll(t0.getNestedTemplates());
t0.getNestedTemplates().forEach(t -> collectTemplates(t, tmpls));
}
/**
* Returns the nested template corresponding to the specified fully-qualified name.
* Contrary to {@link Template#getNestedTemplate(String) Template.getNestedTemplate()}
* this method lets you retrieve deeply nested templates. The fully-qualified name must
* be relative to the specified template and must not start with the specified
* template's name itself.
*
* @param template the template containing the (deeply) nested template
* @param FQN the fully qualified name of the nested template
* @return The (possibly deeply) nested template corresponding to the specified
* fully-qualified name
*/
public static Template getNestedTemplate(Template template, String FQN) {
Check.notNull(template, MTag.TEMPLATE);
Check.that(FQN, MTag.FQN).isNot(empty());
return getNestedTemplate(template, FQN, FQN.split("\\."));
}
private static Template getNestedTemplate(Template t0,
String FQN,
String[] names) {
if (names.length == 0) {
return t0;
}
Check.that(names[0]).is(in(), t0.getNestedTemplateNames(),
ERR_NO_SUCH_TEMPLATE, FQN);
t0 = t0.getNestedTemplate(names[0]);
return getNestedTemplate(t0, FQN, copyOfRange(names, 1, names.length));
}
/**
* Returns the template that directly contains the variable or nested template denoted
* by the specified fully-qualified name. The specified template is supposed to be the
* root template, or a template at some higher level than the template containing the
* variable. The FQName must be relative to the specified template and it must not
* include the specified template's name.
*
* @param template the template relative to which the fully-qualified name should
* be taken
* @param FQN the fully-qualified name
* @return The template containing the variable or nested template denoted by the
* specified fully-qualified name
*/
public static Template getContainingTemplate(Template template, String FQN) {
Check.notNull(template, MTag.TEMPLATE);
Check.that(FQN, MTag.FQN).isNot(empty());
if (count(FQN, ".") == 0) {
Check.that(FQN).is(in(), template.getNames(), ERR_BAD_NAME, FQN);
return template;
}
return getNestedTemplate(template, substringBefore(FQN, ".", 1));
}
/**
* Returns the names of all variables in the specified template and all templates
* descending from the specified template. Each tuple in the returned {@code List}
* contains a {@code Template} instance and a variable name. The {@code Template}
* instance is either the specified template itself or one of its descendants. The
* returned {@code List} is created on demand and modifiable.
*
* @param template the template for which to retrieve the variable names
* @return all variable names in this {@code Template} and the templates nested inside
* it
*/
public static List> getAllVariables(Template template) {
Check.notNull(template, MTag.TEMPLATE);
ArrayList> tuples = new ArrayList<>();
collectVars(template, tuples);
return tuples;
}
private static void collectVars(Template t0,
ArrayList> tuples) {
t0.getVariables().stream().map(s -> Tuple2.of(t0, s)).forEach(tuples::add);
t0.getNestedTemplates().forEach(t -> collectVars(t, tuples));
}
/**
* Returns the fully-qualified names of all variables in the specified template and all
* templates descending from the specified template. The names are fully-qualified
* relative to the root template of the specified template (not to the specified
* template itself).
*
* @param template the template for which to retrieve the variable names
* @return the fully-qualified variable names in this {@code Template} and the templates
* nested inside it
*/
public static List getAllVariableFQNames(Template template) {
Check.notNull(template, MTag.TEMPLATE);
ArrayList fqns = new ArrayList<>();
collectFQNs(template, fqns);
return fqns;
}
static void collectFQNs(Template t0, ArrayList vars) {
t0.getVariables().stream().map(s -> getFQN(t0, s)).forEach(vars::add);
t0.getNestedTemplates().forEach(t -> collectFQNs(t, vars));
}
/**
* Returns the fully-qualified names of all variables in the specified template and all
* templates descending from the specified template.
*
* @param template the template for which to retrieve the variable names
* @param relative if {@code true}, the names are fully-qualified relative to the
* specified template, otherwise relative to the root template of the specified
* template
* @return the fully-qualified variable names in this {@code Template} and the templates
* nested inside it
*/
public static List getAllVariableFQNames(Template template,
boolean relative) {
if (relative) {
Check.notNull(template, MTag.TEMPLATE);
ArrayList paths = new ArrayList<>();
collectFQNs(template, paths, Path.empty());
return paths.stream().map(Path::toString).collect(toList());
}
return getAllVariableFQNames(template);
}
static void collectFQNs(Template t0, ArrayList vars, Path path) {
t0.getVariables().stream().map(path::append).forEach(vars::add);
t0.getNestedTemplates().forEach(
t -> collectFQNs(t, vars, path.append(t.getName())));
}
/**
* Prints out the constituent parts of the specified {@code Template}. Can be used for
* debugging purposes.
*
* @param template the {@code Template} whose parts to print
*/
public static void printParts(Template template) {
printParts(template, System.out);
}
/**
* Prints out the constituent parts of the specified {@code Template}. Can be used for
* debugging purposes.
*
* @param template the {@code Template} whose parts to print
* @param out the {@code PrintStream} to which to print
*/
public static void printParts(Template template, PrintStream out) {
new PartsPrinter(template).printParts(out);
}
}