All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.commonjava.emb.version.autobox.AutoboxingParser Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2011 Red Hat, Inc.
 * 
 * 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.commonjava.emb.version.autobox;

import static org.commonjava.emb.version.autobox.VersionPartSeparatorType.NONE;
import static org.commonjava.emb.version.autobox.VersionPartSeparatorType.separatorTypeOf;
import static org.commonjava.emb.version.autobox.VersionPartType.LOCAL_SNAPSHOT;
import static org.commonjava.emb.version.autobox.VersionPartType.REMOTE_SNAPSHOT;
import static org.commonjava.emb.version.autobox.VersionPartType.SNAPSHOT_DATE_FORMAT;
import static org.commonjava.emb.version.autobox.VersionPartType.SNAPSHOT_TIME_FORMAT;
import static org.commonjava.emb.version.autobox.VersionPartType.partTypeOf;

import org.sonatype.aether.version.InvalidVersionSpecificationException;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.TreeSet;

public final class AutoboxingParser
{

    private AutoboxingParser()
    {
    }

    public static boolean isSingleVersion( final String spec )
    {
        return findRangeBreaks( spec ).isEmpty();
    }

    public static AutoboxableVersionConstraint parseConstraint( final String versionRangeSpec,
                                                                final String rebuildIndicator,
                                                                final String[] qualifiers, final boolean autoBox )
        throws InvalidVersionSpecificationException
    {
        return parseConstraint( versionRangeSpec, rebuildIndicator, Arrays.asList( qualifiers ), autoBox );
    }

    public static AutoboxableVersionConstraint parseConstraint( final String versionRangeSpec,
                                                                final String rebuildIndicator,
                                                                final List qualifiers, final boolean autoBox )
        throws InvalidVersionSpecificationException
    {
        final TreeSet rangeBreaks = findRangeBreaks( versionRangeSpec );
        if ( rangeBreaks.isEmpty() )
        {
            if ( autoBox )
            {
                return new AutoboxableVersionConstraint( parseRange( versionRangeSpec, rebuildIndicator, qualifiers,
                                                                     autoBox ) );
            }
            else
            {
                return new AutoboxableVersionConstraint( parseVersion( versionRangeSpec, rebuildIndicator, qualifiers ) );
            }
        }

        final List ranges = new ArrayList();

        int lastIdx = 0;
        for ( final Integer brk : rangeBreaks )
        {
            final String rawRange = versionRangeSpec.substring( lastIdx, brk );
            ranges.add( parseRange( rawRange, rebuildIndicator, qualifiers, autoBox ) );

            lastIdx = brk;
        }

        if ( lastIdx < versionRangeSpec.length() )
        {
            ranges.add( parseRange( versionRangeSpec.substring( lastIdx ), rebuildIndicator, qualifiers, autoBox ) );
        }

        return new AutoboxableVersionConstraint( ranges );
    }

    public static AutoboxableVersionRange parseRange( final String range, final String rebuildIndicator,
                                                      final String[] qualifiers, final boolean autoBox )
        throws InvalidVersionSpecificationException
    {
        return parseRange( range, rebuildIndicator, Arrays.asList( qualifiers ), autoBox );
    }

