Coverage Report - org.apache.maven.shared.osgi.DefaultMaven2OsgiConverter
 
Classes in this File Line Coverage Branch Coverage Complexity
DefaultMaven2OsgiConverter
93 %
120/129
83 %
67/80
8,714
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one
 3  
  * or more contributor license agreements.  See the NOTICE file
 4  
  * distributed with this work for additional information
 5  
  * regarding copyright ownership.  The ASF licenses this file
 6  
  * to you under the Apache License, Version 2.0 (the
 7  
  * "License"); you may not use this file except in compliance
 8  
  * with the License.  You may obtain a copy of the License at
 9  
  *
 10  
  *   http://www.apache.org/licenses/LICENSE-2.0
 11  
  *
 12  
  * Unless required by applicable law or agreed to in writing,
 13  
  * software distributed under the License is distributed on an
 14  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 15  
  * KIND, either express or implied.  See the License for the
 16  
  * specific language governing permissions and limitations
 17  
  * under the License.
 18  
  */
 19  
 package org.apache.maven.shared.osgi;
 20  
 
 21  
 import java.io.File;
 22  
 import java.io.IOException;
 23  
 import java.util.Enumeration;
 24  
 import java.util.HashSet;
 25  
 import java.util.Iterator;
 26  
 import java.util.Map;
 27  
 import java.util.Set;
 28  
 import java.util.jar.JarFile;
 29  
 import java.util.regex.Matcher;
 30  
 import java.util.regex.Pattern;
 31  
 import java.util.zip.ZipEntry;
 32  
 
 33  
 import org.apache.maven.artifact.Artifact;
 34  
 
 35  
 import aQute.lib.osgi.Analyzer;
 36  
 
 37  
 /**
 38  
  * Default implementation of {@link Maven2OsgiConverter}
 39  
  * 
 40  
  * @plexus.component
 41  
  * 
 42  
  * @author <a href="mailto:carlos@apache.org">Carlos Sanchez</a>
 43  
  * @version $Id: DefaultMaven2OsgiConverter.java 573759 2007-09-07 23:45:40Z carlos $
 44  
  */
 45  12
 public class DefaultMaven2OsgiConverter
 46  
     implements Maven2OsgiConverter
 47  
 {
 48  
 
 49  
     /** Bundle-Version must match this pattern */
 50  3
     private static final Pattern OSGI_VERSION_PATTERN = Pattern
 51  
         .compile( "[0-9]+\\.[0-9]+\\.[0-9]+(\\.[0-9A-Za-z_-]+)?" );
 52  
 
 53  
     /** pattern used to change - to . */
 54  
     // private static final Pattern P_VERSION = Pattern.compile("([0-9]+(\\.[0-9])*)-(.*)");
 55  
     /** pattern that matches strings that contain only numbers */
 56  3
     private static final Pattern ONLY_NUMBERS = Pattern.compile( "[0-9]+" );
 57  
 
 58  3
     private static final String FILE_SEPARATOR = System.getProperty( "file.separator" );
 59  
 
 60  
     private String getBundleSymbolicName( String groupId, String artifactId )
 61  
     {
 62  9
         return groupId + "." + artifactId;
 63  
     }
 64  
 
 65  
     /**
 66  
      * Get the symbolic name as groupId + "." + artifactId, with the following exceptions
 67  
      * <ul>
 68  
      * <li>if artifact.getFile is not null and the jar contains a OSGi Manifest with
 69  
      * Bundle-SymbolicName property then that value is returned</li>
 70  
      * <li>if groupId has only one section (no dots) and artifact.getFile is not null then the
 71  
      * first package name with classes is returned. eg. commons-logging:commons-logging ->
 72  
      * org.apache.commons.logging</li>
 73  
      * <li>if artifactId is equal to last section of groupId then groupId is returned. eg.
 74  
      * org.apache.maven:maven -> org.apache.maven</li>
 75  
      * <li>if artifactId starts with last section of groupId that portion is removed. eg.
 76  
      * org.apache.maven:maven-core -> org.apache.maven.core</li>
 77  
      * </ul>
 78  
      */
 79  
     public String getBundleSymbolicName( Artifact artifact )
 80  
     {
 81  30
         if ( ( artifact.getFile() != null ) && artifact.getFile().exists() )
 82  
         {
 83  30
             Analyzer analyzer = new Analyzer();
 84  
 
 85  
             try
 86  
             {
 87  30
                 JarFile jar = new JarFile( artifact.getFile(), false );
 88  
 
 89  30
                 if ( jar.getManifest() != null )
 90  
                 {
 91  30
                     String symbolicNameAttribute = jar.getManifest().getMainAttributes()
 92  
                         .getValue( Analyzer.BUNDLE_SYMBOLICNAME );
 93  30
                     Map bundleSymbolicNameHeader = analyzer.parseHeader( symbolicNameAttribute );
 94  
 
 95  30
                     Iterator it = bundleSymbolicNameHeader.keySet().iterator();
 96  30
                     if ( it.hasNext() )
 97  
                     {
 98  0
                         return (String) it.next();
 99  
                     }
 100  
                 }
 101  
             }
 102  0
             catch ( IOException e )
 103  
             {
 104  0
                 throw new ManifestReadingException( "Error reading manifest in jar "
 105  
                     + artifact.getFile().getAbsolutePath(), e );
 106  30
             }
 107  
         }
 108  
 
 109  30
         int i = artifact.getGroupId().lastIndexOf( '.' );
 110  30
         if ( ( i < 0 ) && ( artifact.getFile() != null ) && artifact.getFile().exists() )
 111  
         {
 112  18
             String groupIdFromPackage = getGroupIdFromPackage( artifact.getFile() );
 113  18
             if ( groupIdFromPackage != null )
 114  
             {
 115  6
                 return groupIdFromPackage;
 116  
             }
 117  
         }
 118  24
         String lastSection = artifact.getGroupId().substring( ++i );
 119  24
         if ( artifact.getArtifactId().equals( lastSection ) )
 120  
         {
 121  15
             return artifact.getGroupId();
 122  
         }
 123  9
         if ( artifact.getArtifactId().startsWith( lastSection ) )
 124  
         {
 125  6
             String artifactId = artifact.getArtifactId().substring( lastSection.length() );
 126  6
             if ( Character.isLetterOrDigit( artifactId.charAt( 0 ) ) )
 127  
             {
 128  3
                 return getBundleSymbolicName( artifact.getGroupId(), artifactId );
 129  
             }
 130  
             else
 131  
             {
 132  3
                 return getBundleSymbolicName( artifact.getGroupId(), artifactId.substring( 1 ) );
 133  
             }
 134  
         }
 135  3
         return getBundleSymbolicName( artifact.getGroupId(), artifact.getArtifactId() );
 136  
     }
 137  
 
 138  
     private String getGroupIdFromPackage( File artifactFile )
 139  
     {
 140  
         try
 141  
         {
 142  
             /* get package names from jar */
 143  18
             Set packageNames = new HashSet();
 144  18
             JarFile jar = new JarFile( artifactFile, false );
 145  18
             Enumeration entries = jar.entries();
 146  3786
             while ( entries.hasMoreElements() )
 147  
             {
 148  3768
                 ZipEntry entry = (ZipEntry) entries.nextElement();
 149  3768
                 if ( entry.getName().endsWith( ".class" ) )
 150  
                 {
 151  3366
                     File f = new File( entry.getName() );
 152  3366
                     String packageName = f.getParent();
 153  3366
                     if ( packageName != null )
 154  
                     {
 155  3366
                         packageNames.add( packageName );
 156  
                     }
 157  
                 }
 158  3768
             }
 159  
 
 160  
             /* find the top package */
 161  18
             String[] groupIdSections = null;
 162  18
             for ( Iterator it = packageNames.iterator(); it.hasNext(); )
 163  
             {
 164  198
                 String packageName = (String) it.next();
 165  
 
 166  198
                 String[] packageNameSections = packageName.split( "\\" + FILE_SEPARATOR );
 167  198
                 if ( groupIdSections == null )
 168  
                 {
 169  
                     /* first candidate */
 170  18
                     groupIdSections = packageNameSections;
 171  
                 }
 172  
                 else
 173  
                 // if ( packageNameSections.length < groupIdSections.length )
 174  
                 {
 175  
                     /*
 176  
                      * find the common portion of current package and previous selected groupId
 177  
                      */
 178  
                     int i;
 179  330
                     for ( i = 0; ( i < packageNameSections.length ) && ( i < groupIdSections.length ); i++ )
 180  
                     {
 181  165
                         if ( !packageNameSections[i].equals( groupIdSections[i] ) )
 182  
                         {
 183  15
                             break;
 184  
                         }
 185  
                     }
 186  180
                     groupIdSections = new String[i];
 187  180
                     System.arraycopy( packageNameSections, 0, groupIdSections, 0, i );
 188  
                 }
 189  198
             }
 190  
 
 191  18
             if ( ( groupIdSections == null ) || ( groupIdSections.length == 0 ) )
 192  
             {
 193  6
                 return null;
 194  
             }
 195  
 
 196  
             /* only one section as id doesn't seem enough, so ignore it */
 197  12
             if ( groupIdSections.length == 1 )
 198  
             {
 199  6
                 return null;
 200  
             }
 201  
 
 202  6
             StringBuffer sb = new StringBuffer();
 203  30
             for ( int i = 0; i < groupIdSections.length; i++ )
 204  
             {
 205  24
                 sb.append( groupIdSections[i] );
 206  24
                 if ( i < groupIdSections.length - 1 )
 207  
                 {
 208  18
                     sb.append( '.' );
 209  
                 }
 210  
             }
 211  6
             return sb.toString();
 212  
         }
 213  0
         catch ( IOException e )
 214  
         {
 215  
             /* we took all the precautions to avoid this */
 216  0
             throw new RuntimeException( e );
 217  
         }
 218  
     }
 219  
 
 220  
     public String getBundleFileName( Artifact artifact )
 221  
     {
 222  6
         return getBundleSymbolicName( artifact ) + "_" + getVersion( artifact.getVersion() ) + ".jar";
 223  
     }
 224  
 
 225  
     public String getVersion( Artifact artifact )
 226  
     {
 227  3
         return getVersion( artifact.getVersion() );
 228  
     }
 229  
 
 230  
     public String getVersion( String version )
 231  
     {
 232  
         String osgiVersion;
 233  
 
 234  
         // Matcher m = P_VERSION.matcher(version);
 235  
         // if (m.matches()) {
 236  
         // osgiVersion = m.group(1) + "." + m.group(3);
 237  
         // }
 238  
 
 239  
         /* TODO need a regexp guru here */
 240  
 
 241  
         Matcher m;
 242  
 
 243  
         /* if it's already OSGi compliant don't touch it */
 244  54
         m = OSGI_VERSION_PATTERN.matcher( version );
 245  54
         if ( m.matches() )
 246  
         {
 247  9
             return version;
 248  
         }
 249  
 
 250  45
         osgiVersion = version;
 251  
 
 252  
         /* check for dated snapshot versions with only major or major and minor */
 253  45
         Pattern DATED_SNAPSHOT = Pattern.compile( "([0-9])(\\.([0-9]))?(\\.([0-9]))?\\-([0-9]{8}\\.[0-9]{6}\\-[0-9]*)" );
 254  45
         m = DATED_SNAPSHOT.matcher( osgiVersion );
 255  45
         if ( m.matches() )
 256  
         {
 257  9
             String major = m.group( 1 );
 258  9
             String minor = ( m.group( 3 ) != null ) ? m.group( 3 ) : "0";
 259  9
             String service = ( m.group( 5 ) != null ) ? m.group( 5 ) : "0";
 260  9
             String qualifier = m.group( 6 ).replaceAll( "-", "_" ).replaceAll( "\\.", "_" );
 261  9
             osgiVersion = major + "." + minor + "." + service + "." + qualifier;
 262  
         }
 263  
 
 264  
         /* else transform first - to . and others to _ */
 265  45
         osgiVersion = osgiVersion.replaceFirst( "-", "\\." );
 266  45
         osgiVersion = osgiVersion.replaceAll( "-", "_" );
 267  45
         m = OSGI_VERSION_PATTERN.matcher( osgiVersion );
 268  45
         if ( m.matches() )
 269  
         {
 270  12
             return osgiVersion;
 271  
         }
 272  
 
 273  
         /* remove dots in the middle of the qualifier */
 274  33
         Pattern DOTS_IN_QUALIFIER = Pattern.compile( "([0-9])(\\.[0-9])?\\.([0-9A-Za-z_-]+)\\.([0-9A-Za-z_-]+)" );
 275  33
         m = DOTS_IN_QUALIFIER.matcher( osgiVersion );
 276  33
         if ( m.matches() )
 277  
         {
 278  9
             String s1 = m.group( 1 );
 279  9
             String s2 = m.group( 2 );
 280  9
             String s3 = m.group( 3 );
 281  9
             String s4 = m.group( 4 );
 282  
 
 283  9
             Matcher qualifierMatcher = ONLY_NUMBERS.matcher( s3 );
 284  
             /*
 285  
              * if last portion before dot is only numbers then it's not in the middle of the
 286  
              * qualifier
 287  
              */
 288  9
             if ( !qualifierMatcher.matches() )
 289  
             {
 290  3
                 osgiVersion = s1 + s2 + "." + s3 + "_" + s4;
 291  
             }
 292  
         }
 293  
 
 294  
         /* convert
 295  
          * 1.string   -> 1.0.0.string
 296  
          * 1.2.string -> 1.2.0.string
 297  
          * 1          -> 1.0.0
 298  
          * 1.1        -> 1.1.0
 299  
          */
 300  
         //Pattern NEED_TO_FILL_ZEROS = Pattern.compile( "([0-9])(\\.([0-9]))?\\.([0-9A-Za-z_-]+)" );
 301  33
         Pattern NEED_TO_FILL_ZEROS = Pattern.compile( "([0-9])(\\.([0-9]))?(\\.([0-9A-Za-z_-]+))?" );
 302  33
         m = NEED_TO_FILL_ZEROS.matcher( osgiVersion );
 303  33
         if ( m.matches() )
 304  
         {
 305  27
             String major = m.group( 1 );
 306  27
             String minor = m.group( 3 );
 307  27
             String service = null;
 308  27
             String qualifier = m.group( 5 );
 309  
 
 310  
             /* if there's no qualifier just fill with 0s */
 311  27
             if ( qualifier == null )
 312  
             {
 313  15
                 osgiVersion = getVersion( major, minor, service, qualifier );
 314  
             }
 315  
             else
 316  
             {
 317  
                 /* if last portion is only numbers then it's not a qualifier */
 318  12
                 Matcher qualifierMatcher = ONLY_NUMBERS.matcher( qualifier );
 319  12
                 if ( qualifierMatcher.matches() )
 320  
                 {
 321  0
                     if ( minor == null )
 322  
                     {
 323  0
                         minor = qualifier;
 324  
                     }
 325  
                     else
 326  
                     {
 327  0
                         service = qualifier;
 328  
                     }
 329  0
                     osgiVersion = getVersion( major, minor, service, null );
 330  
                 }
 331  
                 else
 332  
                 {
 333  12
                     osgiVersion = getVersion( major, minor, service, qualifier );
 334  
                 }
 335  
             }
 336  
         }
 337  
 
 338  33
         m = OSGI_VERSION_PATTERN.matcher( osgiVersion );
 339  
         /* if still its not OSGi version then add everything as qualifier */
 340  33
         if ( !m.matches() )
 341  
         {
 342  6
             String major = "0";
 343  6
             String minor = "0";
 344  6
             String service = "0";
 345  6
             String qualifier = osgiVersion.replaceAll( "\\.", "_" );
 346  6
             osgiVersion = major + "." + minor + "." + service + "." + qualifier;
 347  
         }
 348  
 
 349  33
         return osgiVersion;
 350  
     }
 351  
 
 352  
     private String getVersion( String major, String minor, String service, String qualifier )
 353  
     {
 354  27
         StringBuffer sb = new StringBuffer();
 355  27
         sb.append( major != null ? major : "0" );
 356  27
         sb.append( '.' );
 357  27
         sb.append( minor != null ? minor : "0" );
 358  27
         sb.append( '.' );
 359  27
         sb.append( service != null ? service : "0" );
 360  27
         if ( qualifier != null )
 361  
         {
 362  12
             sb.append( '.' );
 363  12
             sb.append( qualifier );
 364  
         }
 365  27
         return sb.toString();
 366  
     }
 367  
 }