/**
   * Method that computes the URL for an asset. The computed URL may require completing at a later
   * stage.
   *
   * @param requestContext the mariner request context
   * @param asset the Asset whose URL we are computing
   * @param assetGroup the AssetGroup associated with the Asset
   * @param marinerURL the URL that is being rewritten/computed
   * @return the computed URL
   */
  public static MarinerURL computeURL(
      MarinerRequestContext requestContext,
      Asset asset,
      AssetGroup assetGroup,
      MarinerURL marinerURL) {

    RuntimeProject project = (RuntimeProject) asset.getIdentity().getProject();
    if (project == null) {
      project = (RuntimeProject) requestContext.getCurrentProject();
    }
    MarinerURL prefix = null;
    MarinerURL newURL = marinerURL;

    if (assetGroup != null) {
      prefix = new MarinerURL(assetGroup.getPrefixURL());
      if (logger.isDebugEnabled()) {
        logger.debug("Retrieved prefix from asset group as " + prefix);
      }
    } else {
      prefix = getPrefixURL(project, asset);
      if (logger.isDebugEnabled()) {
        logger.debug("Retrieved prefix from project " + project + " as " + prefix);
      }
    }
    if (prefix != null) {
      newURL = new MarinerURL(prefix, marinerURL);
    }

    return newURL;
  }
 /**
  * Is the asset a client-side asset or a server-side asset
  *
  * @param asset the Asset
  * @param assetGroup the AssetGroup associated with the Asset
  * @return true if the asset is a client-side asset, false if asset is a server side-asset
  */
 protected static boolean isClientSideURL(Asset asset, AssetGroup assetGroup) {
   if (assetGroup != null && assetGroup.getLocationType() == AssetGroup.ON_DEVICE) {
     return true;
   }
   if (asset instanceof ImageAsset && ((ImageAsset) asset).isLocalSrc()) {
     return true;
   }
   return false;
 }
  /**
   * Method that completes a computed URL
   *
   * @param requestContext the mariner request context
   * @param asset the Asset whose URL we are completing
   * @param assetGroup the AssetGroup associated with the Asset
   * @param marinerURL the URL that is being completed
   * @return the completed URL
   */
  public static MarinerURL completeURL(
      MarinerRequestContext requestContext,
      Asset asset,
      AssetGroup assetGroup,
      MarinerURL marinerURL) {

    // Get the project that owns the resource. If an asset group is
    // provided then it is the asset group's project, otherwise it is the
    // asset's project.
    RuntimeProject assetProject = (RuntimeProject) asset.getProject();
    RuntimeProject project = null;
    if (assetGroup == null) {
      project = assetProject;
    } else {
      project = (RuntimeProject) assetGroup.getProject();
    }
    if (project == null) {
      throw new IllegalStateException("Project not set");
    }

    MarinerURL assetsBaseURL = project.getAssetsBaseURL();
    if (isClientSideURL(asset, assetGroup)) {
      // Client side URLs are not affected by the project's base URL.
    } else if (project.getContainsOrphans() && project.isRemote()) {
      // Asset URLs from remote policies that are not in a remote project
      // should already be fully qualified.
      if (!marinerURL.isAbsolute()) {
        synchronized (DefaultAssetURLRewriter.class) {
          if (!vikingCasualtyChipWOWFlag) {
            vikingCasualtyChipWOWFlag = true;
            logger.warn("url-not-absolute", marinerURL.getExternalForm());
          }
        }
      }
    } else if (marinerURL.containsDocumentRelativePath()) {

      // Document relative assets should only be resolved if the project
      // is portable, otherwise leave them as they are.
      // todo Always resolve these and then provide a way later to optimise URLs in the page to try
      // and make them relative if possible.
      if (project.isPortable()) {

        // If the project is portable then get the project relative path to the
        // policy so that it can be used to resolve relative asset references
        // against.
        MarinerURL projectRelativePath;
        String name = asset.getName();
        if (name.startsWith("/")) {
          projectRelativePath = new MarinerURL(name);
        } else {
          projectRelativePath = new MarinerURL(assetProject.makeProjectRelativePath(name, true));
        }

        // Resolve relative asset references against the project
        // relative path and then make sure that it can be resolved
        // against the assets base URL by removing leading /.
        marinerURL = new MarinerURL(projectRelativePath, marinerURL);
        marinerURL =
            new MarinerURL(
                URLNormalizer.convertHostRelativeToDocumentRelative(marinerURL.getExternalForm()));

        // Resolve the document relative asset URL against the assets
        // base URL.
        marinerURL = new MarinerURL(assetsBaseURL, marinerURL);

        // The result must be absolute, or host relative.
        if (marinerURL.isAbsolute()) {
          // Nothing more to do.
        } else if (marinerURL.containsHostRelativePath()) {
          // May need to make it relative to the context.
          EnvironmentContext environmentContext =
              ContextInternals.getEnvironmentContext(requestContext);
          MarinerURL contextPath = environmentContext.getContextPathURL();
          marinerURL =
              new MarinerURL(
                  contextPath,
                  URLNormalizer.convertHostRelativeToDocumentRelative(
                      marinerURL.getExternalForm()));
        } else {
          throw new IllegalStateException(
              "The rewritten URL "
                  + marinerURL
                  + " for remote asset "
                  + asset
                  + " "
                  + "must be absolute or host relative but is not");
        }
      }

    } else if (marinerURL.containsHostRelativePath()) {

      // Host relative paths are treated as being relative to the
      // project's asset base URL. This is because otherwise assets
      // and asset groups would need to know the host relative path to
      // the assets including the web application context (if any).

      // NOTE: I have the feeling this should be dealt with when the url
      // is computed but there is no description for the intermediate form
      // of the url so I am not prepared to change that. This class needs
      // rewriting/clarifying. Until then we are left with the following
      // bodge...
      // todo: later: deal with document relative urls which are from
      // asset groups which are host relative.
      //
      // If the url was computed from an asset group which was relative
      // to the host, then we should leave this as host relative.
      if (assetGroup != null && assetGroup.getLocationType() == AssetGroup.HOST) {
        // Leave the url as host relative. This will mean than when
        // resolved against the context the context is ignored.
        if (logger.isDebugEnabled()) {
          logger.debug(
              "leaving existing host relative url computed "
                  + "from host relative asset group url as host "
                  + "relative");
        }
      } else {
        // Either it was not from an asset group or the asset group was
        // context relative. In either case we should...

        // Strip the / off the front of the host relative URL to make it
        // a document relative URL so it will resolve against the base
        // URL properly.
        String url =
            URLNormalizer.convertHostRelativeToDocumentRelative(marinerURL.getExternalForm());
        marinerURL = new MarinerURL(assetsBaseURL, url);
      }

      // The resulting URL must be either a host relative path or an
      // absolute URL. If it is not then it is a fatal error as it could
      // only have arisen due to invalid configuration which should have
      // been detected during init.
      if (marinerURL.isRelative() && marinerURL.containsDocumentRelativePath()) {
        throw new RuntimeException(
            "The rewritten URL "
                + marinerURL
                + " for Asset "
                + asset
                + " is not host relative or "
                + "absolute.  The configuration is probably wrong.");
      }
    }
    return marinerURL;
  }