    public static AutoboxableVersionRange parseRange( final String range, final String rebuildIndicator,
                                                      final List qualifiers, boolean autoBox )
        throws InvalidVersionSpecificationException
    {
        if ( range == null || ( !autoBox && range.length() < 3 ) || ( autoBox && range.length() < 1 ) )
        {
            throw new InvalidVersionSpecificationException( range, "'" + range
                            + "': Version range must be delimited by brackets,"
                            + " and contain either one or two valid versions within." );
        }

        String rawRange = range;
        final char start = range.charAt( 0 );
        boolean lowerInclusive = true;
        switch ( start )
        {
            case '(':
            {
                lowerInclusive = false;
            }
            case '[':
            {
                rawRange = rawRange.substring( 1 );
                break;
            }
            default:
            {
                if ( !autoBox )
                {
                    throw new InvalidVersionSpecificationException( range, "'" + range
                                    + "': Version range is not delimited on the lower boundary." );
                }
            }
        }

        final char end = range.charAt( range.length() - 1 );
        boolean upperInclusive = true;
        switch ( end )
        {
            case ')':
            {
                upperInclusive = false;
            }
            case ']':
            {
                rawRange = rawRange.substring( 0, rawRange.length() - 1 );
                break;
            }
            default:
            {
                if ( !autoBox )
                {
                    throw new InvalidVersionSpecificationException( range, "'" + range
                                    + "': Version range is not delimited on the upper boundary." );
                }
            }
        }

        final String[] versions = rawRange.split( "," );
        if ( versions == null || versions.length < 1 || versions.length > 2 )
        {
            throw new InvalidVersionSpecificationException( range, "'" + range
                            + "': Version range must contain at least one, and at most two, versions." );
        }
        else if ( versions.length == 1 && ( !lowerInclusive || !upperInclusive ) )
        {
            throw new InvalidVersionSpecificationException( range, "'" + range
                            + "': Version range containing exactly one version MUST be inclusive "
                            + "(i.e. using: [version]) on upper and lower bounds." );
        }

        final AutoboxableVersion lowerBase = parseVersion( versions[0], rebuildIndicator, qualifiers );
        if ( lowerBase.isSnapshot() )
        {
            autoBox = false;
        }

        final AutoboxableVersion lowerAutoboxed = lowerBase.createAutoboxUpperBoundVersion();

        // if (2,... then SHIFT to (2-REDHAT-99999,... so we exclude rebuilds of version 2.
        // otherwise, leave it alone so we match version 2 as well as all of its rebuilds.
        final AutoboxableVersion lower = autoBox && !lowerInclusive ? lowerAutoboxed : lowerBase;
        final AutoboxableVersion upper;

        if ( versions.length == 1 )
        {
            upper = !autoBox ? lowerBase : lowerAutoboxed;
        }
        else
        {
            final AutoboxableVersion version = parseVersion( versions[1], rebuildIndicator, qualifiers );
            if ( upperInclusive )
            {
                upper = !autoBox ? version : version.createAutoboxUpperBoundVersion();
            }
            else
            {
                // if ...,3) DO NOT shift to ...,3-REDHAT-99999
                // in this case, ...,3) will also exclude rebuilds of version 3.
                upper = version;
            }
        }

        return new AutoboxableVersionRange( lower, lowerInclusive, upper, upperInclusive );
    }

    public static AutoboxableVersion parseVersion( final String version, final String rebuildIndicator,
                                                   final String[] qualifiers )
        throws InvalidVersionSpecificationException
    {
        return parseVersion( version, rebuildIndicator, Arrays.asList( qualifiers ) );
    }

    public static AutoboxableVersion parseVersion( final String version, final String rebuildIndicator,
                                                   final List qualifiers )
        throws InvalidVersionSpecificationException
    {
        final TreeSet partBreaks = findPartBreaks( version );

        final List parts = new ArrayList();

        if ( partBreaks.isEmpty() )
        {
            parts.add( parseRawPart( version, rebuildIndicator, qualifiers ) );
        }
        else
        {
            int lastIdx = 0;
            for ( final Integer brk : partBreaks )
            {
                final String rawPart = version.substring( lastIdx, brk );
                parts.add( parseRawPart( rawPart, rebuildIndicator, qualifiers ) );
                lastIdx = brk;
            }

            if ( lastIdx < version.length() )
            {
                parts.add( parseRawPart( version.substring( lastIdx ), rebuildIndicator, qualifiers ) );
            }
        }

        // cull out the nulls, where a part may have been an empty string or something else weird like that.
        for ( final Iterator it = parts.iterator(); it.hasNext(); )
        {
            final VersionPart part = it.next();
            if ( part == null )
            {
                it.remove();
            }
        }

        final boolean isSnapshot = isSnapshot( parts, qualifiers );
        return new AutoboxableVersion( isSnapshot, rebuildIndicator, qualifiers, parts.toArray( new VersionPart[] {} ) );
    }

