/* * 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; import com.intellij.facet.FacetConfiguration; import com.intellij.facet.ui.FacetEditorContext; import com.intellij.facet.ui.FacetEditorTab; import com.intellij.facet.ui.FacetValidatorsManager; import com.intellij.openapi.diagnostic.Log; import com.intellij.openapi.module.ModuleServiceManager; import com.intellij.openapi.roots.CompilerModuleExtension; import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.WriteExternalException; import com.intellij.openapi.vfs.VirtualFile; import org.jdom.Element; import org.jetbrains.annotations.NotNull; import org.osgi.framework.Constants; import org.osmorc.facet.ui.OsmorcFacetGeneralEditorTab; import org.osmorc.facet.ui.OsmorcFacetJAREditorTab; import org.osmorc.facet.ui.OsmorcFacetManifestGenerationEditorTab; import org.osmorc.settings.ProjectSettings; import org.osmorc.util.OrderedProperties; import java.io.File; import java.io.IOException; import java.io.StringReader; import java.util.*; import java.util.regex.Pattern; /** * The facet configuration of an osmorc facet. * * @author <a href="mailto:janthomae@janthomae.de">Jan Thomä</a> * @author Robert F. Beeger (robert@beeger.net) */ public class OsmorcFacetConfiguration implements FacetConfiguration { private OsmorcFacet myFacet; // Important: This setting must be true by default otherwise you get some dialog when importing from // maven metamodel asking if the manifest file should be created. // XXX: this should probably be fixed in the "facetAdded" method in ModuleManifestHolderImpl or ModuleDependencySynchronizer private boolean myOsmorcControlsManifest = true; private String myManifestLocation; private String myJarFileLocation; private String myBundleSymbolicName; private String myBundleActivator; private String myBundleVersion; private String myAdditionalProperties; private List<Pair<String, String>> myAdditionalJARContents; private boolean myUseProjectDefaultManifestFileLocation = true; private boolean myUseBndFile; private String myBndFileLocation; private boolean myUseBundlorFile; private String myBundlorFileLocation; private String myIgnoreFilePattern; private boolean myAlwaysRebuildBundleJAR; private OutputPathType myOutputPathType; // constants private static final String OSMORC_CONTROLS_MANIFEST = "osmorcControlsManifest"; private static final String USE_BND_FILE = "useBndFile"; private static final String BND_FILE_LOCATION = "bndFileLocation"; private static final String USE_BUNDLOR_FILE = "useBundlorFile"; private static final String BUNDLOR_FILE_LOCATION = "bundlorFileLocation"; private static final String MANIFEST_LOCATION = "manifestLocation"; private static final String JARFILE_LOCATION = "jarfileLocation"; private static final String BUNDLE_ACTIVATOR = "bundleActivator"; private static final String BUNDLE_SYMBOLIC_NAME = "bundleSymbolicName"; private static final String BUNDLE_VERSION = "bundleVersion"; private static final String USE_PROJECT_DEFAULT_MANIFEST_FILE_LOCATION = "useProjectDefaultManifestFileLocation"; private static final String ADDITIONAL_PROPERTIES = "additionalProperties"; private static final String IGNORE_FILE_PATTERN = "ignoreFilePattern"; private static final String ALWAYS_REBUILD_BUNDLE_JAR = "alwaysRebuildBundleJAR"; private static final String OUTPUT_PATH_TYPE = "outputPathType"; private static final String PROPERTY = "property"; private static final String KEY = "key"; private static final String VALUE = "value"; public FacetEditorTab[] createEditorTabs(final FacetEditorContext editorContext, final FacetValidatorsManager validatorsManager) { return new FacetEditorTab[]{new OsmorcFacetGeneralEditorTab(editorContext), new OsmorcFacetJAREditorTab(editorContext, validatorsManager), new OsmorcFacetManifestGenerationEditorTab(editorContext)}; } public void readExternal(Element element) throws InvalidDataException { setOsmorcControlsManifest(Boolean.parseBoolean(element.getAttributeValue(OSMORC_CONTROLS_MANIFEST, "true"))); setUseBndFile(Boolean.parseBoolean(element.getAttributeValue(USE_BND_FILE, "false"))); setBndFileLocation(element.getAttributeValue(BND_FILE_LOCATION)); setUseBundlorFile(Boolean.parseBoolean(element.getAttributeValue(USE_BUNDLOR_FILE, "false"))); setBundlorFileLocation(element.getAttributeValue(BUNDLOR_FILE_LOCATION)); setManifestLocation(element.getAttributeValue(MANIFEST_LOCATION)); // IDEADEV-40357 backwards compatibility //if ( !"".equals(getManifestLocation()) && !getManifestLocation().contains("/") && !getManifestLocation().toUpperCase().contains(".MF") ) { // its an old directory setting.fix it by appending MANIFEST.MF //setManifestLocation(getManifestLocation()+"/MANIFEST.MF"); //} //This actually is not a good idea if someone is using a manifest named different than manifest.mf... so i comment it out. String outputPathTypeName = element.getAttributeValue(OUTPUT_PATH_TYPE, OutputPathType.SpecificOutputPath.name()); OutputPathType outputPathType = OutputPathType.valueOf(outputPathTypeName); setJarFileLocation(element.getAttributeValue(JARFILE_LOCATION), outputPathType); setBundleActivator(element.getAttributeValue(BUNDLE_ACTIVATOR)); setBundleSymbolicName(element.getAttributeValue(BUNDLE_SYMBOLIC_NAME)); setBundleVersion(element.getAttributeValue(BUNDLE_VERSION)); setIgnoreFilePattern(element.getAttributeValue(IGNORE_FILE_PATTERN)); setUseProjectDefaultManifestFileLocation(Boolean.parseBoolean(element.getAttributeValue( USE_PROJECT_DEFAULT_MANIFEST_FILE_LOCATION, "true"))); setAlwaysRebuildBundleJAR(Boolean.parseBoolean(element.getAttributeValue( ALWAYS_REBUILD_BUNDLE_JAR, "false"))); Element props = element.getChild(ADDITIONAL_PROPERTIES); if (props != null) { List children = props.getChildren(); if (children.isEmpty()) { // ok this is a legacy file setAdditionalProperties(props.getText()); } else { StringBuilder builder = new StringBuilder(); // new handling as fix for OSMORC-43 for (Object child : children) { Element prop = (Element)child; builder.append(prop.getAttributeValue(KEY)).append(":").append(prop.getAttributeValue(VALUE)).append("\n"); } setAdditionalProperties(builder.toString()); } } List<Pair<String, String>> additionalJARContents = getAdditionalJARContents(); Element additionalJARContentsElement = element.getChild("additionalJARContents"); if (additionalJARContentsElement != null) { @SuppressWarnings({"unchecked"}) List<Element> children = additionalJARContentsElement.getChildren("entry"); for (Element entryElement : children) { additionalJARContents.add(Pair.create( entryElement.getAttributeValue("source"), entryElement.getAttributeValue("dest"))); } } } public void writeExternal(Element element) throws WriteExternalException { element.setAttribute(OSMORC_CONTROLS_MANIFEST, String.valueOf(isOsmorcControlsManifest())); element.setAttribute(MANIFEST_LOCATION, getManifestLocation()); element.setAttribute(JARFILE_LOCATION, myJarFileLocation != null ? myJarFileLocation : ""); element.setAttribute(OUTPUT_PATH_TYPE, getOutputPathType().name()); element.setAttribute(USE_BND_FILE, String.valueOf(isUseBndFile())); element.setAttribute(BND_FILE_LOCATION, getBndFileLocation()); element.setAttribute(USE_BUNDLOR_FILE, String.valueOf(isUseBundlorFile())); element.setAttribute(BUNDLOR_FILE_LOCATION, getBundlorFileLocation()); element.setAttribute(BUNDLE_ACTIVATOR, getBundleActivator()); element.setAttribute(BUNDLE_SYMBOLIC_NAME, getBundleSymbolicName()); element.setAttribute(BUNDLE_VERSION, getBundleVersion()); element.setAttribute(IGNORE_FILE_PATTERN, getIgnoreFilePattern()); element.setAttribute(USE_PROJECT_DEFAULT_MANIFEST_FILE_LOCATION, String.valueOf(isUseProjectDefaultManifestFileLocation())); element.setAttribute(ALWAYS_REBUILD_BUNDLE_JAR, String.valueOf(isAlwaysRebuildBundleJAR())); Element props = new Element(ADDITIONAL_PROPERTIES); Map<String, String> additionalPropertiesAsMap = getAdditionalPropertiesAsMap(); for (String key : additionalPropertiesAsMap.keySet()) { Element prop = new Element(PROPERTY); prop.setAttribute(KEY, key); prop.setAttribute(VALUE, additionalPropertiesAsMap.get(key)); props.addContent(prop); } element.addContent(props); Element additionalJARContentsElement = new Element("additionalJARContents"); List<Pair<String, String>> additionalJARContents = getAdditionalJARContents(); for (Pair<String, String> additionalJARContent : additionalJARContents) { Element entry = new Element("entry"); entry.setAttribute("source", additionalJARContent.getFirst()); entry.setAttribute("dest", additionalJARContent.getSecond()); additionalJARContentsElement.addContent(entry); } element.addContent(additionalJARContentsElement); } /** * @return true if Osmorc controls the manifest, false if is edited manually */ public boolean isOsmorcControlsManifest() { return myOsmorcControlsManifest; } /** * Convenience getter. * * @return true, if the manifest is edited manually, false if osmorc creates it on build */ public boolean isManifestManuallyEdited() { return !myOsmorcControlsManifest; } public void setOsmorcControlsManifest(boolean osmorcControlsManifest) { this.myOsmorcControlsManifest = osmorcControlsManifest; } /** * @return the manifest location, relative to the module's content roots. */ @NotNull public String getManifestLocation() { return myManifestLocation != null ? myManifestLocation : ""; } public void setManifestLocation(String manifestLocation) { this.myManifestLocation = manifestLocation; } /** * @return the jar file to be created for this module */ @NotNull public String getJarFileLocation() { String nullSafeLocation = myJarFileLocation != null ? myJarFileLocation : ""; if (myOutputPathType == null || myFacet == null) { // not initialized return nullSafeLocation; } switch (myOutputPathType) { case CompilerOutputPath: VirtualFile moduleCompilerOutputPath = CompilerModuleExtension.getInstance(myFacet.getModule()).getCompilerOutputPath(); if (moduleCompilerOutputPath != null) { return moduleCompilerOutputPath.getParent().getPath() + "/" + nullSafeLocation; } else { return nullSafeLocation; } case OsgiOutputPath: ProjectSettings projectSettings = ModuleServiceManager.getService(myFacet.getModule(), ProjectSettings.class); String bundlesOutputPath = projectSettings.getBundlesOutputPath(); if (bundlesOutputPath != null && bundlesOutputPath.trim().length() != 0) { return bundlesOutputPath + "/" + nullSafeLocation; } else { return ProjectSettings.getDefaultBundlesOutputPath(myFacet.getModule().getProject()) + "/" + nullSafeLocation; } case SpecificOutputPath: default: return nullSafeLocation; } } /** * Returns the file name of the jar file. * @return the file name of the jar file. */ @NotNull public String getJarFileName() { if (myOutputPathType == null) { return ""; } switch (myOutputPathType) { case CompilerOutputPath: case OsgiOutputPath: case SpecificOutputPath: String completeOutputPath = getJarFileLocation(); File f = new File(completeOutputPath); return f.getName(); default: // not initialized return getJarFileLocation(); } } /** * Returns the path where the jar file name should be stored (excluding the jar's name). * * @return the path name where the jar file is to be stored.. */ @NotNull public String getJarFilePath() { if ( myOutputPathType == null ) { return ""; } switch (myOutputPathType) { case CompilerOutputPath: case OsgiOutputPath: case SpecificOutputPath: String completeOutputPath = getJarFileLocation(); File f = new File(completeOutputPath); String parent = f.getParent(); if ( parent == null ) { return ""; } return parent; default: // not initialized return ""; } } /** * Sethes the location of the jar file * * @param jarFileLocation the path to the jar file. If the output path type is {@link OutputPathType#SpecificOutputPath} this needs to * be a full path otherwise it needs to be just the jar's name. * @param outputPathType the path type */ public void setJarFileLocation(String jarFileLocation, OutputPathType outputPathType) { myJarFileLocation = jarFileLocation; myOutputPathType = outputPathType; } public OutputPathType getOutputPathType() { return myOutputPathType != null ? myOutputPathType : OutputPathType.SpecificOutputPath; } /** * @return the symbolic name of the bundle to build */ @NotNull public String getBundleSymbolicName() { return myBundleSymbolicName != null ? myBundleSymbolicName : ""; } public void setBundleSymbolicName(String bundleSymbolicName) { myBundleSymbolicName = bundleSymbolicName; } /** * @return the bundle activator class */ public String getBundleActivator() { return myBundleActivator != null ? myBundleActivator : ""; } public void setBundleActivator(String bundleActivator) { myBundleActivator = bundleActivator; } /** * @return the version of the bundle. */ @NotNull public String getBundleVersion() { return myBundleVersion != null ? myBundleVersion : "1.0.0"; } public void setBundleVersion(String bundleVersion) { myBundleVersion = bundleVersion; } public void setAdditionalProperties(String additionalProperties) { myAdditionalProperties = additionalProperties; } /** * @return additional properties to be added to the bundle manifest */ @NotNull public String getAdditionalProperties() { return myAdditionalProperties != null ? myAdditionalProperties : ""; } /** * @return the contents of this configuration as a string that comprises a BND configuration file. */ public String asBndFile() { return Constants.BUNDLE_SYMBOLICNAME + ":" + getBundleSymbolicName() + "\n" + Constants.BUNDLE_VERSION + ":" + getBundleVersion() + "\n" + Constants.BUNDLE_ACTIVATOR + ":" + getBundleActivator() + "\n" + getAdditionalProperties() + "\n"; } /** * Returns all additional properties as a map.Changes to this map will not change the facet configuration. If you want * to change additional properties use the {@link #importAdditionalProperties(java.util.Map, boolean)} method to reimport the * map once you have changed it. The returned map is ordered and will return entries in the same order as they have been specified in the * settings dialog. * * @return the additional properties as a Map for convenciene. */ @NotNull public Map<String, String> getAdditionalPropertiesAsMap() { Map<String, String> result = new LinkedHashMap<String, String>(); Properties p = new OrderedProperties(); try { p.load(new StringReader(getAdditionalProperties())); } catch (IOException e) { Log.print("Error when reading properties", true); return result; } Set<String> propNames = p.stringPropertyNames(); for (String propName : propNames) { result.put(propName, p.getProperty(propName)); } return result; } /** * Allows to import properties into the list of additional properties. * * @param properties the properties to import * @param overwrite if true, all properties in this facet configuration will be overwritten by the given properties, * otherwise a merge will be performed with the given properties having precedence before the * existing properties. */ public void importAdditionalProperties(Map<String, String> properties, boolean overwrite) { Map<String, String> existing = overwrite ? properties : getAdditionalPropertiesAsMap(); if (!overwrite) { // merge existing.putAll(properties); } // now create a string. StringBuilder builder = new StringBuilder(); for (String key : existing.keySet()) { String value = existing.get(key); value = value.replace("\n", "\\\n"); builder.append(key).append(": ").append(value).append("\n"); } setAdditionalProperties(builder.toString()); } public void setUseProjectDefaultManifestFileLocation(boolean useProjectDefaultManifestFileLocation) { myUseProjectDefaultManifestFileLocation = useProjectDefaultManifestFileLocation; } public boolean isUseProjectDefaultManifestFileLocation() { return myUseProjectDefaultManifestFileLocation; } public boolean isUseBndFile() { return myUseBndFile; } public void setUseBndFile(boolean useBndFile) { myUseBndFile = useBndFile; } @NotNull public String getBndFileLocation() { return myBndFileLocation != null ? myBndFileLocation : ""; } public void setBndFileLocation(String bndFileLocation) { myBndFileLocation = bndFileLocation; } public boolean isUseBundlorFile() { return myUseBundlorFile; } public void setUseBundlorFile(boolean _useBundlorFile) { this.myUseBundlorFile = _useBundlorFile; } @NotNull public String getBundlorFileLocation() { return myBundlorFileLocation != null ? myBundlorFileLocation : ""; } public void setBundlorFileLocation(String _bundlorFileLocation) { this.myBundlorFileLocation = _bundlorFileLocation; } @NotNull public List<Pair<String, String>> getAdditionalJARContents() { if (myAdditionalJARContents == null) { myAdditionalJARContents = new ArrayList<Pair<String, String>>(); } return myAdditionalJARContents; } public void setAdditionalJARContents(@NotNull List<Pair<String, String>> additionalJARContents) { myAdditionalJARContents = additionalJARContents; } public void setIgnoreFilePattern(String attributeValue) { myIgnoreFilePattern = attributeValue; } public String getIgnoreFilePattern() { return myIgnoreFilePattern != null ? myIgnoreFilePattern : ""; } public boolean isAlwaysRebuildBundleJAR() { return myAlwaysRebuildBundleJAR; } public void setAlwaysRebuildBundleJAR(boolean alwaysRebuildBundleJAR) { myAlwaysRebuildBundleJAR = alwaysRebuildBundleJAR; } public boolean isIgnorePatternValid() { if (myIgnoreFilePattern == null || myIgnoreFilePattern.length() == 0) { return true; // empty pattern is ok } try { Pattern.compile(myIgnoreFilePattern); return true; } catch (Exception e) { return false; } } /** * This is filled when a configuration is added to a facet. We need this to * get some project wide config settings in here. I am not sure if this is really a good way to do it however * doing it another way would break the DMServer plugin. * * @param facet the osmorc facet which this configuration is used for. */ public void setFacet(OsmorcFacet facet) { myFacet = facet; } public enum OutputPathType { CompilerOutputPath, OsgiOutputPath, SpecificOutputPath } }