package com.supaham.commons.placeholders; import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.base.Function; import java.util.Collection; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.annotation.Nonnull; /** * Represents a {@link Function} implementation for placeholder strings replacement task. * * @since 0.1 */ public abstract class PlaceholderFunction implements Function<PlaceholderData, String> { /** * This represents the pattern <b>{@code ([{]+.[^{}]+[}]+)}</b>. The following String is an * example that would get matched by this pattern. Anything formatted in bold is what would be * matched in the actual example. * <pre> * Hi, my name is <b>{name}</b>. I live in <b>{city}</b>, but was born in <b>{birthplace}</b>. * </pre> */ public final static Pattern PH_PATTERN = Pattern.compile("([{]+.[^{}]+[}]+)"); /** * Gets a {@link Collection} of {@link Placeholder}s to use when calling * {@link #apply(PlaceholderData)}. * * @return collection of placeholders */ public abstract Collection<? extends Placeholder> getPlaceholders(); /** * Performs a placeholder replacing task. This method utilizes {@link #getPlaceholders()} for * passing the matched placeholders to them. This method uses the {@link #PH_PATTERN} for matching * placeholders. Please keep in mind that {@link Placeholder#apply(PlaceholderData)} will not * receive the braces, e.g. <em>{abc}</em> is given as <em>abc</em> * * @param data data to apply this function to * * @return the placeholder replaced string */ @Nonnull @Override public String apply(PlaceholderData data) { checkNotNull(data, "data cannot be null."); Collection<? extends Placeholder> placeholders = getPlaceholders(); int index = 0; String input = data.getOriginal(); Matcher matcher = PH_PATTERN.matcher(input); // Every matched placeholder while (matcher.find()) { String origPhStr = matcher.group(); // Remove the braces. data.setPlaceholder(origPhStr.substring(1, origPhStr.length() - 1)); boolean found = false; // Ask each Placeholder to handle the matched placeholder string until one Placeholder has. for (Placeholder placeholder : placeholders) { String match = placeholder.apply(data); // Match is null when the placeholder has not applied anything to it. if (match != null) { data.setPlaceholder(match); found = true; break; } } // Append since the last index till the current matched starting index, then append the // placeholder string. Finally, update the index to the end of the matched placeholder string. data.append(input.substring(index, matcher.start()) + (found ? data.getPlaceholder() : origPhStr)); index = matcher.end(); } // If there's still input after the last matched, append it all to result. if (index < input.length()) { data.append(input.substring(index, input.length())); } // Let's notify the placeholders that we're done for (Placeholder placeholder : placeholders) { placeholder.onComplete(data.getString()); } return data.getString(); } }