001package org.andromda.repositories.emf; 002 003import java.io.IOException; 004import java.io.InputStream; 005import java.net.URL; 006import java.util.ArrayList; 007import java.util.Arrays; 008import java.util.HashMap; 009import java.util.Iterator; 010import java.util.List; 011import java.util.Map; 012import org.andromda.core.common.ResourceFinder; 013import org.andromda.core.engine.ModelProcessor; 014import org.andromda.core.metafacade.ModelAccessFacade; 015import org.andromda.core.repository.RepositoryFacade; 016import org.andromda.core.repository.RepositoryFacadeException; 017import org.apache.log4j.Logger; 018import org.eclipse.emf.common.util.EList; 019import org.eclipse.emf.common.util.URI; 020import org.eclipse.emf.ecore.EObject; 021import org.eclipse.emf.ecore.resource.Resource; 022import org.eclipse.emf.ecore.resource.ResourceSet; 023import org.eclipse.emf.ecore.resource.Resource.Diagnostic; 024 025/** 026 * An abstract EMF {@link RepositoryFacade} instance that should be extended by any repository wishing to load EMF models. 027 * 028 * @author Steve Jerman 029 * @author Chad Brandon 030 * @author Bob Fields (Multiple model support) 031 */ 032public abstract class EMFRepositoryFacade 033 implements RepositoryFacade 034{ 035 /** 036 * The logger instance. 037 */ 038 private static final Logger logger = Logger.getLogger(EMFRepositoryFacade.class); 039 040 /** 041 * Stores the resources (i.e. models) loaded into EMF. 042 */ 043 protected ResourceSet resourceSet; 044 045 /** 046 * 047 */ 048 protected ModelAccessFacade modelFacade; 049 050 /** 051 * Stores the actual loaded model. 052 */ 053 // Modification required for multi-model support - fix compile errors after change 054 // and implement multiple model processing by iterating through models in the list. 055 protected List<Resource> model; 056 057 /** 058 * The options for loading the model. 059 */ 060 private Map<String, Object> loadOptions = new HashMap<String, Object>(); 061 062 /** 063 * Gets the current load options. 064 * 065 * @return the load options. 066 */ 067 protected Map<String, Object> getLoadOptions() 068 { 069 return this.loadOptions; 070 } 071 072 /** 073 * Reads the model with the given <code>uri</code>. 074 * 075 * @param uri the URI to the model 076 */ 077 public void readModel(final String uri) 078 { 079 try 080 { 081 if (this.model==null) 082 { 083 this.model = new ArrayList<Resource>(); 084 } 085 Resource modelResource = this.resourceSet.createResource(EMFRepositoryFacadeUtils.createUri(uri)); 086 if (modelResource == null) 087 { 088 throw new RepositoryFacadeException('\'' + uri + "' is an invalid model"); 089 } 090 modelResource.load(this.getLoadOptions()); 091 // Show errors and warnings, if any.... 092 EList<Diagnostic> errors = modelResource.getErrors(); 093 if (errors!=null && !errors.isEmpty()) 094 { 095 logger.info(errors); 096 } 097 EList<Diagnostic> warnings = modelResource.getWarnings(); 098 if (warnings!=null && !warnings.isEmpty()) 099 { 100 logger.info(warnings); 101 } 102 // Don't validate that model resources can be loaded, if not necessary. Speeds up processing. 103 if (ModelProcessor.getModelValidation()) 104 { 105 try { 106 //logger.debug("EMFRepositoryFacade.readModel.resolve: " + modelResource.getURI()); 107 // Duplicate call to EcoreUtil.resolveAll(modelResource); 108 //long now = System.currentTimeMillis(); 109 //for (EObject eObject : modelResource.getAllContents()) 110 for (Iterator<EObject> i = modelResource.getAllContents(); i.hasNext(); ) 111 { 112 //long now1 = System.currentTimeMillis(); 113 EObject eObject = i.next(); 114 //logger.debug("EMFRepositoryFacade.resolveAll.crossRef: " + EcoreUtil.getURI(eObject) + " " + (System.currentTimeMillis()-now1) + " ms"); 115 for (Iterator<EObject> crossRefIterator = eObject.eCrossReferences().iterator(); crossRefIterator.hasNext(); ) 116 { 117 try 118 { 119 //long now2 = System.currentTimeMillis(); 120 //EObject crossRef = 121 crossRefIterator.next(); 122 //EObject resolved = EcoreUtil.resolve(crossRef, this.resourceSet); 123 //logger.debug("EMFRepositoryFacade.resolveAll.crossRef: " + crossRef.toString() + " = " + EcoreUtil.getURI(crossRef) + " " + (System.currentTimeMillis()-now2) + " ms"); 124 } 125 catch (Exception ex) 126 { 127 logger.error("EMFRepositoryFacade.readModel.resolveAll on " + eObject + ": " + ex); 128 } 129 } 130 } 131 } catch (RuntimeException e) { 132 logger.error("EMFRepositoryFacade.readModel.resolveAll :" + e); 133 } 134 } 135 this.model.add(modelResource); 136 } 137 catch (final Exception exception) 138 { 139 throw new RepositoryFacadeException(exception); 140 } 141 } 142 143 /** 144 * @see org.andromda.core.repository.RepositoryFacade#open() 145 */ 146 public void open() 147 { 148 this.resourceSet = this.createNewResourceSet(); 149 } 150 151 /** 152 * Creates and returns a new resource suitable for loading models into EMF. 153 * This callback is used when (re-)initializing this repository so that it can be reused between different 154 * AndroMDA runs, once a resource set is used for a model it becomes 'polluted' so that subsequent models 155 * will see things from the previous runs, which might mess up the processing. 156 * 157 * @return a new resource set to be used by this repository 158 */ 159 public abstract ResourceSet createNewResourceSet(); 160 161 /** 162 * Ignore. Avoid compiler warning. 163 * @see org.andromda.core.repository.RepositoryFacade#close() 164 */ 165 public abstract void close(); 166 167 /** 168 * The path to any modules found on the classpath. 169 */ 170 private static final String MODULES_PATH = "META-INF/emf/modules"; 171 172 /** 173 * @see org.andromda.core.repository.RepositoryFacade#readModel(String[], String[]) 174 */ 175 public void readModel( 176 String[] modelUris, 177 String[] moduleSearchPaths) 178 { 179 if (modelUris == null || modelUris.length == 0) 180 { 181 throw new RepositoryFacadeException("No model specified."); 182 } 183 final List<String> moduleSearchPathList = new ArrayList<String>(); 184 if (moduleSearchPaths != null) 185 { 186 moduleSearchPathList.addAll(Arrays.asList(moduleSearchPaths)); 187 } 188 189 // - first add the default module search paths maps that are found on the classpath 190 URL[] classpathSearchPaths = ResourceFinder.findResources(MODULES_PATH); 191 if (classpathSearchPaths != null) 192 { 193 classpathSearchPaths = ResourceFinder.findResources("profiles"); 194 } 195 if (classpathSearchPaths != null) 196 { 197 final int numberOfClasspathSearchPaths = classpathSearchPaths.length; 198 for (int ctr = 0; ctr < numberOfClasspathSearchPaths; ctr++) 199 { 200 final URL classpathSearchPath = classpathSearchPaths[ctr]; 201 if (classpathSearchPath != null) 202 { 203 moduleSearchPathList.add(classpathSearchPath.toString()); 204 } 205 } 206 } 207 logger.debug("ModuleSearchPaths: " + moduleSearchPathList); 208 // Add the new URI map to the existing URI map 209 210 this.resourceSet.setURIConverter(new EMFURIConverter(moduleSearchPathList, 211 this.resourceSet.getURIConverter().getURIMap())); 212 if (modelUris.length > 0) 213 { 214 final int numberOfModelUris = modelUris.length; 215 for (int ctr = 0; ctr < numberOfModelUris; ctr++) 216 { 217 this.readModel(modelUris[ctr]); 218 } 219 } 220 } 221 222 /** 223 * @see org.andromda.core.repository.RepositoryFacade#readModel(java.io.InputStream[], String[], String[]) 224 */ 225 public void readModel( 226 InputStream[] stream, 227 String[] modelUri, 228 String[] moduleSearchPaths) 229 { 230 this.readModel( 231 modelUri, 232 moduleSearchPaths); 233 } 234 235 /** 236 * @see org.andromda.core.repository.RepositoryFacade#writeModel(Object, String, String, String) 237 */ 238 public void writeModel( 239 Object modelIn, 240 String location, 241 String version, 242 String encoding) 243 { 244 this.writeModel( 245 modelIn, 246 location, 247 ""); 248 } 249 250 /** 251 * @see org.andromda.core.repository.RepositoryFacade#writeModel(Object, String, String) 252 */ 253 public void writeModel( 254 Object modelIn, 255 String location, 256 String version) 257 { 258 final org.eclipse.emf.ecore.EModelElement element = (org.eclipse.emf.ecore.EModelElement)modelIn; 259 final Resource resource = element.eResource(); 260 final URI uri = URI.createURI(location); 261 resource.setURI(uri); 262 try 263 { 264 resource.save(null); 265 } 266 catch (IOException exception) 267 { 268 throw new RepositoryFacadeException("Could not save model", exception); 269 } 270 } 271 272 /** 273 * @see org.andromda.core.repository.RepositoryFacade#clear() 274 */ 275 public void clear() 276 { 277 this.model = null; 278 this.resourceSet = this.createNewResourceSet(); 279 } 280}