/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2015, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.services.version;
import java.util.Scanner;
import java.util.regex.Pattern;
import javax.annotation.Nonnull;
import com.google.common.base.Preconditions;
import appeng.services.version.exceptions.InvalidBuildException;
import appeng.services.version.exceptions.InvalidChannelException;
import appeng.services.version.exceptions.InvalidRevisionException;
import appeng.services.version.exceptions.InvalidVersionException;
import appeng.services.version.exceptions.MissingSeparatorException;
import appeng.services.version.exceptions.VersionCheckerException;
/**
* can parse a version in form of rv2-beta-8 or rv2.beta.8
*/
public final class VersionParser
{
private static final Pattern PATTERN_DOT = Pattern.compile( "\\." );
private static final Pattern PATTERN_DASH = Pattern.compile( "-" );
private static final Pattern PATTERN_REVISION = Pattern.compile( "[^0-9]+" );
private static final Pattern PATTERN_BUILD = Pattern.compile( "[^0-9]+" );
private static final Pattern PATTERN_NATURAL = Pattern.compile( "[0-9]+" );
private static final Pattern PATTERN_VALID_REVISION = Pattern.compile( "^rv\\d+$" );
/**
* Parses the {@link Version} out of a String
*
* @param raw String in form of rv2-beta-8 or rv2.beta.8
*
* @return {@link Version} encoded in the raw String
*
* @throws VersionCheckerException if parsing the raw string was not successful.
*
*/
public Version parse( @Nonnull final String raw ) throws VersionCheckerException
{
Preconditions.checkNotNull( raw );
final String transformed = this.transformDelimiter( raw );
final String[] split = transformed.split( "_" );
return this.parseVersion( split );
}
/**
* Replaces all "." and "-" into "_" to make them uniform
*
* @param raw raw version string containing "." or "-"
*
* @return transformed raw, where "." and "-" are replaced by "_"
*
* @throws MissingSeparatorException if not containing valid separators
*/
private String transformDelimiter( @Nonnull final String raw ) throws MissingSeparatorException
{
if( !( raw.contains( "." ) || raw.contains( "-" ) ) )
{
throw new MissingSeparatorException();
}
final String withoutDot = PATTERN_DOT.matcher( raw ).replaceAll( "_" );
final String withoutDash = PATTERN_DASH.matcher( withoutDot ).replaceAll( "_" );
return withoutDash;
}
/**
* parses the {@link Version} out of the split.
* The split must have a length of 3,
* representing revision, channel and build.
*
* @param splitRaw raw version split with length of 3
*
* @return {@link Version} represented by the splitRaw
*
* @throws InvalidVersionException when length not 3
* @throws InvalidRevisionException {@link VersionParser#parseRevision(String)}
* @throws InvalidChannelException {@link VersionParser#parseChannel(String)}
* @throws InvalidBuildException {@link VersionParser#parseBuild(String)}
*/
private Version parseVersion( @Nonnull final String[] splitRaw ) throws InvalidVersionException, InvalidRevisionException, InvalidChannelException, InvalidBuildException
{
if( splitRaw.length != 3 )
{
throw new InvalidVersionException();
}
final String rawRevision = splitRaw[0];
final String rawChannel = splitRaw[1];
final String rawBuild = splitRaw[2];
final int revision = this.parseRevision( rawRevision );
final Channel channel = this.parseChannel( rawChannel );
final int build = this.parseBuild( rawBuild );
return new DefaultVersion( revision, channel, build );
}
/**
* A revision starts with the keyword "rv", followed by a natural number
*
* @param rawRevision String containing the revision number
*
* @return revision number
*
* @throws InvalidRevisionException if not matching "rv" followed by a natural number.
*/
private int parseRevision( @Nonnull final String rawRevision ) throws InvalidRevisionException
{
if( !PATTERN_VALID_REVISION.matcher( rawRevision ).matches() )
{
throw new InvalidRevisionException();
}
final Scanner scanner = new Scanner( rawRevision );
final int revision = scanner.useDelimiter( PATTERN_REVISION ).nextInt();
scanner.close();
return revision;
}
/**
* A channel is atm either one of {@link Channel#Alpha}, {@link Channel#Beta} or {@link Channel#Stable}
*
* @param rawChannel String containing the channel
*
* @return matching {@link Channel} to the String
*
* @throws InvalidChannelException if not one of {@link Channel} values.
*/
private Channel parseChannel( @Nonnull final String rawChannel ) throws InvalidChannelException
{
if( !( rawChannel.equalsIgnoreCase( Channel.Alpha.name() ) || rawChannel.equalsIgnoreCase( Channel.Beta.name() ) || rawChannel.equalsIgnoreCase( Channel.Stable.name() ) ) )
{
throw new InvalidChannelException();
}
for( final Channel channel : Channel.values() )
{
if( channel.name().equalsIgnoreCase( rawChannel ) )
{
return channel;
}
}
throw new InvalidChannelException();
}
/**
* A build is just a natural number
*
* @param rawBuild String containing the build number
*
* @return build number
*
* @throws InvalidBuildException if not a natural number.
*/
private int parseBuild( @Nonnull final String rawBuild ) throws InvalidBuildException
{
if( !PATTERN_NATURAL.matcher( rawBuild ).matches() )
{
throw new InvalidBuildException();
}
final Scanner scanner = new Scanner( rawBuild );
final int build = scanner.useDelimiter( PATTERN_BUILD ).nextInt();
scanner.close();
return build;
}
}