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

com.contentful.java.cda.interceptor.ContentfulUserAgentHeaderInterceptor Maven / Gradle / Ivy

package com.contentful.java.cda.interceptor;

import com.contentful.java.cda.Platform;

import java.util.LinkedHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static java.lang.String.format;
import static java.util.Locale.getDefault;
import static java.util.regex.Pattern.compile;

/**
 * This header interceptor will add contentful relevant information to the custom user agent header.
 */
public class ContentfulUserAgentHeaderInterceptor extends HeaderInterceptor {
  public static final String HEADER_NAME = "X-Contentful-User-Agent";
  private static final String NO_ASCII_REGEX = "[^\\p{ASCII}]+";
  private static final Pattern NO_ASCII_PATTERN = Pattern.compile(NO_ASCII_REGEX);

  /**
   * A section of values used to fill out the Contentful custom HTTP header.
   */
  public static class Section {

    /**
     * Fixed enumeration of what operating systems we support.
     */
    public enum OperatingSystem {
      iOS, tvOS, watchOS, macOS, Windows, Linux, Android;

      /**
       * Parses a string to the closes match of a given OperatingSystemConstant.
       *
       * @param osName the name of the os, as returned from the platform.
       * @return one of the {@link OperatingSystem} fields closely matching, or null if unknown.
       * @see Platform
       */
      public static OperatingSystem parse(String osName) {
        osName = removeNonAsciiCharacters(osName);
        if (osName.startsWith("Windows")) {
          return Windows;
        }
        if (osName.startsWith("Mac OS")) {
          return macOS;
        }
        if (osName.startsWith("Android")) {
          return Android;
        }
        return Linux;
      }
    }

    /**
     * Defines a version of this section.
     */
    public static class Version {

      private static final String VERSION_REGEX = "^([0-9]+).([0-9]+).([0-9]+)(.*)?$";
      private static final Pattern VERSION_PATTERN = compile(VERSION_REGEX);
      private static final int VERSION_PATTERN_GROUP_COUNT = 4;
      private static final int VERSION_PATTERN_GROUP_MAJOR = 1;
      private static final int VERSION_PATTERN_GROUP_MINOR = 2;
      private static final int VERSION_PATTERN_GROUP_PATCH = 3;
      private static final String STABILITY_REGEX = "^([a-zA-Z]+[0-9]*).*";
      private static final Pattern STABILITY_PATTERN = compile(STABILITY_REGEX);

