class ExtendedCommunity extends Community {

  @Nonnull
  public static ExtendedCommunity parse(String communityStr) {
    String[] parts = communityStr.trim().split(":");
    checkArgument(parts.length == 3, "Invalid extended community string: %s", communityStr);
    long gaLong;
    long laLong;
    String subType = parts[0].toLowerCase();
    String globalAdministrator = parts[1].toLowerCase();
    String localAdministrator = parts[2].toLowerCase();

    // Try to figure out type/subtype first
    Byte typeByte = null;
    byte subTypeByte;
    // Special values
    if (subType.equals("target")) {
      subTypeByte = 0x02;
    } else if (subType.equals("origin")) {
      subTypeByte = 0x03;
    } else {
      // They type/subtype is a literal integer
      Integer intVal = Ints.tryParse(subType);
      if (intVal != null && intVal <= 0xFFFF && intVal >= 0) {
        subTypeByte = (byte) (intVal & 0xFF);
        typeByte = (byte) ((intVal & 0xFF00) >> 8);
      } else {
        throw new IllegalArgumentException(
            String.format("Invalid extended community type: %s", communityStr));
      }
    }
    // Local administrator, can only be a number
    laLong = Long.parseUnsignedLong(localAdministrator);
    // Global administrator, is complicated. Try a bunch of combinations
    String[] gaParts = globalAdministrator.split("\\.");
    if (gaParts.length == 4) { // Dotted IP address notation
      // type 0x01, 1-byte subtype, 4-byte ip address, 2-byte number la
      typeByte = firstNonNull(typeByte, (byte) 0x01);
      // Ip.parse() ensures IP is valid
      gaLong = Ip.parse(globalAdministrator).asLong();
      checkArgument(laLong <= 0xFFFFL, "Invalid local administrator value %s", localAdministrator);
    } else if (gaParts.length == 2) { // Dotted AS notation
      // type 0x02, 1-byte subtype, 2-byte.2-byte dotted as, 2-byte number la
      typeByte = firstNonNull(typeByte, (byte) 0x02);
      int hi = Integer.parseUnsignedInt(gaParts[0]);
      int lo = Integer.parseUnsignedInt(gaParts[1]);
      checkArgument(
          hi <= 0xFFFF && lo <= 0xFFFF,
          "Invalid global administrator value %s",
          globalAdministrator);
      gaLong = ((long) hi) << 16 | lo;
      checkArgument(laLong <= 0xFFFFL, "Invalid local administrator value %s", localAdministrator);
    } else { // Regular numbers, almost
      if (globalAdministrator.endsWith("l")) { // e.g., 123L, a shorthand for forcing 4-byte GA.
        // type 0x02, 1-byte subtype, 4-byte number ga, 2-byte number la
        typeByte = firstNonNull(typeByte, (byte) 0x02);
        // Strip the "L" and convert to long
        gaLong =
            Long.parseUnsignedLong(
                globalAdministrator.substring(0, globalAdministrator.length() - 1));
        checkArgument(
            gaLong <= 0xFFFFFFFFL, "Invalid global administrator value %s", globalAdministrator);
        checkArgument(
            laLong <= 0xFFFFL, "Invalid local administrator value %s", localAdministrator);
      } else { // No type hint, try both variants with 2-byte GA preferred
        // Try for 2-byte ga, unless the number is too big, in which case it is a 4-byte ga.
        gaLong = Long.parseUnsignedLong(globalAdministrator);
        if (gaLong <= 0xFFFFL) {
          checkArgument(
              laLong <= 0xFFFFFFFFL, "Invalid local administrator value %s", localAdministrator);
          // type 0x00, 1-byte subtype, 2-byte number ga, 4-byte number la
          typeByte = firstNonNull(typeByte, (byte) 0x00);
        } else {
          // type 0x02, 1-byte subtype, 4-byte number ga, 2-byte number la
          checkArgument(
              gaLong <= 0xFFFFFFFFL, "Invalid global administrator value %s", globalAdministrator);
          checkArgument(
              laLong <= 0xFFFFL, "Invalid local administrator value %s", localAdministrator);
          typeByte = firstNonNull(typeByte, (byte) 0x02);
        }
      }
    }
    return of((int) typeByte << 8 | (int) subTypeByte, gaLong, laLong);
  }

  private  ExtendedCommunity(
      byte type, byte subType, long globalAdministrator, long localAdministrator);

}