class MediaTypeHelper {

    public static boolean isCompatible(MediaType mediaTypeOne, MediaType mediaTypeTwo) {
        if (mediaTypeOne == null || mediaTypeTwo == null) {
            throw new IllegalArgumentException("Null arguments are not allowed");
        }

        if (mediaTypeOne.isWildcardType() || mediaTypeTwo.isWildcardType()) {
            return true;
        }

        if (mediaTypeOne.getType().equalsIgnoreCase(mediaTypeTwo.getType())) {

            if (mediaTypeOne.isWildcardSubtype()
                || mediaTypeTwo.isWildcardSubtype()
                || mediaTypeOne.getSubtype().equalsIgnoreCase(mediaTypeTwo.getSubtype())) {

                return true;
            }

            Matcher extSubtypeMatcherOne = EXT_SUBTYPE_PATTERN.matcher(mediaTypeOne.getSubtype());
            Matcher extSubtypeMatcherTwo = EXT_SUBTYPE_PATTERN.matcher(mediaTypeTwo.getSubtype());
            boolean extSubtypeMatcherOneMatches = extSubtypeMatcherOne.matches();
            boolean extSubtypeMatcherTwoMatches = extSubtypeMatcherTwo.matches();

            if (!extSubtypeMatcherOneMatches && extSubtypeMatcherTwoMatches) {

                // one is type such as application/xml
                // two is type such as application/atom+xml, application/*+xml, application/xml+*
                return mediaTypeOne.getSubtype().equalsIgnoreCase(extSubtypeMatcherTwo.group(1))
                       || mediaTypeOne.getSubtype().equalsIgnoreCase(extSubtypeMatcherTwo.group(2));

            } else if (extSubtypeMatcherOneMatches && !extSubtypeMatcherTwoMatches) {

                // one is type such as application/atom+xml, application/*+xml, application/xml+*
                // two is type such as application/xml
                return mediaTypeTwo.getSubtype().equalsIgnoreCase(extSubtypeMatcherOne.group(1))
                       || mediaTypeTwo.getSubtype().equalsIgnoreCase(extSubtypeMatcherOne.group(2));

            } else if (extSubtypeMatcherOneMatches && extSubtypeMatcherTwoMatches) {

                // both types are extended types
                String subtypePrefixOne = extSubtypeMatcherOne.group(1);
                String subTypeSuffixOne = extSubtypeMatcherOne.group(2);
                String subTypePrefixTwo = extSubtypeMatcherTwo.group(1);
                String subtypeSuffixTwo = extSubtypeMatcherTwo.group(2);

                if (subtypePrefixOne.equalsIgnoreCase(subTypePrefixTwo)
                    && (MEDIA_TYPE_WILDCARD.equals(subTypeSuffixOne) || MEDIA_TYPE_WILDCARD.equals(subtypeSuffixTwo))) {
                    // parts before '+' are the same and one of after '+' is wildcard '*'
                    // For example two sub-types: atom+* and atom+xml
                    return true;
                }
                if (subTypeSuffixOne.equalsIgnoreCase(subtypeSuffixTwo)
                    && (MEDIA_TYPE_WILDCARD.equals(subtypePrefixOne) || MEDIA_TYPE_WILDCARD.equals(subTypePrefixTwo))) {
                    // parts after '+' are the same and one of before '+' is wildcard '*'
                    // For example two sub-types: *+xml and atom+xml
                    return true;
                }
            }
        }
        return false;
    }

    private  MediaTypeHelper();

}