/* * Copyright (c) 2007-2009, Osmorc Development Team * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, this list * of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, this * list of conditions and the following disclaimer in the documentation and/or other * materials provided with the distribution. * * Neither the name of 'Osmorc Development Team' nor the names of its contributors may be * used to endorse or promote products derived from this software without specific * prior written permission. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.osmorc.facet.maven; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.Module; import com.intellij.openapi.util.text.StringUtil; import com.jgoodies.binding.beans.BeanUtils; import org.jdom.Element; import org.jetbrains.annotations.NotNull; import org.jetbrains.idea.maven.importing.FacetImporter; import org.jetbrains.idea.maven.importing.MavenModifiableModelsProvider; import org.jetbrains.idea.maven.importing.MavenRootModelAdapter; import org.jetbrains.idea.maven.model.MavenId; import org.jetbrains.idea.maven.model.MavenPlugin; import org.jetbrains.idea.maven.project.*; import org.osgi.framework.Constants; import org.osmorc.facet.OsmorcFacet; import org.osmorc.facet.OsmorcFacetConfiguration; import org.osmorc.facet.OsmorcFacetType; import java.io.File; import java.util.Collection; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * The OsmorcFacetImporter tries to read maven metadata and import OSGi specific settings as an Osmorc facet. * * @author <a href="mailto:janthomae@janthomae.de">Jan Thomä</a> * @version $Id:$ */ public class OsmorcFacetImporter extends FacetImporter<OsmorcFacet, OsmorcFacetConfiguration, OsmorcFacetType> { private final Logger logger = Logger.getInstance("#org.osmorc.facet.maven.OsmorcFacetImporter"); private static final String IncludeExistingManifest = "_include"; public OsmorcFacetImporter() { super("org.apache.felix", "maven-bundle-plugin", OsmorcFacetType.getInstance(), "OSGi"); } public boolean isApplicable(MavenProject mavenProjectModel) { MavenPlugin p = mavenProjectModel.findPlugin(myPluginGroupID, myPluginArtifactID); // fixes: IDEA-56021 String packaging = mavenProjectModel.getPackaging(); return p != null && packaging != null && "bundle".equals(packaging); } protected void setupFacet(OsmorcFacet osmorcFacet, MavenProject mavenProjectModel) { } protected void reimportFacet(MavenModifiableModelsProvider modelsProvider, Module module, MavenRootModelAdapter mavenRootModelAdapter, OsmorcFacet osmorcFacet, MavenProjectsTree mavenProjectsTree, MavenProject mavenProject, MavenProjectChanges changes, Map<MavenProject, String> mavenProjectStringMap, List<MavenProjectsProcessorTask> mavenProjectsProcessorPostConfigurationTasks) { OsmorcFacetConfiguration conf = osmorcFacet.getConfiguration(); MavenPlugin p = mavenProject.findPlugin(myPluginGroupID, myPluginArtifactID); // TODO: check if there is a manifest, in which case use this manifest! // first off, we get the defaults, that is // Symbolic name == groupId + "." + artifactId MavenId id = mavenProject.getMavenId(); conf.setBundleSymbolicName(id.getGroupId() + "." + id.getArtifactId()); conf.setBundleVersion(cleanupVersion(id.getVersion())); if (p != null) { logger.debug("Plugin found."); // Check if there are any overrides set up in the maven plugin settings conf.setBundleSymbolicName(computeSymbolicName(mavenProject)); // IDEA-63243 setConfigProperty(mavenProject, conf, "bundleVersion", "instructions." + Constants.BUNDLE_VERSION); setConfigProperty(mavenProject, conf, "bundleActivator", "instructions." + Constants.BUNDLE_ACTIVATOR); if ("".equals(conf.getBundleVersion().trim())) { // IDEA-74272 // if there is no bundle-version int the instructions, derive it from the maven settings. String version = mavenProject.getMavenId().getVersion(); //that is ${pom.version} conf.setBundleVersion(cleanupVersion(version)); } Map<String, String> props = new LinkedHashMap<String, String>(); // linkedhashmap, because we want to preserve the order of elements. Map<String, String> modelMap = mavenProject.getModelMap(); String description = modelMap.get("description"); if (!StringUtil.isEmptyOrSpaces(description)) { props.put(Constants.BUNDLE_DESCRIPTION, description); } String licenses = modelMap.get("licenses"); if (!StringUtil.isEmptyOrSpaces(licenses)) { props.put("Bundle-License", licenses); } String vendor = modelMap.get("organization.name"); if (!StringUtil.isEmpty(vendor)) { props.put(Constants.BUNDLE_VENDOR, vendor); } String docUrl = modelMap.get("organization.url"); if (!StringUtil.isEmptyOrSpaces(docUrl)) { props.put(Constants.BUNDLE_DOCURL, docUrl); } // now find any additional properties that might have been set up: Element instructionsNode = getConfig(mavenProject, "instructions"); // Fix for IDEADEV-38685, NPE when the element is not set. if (instructionsNode != null) { @SuppressWarnings({"unchecked"}) List<Element> children = instructionsNode.getChildren(); boolean useExistingManifest = false; for (Element child : children) { String name = child.getName(); String value = child.getValue(); if (value != null && !"".equals(value) && !Constants.BUNDLE_SYMBOLICNAME.equals(name) && !Constants.BUNDLE_VERSION.equals(name) && !Constants.BUNDLE_ACTIVATOR.equals(name) && !IncludeExistingManifest.equals(name)) { // ok its an additional setting: props.put(name, value); } if (IncludeExistingManifest.equals(name)) { conf.setManifestLocation(value); conf.setOsmorcControlsManifest(false); conf.setUseBndFile(false); conf.setUseBundlorFile(false); conf.setUseProjectDefaultManifestFileLocation(false); useExistingManifest = true; } } if (!useExistingManifest) { conf.setManifestLocation(""); conf.setOsmorcControlsManifest(true); conf.setUseBndFile(false); conf.setUseBundlorFile(false); conf.setUseProjectDefaultManifestFileLocation(true); } // check if bundle name exists, if not compute it (IDEA-63244) if (!props.containsKey(Constants.BUNDLE_NAME)) { props.put(Constants.BUNDLE_NAME, computeBundleName(mavenProject)); } } // Fix for IDEA-63242 - don't merge it with the existing settings, overwrite them conf.importAdditionalProperties(props, true); // Fix for IDEA-66235 - inherit jar filename from maven String jarFileName = mavenProject.getFinalName() + ".jar"; // FiX for IDEA-67088, preserve existing output path settings on reimport. switch (conf.getOutputPathType()) { case OsgiOutputPath: conf.setJarFileLocation(jarFileName, OsmorcFacetConfiguration.OutputPathType.OsgiOutputPath); break; case SpecificOutputPath: conf.setJarFileLocation(new File(conf.getJarFilePath(), jarFileName).getPath(), OsmorcFacetConfiguration.OutputPathType.SpecificOutputPath); break; default: conf.setJarFileLocation(jarFileName, OsmorcFacetConfiguration.OutputPathType.CompilerOutputPath); } } } /** * Computes the Bundle-Name value from the data given in the maven project. * * @param mavenProject the maven project * @return the bundle's human-readable name */ @NotNull private String computeBundleName(MavenProject mavenProject) { String bundleName = findConfigValue(mavenProject, "instructions." + Constants.BUNDLE_NAME); if (bundleName == null || bundleName.length() == 0) { String mavenProjectName = mavenProject.getName(); if (mavenProjectName != null) { return mavenProjectName; } // when no name is set, use the symbolic name return computeSymbolicName(mavenProject); } return bundleName; } /** * Computes the Bundle-SymbolicName value from the data given in the maven project. * * @param mavenProject the maven project * @return the bundle symbolic name. */ @NotNull private String computeSymbolicName(MavenProject mavenProject) { String bundleSymbolicName = findConfigValue(mavenProject, "instructions." + Constants.BUNDLE_SYMBOLICNAME); // if it's not set compute it if (bundleSymbolicName == null || bundleSymbolicName.length() == 0) { // Get the symbolic name as groupId + "." + artifactId, with the following exceptions: MavenId mavenId = mavenProject.getMavenId(); String groupId = mavenId.getGroupId(); String artifactId = mavenId.getArtifactId(); if (groupId == null || artifactId == null) { return ""; } String lastSectionOfGroupId = groupId.substring(groupId.lastIndexOf(".") + 1); // if artifactId is equal to last section of groupId then groupId is returned. eg. org.apache.maven:maven -> org.apache.maven if (lastSectionOfGroupId.endsWith(artifactId)) { return groupId; } // if artifactId starts with last section of groupId that portion is removed. eg. org.apache.maven:maven-core -> org.apache.maven.core " String doubledNamePart = lastSectionOfGroupId + "-"; if (artifactId.startsWith(doubledNamePart) && artifactId.length() > doubledNamePart.length()) { return groupId + "." + artifactId.substring(doubledNamePart.length()); } return groupId + "." + artifactId; } else { return bundleSymbolicName; } } private void setConfigProperty(MavenProject mavenProjectModel, OsmorcFacetConfiguration conf, String confProperty, String mavenConfProperty) { String value = findConfigValue(mavenProjectModel, mavenConfProperty); // Fix for IDEA-63242 - don't merge it with the existing settings, overwrite them if (value == null) { value = ""; } try { BeanUtils.setValue(conf, BeanUtils.getPropertyDescriptor(OsmorcFacetConfiguration.class, confProperty), value); } catch (Exception e) { logger.error("Problem when setting property", e); } } @Override public void getSupportedDependencyTypes(Collection<String> result, SupportedRequestType type) { result.add("bundle"); } /* * Copied from DefaultMaven2OsgiConverter of Maven Bundle Plugin under Apache2 license. * @author <a href="mailto:carlos@apache.org">Carlos Sanchez</a> */ private static final Pattern FUZZY_VERSION = Pattern.compile("(\\d+)(\\.(\\d+)(\\.(\\d+))?)?([^a-zA-Z0-9](.*))?", Pattern.DOTALL); /** * Clean up version parameters. Other builders use more fuzzy definitions of * the version syntax. This method cleans up such a version to match an OSGi * version. * * @param VERSION_STRING * @return */ private static String cleanupVersion(String version) { StringBuffer result = new StringBuffer(); Matcher m = FUZZY_VERSION.matcher(version); if (m.matches()) { String major = m.group(1); String minor = m.group(3); String micro = m.group(5); String qualifier = m.group(7); if (major != null) { result.append(major); if (minor != null) { result.append("."); result.append(minor); if (micro != null) { result.append("."); result.append(micro); if (qualifier != null) { result.append("."); cleanupModifier(result, qualifier); } } else if (qualifier != null) { result.append(".0."); cleanupModifier(result, qualifier); } else { result.append(".0"); } } else if (qualifier != null) { result.append(".0.0."); cleanupModifier(result, qualifier); } else { result.append(".0.0"); } } } else { result.append("0.0.0."); cleanupModifier(result, version); } return result.toString(); } private static void cleanupModifier(StringBuffer result, String modifier) { for (int i = 0; i < modifier.length(); i++) { char c = modifier.charAt(i); if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || c == '-') { result.append(c); } else { result.append('_'); } } } }