    private static boolean isSnapshot( final List parts, final List qualifiers )
    {
        if ( parts != null && !parts.isEmpty() )
        {
            final VersionPart lastPart = parts.get( parts.size() - 1 );
            if ( lastPart.getType() == LOCAL_SNAPSHOT )
            {
                return true;
            }

            if ( parts.size() > 2 )
            {
                final VersionPart timePart = parts.get( parts.size() - 2 );
                final VersionPart datePart = parts.get( parts.size() - 3 );
                try
                {
                    new SimpleDateFormat( SNAPSHOT_TIME_FORMAT ).parse( timePart.getRawPart() );
                    new SimpleDateFormat( SNAPSHOT_DATE_FORMAT ).parse( datePart.getRawPart() );

                    // remove the last three; we're going to combine them and add one of type == REMOTE_SNAPSHOT.
                    parts.remove( parts.size() - 1 );
                    parts.remove( parts.size() - 1 );
                    parts.remove( parts.size() - 1 );

                    parts.add( new VersionPart( REMOTE_SNAPSHOT, datePart.getRawPart() + timePart + lastPart,
                                                datePart.getSeparatorType(), datePart.getSeparator(), qualifiers ) );

                    return true;
                }
                catch ( final ParseException e )
                {
                    // NOT a snapshot.
                }
            }
        }

        return false;
    }

    /**
     * Parse a version part into one (or more) VersionPart instances. Some things to note for input:
     * 
     * 
    *
  1. The rawPart will be prefixed by its separator from the previous part, if there is a separator.
  2. *
  3. The rawPart may in fact be a composite. In which case, the break point(s) will be calculated, and the * sub-parts fed back into this method again.
  4. *
* * @param rawPart * The raw version-part string to be parsed into one (or more) VersionPart instances. * @param rebuildIndicator * @param qualifiers * @param parts * The accumulated version-parts from the version being parsed. This is used to accumulate parts, even in * recursive calls. */ private static VersionPart parseRawPart( String rawPart, final String rebuildIndicator, final List qualifiers ) { if ( rawPart == null || rawPart.trim().length() < 1 ) { return null; } Character separator = rawPart.charAt( 0 ); final VersionPartSeparatorType separatorType = separatorTypeOf( separator ); if ( separatorType != NONE ) { rawPart = rawPart.substring( 1 ); } else { separator = null; } int idx = 0; while ( idx < rawPart.length() && !Character.isLetterOrDigit( rawPart.charAt( idx ) ) ) { idx++; } if ( idx > 0 ) { rawPart = rawPart.substring( idx ); } if ( rawPart.length() < 1 ) { return null; } final VersionPartType type = partTypeOf( rawPart, rebuildIndicator, qualifiers ); return new VersionPart( type, rawPart, separatorType, separator, qualifiers ); } /** * Breaks consist of any non-alphanumeric character, or any alpha-to-numeric or numeric-to-alpha boundary. * * @param version * The version to be parsed for breaks. * * @return The list of indexes in the version where breaks occur, for feeding into * {@link String#substring(int, int)}. Return an empty set if no breaks are detected. NEVER NULL. */ private static TreeSet findPartBreaks( final String version ) { final TreeSet breaks = new TreeSet(); char last = '\u0000'; int idx = 0; for ( final char c : version.toCharArray() ) { if ( !Character.isLetterOrDigit( c ) ) { breaks.add( idx ); } else if ( Character.isLetterOrDigit( last ) && ( ( Character.isDigit( last ) && !Character.isDigit( c ) ) || ( !Character.isDigit( last ) && Character.isDigit( c ) ) ) ) { breaks.add( idx ); } last = c; idx++; } return breaks; } /** * Breaks consist of any [(,)] character. * * @param version * The version-range to be parsed for breaks. * * @return The list of indexes in the version-range where breaks occur, for feeding into * {@link String#substring(int, int)}. Return an empty set if no breaks are detected. NEVER NULL. */ private static TreeSet findRangeBreaks( final String version ) { final TreeSet breaks = new TreeSet(); int idx = 0; for ( final char c : version.toCharArray() ) { if ( idx > 0 && ( ( '[' == c ) || ( '(' == c ) ) ) { breaks.add( idx ); } idx++; } return breaks; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy