SquirrelJME/SquirrelJME

View on GitHub
modules/cldc-compact/src/main/java/cc/squirreljme/jvm/suite/SuiteDependency.java

Summary

Maintainability
A
0 mins
Test Coverage
// -*- Mode: Java; indent-tabs-mode: t; tab-width: 4 -*-
// ---------------------------------------------------------------------------
// SquirrelJME
//     Copyright (C) Stephanie Gawroriski <xer@multiphasicapps.net>
// ---------------------------------------------------------------------------
// SquirrelJME is under the Mozilla Public License Version 2.0.
// See license.mkd for licensing and copyright information.
// ---------------------------------------------------------------------------

package cc.squirreljme.jvm.suite;

import cc.squirreljme.runtime.cldc.util.NaturalComparator;
import cc.squirreljme.runtime.cldc.util.StringUtils;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.Objects;

/**
 * This represents a dependency that a LIBlet or MIDlet may depend on.
 *
 * @since 2017/02/22
 */
public final class SuiteDependency
    implements Comparable<SuiteDependency>, MarkedDependency
{
    /** The dependency type. */
    protected final SuiteDependencyType type;
    
    /** The dependency level. */
    protected final SuiteDependencyLevel level;
    
    /** The name. */
    protected final SuiteName name;
    
    /** The vendor. */
    protected final SuiteVendor vendor;
    
    /** The version range. */
    protected final SuiteVersionRange version;
    
    /** String representation. */
    private Reference<String> _string;
    
    /**
     * Initializes the dependency which is parsed from the given input string.
     *
     * @param __s The string to parse.
     * @throws InvalidSuiteException If the string is not valid.
     * @throws NullPointerException On null arguments.
     * @since 2017/02/22
     */
    public SuiteDependency(String __s)
        throws InvalidSuiteException, NullPointerException
    {
        // Check
        if (__s == null)
            throw new NullPointerException("NARG");
        
        // Trim whitespace
        __s = __s.trim();
        
        // Extract all semicolon positions
        int[] sc = StringUtils.multipleIndexOf(';', __s);
        
        /* {@squirreljme.error DG06 Expected four semi-colons in the
        dependency field. (The input dependency)} */
        if (sc.length != 4)
            throw new InvalidSuiteException(String.format(
                "AR06 %s", __s));
        
        // Split fields
        String intype = __s.substring(0, sc[0]).trim(),
            inlevel = __s.substring(sc[0] + 1, sc[1]).trim(),
            inname = __s.substring(sc[1] + 1, sc[2]).trim(),
            invendor = __s.substring(sc[2] + 1, sc[3]).trim(),
            inversion = __s.substring(sc[3] + 1).trim();
        
        // Required fields
        SuiteDependencyType type;
        this.type = (type = SuiteDependencyType.of(intype));
        this.level = SuiteDependencyLevel.of(inlevel);
        
        // Optional fields
        SuiteName name;
        SuiteVendor vendor;
        SuiteVersionRange version;
        this.name = (name = (inname.isEmpty() ? null :
            new SuiteName(inname)));
        this.vendor = (vendor = (invendor.isEmpty() ? null :
            new SuiteVendor(invendor)));
        this.version = (version = (inversion.isEmpty() ? null :
            new SuiteVersionRange(inversion)));
        
        // Check
        SuiteDependency.__check(type, this.level, name, vendor, version);
    }
    
    /**
     * Initializes the depedency with the given type, level, and where the
     * remainder of the dependencies are parsed from the specified string.
     *
     * @param __type The type of dependency this is.
     * @param __level The level of the dependency.
     * @param __s The string to decode for the remainder of the dependency.
     * @throws InvalidSuiteException If the input parameters are not valid.
     * @throws NullPointerException On null arguments.
     * @since 2017/11/26
     */
    public SuiteDependency(SuiteDependencyType __type,
        SuiteDependencyLevel __level, String __s)
        throws InvalidSuiteException, NullPointerException
    {
        if (__type == null || __level == null || __s == null)
            throw new NullPointerException("NARG");
        
        // Trim whitespace
        __s = __s.trim();
        
        // Extract all semicolon positions
        int[] sc = StringUtils.multipleIndexOf(';', __s);
        
        /* {@squirreljme.error DG07 Expected two semi-colons in the
        dependency field. (The input dependency)} */
        if (sc.length != 2)
            throw new InvalidSuiteException(String.format(
                "AR07 %s", __s));
        
        // Split fields
        String inname = __s.substring(0, sc[0]).trim(),
            invendor = __s.substring(sc[0] + 1, sc[1]).trim(),
            inversion = __s.substring(sc[1] + 1).trim();
        
        // Parse areas fields
        SuiteName name;
        SuiteVendor vendor;
        SuiteVersionRange version;
        this.name = (name = (inname.isEmpty() ? null :
            new SuiteName(inname)));
        this.vendor = (vendor = (invendor.isEmpty() ? null :
            new SuiteVendor(invendor)));
        this.version = (version = (inversion.isEmpty() ? null :
            new SuiteVersionRange(inversion)));
        
        SuiteDependency.__check(__type, __level, name, vendor, version);
        
        // Set
        this.type = __type;
        this.level = __level;
    }
    
    /**
     * Initializes the dependency using the given parameters.
     *
     * @param __type The type of dependency this is.
     * @param __level The level of the dependency.
     * @param __name The name.
     * @param __vendor The vendor.
     * @param __version The version.
     * @throws InvalidSuiteException If the input parameters are not valid.
     * @throws NullPointerException If no type and/or name were specified.
     * @since 2017/11/26
     */
    public SuiteDependency(SuiteDependencyType __type,
        SuiteDependencyLevel __level, SuiteName __name,
        SuiteVendor __vendor, SuiteVersionRange __version)
        throws InvalidSuiteException, NullPointerException
    {
        if (__type == null || __level == null)
            throw new NullPointerException("NARG");
        
        SuiteDependency.__check(__type, __level, __name, __vendor, __version);
        
        // Set
        this.type = __type;
        this.level = __level;
        this.name = __name;
        this.vendor = __vendor;
        this.version = __version;
    }
    
    /**
     * {@inheritDoc}
     * @since 2017/11/30
     */
    @Override
    public final int compareTo(SuiteDependency __d)
        throws NullPointerException
    {
        if (__d == null)
            throw new NullPointerException("NARG");
        
        // Type first
        int rv = this.type.compareTo(__d.type);
        if (rv != 0)
            return rv;
            
        // Optionality
        rv = this.level.compareTo(__d.level);
        if (rv != 0)
            return rv;
        
        // Name
        rv = Objects.<SuiteName>compare(this.name, __d.name,
            NaturalComparator.<SuiteName>instance());
        if (rv != 0)
            return rv;
        
        // Vendor
        rv = Objects.<SuiteVendor>compare(this.vendor, __d.vendor,
            NaturalComparator.<SuiteVendor>instance());
        if (rv != 0)
            return rv;
        
        // Version
        return Objects.<SuiteVersionRange>compare(this.version, __d.version,
            NaturalComparator.<SuiteVersionRange>instance());
    }
    
    /**
     * {@inheritDoc}
     * @since 2017/02/22
     */
    @Override
    public boolean equals(Object __o)
    {
        if (this == __o)
            return true;
        
        // Check
        if (!(__o instanceof SuiteDependency))
            return false;
        
        // Compare
        SuiteDependency o = (SuiteDependency)__o;
        return this.type.equals(o.type) &&
            this.level.equals(o.level) &&
            Objects.equals(this.name, o.name) &&
            Objects.equals(this.vendor, o.vendor) &&
            Objects.equals(this.version, o.version);
    }
    
    /**
     * {@inheritDoc}
     * @since 2017/02/22
     */
    @Override
    public int hashCode()
    {
        return this.type.hashCode() ^
            this.level.hashCode() ^
            Objects.hashCode(this.name) ^
            Objects.hashCode(this.vendor) ^
            Objects.hashCode(this.version);
    }
    
    /**
     * {@inheritDoc}
     * @since 2017/11/22
     */
    @Override
    public boolean isOptional()
    {
        return this.level.isOptional();
    }
    
    /**
     * Is this a required dependency?
     *
     * @return {@code true} if this is a required dependency.
     * @since 2017/11/22
     */
    public boolean isRequired()
    {
        return this.level.isRequired();
    }
    
    /**
     * Returns the dependency level.
     *
     * @return The dependency level.
     * @since 2017/02/22
     */
    public SuiteDependencyLevel level()
    {
        return this.level;
    }
    
    /**
     * Returns the dependency name.
     *
     * @return The dependency name, may be {@code null}.
     * @since 2017/02/22
     */
    public SuiteName name()
    {
        return this.name;
    }
    
    /**
     * {@inheritDoc}
     * @since 2017/12/31
     */
    @Override
    public boolean matchesProvided(MarkedProvided __mp)
        throws NullPointerException
    {
        if (__mp == null)
            throw new NullPointerException("NARG");
        
        SuiteDependencyType type = this.type;
        Class<?> mpclass = __mp.getClass();
        SuiteName name = this.name;
        SuiteVendor vendor = this.vendor;
        SuiteVersionRange version = this.version;
        
        // Depends
        switch (type)
        {
                // Proprietary match
            case PROPRIETARY:
                // Potential internal project name
                if (__mp instanceof InternalName)
                {
                    String myname = name.toString();
                    
                    // Needs at sign
                    int dxat;
                    if ((dxat = myname.indexOf('@')) < 0)
                        return false;
                    
                    // Prefix must be project reference
                    if (!myname.substring(0, dxat).
                        equals("squirreljme.project"))
                        return false;
                    
                    // Otherwise the project name must match
                    return myname.substring(dxat + 1).
                        equals(((InternalName)__mp).name());
                }
                
                // No matches
                return false;
                
                // Library
            case LIBLET:
                // Typed suite information
                if (__mp instanceof TypedSuite)
                {
                    TypedSuite other = (TypedSuite)__mp;
                    SuiteIdentifier ident = other.suite();
                    
                    // Only match other libraries
                    if (other.type() != SuiteType.LIBLET)
                        return false;
                    
                    // Must match name
                    if (!name.equals(ident.name()))
                        return false;
                    
                    // Match vendor if specified
                    if (vendor != null)
                        if (!vendor.equals(ident.vendor()))
                            return false;
                    
                    // Check if version in range if specified
                    if (version != null)
                        if (!version.inRange(ident.version()))
                            return false;
                    
                    // Is okay!
                    return true;
                }
                
                // Unknown
                else
                    return false;
                    
                // Standard
            case STANDARD:
                if (__mp instanceof Standard)
                {
                    Standard other = (Standard)__mp;
                    
                    // Must match name
                    if (!name.equals(other.name()))
                        return false;
                    
                    // Match vendor if specified
                    if (vendor != null)
                        if (!vendor.equals(other.vendor()))
                            return false;
                    
                    // Check if version in range if specified
                    if (version != null)
                        if (!version.inRange(other.version()))
                            return false;
                    
                    // Matches!
                    return true;
                }
                
                // Not a standard
                else
                    return false;
                
                /* {@squirreljme.error DG08 Illegal dependency check.
                (The dependency type; The target class)} */
            default:
                throw new RuntimeException(String.format("AR08 %s %s",
                    type, mpclass));
        }
    }
    
    /**
     * Returns a dependency which is the same as this one except that it is
     * required.
     *
     * @return This dependency but required.
     * @since 2017/11/26
     */
    public SuiteDependency toRequired()
    {
        if (this.isRequired())
            return this;
        return new SuiteDependency(this.type, SuiteDependencyLevel.REQUIRED,
            this.name, this.vendor, this.version);
    }
    
    /**
     * Returns a dependency which is the same as this one except that it is
     * optional.
     *
     * @return This dependency but optional.
     * @since 2017/11/26
     */
    public SuiteDependency toOptional()
    {
        if (this.isOptional())
            return this;
        return new SuiteDependency(this.type, SuiteDependencyLevel.OPTIONAL,
            this.name, this.vendor, this.version);
    }
    
    /**
     * {@inheritDoc}
     * @since 2017/02/22
     */
    @Override
    public String toString()
    {
        // Get
        Reference<String> ref = this._string;
        String rv;
        
        // Cache?
        if (ref == null || null == (rv = ref.get()))
        {
            // These are optional
            SuiteName name = this.name;
            SuiteVendor vendor = this.vendor;
            SuiteVersionRange version = this.version;
            
            // Generate
            this._string = new WeakReference<>((rv = String.format(
                "%s;%s;%s;%s;%s", this.type, this.level,
                (name == null ? "" : name),
                (vendor == null ? "" : vendor),
                (version == null ? "" : version))));
        }
        
        // Return it
        return rv;
    }
    
    /**
     * Returns the dependency type.
     *
     * @return The dependency type.
     * @since 2017/02/22
     */
    public SuiteDependencyType type()
    {
        return this.type;
    }
    
    /**
     * Returns the dependency vendor.
     *
     * @return The dependency vendor, may be {@code null}.
     * @since 2017/02/22
     */
    public SuiteVendor vendor()
    {
        return this.vendor;
    }
    
    /**
     * Returns the dependency version.
     *
     * @return The dependency version, may be {@code null}.
     * @since 2017/02/22
     */
    public SuiteVersionRange version()
    {
        return this.version;
    }
    
    /**
     * Checks whether the provided parameters are correct.
     *
     * @param __type The type of dependency this is.
     * @param __level The level of the dependency.
     * @param __name The name.
     * @param __vendor The vendor.
     * @param __version The version.
     * @throws InvalidSuiteException If the input parameters are not valid.
     * @since 2017/11/26
     */
    private static void __check(SuiteDependencyType __type,
        SuiteDependencyLevel __level, SuiteName __name,
        SuiteVendor __vendor, SuiteVersionRange __version)
        throws InvalidSuiteException
    {
        /* {@squirreljme.error DG09 Dependencies on LIBlets must have the
        name, vendor, and version set. (The type; The level; The name;
        The vendor; The version)} */
        if (__type == SuiteDependencyType.LIBLET && (__name == null ||
            __vendor == null || __version == null))
            throw new InvalidSuiteException(
                String.format("AR09 %s %s %s %s %s", __type, __level, __name,
                    __vendor, __version));
    }
}