blob: 8a11d1b3ab56e2de8f402712ef8673e81ce7cc8d [file] [log] [blame]
/******************************************************************************
* Copyright (c) 2005-2007 BEA Systems, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Konstantin Komissarchik
******************************************************************************/
package org.eclipse.wst.common.project.facet.core;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.StringTokenizer;
/**
* The default version comparator that will be used when one is not explicitly
* specified. The default version comparator can handle version strings using
* the standard decimal notation. It can also be subclassed to modify the
* separators that are used or to provide custom parsing for a version segment.
*
* @author <a href="mailto:kosta@bea.com">Konstantin Komissarchik</a>
*/
public class DefaultVersionComparator
implements Comparator<String>
{
public final int compare( final String obj1,
final String obj2 )
throws VersionFormatException
{
final Comparable<Object>[] parsed1 = parse( obj1 );
final Comparable<Object>[] parsed2 = parse( obj2 );
for( int i = 0; i < parsed1.length && i < parsed2.length; i++ )
{
final int res = parsed1[ i ].compareTo( parsed2[ i ] );
if( res != 0 ) return res;
}
if( parsed1.length > parsed2.length )
{
return 1;
}
else if( parsed1.length < parsed2.length )
{
return -1;
}
else
{
return 0;
}
}
/**
* Returns the string containing the separator characters that should be
* used when breaking the version string into segments. The default
* implementation returns ".". Subclasses can override this method.
*
* @return the separator characters
*/
protected String getSeparators()
{
return "."; //$NON-NLS-1$
}
/**
* Parses a segment of the version string. The default implementation parses
* the first segment as an integer (leading zeroes are ignored) and the
* rest of the segments as decimals (leading zeroes are kept). Subclasses
* can override this method to provide custom parsing for any number of
* segments.
*
* @param version the full version string
* @param segment the version segment
* @param position the position of the segment in the version string
* @return the parsed representation of the segment as a {@see Comparable}
* @throws VersionFormatException if encountered an error while parsing
*/
protected Comparable<? extends Object> parse( final String version,
final String segment,
final int position )
throws VersionFormatException
{
try
{
if( position == 0 )
{
return new Integer( segment );
}
else
{
return new BigDecimal( "." + segment ); //$NON-NLS-1$
}
}
catch( NumberFormatException e )
{
throw new VersionFormatException( this, version );
}
}
/**
* Parses the version string.
*
* @param ver the version string
* @return an array containing the parsed representation of the version
*/
@SuppressWarnings( "unchecked" )
private Comparable<Object>[] parse( final String ver )
{
final List<String> segments = new ArrayList<String>();
for( StringTokenizer t = new StringTokenizer( ver, getSeparators() );
t.hasMoreTokens(); )
{
segments.add( t.nextToken() );
}
final Comparable[] parsed = new Comparable[ segments.size() ];
for( int i = 0, n = segments.size(); i < n; i++ )
{
parsed[ i ] = parse( ver, segments.get( i ), i );
}
return parsed;
}
}