/*******************************************************************************
* Copyright (c) 2012 Obeo.
* 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
*
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
package fr.obeo.releng.targetplatform.pde;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.emf.common.util.BasicDiagnostic;
import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.common.util.DiagnosticChain;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.util.Diagnostician;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.equinox.p2.core.IProvisioningAgent;
import org.eclipse.equinox.p2.repository.metadata.IMetadataRepositoryManager;
import org.eclipse.xtext.resource.XtextResourceSet;
import com.google.common.base.Charsets;
import com.google.common.io.Closeables;
import com.google.common.io.Files;
import com.google.inject.Inject;
import com.google.inject.Provider;
import fr.obeo.releng.targetplatform.TargetPlatform;
import fr.obeo.releng.targetplatform.TargetPlatformBundleActivator;
import fr.obeo.releng.targetplatform.TargetPlatformPackage;
import fr.obeo.releng.targetplatform.resolved.ResolvedTargetPlatform;
import fr.obeo.releng.targetplatform.util.LocationIndexBuilder;
/**
* @author <a href="mailto:mikael.barbero@obeo.fr">Mikael Barbero</a>
*
*/
public class Converter {
private static final Pattern SEQUENCE_NUMBER__PATTERN = Pattern.compile("sequenceNumber\\s*=\\s*\"[0-9]+\"");
@Inject
private Provider<XtextResourceSet> resourceSetProvider;
@Inject
private LocationIndexBuilder indexBuilder;
@Inject
private IProvisioningAgent agent;
public Diagnostic generateTargetDefinitionFile(URI uri, IProgressMonitor monitor) {
BasicDiagnostic ret = new BasicDiagnostic(TargetPlatformBundleActivator.PLUGIN_ID, 0, "Diagnostic of the generation of the target platform.", null);
if (!"targetplatform".equals(uri.fileExtension()) && !"tpd".equals(uri.fileExtension())) {
ret.merge(new BasicDiagnostic(Diagnostic.ERROR, TargetPlatformBundleActivator.PLUGIN_ID, -1, "The target platform file '" + uri + "' must ends with '.tpd' or '.targetplatform' extensions.", null));
} else {
Diagnostic diagnostic = doGenerateTargetDefinitionFile(uri, monitor);
if (diagnostic.getSeverity() >= Diagnostic.WARNING) {
ret.merge(diagnostic);
}
}
return ret;
}
private Diagnostic doGenerateTargetDefinitionFile(URI uri, IProgressMonitor monitor) {
BasicDiagnostic ret = new BasicDiagnostic();
SubMonitor subMonitor = SubMonitor.convert(monitor, 100);
try {
Resource resource = loadTargetPlatform(uri);
subMonitor.worked(2);
Diagnostic resourceDiagnostic = EcoreUtil.computeDiagnostic(resource, true);
if (resourceDiagnostic.getSeverity() >= Diagnostic.WARNING) {
ret.merge(resourceDiagnostic);
}
if (resourceDiagnostic.getSeverity() < Diagnostic.ERROR) {
TargetPlatform targetPlatform = null;
if (resource != null && !resource.getContents().isEmpty()) {
targetPlatform = (TargetPlatform) resource.getContents().get(0);
}
if (targetPlatform == null) {
ret.merge(new BasicDiagnostic(Diagnostic.ERROR, TargetPlatformBundleActivator.PLUGIN_ID, -1, "Error occured while loading the file " + uri + ".", null));
} else if (subMonitor.isCanceled()) {
ret.merge(Diagnostic.CANCEL_INSTANCE);
} else {
SubMonitor diagnosticianMonitor = subMonitor.newChild(90).setWorkRemaining(targetPlatform.getLocations().size());
Diagnostic validation = new CustomDiagnostician(diagnosticianMonitor).validate(targetPlatform);
if (validation.getSeverity() >= Diagnostic.WARNING) {
ret.merge(validation);
} else if (subMonitor.isCanceled()) {
ret.merge(Diagnostic.CANCEL_INSTANCE);
}
if (validation.getSeverity() < Diagnostic.ERROR) {
Diagnostic diagnostic = doGenerateTargetDefinitionFile(uri, targetPlatform, subMonitor.newChild(8));
if (diagnostic.getSeverity() >= Diagnostic.WARNING) {
ret.merge(diagnostic);
}
}
subMonitor.setWorkRemaining(0);
}
}
} catch (Exception e) {
ret.merge(BasicDiagnostic.toDiagnostic(e));
}
return ret;
}
private Diagnostic doGenerateTargetDefinitionFile(URI uri, TargetPlatform targetPlatform, IProgressMonitor monitor) {
final BasicDiagnostic ret = new BasicDiagnostic();
SubMonitor subMonitor = SubMonitor.convert(monitor, 100);
try {
IMetadataRepositoryManager repositoryManager = (IMetadataRepositoryManager) agent.getService(IMetadataRepositoryManager.SERVICE_NAME);
ResolvedTargetPlatform resolvedTargetPlatform = ResolvedTargetPlatform.create(targetPlatform, indexBuilder);
subMonitor.worked(5);
if (subMonitor.isCanceled()) {
ret.merge(Diagnostic.CANCEL_INSTANCE);
} else {
Diagnostic diagnostic = resolvedTargetPlatform.resolve(repositoryManager, subMonitor.newChild(90));
if (diagnostic.getSeverity() >= Diagnostic.WARNING) {
ret.merge(diagnostic);
}
if (subMonitor.isCanceled()) {
ret.merge(Diagnostic.CANCEL_INSTANCE);
} else if (ret.getSeverity() < Diagnostic.ERROR) {
diagnostic = doGenerateTargetDefinitionFile(uri, resolvedTargetPlatform);
if (diagnostic.getSeverity() >= Diagnostic.WARNING) {
ret.merge(diagnostic);
}
subMonitor.worked(5);
}
}
} catch (Exception e) {
ret.merge(BasicDiagnostic.toDiagnostic(e));
}
return ret;
}
private Diagnostic doGenerateTargetDefinitionFile(URI uri, ResolvedTargetPlatform resolvedTargetPlatform) {
BasicDiagnostic ret = new BasicDiagnostic();
TargetDefinitionGenerator generator = new TargetDefinitionGenerator();
String xml = generator.generate(resolvedTargetPlatform, (int)TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()));
final URI targetDefinitionLocation = uri.trimFileExtension().appendFileExtension("target");
Diagnostic diagnostic = serialize(targetDefinitionLocation, xml);
if (diagnostic.getSeverity() >= Diagnostic.WARNING) {
ret.merge(diagnostic);
}
return ret;
}
private Diagnostic serialize(URI targetPlatformLocation, String xml) {
BasicDiagnostic ret = new BasicDiagnostic();
URI targetDefinitionLocation = targetPlatformLocation.trimFileExtension().appendFileExtension("target");
if (hasContentDifferencesOtherThanSequenceNumber(targetDefinitionLocation, xml)) {
OutputStream outputStream = null;
try {
outputStream = new BufferedOutputStream(new FileOutputStream(new File(targetDefinitionLocation.toFileString())));
outputStream.write(xml.getBytes());
} catch (Exception e) {
ret.merge(BasicDiagnostic.toDiagnostic(e));
} finally {
try {
Closeables.close(outputStream, true);
} catch (IOException e) {
// swallowed
}
}
} else {
ret.merge(new BasicDiagnostic(Diagnostic.INFO, TargetPlatformBundleActivator.PLUGIN_ID, -1, "The target definition '"+targetDefinitionLocation+"' did not changed since previous generation, we did not overwrote it.", null));
}
return ret;
}
private boolean hasContentDifferencesOtherThanSequenceNumber(URI targetDefinitionLocation, String xml) {
try {
File targetDefinition = new File(targetDefinitionLocation.toFileString());
if (targetDefinition.exists()) {
String oldXml = Files.toString(targetDefinition, Charsets.UTF_8);
oldXml = SEQUENCE_NUMBER__PATTERN.matcher(oldXml).replaceFirst("");
String newXml = SEQUENCE_NUMBER__PATTERN.matcher(xml).replaceFirst("");
return !oldXml.equals(newXml);
}
} catch (IOException e) {
TargetPlatformBundleActivator.getInstance().getLog().log(new Status(IStatus.ERROR, TargetPlatformBundleActivator.PLUGIN_ID, e.getMessage()));
}
return true;
}
private Resource loadTargetPlatform(URI fileLocation) {
ResourceSet resourceSet = resourceSetProvider.get();
return resourceSet.getResource(fileLocation, true);
}
private static class CustomDiagnostician extends Diagnostician {
private final SubMonitor monitor;
public CustomDiagnostician(SubMonitor monitor) {
this.monitor = monitor;
}
@Override
public boolean validate(EClass eClass, EObject eObject, DiagnosticChain diagnostics, Map<Object, Object> context) {
if (monitor.isCanceled()) {
return true;
}
if (eClass == TargetPlatformPackage.Literals.LOCATION) {
context.put(IProgressMonitor.class, monitor.newChild(1));
}
return super.validate(eClass, eObject, diagnostics, context);
}
}
}