View Javadoc
1   package org.andromda.core.common;
2   
3   import java.io.FileNotFoundException;
4   import java.io.InputStream;
5   import java.util.ArrayList;
6   import java.util.Collection;
7   import java.util.LinkedHashMap;
8   import java.util.Map;
9   import org.andromda.core.configuration.NamespaceProperties;
10  import org.andromda.core.configuration.Namespaces;
11  import org.andromda.core.configuration.Property;
12  import org.andromda.core.mapping.Mapping;
13  import org.andromda.core.mapping.Mappings;
14  import org.apache.commons.io.IOUtils;
15  import org.apache.commons.lang.StringUtils;
16  import org.apache.log4j.Logger;
17  
18  /**
19   * <p>
20   * A class that performs the merging abilities for the AndroMDA core. </p>
21   * <p>
22   * Merging takes place when the {@link NamespaceProperties#MERGE_MAPPINGS_URI} is found within the
23   * <code>namespace</code> and merge mappings are used to replace any matching patterns in the given <code>string</code>.
24   * </p>
25   *
26   * @author Chad Brandon
27   */
28  public class Merger
29  {
30      private static final Logger logger = Logger.getLogger(Merger.class);
31  
32      /**
33       * The shared instance
34       */
35      private static final Merger instance = new Merger();
36  
37      /**
38       * Stores the cached merge mappings already found (so we don't need to reconstruct again each time).
39       */
40      private final Map<String, Mappings> mergeMappingsCache = new LinkedHashMap<String, Mappings>();
41  
42      /**
43       * Gets the shared Merger instance. Normally you'll want to retrieve the instance through this method.
44       *
45       * @return the shared instance.
46       */
47      public static Merger instance()
48      {
49          return instance;
50      }
51  
52      /**
53       * <p>
54       * Retrieves the <em>merged</em> string. The merging takes place when
55       * the {@link NamespaceProperties#MERGE_MAPPINGS_URI}is found within the
56       * <code>namespace</code> and the merge mappings are used to replace any
57       * matching patterns in the given <code>string</code>.
58       * </p>
59       *
60       * @param string the String to be replaced
61       * @param namespace This namespace is searched when attempting to find the
62       *        {@link NamespaceProperties#MERGE_MAPPINGS_URI}.
63       * @return the replaced String.
64       */
65      public String getMergedString(
66          String string,
67          final String namespace)
68      {
69          // avoid any possible infinite recursion with the mergedStringCache
70          // check (may need to refactor the mergedStringCache solution)
71          if (namespace != null && string != null)
72          {
73              string = string.replaceAll("\\r", "");
74              final Collection<Mappings> mappingInstances = this.getMergeMappings(namespace);
75              for (final Mappings mergeMappings : mappingInstances)
76              {
77                  final Collection<Mapping> mappings = mergeMappings.getMappings();
78                  if (mappings != null && !mappings.isEmpty())
79                  {
80                      for (final Mapping mapping : mappings)
81                      {
82                          final Collection<String> froms = mapping.getFroms();
83                          for (String from : froms)
84                         {
85                              from = StringUtils.trimToEmpty(from);
86                              if (StringUtils.isNotBlank(from) && string.contains(from))
87                              {
88                                  final String to = StringUtils.trimToEmpty(mapping.getTo());
89                                  string = StringUtils.replace(string, from, to);
90                              }
91                          }
92                      }
93                  }
94              }
95          }
96          return string;
97      }
98  
99      /**
100      * Retrieves the <em>merged</em> string. The merging takes place when
101      * the {@link NamespaceProperties#MERGE_MAPPINGS_URI}is found within the
102      * <code>namespace</code> and the merge mappings are used to replace any
103      * matching patterns in the given <code>inputStream</code>.
104      * </p>
105      *
106      * @param inputStream the InputStream instance which is first converted
107      *        to a String and then merged.
108      * @param namespace This namespace is searched when attempting to find the
109      *        {@link NamespaceProperties#MERGE_MAPPINGS_URI}.
110      * @return the replaced String.
111      */
112     public String getMergedString(
113         final InputStream inputStream,
114         final String namespace)
115     {
116         try
117         {
118             final String string = IOUtils.toString(inputStream);
119             return this.getMergedString(string, namespace);
120         }
121         catch (final Exception exception)
122         {
123             throw new MergerException(exception);
124         }
125         finally
126         {
127             IOUtils.closeQuietly(inputStream);
128         }
129     }
130 
131     /**
132      * Indicates whether or not the given <code>namespace</code>
133      * requires a merge.
134      * @param namespace the namespace to evaluate.
135      * @return true/false
136      */
137     public boolean requiresMerge(final String namespace)
138     {
139         boolean requiresMerge = false;
140         final Collection<Mappings> mergeMappings = this.getMergeMappings(namespace);
141         for (final Mappings mappings : mergeMappings)
142         {
143             requiresMerge = !mappings.getMappings().isEmpty();
144             if (requiresMerge)
145             {
146                 break;
147             }
148         }
149         return requiresMerge;
150     }
151 
152     /**
153      * Attempts to retrieve the Mappings instance for the given <code>mergeMappingsUri</code> belonging to the given
154      * <code>namespace</code>.
155      *
156      * @param namespace the namespace to which the mappings belong.
157      * @return the Mappings instance.
158      */
159     private Collection<Mappings> getMergeMappings(final String namespace)
160     {
161         final Collection<Mappings> mappings = new ArrayList<Mappings>();
162         if (StringUtils.isNotBlank(namespace))
163         {
164             final Collection<Property> mergeMappingsUris =
165                 Namespaces.instance().getProperties(namespace, NamespaceProperties.MERGE_MAPPINGS_URI, false);
166             if (mergeMappingsUris != null)
167             {
168                 for (final Property mergeMappingsUri : mergeMappingsUris)
169                 {
170                     String mergeMappingsUriValue = (mergeMappingsUri != null) ? mergeMappingsUri.getValue() : null;
171                     if (StringUtils.isNotBlank(mergeMappingsUriValue))
172                     {
173                         Mappings mergeMappings = this.mergeMappingsCache.get(mergeMappingsUriValue);
174                         if (mergeMappings == null)
175                         {
176                             try
177                             {
178                                 mergeMappings = Mappings.getInstance(mergeMappingsUriValue);
179                                 this.mergeMappingsCache.put(mergeMappingsUriValue, mergeMappings);
180                             }
181                             catch (Exception exception)
182                             {
183                                 if (ExceptionUtils.getRootCause(exception) instanceof FileNotFoundException)
184                                 {
185                                     if (logger.isDebugEnabled())
186                                     {
187                                         logger.debug(exception);
188                                     }
189                                 }
190                                 else
191                                 {
192                                     throw new MergerException(exception);
193                                 }
194                             }
195                         }
196                         if (mergeMappings != null)
197                         {
198                             mappings.add(mergeMappings);
199                         }
200                     }
201                 }
202             }
203         }
204         return mappings;
205     }
206 }