View Javadoc
1   package org.apache.maven.index.artifact;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import javax.inject.Named;
23  import javax.inject.Singleton;
24  import java.text.ParseException;
25  import java.text.SimpleDateFormat;
26  
27  /**
28   * An M2 <code>GavCalculator</code>.
29   * 
30   * @author Jason van Zyl
31   * @author Tamas Cservenak
32   */
33  @Singleton
34  @Named( "maven2" )
35  public class M2GavCalculator
36      implements GavCalculator
37  {
38      public Gav pathToGav( String str )
39      {
40          try
41          {
42              String s = str.startsWith( "/" ) ? str.substring( 1 ) : str;
43  
44              int vEndPos = s.lastIndexOf( '/' );
45  
46              if ( vEndPos == -1 )
47              {
48                  return null;
49              }
50  
51              int aEndPos = s.lastIndexOf( '/', vEndPos - 1 );
52  
53              if ( aEndPos == -1 )
54              {
55                  return null;
56              }
57  
58              int gEndPos = s.lastIndexOf( '/', aEndPos - 1 );
59  
60              if ( gEndPos == -1 )
61              {
62                  return null;
63              }
64  
65              String groupId = s.substring( 0, gEndPos ).replace( '/', '.' );
66              String artifactId = s.substring( gEndPos + 1, aEndPos );
67              String version = s.substring( aEndPos + 1, vEndPos );
68              String fileName = s.substring( vEndPos + 1 );
69  
70              boolean checksum = false;
71              boolean signature = false;
72              Gav.HashType checksumType = null;
73              Gav.SignatureType signatureType = null;
74              if ( s.endsWith( ".md5" ) )
75              {
76                  checksum = true;
77                  checksumType = Gav.HashType.md5;
78                  s = s.substring( 0, s.length() - 4 );
79              }
80              else if ( s.endsWith( ".sha1" ) )
81              {
82                  checksum = true;
83                  checksumType = Gav.HashType.sha1;
84                  s = s.substring( 0, s.length() - 5 );
85              }
86  
87              if ( s.endsWith( ".asc" ) )
88              {
89                  signature = true;
90                  signatureType = Gav.SignatureType.gpg;
91                  s = s.substring( 0, s.length() - 4 );
92              }
93  
94              if ( s.endsWith( "maven-metadata.xml" )
95                      || ( fileName.startsWith( "maven-metadata-" ) && fileName.contains( ".xml" ) ) )
96              {
97                  return null;
98              }
99  
100             boolean snapshot = version.endsWith( "SNAPSHOT" );
101 
102             if ( snapshot )
103             {
104                 return getSnapshotGav( s, vEndPos, groupId, artifactId, version, fileName, checksum, signature,
105                     checksumType, signatureType );
106             }
107             else
108             {
109                 return getReleaseGav( s, vEndPos, groupId, artifactId, version, fileName, checksum, signature,
110                     checksumType, signatureType );
111             }
112         }
113         catch ( NumberFormatException | StringIndexOutOfBoundsException e )
114         {
115             return null;
116         }
117     }
118 
119     private Gav getReleaseGav( String s, int vEndPos, String groupId, String artifactId, String version,
120                                String fileName, boolean checksum, boolean signature, Gav.HashType checksumType,
121                                Gav.SignatureType signatureType )
122     {
123         if ( !fileName.startsWith( artifactId + "-" + version + "." )
124             && !fileName.startsWith( artifactId + "-" + version + "-" ) )
125         {
126             // The path does not represents an artifact (filename does not match artifactId-version)!
127             return null;
128         }
129 
130         int nTailPos = vEndPos + artifactId.length() + version.length() + 2;
131 
132         String tail = s.substring( nTailPos );
133 
134         int nExtPos = tail.indexOf( '.' );
135 
136         if ( nExtPos == -1 )
137         {
138             // NX-563: not allowing extensionless paths to be interpreted as artifact
139             return null;
140         }
141 
142         String ext = tail.substring( nExtPos + 1 );
143 
144         String classifier = tail.charAt( 0 ) == '-' ? tail.substring( 1, nExtPos ) : null;
145 
146         return new Gav( groupId, artifactId, version, classifier, ext, null, null, fileName, checksum, checksumType,
147             signature, signatureType );
148     }
149 
150     private Gav getSnapshotGav( String s, int vEndPos, String groupId, String artifactId, String version,
151                                 String fileName, boolean checksum, boolean signature, Gav.HashType checksumType,
152                                 Gav.SignatureType signatureType )
153     {
154 
155         Integer snapshotBuildNo = null;
156 
157         Long snapshotTimestamp = null;
158 
159         int vSnapshotStart = vEndPos + artifactId.length() + version.length() - 9 + 3;
160 
161         String vSnapshot = s.substring( vSnapshotStart, vSnapshotStart + 8 );
162 
163         String classifier;
164 
165         String ext;
166 
167         if ( "SNAPSHOT".equals( vSnapshot ) )
168         {
169             int nTailPos = vEndPos + artifactId.length() + version.length() + 2;
170 
171             String tail = s.substring( nTailPos );
172 
173             int nExtPos = tail.indexOf( '.' );
174 
175             if ( nExtPos == -1 )
176             {
177                 // NX-563: not allowing extensionless paths to be interpreted as artifact
178                 return null;
179             }
180 
181             ext = tail.substring( nExtPos + 1 );
182 
183             classifier = tail.charAt( 0 ) == '-' ? tail.substring( 1, nExtPos ) : null;
184         }
185         else
186         {
187             StringBuilder sb = new StringBuilder( vSnapshot );
188             sb.append( s, vSnapshotStart + sb.length(), vSnapshotStart + sb.length() + 8 );
189 
190             try
191             {
192                 SimpleDateFormat df = new SimpleDateFormat( "yyyyMMdd.HHmmss" );
193                 snapshotTimestamp = df.parse( sb.toString() ).getTime();
194             }
195             catch ( ParseException e )
196             {
197             }
198 
199             int buildNumberPos = vSnapshotStart + sb.length();
200             StringBuilder bnr = new StringBuilder();
201             while ( s.charAt( buildNumberPos ) >= '0' && s.charAt( buildNumberPos ) <= '9' )
202             {
203                 sb.append( s.charAt( buildNumberPos ) );
204                 bnr.append( s.charAt( buildNumberPos ) );
205                 buildNumberPos++;
206             }
207             String snapshotBuildNumber = sb.toString();
208             snapshotBuildNo = Integer.parseInt( bnr.toString() );
209 
210             int n = version.length() > 9 ? version.length() - 9 + 1 : 0;
211 
212             String tail = s.substring( vEndPos + artifactId.length() + n + snapshotBuildNumber.length() + 2 );
213 
214             int nExtPos = tail.indexOf( '.' );
215 
216             if ( nExtPos == -1 )
217             {
218                 // NX-563: not allowing extensionless paths to be interpreted as artifact
219                 return null;
220             }
221 
222             ext = tail.substring( nExtPos + 1 );
223 
224             classifier = tail.charAt( 0 ) == '-' ? tail.substring( 1, nExtPos ) : null;
225 
226             version = version.substring( 0, version.length() - 8 ) + snapshotBuildNumber;
227         }
228 
229         return new Gav( groupId, artifactId, version, classifier, ext, snapshotBuildNo, snapshotTimestamp, fileName,
230             checksum, checksumType, signature, signatureType );
231     }
232 
233     public String gavToPath( Gav gav )
234     {
235 
236         return "/" + gav.getGroupId().replaceAll( "(?m)(.)\\.",
237                 "$1/" ) // replace all '.' except the first char
238                 + "/" + gav.getArtifactId() + "/" + gav.getBaseVersion() + "/" + calculateArtifactName( gav );
239     }
240 
241     public String calculateArtifactName( Gav gav )
242     {
243         if ( gav.getName() != null && gav.getName().trim().length() > 0 )
244         {
245             return gav.getName();
246         }
247         else
248         {
249             StringBuilder path = new StringBuilder( gav.getArtifactId() );
250 
251             path.append( "-" );
252 
253             path.append( gav.getVersion() );
254 
255             if ( gav.getClassifier() != null && gav.getClassifier().trim().length() > 0 )
256             {
257                 path.append( "-" );
258 
259                 path.append( gav.getClassifier() );
260             }
261 
262             if ( gav.getExtension() != null )
263             {
264                 path.append( "." );
265 
266                 path.append( gav.getExtension() );
267             }
268 
269             if ( gav.isSignature() )
270             {
271                 path.append( "." );
272 
273                 path.append( gav.getSignatureType().toString() );
274             }
275 
276             if ( gav.isHash() )
277             {
278                 path.append( "." );
279 
280                 path.append( gav.getHashType().toString() );
281             }
282 
283             return path.toString();
284         }
285     }
286 
287 }