001package org.andromda.core.common;
002
003import java.io.File;
004import java.net.URL;
005import java.util.ArrayList;
006import java.util.Collection;
007import java.util.LinkedHashMap;
008import java.util.List;
009import java.util.Map;
010import org.andromda.core.configuration.NamespaceProperties;
011import org.andromda.core.configuration.Namespaces;
012import org.andromda.core.configuration.Property;
013import org.andromda.core.namespace.BaseNamespaceComponent;
014import org.andromda.core.templateengine.TemplateEngine;
015import org.apache.log4j.Logger;
016
017/**
018 * Represents the base plugin of AndroMDA. All Plugin instances inherit from this class.
019 *
020 * @author Chad Brandon
021 */
022public abstract class BasePlugin
023    extends BaseNamespaceComponent
024    implements Plugin
025{
026    /**
027     * Property references made available to the plugin
028     */
029    private final Collection<String> propertyReferences = new ArrayList<String>();
030
031    /**
032     * The template objects made available to templates of this BasePlugin.
033     */
034    private final Collection<TemplateObject> templateObjects = new ArrayList<TemplateObject>();
035
036    /**
037     * @see org.andromda.core.common.Plugin#initialize()
038     */
039    public void initialize()
040        throws Exception
041    {
042        // set the template engine merge location (this needs to be
043        // set before the template engine is initialized) so that the
044        // merge property can be set once on the template engine.
045        final Property mergeProperty =
046            Namespaces.instance().getProperty(
047                this.getNamespace(),
048                NamespaceProperties.MERGE_LOCATION,
049                false);
050        this.mergeLocation = mergeProperty != null ? new File(mergeProperty.getValue()).toURI().toURL() : null;
051        if (this.mergeLocation != null)
052        {
053            this.getTemplateEngine().setMergeLocation(this.getMergeLocation().getFile());
054        }
055        this.getTemplateEngine().initialize(this.getNamespace());
056        for (final TemplateObject templateObject : this.templateObjects) {
057            templateObject.setResource(this.getResource());
058            templateObject.setNamespace(this.getNamespace());
059        }
060    }
061
062    /**
063     * The current cartridge merge location.
064     */
065    private URL mergeLocation;
066
067    /**
068     * Gets the current merge location for this plugin.
069     *
070     * @return the merge location (a file path).
071     */
072    protected URL getMergeLocation()
073    {
074        return this.mergeLocation;
075    }
076
077    /**
078     * @see org.andromda.core.common.Plugin#shutdown()
079     */
080    public void shutdown()
081    {
082        this.getTemplateEngine().shutdown();
083    }
084
085    /**
086     * Adds the <code>templateObject</code> to the collection of template objects that will be made available to the
087     * plugin during processing.
088     *
089     * @param templateObject the TemplateObject to add.
090     */
091    public void addTemplateObject(final TemplateObject templateObject)
092    {
093        if (templateObject != null)
094        {
095            this.templateObjects.add(templateObject);
096        }
097    }
098
099    /**
100     * Adds a macro library to the TemplateEngine used by this BasePlugin.
101     *
102     * @param macrolibrary
103     */
104    public void addMacrolibrary(final String macrolibrary)
105    {
106        this.getTemplateEngine().addMacroLibrary(macrolibrary);
107    }
108
109    /**
110     * @see org.andromda.core.common.Plugin#getTemplateObjects()
111     */
112    public Collection<TemplateObject> getTemplateObjects()
113    {
114        return this.templateObjects;
115    }
116
117    private String templateEngineClass;
118
119    /**
120     * Sets the template engine class for this cartridge.
121     *
122     * @param templateEngineClass the Class of the template engine implementation.
123     */
124    public void setTemplateEngineClass(final String templateEngineClass)
125    {
126        this.templateEngineClass = templateEngineClass;
127    }
128
129    /**
130     * The template engine that this plugin will use.
131     */
132    private TemplateEngine templateEngine = null;
133
134    /**
135     * @see org.andromda.core.common.Plugin#getTemplateEngine()
136     */
137    public TemplateEngine getTemplateEngine()
138    {
139        if (this.templateEngine == null)
140        {
141            this.templateEngine =
142                (TemplateEngine)ComponentContainer.instance().newComponent(
143                    this.templateEngineClass, TemplateEngine.class);
144        }
145        return this.templateEngine;
146    }
147
148    /**
149     * @see org.andromda.core.common.Plugin#getPropertyReferences()
150     */
151    public String[] getPropertyReferences()
152    {
153        return this.propertyReferences.toArray(new String[this.propertyReferences.size()]);
154    }
155
156    /**
157     * Adds a property reference. Property references are those properties that are expected to be supplied by the
158     * calling client. These supplied properties are made available to the template during processing.
159     *
160     * @param reference the namespace of the reference.
161     */
162    public void addPropertyReference(final String reference)
163    {
164        this.propertyReferences.add(reference);
165    }
166
167    /**
168     * Populates the <code>templateContext</code> with the properties and template objects defined in the
169     * <code>plugin</code>'s descriptor. If the <code>templateContext</code> is null, a new Map instance will be created
170     * before populating the context.
171     *
172     * @param templateContext the context of the template to populate.
173     */
174    protected void populateTemplateContext(Map<String, Object> templateContext)
175    {
176        if (templateContext == null)
177        {
178            templateContext = new LinkedHashMap<String, Object>();
179        }
180        this.addTemplateObjectsToContext(templateContext);
181        this.addPropertyReferencesToContext(templateContext);
182    }
183
184    /**
185     * Takes all the template objects defined in the plugin's descriptor and places them in the
186     * <code>templateContext</code>.
187     *
188     * @param templateContext the template context
189     */
190    private void addTemplateObjectsToContext(final Map<String, Object> templateContext)
191    {
192        // add all the TemplateObject objects to the template context
193        final Collection<TemplateObject> templateObjects = this.getTemplateObjects();
194        if (templateObjects != null && !templateObjects.isEmpty())
195        {
196            for (TemplateObject templateObject : templateObjects)
197            {
198                templateContext.put(
199                    templateObject.getName(),
200                    templateObject.getObject());
201            }
202        }
203    }
204
205    /**
206     * Takes all the property references defined in the plugin's descriptor and looks up the corresponding values
207     * supplied by the calling client and supplies them to the <code>templateContext</code>.
208     *
209     * @param templateContext the template context
210     */
211    private void addPropertyReferencesToContext(final Map<String, Object> templateContext)
212    {
213        final String[] propertyReferences = this.getPropertyReferences();
214        if (propertyReferences != null && propertyReferences.length > 0)
215        {
216            final Namespaces namespaces = Namespaces.instance();
217            for (final String reference : propertyReferences) {
218                templateContext.put(
219                        reference,
220                        namespaces.getPropertyValue(
221                                this.getNamespace(),
222                                reference));
223            }
224        }
225    }
226
227    /**
228     * Stores the contents of the plugin.
229     */
230    private List<String> contents = null;
231
232    /**
233     * @see org.andromda.core.common.Plugin#getContents()
234     */
235    public List<String> getContents()
236    {
237        if (this.contents == null)
238        {
239            if (ResourceUtils.isArchive(this.getResource()))
240            {
241                this.contents = ResourceUtils.getClassPathArchiveContents(this.getResource());
242                if (this.getMergeLocation() != null)
243                {
244                    final Collection<String> mergeContents = ResourceUtils.getDirectoryContents(
245                            this.getMergeLocation(),
246                            0);
247                    if (mergeContents != null && !mergeContents.isEmpty())
248                    {
249                        this.contents.addAll(mergeContents);
250                    }
251                }
252            }
253            else
254            {
255                // we step down 1 level if its a directory (instead of an
256                // archive since we get the contents relative to the plugin
257                // resource which is in the META-INF directory
258                this.contents = ResourceUtils.getDirectoryContents(
259                        this.getResource(),
260                        2);
261            }
262        }
263        return contents;
264    }
265
266    /**
267     * Retrieves the logger instance that should be used for logging output for the plugin sub classes.
268     *
269     * @return the logger.
270     */
271    protected Logger getLogger()
272    {
273        return AndroMDALogger.getNamespaceLogger(this.getNamespace());
274    }
275
276    /**
277     * @see Object#toString()
278     */
279    public String toString()
280    {
281        return super.toString() + '[' + this.getNamespace() + ']';
282    }
283}