001package org.andromda.translation.ocl.query;
002
003import java.util.HashMap;
004import java.util.Map;
005import org.andromda.core.translation.TranslationUtils;
006import org.apache.commons.lang.StringUtils;
007
008/**
009 * Performs translation to the following: <ul> <li>EJB-QL</li> </ul>
010 *
011 * @author Chad Brandon
012 */
013public class EjbQLTranslator
014        extends QueryTranslator
015{
016    /**
017     * Used to replace the 'counter' reference in the EJB-QL template
018     */
019    private static final String ARG_COUNTER = "counter";
020
021    /**
022     * Keeps track of an incrementing argument number.
023     */
024    private short argCounter;
025
026    /**
027     * Holds the arguments which have previously been used during translation. The key is the argument name BEFORE
028     * translation, and the value is the argument name AFTER translation.
029     */
030    private Map<String, String> usedArguments = new HashMap<String, String>();
031
032    /**
033     * Called by super class to reset any objects.
034     */
035    public void preProcess()
036    {
037        super.preProcess();
038        this.usedArguments.clear();
039        this.resetArgCounter();
040    }
041
042    /**
043     * Resets the argCounter variable to its beginning value.
044     */
045    private void resetArgCounter()
046    {
047        this.argCounter = 1;
048    }
049
050    /**
051     * Returns a String representing an incrementing number. It increments and returns the next value each time this
052     * method is called.
053     *
054     * @return String the counter represented by a String.
055     */
056    protected String getCounter()
057    {
058        return String.valueOf(argCounter++);
059    }
060
061    /**
062     * Checks to see if the replacement is an argument and if so replaces the {index} in the fragment with the
063     * 'argument' fragment from the template. Otherwise replaces the {index} with the passed in replacement value.
064     *
065     * @param fragment
066     * @param replacement
067     * @param index
068     * @return String the fragment with any replacements.
069     */
070    protected String replaceFragment(String fragment, String replacement, int index)
071    {
072        if (this.isOperationArgument(replacement))
073        {
074            // get the used argument and if it exists, use that for the
075            // replacement, otherwise use a new one.
076            String usedArgument = this.usedArguments.get(replacement);
077            if (StringUtils.isEmpty(usedArgument))
078            {
079                String argument = this.getTranslationFragment("argument");
080                argument = this.replaceCounterPattern(argument);
081                this.usedArguments.put(replacement, argument);
082                replacement = argument;
083            }
084            else
085            {
086                replacement = usedArgument;
087            }
088        }
089        return super.replaceFragment(fragment, replacement, index);
090    }
091
092    /**
093     * Handles the replacement of the references to 'counter' with the incrementing counter (currently just used for
094     * EJB-QL translation) --> may want to find a cleaner way to do this.
095     * @param fragment
096     * @return fragment
097     */
098    protected String replaceCounterPattern(String fragment)
099    {
100        if (TranslationUtils.containsPattern(fragment, EjbQLTranslator.ARG_COUNTER))
101        {
102            fragment = TranslationUtils.replacePattern(fragment, EjbQLTranslator.ARG_COUNTER, this.getCounter());
103        }
104        return fragment;
105    }
106}