      /**
       * Convert a version into a Semver and Contentful conform version number.
       * 

* A valid version string would be one similar to *

    *
  • 0.9.7
  • *
  • 1.0.0
  • *
  • 1.3.4
  • *
  • 0.2.6-beta
  • *
  • 0.2.6-beta1
  • *
  • 1.0.3-RC43
  • *
* * @param version with 3 numbers for major, minor, patch and stability (dev, BETA3, etc) * @return a version of (1, 2, 3, dev), or null if a parsing error occurred. * @see Semver.org */ public static Version parse(String version) { if (version == null || version.length() <= 0) { return null; } version = removeNonAsciiCharacters(version); if (!version.contains(".")) { return null; } else if (version.indexOf(".") == version.lastIndexOf(".")) { version = version + ".0"; } final Matcher matcher = VERSION_PATTERN.matcher(version); if (matcher.find() && matcher.groupCount() == VERSION_PATTERN_GROUP_COUNT) { // ignore first full matching result int major = extractNumberFromGroup(matcher, VERSION_PATTERN_GROUP_MAJOR); int minor = extractNumberFromGroup(matcher, VERSION_PATTERN_GROUP_MINOR); int patch = extractNumberFromGroup(matcher, VERSION_PATTERN_GROUP_PATCH); if (major == minor && minor == patch && patch == 0) { return null; } final String stability = parseStability(matcher.group(4)); return new Version(major, minor, patch, stability); } else { return null; } } private static int extractNumberFromGroup(Matcher matcher, int group) { try { return Integer.parseInt(matcher.group(group)); } catch (IllegalArgumentException e) { return 0; } } private static String parseStability(String stability) { // check for hyphen if (!stability.startsWith("-")) { return null; } stability = stability.substring(1); stability = removeNonAsciiCharacters(stability); // check for consecutive letters final Matcher matcher = STABILITY_PATTERN.matcher(stability); if (matcher.find()) { return matcher.group(0); } else { return null; } } private final int major; private final int minor; private final int patch; private final String stability; /** * Create a release version, omitting stability * * @param major How many breaking changes did this version release? * @param minor How many additional backwards compatible changes were added? * @param patch How many bugs were fixed in the release? * @see #parse(String) */ public Version(int major, int minor, int patch) { this(major, minor, patch, null); } /** * Create a version including a stability. * * @param major How many breaking changes did this version release? * @param minor How many additional backwards compatible changes were added? * @param patch How many bugs were fixed in the release? * @param stability Is this a stable version(null) or is this not (dev, BETA, …)? * @see #parse(String) */ public Version(int major, int minor, int patch, String stability) { this.major = major; this.minor = minor; this.patch = patch; this.stability = stability; } /** * @return major version part. */ public int getMajor() { return major; } /** * @return minor version part. */ public int getMinor() { return minor; } /** * @return patch part. */ public int getPatch() { return patch; } /** * @return stability or null. */ public String getStability() { return stability; } /** * @return version into a human and machine readable String. */ @Override public String toString() { if (stability == null) { return format(getDefault(), "%d.%d.%d", major, minor, patch); } else { return format(getDefault(), "%d.%d.%d-%s", major, minor, patch, stability); } } } private static final String APP = "app"; private static final String INTEGRATION = "integration"; private static final String SDK = "sdk"; private static final String PLATFORM = "platform"; private static final String OS = "os"; /** * Create an app section. * * @param name of the app. * @param version of the app. * @return a new Section. */ public static Section integration(String name, Version version) { return new Section(INTEGRATION, name, version); } /** * Create an sdk section. * * @param name of the sdk. * @param version of the sdk. * @return a new Section. */ public static Section sdk(String name, Version version) { return new Section(SDK, name, version); } /** * Create an platform section. * * @param name of the platform. * @param version of the platform. * @return a new Section. */ public static Section platform(String name, Version version) { return new Section(PLATFORM, name, version); } /** * Create an os section. * * @param os one of the supported and understood operation systems. * @param version the version of the os. * @return a new Section. */ public static Section os(OperatingSystem os, Version version) { return new Section(OS, os.name(), version); } /** * Create an app section. * * @param name of the app. * @param version of the app. * @return a new Section or null, if app name is invalid. */ public static Section app(String name, Version version) { name = check(name); if (name == null) { return null; } else { return new Section(APP, name, version); } } private static String check(String name) { if (name == null || name.length() <= 0) { return null; } name = removeNonAsciiCharacters(name); return name.replace(" ", "-").toLowerCase(); } private final String identifier; private final String name; private final Version version; /** * Create a section. * * @param name How do we call this header section? * @param version What is the version of this fields' value? */ private Section(String identifier, String name, Version version) { this.identifier = removeNonAsciiCharacters(identifier); this.name = removeNonAsciiCharacters(name); this.version = version; } /** * @return the name of this section. */ public String getName() { return name; } /** * @return the version of this section. */ public Version getVersion() { return version; } /** * @return which identifier this section uses. */ public String getIdentifier() { return identifier; } /** * @return a string representing the section. */ public String toString() { if (getVersion() == null) { return format( getDefault(), "%s %s; ", getIdentifier(), getName() ); } else { return format( getDefault(), "%s %s/%s; ", getIdentifier(), getName(), getVersion().toString() ); } } } /** * Create Header interceptor, saving parameters. * * @param sections a list of sections to be used to identify this application. */ public ContentfulUserAgentHeaderInterceptor(Section... sections) { super(HEADER_NAME, sectionsToString(checkSections(sections))); } private static Section[] checkSections(Section[] sections) { if (sections == null || sections.length <= 0) { throw new IllegalArgumentException("sections cannot be empty."); } return sections; } private static String sectionsToString(Section[] sections) { // take last section of same identifier final LinkedHashMap mappedSections = new LinkedHashMap(); for (final Section section : sections) { if (section != null) { final String identifier = section.getIdentifier(); mappedSections.put(identifier, section); } } // Stringify sections final StringBuilder builder = new StringBuilder(); for (final Section section : mappedSections.values()) { builder.append(section.toString()); } return builder.toString(); } private static String removeNonAsciiCharacters(String input) { final Matcher m = NO_ASCII_PATTERN.matcher(input); String result = input; if (m.find()) { result = m.replaceAll(""); } return result; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy