class OakpalPlan implements JsonObjectConvertible {

    static URI relativizeToBaseParent(final @NotNull URI baseUri, final @NotNull URI uri) throws URISyntaxException {
        if (baseUri.isOpaque() || uri.isOpaque()) {
            return uri;
        } else if (baseUri.getPath().contains("/") && baseUri.getPath().endsWith(".json")) {
            final String basePath = baseUri.getPath().substring(0, baseUri.getPath().lastIndexOf("/") + 1);
            final URI newBase = new URI(baseUri.getScheme(), baseUri.getUserInfo(), baseUri.getHost(),
                    baseUri.getPort(), basePath, baseUri.getQuery(), baseUri.getFragment());
            return newBase.relativize(uri);
        } else {
            return baseUri.relativize(uri);
        }
    }

    private  OakpalPlan(final @Nullable URL base,
                       final @Nullable JsonObject originalJson,
                       final @NotNull String name,
                       final @NotNull List<String> runModes,
                       final @NotNull List<String> checklists,
                       final @NotNull List<URL> preInstallUrls,
                       final @NotNull List<JcrNs> jcrNamespaces,
                       final @NotNull List<QNodeTypeDefinition> jcrNodetypes,
                       final @NotNull List<PrivilegeDefinition> jcrPrivileges,
                       final @NotNull List<ForcedRoot> forcedRoots,
                       final @NotNull List<CheckSpec> checks,
                       final boolean enablePreInstallHooks,
                       final @Nullable InstallHookPolicy installHookPolicy,
                       final @NotNull List<URL> repoInitUrls,
                       final @NotNull List<String> repoInits);

}