/* * Copyright 2014 Bernd Vogt and others. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.sourcepit.b2.validation; import static org.sourcepit.common.manifest.osgi.BundleHeaderName.BUNDLE_REQUIREDEXECUTIONENVIRONMENT; import java.io.File; import java.util.HashMap; import java.util.Map; import javax.inject.Inject; import javax.inject.Named; import org.eclipse.emf.ecore.EObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sourcepit.b2.model.module.PluginProject; import org.sourcepit.common.manifest.osgi.BundleManifest; import org.sourcepit.common.utils.props.LinkedPropertiesMap; import org.sourcepit.common.utils.props.PropertiesMap; import org.sourcepit.common.utils.props.PropertiesSource; import org.sourcepit.common.utils.xml.XmlUtils; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import com.google.common.base.Strings; @Named("executionEnvironment") public class ExecutionEnvironmentConstraint implements ModuleValidationConstraint { private Logger logger; private static class ComplianceSetting { private final String compliance; private final String target; private final String source; public ComplianceSetting(String compliance, String target, String source) { this.compliance = compliance; this.target = target; this.source = source; } public String getCompliance() { return compliance; } public String getTarget() { return target; } public String getSource() { return source; } } private final static Map<String, ComplianceSetting> COMPLIANCE = new HashMap<String, ExecutionEnvironmentConstraint.ComplianceSetting>(); static { COMPLIANCE.put("J2SE-1.3", new ComplianceSetting("1.3", "1.1", "1.3")); COMPLIANCE.put("J2SE-1.4", new ComplianceSetting("1.4", "1.2", "1.3")); COMPLIANCE.put("J2SE-1.5", new ComplianceSetting("1.5", "1.5", "1.5")); COMPLIANCE.put("JavaSE-1.6", new ComplianceSetting("1.6", "1.6", "1.6")); COMPLIANCE.put("JavaSE-1.7", new ComplianceSetting("1.7", "1.7", "1.7")); } @Inject public ExecutionEnvironmentConstraint() { this(LoggerFactory.getLogger(ExecutionEnvironmentConstraint.class)); } @Inject public ExecutionEnvironmentConstraint(Logger logger) { this.logger = logger; } public void validate(EObject eObject, PropertiesSource properties, boolean quickFixesEnabled) { if (eObject instanceof PluginProject) { PluginProject project = (PluginProject) eObject; if (!project.isDerived() && hasJavaNature(project.getDirectory())) { String id = project.getId(); String ee = properties.get("b2.executionEnvironment." + id); if (Strings.isNullOrEmpty(ee)) { ee = properties.get("b2.executionEnvironment"); } if (!Strings.isNullOrEmpty(ee)) { validateClasspath(project, ee, quickFixesEnabled); validateCompliance(project, ee, quickFixesEnabled); validateMF(project, ee, quickFixesEnabled); } } } } private void validateMF(PluginProject project, String ee, boolean quickFixesEnabled) { final BundleManifest manifest = project.getBundleManifest(); final String actualEE = manifest.getHeaderValue(BUNDLE_REQUIREDEXECUTIONENVIRONMENT); if (!ee.equals(actualEE)) { final StringBuilder msg = new StringBuilder(); msg.append(project.getId()); msg.append(": Expected value for manifest header 'Bundle-RequiredExecutionEnvironment' is '"); msg.append(ee); msg.append("' but is '"); msg.append(actualEE); msg.append("'."); if (quickFixesEnabled) { msg.append(" (applied quick fix)"); manifest.setHeader(BUNDLE_REQUIREDEXECUTIONENVIRONMENT, ee); EclipseBundleShapeConstraint.save(project.getDirectory(), manifest); } else { msg.append(" (quick fix available)"); } logger.warn(msg.toString()); } } private void validateCompliance(PluginProject project, String ee, boolean quickFixesEnabled) { final ComplianceSetting setting = COMPLIANCE.get(ee); if (setting != null) { final PropertiesMap jdtPrefs = new LinkedPropertiesMap(); final File prefsFile = new File(project.getDirectory(), ".settings/org.eclipse.jdt.core.prefs"); if (prefsFile.exists()) { jdtPrefs.load(prefsFile); } boolean changed = false; final String compliance = jdtPrefs.get("org.eclipse.jdt.core.compiler.compliance"); if (!setting.getCompliance().equals(compliance)) { jdtPrefs.put("org.eclipse.jdt.core.compiler.compliance", setting.getCompliance()); warn(project, "compiler compliance level", setting.getCompliance(), compliance, quickFixesEnabled); changed = true; } final String target = jdtPrefs.get("org.eclipse.jdt.core.compiler.codegen.targetPlatform"); if (!setting.getTarget().equals(target)) { jdtPrefs.put("org.eclipse.jdt.core.compiler.codegen.targetPlatform", setting.getTarget()); warn(project, "compiler target compatibility", setting.getTarget(), target, quickFixesEnabled); changed = true; } final String source = jdtPrefs.get("org.eclipse.jdt.core.compiler.source"); if (!setting.getSource().equals(source)) { jdtPrefs.put("org.eclipse.jdt.core.compiler.source", setting.getSource()); warn(project, "compiler source compatibility", setting.getSource(), source, quickFixesEnabled); changed = true; } if (quickFixesEnabled && changed) { jdtPrefs.store(prefsFile); } } } private void warn(PluginProject project, String what, String expected, final String actual, boolean quickFixesEnabled) { final StringBuilder msg = new StringBuilder(); msg.append(project.getId()); msg.append(": Expected "); msg.append(what); msg.append(" "); msg.append(expected); msg.append(" but is "); msg.append(actual); msg.append("."); if (quickFixesEnabled) { msg.append(" (applied quick fix)"); } else { msg.append(" (quick fix available)"); } logger.warn(msg.toString()); } private void validateClasspath(PluginProject project, String ee, boolean quickFixesEnabled) { final File cpFile = new File(project.getDirectory(), ".classpath"); final Document cpDoc; if (cpFile.exists()) { cpDoc = XmlUtils.readXml(cpFile); } else { cpDoc = XmlUtils.newDocument(); } Element eeNode = (Element) XmlUtils.queryNode(cpDoc, "/classpath/classpathentry[@kind='con' and starts-with(@path,'org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/')]"); final String eeName; if (eeNode == null) { eeName = null; } else { final String eePath = eeNode.getAttribute("path"); eeName = eePath.substring(eePath.lastIndexOf('/') + 1); } if (!ee.equals(eeName)) { final StringBuilder msg = new StringBuilder(); msg.append(project.getId()); msg.append(": Expected execution environment '"); msg.append(ee); msg.append("' but was '"); msg.append(eeName); msg.append("'."); if (quickFixesEnabled) { msg.append(" (applied quick fix)"); if (eeNode == null) { Node cpNode = cpDoc.getFirstChild(); if (cpNode == null) { cpNode = cpDoc.createElement("classpath"); cpDoc.appendChild(cpNode); } eeNode = cpDoc.createElement("classpathentry"); eeNode.setAttribute("kind", "con"); cpNode.appendChild(eeNode); } eeNode.setAttribute("path", "org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/" + ee); XmlUtils.writeXml(cpDoc, cpFile); } else { msg.append(" (quick fix available)"); } logger.warn(msg.toString()); } } private boolean hasJavaNature(File projectDir) { final File prjFile = new File(projectDir, ".project"); if (prjFile.exists()) { final Document doc = XmlUtils.readXml(prjFile); final Node node = XmlUtils.queryNode(doc, "/projectDescription/natures[nature='org.eclipse.jdt.core.javanature']"); return node != null; } return false; } }