/**
   * Check whether it's legal to add the {@link CMAbstractCode resource} with the given parameters.
   */
  private static boolean shouldAddCode(
      CMAbstractCode code, boolean onlyIeExcludes, boolean onlyExternalLinks) {

    boolean add = Boolean.FALSE;

    // add code if both "only" parameters are false
    if (!onlyExternalLinks && !onlyIeExcludes) {
      add = Boolean.TRUE;
    } else {
      // add either all ieExcludes, or all externalLinks. Do not add ExternalLinks if it's also an
      // IeExclude.
      boolean isIeExclude = code.getIeExpression().length() != 0;
      boolean isExternalLink = code.getDataUrl().length() != 0;

      if (isIeExclude) {
        if (onlyIeExcludes) {
          add = Boolean.TRUE;
        }
      } else {
        if (isExternalLink && onlyExternalLinks) {
          add = Boolean.TRUE;
        }
      }
    }

    return add;
  }
  /**
   * Return a filtered list of {@link CMAbstractCode codes} for the given list of codes and their
   * {@link CMAbstractCode#getInclude() includes}. Does not include {@link
   * CMAbstractCode#getDataUrl() external links} and {@link CMAbstractCode#getIeExpression() ie
   * specific links}.
   */
  @Nonnull
  private static List<CMAbstractCode> getCodeListForHash(
      @Nullable List<? extends CMAbstractCode> codes,
      @Nullable List excludes,
      @Nonnull List<CMAbstractCode> results) {

    if (codes != null) {
      for (CMAbstractCode code : codes) {

        boolean excluded = excludes != null && excludes.contains(code);

        if (!excluded) {

          // get all included contents as well. Do not use includes of excluded contents.
          if (code.getInclude().size() != 0) {
            getCodeListForHash(code.getInclude(), excludes, results);
          }

          // only add resources that are not IeExcludes nor ExternalLinks
          boolean isIeExclude = code.getIeExpression().length() != 0;
          boolean isExternalLink = code.getDataUrl().length() != 0;
          if (!isIeExclude && !isExternalLink) {
            results.add(code);
          }
        }
      }
    }

    return results;
  }
  /**
   * Return a filtered list of {@link CMAbstractCode codes} for the given list of codes and their
   * {@link CMAbstractCode#getInclude() includes}.
   *
   * @param codes The list of resources.
   * @return the codes.
   */
  @Nonnull
  private static List<CMAbstractCode> getFilteredCodeList(
      @Nullable List<? extends CMAbstractCode> codes,
      @Nullable List excludes,
      @Nonnull List<CMAbstractCode> results,
      boolean onlyIeExcludes,
      boolean onlyExternalLinks) {

    if (codes != null) {
      for (CMAbstractCode code : codes) {

        // check if resource is excluded in config
        boolean excluded = excludes != null && excludes.contains(code);

        // only work on non-excluded resources
        if (!excluded) {
          // get all included contents as well. Do not use includes of excluded contents.
          if (code.getInclude().size() != 0) {
            getFilteredCodeList(
                code.getInclude(), excludes, results, onlyIeExcludes, onlyExternalLinks);
          }

          // add resource only if it's valid to do so.
          boolean add = shouldAddCode(code, onlyIeExcludes, onlyExternalLinks);

          if (add) {
            results.add(code);
          }
        }
      }
    }

    return results;
  }
  /**
   * Compile a single hashCode for the tree of codes linked in a navigation.
   *
   * @param navigation the navigation
   * @param extension the extension of the codes
   * @param excludes exludes codes to exclude
   * @return the hash
   */
  public static int getHashes(CMNavigation navigation, String extension, List excludes) {
    List<CMAbstractCode> codes = new ArrayList<>();

    if (CSS_EXTENSION.equals(extension)) {
      codes = getCodeListForHash(navigation.getCss(), excludes, codes);
    } else if (JAVASCRIPT_EXTENSION.equals(extension)) {
      codes = getCodeListForHash(navigation.getJavaScript(), excludes, codes);
    }

    int hash = 0;

    for (CMAbstractCode code : codes) {

      if (code.getCode() != null) {
        hash += code.getCode().hashCode();
      }
    }

    return hash;
  }