| /* ========================================================================== |
| * Copyright 2003-2004 Mevenide Team |
| * |
| * 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.netbeans.nbm.utils; |
| |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.StringTokenizer; |
| import java.util.jar.Attributes; |
| import java.util.jar.JarFile; |
| import java.util.jar.Manifest; |
| import org.apache.maven.plugin.MojoExecutionException; |
| import org.apache.maven.plugin.logging.Log; |
| import org.codehaus.plexus.util.StringUtils; |
| |
| /** |
| * Tag examines the manifest of a jar file and retrieves NetBeans specific information. |
| * @author <a href="mailto:mkleint@codehaus.org">Milos Kleint</a> |
| * |
| */ |
| public class ExamineManifest |
| { |
| |
| private final Log logger; |
| private File jarFile; |
| private File manifestFile; |
| private boolean netBeansModule; |
| private boolean osgiBundle; |
| |
| private boolean localized; |
| private String specVersion; |
| private String implVersion; |
| private String module; |
| private String locBundle; |
| private String classpath; |
| private boolean publicPackages; |
| private boolean populateDependencies = false; |
| private List<String> dependencyTokens = Collections.<String>emptyList(); |
| private Set<String> osgiImports = Collections.<String>emptySet(); |
| private Set<String> osgiExports = Collections.<String>emptySet(); |
| |
| private boolean friendPackages = false; |
| private List<String> friends = Collections.<String>emptyList(); |
| private List<String> packages = Collections.<String>emptyList(); |
| |
| private List<String> requires = Collections.<String>emptyList(); |
| |
| private List<String> provides = Collections.<String>emptyList(); |
| //that's the default behaviour without the special manifest entry |
| private boolean bundleAutoload = true; |
| |
| public ExamineManifest( Log logger ) |
| { |
| this.logger = logger; |
| } |
| |
| public void checkFile() |
| throws MojoExecutionException |
| { |
| |
| resetExamination(); |
| |
| Manifest mf = null; |
| if ( jarFile != null ) |
| { |
| JarFile jar = null; |
| try |
| { |
| jar = new JarFile( jarFile ); |
| mf = jar.getManifest(); |
| } |
| catch ( Exception exc ) |
| { |
| throw new MojoExecutionException( "Could not open " + jarFile + ": " + exc.getMessage(), exc ); |
| } |
| finally |
| { |
| if ( jar != null ) |
| { |
| try |
| { |
| jar.close(); |
| } |
| catch ( IOException io ) |
| { |
| throw new MojoExecutionException( io.getMessage(), io ); |
| } |
| } |
| } |
| } |
| else if ( manifestFile != null ) |
| { |
| InputStream stream = null; |
| try |
| { |
| stream = new FileInputStream( manifestFile ); |
| mf = new Manifest( stream ); |
| } |
| catch ( Exception exc ) |
| { |
| throw new MojoExecutionException( exc.getMessage(), exc ); |
| } |
| finally |
| { |
| if ( stream != null ) |
| { |
| try |
| { |
| stream.close(); |
| } |
| catch ( IOException io ) |
| { |
| throw new MojoExecutionException( io.getMessage(), io ); |
| } |
| } |
| } |
| } |
| if ( mf != null ) |
| { |
| processManifest( mf ); |
| } |
| else |
| { |
| //MNBMODULE-22 |
| File source = manifestFile; |
| if ( source == null ) |
| { |
| source = jarFile; |
| } |
| if ( source == null ) |
| { |
| logger.debug( "No manifest to examine" ); |
| } |
| else |
| { |
| logger.debug( "Cannot find manifest entries in " + source.getAbsolutePath() ); |
| } |
| } |
| } |
| |
| private void resetExamination() |
| { |
| setNetBeansModule( false ); |
| this.localized = false; |
| this.specVersion = null; |
| this.implVersion = null; |
| this.module = null; |
| this.locBundle = null; |
| this.publicPackages = false; |
| classpath = ""; |
| } |
| |
| private void processManifest( Manifest mf ) |
| { |
| Attributes attrs = mf.getMainAttributes(); |
| this.module = attrs.getValue( "OpenIDE-Module" ); |
| setNetBeansModule( getModule() != null ); |
| if ( isNetBeansModule() ) |
| { |
| this.locBundle = attrs.getValue( "OpenIDE-Module-Localizing-Bundle" ); |
| this.localized = locBundle != null; |
| this.specVersion = attrs.getValue( "OpenIDE-Module-Specification-Version" ); |
| this.implVersion = attrs.getValue( "OpenIDE-Module-Implementation-Version" ); |
| String cp = attrs.getValue( Attributes.Name.CLASS_PATH ); |
| classpath = cp == null ? "" : cp; |
| String value = attrs.getValue( "OpenIDE-Module-Public-Packages" ); |
| String frList = attrs.getValue( "OpenIDE-Module-Friends" ); |
| if ( value == null || value.trim().equals( "-" ) ) |
| { |
| this.publicPackages = false; |
| } |
| else |
| { |
| if ( frList != null ) |
| { |
| this.publicPackages = false; |
| String[] friendList = StringUtils.stripAll( StringUtils.split( frList, "," ) ); |
| friendPackages = true; |
| friends = Arrays.asList( friendList ); |
| } |
| else |
| { |
| this.publicPackages = true; |
| } |
| String[] packageList = StringUtils.stripAll( StringUtils.split( value, "," ) ); |
| packages = Arrays.asList( packageList ); |
| } |
| if ( populateDependencies ) |
| { |
| String deps = attrs.getValue( "OpenIDE-Module-Module-Dependencies" ); |
| if ( deps != null ) |
| { |
| StringTokenizer tokens = new StringTokenizer( deps, "," ); |
| List<String> depList = new ArrayList<String>(); |
| while ( tokens.hasMoreTokens() ) |
| { |
| String tok = tokens.nextToken(); |
| //we are just interested in specification and loose dependencies. |
| int spec = tok.indexOf( '>' ); |
| int impl = tok.indexOf( '='); |
| if ( spec > 0 ) |
| { |
| tok = tok.substring( 0, spec ); |
| } |
| else if ( impl > 0 ) |
| { |
| tok = tok.substring( 0, impl ); |
| } |
| int slash = tok.indexOf( '/' ); |
| if ( slash > 0 ) |
| { |
| tok = tok.substring( 0, slash ); |
| } |
| depList.add( tok.trim().intern() ); |
| } |
| this.dependencyTokens = depList; |
| } |
| String req = attrs.getValue( "OpenIDE-Module-Requires" ); |
| String prov = attrs.getValue( "OpenIDE-Module-Provides" ); |
| String needs = attrs.getValue( "OpenIDE-Module-Needs" ); |
| if (prov != null) { |
| provides = Arrays.asList( StringUtils.stripAll( StringUtils.split( prov, "," ) )); |
| } |
| if (req != null || needs != null) { |
| requires = new ArrayList<String>(); |
| if (req != null) { |
| requires.addAll(Arrays.asList( StringUtils.stripAll( StringUtils.split( req, "," ) ))); |
| } |
| if (needs != null) { |
| requires.addAll(Arrays.asList( StringUtils.stripAll( StringUtils.split( needs, "," ) ))); |
| } |
| } |
| } |
| |
| } |
| else |
| { |
| |
| //check osgi headers first, let nb stuff override it, making nb default |
| String bndName = attrs.getValue( "Bundle-SymbolicName" ); |
| if ( bndName != null ) |
| { |
| this.osgiBundle = true; |
| this.module = |
| bndName./* MNBMODULE-125 */replaceFirst( " *;.+", "" )./* MNBMODULE-96 */replace( '-', '_' ); |
| this.specVersion = attrs.getValue( "Bundle-Version" ); |
| String exp = attrs.getValue( "Export-Package" ); |
| String autoload = attrs.getValue( "Nbm-Maven-Plugin-Autoload"); |
| if (autoload != null) { |
| bundleAutoload = Boolean.parseBoolean( autoload ); |
| } |
| this.publicPackages = exp != null; |
| if ( populateDependencies ) |
| { |
| //well, this doesn't appear to cover the major way of declation dependencies in osgi - Import-Package |
| String deps = attrs.getValue( "Require-Bundle" ); |
| if ( deps != null ) |
| { |
| List<String> depList = new ArrayList<String>(); |
| // http://stackoverflow.com/questions/1757065/java-splitting-a-comma-separated-string-but-ignoring-commas-in-quotes |
| for ( String piece : deps.split( ",(?=([^\"]*\"[^\"]*\")*[^\"]*$)" ) ) |
| { |
| depList.add( piece.replaceFirst( ";.+", "" ).trim().intern() ); |
| } |
| this.dependencyTokens = depList; |
| } |
| String imps = attrs.getValue( "Import-Package" ); |
| if ( imps != null ) |
| { |
| Set<String> depList = new HashSet<String>(); |
| // http://stackoverflow.com/questions/1757065/java-splitting-a-comma-separated-string-but-ignoring-commas-in-quotes |
| for ( String piece : imps.split( ",(?=([^\"]*\"[^\"]*\")*[^\"]*$)" ) ) |
| { |
| depList.add( piece.replaceFirst( ";.+", "" ).trim().intern() ); |
| } |
| this.osgiImports = depList; |
| } |
| String exps = attrs.getValue( "Export-Package" ); |
| if ( exps != null ) |
| { |
| Set<String> depList = new HashSet<String>(); |
| // http://stackoverflow.com/questions/1757065/java-splitting-a-comma-separated-string-but-ignoring-commas-in-quotes |
| for ( String piece : exps.split( ",(?=([^\"]*\"[^\"]*\")*[^\"]*$)" ) ) |
| { |
| depList.add( piece.replaceFirst( ";.+", "" ).trim().intern() ); |
| } |
| this.osgiExports = depList; |
| } |
| |
| } |
| } |
| else |
| { |
| |
| // for non-netbeans, non-osgi jars. |
| this.specVersion = attrs.getValue( "Specification-Version" ); |
| this.implVersion = attrs.getValue( "Implementation-Version" ); |
| this.module = attrs.getValue( "Package" ); |
| this.publicPackages = false; |
| classpath = ""; |
| /* if ( module != null ) |
| { |
| // now we have the package to make it a module definition, add the version there.. |
| module = module + "/1"; |
| } |
| */ |
| if ( getModule() == null ) |
| { |
| // do we want to do that? |
| this.module = attrs.getValue( "Extension-Name" ); |
| } |
| } |
| } |
| |
| } |
| |
| /** |
| * The jar file to examine. It is exclusive with manifestFile. |
| * @param jarFileLoc jar file |
| */ |
| public void setJarFile( File jarFileLoc ) |
| { |
| jarFile = jarFileLoc; |
| } |
| |
| /** |
| * Manifest file to be examined. It is exclusive with jarFile. |
| * @param manifestFileLoc manifedt file |
| */ |
| public void setManifestFile( File manifestFileLoc ) |
| { |
| manifestFile = manifestFileLoc; |
| } |
| |
| /** |
| * Either call {@link #setJarFile} or {@link #setManifestFile} as appropriate. |
| * @param artifactFileLoc a JAR or folder |
| */ |
| public void setArtifactFile( File artifactFileLoc ) |
| { |
| if ( artifactFileLoc.isFile() ) |
| { |
| setJarFile( artifactFileLoc ); |
| } |
| else if ( artifactFileLoc.isDirectory() ) |
| { |
| File mani = new File( artifactFileLoc, "META-INF/MANIFEST.MF" ); |
| if ( mani.isFile() ) |
| { |
| setManifestFile( mani ); |
| } // else e.g. jarprj/target/classes has no manifest, so nothing to examine |
| } |
| else |
| { |
| throw new IllegalArgumentException( artifactFileLoc.getAbsolutePath() ); |
| } |
| } |
| |
| public String getClasspath() |
| { |
| return classpath; |
| } |
| |
| public boolean isNetBeansModule() |
| { |
| return netBeansModule; |
| } |
| |
| public void setNetBeansModule( boolean netBeansModule ) |
| { |
| this.netBeansModule = netBeansModule; |
| } |
| |
| public boolean isLocalized() |
| { |
| return localized; |
| } |
| |
| public String getSpecVersion() |
| { |
| return specVersion; |
| } |
| |
| public String getImplVersion() |
| { |
| return implVersion; |
| } |
| |
| /** |
| * Code name base of the module only. |
| * Does not include any release version. |
| * @return module code name base |
| */ |
| public String getModule() |
| { |
| return module != null ? module.replaceFirst( "/\\d+$", "" ) : module; |
| } |
| |
| /** |
| * Full name of module: code name base, then optionally slash and major release version. |
| * @return module full name |
| */ |
| public String getModuleWithRelease() |
| { |
| return module; |
| } |
| |
| /** |
| * returns true if there are defined public packages and there is no friend |
| * declaration. |
| * @return true if has public package |
| */ |
| public boolean hasPublicPackages() |
| { |
| return publicPackages; |
| } |
| |
| public void setPopulateDependencies( boolean populateDependencies ) |
| { |
| this.populateDependencies = populateDependencies; |
| } |
| |
| public List<String> getDependencyTokens() |
| { |
| return dependencyTokens; |
| } |
| |
| /** |
| * returns true if both public packages and friend list are declared. |
| * @return true if has friend package |
| */ |
| public boolean hasFriendPackages() |
| { |
| return friendPackages; |
| } |
| |
| public List<String> getFriends() |
| { |
| return friends; |
| } |
| |
| /** |
| * list of package statements from OpenIDE-Module-Public-Packages. |
| * All items end with .* |
| * @return list of package |
| */ |
| public List<String> getPackages() |
| { |
| return packages; |
| } |
| |
| public boolean isOsgiBundle() |
| { |
| return osgiBundle; |
| } |
| |
| public Set<String> getOsgiImports() |
| { |
| return osgiImports; |
| } |
| |
| public Set<String> getOsgiExports() |
| { |
| return osgiExports; |
| } |
| |
| public List<String> getNetBeansRequiresTokens() |
| { |
| return requires; |
| } |
| |
| public List<String> getNetBeansProvidesTokens() |
| { |
| return provides; |
| } |
| |
| public boolean isBundleAutoload() |
| { |
| return bundleAutoload; |
| } |
| |
| } |