001package org.andromda.core.common; 002 003import java.io.FileNotFoundException; 004import java.io.InputStream; 005import java.util.ArrayList; 006import java.util.Collection; 007import java.util.LinkedHashMap; 008import java.util.Map; 009import org.andromda.core.configuration.NamespaceProperties; 010import org.andromda.core.configuration.Namespaces; 011import org.andromda.core.configuration.Property; 012import org.andromda.core.mapping.Mapping; 013import org.andromda.core.mapping.Mappings; 014import org.apache.commons.io.IOUtils; 015import org.apache.commons.lang.StringUtils; 016import org.apache.log4j.Logger; 017 018/** 019 * <p> 020 * A class that performs the merging abilities for the AndroMDA core. </p> 021 * <p> 022 * Merging takes place when the {@link NamespaceProperties#MERGE_MAPPINGS_URI} is found within the 023 * <code>namespace</code> and merge mappings are used to replace any matching patterns in the given <code>string</code>. 024 * </p> 025 * 026 * @author Chad Brandon 027 */ 028public class Merger 029{ 030 private static final Logger logger = Logger.getLogger(Merger.class); 031 032 /** 033 * The shared instance 034 */ 035 private static final Merger instance = new Merger(); 036 037 /** 038 * Stores the cached merge mappings already found (so we don't need to reconstruct again each time). 039 */ 040 private final Map<String, Mappings> mergeMappingsCache = new LinkedHashMap<String, Mappings>(); 041 042 /** 043 * Gets the shared Merger instance. Normally you'll want to retrieve the instance through this method. 044 * 045 * @return the shared instance. 046 */ 047 public static Merger instance() 048 { 049 return instance; 050 } 051 052 /** 053 * <p> 054 * Retrieves the <em>merged</em> string. The merging takes place when 055 * the {@link NamespaceProperties#MERGE_MAPPINGS_URI}is found within the 056 * <code>namespace</code> and the merge mappings are used to replace any 057 * matching patterns in the given <code>string</code>. 058 * </p> 059 * 060 * @param string the String to be replaced 061 * @param namespace This namespace is searched when attempting to find the 062 * {@link NamespaceProperties#MERGE_MAPPINGS_URI}. 063 * @return the replaced String. 064 */ 065 public String getMergedString( 066 String string, 067 final String namespace) 068 { 069 // avoid any possible infinite recursion with the mergedStringCache 070 // check (may need to refactor the mergedStringCache solution) 071 if (namespace != null && string != null) 072 { 073 string = string.replaceAll("\\r", ""); 074 final Collection<Mappings> mappingInstances = this.getMergeMappings(namespace); 075 for (final Mappings mergeMappings : mappingInstances) 076 { 077 final Collection<Mapping> mappings = mergeMappings.getMappings(); 078 if (mappings != null && !mappings.isEmpty()) 079 { 080 for (final Mapping mapping : mappings) 081 { 082 final Collection<String> froms = mapping.getFroms(); 083 for (String from : froms) 084 { 085 from = StringUtils.trimToEmpty(from); 086 if (StringUtils.isNotBlank(from) && string.contains(from)) 087 { 088 final String to = StringUtils.trimToEmpty(mapping.getTo()); 089 string = StringUtils.replace(string, from, to); 090 } 091 } 092 } 093 } 094 } 095 } 096 return string; 097 } 098 099 /** 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}