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 }