Translation.java

package org.andromda.core.translation.library;

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import org.andromda.core.common.ExceptionUtils;
import org.andromda.core.translation.TranslationUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.ToStringBuilder;

/**
 * Represents a translation XML template found within a translation library.
 *
 * @author Chad Brandon
 */
public class Translation
{
    private String name;
    private final Map<String, Fragment> fragments = new LinkedHashMap<String, Fragment>();
    private final Collection<String> ignorePatterns = new ArrayList<String>();

    /**
     * The library translation to which this translation belongs.
     */
    private LibraryTranslation libraryTranslation;

    /**
     * Gets the LibraryTranslation to which this Translation belongs.
     *
     * @return LibraryTranslation
     */
    protected LibraryTranslation getLibraryTranslation()
    {
        // should never happen, but it doesn't hurt to be safe
        if (this.libraryTranslation == null)
        {
            throw new LibraryException("Translation.getLibraryTranslation - libraryTranslation can not be null");
        }
        return libraryTranslation;
    }

    /**
     * Sets the LibraryTranslation to which this Translation belongs.
     *
     * @param translation the LibraryTranslation to which this Translation belongs.
     */
    protected void setLibraryTranslation(final LibraryTranslation translation)
    {
        libraryTranslation = translation;
    }

    /**
     * Gets the fragment matching (using regular expressions) the specified name.
     *
     * @param name the name of the fragment to retrieve.
     * @return Fragment
     */
    protected Fragment getFragment(final String name)
    {
        Fragment fragment = null;
        // search through the names and the first name that matches
        // one of the names return the value of that name.
        for (String nextName : fragments.keySet())
        {
            if (name.matches(nextName))
            {
                fragment = fragments.get(nextName);
            }
        }

        // if the fragment is null, and the name isn't in an ignorePattern
        // element, then give an error
        if (fragment == null && !this.isIgnorePattern(name))
        {
            // TODO: make this work correctly with unsupported functions.

            /*
             * logger.error("ERROR! expression fragment '" + name + "' is not
             * currently supported --> add a <fragment/> with " + " a name that
             * matches this expression to your translation file " + "'" +
             * this.getLibraryTranslation().getFile() + "' to enable support");
             */
        }
        return fragment;
    }

    /**
     * Adds a new Translation fragment to the Translation.
     *
     * @param fragment new Translation fragment
     */
    public void addFragment(final Fragment fragment)
    {
        ExceptionUtils.checkNull(
            "fragment",
            fragment);
        fragment.setTranslation(this);
        this.fragments.put(
            fragment.getName(),
            fragment);
    }

    /**
     * Gets the name of this Translation.
     *
     * @return String
     */
    protected String getName()
    {
        return name;
    }

    /**
     * @param name
     */
    protected void setName(final String name)
    {
        this.name = name;
    }

    /**
     * Adds an <code>ignorePattern</code> to the Collection of ignorePatterns.
     *
     * @param ignorePattern the pattern to ignore.
     */
    public void addIgnorePattern(final String ignorePattern)
    {
        this.ignorePatterns.add(StringUtils.trimToEmpty(ignorePattern));
    }

    /**
     * Checks to see if the pattern is an ignore pattern. What this means is that if if this pattern matches on a
     * regular expression found in the collection of ignore patterns then the TranslationLibrary won't complain if it
     * doesn't match a fragment name.
     *
     * @param pattern
     * @return boolean <code>true</code> if its an ignore pattern, <code>false</code> otherwise.
     */
    public boolean isIgnorePattern(String pattern)
    {
        boolean isIgnorePattern = false;
        pattern = StringUtils.trimToEmpty(pattern);
        // search through the ignorePatterns and see if one
        // of them matches the passed in pattern.
        for (String nextIgnorePattern : this.ignorePatterns)
        {
            isIgnorePattern = pattern.matches(StringUtils.trimToEmpty(nextIgnorePattern));
            if (isIgnorePattern)
            {
                break;
            }
        }
        return isIgnorePattern;
    }

    /**
     * Gets the "translated" value of this Fragment if it exists. That is, it retrieves the fragment body for the name
     * of this fragment and replaces any fragment references with other fragment bodies (if they exist)
     *
     * @param name the name of the fragment.
     * @param kind the kind of the fragment.
     * @return String the translated body of the fragment kind.
     */
    protected String getTranslated(
        String name,
        String kind)
    {
        // clean the strings first
        name = StringUtils.trimToEmpty(name);
        kind = StringUtils.trimToEmpty(kind);

        ExceptionUtils.checkEmpty(
            "name",
            name);

        Fragment fragment = this.getFragment(name);
        String translated = "";
        if (fragment != null)
        {
            translated = fragment.getKind(kind);
            String begin = "fragment{";
            int beginLength = begin.length();
            String end = "}";
            for (int beginIndex = translated.indexOf(begin); beginIndex != -1;
                beginIndex = translated.indexOf(begin))
            {
                String fragmentName = translated.substring(
                        beginIndex + beginLength,
                        translated.length());
                int endIndex = fragmentName.indexOf(end);
                if (endIndex != -1)
                {
                    fragmentName = fragmentName.substring(
                            0,
                            endIndex);
                }
                StringBuilder toReplace = new StringBuilder(begin);
                toReplace.append(fragmentName);
                toReplace.append(end);
                translated =
                    StringUtils.replace(
                        translated,
                        toReplace.toString(),
                        this.getTranslated(
                            fragmentName,
                            kind));
            }
        }
        return TranslationUtils.removeExtraWhitespace(translated);
    }

    /**
     * @see Object#toString()
     */
    public String toString()
    {
        return ToStringBuilder.reflectionToString(this);
    }
}