Java tutorial
/******************************************************************************* * Copyright (c) 2009 InfAI.org * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * *******************************************************************************/ package org.infai.amor.test; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.*; import java.net.URL; import java.nio.channels.FileChannel; import java.util.*; import java.util.logging.Logger; import org.apache.commons.lang.StringUtils; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.compare.diff.metamodel.*; import org.eclipse.emf.compare.diff.service.DiffService; import org.eclipse.emf.compare.epatch.Epatch; import org.eclipse.emf.compare.epatch.diff.DiffEpatchService; import org.eclipse.emf.compare.match.MatchOptions; import org.eclipse.emf.compare.match.metamodel.MatchModel; import org.eclipse.emf.compare.match.service.MatchService; import org.eclipse.emf.compare.util.ModelUtils; import org.eclipse.emf.ecore.*; import org.eclipse.emf.ecore.EPackage.Registry; import org.eclipse.emf.ecore.resource.*; import org.eclipse.emf.ecore.resource.Resource.Diagnostic; import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; import org.eclipse.emf.ecore.xmi.XMLResource; import org.eclipse.emf.ecore.xmi.impl.*; /** * Just some static helper methods for model io, asserts etc. * * @author sdienst * */ public class ModelUtil { static { // init persistence mappings for ecore and xmi Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().put("ecore", new EcoreResourceFactoryImpl()); Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().put("xmi", new XMIResourceFactoryImpl()); Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().put("xml", new XMLResourceFactoryImpl()); Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().put("filesystem", new XMIResourceFactoryImpl()); } private static final Logger logger = Logger.getLogger(ModelUtil.class.getName()); /** * @param epckg * @param packageRegistry */ private static void addPackages(final EPackage epckg, final Registry packageRegistry) { packageRegistry.put(epckg.getNsURI(), epckg); for (final EPackage subpck : epckg.getESubpackages()) { addPackages(subpck, packageRegistry); } } /** * @param content * @param content2 */ public static void assertModelEqual(final EObject orig, final EObject changed) { try { final MatchModel match = MatchService.doContentMatch(orig, changed, getMatchOptions()); final DiffModel diff = DiffService.doDiff(match, false); final List<DiffElement> differences = new ArrayList<DiffElement>( stripOrderChanges(diff.getOwnedElements())); saveDiff(diff, match, changed.eResource().getURI().toString().replaceAll("/", "_")); describeDiff(differences, 0); // if there are no differences, there is still an empty change, bug in emfcompare? // TODO ReferenceChangeLeftTarget/ReferenceChangeRightTarget may point to absolute/relative pathes, might be the same // though assertTrue(differences.isEmpty() || differences.get(0).getSubDiffElements().isEmpty()); } catch (final InterruptedException e) { fail(); } } public static void assertModelEqual(final Resource orig, final Resource changed) { try { final MatchModel match = MatchService.doResourceMatch(orig, changed, getMatchOptions()); final DiffModel diff = DiffService.doDiff(match, false); final List<DiffElement> differences = new ArrayList<DiffElement>( stripOrderChanges(diff.getOwnedElements())); saveDiff(diff, match, changed.getURI().toString().replace('/', '_')); describeDiff(differences, 0); // if there are no differences, there is still an empty change, bug in emfcompare? assertTrue(differences.isEmpty() || differences.get(0).getSubDiffElements().isEmpty()); } catch (final InterruptedException e) { fail(); } } public static void copyFile(final File in, final File out) throws IOException { final FileChannel inChannel = new FileInputStream(in).getChannel(); final FileChannel outChannel = new FileOutputStream(out).getChannel(); try { inChannel.transferTo(0, inChannel.size(), outChannel); } catch (final IOException e) { throw e; } finally { if (inChannel != null) { inChannel.close(); } if (outChannel != null) { outChannel.close(); } } } /** * @param file * @return */ private static String copyToTempDirectory(final String file) { try { final String ending = file.substring(file.lastIndexOf('.')); final File tempfile = File.createTempFile("model", ending); copyFile(new File(file), tempfile); return tempfile.getAbsolutePath(); } catch (final IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } /** * @param de * @return */ public static String createDescriptionOf(final DiffElement de) { final DifferenceKind kind = de.getKind(); String prefix = de.isConflicting() ? "CONFLICT! " : ""; if (de instanceof ModelElementChangeLeftTarget) { return prefix + kind + ": " + ((ModelElementChangeLeftTarget) de).getLeftElement().toString(); } else if (de instanceof ModelElementChangeRightTarget) { return prefix + kind + ": " + ((ModelElementChangeRightTarget) de).getRightElement().toString(); } else if (de instanceof AttributeChange) { return prefix + kind + ": " + ((AttributeChange) de).getLeftElement().toString() + " -> " + ((AttributeChange) de).getRightElement().toString(); } else if (de instanceof MoveModelElement) { return prefix + kind + ": " + ((MoveModelElement) de).getLeftElement().toString() + " in " + ((MoveModelElement) de).getLeftTarget() + " -> " + ((MoveModelElement) de).getRightElement().toString() + " in " + ((MoveModelElement) de).getRightTarget(); } else { return prefix + kind.toString(); } } /** * @param origModel * @param changedModel * @return * @throws InterruptedException */ public static Epatch createEpatch(final EObject origModel, final EObject changedModel) throws InterruptedException { final MatchModel match = MatchService.doMatch(origModel, changedModel, getMatchOptions()); final DiffModel diff = DiffService.doDiff(match, false); return DiffEpatchService.createEpatch(match, diff, "testpatch"); } /** * @param differences */ public static void describeDiff(final List<DiffElement> differences, final int depth) { for (final DiffElement de : differences) { System.out.print(StringUtils.repeat(" ", depth)); final String diffDescription = ModelUtil.createDescriptionOf(de); System.out.println(diffDescription); final EList<DiffElement> subDiffs = de.getSubDiffElements(); if (!subDiffs.isEmpty()) { describeDiff(subDiffs, depth + 1); } } } /** * @param relativePath * @param file * @return */ private static String getAbsolutePathToTestModel(final String relativePath) { String file = relativePath; final URL url = ModelUtil.class.getClassLoader().getResource(relativePath); if (url != null) { file = url.toExternalForm(); file = file.substring("file:/".length()); } return file; } /** * @return */ private static Map<String, Object> getMatchOptions() { // we assume the very same metamodel, no matter where it was loaded from final Map<String, Object> options = new HashMap<String, Object>(); options.put(MatchOptions.OPTION_DISTINCT_METAMODELS, true); options.put(MatchOptions.OPTION_IGNORE_XMI_ID, true); return options; } /** * @param eResource */ private static void logResourceErrors(final Resource res) { if (res == null || res.getErrors().isEmpty()) { return; } logger.info("There are errors in resource " + res); for (final Diagnostic diag : res.getErrors()) { logger.info(diag.toString()); } } /** * @param relativePath * @return * @throws IOException */ public static EObject readInputModel(final String relativePath) throws IOException { return readInputModel(relativePath, new ResourceSetImpl()); } /** * @param string * @return * @throws IOException */ public static EObject readInputModel(final String relativePath, final ResourceSet rs) throws IOException { return readInputModels(relativePath, rs).get(0); } /** * @param relativePath * @return * @throws IOException */ public static List<EObject> readInputModels(final String relativePath) throws IOException { return readInputModels(relativePath, new ResourceSetImpl()); } /** * @param string * @return * @throws IOException */ public static List<EObject> readInputModels(final String relativePath, final ResourceSet rs) throws IOException { return readInputModels(relativePath, rs, false); } public static List<EObject> readInputModels(final String relativePath, final ResourceSet rs, final boolean simulateRemote) throws IOException { final String modelPath = getAbsolutePathToTestModel(relativePath); Resource resource = null; if (simulateRemote) { // FIXME ugly hack: use filename of relative path only, assuming all connected models are within the same directory String fakePath = relativePath; if (relativePath.contains("/")) { fakePath = relativePath.substring(relativePath.lastIndexOf('/') + 1); } resource = rs.createResource(URI.createFileURI(fakePath)); resource.load(new FileInputStream(modelPath), Collections.emptyMap()); } else { resource = rs.getResource(URI.createFileURI(modelPath), true); resource.load(null); } // register packages for (final EObject eObject : resource.getContents()) { if (eObject instanceof EPackage) { final EPackage epckg = (EPackage) eObject; if (!epckg.getNsURI().equals(EcorePackage.eNS_URI)) { addPackages(epckg, rs.getPackageRegistry()); } } } return resource.getContents(); } /** * Load test model into string. * * @param relativePath * to test model * @return */ public static String readModel(final String relativePath) { final String absPath = getAbsolutePathToTestModel(relativePath); final StringBuilder sb = new StringBuilder(); BufferedReader br = null; try { br = new BufferedReader(new FileReader(absPath)); String line = null; while ((line = br.readLine()) != null) { sb.append(line).append("\n"); } } catch (final IOException e) { throw new RuntimeException(e); } finally { if (br != null) { try { br.close(); } catch (final IOException e) { e.printStackTrace(); } } } return sb.toString(); } /** * @param diff * @param match * @param filename */ public static void saveDiff(final DiffModel diff, final MatchModel match, String filename) { final ComparisonResourceSnapshot snapshot = DiffFactory.eINSTANCE.createComparisonResourceSnapshot(); snapshot.setDate(Calendar.getInstance().getTime()); snapshot.setMatch(match); snapshot.setDiff(diff); try { ModelUtils.save(snapshot, "foo/" + filename + "result.emfdiff.xmi"); } catch (final IOException e) { e.printStackTrace(); } //$NON-NLS-1$ } /** * @param input * @return * @throws IOException */ public static List<String> storeViaXml(final EObject... input) throws IOException { final List<String> result = new ArrayList<String>(); final ResourceSetImpl rs = new ResourceSetImpl(); final Map<String, String> options = new HashMap<String, String>(); options.put(XMLResource.OPTION_ENCODING, "UTF8"); options.put(XMLResource.OPTION_PROCESS_DANGLING_HREF, XMLResource.OPTION_PROCESS_DANGLING_HREF_RECORD); for (final EObject eo : input) { final String relativePath = "foo/" + eo.hashCode() + ".xmi"; result.add(relativePath); final Resource res = rs.createResource(URI.createFileURI(relativePath)); res.getContents().add(eo); res.save(options); logResourceErrors(eo.eResource()); // TODO throw some exception to fail tests } return result; } public static void storeViaXml(final List<EObject> model, final String relPath) throws IOException { final ResourceSetImpl rs = new ResourceSetImpl(); final Map<String, String> options = new HashMap<String, String>(); options.put(XMLResource.OPTION_ENCODING, "UTF8"); options.put(XMLResource.OPTION_PROCESS_DANGLING_HREF, XMLResource.OPTION_PROCESS_DANGLING_HREF_RECORD); final Resource res = rs .createResource(URI.createFileURI("foo/" + System.currentTimeMillis() + "/" + relPath)); logger.info("Storing models to " + res.getURI()); res.getContents().addAll(model); res.save(options); for (final EObject eo : model) { logResourceErrors(eo.eResource()); } } /** * {@link ReferenceOrderChange} is a irrelevant change for our test cases. Remove all differences of this kind. * * @param ownedElements * @return */ private static Collection<DiffElement> stripOrderChanges(final EList<DiffElement> ownedElements) { final Collection<DiffElement> res = new ArrayList<DiffElement>(); for (final DiffElement de : ownedElements) { if (!(de instanceof DiffGroup)) { res.addAll(stripOrderChanges(de.getSubDiffElements())); } if (!(de instanceof ReferenceOrderChange)) { res.add(de); } } return res; } }