001package org.andromda.core.common;
002
003import java.net.URL;
004import java.util.ArrayList;
005import java.util.Collection;
006import java.util.HashMap;
007import java.util.Map;
008import org.andromda.core.configuration.Namespaces;
009import org.apache.commons.lang.StringUtils;
010import org.apache.commons.lang.builder.ToStringBuilder;
011import org.apache.log4j.Logger;
012
013/**
014 * Contains the configuration of a template object which are objects that are
015 * made available to the cartridge templates.
016 *
017 * @author Chad Brandon
018 * @author Bob Fields
019 */
020public class TemplateObject
021{
022    private static final Logger logger = Logger.getLogger(TemplateObject.class);
023
024    /**
025     * The name of this template object made available to the
026     * template.
027     */
028    private String name;
029
030    /**
031     * Gets the current name of this TemplateObject.
032     *
033     * @return String
034     */
035    public String getName()
036    {
037        final String methodName = "TemplateObject.getName";
038        if (StringUtils.isEmpty(this.name))
039        {
040            throw new TemplateObjectException(methodName + " - templateObject '" + this + "' has no name defined");
041        }
042        return this.name;
043    }
044
045    /**
046     * Caches the template objects.
047     */
048    private final Map<String, Object> objectCache = new HashMap<String, Object>();
049
050    /**
051     * Defines the class should be used in a static way
052     */
053    private static final String STATIC_SUFFIX = ".class";
054    
055    /**
056     * Returns the actuall object instance described by this
057     * template object.
058     *
059     * @return the actual object instance.
060     */
061    public Object getObject()
062    {
063        final String methodName = "TemplateObject.getTemplateObject";
064        if (StringUtils.isEmpty(this.className))
065        {
066            throw new TemplateObjectException(methodName + " - templateObject '" + this + "' has no className defined");
067        }
068        Object templateObject = this.objectCache.get(this.className);
069        try
070        {
071            if (templateObject == null)
072            {
073                final Class templateObjectClass;
074                if(this.className.endsWith(STATIC_SUFFIX))
075                {
076                    templateObjectClass = ClassUtils.loadClass(StringUtils.substringBefore(this.className, STATIC_SUFFIX));
077                    templateObject = templateObjectClass; 
078                }
079                else
080                {
081                    templateObjectClass = ClassUtils.loadClass(this.className);
082                    templateObject = templateObjectClass.newInstance();
083                }
084                this.objectCache.put(this.className, templateObject);
085            }
086
087            // - we want to set the properties each time we retrieve (in case they've changed)
088            this.setProperties(templateObject);
089        }
090        catch (final Throwable throwable)
091        {
092            throw new TemplateObjectException(throwable);
093        }
094        return templateObject;
095    }
096
097    /**
098     * Sets all the nested properties on the templateObject object.
099     *
100     * @param templateObject the template object on which to populate properties.
101     */
102    protected void setProperties(final Object templateObject)
103    {
104        for (final String reference : propertyReferences)
105        {
106            String value = Namespaces.instance().getPropertyValue(
107                    this.getNamespace(),
108                    reference);
109            if (value != null)
110            {
111                if (this.getLogger().isDebugEnabled())
112                {
113                    this.getLogger().debug(
114                        "populating template object '" + this.name + "' property '" + reference + "' with value '" +
115                        value + "' for namespace '" + namespace + '\'');
116                }
117                try
118                {
119                    Introspector.instance().setProperty(templateObject, reference, value);
120                }
121                catch (final Exception exception)
122                {
123                    // - don't throw the exception
124                    final String message =
125                        "Error setting property '" + reference + "' with '" + value + "' on templateObject --> '" +
126                        templateObject + '\'';
127                    logger.warn(message);
128                }
129            }
130        }
131    }
132
133    /**
134     * Sets the name of the template object (this name will be what the template class is stored under in the template)
135     *
136     * @param name the name of the template object.
137     */
138    public void setName(final String name)
139    {
140        this.name = StringUtils.trimToEmpty(name);
141    }
142
143    /**
144     * The name of the class for this template object.
145     */
146    private String className;
147
148    /**
149     * Sets the class of the transformation object.
150     *
151     * @param className the name of the template object class.
152     */
153    public void setClassName(final String className)
154    {
155        ExceptionUtils.checkEmpty("className", className);
156        this.className = className;
157    }
158
159    /**
160     * The property references that configure this template object.
161     */
162    private final Collection<String> propertyReferences = new ArrayList<String>();
163
164    /**
165     * Adds a templateObject property reference (used to customize templateObjects). Property references are used to
166     * populate bean like properties of template objects.
167     *
168     * @param reference the name of the property reference.
169     */
170    public void addPropertyReference(final String reference)
171    {
172        this.propertyReferences.add(reference);
173    }
174
175    /**
176     * The resource in which the template object was found.
177     */
178    private URL resource;
179
180    /**
181     * The resource in which the templateObject was found.
182     *
183     * @return the resource as a URL.
184     */
185    public URL getResource()
186    {
187        return resource;
188    }
189
190    /**
191     * Sets the resource in which the templateObject was defined.
192     *
193     * @param resource the resource on which this template object was defined.
194     */
195    public void setResource(final URL resource)
196    {
197        this.resource = resource;
198    }
199
200    /**
201     * The namespace to which this template object belongs.
202     */
203    private String namespace;
204
205    /**
206     * Gets the namespace to which this template object belongs.
207     *
208     * @return Returns the namespace.
209     */
210    public String getNamespace()
211    {
212        return namespace;
213    }
214
215    /**
216     * Sets the namespace to which this template object belongs.
217     *
218     * @param namespace The namespace to set.
219     */
220    public void setNamespace(final String namespace)
221    {
222        this.namespace = StringUtils.trimToEmpty(namespace);
223    }
224
225    /**
226     * Gets the namespace logger (the logger under which output for this
227     * template object should be written).
228     *
229     * @return the logger instance.
230     */
231    protected Logger getLogger()
232    {
233        return AndroMDALogger.getNamespaceLogger(this.namespace);
234    }
235
236    /**
237     * @see Object#toString()
238     */
239    public String toString()
240    {
241        return ToStringBuilder.reflectionToString(this);
242    }
243}