001package org.andromda.core.translation.library;
002
003import java.util.ArrayList;
004import java.util.Collection;
005import java.util.LinkedHashMap;
006import java.util.Map;
007import org.andromda.core.common.ExceptionUtils;
008import org.andromda.core.translation.TranslationUtils;
009import org.apache.commons.lang.StringUtils;
010import org.apache.commons.lang.builder.ToStringBuilder;
011
012/**
013 * Represents a translation XML template found within a translation library.
014 *
015 * @author Chad Brandon
016 */
017public class Translation
018{
019    private String name;
020    private final Map<String, Fragment> fragments = new LinkedHashMap<String, Fragment>();
021    private final Collection<String> ignorePatterns = new ArrayList<String>();
022
023    /**
024     * The library translation to which this translation belongs.
025     */
026    private LibraryTranslation libraryTranslation;
027
028    /**
029     * Gets the LibraryTranslation to which this Translation belongs.
030     *
031     * @return LibraryTranslation
032     */
033    protected LibraryTranslation getLibraryTranslation()
034    {
035        // should never happen, but it doesn't hurt to be safe
036        if (this.libraryTranslation == null)
037        {
038            throw new LibraryException("Translation.getLibraryTranslation - libraryTranslation can not be null");
039        }
040        return libraryTranslation;
041    }
042
043    /**
044     * Sets the LibraryTranslation to which this Translation belongs.
045     *
046     * @param translation the LibraryTranslation to which this Translation belongs.
047     */
048    protected void setLibraryTranslation(final LibraryTranslation translation)
049    {
050        libraryTranslation = translation;
051    }
052
053    /**
054     * Gets the fragment matching (using regular expressions) the specified name.
055     *
056     * @param name the name of the fragment to retrieve.
057     * @return Fragment
058     */
059    protected Fragment getFragment(final String name)
060    {
061        Fragment fragment = null;
062        // search through the names and the first name that matches
063        // one of the names return the value of that name.
064        for (String nextName : fragments.keySet())
065        {
066            if (name.matches(nextName))
067            {
068                fragment = fragments.get(nextName);
069            }
070        }
071
072        // if the fragment is null, and the name isn't in an ignorePattern
073        // element, then give an error
074        if (fragment == null && !this.isIgnorePattern(name))
075        {
076            // TODO: make this work correctly with unsupported functions.
077
078            /*
079             * logger.error("ERROR! expression fragment '" + name + "' is not
080             * currently supported --> add a <fragment/> with " + " a name that
081             * matches this expression to your translation file " + "'" +
082             * this.getLibraryTranslation().getFile() + "' to enable support");
083             */
084        }
085        return fragment;
086    }
087
088    /**
089     * Adds a new Translation fragment to the Translation.
090     *
091     * @param fragment new Translation fragment
092     */
093    public void addFragment(final Fragment fragment)
094    {
095        ExceptionUtils.checkNull(
096            "fragment",
097            fragment);
098        fragment.setTranslation(this);
099        this.fragments.put(
100            fragment.getName(),
101            fragment);
102    }
103
104    /**
105     * Gets the name of this Translation.
106     *
107     * @return String
108     */
109    protected String getName()
110    {
111        return name;
112    }
113
114    /**
115     * @param name
116     */
117    protected void setName(final String name)
118    {
119        this.name = name;
120    }
121
122    /**
123     * Adds an <code>ignorePattern</code> to the Collection of ignorePatterns.
124     *
125     * @param ignorePattern the pattern to ignore.
126     */
127    public void addIgnorePattern(final String ignorePattern)
128    {
129        this.ignorePatterns.add(StringUtils.trimToEmpty(ignorePattern));
130    }
131
132    /**
133     * Checks to see if the pattern is an ignore pattern. What this means is that if if this pattern matches on a
134     * regular expression found in the collection of ignore patterns then the TranslationLibrary won't complain if it
135     * doesn't match a fragment name.
136     *
137     * @param pattern
138     * @return boolean <code>true</code> if its an ignore pattern, <code>false</code> otherwise.
139     */
140    public boolean isIgnorePattern(String pattern)
141    {
142        boolean isIgnorePattern = false;
143        pattern = StringUtils.trimToEmpty(pattern);
144        // search through the ignorePatterns and see if one
145        // of them matches the passed in pattern.
146        for (String nextIgnorePattern : this.ignorePatterns)
147        {
148            isIgnorePattern = pattern.matches(StringUtils.trimToEmpty(nextIgnorePattern));
149            if (isIgnorePattern)
150            {
151                break;
152            }
153        }
154        return isIgnorePattern;
155    }
156
157    /**
158     * Gets the "translated" value of this Fragment if it exists. That is, it retrieves the fragment body for the name
159     * of this fragment and replaces any fragment references with other fragment bodies (if they exist)
160     *
161     * @param name the name of the fragment.
162     * @param kind the kind of the fragment.
163     * @return String the translated body of the fragment kind.
164     */
165    protected String getTranslated(
166        String name,
167        String kind)
168    {
169        // clean the strings first
170        name = StringUtils.trimToEmpty(name);
171        kind = StringUtils.trimToEmpty(kind);
172
173        ExceptionUtils.checkEmpty(
174            "name",
175            name);
176
177        Fragment fragment = this.getFragment(name);
178        String translated = "";
179        if (fragment != null)
180        {
181            translated = fragment.getKind(kind);
182            String begin = "fragment{";
183            int beginLength = begin.length();
184            String end = "}";
185            for (int beginIndex = translated.indexOf(begin); beginIndex != -1;
186                beginIndex = translated.indexOf(begin))
187            {
188                String fragmentName = translated.substring(
189                        beginIndex + beginLength,
190                        translated.length());
191                int endIndex = fragmentName.indexOf(end);
192                if (endIndex != -1)
193                {
194                    fragmentName = fragmentName.substring(
195                            0,
196                            endIndex);
197                }
198                StringBuilder toReplace = new StringBuilder(begin);
199                toReplace.append(fragmentName);
200                toReplace.append(end);
201                translated =
202                    StringUtils.replace(
203                        translated,
204                        toReplace.toString(),
205                        this.getTranslated(
206                            fragmentName,
207                            kind));
208            }
209        }
210        return TranslationUtils.removeExtraWhitespace(translated);
211    }
212
213    /**
214     * @see Object#toString()
215     */
216    public String toString()
217    {
218        return ToStringBuilder.reflectionToString(this);
219    }
220}