001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions Copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2010-2011 ApexIdentity Inc.
015 * Portions Copyright 2011-2014 ForgeRock AS.
016 */
017
018package org.forgerock.openig.regex;
019
020import java.util.regex.MatchResult;
021
022/**
023 * Expresses a transformation to be applied to a regular expression pattern match. A template
024 * may contain references to groups captured in the match. Each occurrence of
025 * {@code $}<em>g</em> will be substituted by capture group <em>g</em> in a match result. A
026 * dollar sign or numeral literal immediately following a capture group reference may be
027 * included as a literal in the template by preceding it with a backslash ({@code \}).
028 * Backslash itself must be also escaped in this manner.
029 */
030public class PatternTemplate {
031
032    /** The transformation template to apply to regular expression pattern matches. */
033    private final String value;
034
035    /**
036     * Constructs a new template with the specified value.
037     *
038     * @param value the template to apply to regular expression pattern matches.
039     */
040    public PatternTemplate(final String value) {
041        this.value = value;
042    }
043
044    /**
045     * Performs a transformation of a match result by applying the template. References to
046     * matching groups that are not in the match result resolve to a blank ({@code ""}) value.
047     *
048     * @param result the match result to apply the template to.
049     * @return the value of the matching result with the template applied.
050     */
051    public String applyTo(final MatchResult result) {
052        int len = value.length();
053        int groups = result.groupCount();
054        StringBuilder sb = new StringBuilder();
055        boolean escaped = false;
056        for (int n = 0; n < len; n++) {
057            char c = value.charAt(n);
058            if (escaped) {
059                sb.append(c);
060                escaped = false;
061            } else if (c == '\\') {
062                escaped = true;
063            } else if (c == '$') {
064                int group = -1;
065                while (n + 1 < len) {
066                    int digit = value.charAt(n + 1) - '0';
067                    if (digit < 0 || digit > 9) {
068                        break;
069                    }
070                    // add digit
071                    group = (group == -1 ? 0 : group) * 10 + digit;
072                    n++;
073                }
074                if (group >= 0 && group <= groups) {
075                    sb.append(result.group(group));
076                }
077            } else {
078                sb.append(c);
079            }
080        }
081        return sb.toString();
082    }
083
084    /**
085     * Returns the literal template value.
086     * @return the literal template value.
087     */
088    @Override
089    public String toString() {
090        return value;
091    }
092}