/* * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package org.aitools.programd.util; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.log4j.Logger; /** * Provides substitution utilities for all classes. * * @author <a href="mailto:noel@aitools.org">Noel Bush</a> */ public class Substituter { private static final Logger aimlLogger = Logger.getLogger("programd.aiml-processing"); /** * Performs replacements specified by the <code>substitutionMap</code> in the given <code>input</code>. * * @param substitutionMap the map of substitutions to be performed * @param input the string on which to perform the replacement * @return the input with substitutions applied */ @SuppressWarnings("boxing") public static String applySubstitutions(Map<Pattern, String> substitutionMap, String input) { if (substitutionMap == null || input == null) { return input; } if (aimlLogger.isDebugEnabled()) { aimlLogger.debug(String.format("Applying %,d-element substituion map to input \"%s\".", substitutionMap.size(), input)); } // This will contain all pieces of the input untouched by substitution. List<String> untouchedPieces = Collections.checkedList(new LinkedList<String>(), String.class); untouchedPieces.add(input); // This will contain all replacements to be inserted in the result. LinkedList<String> replacements = new LinkedList<String>(); // Iterate over all substitutions. for (Pattern find : substitutionMap.keySet()) { Matcher matcher = null; // Iterate through all untouched pieces of the inputs. ListIterator<String> untouchedIterator = untouchedPieces.listIterator(0); while (untouchedIterator.hasNext()) { // Get the next untouched piece, and set up the matcher. String untouchedTest = untouchedIterator.next(); if (matcher != null) { matcher.reset(untouchedTest); } else { matcher = find.matcher(untouchedTest); } // Is the find string in the untouched input? We only look at the first match -- we'll get others later if (matcher.find()) { if (aimlLogger.isDebugEnabled()) { aimlLogger.debug(String.format("Matched \"%s\" in \"%s\".", find, untouchedTest)); } // If there is a match, replace the current untouched input with the // substring up to startIndex, int startIndex = matcher.start(); String newUntouched = untouchedTest.substring(0, startIndex); untouchedIterator.set(newUntouched); if (aimlLogger.isDebugEnabled()) { aimlLogger.debug(String.format("From \"%s\" leaving untouched \"%s\".", untouchedTest, newUntouched)); } // put the replacement text into the replacements list, String replacement = substitutionMap.get(find); replacements.add(untouchedIterator.nextIndex() - 1, replacement); if (aimlLogger.isDebugEnabled()) { aimlLogger.debug(String.format("Added \"%s\" to replacements list.", replacement)); } // and put the remainder of the untouched input into the // untouched list. String remainingUntouched = untouchedTest.substring(matcher.end()); untouchedIterator.add(remainingUntouched); untouchedIterator.previous(); if (aimlLogger.isDebugEnabled()) { aimlLogger.debug(String.format("Stored remaining untouched: \"%s\".", remainingUntouched)); } } } } // Now construct the result. StringBuilder result = new StringBuilder(); if (aimlLogger.isDebugEnabled()) { aimlLogger.debug(String.format("Constructing result using %d untouched piece(s) and %d replacement(s).", untouchedPieces.size(), replacements.size())); } // Iterate through the untouched pieces and the replacements. ListIterator<String> untouchedIterator = untouchedPieces.listIterator(0); ListIterator<String> replaceIterator = replacements.listIterator(0); while (untouchedIterator.hasNext()) { result.append(untouchedIterator.next()); // It can be that there is one less replacement than untouched // pieces. if (replaceIterator.hasNext()) { result.append(replaceIterator.next()); } } return result.toString(); } }