001package org.andromda.core.profile;
002
003import java.io.Serializable;
004import java.net.URL;
005import java.util.Collection;
006import java.util.LinkedHashMap;
007import java.util.Map;
008import org.andromda.core.common.ComponentContainer;
009import org.andromda.core.common.XmlObjectFactory;
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.apache.commons.lang.StringUtils;
015
016/**
017 * Represents an AndroMDA profile applied to a model.
018 * Profiles allow us to extend aspects of a model.
019 *
020 * @author Chad Brandon
021 * @author Bob Fields
022 */
023public class Profile
024    extends BaseNamespaceComponent
025    implements Serializable
026{
027    private static final long serialVersionUID = 34L;
028
029    /**
030     * The shared instance of this class.
031     */
032    private static Profile instance;
033
034    /**
035     * Gets the shared instance of this class.
036     *
037     * @return the shared instance.
038     */
039    public static Profile instance()
040    {
041        if (instance == null)
042        {
043            instance = new Profile();
044        }
045        return instance;
046    }
047
048    /**
049     * Stores the elements for the profile (by name).
050     */
051    private final Map<String, String> elements = new LinkedHashMap<String, String>();
052
053    /**
054     * Adds a new element to this namespace registry.
055     * @param name
056     * @param value
057     */
058    public void addElement(
059        final String name,
060        final String value)
061    {
062        this.elements.put(
063            name,
064            value);
065    }
066
067    /**
068     * Gets the profile value (if one is available)
069     * for the given name, otherwise returns name.
070     *
071     * @param name the profile name to retrieve.
072     * @return the value.
073     */
074    public String get(String name)
075    {
076        name = StringUtils.trim(name);
077        // - attempt to get the profile value from the profile defined
078        //   by the profile mappings uri first
079        String value = this.elements.get(name);
080
081        // - if we can't get any profile value from an the override profile
082        //   mapping, then we resort to the ones defined in the namespace
083        if (StringUtils.isBlank(value))
084        {
085            Map<String, String> namespaceElements = this.getNamespaceElements(this.getNamespace());
086            if (namespaceElements != null)
087            {
088                value = namespaceElements.get(name);
089            }
090            if (value == null)
091            {
092                namespaceElements = this.getNamespaceElements(Namespaces.DEFAULT);
093                if (namespaceElements != null)
094                {
095                    value = namespaceElements.get(name);
096                }
097            }
098        }
099        return value != null ? value : name;
100    }
101
102    /**
103     * Initializes this profile instance.
104     */
105    public void initialize()
106    {
107        final Collection<Profile> profiles = ComponentContainer.instance().findComponentsOfType(Profile.class);
108        for (final Profile profile : profiles)
109        {
110            String namespace = profile.getNamespace();
111            if (Namespaces.instance().isShared(namespace))
112            {
113                profile.setNamespace(Namespaces.DEFAULT);
114            }
115            this.addElements(profile);
116        }
117    }
118
119    /**
120     * Refreshes the profile instance.
121     */
122    public void refresh()
123    {
124        // - clear out the instance's elements
125        this.elements.clear();
126        try
127        {
128            final Property mappingsUri =
129                Namespaces.instance().getProperty(
130                    Namespaces.DEFAULT,
131                    NamespaceProperties.PROFILE_MAPPINGS_URI,
132                    false);
133            final String mappingsUriValue = mappingsUri != null ? mappingsUri.getValue() : null;
134            if (mappingsUriValue != null)
135            {
136                final XmlObjectFactory factory = XmlObjectFactory.getInstance(Profile.class);
137                final Profile profile = (Profile)factory.getObject(new URL(mappingsUriValue.trim()));
138                this.elements.putAll(profile.elements);
139            }
140        }
141        catch (final Throwable throwable)
142        {
143            throw new ProfileException(throwable);
144        }
145    }
146
147    /**
148     * Stores all elements.
149     */
150    private final Map<String, Map<String, String>> allElements = new LinkedHashMap<String, Map<String, String>>();
151
152    /**
153     * Adds the elements to the internal elements map.
154     * @param profile Profile
155     */
156    private void addElements(final Profile profile)
157    {
158        final Collection<String> elements = profile != null ? profile.elements.keySet() : null;
159        if (elements != null && profile != null)
160        {
161            final String namespace = profile.getNamespace();
162            final Map<String, String> namespaceElements = this.getNamespaceElements(namespace);
163            for (final String name : elements)
164            {
165                namespaceElements.put(
166                    name,
167                    profile.elements.get(name));
168            }
169        }
170    }
171
172    /**
173     * Adds a namespace element for the given namespace with the given name and
174     * value.
175     * @param namespace the namespace for which to add the namespace element.
176     * @param name the element name.
177     * @param value the element value.
178     */
179    public void addElement(
180        final String namespace,
181        final String name,
182        final String value)
183    {
184        final Map<String, String> namespaceElements = this.getNamespaceElements(namespace);
185        namespaceElements.put(
186            name,
187            value);
188    }
189
190    /**
191     * Retrieves the namespace elements map for the given namespace.
192     * If one doesn't exist, then a new one is created.
193     * @param namespace the namespace for which to retrieve the namespace elements
194     * @return the namespace element map
195     */
196    private Map<String, String> getNamespaceElements(final String namespace)
197    {
198        Map<String, String> namespaceElements = this.allElements.get(namespace);
199        if (namespaceElements == null)
200        {
201            namespaceElements = new LinkedHashMap<String, String>();
202            this.allElements.put(
203                namespace,
204                namespaceElements);
205        }
206        return namespaceElements;
207    }
208
209    /**
210     * Shuts down the shared instance and releases any used resources.
211     */
212    public void shutdown()
213    {
214        Profile.instance = null;
215    }
216}